mirror of
https://github.com/git/git
synced 2024-08-27 03:29:21 +00:00
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
This commit is contained in:
commit
922cc26e41
|
@ -911,6 +911,16 @@ for full details.
|
||||||
should not normally need to set this to `0`, but it may be
|
should not normally need to set this to `0`, but it may be
|
||||||
useful when trying to salvage data from a corrupted repository.
|
useful when trying to salvage data from a corrupted repository.
|
||||||
|
|
||||||
|
`GIT_COMMIT_GRAPH_PARANOIA`::
|
||||||
|
When loading a commit object from the commit-graph, Git performs an
|
||||||
|
existence check on the object in the object database. This is done to
|
||||||
|
avoid issues with stale commit-graphs that contain references to
|
||||||
|
already-deleted commits, but comes with a performance penalty.
|
||||||
|
+
|
||||||
|
The default is "true", which enables the aforementioned behavior.
|
||||||
|
Setting this to "false" disables the existence check. This can lead to
|
||||||
|
a performance improvement at the cost of consistency.
|
||||||
|
|
||||||
`GIT_ALLOW_PROTOCOL`::
|
`GIT_ALLOW_PROTOCOL`::
|
||||||
If set to a colon-separated list of protocols, behave as if
|
If set to a colon-separated list of protocols, behave as if
|
||||||
`protocol.allow` is set to `never`, and each of the listed
|
`protocol.allow` is set to `never`, and each of the listed
|
||||||
|
|
|
@ -1024,14 +1024,18 @@ int repo_find_commit_pos_in_graph(struct repository *r, struct commit *c,
|
||||||
|
|
||||||
struct commit *lookup_commit_in_graph(struct repository *repo, const struct object_id *id)
|
struct commit *lookup_commit_in_graph(struct repository *repo, const struct object_id *id)
|
||||||
{
|
{
|
||||||
|
static int commit_graph_paranoia = -1;
|
||||||
struct commit *commit;
|
struct commit *commit;
|
||||||
uint32_t pos;
|
uint32_t pos;
|
||||||
|
|
||||||
|
if (commit_graph_paranoia == -1)
|
||||||
|
commit_graph_paranoia = git_env_bool(GIT_COMMIT_GRAPH_PARANOIA, 1);
|
||||||
|
|
||||||
if (!prepare_commit_graph(repo))
|
if (!prepare_commit_graph(repo))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (!search_commit_pos_in_graph(id, repo->objects->commit_graph, &pos))
|
if (!search_commit_pos_in_graph(id, repo->objects->commit_graph, &pos))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (!has_object(repo, id, 0))
|
if (commit_graph_paranoia && !has_object(repo, id, 0))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
commit = lookup_commit(repo, id);
|
commit = lookup_commit(repo, id);
|
||||||
|
|
|
@ -8,6 +8,12 @@
|
||||||
#define GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE "GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE"
|
#define GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE "GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE"
|
||||||
#define GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS "GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS"
|
#define GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS "GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This environment variable controls whether commits looked up via the
|
||||||
|
* commit graph will be double checked to exist in the object database.
|
||||||
|
*/
|
||||||
|
#define GIT_COMMIT_GRAPH_PARANOIA "GIT_COMMIT_GRAPH_PARANOIA"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This method is only used to enhance coverage of the commit-graph
|
* This method is only used to enhance coverage of the commit-graph
|
||||||
* feature in the test suite with the GIT_TEST_COMMIT_GRAPH and
|
* feature in the test suite with the GIT_TEST_COMMIT_GRAPH and
|
||||||
|
|
16
commit.c
16
commit.c
|
@ -28,6 +28,7 @@
|
||||||
#include "shallow.h"
|
#include "shallow.h"
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
#include "hook.h"
|
#include "hook.h"
|
||||||
|
#include "parse.h"
|
||||||
|
|
||||||
static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
|
static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
|
||||||
|
|
||||||
|
@ -572,8 +573,21 @@ int repo_parse_commit_internal(struct repository *r,
|
||||||
return -1;
|
return -1;
|
||||||
if (item->object.parsed)
|
if (item->object.parsed)
|
||||||
return 0;
|
return 0;
|
||||||
if (use_commit_graph && parse_commit_in_graph(r, item))
|
if (use_commit_graph && parse_commit_in_graph(r, item)) {
|
||||||
|
static int commit_graph_paranoia = -1;
|
||||||
|
|
||||||
|
if (commit_graph_paranoia == -1)
|
||||||
|
commit_graph_paranoia = git_env_bool(GIT_COMMIT_GRAPH_PARANOIA, 1);
|
||||||
|
|
||||||
|
if (commit_graph_paranoia && !has_object(r, &item->object.oid, 0)) {
|
||||||
|
unparse_commit(r, &item->object.oid);
|
||||||
|
return quiet_on_missing ? -1 :
|
||||||
|
error(_("commit %s exists in commit-graph but not in the object database"),
|
||||||
|
oid_to_hex(&item->object.oid));
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (oid_object_info_extended(r, &item->object.oid, &oi, flags) < 0)
|
if (oid_object_info_extended(r, &item->object.oid, &oi, flags) < 0)
|
||||||
return quiet_on_missing ? -1 :
|
return quiet_on_missing ? -1 :
|
||||||
|
|
|
@ -895,4 +895,52 @@ test_expect_success 'reader notices too-small generations chunk' '
|
||||||
test_cmp expect.err err
|
test_cmp expect.err err
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'stale commit cannot be parsed when given directly' '
|
||||||
|
test_when_finished "rm -rf repo" &&
|
||||||
|
git init repo &&
|
||||||
|
(
|
||||||
|
cd repo &&
|
||||||
|
test_commit A &&
|
||||||
|
test_commit B &&
|
||||||
|
git commit-graph write --reachable &&
|
||||||
|
|
||||||
|
oid=$(git rev-parse B) &&
|
||||||
|
rm .git/objects/"$(test_oid_to_path "$oid")" &&
|
||||||
|
|
||||||
|
# Verify that it is possible to read the commit from the
|
||||||
|
# commit graph when not being paranoid, ...
|
||||||
|
GIT_COMMIT_GRAPH_PARANOIA=false git rev-list B &&
|
||||||
|
# ... but parsing the commit when double checking that
|
||||||
|
# it actually exists in the object database should fail.
|
||||||
|
test_must_fail git rev-list -1 B
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'stale commit cannot be parsed when traversing graph' '
|
||||||
|
test_when_finished "rm -rf repo" &&
|
||||||
|
git init repo &&
|
||||||
|
(
|
||||||
|
cd repo &&
|
||||||
|
|
||||||
|
test_commit A &&
|
||||||
|
test_commit B &&
|
||||||
|
test_commit C &&
|
||||||
|
git commit-graph write --reachable &&
|
||||||
|
|
||||||
|
# Corrupt the repository by deleting the intermediate commit
|
||||||
|
# object. Commands should notice that this object is absent and
|
||||||
|
# thus that the repository is corrupt even if the commit graph
|
||||||
|
# exists.
|
||||||
|
oid=$(git rev-parse B) &&
|
||||||
|
rm .git/objects/"$(test_oid_to_path "$oid")" &&
|
||||||
|
|
||||||
|
# Again, we should be able to parse the commit when not
|
||||||
|
# being paranoid about commit graph staleness...
|
||||||
|
GIT_COMMIT_GRAPH_PARANOIA=false git rev-parse HEAD~2 &&
|
||||||
|
# ... but fail when we are paranoid.
|
||||||
|
test_must_fail git rev-parse HEAD~2 2>error &&
|
||||||
|
grep "error: commit $oid exists in commit-graph but not in the object database" error
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
Loading…
Reference in a new issue