Skip to content

Commit ed258da

Browse files
committed
feat!: describe usees commitgraph.
With it it can leverage the commitgraph data structure would would be more prominent on server-side applications, presumably.
1 parent 59ce4c6 commit ed258da

File tree

5 files changed

+58
-157
lines changed

5 files changed

+58
-157
lines changed

gix-commitgraph/tests/fixtures/create_fixtures.sh

Lines changed: 0 additions & 27 deletions
This file was deleted.

gix-revision/src/describe.rs

Lines changed: 39 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ impl<'a> Display for Format<'a> {
9494
}
9595
}
9696

97-
type Flags = u32;
97+
/// A bit-field which keeps track of which commit is reachable by one of 32 candidates names.
98+
pub type Flags = u32;
9899
const MAX_CANDIDATES: usize = std::mem::size_of::<Flags>() * 8;
99100

100101
/// The options required to call [`describe()`][function::describe()].
@@ -129,14 +130,11 @@ impl<'name> Default for Options<'name> {
129130
/// The error returned by the [`describe()`][function::describe()] function.
130131
#[derive(Debug, thiserror::Error)]
131132
#[allow(missing_docs)]
132-
pub enum Error<E>
133-
where
134-
E: std::error::Error + Send + Sync + 'static,
135-
{
136-
#[error("Commit {} could not be found during graph traversal", .oid.to_hex())]
137-
Find {
133+
pub enum Error {
134+
#[error("The parents of commit {} could not be added to graph during traversal", oid.to_hex())]
135+
InsertParentsToGraph {
138136
#[source]
139-
err: Option<E>,
137+
err: crate::graph::insert_parents::Error,
140138
oid: gix_hash::ObjectId,
141139
},
142140
#[error("A commit could not be decoded during traversal")]
@@ -148,32 +146,26 @@ pub(crate) mod function {
148146

149147
use bstr::BStr;
150148
use gix_hash::oid;
151-
use gix_hashtable::{hash_map, HashMap};
152-
use gix_object::CommitRefIter;
153149

154150
use super::{Error, Outcome};
155151
use crate::describe::{CommitTime, Flags, Options, MAX_CANDIDATES};
156-
use crate::PriorityQueue;
152+
use crate::{Graph, PriorityQueue};
157153

158-
/// Given a `commit` id, traverse the commit graph and collect candidate names from the `name_by_oid` mapping to produce
154+
/// Given a `commit` id, traverse the commit `graph` and collect candidate names from the `name_by_oid` mapping to produce
159155
/// an `Outcome`, which converted [`into_format()`][Outcome::into_format()] will produce a typical `git describe` string.
160156
///
161157
/// Note that the `name_by_oid` map is returned in the [`Outcome`], which can be forcefully returned even if there was no matching
162158
/// candidate by setting `fallback_to_oid` to true.
163-
pub fn describe<'name, Find, E>(
159+
pub fn describe<'name>(
164160
commit: &oid,
165-
mut find: Find,
161+
graph: &mut Graph<'_, Flags>,
166162
Options {
167163
name_by_oid,
168164
mut max_candidates,
169165
fallback_to_oid,
170166
first_parent,
171167
}: Options<'name>,
172-
) -> Result<Option<Outcome<'name>>, Error<E>>
173-
where
174-
Find: for<'b> FnMut(&oid, &'b mut Vec<u8>) -> Result<Option<CommitRefIter<'b>>, E>,
175-
E: std::error::Error + Send + Sync + 'static,
176-
{
168+
) -> Result<Option<Outcome<'name>>, Error> {
177169
max_candidates = max_candidates.min(MAX_CANDIDATES);
178170
if let Some(name) = name_by_oid.get(commit) {
179171
return Ok(Some(Outcome {
@@ -199,15 +191,12 @@ pub(crate) mod function {
199191
};
200192
}
201193

202-
let mut buf = Vec::new();
203-
let mut parent_buf = Vec::new();
204-
205194
let mut queue = PriorityQueue::from_iter(Some((u32::MAX, commit.to_owned())));
206195
let mut candidates = Vec::new();
207196
let mut commits_seen = 0;
208197
let mut gave_up_on_commit = None;
209-
let mut seen = HashMap::<gix_hash::ObjectId, Flags>::default();
210-
seen.insert(commit.to_owned(), 0u32);
198+
graph.clear();
199+
graph.insert(commit.to_owned(), 0u32);
211200

212201
while let Some(commit) = queue.pop() {
213202
commits_seen += 1;
@@ -220,15 +209,15 @@ pub(crate) mod function {
220209
identity_bit,
221210
order: candidates.len(),
222211
});
223-
let flags = seen.get_mut(&commit).expect("inserted");
212+
let flags = graph.get_mut(&commit).expect("inserted");
224213
*flags |= identity_bit;
225214
*flags
226215
} else {
227216
gave_up_on_commit = Some(commit);
228217
break;
229218
}
230219
} else {
231-
seen[&commit]
220+
graph[&commit]
232221
};
233222

234223
for candidate in candidates
@@ -261,16 +250,7 @@ pub(crate) mod function {
261250
}
262251
}
263252

264-
parents_by_date_onto_queue_and_track_names(
265-
&mut find,
266-
&mut buf,
267-
&mut parent_buf,
268-
&mut queue,
269-
&mut seen,
270-
&commit,
271-
flags,
272-
first_parent,
273-
)?;
253+
parents_by_date_onto_queue_and_track_names(graph, &mut queue, commit, flags, first_parent)?;
274254
}
275255

276256
if candidates.is_empty() {
@@ -300,11 +280,8 @@ pub(crate) mod function {
300280

301281
commits_seen += finish_depth_computation(
302282
queue,
303-
find,
283+
graph,
304284
candidates.first_mut().expect("at least one candidate"),
305-
seen,
306-
buf,
307-
parent_buf,
308285
first_parent,
309286
)?;
310287

@@ -317,105 +294,49 @@ pub(crate) mod function {
317294
}))
318295
}
319296

320-
#[allow(clippy::too_many_arguments)]
321-
fn parents_by_date_onto_queue_and_track_names<Find, E>(
322-
find: &mut Find,
323-
buf: &mut Vec<u8>,
324-
parent_buf: &mut Vec<u8>,
297+
fn parents_by_date_onto_queue_and_track_names(
298+
graph: &mut Graph<'_, Flags>,
325299
queue: &mut PriorityQueue<CommitTime, gix_hash::ObjectId>,
326-
seen: &mut HashMap<gix_hash::ObjectId, Flags>,
327-
commit: &gix_hash::oid,
300+
commit: gix_hash::ObjectId,
328301
commit_flags: Flags,
329302
first_parent: bool,
330-
) -> Result<(), Error<E>>
331-
where
332-
Find: for<'b> FnMut(&oid, &'b mut Vec<u8>) -> Result<Option<CommitRefIter<'b>>, E>,
333-
E: std::error::Error + Send + Sync + 'static,
334-
{
335-
let commit_iter = find(commit, buf)
336-
.map_err(|err| Error::Find {
337-
err: Some(err),
338-
oid: commit.to_owned(),
339-
})?
340-
.ok_or_else(|| Error::Find {
341-
err: None,
342-
oid: commit.to_owned(),
343-
})?;
344-
for token in commit_iter {
345-
match token {
346-
Ok(gix_object::commit::ref_iter::Token::Tree { .. }) => continue,
347-
Ok(gix_object::commit::ref_iter::Token::Parent { id: parent_id }) => match seen.entry(parent_id) {
348-
hash_map::Entry::Vacant(entry) => {
349-
let parent = match find(&parent_id, parent_buf).map_err(|err| Error::Find {
350-
err: Some(err),
351-
oid: commit.to_owned(),
352-
})? {
353-
Some(p) => p,
354-
None => continue, // skip missing objects, they don't exist.
355-
};
356-
357-
let parent_commit_date = parent
358-
.committer()
359-
.map(|committer| committer.time.seconds_since_unix_epoch)
360-
.unwrap_or_default();
361-
362-
entry.insert(commit_flags);
363-
queue.insert(parent_commit_date, parent_id);
364-
}
365-
hash_map::Entry::Occupied(mut entry) => {
366-
*entry.get_mut() |= commit_flags;
367-
}
303+
) -> Result<(), Error> {
304+
graph
305+
.insert_parents(
306+
&commit,
307+
|parent_id, parent_commit_date| {
308+
queue.insert(parent_commit_date as u32, parent_id);
309+
commit_flags
368310
},
369-
Ok(_unused_token) => break,
370-
Err(err) => return Err(err.into()),
371-
}
372-
if first_parent {
373-
break;
374-
}
375-
}
376-
311+
|_parent_id, flags| *flags |= commit_flags,
312+
first_parent,
313+
)
314+
.map_err(|err| Error::InsertParentsToGraph { err, oid: commit })?;
377315
Ok(())
378316
}
379317

380-
#[allow(clippy::too_many_arguments)]
381-
fn finish_depth_computation<'name, Find, E>(
318+
fn finish_depth_computation(
382319
mut queue: PriorityQueue<CommitTime, gix_hash::ObjectId>,
383-
mut find: Find,
384-
best_candidate: &mut Candidate<'name>,
385-
mut seen: HashMap<gix_hash::ObjectId, Flags>,
386-
mut buf: Vec<u8>,
387-
mut parent_buf: Vec<u8>,
320+
graph: &mut Graph<'_, Flags>,
321+
best_candidate: &mut Candidate<'_>,
388322
first_parent: bool,
389-
) -> Result<u32, Error<E>>
390-
where
391-
Find: for<'b> FnMut(&oid, &'b mut Vec<u8>) -> Result<Option<CommitRefIter<'b>>, E>,
392-
E: std::error::Error + Send + Sync + 'static,
393-
{
323+
) -> Result<u32, Error> {
394324
let mut commits_seen = 0;
395325
while let Some(commit) = queue.pop() {
396326
commits_seen += 1;
397-
let flags = seen[&commit];
327+
let flags = graph[&commit];
398328
if (flags & best_candidate.identity_bit) == best_candidate.identity_bit {
399329
if queue
400330
.iter_random()
401-
.all(|id| (seen[id] & best_candidate.identity_bit) == best_candidate.identity_bit)
331+
.all(|id| (graph[id] & best_candidate.identity_bit) == best_candidate.identity_bit)
402332
{
403333
break;
404334
}
405335
} else {
406336
best_candidate.commits_in_its_future += 1;
407337
}
408338

409-
parents_by_date_onto_queue_and_track_names(
410-
&mut find,
411-
&mut buf,
412-
&mut parent_buf,
413-
&mut queue,
414-
&mut seen,
415-
&commit,
416-
flags,
417-
first_parent,
418-
)?;
339+
parents_by_date_onto_queue_and_track_names(graph, &mut queue, commit, flags, first_parent)?;
419340
}
420341
Ok(commits_seen)
421342
}
@@ -431,4 +352,5 @@ pub(crate) mod function {
431352
}
432353
}
433354

355+
/// The timestamp for the creation date of a commit in seconds since unix epoch.
434356
type CommitTime = u32;

gix-revision/tests/describe/mod.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,29 @@ mod format;
1313
fn run_test(
1414
transform_odb: impl FnOnce(gix_odb::Handle) -> gix_odb::Handle,
1515
options: impl Fn(gix_hash::ObjectId) -> gix_revision::describe::Options<'static>,
16-
run_assertions: impl Fn(
17-
Result<Option<Outcome<'static>>, Error<gix_odb::store::find::Error>>,
18-
gix_hash::ObjectId,
19-
) -> crate::Result,
16+
run_assertions: impl Fn(Result<Option<Outcome<'static>>, Error>, gix_hash::ObjectId) -> crate::Result,
2017
) -> crate::Result {
2118
let store = odb_at(".");
2219
let store = transform_odb(store);
2320
let commit_id = hex_to_id("01ec18a3ebf2855708ad3c9d244306bc1fae3e9b");
24-
run_assertions(
25-
gix_revision::describe(
26-
&commit_id,
21+
for use_commitgraph in [false, true] {
22+
let cache = use_commitgraph
23+
.then(|| gix_commitgraph::Graph::from_info_dir(store.store_ref().path().join("info")).ok())
24+
.flatten();
25+
let mut graph = gix_revision::Graph::new(
2726
|id, buf| {
2827
store
2928
.try_find(id, buf)
3029
.map(|r| r.and_then(|d| d.try_into_commit_iter()))
3130
},
32-
options(commit_id),
33-
),
34-
commit_id,
35-
)
31+
cache,
32+
);
33+
run_assertions(
34+
gix_revision::describe(&commit_id, &mut graph, options(commit_id)),
35+
commit_id,
36+
)?;
37+
}
38+
Ok(())
3639
}
3740

3841
#[test]
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
version https://git-lfs.github.com/spec/v1
2-
oid sha256:51c255848776de0f426ffb8858ce44f0b09ab3ac2075628b594f520cf86a55ba
3-
size 13124
2+
oid sha256:7e3e6e069d37db14bb8138eaf7f9d7fade2db6c9985a065da1bbf0684678c961
3+
size 12452

gix-revision/tests/fixtures/make_repo_with_branches.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,8 @@ git commit -q --allow-empty -m c5
2121
git tag at-c5
2222
git merge branch1 -m m1b1
2323

24+
git commit-graph write --no-progress --reachable
25+
git repack -adq
26+
2427
git clone --depth 1 file://$PWD shallow-1-clone
2528
git clone --depth 2 file://$PWD shallow-2-clone

0 commit comments

Comments
 (0)