Skip to content

Commit a856e7d

Browse files
committed
Merge branch 'ds/commit-graph-lockfile-fix'
Update to ds/generation-numbers topic. * ds/commit-graph-lockfile-fix: commit-graph: fix UX issue when .lock file exists commit-graph.txt: update design document merge: check config before loading commits commit: use generation number in remove_redundant() commit: add short-circuit to paint_down_to_common() commit: use generation numbers for in_merge_bases() ref-filter: use generation number for --contains commit-graph: always load commit-graph information commit: use generations in paint_down_to_common() commit-graph: compute generation numbers commit: add generation number to struct commit ref-filter: fix outdated comment on in_commit_list
2 parents b3b2aaf + 33286dc commit a856e7d

File tree

11 files changed

+209
-56
lines changed

11 files changed

+209
-56
lines changed

Documentation/technical/commit-graph.txt

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,29 @@ in the commit graph. We can treat these commits as having "infinite"
7777
generation number and walk until reaching commits with known generation
7878
number.
7979

80+
We use the macro GENERATION_NUMBER_INFINITY = 0xFFFFFFFF to mark commits not
81+
in the commit-graph file. If a commit-graph file was written by a version
82+
of Git that did not compute generation numbers, then those commits will
83+
have generation number represented by the macro GENERATION_NUMBER_ZERO = 0.
84+
85+
Since the commit-graph file is closed under reachability, we can guarantee
86+
the following weaker condition on all commits:
87+
88+
If A and B are commits with generation numbers N amd M, respectively,
89+
and N < M, then A cannot reach B.
90+
91+
Note how the strict inequality differs from the inequality when we have
92+
fully-computed generation numbers. Using strict inequality may result in
93+
walking a few extra commits, but the simplicity in dealing with commits
94+
with generation number *_INFINITY or *_ZERO is valuable.
95+
96+
We use the macro GENERATION_NUMBER_MAX = 0x3FFFFFFF to for commits whose
97+
generation numbers are computed to be at least this value. We limit at
98+
this value since it is the largest value that can be stored in the
99+
commit-graph file using the 30 bits available to generation numbers. This
100+
presents another case where a commit can have generation number equal to
101+
that of a parent.
102+
80103
Design Details
81104
--------------
82105

@@ -98,18 +121,14 @@ Future Work
98121
- The 'commit-graph' subcommand does not have a "verify" mode that is
99122
necessary for integration with fsck.
100123

101-
- The file format includes room for precomputed generation numbers. These
102-
are not currently computed, so all generation numbers will be marked as
103-
0 (or "uncomputed"). A later patch will include this calculation.
104-
105124
- After computing and storing generation numbers, we must make graph
106125
walks aware of generation numbers to gain the performance benefits they
107126
enable. This will mostly be accomplished by swapping a commit-date-ordered
108127
priority queue with one ordered by generation number. The following
109128
operations are important candidates:
110129

111-
- paint_down_to_common()
112130
- 'log --topo-order'
131+
- 'tag --merged'
113132

114133
- Currently, parse_commit_gently() requires filling in the root tree
115134
object for a commit. This passes through lookup_tree() and consequently

alloc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ void *alloc_commit_node(void)
9494
c->object.type = OBJ_COMMIT;
9595
c->index = alloc_commit_index();
9696
c->graph_pos = COMMIT_NOT_FROM_GRAPH;
97+
c->generation = GENERATION_NUMBER_INFINITY;
9798
return c;
9899
}
99100

builtin/merge.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,14 +1186,15 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
11861186
branch = branch_to_free = resolve_refdup("HEAD", 0, &head_oid, NULL);
11871187
if (branch)
11881188
skip_prefix(branch, "refs/heads/", &branch);
1189+
1190+
init_diff_ui_defaults();
1191+
git_config(git_merge_config, NULL);
1192+
11891193
if (!branch || is_null_oid(&head_oid))
11901194
head_commit = NULL;
11911195
else
11921196
head_commit = lookup_commit_or_die(&head_oid, "HEAD");
11931197

1194-
init_diff_ui_defaults();
1195-
git_config(git_merge_config, NULL);
1196-
11971198
if (branch_mergeoptions)
11981199
parse_branch_merge_options(branch_mergeoptions);
11991200
argc = parse_options(argc, argv, prefix, builtin_merge_options,

commit-graph.c

Lines changed: 81 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "cache.h"
22
#include "config.h"
3+
#include "dir.h"
34
#include "git-compat-util.h"
45
#include "lockfile.h"
56
#include "pack.h"
@@ -248,6 +249,13 @@ static struct commit_list **insert_parent_or_die(struct commit_graph *g,
248249
return &commit_list_insert(c, pptr)->next;
249250
}
250251

252+
static void fill_commit_graph_info(struct commit *item, struct commit_graph *g, uint32_t pos)
253+
{
254+
const unsigned char *commit_data = g->chunk_commit_data + GRAPH_DATA_WIDTH * pos;
255+
item->graph_pos = pos;
256+
item->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
257+
}
258+
251259
static int fill_commit_in_graph(struct commit *item, struct commit_graph *g, uint32_t pos)
252260
{
253261
uint32_t edge_value;
@@ -265,6 +273,8 @@ static int fill_commit_in_graph(struct commit *item, struct commit_graph *g, uin
265273
date_low = get_be32(commit_data + g->hash_len + 12);
266274
item->date = (timestamp_t)((date_high << 32) | date_low);
267275

276+
item->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
277+
268278
pptr = &item->parents;
269279

270280
edge_value = get_be32(commit_data + g->hash_len);
@@ -293,31 +303,40 @@ static int fill_commit_in_graph(struct commit *item, struct commit_graph *g, uin
293303
return 1;
294304
}
295305

306+
static int find_commit_in_graph(struct commit *item, struct commit_graph *g, uint32_t *pos)
307+
{
308+
if (item->graph_pos != COMMIT_NOT_FROM_GRAPH) {
309+
*pos = item->graph_pos;
310+
return 1;
311+
} else {
312+
return bsearch_graph(g, &(item->object.oid), pos);
313+
}
314+
}
315+
296316
int parse_commit_in_graph(struct commit *item)
297317
{
318+
uint32_t pos;
319+
298320
if (!core_commit_graph)
299321
return 0;
300322
if (item->object.parsed)
301323
return 1;
302-
303324
prepare_commit_graph();
304-
if (commit_graph) {
305-
uint32_t pos;
306-
int found;
307-
if (item->graph_pos != COMMIT_NOT_FROM_GRAPH) {
308-
pos = item->graph_pos;
309-
found = 1;
310-
} else {
311-
found = bsearch_graph(commit_graph, &(item->object.oid), &pos);
312-
}
313-
314-
if (found)
315-
return fill_commit_in_graph(item, commit_graph, pos);
316-
}
317-
325+
if (commit_graph && find_commit_in_graph(item, commit_graph, &pos))
326+
return fill_commit_in_graph(item, commit_graph, pos);
318327
return 0;
319328
}
320329

330+
void load_commit_graph_info(struct commit *item)
331+
{
332+
uint32_t pos;
333+
if (!core_commit_graph)
334+
return;
335+
prepare_commit_graph();
336+
if (commit_graph && find_commit_in_graph(item, commit_graph, &pos))
337+
fill_commit_graph_info(item, commit_graph, pos);
338+
}
339+
321340
static struct tree *load_tree_for_commit(struct commit_graph *g, struct commit *c)
322341
{
323342
struct object_id oid;
@@ -440,6 +459,8 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
440459
else
441460
packedDate[0] = 0;
442461

462+
packedDate[0] |= htonl((*list)->generation << 2);
463+
443464
packedDate[1] = htonl((*list)->date);
444465
hashwrite(f, packedDate, 8);
445466

@@ -572,6 +593,45 @@ static void close_reachable(struct packed_oid_list *oids)
572593
}
573594
}
574595

596+
static void compute_generation_numbers(struct packed_commit_list* commits)
597+
{
598+
int i;
599+
struct commit_list *list = NULL;
600+
601+
for (i = 0; i < commits->nr; i++) {
602+
if (commits->list[i]->generation != GENERATION_NUMBER_INFINITY &&
603+
commits->list[i]->generation != GENERATION_NUMBER_ZERO)
604+
continue;
605+
606+
commit_list_insert(commits->list[i], &list);
607+
while (list) {
608+
struct commit *current = list->item;
609+
struct commit_list *parent;
610+
int all_parents_computed = 1;
611+
uint32_t max_generation = 0;
612+
613+
for (parent = current->parents; parent; parent = parent->next) {
614+
if (parent->item->generation == GENERATION_NUMBER_INFINITY ||
615+
parent->item->generation == GENERATION_NUMBER_ZERO) {
616+
all_parents_computed = 0;
617+
commit_list_insert(parent->item, &list);
618+
break;
619+
} else if (parent->item->generation > max_generation) {
620+
max_generation = parent->item->generation;
621+
}
622+
}
623+
624+
if (all_parents_computed) {
625+
current->generation = max_generation + 1;
626+
pop_commit(&list);
627+
628+
if (current->generation > GENERATION_NUMBER_MAX)
629+
current->generation = GENERATION_NUMBER_MAX;
630+
}
631+
}
632+
}
633+
}
634+
575635
void write_commit_graph(const char *obj_dir,
576636
const char **pack_indexes,
577637
int nr_packs,
@@ -584,7 +644,6 @@ void write_commit_graph(const char *obj_dir,
584644
struct hashfile *f;
585645
uint32_t i, count_distinct = 0;
586646
char *graph_name;
587-
int fd;
588647
struct lock_file lk = LOCK_INIT;
589648
uint32_t chunk_ids[5];
590649
uint64_t chunk_offsets[5];
@@ -695,24 +754,14 @@ void write_commit_graph(const char *obj_dir,
695754
if (commits.nr >= GRAPH_PARENT_MISSING)
696755
die(_("too many commits to write graph"));
697756

698-
graph_name = get_commit_graph_filename(obj_dir);
699-
fd = hold_lock_file_for_update(&lk, graph_name, 0);
700-
701-
if (fd < 0) {
702-
struct strbuf folder = STRBUF_INIT;
703-
strbuf_addstr(&folder, graph_name);
704-
strbuf_setlen(&folder, strrchr(folder.buf, '/') - folder.buf);
705-
706-
if (mkdir(folder.buf, 0777) < 0)
707-
die_errno(_("cannot mkdir %s"), folder.buf);
708-
strbuf_release(&folder);
709-
710-
fd = hold_lock_file_for_update(&lk, graph_name, LOCK_DIE_ON_ERROR);
757+
compute_generation_numbers(&commits);
711758

712-
if (fd < 0)
713-
die_errno("unable to create '%s'", graph_name);
714-
}
759+
graph_name = get_commit_graph_filename(obj_dir);
760+
if (safe_create_leading_directories(graph_name))
761+
die_errno(_("unable to create leading directories of %s"),
762+
graph_name);
715763

764+
hold_lock_file_for_update(&lk, graph_name, LOCK_DIE_ON_ERROR);
716765
f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);
717766

718767
hashwrite_be32(f, GRAPH_SIGNATURE);

commit-graph.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ char *get_commit_graph_filename(const char *obj_dir);
1717
*/
1818
int parse_commit_in_graph(struct commit *item);
1919

20+
/*
21+
* It is possible that we loaded commit contents from the commit buffer,
22+
* but we also want to ensure the commit-graph content is correctly
23+
* checked and filled. Fill the graph_pos and generation members of
24+
* the given commit.
25+
*/
26+
void load_commit_graph_info(struct commit *item);
27+
2028
struct tree *get_commit_tree_in_graph(const struct commit *c);
2129

2230
struct commit_graph {

0 commit comments

Comments
 (0)