Skip to content

Commit 922cc26

Browse files
committed
Merge branch 'ps/do-not-trust-commit-graph-blindly-for-existence' into kn/rev-list-missing-fix
* ps/do-not-trust-commit-graph-blindly-for-existence: commit: detect commits that exist in commit-graph but not in the ODB commit-graph: introduce envvar to disable commit existence checks
2 parents 2e8e77c + 7a5d604 commit 922cc26

File tree

5 files changed

+84
-2
lines changed

5 files changed

+84
-2
lines changed

Documentation/git.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,16 @@ for full details.
911911
should not normally need to set this to `0`, but it may be
912912
useful when trying to salvage data from a corrupted repository.
913913

914+
`GIT_COMMIT_GRAPH_PARANOIA`::
915+
When loading a commit object from the commit-graph, Git performs an
916+
existence check on the object in the object database. This is done to
917+
avoid issues with stale commit-graphs that contain references to
918+
already-deleted commits, but comes with a performance penalty.
919+
+
920+
The default is "true", which enables the aforementioned behavior.
921+
Setting this to "false" disables the existence check. This can lead to
922+
a performance improvement at the cost of consistency.
923+
914924
`GIT_ALLOW_PROTOCOL`::
915925
If set to a colon-separated list of protocols, behave as if
916926
`protocol.allow` is set to `never`, and each of the listed

commit-graph.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1024,14 +1024,18 @@ int repo_find_commit_pos_in_graph(struct repository *r, struct commit *c,
10241024

10251025
struct commit *lookup_commit_in_graph(struct repository *repo, const struct object_id *id)
10261026
{
1027+
static int commit_graph_paranoia = -1;
10271028
struct commit *commit;
10281029
uint32_t pos;
10291030

1031+
if (commit_graph_paranoia == -1)
1032+
commit_graph_paranoia = git_env_bool(GIT_COMMIT_GRAPH_PARANOIA, 1);
1033+
10301034
if (!prepare_commit_graph(repo))
10311035
return NULL;
10321036
if (!search_commit_pos_in_graph(id, repo->objects->commit_graph, &pos))
10331037
return NULL;
1034-
if (!has_object(repo, id, 0))
1038+
if (commit_graph_paranoia && !has_object(repo, id, 0))
10351039
return NULL;
10361040

10371041
commit = lookup_commit(repo, id);

commit-graph.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88
#define GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE "GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE"
99
#define GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS "GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS"
1010

11+
/*
12+
* This environment variable controls whether commits looked up via the
13+
* commit graph will be double checked to exist in the object database.
14+
*/
15+
#define GIT_COMMIT_GRAPH_PARANOIA "GIT_COMMIT_GRAPH_PARANOIA"
16+
1117
/*
1218
* This method is only used to enhance coverage of the commit-graph
1319
* feature in the test suite with the GIT_TEST_COMMIT_GRAPH and

commit.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "shallow.h"
2929
#include "tree.h"
3030
#include "hook.h"
31+
#include "parse.h"
3132

3233
static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
3334

@@ -572,8 +573,21 @@ int repo_parse_commit_internal(struct repository *r,
572573
return -1;
573574
if (item->object.parsed)
574575
return 0;
575-
if (use_commit_graph && parse_commit_in_graph(r, item))
576+
if (use_commit_graph && parse_commit_in_graph(r, item)) {
577+
static int commit_graph_paranoia = -1;
578+
579+
if (commit_graph_paranoia == -1)
580+
commit_graph_paranoia = git_env_bool(GIT_COMMIT_GRAPH_PARANOIA, 1);
581+
582+
if (commit_graph_paranoia && !has_object(r, &item->object.oid, 0)) {
583+
unparse_commit(r, &item->object.oid);
584+
return quiet_on_missing ? -1 :
585+
error(_("commit %s exists in commit-graph but not in the object database"),
586+
oid_to_hex(&item->object.oid));
587+
}
588+
576589
return 0;
590+
}
577591

578592
if (oid_object_info_extended(r, &item->object.oid, &oi, flags) < 0)
579593
return quiet_on_missing ? -1 :

t/t5318-commit-graph.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,4 +895,52 @@ test_expect_success 'reader notices too-small generations chunk' '
895895
test_cmp expect.err err
896896
'
897897

898+
test_expect_success 'stale commit cannot be parsed when given directly' '
899+
test_when_finished "rm -rf repo" &&
900+
git init repo &&
901+
(
902+
cd repo &&
903+
test_commit A &&
904+
test_commit B &&
905+
git commit-graph write --reachable &&
906+
907+
oid=$(git rev-parse B) &&
908+
rm .git/objects/"$(test_oid_to_path "$oid")" &&
909+
910+
# Verify that it is possible to read the commit from the
911+
# commit graph when not being paranoid, ...
912+
GIT_COMMIT_GRAPH_PARANOIA=false git rev-list B &&
913+
# ... but parsing the commit when double checking that
914+
# it actually exists in the object database should fail.
915+
test_must_fail git rev-list -1 B
916+
)
917+
'
918+
919+
test_expect_success 'stale commit cannot be parsed when traversing graph' '
920+
test_when_finished "rm -rf repo" &&
921+
git init repo &&
922+
(
923+
cd repo &&
924+
925+
test_commit A &&
926+
test_commit B &&
927+
test_commit C &&
928+
git commit-graph write --reachable &&
929+
930+
# Corrupt the repository by deleting the intermediate commit
931+
# object. Commands should notice that this object is absent and
932+
# thus that the repository is corrupt even if the commit graph
933+
# exists.
934+
oid=$(git rev-parse B) &&
935+
rm .git/objects/"$(test_oid_to_path "$oid")" &&
936+
937+
# Again, we should be able to parse the commit when not
938+
# being paranoid about commit graph staleness...
939+
GIT_COMMIT_GRAPH_PARANOIA=false git rev-parse HEAD~2 &&
940+
# ... but fail when we are paranoid.
941+
test_must_fail git rev-parse HEAD~2 2>error &&
942+
grep "error: commit $oid exists in commit-graph but not in the object database" error
943+
)
944+
'
945+
898946
test_done

0 commit comments

Comments
 (0)