Skip to content

Commit 759ff30

Browse files
committed
sketch a new peek method that uses gitoxide to fetch things
1 parent ad2f6d9 commit 759ff30

File tree

5 files changed

+134
-8
lines changed

5 files changed

+134
-8
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ include = ["src/**/*", "LICENSE.md", "README.md", "CHANGELOG.md"]
1515
test = false
1616

1717
[dependencies]
18-
git-repository = { version = "0.24.0", default-features = false, features = ["max-performance-safe"] }
18+
git-repository = { version = "0.24.0", default-features = false, features = ["max-performance-safe", "blocking-network-client", "blocking-http-transport"], git = "https://github.com/byron/gitoxide" }
1919
similar = { version = "2.2.0", features = ["bytes"] }
2020
serde = { version = "1", features = ["std", "derive"] }
2121
serde_json = "1"

src/index/diff/mod.rs

Lines changed: 129 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use git_repository as git;
33
use git_repository::prelude::ObjectIdExt;
44
use git_repository::refs::transaction::PreviousValue;
55
use std::convert::TryFrom;
6+
use std::sync::atomic::AtomicBool;
67

78
mod delegate;
89
use delegate::Delegate;
@@ -12,7 +13,7 @@ use delegate::Delegate;
1213
#[allow(missing_docs)]
1314
pub enum Error {
1415
#[error("Failed to fetch crates.io index repository")]
15-
Fetch(#[from] git2::Error),
16+
FetchGit2(#[from] git2::Error),
1617
#[error("Couldn't update marker reference")]
1718
ReferenceEdit(#[from] git::reference::edit::Error),
1819
#[error("Failed to parse rev-spec to determine which revisions to diff")]
@@ -29,6 +30,23 @@ pub enum Error {
2930
file_name: bstr::BString,
3031
line: bstr::BString,
3132
},
33+
#[error(transparent)]
34+
FindRemote(#[from] git::remote::find::existing::Error),
35+
#[error(transparent)]
36+
FindReference(#[from] git::reference::find::existing::Error),
37+
#[error(transparent)]
38+
Connect(#[from] git::remote::connect::Error),
39+
#[error(transparent)]
40+
PrepareFetch(#[from] git::remote::fetch::prepare::Error),
41+
#[error(transparent)]
42+
Fetch(#[from] git::remote::fetch::Error),
43+
#[error(transparent)]
44+
InitAnonymousRemote(#[from] git::remote::init::Error),
45+
#[error("Could not find local tracking branch for remote branch {name:?} in any of {} fetched refs", mappings.len())]
46+
NoMatchingBranch {
47+
name: String,
48+
mappings: Vec<git::remote::fetch::Mapping>,
49+
},
3250
}
3351

3452
/// Find changes without modifying the underling repository
@@ -63,13 +81,16 @@ impl Index {
6381
.ok()
6482
.and_then(|r| r.try_id().map(|id| id.detach()))
6583
.unwrap_or_else(|| git::hash::ObjectId::empty_tree(repo.object_hash()));
84+
let remote_name = self
85+
.remote_name
86+
.expect("always set for this old portion of the code");
6687
let to = {
6788
let repo = git2::Repository::open(repo.git_dir())?;
68-
repo.find_remote(self.remote_name).and_then(|mut r| {
89+
repo.find_remote(remote_name).and_then(|mut r| {
6990
r.fetch(
7091
&[format!(
7192
"+refs/heads/{branch}:refs/remotes/{remote}/{branch}",
72-
remote = self.remote_name,
93+
remote = remote_name,
7394
branch = self.branch_name,
7495
)],
7596
options,
@@ -79,7 +100,7 @@ impl Index {
79100
git::hash::ObjectId::try_from(
80101
repo.refname_to_id(&format!(
81102
"refs/remotes/{}/{}",
82-
self.remote_name, self.branch_name
103+
remote_name, self.branch_name
83104
))?
84105
.as_bytes(),
85106
)
@@ -89,6 +110,110 @@ impl Index {
89110
Ok((self.changes_between_commits(from, to)?, to))
90111
}
91112

113+
/// Return all `Change`s that are observed between the last time `peek_changes*(…)` was called
114+
/// and the latest state of the `crates.io` index repository, which is obtained by fetching
115+
/// the remote called `origin` or whatever is configured for the current `HEAD` branch and lastly
116+
/// what it should be based on knowledge about he crates index.
117+
/// The `last_seen_reference()` will not be created or updated.
118+
/// The second field in the returned tuple is the commit object to which the changes were provided.
119+
/// If one would set the `last_seen_reference()` to that object, the effect is exactly the same
120+
/// as if `fetch_changes(…)` had been called.
121+
///
122+
/// # Resource Usage
123+
///
124+
/// As this method fetches the git repository, loose objects or small packs may be created. Over time,
125+
/// these will accumulate and either slow down subsequent operations, or cause them to fail due to exhaustion
126+
/// of the maximum number of open file handles as configured with `ulimit`.
127+
///
128+
/// Thus it is advised for the caller to run `git gc` occasionally based on their own requirements and usage patterns.
129+
// TODO: update this once it's clear how auto-gc works in `gitoxide`.
130+
pub fn peek_changes_with_options2(
131+
&self,
132+
progress: impl git::Progress,
133+
should_interrupt: &AtomicBool,
134+
) -> Result<(Vec<Change>, git::hash::ObjectId), Error> {
135+
let repo = &self.repo;
136+
let from = repo
137+
.find_reference(self.seen_ref_name)
138+
.ok()
139+
.and_then(|r| r.try_id().map(|id| id.detach()))
140+
.unwrap_or_else(|| git::hash::ObjectId::empty_tree(repo.object_hash()));
141+
let to = {
142+
let remote = self
143+
.remote_name
144+
.and_then(|name| {
145+
self.repo.find_remote(name).ok().or_else(|| {
146+
self.repo
147+
.head()
148+
.ok()
149+
.and_then(|head| {
150+
head.into_remote(git::remote::Direction::Fetch)
151+
.map(|r| r.ok())
152+
.flatten()
153+
})
154+
.or_else(|| {
155+
self.repo
156+
.find_default_remote(git::remote::Direction::Fetch)
157+
.map(|r| r.ok())
158+
.flatten()
159+
})
160+
})
161+
})
162+
.map(Ok)
163+
.unwrap_or_else(|| {
164+
self.repo
165+
.head()?
166+
.into_remote(git::remote::Direction::Fetch)
167+
.map(|r| r.map_err(Error::from))
168+
.or_else(|| {
169+
self.repo
170+
.find_default_remote(git::remote::Direction::Fetch)
171+
.map(|r| r.map_err(Error::from))
172+
})
173+
.unwrap_or_else(|| {
174+
let spec = format!(
175+
"+refs/heads/{branch}:refs/remotes/{remote}/{branch}",
176+
remote = self.remote_name.unwrap_or("origin"),
177+
branch = self.branch_name,
178+
);
179+
self.repo
180+
.remote_at("https://github.com/rust-lang/crates.io-index")
181+
.map_err(Into::into)
182+
.map(|r| {
183+
r.with_refspec(spec.as_str(), git::remote::Direction::Fetch)
184+
.expect("valid refspec")
185+
})
186+
})
187+
})?;
188+
let res: git::remote::fetch::Outcome<'_> = remote
189+
.connect(git::remote::Direction::Fetch, progress)?
190+
.prepare_fetch(Default::default())?
191+
.receive(should_interrupt)?;
192+
let branch_name = format!("refs/heads/{}", self.branch_name);
193+
let local_tracking = res
194+
.ref_map
195+
.mappings
196+
.iter()
197+
.find_map(|m| match &m.remote {
198+
git::remote::fetch::Source::Ref(r) => (r.unpack().0 == branch_name)
199+
.then(|| m.local.as_ref())
200+
.flatten(),
201+
_ => None,
202+
})
203+
.ok_or_else(|| Error::NoMatchingBranch {
204+
name: branch_name,
205+
mappings: res.ref_map.mappings.clone(),
206+
})?;
207+
self.repo
208+
.find_reference(local_tracking)
209+
.expect("local tracking branch exists if we see it here")
210+
.id()
211+
.detach()
212+
};
213+
214+
Ok((self.changes_between_commits(from, to)?, to))
215+
}
216+
92217
/// Similar to `changes()`, but requires `from` and `to` objects to be provided. They may point
93218
/// to either `Commit`s or `Tree`s.
94219
pub fn changes_between_commits(

src/index/init.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ impl Index {
8888
repo.object_cache_size_if_unset(4 * 1024 * 1024);
8989
Ok(Index {
9090
repo,
91-
remote_name: "origin",
91+
remote_name: Some("origin"),
9292
branch_name: "master",
9393
seen_ref_name: LAST_SEEN_REFNAME,
9494
})

src/types.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ pub struct Index {
1212
/// The name of the branch to fetch. This value also affects the tracking branch.
1313
pub branch_name: &'static str,
1414
/// The name of the symbolic name of the remote to fetch from.
15-
pub remote_name: &'static str,
15+
/// If `None`, obtain the remote name from the configuration of the currently checked-out branch.
16+
pub remote_name: Option<&'static str>,
1617
/// The git repository to use for diffing
1718
pub(crate) repo: git::Repository,
1819
}

tests/index/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ fn changes_since_last_fetch() -> crate::Result {
9393

9494
// now the remote has squashed their history, we should still be able to get the correct changes.
9595
git2::Repository::open(repo.git_dir())?.remote("local", repo.git_dir().to_str().unwrap())?;
96-
index.remote_name = "local";
96+
index.remote_name = Some("local");
9797
index
9898
.repository()
9999
.find_reference("refs/heads/main")?

0 commit comments

Comments
 (0)