Clarify commit-graph and grafts/replace/shallow incompatibilities
cover Clarify commit-graph and grafts/replace/shallow incompatibilities
One unresolved issue with the commit-graph feature is that it can cause issues when combined with replace objects, commit grafts, or shallow clones. These are not 100% incompatible, as one could be reasonably successful writing a commit-graph after replacing some objects and not have issues. The problems happen when commits that are already in the commit-graph file are replaced, or when git is run with the --no-replace-objects option; this can cause incorrect parents or incorrect generation numbers. Similar things occur with commit grafts and shallow clones, especially when running git fetch --unshallow in a shallow repo.
Instead of trying (and probably failing) to make these features work together, default to making the commit-graph feature unavailable in these situations. Create a new method 'commit_graph_compatible(r)' that checks if the repository 'r' has any of these features enabled.
I will send a follow-up patch that shows how I tested these interactions by computing the commit-graph on every 'git commit'.
This approach works for most cases, but I found one nagging test case that was causing problems. This led to the commit "commit-graph: close_commit_graph before shallow walk" and is the patch I am least confident about. Please take a close look at that one and suggest alternatives.
This approach is very different from the previous RFC on the subject [1].
While building this series, I got some test failures in the non-the_repository tests. These issues are related to missing references to an arbitrary repository (instead of the_repository) and some uninitialized values in the tests. Stefan already sent a patch to address this [2], and I've included those commits (along with a small tweak [3]). These are only included for convenience.
Thanks,
-Stolee
[1] https://public-inbox.org/git/[email protected]/
[RFC PATCH 0/6] Fix commit-graph/graft/replace/shallow combo
[2] https://public-inbox.org/git/[email protected]/T/#t
[PATCH 0/2] RFC ref store to repository migration
[3] https://public-inbox.org/git/[email protected]/T/#m966eac85fd58c66523654ddaf0bec72877d3295a
[PATCH] TO-SQUASH: replace the_repository with arbitrary r
Based-On: jt/commit-graph-per-object-store
Cc: [email protected]
Cc: [email protected]
Derrick Stolee (6):
commit-graph: update design document
test-repository: properly init repo
commit-graph: not compatible with replace objects
commit-graph: not compatible with grafts
commit-graph: not compatible with uninitialized repo
commit-graph: close_commit_graph before shallow walk
Stefan Beller (2):
refs.c: migrate internal ref iteration to pass thru repository
argument
refs.c: upgrade for_each_replace_ref to be a each_repo_ref_fn callback
Documentation/technical/commit-graph.txt | 18 ++++++-- builtin/replace.c | 8 ++-- commit-graph.c | 34 ++++++++++++-- commit-graph.h | 1 + commit.c | 2 +- commit.h | 1 + refs.c | 48 +++++++++++++++++--- refs.h | 12 ++++- refs/iterator.c | 6 +-- refs/refs-internal.h | 5 +- replace-object.c | 7 +-- replace-object.h | 2 + t/helper/test-repository.c | 10 +++- t/t5318-commit-graph.sh | 58 ++++++++++++++++++++++++ upload-pack.c | 2 + 15 files changed, 184 insertions(+), 30 deletions(-)
base-commit: dade47c06cf849b0ca180a8e6383b55ea6f75812
Published-As: https://github.com/gitgitgadget/git/releases/tags/pr-11%2Fderrickstolee%2Fshallow%2Fupstream-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-11/derrickstolee/shallow/upstream-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/11
Signature · 1 line
gitgitgadget
PATCH 1/8 refs.c: migrate internal ref iteration to pass thru repository argument
From: Stefan Beller <[email protected]>
In 60ce76d3581 (refs: add repository argument to for_each_replace_ref, 2018-04-11) and 0d296c57aec (refs: allow for_each_replace_ref to handle arbitrary repositories, 2018-04-11), for_each_replace_ref learned how to iterate over refs by a given arbitrary repository.
New attempts in the object store conversion have shown that it is useful to have the repository handle available that the refs iteration is currently iterating over.
To achieve this goal we will need to add a repository argument to each_ref_fn in refs.h. However as many callers rely on the signature such a patch would be too large.
So convert the internals of the ref subsystem first to pass through a repository argument without exposing the change to the user. Assume the_repository for the passed through repository, although it is not used anywhere yet.
--- refs.c | 39 +++++++++++++++++++++++++++++++++++++-- refs.h | 10 ++++++++++ refs/iterator.c | 6 +++--- refs/refs-internal.h | 5 +++-- 4 files changed, 53 insertions(+), 7 deletions(-)
refs.c +37 -2
@@ -1390,17 +1390,50 @@ struct ref_iterator *refs_ref_iterator_begin(* non-zero value, stop the iteration and return that value; * otherwise, return 0. */ +static int do_for_each_repo_ref (struct repository *r,const char *prefix, +each_repo_ref_fn fn,int trim,int flags, +void *cb_data) +{ +struct ref_iterator *iter; +struct ref_store *refs =get_main_ref_store (r); + +if (!refs) +return 0 ; + + iter =refs_ref_iterator_begin (refs, prefix, trim, flags); + +return do_for_each_repo_ref_iterator (r, iter, fn, cb_data); +} + +struct do_for_each_ref_help { +each_ref_fn *fn; +void *cb_data; +}; + +static int do_for_each_ref_helper (struct repository *r, +const char *refname, +const struct object_id *oid, +int flags, +void *cb_data) +{ +struct do_for_each_ref_help *hp = cb_data; + +return hp->fn (refname, oid, flags, hp->cb_data); +} +static int do_for_each_ref (struct ref_store *refs,const char *prefix,each_ref_fn fn,int trim,int flags,void *cb_data) {struct ref_iterator *iter; +struct do_for_each_ref_help hp = { fn, cb_data };if (!refs)return 0 ; iter =refs_ref_iterator_begin (refs, prefix, trim, flags); -return do_for_each_ref_iterator (iter, fn, cb_data); +return do_for_each_repo_ref_iterator (the_repository, iter, + do_for_each_ref_helper, &hp); }int refs_for_each_ref (struct ref_store *refs,each_ref_fn fn,void *cb_data)
@@ -2029,10 +2062,12 @@ cleanup:int refs_for_each_reflog (struct ref_store *refs,each_ref_fn fn,void *cb_data) {struct ref_iterator *iter; +struct do_for_each_ref_help hp = { fn, cb_data }; iter = refs->be->reflog_iterator_begin (refs); -return do_for_each_ref_iterator (iter, fn, cb_data); +return do_for_each_repo_ref_iterator (the_repository, iter, + do_for_each_ref_helper, &hp); }int for_each_reflog (each_ref_fn fn,void *cb_data)
refs.h +10 -0
@@ -274,6 +274,16 @@ struct ref_transaction;typedef int each_ref_fn (const char *refname,const struct object_id *oid,int flags,void *cb_data); +/* +* The same as each_ref_fn, but also with a repository argument that +* contains the repository associated with the callback. +*/ +typedef int each_repo_ref_fn (struct repository *r, +const char *refname, +const struct object_id *oid, +int flags, +void *cb_data); +/* * The following functions invoke the specified callback function for * each reference indicated. If the function ever returns a nonzero
refs/iterator.c +3 -3
@@ -407,15 +407,15 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,struct ref_iterator *current_ref_iter =NULL ; -int do_for_each_ref_iterator (struct ref_iterator *iter, -each_ref_fn fn,void *cb_data) +int do_for_each_repo_ref_iterator (struct repository *r,struct ref_iterator *iter, +each_repo_ref_fn fn,void *cb_data) {int retval =0 , ok;struct ref_iterator *old_ref_iter = current_ref_iter; current_ref_iter = iter;while ((ok =ref_iterator_advance (iter)) ==ITER_OK ) { - retval =fn (iter->refname, iter->oid, iter->flags, cb_data); + retval =fn (r, iter->refname, iter->oid, iter->flags, cb_data);if (retval) {/* * If ref_iterator_abort() returns ITER_ERROR,
refs/refs-internal.h +3 -2
@@ -473,8 +473,9 @@ extern struct ref_iterator *current_ref_iter;* adapter between the callback style of reference iteration and the * iterator style. */ -int do_for_each_ref_iterator (struct ref_iterator *iter, -each_ref_fn fn,void *cb_data); +int do_for_each_repo_ref_iterator (struct repository *r, +struct ref_iterator *iter, +each_repo_ref_fn fn,void *cb_data);/* * Only include per-worktree refs in a do_for_each_ref*() iteration.
Signature · 1 line
gitgitgadget
PATCH 2/8 refs.c: upgrade for_each_replace_ref to be a each_repo_ref_fn callback
In reply to Derrick Stolee via GitGitGadgetFrom: Stefan Beller <[email protected]>
--- builtin/replace.c | 8 ++++---- refs.c | 9 ++++----- refs.h | 2 +- replace-object.c | 5 +++-- 4 files changed, 12 insertions(+), 12 deletions(-)
builtin/replace.c +4 -4
@@ -39,7 +39,8 @@ struct show_data {enum replace_format format; }; -static int show_reference (const char *refname,const struct object_id *oid, +static int show_reference (struct repository *r,const char *refname, +const struct object_id *oid,int flag,void *cb_data) {struct show_data *data = cb_data;
@@ -56,9 +57,8 @@ static int show_reference(const char *refname, const struct object_id *oid,if (get_oid (refname, &object))return error ("Failed to resolve '%s' as a valid ref." , refname); - obj_type =oid_object_info (the_repository, &object, -NULL ); - repl_type =oid_object_info (the_repository, oid,NULL ); + obj_type =oid_object_info (r, &object,NULL ); + repl_type =oid_object_info (r, oid,NULL );printf ("%s (%s) -> %s (%s)\n" , refname,type_name (obj_type),oid_to_hex (oid),type_name (repl_type));
refs.c +4 -5
@@ -1478,12 +1478,11 @@ int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,return do_for_each_ref (refs, prefix, fn,0 , flag, cb_data); } -int for_each_replace_ref (struct repository *r,each_ref_fn fn,void *cb_data) +int for_each_replace_ref (struct repository *r,each_repo_ref_fn fn,void *cb_data) { -return do_for_each_ref (get_main_ref_store (r), - git_replace_ref_base, fn, -strlen (git_replace_ref_base), -DO_FOR_EACH_INCLUDE_BROKEN , cb_data); +return do_for_each_repo_ref (r, git_replace_ref_base, fn, +strlen (git_replace_ref_base), +DO_FOR_EACH_INCLUDE_BROKEN , cb_data); }int for_each_namespaced_ref (each_ref_fn fn,void *cb_data)
refs.h +1 -1
@@ -317,7 +317,7 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,int for_each_tag_ref (each_ref_fn fn,void *cb_data);int for_each_branch_ref (each_ref_fn fn,void *cb_data);int for_each_remote_ref (each_ref_fn fn,void *cb_data); -int for_each_replace_ref (struct repository *r,each_ref_fn fn,void *cb_data); +int for_each_replace_ref (struct repository *r,each_repo_ref_fn fn,void *cb_data);int for_each_glob_ref (each_ref_fn fn,const char *pattern,void *cb_data);int for_each_glob_ref_in (each_ref_fn fn,const char *pattern,const char *prefix,void *cb_data);
replace-object.c +3 -2
@@ -6,7 +6,8 @@#include "repository.h" #include "commit.h" -static int register_replace_ref (const char *refname, +static int register_replace_ref (struct repository *r, +const char *refname,const struct object_id *oid,int flag,void *cb_data) {
@@ -25,7 +26,7 @@ static int register_replace_ref(const char *refname,oidcpy (&repl_obj->replacement, oid);/* Register new object */ -if (oidmap_put (the_repository->objects->replace_map, repl_obj)) +if (oidmap_put (r->objects->replace_map, repl_obj))die ("duplicate replace ref: %s" , refname);return 0 ;
Signature · 1 line
gitgitgadget
PATCH 3/8 commit-graph: update design document
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
As it exists right now, the commit-graph feature may provide inconsistent results when combined with commit grafts, replace objects, and shallow clones. Update the design document to discuss why these interactions are difficult to reconcile and how we will avoid errors by preventing updates to and reads from the commit-graph file when these other features exist.
--- Documentation/technical/commit-graph.txt | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-)
Documentation/technical/commit-graph.txt +15 -3
@@ -112,12 +112,24 @@ Design Details - The file format includes parameters for the object ID hash function, so a future change of hash algorithm does not require a change in format. +- Commit grafts and replace objects can change the shape of the commit + history. These can also be enabled/disabled on the fly using + `--no-replace-objects`. This leads to difficultly storing both possible + interpretations of a commit id, especially when computing generation + numbers. The commit-graph will not be read or written when + replace-objects or grafts are present. + +- Shallow clones create grafts of commits by dropping their parents. This + leads the commit-graph to think those commits have generation number 1. + If and when those commits are made unshallow, those generation numbers + become invalid. Since shallow clones are intended to restrict the commit + history to a very small set of commits, the commit-graph feature is less + helpful for these clones, anyway. The commit-graph will not be read or + written when shallow commits are present. + Future Work ----------- -- The commit graph feature currently does not honor commit grafts. This can - be remedied by duplicating or refactoring the current graft logic. - - After computing and storing generation numbers, we must make graph walks aware of generation numbers to gain the performance benefits they enable. This will mostly be accomplished by swapping a commit-date-ordered
Signature · 1 line
gitgitgadget
PATCH 4/8 test-repository: properly init repo
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
--- t/helper/test-repository.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
t/helper/test-repository.c +8 -2
@@ -15,7 +15,10 @@ static void test_parse_commit_in_graph(const char *gitdir, const char *worktree,struct commit *c;struct commit_list *parent; -repo_init (&r, gitdir, worktree); +setup_git_env (gitdir); + +if (repo_init (&r, gitdir, worktree)) +die ("Couldn't init repo" ); c =lookup_commit (&r, commit_oid);
@@ -38,7 +41,10 @@ static void test_get_commit_tree_in_graph(const char *gitdir,struct commit *c;struct tree *tree; -repo_init (&r, gitdir, worktree); +setup_git_env (gitdir); + +if (repo_init (&r, gitdir, worktree)) +die ("Couldn't init repo" ); c =lookup_commit (&r, commit_oid);
Signature · 1 line
gitgitgadget
PATCH 5/8 commit-graph: not compatible with replace objects
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
Create new method commit_graph_compatible(r) to check if a given repository r is compatible with the commit-graph feature. Fill the method with a check to see if replace-objects exist. Test this interaction succeeds, including ignoring an existing commit-graph and failing to write a new commit-graph.
--- commit-graph.c | 17 +++++++++++++++++ replace-object.c | 2 +- replace-object.h | 2 ++ t/t5318-commit-graph.sh | 22 ++++++++++++++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-)
commit-graph.c +17 -0
@@ -13,6 +13,8 @@#include "commit-graph.h" #include "object-store.h" #include "alloc.h" +#include "hashmap.h" +#include "replace-object.h" #define GRAPH_SIGNATURE 0x43475048/* "CGPH" */ #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446/* "OIDF" */
@@ -56,6 +58,15 @@ static struct commit_graph *alloc_commit_graph(void)return g; } +static int commit_graph_compatible (struct repository *r) +{ +prepare_replace_object (r); +if (hashmap_get_size (&r->objects->replace_map->map)) +return 0 ; + +return 1 ; +} +struct commit_graph *load_commit_graph_one (const char *graph_file) {void *graph_map;
@@ -223,6 +234,9 @@ static int prepare_commit_graph(struct repository *r)*/ return 0 ; +if (!commit_graph_compatible (r)) +return 0 ; + obj_dir = r->objects->objectdir;prepare_commit_graph_one (r, obj_dir);prepare_alt_odb (r);
@@ -693,6 +707,9 @@ void write_commit_graph(const char *obj_dir,int num_extra_edges;struct commit_list *parent; +if (!commit_graph_compatible (the_repository)) +return ; + oids.nr =0 ; oids.alloc =approximate_object_count () /4 ;
replace-object.c +1 -1
@@ -32,7 +32,7 @@ static int register_replace_ref(struct repository *r,return 0 ; } -static void prepare_replace_object (struct repository *r) +void prepare_replace_object (struct repository *r) {if (r->objects->replace_map)return ;
replace-object.h +2 -0
@@ -10,6 +10,8 @@ struct replace_object {struct object_id replacement; }; +void prepare_replace_object (struct repository *r); +/* * This internal function is only declared here for the benefit of * lookup_replace_object(). Please do not call it directly.
t/t5318-commit-graph.sh +22 -0
@@ -259,6 +259,28 @@ test_expect_success 'check that gc computes commit-graph' ' test_cmp commit-graph-after-gc $objdir/info/commit-graph ' +test_expect_success 'replace-objects invalidates commit-graph' ' + cd "$TRASH_DIRECTORY" && + test_when_finished rm -rf replace && + git clone full replace && + ( + cd replace && + git commit-graph write --reachable && + test_path_is_file .git/objects/info/commit-graph && + git replace HEAD~1 HEAD~2 && + git -c core.commitGraph=false log >expect && + git -c core.commitGraph=true log >actual && + test_cmp expect actual && + git commit-graph write --reachable && + git -c core.commitGraph=false --no-replace-objects log >expect && + git -c core.commitGraph=true --no-replace-objects log >actual && + test_cmp expect actual && + rm -rf .git/objects/info/commit-graph && + git commit-graph write --reachable && + test_path_is_missing .git/objects/info/commit-graph + ) +' + # the verify tests below expect the commit-graph to contain # exactly the commits reachable from the commits/8 branch. # If the file changes the set of commits in the list, then the
Signature · 1 line
gitgitgadget
PATCH 6/8 commit-graph: not compatible with grafts
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
Augment commit_graph_compatible(r) to return false when the given repository r has commit grafts or is a shallow clone. Test that in these situations we ignore existing commit-graph files and we do not write new commit-graph files.
--- commit-graph.c | 6 ++++++ commit.c | 2 +- commit.h | 1 + t/t5318-commit-graph.sh | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-)
commit-graph.c +6 -0
@@ -60,6 +60,12 @@ static struct commit_graph *alloc_commit_graph(void)static int commit_graph_compatible (struct repository *r) { +prepare_commit_graft (r); +if (r->parsed_objects && r->parsed_objects->grafts_nr) +return 0 ; +if (is_repository_shallow (r)) +return 0 ; +prepare_replace_object (r);if (hashmap_get_size (&r->objects->replace_map->map))return 0 ;
commit.c +1 -1
@@ -209,7 +209,7 @@ static int read_graft_file(struct repository *r, const char *graft_file)return 0 ; } -static void prepare_commit_graft (struct repository *r) +void prepare_commit_graft (struct repository *r) {char *graft_file;
commit.h +1 -0
@@ -202,6 +202,7 @@ typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);struct commit_graft *read_graft_line (struct strbuf *line);int register_commit_graft (struct repository *r,struct commit_graft *,int ); +void prepare_commit_graft (struct repository *r);struct commit_graft *lookup_commit_graft (struct repository *r,const struct object_id *oid);extern struct commit_list *get_merge_bases (struct commit *rev1,struct commit *rev2);
t/t5318-commit-graph.sh +36 -0
@@ -281,6 +281,42 @@ test_expect_success 'replace-objects invalidates commit-graph' ' ) ' +test_expect_success 'commit grafts invalidate commit-graph' ' + cd "$TRASH_DIRECTORY" && + test_when_finished rm -rf graft && + git clone full graft && + ( + cd graft && + git commit-graph write --reachable && + test_path_is_file .git/objects/info/commit-graph && + git replace --graft HEAD~1 HEAD~3 && + git -c core.commitGraph=false log >expect && + git -c core.commitGraph=true log >actual && + test_cmp expect actual && + git commit-graph write --reachable && + git -c core.commitGraph=false --no-replace-objects log >expect && + git -c core.commitGraph=true --no-replace-objects log >actual && + test_cmp expect actual && + rm -rf .git/objects/info/commit-graph && + git commit-graph write --reachable && + test_path_is_missing .git/objects/info/commit-graph + ) +' + +test_expect_success 'replace-objects invalidates commit-graph' ' + cd "$TRASH_DIRECTORY" && + test_when_finished rm -rf shallow && + git clone --depth 2 "file://$TRASH_DIRECTORY/full" shallow && + ( + cd shallow && + git commit-graph write --reachable && + test_path_is_missing .git/objects/info/commit-graph && + git fetch origin --unshallow && + git commit-graph write --reachable && + test_path_is_file .git/objects/info/commit-graph + ) +' + # the verify tests below expect the commit-graph to contain # exactly the commits reachable from the commits/8 branch. # If the file changes the set of commits in the list, then the
Signature · 1 line
gitgitgadget
PATCH 7/8 commit-graph: not compatible with uninitialized repo
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
--- commit-graph.c | 3 +++ 1 file changed, 3 insertions(+)
commit-graph.c +3 -0
@@ -60,6 +60,9 @@ static struct commit_graph *alloc_commit_graph(void)static int commit_graph_compatible (struct repository *r) { +if (!r->gitdir) +return 0 ; +prepare_commit_graft (r);if (r->parsed_objects && r->parsed_objects->grafts_nr)return 0 ;
Signature · 1 line
gitgitgadget
PATCH 8/8 commit-graph: close_commit_graph before shallow walk
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
Make close_commit_graph() work for arbitrary repositories.
Call close_commit_graph() when about to start a rev-list walk that includes shallow commits. This is necessary in code paths that "fake" shallow commits for the sake of fetch. Specifically, test 351 in t5500-fetch-pack.sh runs
git fetch --shallow-exclude one origin
with a file-based transfer. When the "remote" has a commit-graph, we do not prevent the commit-graph from being loaded, but then the commits are intended to be dynamically transferred into shallow commits during get_shallow_commits_by_rev_list(). By closing the commit-graph before this call, we prevent this interaction.
--- commit-graph.c | 8 ++++---- commit-graph.h | 1 + upload-pack.c | 2 ++ 3 files changed, 7 insertions(+), 4 deletions(-)
commit-graph.c +4 -4
@@ -256,10 +256,10 @@ static int prepare_commit_graph(struct repository *r)return !!r->objects->commit_graph; } -static void close_commit_graph (void ) +void close_commit_graph (struct repository *r) { -free_commit_graph (the_repository->objects->commit_graph); - the_repository->objects->commit_graph =NULL ; +free_commit_graph (r->objects->commit_graph); + r->objects->commit_graph =NULL ; }static int bsearch_graph (struct commit_graph *g,struct object_id *oid,uint32_t *pos)
@@ -871,7 +871,7 @@ void write_commit_graph(const char *obj_dir,write_graph_chunk_data (f,GRAPH_OID_LEN , commits.list, commits.nr);write_graph_chunk_large_edges (f, commits.list, commits.nr); -close_commit_graph (); +close_commit_graph (the_repository);finalize_hashfile (f,NULL ,CSUM_HASH_IN_STREAM |CSUM_FSYNC );commit_lock_file (&lk);
commit-graph.h +1 -0
@@ -59,6 +59,7 @@ void write_commit_graph(const char *obj_dir,int verify_commit_graph (struct repository *r,struct commit_graph *g); +void close_commit_graph (struct repository *);void free_commit_graph (struct commit_graph *);#endif
upload-pack.c +2 -0
@@ -24,6 +24,7 @@#include "quote.h" #include "upload-pack.h" #include "serve.h" +#include "commit-graph.h" /* Remember to update object flag allocation in object.h */ #define THEY_HAVE (1u << 11)
@@ -739,6 +740,7 @@ static void deepen_by_rev_list(int ac, const char **av, {struct commit_list *result; +close_commit_graph (the_repository); result =get_shallow_commits_by_rev_list (ac, av,SHALLOW ,NOT_SHALLOW );send_shallow (result);free_commit_list (result);
Signature · 1 line
gitgitgadget
PATCH 1/1 DO-NOT-MERGE: write and read commit-graph always
In reply to Derrick Stolee via GitGitGadgetThis commit is not intended to be merged, but is only to create a test environment to see what works with the commit-graph feature and what does not. The tests that fail are primarily related to corrupting the object store to remove a commit from visibility and testing that rev-list fails -- except it doesn't when there is a commit-graph that prevents parsing from the object database. The following tests will fail with this commit, but are not "real" bugs:
t0410-partial-clone.sh, Test 9t5307-pack-missing-commit.sh, Tests 3-4t6011-rev-list-with-bad-commit.sh, Test 4
The following test fails because the repo has ambiguous merge-bases, and the commit-graph changes the walk order so we select a different one. This alters the resulting merge from the expected result.
t6024-recursive-merge.sh, Test 4
The tests above are made to pass by deleting the commit-graph file before the necessary steps.
--- builtin/commit.c | 2 ++ builtin/gc.c | 3 +-- commit-graph.c | 11 ----------- t/t0410-partial-clone.sh | 1 + t/t5307-pack-missing-commit.sh | 2 ++ t/t6011-rev-list-with-bad-commit.sh | 1 + t/t6024-recursive-merge.sh | 1 + 7 files changed, 8 insertions(+), 13 deletions(-)
builtin/commit.c +2 -0
@@ -33,6 +33,7 @@#include "sequencer.h" #include "mailmap.h" #include "help.h" +#include "commit-graph.h" static const char *const builtin_commit_usage[] = {N_ ("git commit [<options>] [--] <pathspec>..." ),
@@ -1652,6 +1653,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) "not exceeded, and then \"git reset HEAD\" to recover."));rerere (0 ); +write_commit_graph_reachable (get_object_directory (),1 );run_command_v_opt (argv_gc_auto,RUN_GIT_CMD );run_commit_hook (use_editor,get_index_file (),"post-commit" ,NULL );if (amend && !no_post_rewrite) {
builtin/gc.c +1 -2
@@ -41,7 +41,7 @@ static int aggressive_depth = 50;static int aggressive_window =250 ;static int gc_auto_threshold =6700 ;static int gc_auto_pack_limit =50 ; -static int gc_write_commit_graph; +static int gc_write_commit_graph =1 ;static int detach_auto =1 ;static timestamp_t gc_log_expire_time;static const char *gc_log_expire ="1.day.ago" ;
@@ -131,7 +131,6 @@ static void gc_config(void)git_config_get_int ("gc.aggressivedepth" , &aggressive_depth);git_config_get_int ("gc.auto" , &gc_auto_threshold);git_config_get_int ("gc.autopacklimit" , &gc_auto_pack_limit); -git_config_get_bool ("gc.writecommitgraph" , &gc_write_commit_graph);git_config_get_bool ("gc.autodetach" , &detach_auto);git_config_get_expiry ("gc.pruneexpire" , &prune_expire);git_config_get_expiry ("gc.worktreepruneexpire" , &prune_worktrees_expire);
commit-graph.c +0 -11
@@ -227,22 +227,11 @@ static int prepare_commit_graph(struct repository *r) {struct alternate_object_database *alt;char *obj_dir; -int config_value;if (r->objects->commit_graph_attempted)return !!r->objects->commit_graph; r->objects->commit_graph_attempted =1 ; -if (repo_config_get_bool (r,"core.commitgraph" , &config_value) || - !config_value) -/* -* This repository is not configured to use commit graphs, so -* do not load one. (But report commit_graph_attempted anyway -* so that commit graph loading is not attempted again for this -* repository.) -*/ -return 0 ; -if (!commit_graph_compatible (r))return 0 ;
t/t0410-partial-clone.sh +1 -0
@@ -181,6 +181,7 @@ test_expect_success 'rev-list stops traversal at missing and promised commit' ' git -C repo config core.repositoryformatversion 1 && git -C repo config extensions.partialclone "arbitrary string" && + rm -rf repo/.git/objects/info/commit-graph && git -C repo rev-list --exclude-promisor-objects --objects bar >out && grep $(git -C repo rev-parse bar) out && ! grep $FOO out
t/t5307-pack-missing-commit.sh +2 -0
@@ -24,10 +24,12 @@ test_expect_success 'check corruption' ' ' test_expect_success 'rev-list notices corruption (1)' ' + rm -rf .git/objects/info/commit-graph && test_must_fail git rev-list HEAD ' test_expect_success 'rev-list notices corruption (2)' ' + rm -rf .git/objects/info/commit-graph && test_must_fail git rev-list --objects HEAD '
t/t6011-rev-list-with-bad-commit.sh +1 -0
@@ -43,6 +43,7 @@ test_expect_success 'corrupt second commit object' \ test_expect_success 'rev-list should fail' \ ' + rm -rf .git/objects/info/commit-graph && test_must_fail git rev-list --all > /dev/null '
t/t6024-recursive-merge.sh +1 -0
@@ -61,6 +61,7 @@ GIT_AUTHOR_DATE="2006-12-12 23:00:08" git commit -m F ' test_expect_success "combined merge conflicts" " + rm -rf .git/objects/info/commit-graph && test_must_fail git merge -m final G "
Signature · 1 line
2.18.0.118.gd4f65b8d14
Re: [PATCH 2/8] refs.c: upgrade for_each_replace_ref to be a each_repo_ref_fn callback
In reply to Stefan Beller via GitGitGadgetQuoted 63 lines · Stefan Beller via GitGitGadget
From: Stefan Beller <[email protected]>
Signed-off-by Stefan Beller <[email protected]>Signed-off-by Derrick Stolee <[email protected]>--- builtin/replace.c | 8 ++++---- refs.c | 9 ++++----- refs.h | 2 +- replace-object.c | 5 +++-- 4 files changed, 12 insertions(+), 12 deletions(-)builtin/replace.c +4
-4@@ -39,7 +39,8 @@ struct show_data {enum replace_format format; }; -static int show_reference (const char *refname,const struct object_id *oid, +static int show_reference (struct repository *r,const char *refname, +const struct object_id *oid,int flag,void *cb_data) {struct show_data *data = cb_data;@@ -56,9 +57,8 @@ static int show_reference(const char *refname, const struct object_id *oid,if (get_oid (refname, &object))return error ("Failed to resolve '%s' as a valid ref." , refname); - obj_type =oid_object_info (the_repository, &object, -NULL ); - repl_type =oid_object_info (the_repository, oid,NULL ); + obj_type =oid_object_info (r, &object,NULL ); + repl_type =oid_object_info (r, oid,NULL );printf ("%s (%s) -> %s (%s)\n" , refname,type_name (obj_type),oid_to_hex (oid),type_name (repl_type));refs.c +4
-5@@ -1478,12 +1478,11 @@ int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,return do_for_each_ref (refs, prefix, fn,0 , flag, cb_data); } -int for_each_replace_ref (struct repository *r,each_ref_fn fn,void *cb_data) +int for_each_replace_ref (struct repository *r,each_repo_ref_fn fn,void *cb_data) { -return do_for_each_ref (get_main_ref_store (r), - git_replace_ref_base, fn, -strlen (git_replace_ref_base), -DO_FOR_EACH_INCLUDE_BROKEN , cb_data); +return do_for_each_repo_ref (r, git_replace_ref_base, fn, +strlen (git_replace_ref_base), +DO_FOR_EACH_INCLUDE_BROKEN , cb_data); }int for_each_namespaced_ref (each_ref_fn fn,void *cb_data)refs.h +0
-0
refs.h int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, · line 320
Quoted 5 lines
refs.h +1
-1@@ -317,7 +317,7 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,int for_each_tag_ref (each_ref_fn fn,void *cb_data);int for_each_branch_ref (each_ref_fn fn,void *cb_data);int for_each_remote_ref (each_ref_fn fn,void *cb_data); -int for_each_replace_ref (struct repository *r,each_ref_fn fn,void *cb_data); +int for_each_replace_ref (struct repository *r,each_repo_ref_fn fn,void *cb_data);
With a signature change like this, any change that introduces new call to for_each_replace_ref() using eac_ref_fn() would get compilation error, so this is minimally correct.
Two things that bothersome are that
- for_each_tag/branch/remote_ref() and for_each_replace_ref() now work and look quite differently.
- existing users of for_each_replace_ref() who were all happy working in the_repository have to pass it explicitly, even thought they do not have any need to.
In this case, even if you introduced for_each_replace_ref_in_repo(), making for_each_replace_ref() a thin wrapper that always uses the_repository is a bit more cumbersome than just a simple macro.
But it *is* doable (you'd need to use a wrapping structure around cb_data), and a developer who case about maintainability during API transition would have taken pains to do so. A bit dissapointing.
Quoted 26 lines
refs.h +0
-0int for_each_glob_ref (each_ref_fn fn,const char *pattern,void *cb_data);int for_each_glob_ref_in (each_ref_fn fn,const char *pattern,const char *prefix,void *cb_data);replace-object.c +3
-2@@ -6,7 +6,8 @@#include "repository.h" #include "commit.h" -static int register_replace_ref (const char *refname, +static int register_replace_ref (struct repository *r, +const char *refname,const struct object_id *oid,int flag,void *cb_data) {@@ -25,7 +26,7 @@ static int register_replace_ref(const char *refname,oidcpy (&repl_obj->replacement, oid);/* Register new object */ -if (oidmap_put (the_repository->objects->replace_map, repl_obj)) +if (oidmap_put (r->objects->replace_map, repl_obj))die ("duplicate replace ref: %s" , refname);return 0 ;
Re: [PATCH 2/8] refs.c: upgrade for_each_replace_ref to be a each_repo_ref_fn callback
Quoted 11 lines
refs.h int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, · line 320
Quoted 6 lines
refs.h +1
-1@@ -317,7 +317,7 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,int for_each_tag_ref (each_ref_fn fn,void *cb_data);int for_each_branch_ref (each_ref_fn fn,void *cb_data);int for_each_remote_ref (each_ref_fn fn,void *cb_data); -int for_each_replace_ref (struct repository *r,each_ref_fn fn,void *cb_data); +int for_each_replace_ref (struct repository *r,each_repo_ref_fn fn,void *cb_data);With a signature change like this, any change that introduces new call to
for_each_replace_ref()usingeac_ref_fn()would get compilation error, so this is minimally correct.
Quoted 4 lines
Two things that bothersome are that
- for_each_tag/branch/remote_ref() and
for_each_replace_ref()now work and look quite differently.
Yes, this series is not finished; we need to convert/upgrade for_each_tag et al, too.
Quoted 3 lines
- existing users of
for_each_replace_ref()who were all happy working inthe_repositoryhave to pass it explicitly, even thought they do not have any need to.
All callbacks that are passed to for_each_replace_ref now operate on 'r' instead of the_repository, and that actually fixes a bug (commit message is lacking), but the cover letter hints at:
While building this series, I got some test failures in the non-the_repository tests. These issues are related to missing references to an arbitrary repository (instead of the_repository) and some uninitialized values in the tests. Stefan already sent a patch to address this [2], and I've included those commits (along with a small tweak [3]). These are only included for convenience.
Quoted 3 lines
In this case, even if you introduced
for_each_replace_ref_in_repo(), makingfor_each_replace_ref()a thin wrapper that always usesthe_repositoryis a bit more cumbersome than just a simple macro.
Yes, but such a callback would do the *wrong* subtle thing in some cases as you really want to work in the correct repository for e.g. looking up commits.
Quoted 3 lines
But it *is* doable (you'd need to use a wrapping structure around
cb_data), and a developer who case about maintainability during API transition would have taken pains to do so. A bit dissapointing.
My original patches were RFC-ish and a trade off as for the reflog only there is nothing in flight to care about.
Given that we would want to upgrade all the ref callbacks, we have to take this route, I think.
Thanks,
Stefan
Re: [PATCH 5/8] commit-graph: not compatible with replace objects
In reply to Derrick Stolee via GitGitGadgetQuoted 7 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
Create new method
commit_graph_compatible(r)to check if a given repository r is compatible with the commit-graph feature. Fill the method with a check to see if replace-objects exist. Test this interaction succeeds, including ignoring an existing commit-graph and failing to write a new commit-graph.
I think this approach is sensible. These are optimizations, and it's not a big deal to just punt no cases we can't handle.
I do have one complaint about the implementation, though:
commit-graph.c static struct commit_graph *alloc_commit_graph(void) · line 68
Quoted 8 lines
commit-graph.c +8
-0+static int commit_graph_compatible (struct repository *r) +{ +prepare_replace_object (r); +if (hashmap_get_size (&r->objects->replace_map->map)) +return 0 ; + +return 1 ; +}
If I'm reading the code correctly, this will predicate the decision entirely on the presence of refs in refs/replace/. But you may have a situation where those refs exist, but you are not respecting them in this run.
For instance, imagine[1] a server that hosts arbitrary repositories, but wants to use commit graphs to speed up server-side operations (e.g., serving fetches, but also perhaps a web interface doing --contains, etc). If it runs all of the server-side commands with GIT_NO_REPLACE_OBJECTS=1, then there should be no problem. But if a user pushes anything to refs/replace (because they _do_ use replace refs locally, and want to share them with other clients), that would disable graphs on the server.
So I think this should at least be loosened to:
if (check_replace_refs) {
prepare_replace_object(r);
if (...)}
which would make this case work. I'd even go so far as to say that for writing, we should just always ignore replace refs and generate the full graph (just like pack-objects does so when writing a pack). Then the resulting graph can be used selectively by disabling replace refs for particular commands. But for the scenario I described above, the distinction is mostly academic, as replacements would be disabled anyway during the write command anyway.
-Peff
[1] You can probably guess that this is how GitHub handles replace refs. We ran into this long ago because replacements and grafts mess up any other caches external to Git that rely on the immutability of the hash.
We do it with a config option, though, which requires a trivial patch. I'll post that momentarily.
Re: [PATCH 5/8] commit-graph: not compatible with replace objects
Quoted 4 lines · Jeff King
Quoted 2 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
Quoted 8 lines
Quoted 5 lines
Create new method
commit_graph_compatible(r)to check if a given repository r is compatible with the commit-graph feature. Fill the method with a check to see if replace-objects exist. Test this interaction succeeds, including ignoring an existing commit-graph and failing to write a new commit-graph.I think this approach is sensible. These are optimizations, and it's not a big deal to just punt no cases we can't handle.
Er, this should be "punt _on_ cases we can't handle". Sorry for my belated proof-reading.
-Peff
Re: [PATCH 5/8] commit-graph: not compatible with replace objects
In reply to Jeff KingQuoted 53 lines · Jeff King
Quoted 7 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
Create new method
commit_graph_compatible(r)to check if a given repository r is compatible with the commit-graph feature. Fill the method with a check to see if replace-objects exist. Test this interaction succeeds, including ignoring an existing commit-graph and failing to write a new commit-graph.I think this approach is sensible. These are optimizations, and it's not a big deal to just punt no cases we can't handle.
I do have one complaint about the implementation, though:
commit-graph.c static struct commit_graph *alloc_commit_graph(void) · line 68
Quoted 8 lines
commit-graph.c +8
-0+static int commit_graph_compatible (struct repository *r) +{ +prepare_replace_object (r); +if (hashmap_get_size (&r->objects->replace_map->map)) +return 0 ; + +return 1 ; +}If I'm reading the code correctly, this will predicate the decision entirely on the presence of refs in refs/replace/. But you may have a situation where those refs exist, but you are not respecting them in this run.
For instance, imagine[1] a server that hosts arbitrary repositories, but wants to use commit graphs to speed up server-side operations (e.g., serving fetches, but also perhaps a web interface doing --contains, etc). If it runs all of the server-side commands with GIT_NO_REPLACE_OBJECTS=1, then there should be no problem. But if a user pushes anything to refs/replace (because they _do_ use replace refs locally, and want to share them with other clients), that would disable graphs on the server.
So I think this should at least be loosened to:
if (check_replace_refs) { prepare_replace_object(r); if (...) }which would make this case work. I'd even go so far as to say that for writing, we should just always ignore replace refs and generate the full graph (just like pack-objects does so when writing a pack). Then the resulting graph can be used selectively by disabling replace refs for particular commands. But for the scenario I described above, the distinction is mostly academic, as replacements would be disabled anyway during the write command anyway.
-Peff
Quoted 7 lines
[1] You can probably guess that this is how GitHub handles replace refs. We ran into this long ago because replacements and grafts mess up any other caches external to Git that rely on the immutability of the hash.
We do it with a config option, though, which requires a trivial patch. I'll post that momentarily.
Thanks for the details! I never considered that someone would have these refs around, but would ignore them most of the time.
The biggest reason I wanted to punt here was that it is easy to toggle between using replace refs and not using them. Writing and reading as long as we are ignoring those refs is a good idea, and I'll use that approach in v2.
Thanks,
-Stolee
Re: [PATCH 2/8] refs.c: upgrade for_each_replace_ref to be a each_repo_ref_fn callback
In reply to Stefan BellerQuoted 20 lines · Stefan Beller
Quoted 3 lines
- existing users of
for_each_replace_ref()who were all happy working inthe_repositoryhave to pass it explicitly, even thought they do not have any need to.All callbacks that are passed to
for_each_replace_refnow operate on 'r' instead ofthe_repository, and that actually fixes a bug (commit message is lacking), but the cover letter hints at:
...Quoted 3 lines
In this case, even if you introduced
for_each_replace_ref_in_repo(), makingfor_each_replace_ref()a thin wrapper that always usesthe_repositoryis a bit more cumbersome than just a simple macro.Yes, but such a callback would do the *wrong* subtle thing in some cases as you really want to work in the correct repository for e.g. looking up commits.
Quoted 3 lines
But it *is* doable (you'd need to use a wrapping structure around
cb_data), and a developer who case about maintainability during API transition would have taken pains to do so. A bit dissapointing.
Quoted 5 lines
My original patches were RFC-ish and a trade off as for the reflog only there is nothing in flight to care about.
Given that we would want to upgrade all the ref callbacks, we have to take this route, I think.
Try to rebuild 'pu' on top of 'master' yoruself to realize how many in-flight topics you are hurting and causing unnecessary load on the maintainer with the "let's add the 'r' parameter without changing the function names; compiler would catch and cause breakages" approach.
Until that happens, I won't waste any more time trying to educate you on this further.
Re: [PATCH 5/8] commit-graph: not compatible with replace objects
In reply to Derrick StoleeQuoted 59 lines · Derrick Stolee
Quoted 58 lines · Jeff King
Quoted 7 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
Create new method
commit_graph_compatible(r)to check if a given repository r is compatible with the commit-graph feature. Fill the method with a check to see if replace-objects exist. Test this interaction succeeds, including ignoring an existing commit-graph and failing to write a new commit-graph.I think this approach is sensible. These are optimizations, and it's not a big deal to just punt no cases we can't handle.
I do have one complaint about the implementation, though:
commit-graph.c static struct commit_graph *alloc_commit_graph(void) · line 68
Quoted 8 lines
commit-graph.c +8
-0+static int commit_graph_compatible (struct repository *r) +{ + prepare_replace_object(r); + if (hashmap_get_size(&r->objects->replace_map->map)) + return 0; + + return 1; +}If I'm reading the code correctly, this will predicate the decision entirely on the presence of refs in refs/replace/. But you may have a situation where those refs exist, but you are not respecting them in this run.
For instance, imagine[1] a server that hosts arbitrary repositories, but wants to use commit graphs to speed up server-side operations (e.g., serving fetches, but also perhaps a web interface doing --contains, etc). If it runs all of the server-side commands with GIT_NO_REPLACE_OBJECTS=1, then there should be no problem. But if a user pushes anything to refs/replace (because they _do_ use replace refs locally, and want to share them with other clients), that would disable graphs on the server.
So I think this should at least be loosened to:
if (
check_replace_refs) {
prepare_replace_object(r);
if (...)
}which would make this case work. I'd even go so far as to say that for writing, we should just always ignore replace refs and generate the full graph (just like pack-objects does so when writing a pack). Then the resulting graph can be used selectively by disabling replace refs for particular commands. But for the scenario I described above, the distinction is mostly academic, as replacements would be disabled anyway during the write command anyway.
-Peff
[1] You can probably guess that this is how GitHub handles replace refs. We ran into this long ago because replacements and grafts mess up any other caches external to Git that rely on the immutability of the hash.
Quoted 6 lines
Quoted 2 lines
We do it with a config option, though, which requires a trivial patch. I'll post that momentarily.
Thanks for the details! I never considered that someone would have these refs around, but would ignore them most of the time.
The biggest reason I wanted to punt here was that it is easy to toggle between using replace refs and not using them. Writing and reading as long as we are ignoring those refs is a good idea, and I'll use that approach in v2.
Thanks, Peff, for improving the check_replace_refs interaction [1].
Since this series now has two dependencies (including Stefan's ref-store fix [2] that I had included in my v1), I'll let those topics settle before I send a new v2.
If there are more comments about how I'm handling these cases, then please jump in and tell me. I'll revisit this topic in a few weeks.
Thanks,
-Stolee
[1] https://public-inbox.org/git/[email protected]/T/#u
[PATCH 1/3] check_replace_refs: fix outdated comment
[2] https://public-inbox.org/git/[email protected]/T/#u
[PATCH 0/2] RFC ref store to repository migration
Re: [PATCH 5/8] commit-graph: not compatible with replace objects
Quoted 5 lines
Thanks, Peff, for improving the
check_replace_refsinteraction [1].Since this series now has two dependencies (including Stefan's ref-store fix [2] that I had included in my v1), I'll let those topics settle before I send a new v2.
FYI: I have not been working on, nor plan to work on (in the short term) on the ref store fix series, which needs another abstraction layer IIUC to make it easier to integrate such a thing as well as extend the series to all functions in the refstore.
Feel free to take over that series partially (and defer the extension for all functions in the ref store until later) ?
Stefan
Re: [PATCH 5/8] commit-graph: not compatible with replace objects
Quoted 5 lines · Stefan Beller
Quoted 5 lines
Thanks, Peff, for improving the
check_replace_refsinteraction [1].Since this series now has two dependencies (including Stefan's ref-store fix [2] that I had included in my v1), I'll let those topics settle before I send a new v2.
Quoted 7 lines
FYI: I have not been working on, nor plan to work on (in the short term) on the ref store fix series, which needs another abstraction layer IIUC to make it easier to integrate such a thing as well as extend the series to all functions in the refstore.
Feel free to take over that series partially (and defer the extension for all functions in the ref store until later) ?
OK. Thanks for letting me know.
Perhaps I'll give it a try to pass the repository struct in the cbdata for the function pointers, as a minimum amount of change option.
Thanks,
-Stolee
"Derrick Stolee via GitGitGadget" <[email protected]> writes:
Sidenote: the GitGitGadget doesn't fold/wrap lines properly in the cover-letter. Not that it is much of a problem.
Quoted 10 lines
One unresolved issue with the commit-graph feature is that it can cause issues when combined with replace objects, commit grafts, or shallow clones. These are not 100% incompatible, as one could be reasonably successful writing a commit-graph after replacing some objects and not have issues. The problems happen when commits that are already in the commit-graph file are replaced, or when git is run with the
--no-replace-objectsoption; this can cause incorrect parents or incorrect generation numbers. Similar things occur with commit grafts and shallow clones, especially when runninggit fetch --unshallowin a shallow repo.
This means that these features can change the view of the history, and if the commit-graph was created before the change, it can have stale incorrect information -- which would lead to incorrect results if the commit-graph feature is used.
Quoted 5 lines
Instead of trying (and probably failing) to make these features work together, default to making the commit-graph feature unavailable in these situations. Create a new method '
commit_graph_compatible(r)' that checks if the repository 'r' has any of these features enabled.
Yes, the alternative would be trying to keep the commit-graph file up-to-date with the current view of history. But there are problems with this approach.
With grafts (which is a deprecated feature), there is no automated entry point. Best what one could do is to store some kind of fingerprint of graft file, and invalidate / recreate commit-graft file if it does not match what is in graft file at the time of running git command that may use commit-graft feature.
With replace objects we have two separate entry points: the git-replace command and fetching references in refs/replace/* namespace (and equivalent). While the first should be not difficult to do, the second one seems to be harder. Additionally, there is '--no-replace-objects' option to turn off the feature (fetch and push ignore replacement objects automatically); so we might want to have two versions of
commit-graph: one with replacement objects feature and one without (or something like that).
The shallow clone seems easiest, with only one automated entry points for changing the view of history this way, and no option to disable this feature -- but it is also the least interesting, as the intent of shallow clone is to have less history, so the commit-graph is less necessary, as you wrote.
We might want to revisit it later, but I agree that starting from this simple approach would be best.
One thing I would like to see added is for the user to know when commit-graph is not available (in manpages), and maybe even a way to see if it is on (e.g. with 'git commit-graph verify' and/or maybe in 'git status' output). But this is a separate issue.
Quoted 3 lines
I will send a follow-up patch that shows how I tested these interactions by computing the commit-graph on every 'git commit'.
Wouldn't it be enough to create commit-graph file, change the view of the history (via grafts, via replace objects, via shallow clone), and check that you still get correct results?
[cut]
Quoted 2 lines
This approach is very different from the previous RFC on the subject [1].
The previous RFC was in my opinion needlessly complicated. This one is much simpler, and better.
[...]
Quoted 51 lines
Thanks,
-Stolee[1] https://public-inbox.org/git/[email protected]/
[RFC PATCH 0/6] Fix commit-graph/graft/replace/shallow combo[2] https://public-inbox.org/git/[email protected]/T/#t
[PATCH 0/2] RFC ref store to repository migration[3] https://public-inbox.org/git/[email protected]/T/#m966eac85fd58c66523654ddaf0bec72877d3295a
[PATCH] TO-SQUASH: replacethe_repositorywith arbitrary rBased-On: jt/commit-graph-per-object-store
Cc: [email protected]
Cc: [email protected]Derrick Stolee (6):
commit-graph: update design document
test-repository: properly init repo
commit-graph: not compatible with replace objects
commit-graph: not compatible with grafts
commit-graph: not compatible with uninitialized repo
commit-graph:close_commit_graphbefore shallow walkStefan Beller (2):
refs.c: migrate internal ref iteration to pass thru repository
argument
refs.c: upgradefor_each_replace_refto be aeach_repo_ref_fncallbackDocumentation/technical/commit-graph.txt | 18 ++++++-- builtin/replace.c | 8 ++-- commit-graph.c | 34 ++++++++++++-- commit-graph.h | 1 + commit.c | 2 +- commit.h | 1 + refs.c | 48 +++++++++++++++++--- refs.h | 12 ++++- refs/iterator.c | 6 +-- refs/refs-internal.h | 5 +- replace-object.c | 7 +-- replace-object.h | 2 + t/helper/test-repository.c | 10 +++- t/t5318-commit-graph.sh | 58 ++++++++++++++++++++++++ upload-pack.c | 2 + 15 files changed, 184 insertions(+), 30 deletions(-)base-commit:
dade47c06cf849b0ca180a8e6383b55ea6f75812
Published-As: https://github.com/gitgitgadget/git/releases/tags/pr-11%2Fderrickstolee%2Fshallow%2Fupstream-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-11/derrickstolee/shallow/upstream-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/11
Re: [PATCH 3/8] commit-graph: update design document
In reply to Derrick Stolee via GitGitGadgetQuoted 9 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
As it exists right now, the commit-graph feature may provide inconsistent results when combined with commit grafts, replace objects, and shallow clones. Update the design document to discuss why these interactions are difficult to reconcile and how we will avoid errors by preventing updates to and reads from the commit-graph file when these other features exist.
Quoted 4 lines
Signed-off-by Derrick Stolee <[email protected]>--- Documentation/technical/commit-graph.txt | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-)
It would be nice to have this information not only in the technical documentation, but also in user-facing documentation, for example git-commit-graph manpage.
Documentation/technical/commit-graph.txt Design Details · line 116
Quoted 11 lines
Documentation/technical/commit-graph.txt +2
-0@@ -112,12 +112,24 @@ Design Details - The file format includes parameters for the object ID hash function, so a future change of hash algorithm does not require a change in format. +- Commit grafts and replace objects can change the shape of the commit + history. These can also be enabled/disabled on the fly using
I think you meant "The latter can..." instead of "These can...", as grafts cannot be disabled with --no-replace-objects option.
Quoted 22 lines
Documentation/technical/commit-graph.txt +13
-3+ `--no-replace-objects`. This leads to difficultly storing both possible + interpretations of a commit id, especially when computing generation + numbers. The commit-graph will not be read or written when + replace-objects or grafts are present. + +- Shallow clones create grafts of commits by dropping their parents. This + leads the commit-graph to think those commits have generation number 1. + If and when those commits are made unshallow, those generation numbers + become invalid. Since shallow clones are intended to restrict the commit + history to a very small set of commits, the commit-graph feature is less + helpful for these clones, anyway. The commit-graph will not be read or + written when shallow commits are present. + Future Work ----------- -- The commit graph feature currently does not honor commit grafts. This can - be remedied by duplicating or refactoring the current graft logic. - - After computing and storing generation numbers, we must make graph walks aware of generation numbers to gain the performance benefits they enable. This will mostly be accomplished by swapping a commit-date-ordered
Quoted 2 lines · Jakub Narebski
Sidenote: the GitGitGadget doesn't fold/wrap lines properly in the cover-letter. Not that it is much of a problem.
Stefan opened a ticket at the gitgitgadget project:
https://github.com/gitgitgadget/gitgitgadget/issues/26
Re: [PATCH 5/8] commit-graph: not compatible with replace objects
In reply to Derrick Stolee via GitGitGadgetQuoted 5 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
Create new method
commit_graph_compatible(r)to check if a given repository r is compatible with the commit-graph feature. Fill the method with a check to see if replace-objects exist.
All right, looks sensible. Did you start with the history-"altering" feature with simplest detection mechanism?
Quoted 3 lines
Test this
interaction succeeds, including ignoring an existing commit-graph and failing to write a new commit-graph.
I wonder if we should test the implementation, or just that you get correct answer when history view is altered after writing commit-graph file... oh, wait, you do that (well, except for testing that git commit-graph write does not create commit-graph file instead of testing that the commit-graph file stores correct information even after the feature is removed (with git replace --delete) or turned off (with --no-replace-objects or equivalent). Good.
Sidenote: the inconsistency between command options and subcommands... ehh... compare e.g. git-replace and git-remote.
Quoted 23 lines
Signed-off-by Derrick Stolee <[email protected]>--- commit-graph.c | 17 +++++++++++++++++ replace-object.c | 2 +- replace-object.h | 2 ++ t/t5318-commit-graph.sh | 22 ++++++++++++++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-)commit-graph.c +2
-0@@ -13,6 +13,8 @@#include "commit-graph.h" #include "object-store.h" #include "alloc.h" +#include "hashmap.h" +#include "replace-object.h" #define GRAPH_SIGNATURE 0x43475048/* "CGPH" */ #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446/* "OIDF" */
commit-graph.c static struct commit_graph *alloc_commit_graph(void) · line 64
Quoted 7 lines
commit-graph.c +4
-0@@ -56,6 +58,15 @@ static struct commit_graph *alloc_commit_graph(void)return g; } +static int commit_graph_compatible (struct repository *r) +{ +prepare_replace_object (r); +if (hashmap_get_size (&r->objects->replace_map->map))
A very minor nitpick, feel free to ignore. I think that the condition would be more readable written as
+ if (hashmap_get_size(&r->objects->replace_map->map) > 0)
Another issue is that the condition may be too strict (but it is also the easiest to test for). You might have replacement objects for non-commit objects (e.g. replacing contents of configuration files with one with API keys and/or other secrets - not that I know any tool that does that), or replacing commit-objects without changing their history (e.g. to fix some annoying error in demo repository, where you want to show reflog). But I think that would be rare, so easier if maybe over-eager test is fine for me.
Quoted 19 lines
commit-graph.c +8
-0+return 0 ; + +return 1 ; +} +struct commit_graph *load_commit_graph_one (const char *graph_file) {void *graph_map;@@ -223,6 +234,9 @@ static int prepare_commit_graph(struct repository *r)*/ return 0 ; +if (!commit_graph_compatible (r)) +return 0 ; + obj_dir = r->objects->objectdir;prepare_commit_graph_one (r, obj_dir);prepare_alt_odb (r);
commit-graph.c void write_commit_graph(const char *obj_dir, · line 712
Quoted 6 lines
commit-graph.c +3
-0@@ -693,6 +707,9 @@ void write_commit_graph(const char *obj_dir,int num_extra_edges;struct commit_list *parent; +if (!commit_graph_compatible (the_repository)) +return ; +
Hmmm... we might want to write commit-graph for immutable history; but not all history-"altering" feature can be easily turned off. So if it is to stay universal, without aadditional complications, it is good enough.
Quoted 21 lines
commit-graph.c +0
-0oids.nr =0 ; oids.alloc =approximate_object_count () /4 ;replace-object.c +1
-1@@ -32,7 +32,7 @@ static int register_replace_ref(struct repository *r,return 0 ; } -static void prepare_replace_object (struct repository *r) +void prepare_replace_object (struct repository *r) {if (r->objects->replace_map)return ;replace-object.h +0
-0
replace-object.h struct replace_object { · line 14
Quoted 5 lines
replace-object.h +2
-0@@ -10,6 +10,8 @@ struct replace_object {struct object_id replacement; }; +void prepare_replace_object (struct repository *r); +
Hmmm... how it is that this function didn't need to be exported before, I do wonder?
Quoted 9 lines
replace-object.h +0
-0/* * This internal function is only declared here for the benefit of * lookup_replace_object(). Please do not call it directly. t/t5318-commit-graph.sh +0
-0@@ -259,6 +259,28 @@ test_expect_success 'check that gc computes commit-graph' ' test_cmp commit-graph-after-gc $objdir/info/commit-graph
t/t5318-commit-graph.sh test_expect_success 'check that gc computes commit-graph' ' · line 269
Quoted 10 lines
t/t5318-commit-graph.sh +8
-0' +test_expect_success 'replace-objects invalidates commit-graph' ' + cd "$TRASH_DIRECTORY" && + test_when_finished rm -rf replace && + git clone full replace && + ( + cd replace && + git commit-graph write --reachable && + test_path_is_file .git/objects/info/commit-graph &&
All right, this is preparation / setup; create commit-graph file, and check that it was created.
t/t5318-commit-graph.sh test_expect_success 'check that gc computes commit-graph' ' · line 270
Quoted 1 line
t/t5318-commit-graph.sh +1
-0+ git replace HEAD~1 HEAD~2 &&
All right, so before replacement the history looks like this:
... <--- A <--- B <--- C <--- D <--- master <=== HEAD
~3 ~2 ~1 HEAD
While after the replacement the history view looks like this, if I understand it correctly:
... <--- A <--- B
\
\---[B] <--- D <--- master <=== HEADWouldn't it be better (and more similar to the grafts test) to use git replace --graft, e.g.:
+ git replace --graft HEAD~1 HEAD~3 &&
But that might be just a matter of taste.
t/t5318-commit-graph.sh test_expect_success 'check that gc computes commit-graph' ' · line 273
Quoted 3 lines
t/t5318-commit-graph.sh +3
-0+ git -c core.commitGraph=false log >expect && + git -c core.commitGraph=true log >actual && + test_cmp expect actual &&
All right, so instead of testing if the output is what is expected, we test that the commit-graph feature did not change it. Good.
t/t5318-commit-graph.sh test_expect_success 'check that gc computes commit-graph' ' · line 274
Quoted 1 line
t/t5318-commit-graph.sh +1
-0+ git commit-graph write --reachable &&
Is this necessary [see below]?
t/t5318-commit-graph.sh test_expect_success 'check that gc computes commit-graph' ' · line 276
Quoted 2 lines
t/t5318-commit-graph.sh +2
-0+ git -c core.commitGraph=false --no-replace-objects log >expect && + git -c core.commitGraph=true --no-replace-objects log >actual &&
All right, and here we test the same for --no-replace-objects case. Good.
By the way, is this git commit-graph write --reachable preceding this part of test necessary?
t/t5318-commit-graph.sh test_expect_success 'check that gc computes commit-graph' ' · line 280
Quoted 4 lines
t/t5318-commit-graph.sh +4
-0+ test_cmp expect actual && + rm -rf .git/objects/info/commit-graph && + git commit-graph write --reachable && + test_path_is_missing .git/objects/info/commit-graph
All right, so here we test that we wont write commit-graph file with altered view of history; which would store wrong information if the replace feature is turned off or if replace object is deleted (or if commit graph gets transferred in the furure, but the replacement refs do not).
Checking that the commit-graph file ws not created might be seen as testing implementation details, but full test would require testing with --no-replace-objects and after git replace -d; the above is simpler, so good enough.
Quoted 6 lines
t/t5318-commit-graph.sh +3
-0+ ) +' + # the verify tests below expect the commit-graph to contain # exactly the commits reachable from the commits/8 branch. # If the file changes the set of commits in the list, then the
Re: [PATCH 4/8] test-repository: properly init repo
In reply to Derrick Stolee via GitGitGadgetQuoted 12 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
Signed-off-by Derrick Stolee <[email protected]>--- t/helper/test-repository.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)t/helper/test-repository.c +0
-0
t/helper/test-repository.c static void test_parse_commit_in_graph(const char *gitdir, const char *worktree, · line 21
Quoted 8 lines
t/helper/test-repository.c +4
-1@@ -15,7 +15,10 @@ static void test_parse_commit_in_graph(const char *gitdir, const char *worktree,struct commit *c;struct commit_list *parent; -repo_init (&r, gitdir, worktree); +setup_git_env (gitdir); + +if (repo_init (&r, gitdir, worktree)) +die ("Couldn't init repo" );
Shouldn't this message be marked for translation?
+ die(_("Couldn't init repo"));Perhaps we could provide also more detail about error, if possible.
t/helper/test-repository.c static void test_get_commit_tree_in_graph(const char *gitdir, · line 47
Quoted 12 lines
t/helper/test-repository.c +4
-1c =lookup_commit (&r, commit_oid);@@ -38,7 +41,10 @@ static void test_get_commit_tree_in_graph(const char *gitdir,struct commit *c;struct tree *tree; -repo_init (&r, gitdir, worktree); +setup_git_env (gitdir); + +if (repo_init (&r, gitdir, worktree)) +die ("Couldn't init repo" );
I wonder if it would be worth it to extract this idiom into separate function, called for exaple repo_init_or_die(), to avoid code duplication (and maybe use it in other places?).
Quoted 2 lines
t/helper/test-repository.c +0
-0c =lookup_commit (&r, commit_oid);
Re: [PATCH 6/8] commit-graph: not compatible with grafts
In reply to Derrick Stolee via GitGitGadgetQuoted 6 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
Augment
commit_graph_compatible(r)to return false when the given repository r has commit grafts or is a shallow clone. Test that in these situations we ignore existing commit-graph files and we do not write new commit-graph files.
Tests for grafts are wrong, as they test replace objects.
Quoted 14 lines
Signed-off-by Derrick Stolee <[email protected]>--- commit-graph.c | 6 ++++++ commit.c | 2 +- commit.h | 1 + t/t5318-commit-graph.sh | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-)commit-graph.c +0
-0
commit-graph.c static struct commit_graph *alloc_commit_graph(void) · line 67
Quoted 8 lines
commit-graph.c +5
-0@@ -60,6 +60,12 @@ static struct commit_graph *alloc_commit_graph(void)static int commit_graph_compatible (struct repository *r) { +prepare_commit_graft (r); +if (r->parsed_objects && r->parsed_objects->grafts_nr) +return 0 ; +if (is_repository_shallow (r)) +return 0 ;
Do I assume correctly that is_repository_shallow(r) needs prepare_commit_graph(r) done earlier? If it is so, all right.
If it is not so, then why put two separate history-"altering" features, namely grafts and shallow clones in single commit instead of in separate commits?
Quoted 22 lines
commit-graph.c +1
-0+prepare_replace_object (r);if (hashmap_get_size (&r->objects->replace_map->map))return 0 ;commit.c +1
-1@@ -209,7 +209,7 @@ static int read_graft_file(struct repository *r, const char *graft_file)return 0 ; } -static void prepare_commit_graft (struct repository *r) +void prepare_commit_graft (struct repository *r) {char *graft_file;commit.h +0
-0
commit.h typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *); · line 207
Quoted 6 lines
commit.h +1
-0@@ -202,6 +202,7 @@ typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);struct commit_graft *read_graft_line (struct strbuf *line);int register_commit_graft (struct repository *r,struct commit_graft *,int ); +void prepare_commit_graft (struct repository *r);struct commit_graft *lookup_commit_graft (struct repository *r,const struct object_id *oid);
I guess that prepare_commit_graft() was not needed outside commit.c before.
Quoted 8 lines
commit.h +0
-0extern struct commit_list *get_merge_bases (struct commit *rev1,struct commit *rev2);t/t5318-commit-graph.sh +0
-0@@ -281,6 +281,42 @@ test_expect_success 'replace-objects invalidates commit-graph' ' ) '
t/t5318-commit-graph.sh test_expect_success 'replace-objects invalidates commit-graph' ' · line 292
Quoted 10 lines
t/t5318-commit-graph.sh +9
-0+test_expect_success 'commit grafts invalidate commit-graph' ' + cd "$TRASH_DIRECTORY" && + test_when_finished rm -rf graft && + git clone full graft && + ( + cd graft && + git commit-graph write --reachable && + test_path_is_file .git/objects/info/commit-graph && + git replace --graft HEAD~1 HEAD~3 &&
Errr... this uses *replace objects* mechanism, not graft file!! The --graft option of git-remote command is here to make it easier to use replace objects to alter the view of history, and make if easy to convert from deprected grafts feature to replace objects.
Grafts file structure is described in gitrepository-layout(5) manpage. You would need to do something like the following:
+ H1=$(git rev-parse --verify HEAD~1) && + H3=$(git rev-parse --verify HEAD~3) && + echo '$H1 $H3' >.git/info/grafts &&
You could have used "git replace --graft HEAD~1 HEAD~3" in previous commit test, like I wrote in reply to it.
t/t5318-commit-graph.sh test_expect_success 'replace-objects invalidates commit-graph' ' · line 295
Quoted 3 lines
t/t5318-commit-graph.sh +3
-0+ git -c core.commitGraph=false log >expect && + git -c core.commitGraph=true log >actual && + test_cmp expect actual &&
All right, we test that we get the same result (when graft-file is written before altering the view of history) with and without the commit-graph feature.
Sidenote: shouldn't we use rev-list instead? Though here git-log is actually safe to use...
t/t5318-commit-graph.sh test_expect_success 'replace-objects invalidates commit-graph' ' · line 298
Quoted 3 lines
t/t5318-commit-graph.sh +3
-0+ git commit-graph write --reachable && + git -c core.commitGraph=false --no-replace-objects log >expect && + git -c core.commitGraph=true --no-replace-objects log >actual &&
The --no-replace-objects does not affect the graph file!! You would want to remove graft file instead:
+ git commit-graph write --reachable && + rm -f .git/info/grafts && + git -c core.commitGraph=false log >expect && + git -c core.commitGraph=true log >actual &&
And then check the results.
t/t5318-commit-graph.sh test_expect_success 'replace-objects invalidates commit-graph' ' · line 302
Quoted 4 lines
t/t5318-commit-graph.sh +4
-0+ test_cmp expect actual && + rm -rf .git/objects/info/commit-graph && + git commit-graph write --reachable && + test_path_is_missing .git/objects/info/commit-graph
All right.
Though, if it is the same for grafts and for replacements... nah. Adding a feature and deleting a feature is different, and it is only a single repetition.
t/t5318-commit-graph.sh test_expect_success 'replace-objects invalidates commit-graph' ' · line 309
Quoted 7 lines
t/t5318-commit-graph.sh +7
-0+ ) +' + +test_expect_success 'replace-objects invalidates commit-graph' ' + cd "$TRASH_DIRECTORY" && + test_when_finished rm -rf shallow && + git clone --depth 2 "file://$TRASH_DIRECTORY/full" shallow &&
Why do we need to use "file://..." URL, instead of simply "full"?
t/t5318-commit-graph.sh test_expect_success 'replace-objects invalidates commit-graph' ' · line 316
Quoted 7 lines
t/t5318-commit-graph.sh +7
-0+ ( + cd shallow && + git commit-graph write --reachable && + test_path_is_missing .git/objects/info/commit-graph && + git fetch origin --unshallow && + git commit-graph write --reachable && + test_path_is_file .git/objects/info/commit-graph
All right, so in this case because you need to start (clone) with shallow feature, the test could be simplified.
Quoted 6 lines
t/t5318-commit-graph.sh +3
-0+ ) +' + # the verify tests below expect the commit-graph to contain # exactly the commits reachable from the commits/8 branch. # If the file changes the set of commits in the list, then the
Re: [PATCH 7/8] commit-graph: not compatible with uninitialized repo
In reply to Derrick Stolee via GitGitGadget"Derrick Stolee via GitGitGadget" <[email protected]> writes:
[I hope that the rest of replies would make it all right through GitGitGadget]
Quoted 12 lines
From: Derrick Stolee <[email protected]>
Signed-off-by Derrick Stolee <[email protected]>--- commit-graph.c | 3 +++ 1 file changed, 3 insertions(+)commit-graph.c +0
-0
commit-graph.c static struct commit_graph *alloc_commit_graph(void) · line 65
Quoted 6 lines
commit-graph.c +3
-0@@ -60,6 +60,9 @@ static struct commit_graph *alloc_commit_graph(void)static int commit_graph_compatible (struct repository *r) { +if (!r->gitdir) +return 0 ; +
Nice catch.
I wonder if this is the problem because of the following tests (in which case this test should probably be earlier in this series), or not (and it doesn't need to).
I wonder how this issue was caught.
commit-graph.c static struct commit_graph *alloc_commit_graph(void) · line 68
Quoted 3 lines
commit-graph.c +0
-0prepare_commit_graft (r);if (r->parsed_objects && r->parsed_objects->grafts_nr)return 0 ;
No tests?
Re: [PATCH] DO-NOT-MERGE: write and read commit-graph always
In reply to Derrick StoleeQuoted 45 lines · Derrick Stolee
This commit is not intended to be merged, but is only to create a test environment to see what works with the commit-graph feature and what does not. The tests that fail are primarily related to corrupting the object store to remove a commit from visibility and testing that rev-list fails -- except it doesn't when there is a commit-graph that prevents parsing from the object database. The following tests will fail with this commit, but are not "real" bugs:
t0410-partial-clone.sh, Test 9t5307-pack-missing-commit.sh, Tests 3-4t6011-rev-list-with-bad-commit.sh, Test 4The following test fails because the repo has ambiguous merge-bases, and the commit-graph changes the walk order so we select a different one. This alters the resulting merge from the expected result.
t6024-recursive-merge.sh, Test 4The tests above are made to pass by deleting the commit-graph file before the necessary steps.
Signed-off-by Derrick Stolee <[email protected]>--- builtin/commit.c | 2 ++ builtin/gc.c | 3 +-- commit-graph.c | 11 ----------- t/t0410-partial-clone.sh | 1 + t/t5307-pack-missing-commit.sh | 2 ++ t/t6011-rev-list-with-bad-commit.sh | 1 + t/t6024-recursive-merge.sh | 1 + 7 files changed, 8 insertions(+), 13 deletions(-)builtin/commit.c +1
-0@@ -33,6 +33,7 @@#include "sequencer.h" #include "mailmap.h" #include "help.h" +#include "commit-graph.h" static const char *const builtin_commit_usage[] = {N_ ("git commit [<options>] [--] <pathspec>..." ),
builtin/commit.c int cmd_commit(int argc, const char **argv, const char *prefix) · line 1659
Quoted 7 lines
builtin/commit.c +1
-0@@ -1652,6 +1653,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) "not exceeded, and then \"git reset HEAD\" to recover."));rerere (0 ); +write_commit_graph_reachable (get_object_directory (),1 );run_command_v_opt (argv_gc_auto,RUN_GIT_CMD );run_commit_hook (use_editor,get_index_file (),"post-commit" ,NULL );if (amend && !no_post_rewrite) {
This is certainly not for merging.
builtin/gc.c static int aggressive_depth = 50; · line 47
Quoted 13 lines
builtin/gc.c +1
-1@@ -41,7 +41,7 @@ static int aggressive_depth = 50;static int aggressive_window =250 ;static int gc_auto_threshold =6700 ;static int gc_auto_pack_limit =50 ; -static int gc_write_commit_graph; +static int gc_write_commit_graph =1 ;static int detach_auto =1 ;static timestamp_t gc_log_expire_time;static const char *gc_log_expire ="1.day.ago" ;
This is switching from default to off to default to on. I think with this feature we would default to off for a long time (wishful thinking:
maybe automatically enabling it for large repositories, or if commit-graph file exists already?).
builtin/gc.c static void gc_config(void) · line 136
Quoted 8 lines
builtin/gc.c +0
-1@@ -131,7 +131,6 @@ static void gc_config(void)git_config_get_int ("gc.aggressivedepth" , &aggressive_depth);git_config_get_int ("gc.auto" , &gc_auto_threshold);git_config_get_int ("gc.autopacklimit" , &gc_auto_pack_limit); -git_config_get_bool ("gc.writecommitgraph" , &gc_write_commit_graph);git_config_get_bool ("gc.autodetach" , &detach_auto);git_config_get_expiry ("gc.pruneexpire" , &prune_expire);git_config_get_expiry ("gc.worktreepruneexpire" , &prune_worktrees_expire);
This is certainly not for merging, as it disables gc.writeCommitGraph config option entirely.
Quoted 17 lines
commit-graph.c +0
-4@@ -227,22 +227,11 @@ static int prepare_commit_graph(struct repository *r) {struct alternate_object_database *alt;char *obj_dir; -int config_value;if (r->objects->commit_graph_attempted)return !!r->objects->commit_graph; r->objects->commit_graph_attempted =1 ; -if (repo_config_get_bool (r,"core.commitgraph" , &config_value) || - !config_value) -/*
commit-graph.c static int prepare_commit_graph(struct repository *r) · line 237
Quoted 10 lines
commit-graph.c +0
-7-* This repository is not configured to use commit graphs, so -* do not load one. (But report commit_graph_attempted anyway -* so that commit graph loading is not attempted again for this -* repository.) -*/ -return 0 ; -if (!commit_graph_compatible (r))return 0 ;
This is certainly not for merging, as it disables core.commitGraph config option entirely.
Quoted 46 lines
t/t0410-partial-clone.sh +1
-0@@ -181,6 +181,7 @@ test_expect_success 'rev-list stops traversal at missing and promised commit' ' git -C repo config core.repositoryformatversion 1 && git -C repo config extensions.partialclone "arbitrary string" && + rm -rf repo/.git/objects/info/commit-graph && git -C repo rev-list --exclude-promisor-objects --objects bar >out && grep $(git -C repo rev-parse bar) out && ! grep $FOO outt/t5307-pack-missing-commit.sh +2
-0@@ -24,10 +24,12 @@ test_expect_success 'check corruption' ' ' test_expect_success 'rev-list notices corruption (1)' ' + rm -rf .git/objects/info/commit-graph && test_must_fail git rev-list HEAD ' test_expect_success 'rev-list notices corruption (2)' ' + rm -rf .git/objects/info/commit-graph && test_must_fail git rev-list --objects HEAD 't/t6011-rev-list-with-bad-commit.sh +1
-0@@ -43,6 +43,7 @@ test_expect_success 'corrupt second commit object' \ test_expect_success 'rev-list should fail' \ ' + rm -rf .git/objects/info/commit-graph && test_must_fail git rev-list --all > /dev/null 't/t6024-recursive-merge.sh +0
-0
t/t6024-recursive-merge.sh GIT_AUTHOR_DATE="2006-12-12 23:00:08" git commit -m F · line 66
Quoted 6 lines
t/t6024-recursive-merge.sh +1
-0@@ -61,6 +61,7 @@ GIT_AUTHOR_DATE="2006-12-12 23:00:08" git commit -m F ' test_expect_success "combined merge conflicts" " + rm -rf .git/objects/info/commit-graph && test_must_fail git merge -m final G "
I wonder though if all those changes to the testsuite shouldn't be merged.
Regards,
Signature · 1 line
Jakub Narębski
Re: [PATCH] DO-NOT-MERGE: write and read commit-graph always
Quoted 2 lines
I wonder though if all those changes to the testsuite shouldn't be merged.
I think Stolee doesn't want this to be merged after rereading subject and the commit message.
Thanks,
Stefan
Re: [PATCH 8/8] commit-graph: close_commit_graph before shallow walk
In reply to Derrick Stolee via GitGitGadgetQuoted 3 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
Make
close_commit_graph()work for arbitrary repositories.
The first line (above) does not match the rest of the commit message.
Quoted 79 lines
Call
close_commit_graph()when about to start a rev-list walk that includes shallow commits. This is necessary in code paths that "fake" shallow commits for the sake of fetch. Specifically, test 351 int5500-fetch-pack.shrunsgit fetch --shallow-exclude one originwith a file-based transfer. When the "remote" has a commit-graph, we do not prevent the commit-graph from being loaded, but then the commits are intended to be dynamically transferred into shallow commits during
get_shallow_commits_by_rev_list(). By closing the commit-graph before this call, we prevent this interaction.Signed-off-by Derrick Stolee <[email protected]>--- commit-graph.c | 8 ++++---- commit-graph.h | 1 + upload-pack.c | 2 ++ 3 files changed, 7 insertions(+), 4 deletions(-)commit-graph.c +4
-4@@ -256,10 +256,10 @@ static int prepare_commit_graph(struct repository *r)return !!r->objects->commit_graph; } -static void close_commit_graph (void ) +void close_commit_graph (struct repository *r) { -free_commit_graph (the_repository->objects->commit_graph); - the_repository->objects->commit_graph =NULL ; +free_commit_graph (r->objects->commit_graph); + r->objects->commit_graph =NULL ; }static int bsearch_graph (struct commit_graph *g,struct object_id *oid,uint32_t *pos)@@ -871,7 +871,7 @@ void write_commit_graph(const char *obj_dir,write_graph_chunk_data (f,GRAPH_OID_LEN , commits.list, commits.nr);write_graph_chunk_large_edges (f, commits.list, commits.nr); -close_commit_graph (); +close_commit_graph (the_repository);finalize_hashfile (f,NULL ,CSUM_HASH_IN_STREAM |CSUM_FSYNC );commit_lock_file (&lk);commit-graph.h +1
-0@@ -59,6 +59,7 @@ void write_commit_graph(const char *obj_dir,int verify_commit_graph (struct repository *r,struct commit_graph *g); +void close_commit_graph (struct repository *);void free_commit_graph (struct commit_graph *);#endif upload-pack.c +2
-0@@ -24,6 +24,7 @@#include "quote.h" #include "upload-pack.h" #include "serve.h" +#include "commit-graph.h" /* Remember to update object flag allocation in object.h */ #define THEY_HAVE (1u << 11)@@ -739,6 +740,7 @@ static void deepen_by_rev_list(int ac, const char **av, {struct commit_list *result; +close_commit_graph (the_repository); result =get_shallow_commits_by_rev_list (ac, av,SHALLOW ,NOT_SHALLOW );send_shallow (result);free_commit_list (result);
Re: [PATCH] DO-NOT-MERGE: write and read commit-graph always
In reply to Stefan BellerQuoted 5 lines · Stefan Beller
Quoted 2 lines
I wonder though if all those changes to the testsuite shouldn't be merged.
I think Stolee doesn't want this to be merged after rereading subject and the commit message.
Yes, I understand that, and for the most part I agree with it. This commit main purpose is to thoroughly exercise the new feature.
But I think that changes to the testsuite could have been extracted into separate commit, and merged (and only those changes). It could serve as note about the intent of the test. Well, perhaps after encapsulating it in some function with a good name... ;-)
Anyway, this is a minor insignificant thing.
Signature · 1 line
Jakub Narębski
Re: [PATCH] DO-NOT-MERGE: write and read commit-graph always
In reply to Derrick StoleeQuoted 8 lines · Derrick Stolee
The following test fails because the repo has ambiguous merge-bases, and the commit-graph changes the walk order so we select a different one. This alters the resulting merge from the expected result.
t6024-recursive-merge.sh, Test 4The tests above are made to pass by deleting the commit-graph file before the necessary steps.
I know you meant for these to not be merged, but perhaps the test in t6024 could be made to be less stringent about order of merge bases. In particular, instead of expecting a certain sha1 to be at stage 2 and a different one to be at stage 3, it could just check that both shas appear in the git ls-files --stage output.
Quoted 14 lines
t/t6024-recursive-merge.sh +1
-1@@ -61,6 +61,7 @@ GIT_AUTHOR_DATE="2006-12-12 23:00:08" git commit -m F ' test_expect_success "combined merge conflicts" " + rm -rf .git/objects/info/commit-graph && test_must_fail git merge -m final G " --2.18.0.118.gd4f65b8d14
v2 cover Clarify commit-graph and grafts/replace/shallow incompatibilities
In reply to Derrick Stolee via GitGitGadgetOne unresolved issue with the commit-graph feature is that it can cause issues when combined with replace objects, commit grafts, or shallow clones. These are not 100% incompatible, as one could be reasonably successful writing a commit-graph after replacing some objects and not have issues. The problems happen when commits that are already in the commit-graph file are replaced, or when git is run with the --no-replace-objects option; this can cause incorrect parents or incorrect generation numbers. Similar things occur with commit grafts and shallow clones, especially when running git fetch --unshallow in a shallow repo.
Instead of trying (and probably failing) to make these features work together, default to making the commit-graph feature unavailable in these situations. Create a new method 'commit_graph_compatible(r)' that checks if the repository 'r' has any of these features enabled.
CHANGES IN V2:
* The first two commits regarding the ref iterators are unchanged, despite a lot of discussion on the subject [1].
* I included Peff's changes in jk/core-use-replace-refs, changing the base commit for the series to 1689c22c1c328e9135ed51458e9f9a5d224c5057 (the merge that brought that topic into 'msater').
* I fixed the tests for the interactions with the graft feature.
Because of the change of base, it is hard to provide a side-by-side diff from v1.
Thanks,
-Stolee
[1] https://public-inbox.org/git/CAGZ79kZ3PzqpGzXWcmxjzi98gA+LT2MBOf8KaA89hOa-Qig=Og@mail.gmail.com/
Stefan's response recommending we keep the first two commits.
Derrick Stolee (6):
commit-graph: update design document
test-repository: properly init repo
commit-graph: not compatible with replace objects
commit-graph: not compatible with grafts
commit-graph: not compatible with uninitialized repo
commit-graph: close_commit_graph before shallow walk
Stefan Beller (2):
refs.c: migrate internal ref iteration to pass thru repository
argument
refs.c: upgrade for_each_replace_ref to be a each_repo_ref_fn callback
Documentation/technical/commit-graph.txt | 18 +++++-- builtin/commit-graph.c | 4 ++ builtin/replace.c | 8 ++-- commit-graph.c | 38 +++++++++++++-- commit-graph.h | 1 + commit.c | 2 +- commit.h | 1 + refs.c | 48 ++++++++++++++++--- refs.h | 12 ++++- refs/iterator.c | 6 +-- refs/refs-internal.h | 5 +- replace-object.c | 7 +-- replace-object.h | 2 + t/helper/test-repository.c | 10 +++- t/t5318-commit-graph.sh | 60 ++++++++++++++++++++++++ upload-pack.c | 2 + 16 files changed, 194 insertions(+), 30 deletions(-)
base-commit: 1689c22c1c328e9135ed51458e9f9a5d224c5057
Signature · 1 line
2.18.0.118.gd4f65b8d14
PATCH v2 6/8 commit-graph: not compatible with grafts
Augment commit_graph_compatible(r) to return false when the given repository r has commit grafts or is a shallow clone. Test that in these situations we ignore existing commit-graph files and we do not write new commit-graph files.
--- commit-graph.c | 6 ++++++ commit.c | 2 +- commit.h | 1 + t/t5318-commit-graph.sh | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-)
commit-graph.c +6 -0
@@ -68,6 +68,12 @@ static int commit_graph_compatible(struct repository *r)return 0 ; } +prepare_commit_graft (r); +if (r->parsed_objects && r->parsed_objects->grafts_nr) +return 0 ; +if (is_repository_shallow (r)) +return 0 ; +return 1 ; }
commit.c +1 -1
@@ -209,7 +209,7 @@ static int read_graft_file(struct repository *r, const char *graft_file)return 0 ; } -static void prepare_commit_graft (struct repository *r) +void prepare_commit_graft (struct repository *r) {char *graft_file;
commit.h +1 -0
@@ -202,6 +202,7 @@ typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);struct commit_graft *read_graft_line (struct strbuf *line);int register_commit_graft (struct repository *r,struct commit_graft *,int ); +void prepare_commit_graft (struct repository *r);struct commit_graft *lookup_commit_graft (struct repository *r,const struct object_id *oid);extern struct commit_list *get_merge_bases (struct commit *rev1,struct commit *rev2);
t/t5318-commit-graph.sh +38 -0
@@ -281,6 +281,44 @@ test_expect_success 'replace-objects invalidates commit-graph' ' ) ' +test_expect_success 'commit grafts invalidate commit-graph' ' + cd "$TRASH_DIRECTORY" && + test_when_finished rm -rf graft && + git clone full graft && + ( + cd graft && + git commit-graph write --reachable && + test_path_is_file .git/objects/info/commit-graph && + H1=$(git rev-parse --verify HEAD~1) && + H3=$(git rev-parse --verify HEAD~3) && + echo "$H1 $H3" >.git/info/grafts && + git -c core.commitGraph=false log >expect && + git -c core.commitGraph=true log >actual && + test_cmp expect actual && + git commit-graph write --reachable && + git -c core.commitGraph=false --no-replace-objects log >expect && + git -c core.commitGraph=true --no-replace-objects log >actual && + test_cmp expect actual && + rm -rf .git/objects/info/commit-graph && + git commit-graph write --reachable && + test_path_is_missing .git/objects/info/commit-graph + ) +' + +test_expect_success 'replace-objects invalidates commit-graph' ' + cd "$TRASH_DIRECTORY" && + test_when_finished rm -rf shallow && + git clone --depth 2 "file://$TRASH_DIRECTORY/full" shallow && + ( + cd shallow && + git commit-graph write --reachable && + test_path_is_missing .git/objects/info/commit-graph && + git fetch origin --unshallow && + git commit-graph write --reachable && + test_path_is_file .git/objects/info/commit-graph + ) +' + # the verify tests below expect the commit-graph to contain # exactly the commits reachable from the commits/8 branch. # If the file changes the set of commits in the list, then the
Signature · 1 line
2.18.0.118.gd4f65b8d14
PATCH v2 7/8 commit-graph: not compatible with uninitialized repo
In reply to Derrick Stolee--- commit-graph.c | 3 +++ 1 file changed, 3 insertions(+)
commit-graph.c +3 -0
@@ -62,6 +62,9 @@ extern int read_replace_refs;static int commit_graph_compatible (struct repository *r) { +if (!r->gitdir) +return 0 ; +if (read_replace_refs) {prepare_replace_object (r);if (hashmap_get_size (&r->objects->replace_map->map))
Signature · 1 line
2.18.0.118.gd4f65b8d14
PATCH v2 2/8 refs.c: upgrade for_each_replace_ref to be a each_repo_ref_fn callback
In reply to Derrick StoleeFrom: Stefan Beller <[email protected]>
--- builtin/replace.c | 8 ++++---- refs.c | 9 ++++----- refs.h | 2 +- replace-object.c | 5 +++-- 4 files changed, 12 insertions(+), 12 deletions(-)
builtin/replace.c +4 -4
@@ -39,7 +39,8 @@ struct show_data {enum replace_format format; }; -static int show_reference (const char *refname,const struct object_id *oid, +static int show_reference (struct repository *r,const char *refname, +const struct object_id *oid,int flag,void *cb_data) {struct show_data *data = cb_data;
@@ -56,9 +57,8 @@ static int show_reference(const char *refname, const struct object_id *oid,if (get_oid (refname, &object))return error ("Failed to resolve '%s' as a valid ref." , refname); - obj_type =oid_object_info (the_repository, &object, -NULL ); - repl_type =oid_object_info (the_repository, oid,NULL ); + obj_type =oid_object_info (r, &object,NULL ); + repl_type =oid_object_info (r, oid,NULL );printf ("%s (%s) -> %s (%s)\n" , refname,type_name (obj_type),oid_to_hex (oid),type_name (repl_type));
refs.c +4 -5
@@ -1474,12 +1474,11 @@ int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,return do_for_each_ref (refs, prefix, fn,0 , flag, cb_data); } -int for_each_replace_ref (struct repository *r,each_ref_fn fn,void *cb_data) +int for_each_replace_ref (struct repository *r,each_repo_ref_fn fn,void *cb_data) { -return do_for_each_ref (get_main_ref_store (r), - git_replace_ref_base, fn, -strlen (git_replace_ref_base), -DO_FOR_EACH_INCLUDE_BROKEN , cb_data); +return do_for_each_repo_ref (r, git_replace_ref_base, fn, +strlen (git_replace_ref_base), +DO_FOR_EACH_INCLUDE_BROKEN , cb_data); }int for_each_namespaced_ref (each_ref_fn fn,void *cb_data)
refs.h +1 -1
@@ -317,7 +317,7 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,int for_each_tag_ref (each_ref_fn fn,void *cb_data);int for_each_branch_ref (each_ref_fn fn,void *cb_data);int for_each_remote_ref (each_ref_fn fn,void *cb_data); -int for_each_replace_ref (struct repository *r,each_ref_fn fn,void *cb_data); +int for_each_replace_ref (struct repository *r,each_repo_ref_fn fn,void *cb_data);int for_each_glob_ref (each_ref_fn fn,const char *pattern,void *cb_data);int for_each_glob_ref_in (each_ref_fn fn,const char *pattern,const char *prefix,void *cb_data);
replace-object.c +3 -2
@@ -6,7 +6,8 @@#include "repository.h" #include "commit.h" -static int register_replace_ref (const char *refname, +static int register_replace_ref (struct repository *r, +const char *refname,const struct object_id *oid,int flag,void *cb_data) {
@@ -25,7 +26,7 @@ static int register_replace_ref(const char *refname,oidcpy (&repl_obj->replacement, oid);/* Register new object */ -if (oidmap_put (the_repository->objects->replace_map, repl_obj)) +if (oidmap_put (r->objects->replace_map, repl_obj))die ("duplicate replace ref: %s" , refname);return 0 ;
Signature · 1 line
2.18.0.118.gd4f65b8d14
PATCH v2 3/8 commit-graph: update design document
In reply to Derrick StoleeAs it exists right now, the commit-graph feature may provide inconsistent results when combined with commit grafts, replace objects, and shallow clones. Update the design document to discuss why these interactions are difficult to reconcile and how we will avoid errors by preventing updates to and reads from the commit-graph file when these other features exist.
--- Documentation/technical/commit-graph.txt | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-)
Documentation/technical/commit-graph.txt +15 -3
@@ -112,12 +112,24 @@ Design Details - The file format includes parameters for the object ID hash function, so a future change of hash algorithm does not require a change in format. +- Commit grafts and replace objects can change the shape of the commit + history. The latter can also be enabled/disabled on the fly using + `--no-replace-objects`. This leads to difficultly storing both possible + interpretations of a commit id, especially when computing generation + numbers. The commit-graph will not be read or written when + replace-objects or grafts are present. + +- Shallow clones create grafts of commits by dropping their parents. This + leads the commit-graph to think those commits have generation number 1. + If and when those commits are made unshallow, those generation numbers + become invalid. Since shallow clones are intended to restrict the commit + history to a very small set of commits, the commit-graph feature is less + helpful for these clones, anyway. The commit-graph will not be read or + written when shallow commits are present. + Future Work ----------- -- The commit graph feature currently does not honor commit grafts. This can - be remedied by duplicating or refactoring the current graft logic. - - After computing and storing generation numbers, we must make graph walks aware of generation numbers to gain the performance benefits they enable. This will mostly be accomplished by swapping a commit-date-ordered
Signature · 1 line
2.18.0.118.gd4f65b8d14
PATCH v2 4/8 test-repository: properly init repo
In reply to Derrick Stolee--- t/helper/test-repository.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
t/helper/test-repository.c +8 -2
@@ -15,7 +15,10 @@ static void test_parse_commit_in_graph(const char *gitdir, const char *worktree,struct commit *c;struct commit_list *parent; -repo_init (&r, gitdir, worktree); +setup_git_env (gitdir); + +if (repo_init (&r, gitdir, worktree)) +die ("Couldn't init repo" ); c =lookup_commit (&r, commit_oid);
@@ -38,7 +41,10 @@ static void test_get_commit_tree_in_graph(const char *gitdir,struct commit *c;struct tree *tree; -repo_init (&r, gitdir, worktree); +setup_git_env (gitdir); + +if (repo_init (&r, gitdir, worktree)) +die ("Couldn't init repo" ); c =lookup_commit (&r, commit_oid);
Signature · 1 line
2.18.0.118.gd4f65b8d14
PATCH v2 5/8 commit-graph: not compatible with replace objects
In reply to Derrick StoleeCreate new method commit_graph_compatible(r) to check if a given repository r is compatible with the commit-graph feature. Fill the method with a check to see if replace-objects exist. Test this interaction succeeds, including ignoring an existing commit-graph and failing to write a new commit-graph. However, we do ensure that we write a new commit-graph by setting read_replace_refs to 0, thereby ignoring the replace refs.
--- builtin/commit-graph.c | 4 ++++ commit-graph.c | 21 +++++++++++++++++++++ replace-object.c | 2 +- replace-object.h | 2 ++ t/t5318-commit-graph.sh | 22 ++++++++++++++++++++++ 5 files changed, 50 insertions(+), 1 deletion(-)
builtin/commit-graph.c +4 -0
@@ -120,6 +120,8 @@ static int graph_read(int argc, const char **argv)return 0 ; } +extern int read_replace_refs; +static int graph_write (int argc,const char **argv) {struct string_list *pack_indexes =NULL ;
@@ -150,6 +152,8 @@ static int graph_write(int argc, const char **argv)if (!opts.obj_dir) opts.obj_dir =get_object_directory (); + read_replace_refs =0 ; +if (opts.reachable) {write_commit_graph_reachable (opts.obj_dir, opts.append);return 0 ;
commit-graph.c +21 -0
@@ -13,6 +13,8 @@#include "commit-graph.h" #include "object-store.h" #include "alloc.h" +#include "hashmap.h" +#include "replace-object.h" #define GRAPH_SIGNATURE 0x43475048/* "CGPH" */ #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446/* "OIDF" */
@@ -56,6 +58,19 @@ static struct commit_graph *alloc_commit_graph(void)return g; } +extern int read_replace_refs; + +static int commit_graph_compatible (struct repository *r) +{ +if (read_replace_refs) { +prepare_replace_object (r); +if (hashmap_get_size (&r->objects->replace_map->map)) +return 0 ; + } + +return 1 ; +} +struct commit_graph *load_commit_graph_one (const char *graph_file) {void *graph_map;
@@ -223,6 +238,9 @@ static int prepare_commit_graph(struct repository *r)*/ return 0 ; +if (!commit_graph_compatible (r)) +return 0 ; + obj_dir = r->objects->objectdir;prepare_commit_graph_one (r, obj_dir);prepare_alt_odb (r);
@@ -693,6 +711,9 @@ void write_commit_graph(const char *obj_dir,int num_extra_edges;struct commit_list *parent; +if (!commit_graph_compatible (the_repository)) +return ; + oids.nr =0 ; oids.alloc =approximate_object_count () /4 ;
replace-object.c +1 -1
@@ -32,7 +32,7 @@ static int register_replace_ref(struct repository *r,return 0 ; } -static void prepare_replace_object (struct repository *r) +void prepare_replace_object (struct repository *r) {if (r->objects->replace_map)return ;
replace-object.h +2 -0
@@ -10,6 +10,8 @@ struct replace_object {struct object_id replacement; }; +void prepare_replace_object (struct repository *r); +/* * This internal function is only declared here for the benefit of * lookup_replace_object(). Please do not call it directly.
t/t5318-commit-graph.sh +22 -0
@@ -259,6 +259,28 @@ test_expect_success 'check that gc computes commit-graph' ' test_cmp commit-graph-after-gc $objdir/info/commit-graph ' +test_expect_success 'replace-objects invalidates commit-graph' ' + cd "$TRASH_DIRECTORY" && + test_when_finished rm -rf replace && + git clone full replace && + ( + cd replace && + git commit-graph write --reachable && + test_path_is_file .git/objects/info/commit-graph && + git replace HEAD~1 HEAD~2 && + git -c core.commitGraph=false log >expect && + git -c core.commitGraph=true log >actual && + test_cmp expect actual && + git commit-graph write --reachable && + git -c core.commitGraph=false --no-replace-objects log >expect && + git -c core.commitGraph=true --no-replace-objects log >actual && + test_cmp expect actual && + rm -rf .git/objects/info/commit-graph && + git commit-graph write --reachable && + test_path_is_file .git/objects/info/commit-graph + ) +' + # the verify tests below expect the commit-graph to contain # exactly the commits reachable from the commits/8 branch. # If the file changes the set of commits in the list, then the
Signature · 1 line
2.18.0.118.gd4f65b8d14
PATCH v2 8/8 commit-graph: close_commit_graph before shallow walk
In reply to Derrick StoleeCall close_commit_graph() when about to start a rev-list walk that includes shallow commits. This is necessary in code paths that "fake" shallow commits for the sake of fetch. Specifically, test 351 in t5500-fetch-pack.sh runs
git fetch --shallow-exclude one origin
with a file-based transfer. When the "remote" has a commit-graph, we do not prevent the commit-graph from being loaded, but then the commits are intended to be dynamically transferred into shallow commits during get_shallow_commits_by_rev_list(). By closing the commit-graph before this call, we prevent this interaction.
--- commit-graph.c | 8 ++++---- commit-graph.h | 1 + upload-pack.c | 2 ++ 3 files changed, 7 insertions(+), 4 deletions(-)
commit-graph.c +4 -4
@@ -260,10 +260,10 @@ static int prepare_commit_graph(struct repository *r)return !!r->objects->commit_graph; } -static void close_commit_graph (void ) +void close_commit_graph (struct repository *r) { -free_commit_graph (the_repository->objects->commit_graph); - the_repository->objects->commit_graph =NULL ; +free_commit_graph (r->objects->commit_graph); + r->objects->commit_graph =NULL ; }static int bsearch_graph (struct commit_graph *g,struct object_id *oid,uint32_t *pos)
@@ -875,7 +875,7 @@ void write_commit_graph(const char *obj_dir,write_graph_chunk_data (f,GRAPH_OID_LEN , commits.list, commits.nr);write_graph_chunk_large_edges (f, commits.list, commits.nr); -close_commit_graph (); +close_commit_graph (the_repository);finalize_hashfile (f,NULL ,CSUM_HASH_IN_STREAM |CSUM_FSYNC );commit_lock_file (&lk);
commit-graph.h +1 -0
@@ -59,6 +59,7 @@ void write_commit_graph(const char *obj_dir,int verify_commit_graph (struct repository *r,struct commit_graph *g); +void close_commit_graph (struct repository *);void free_commit_graph (struct commit_graph *);#endif
upload-pack.c +2 -0
@@ -24,6 +24,7 @@#include "quote.h" #include "upload-pack.h" #include "serve.h" +#include "commit-graph.h" /* Remember to update object flag allocation in object.h */ #define THEY_HAVE (1u << 11)
@@ -740,6 +741,7 @@ static void deepen_by_rev_list(int ac, const char **av, {struct commit_list *result; +close_commit_graph (the_repository); result =get_shallow_commits_by_rev_list (ac, av,SHALLOW ,NOT_SHALLOW );send_shallow (result);free_commit_list (result);
Signature · 1 line
2.18.0.118.gd4f65b8d14
PATCH v2 1/8 refs.c: migrate internal ref iteration to pass thru repository argument
In reply to Derrick StoleeFrom: Stefan Beller <[email protected]>
In 60ce76d3581 (refs: add repository argument to for_each_replace_ref, 2018-04-11) and 0d296c57aec (refs: allow for_each_replace_ref to handle arbitrary repositories, 2018-04-11), for_each_replace_ref learned how to iterate over refs by a given arbitrary repository.
New attempts in the object store conversion have shown that it is useful to have the repository handle available that the refs iteration is currently iterating over.
To achieve this goal we will need to add a repository argument to each_ref_fn in refs.h. However as many callers rely on the signature such a patch would be too large.
So convert the internals of the ref subsystem first to pass through a repository argument without exposing the change to the user. Assume the_repository for the passed through repository, although it is not used anywhere yet.
--- refs.c | 39 +++++++++++++++++++++++++++++++++++++-- refs.h | 10 ++++++++++ refs/iterator.c | 6 +++--- refs/refs-internal.h | 5 +++-- 4 files changed, 53 insertions(+), 7 deletions(-)
refs.c +37 -2
@@ -1386,17 +1386,50 @@ struct ref_iterator *refs_ref_iterator_begin(* non-zero value, stop the iteration and return that value; * otherwise, return 0. */ +static int do_for_each_repo_ref (struct repository *r,const char *prefix, +each_repo_ref_fn fn,int trim,int flags, +void *cb_data) +{ +struct ref_iterator *iter; +struct ref_store *refs =get_main_ref_store (r); + +if (!refs) +return 0 ; + + iter =refs_ref_iterator_begin (refs, prefix, trim, flags); + +return do_for_each_repo_ref_iterator (r, iter, fn, cb_data); +} + +struct do_for_each_ref_help { +each_ref_fn *fn; +void *cb_data; +}; + +static int do_for_each_ref_helper (struct repository *r, +const char *refname, +const struct object_id *oid, +int flags, +void *cb_data) +{ +struct do_for_each_ref_help *hp = cb_data; + +return hp->fn (refname, oid, flags, hp->cb_data); +} +static int do_for_each_ref (struct ref_store *refs,const char *prefix,each_ref_fn fn,int trim,int flags,void *cb_data) {struct ref_iterator *iter; +struct do_for_each_ref_help hp = { fn, cb_data };if (!refs)return 0 ; iter =refs_ref_iterator_begin (refs, prefix, trim, flags); -return do_for_each_ref_iterator (iter, fn, cb_data); +return do_for_each_repo_ref_iterator (the_repository, iter, + do_for_each_ref_helper, &hp); }int refs_for_each_ref (struct ref_store *refs,each_ref_fn fn,void *cb_data)
@@ -2025,10 +2058,12 @@ int refs_verify_refname_available(struct ref_store *refs,int refs_for_each_reflog (struct ref_store *refs,each_ref_fn fn,void *cb_data) {struct ref_iterator *iter; +struct do_for_each_ref_help hp = { fn, cb_data }; iter = refs->be->reflog_iterator_begin (refs); -return do_for_each_ref_iterator (iter, fn, cb_data); +return do_for_each_repo_ref_iterator (the_repository, iter, + do_for_each_ref_helper, &hp); }int for_each_reflog (each_ref_fn fn,void *cb_data)
refs.h +10 -0
@@ -274,6 +274,16 @@ struct ref_transaction;typedef int each_ref_fn (const char *refname,const struct object_id *oid,int flags,void *cb_data); +/* +* The same as each_ref_fn, but also with a repository argument that +* contains the repository associated with the callback. +*/ +typedef int each_repo_ref_fn (struct repository *r, +const char *refname, +const struct object_id *oid, +int flags, +void *cb_data); +/* * The following functions invoke the specified callback function for * each reference indicated. If the function ever returns a nonzero
refs/iterator.c +3 -3
@@ -407,15 +407,15 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,struct ref_iterator *current_ref_iter =NULL ; -int do_for_each_ref_iterator (struct ref_iterator *iter, -each_ref_fn fn,void *cb_data) +int do_for_each_repo_ref_iterator (struct repository *r,struct ref_iterator *iter, +each_repo_ref_fn fn,void *cb_data) {int retval =0 , ok;struct ref_iterator *old_ref_iter = current_ref_iter; current_ref_iter = iter;while ((ok =ref_iterator_advance (iter)) ==ITER_OK ) { - retval =fn (iter->refname, iter->oid, iter->flags, cb_data); + retval =fn (r, iter->refname, iter->oid, iter->flags, cb_data);if (retval) {/* * If ref_iterator_abort() returns ITER_ERROR,
refs/refs-internal.h +3 -2
@@ -474,8 +474,9 @@ extern struct ref_iterator *current_ref_iter;* adapter between the callback style of reference iteration and the * iterator style. */ -int do_for_each_ref_iterator (struct ref_iterator *iter, -each_ref_fn fn,void *cb_data); +int do_for_each_repo_ref_iterator (struct repository *r, +struct ref_iterator *iter, +each_repo_ref_fn fn,void *cb_data);/* * Only include per-worktree refs in a do_for_each_ref*() iteration.
Signature · 1 line
2.18.0.118.gd4f65b8d14
Quoted 2 lines · Derrick Stolee
Because of the change of base, it is hard to provide a side-by-side diff from v1.
I thought that was the whole point of range-diff. ;-)
I'll take a look.
Thanks,
Stefan
Quoted 28 lines · Derrick Stolee
One unresolved issue with the commit-graph feature is that it can cause issues when combined with replace objects, commit grafts, or shallow clones. These are not 100% incompatible, as one could be reasonably successful writing a commit-graph after replacing some objects and not have issues. The problems happen when commits that are already in the commit-graph file are replaced, or when git is run with the
--no-replace-objectsoption; this can cause incorrect parents or incorrect generation numbers. Similar things occur with commit grafts and shallow clones, especially when runninggit fetch --unshallowin a shallow repo.Instead of trying (and probably failing) to make these features work together, default to making the commit-graph feature unavailable in these situations. Create a new method '
commit_graph_compatible(r)' that checks if the repository 'r' has any of these features enabled.CHANGES IN V2:
* The first two commits regarding the ref iterators are unchanged, despite a lot of discussion on the subject [1].
* I included Peff's changes in jk/core-use-replace-refs, changing the base commit for the series to
1689c22c1c328e9135ed51458e9f9a5d224c5057(the merge that brought that topic into 'msater').* I fixed the tests for the interactions with the graft feature.
Quoted 9 lines
Because of the change of base, it is hard to provide a side-by-side diff from v1.
Thanks,
-Stolee[1] https://public-inbox.org/git/CAGZ79kZ3PzqpGzXWcmxjzi98gA+LT2MBOf8KaA89hOa-Qig=Og@mail.gmail.com/
Stefan's response recommending we keep the first two commits.
After reviewing my own patches, I flipped again (Sorry!) and would rather not see my patches be merged, but the very original solution by you, that you proposed at [1]. That said, I will not insist on it, and if this is merged as is, we can fix it up later.
With that said, I just read through the remaining patches, I think they are a valuable addition to Git and could be merged as-is.
[1] https://github.com/gitgitgadget/git/pull/11/commits/300db80140dacc927db0d46c804ca0ef4dcc1be1
Thanks,
Stefan
Quoted 42 lines · Stefan Beller
Quoted 27 lines · Derrick Stolee
One unresolved issue with the commit-graph feature is that it can cause issues when combined with replace objects, commit grafts, or shallow clones. These are not 100% incompatible, as one could be reasonably successful writing a commit-graph after replacing some objects and not have issues. The problems happen when commits that are already in the commit-graph file are replaced, or when git is run with the
--no-replace-objectsoption; this can cause incorrect parents or incorrect generation numbers. Similar things occur with commit grafts and shallow clones, especially when runninggit fetch --unshallowin a shallow repo.Instead of trying (and probably failing) to make these features work together, default to making the commit-graph feature unavailable in these situations. Create a new method '
commit_graph_compatible(r)' that checks if the repository 'r' has any of these features enabled.CHANGES IN V2:
* The first two commits regarding the ref iterators are unchanged, despite a lot of discussion on the subject [1].
* I included Peff's changes in jk/core-use-replace-refs, changing the base commit for the series to
1689c22c1c328e9135ed51458e9f9a5d224c5057(the merge that brought that topic into 'msater').* I fixed the tests for the interactions with the graft feature.
Quoted 9 lines
Because of the change of base, it is hard to provide a side-by-side diff from v1.
Thanks,
-Stolee[1] https://public-inbox.org/git/CAGZ79kZ3PzqpGzXWcmxjzi98gA+LT2MBOf8KaA89hOa-Qig=Og@mail.gmail.com/
Stefan's response recommending we keep the first two commits.After reviewing my own patches, I flipped again (Sorry!) and would rather not see my patches be merged, but the very original solution by you, that you proposed at [1]. That said, I will not insist on it, and if this is merged as is, we can fix it up later.
Quoted 7 lines
With that said, I just read through the remaining patches, I think they are a valuable addition to Git and could be merged as-is.
[1] https://github.com/gitgitgadget/git/pull/11/commits/300db80140dacc927db0d46c804ca0ef4dcc1be1
Thanks,
Stefan
Just to keep things on-list as possible, here is the patch for the commit linked above. It would replace patches 1 & 2 from this series.
Junio: I can send a v3 that includes this commit if you need it, or if there are other edits to be made in this series.
commit 300db80140dacc927db0d46c804ca0ef4dcc1be1
Author: Derrick Stolee <[email protected]>
Date: Fri Jul 20 15:39:06 2018 -0400
replace-objects: use arbitrary repositories
This is the smallest possible change that makes prepare_replace_objects work properly with arbitrary repositories. By supplying the repository as the cb_data, we do not need to modify any code in the ref iterator logic. We will likely want to do a full replacement of the ref iterator logic to provide a repository struct as a concrete parameter.
replace-object.c +0 -0
@@ -14,6 +14,7 @@ static int register_replace_ref(const char *refname,
const char *slash = strrchr(refname, '/');
const char *hash = slash ? slash + 1 : refname; struct replace_object *repl_obj = xmalloc(sizeof(*repl_obj)); + struct repository *r = (struct repository *)cb_data;
if (get_oid_hex(hash, &repl_obj->original.oid)) { free(repl_obj);
@@ -25,7 +26,7 @@ static int register_replace_ref(const char *refname, oidcpy(&repl_obj->replacement, oid);
/* Register new object */
- if (oidmap_put(the_repository->objects->replace_map, repl_obj)) + if (oidmap_put(r->objects->replace_map, repl_obj)) die("duplicate replace ref: %s", refname);
return 0;
@@ -40,7 +41,7 @@ static void prepare_replace_object(struct repository *r) xmalloc(sizeof(*r->objects->replace_map)); oidmap_init(r->objects->replace_map, 0);
- for_each_replace_ref(r, register_replace_ref, NULL); + for_each_replace_ref(r, register_replace_ref, r); }
/* We allow "recursive" replacement. Only within reason, though */
Re: [PATCH v2 5/8] commit-graph: not compatible with replace objects
In reply to Derrick StoleeQuoted 22 lines · Derrick Stolee
Create new method
commit_graph_compatible(r)to check if a given repository r is compatible with the commit-graph feature. Fill the method with a check to see if replace-objects exist. Test this interaction succeeds, including ignoring an existing commit-graph and failing to write a new commit-graph. However, we do ensure that we write a new commit-graph by settingread_replace_refsto 0, thereby ignoring the replace refs.Signed-off-by Derrick Stolee <[email protected]>--- builtin/commit-graph.c | 4 ++++ commit-graph.c | 21 +++++++++++++++++++++ replace-object.c | 2 +- replace-object.h | 2 ++ t/t5318-commit-graph.sh | 22 ++++++++++++++++++++++ 5 files changed, 50 insertions(+), 1 deletion(-)builtin/commit-graph.c +0
-0
builtin/commit-graph.c static int graph_read(int argc, const char **argv) · line 124
Quoted 5 lines
builtin/commit-graph.c +2
-0@@ -120,6 +120,8 @@ static int graph_read(int argc, const char **argv)return 0 ; } +extern int read_replace_refs; +
Why do you need this (and also in commit-graph.c)? I thought cache.h includes it, which you can just make use of it.
Quoted 16 lines
builtin/commit-graph.c +10
-0+static int commit_graph_compatible (struct repository *r) +{ +if (read_replace_refs) { +prepare_replace_object (r); +if (hashmap_get_size (&r->objects->replace_map->map)) +return 0 ; + } + +return 1 ; +}replace-object.c +0
-0
replace-object.c static int register_replace_ref(struct repository *r, · line 38
Quoted 8 lines
replace-object.c +1
-1@@ -32,7 +32,7 @@ static int register_replace_ref(struct repository *r,return 0 ; } -static void prepare_replace_object (struct repository *r) +void prepare_replace_object (struct repository *r) {if (r->objects->replace_map)return ;
The way the new caller is written, I am wondering if this function should return "we are (or, are not) using the replace object feature", making it unnecessary for callers on the reading side to know about "read-replace-refs" external variable, for example.
/*
* To be called on-demand from codepaths that want to make
* sure that replacement objects can be found as necessary.
*
* Return number of replacement defined for the repository, or
* -1 when !read_replace_refs tells us not to use replacement
* mechanism at all.
*/
int prepare_replace_object(struct repository *r)
{
if (!read_replace_refs)
return -1;
if (!r->objects->replace_map) {
r->objects->replace_map =
xmalloc(...);
oidmap_init(r->objects->replace_map, 0);
for_each_refplace_ref(r, register_...);
}
return hashmap_get_size(&r->objects->replace_map->map);
}Then, the caller side can simply become something like:
#define cgraph_compat(r) (prepare_replace_object(r) <= 0)
There are various writers to read_replace_refs variable, but I think they should first be replaced with calls to something like:
void use_replace_refs(struct repository *r, int enable);
which allows us to hide the global variable and later make it per repository if we wanted to.
Quoted 5 lines · Stefan Beller
Quoted 2 lines · Derrick Stolee
Because of the change of base, it is hard to provide a side-by-side diff from v1.
I thought that was the whole point of range-diff. ;-)
I thought so, too. Was there any change that confused range-diff machinery?
Quoted 8 lines · Junio C Hamano
Quoted 4 lines · Stefan Beller
Quoted 2 lines · Derrick Stolee
Because of the change of base, it is hard to provide a side-by-side diff from v1.
I thought that was the whole point of range-diff. ;-)
I thought so, too. Was there any change that confused range-diff machinery?
I've just been out of the loop and didn't realize what range-diff did. Here is the range-diff:
1: 43ddcc9ef9 = 1: c8edae4179 refs.c: migrate internal ref iteration to pass thru repository argument 2: 22dc9ce836 = 2: f89451e884 refs.c: upgrade for_each_replace_ref to be a each_repo_ref_fn callback 3: 5e90d36f84 ! 3: d95aa3472c commit-graph: update design document
@@ -19,7 +19,7 @@
so a future change of hash algorithm does not require a change in format.
+- Commit grafts and replace objects can change the shape of the commit -+ history. These can also be enabled/disabled on the fly using ++ history. The latter can also be enabled/disabled on the fly using + --no-replace-objects. This leads to difficultly storing both possible + interpretations of a commit id, especially when computing generation + numbers. The commit-graph will not be read or written when 4: 88711a3cf4 = 4: ec89c88e14 test-repository: properly init repo 5: 7f596c1718 ! 5: 0321a3cf10 commit-graph: not compatible with replace objects @@ -6,10 +6,34 @@
repository r is compatible with the commit-graph feature. Fill the method with a check to see if replace-objects exist. Test this interaction succeeds, including ignoring an existing commit-graph and - failing to write a new commit-graph.
+ failing to write a new commit-graph. However, we do ensure that + we write a new commit-graph by setting read_replace_refs to 0, thereby + ignoring the replace refs.
+diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c +--- a/builtin/commit-graph.c
++++ b/builtin/commit-graph.c
+@@
+ return 0;
+ }
+
++extern int read_replace_refs;
++
+ static int graph_write(int argc, const char **argv)
+ {
+ struct string_list *pack_indexes = NULL;
+@@
+ if (!opts.obj_dir)
+ opts.obj_dir = get_object_directory();
+
++ read_replace_refs = 0;
++
+ if (opts.reachable) {
+ write_commit_graph_reachable(opts.obj_dir, opts.append); + return 0;
+
diff --git a/commit-graph.c b/commit-graph.c
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -26,11 +50,15 @@
return g;
}
++extern int read_replace_refs;
++
+static int commit_graph_compatible(struct repository *r)
+{
-+ prepare_replace_object(r);
-+ if (hashmap_get_size(&r->objects->replace_map->map))
-+ return 0;
++ if (read_replace_refs) {
++ prepare_replace_object(r);
++ if (hashmap_get_size(&r->objects->replace_map->map)) ++ return 0;
++ }
+
+ return 1;
+}
@@ -110,7 +138,7 @@
+ test_cmp expect actual &&
+ rm -rf .git/objects/info/commit-graph &&
+ git commit-graph write --reachable &&
-+ test_path_is_missing .git/objects/info/commit-graph ++ test_path_is_file .git/objects/info/commit-graph
+ )
+'
+
6: 94dd91ac35 ! 6: d18ecc0124 commit-graph: not compatible with grafts @@ -7,24 +7,25 @@
situations we ignore existing commit-graph files and we do not write new commit-graph files.
+ Helped-by: Jakub Narebski <[email protected]>
diff --git a/commit-graph.c b/commit-graph.c
--- a/commit-graph.c
+++ b/commit-graph.c
@@
+ return 0;
+ }
- static int commit_graph_compatible(struct repository *r)
- {
+ prepare_commit_graft(r);
+ if (r->parsed_objects && r->parsed_objects->grafts_nr) + return 0;
+ if (is_repository_shallow(r))
+ return 0;
+
- prepare_replace_object(r);
- if (hashmap_get_size(&r->objects->replace_map->map)) - return 0;
+ return 1;
+ }
+
diff --git a/commit.c b/commit.c
--- a/commit.c
@@ -66,7 +67,9 @@
+ cd graft &&
+ git commit-graph write --reachable &&
+ test_path_is_file .git/objects/info/commit-graph &&
-+ git replace --graft HEAD~1 HEAD~3 &&
++ H1=$(git rev-parse --verify HEAD~1) &&
++ H3=$(git rev-parse --verify HEAD~3) &&
++ echo "$H1 $H3" >.git/info/grafts &&
+ git -c core.commitGraph=false log >expect &&
+ git -c core.commitGraph=true log >actual &&
+ test_cmp expect actual &&
7: 5314a5a93d ! 7: 87d3397700 commit-graph: not compatible with uninitialized repo @@ -14,6 +14,6 @@
+ if (!r->gitdir)
+ return 0;
+
- prepare_commit_graft(r);
- if (r->parsed_objects && r->parsed_objects->grafts_nr)
- return 0;
+ if (read_replace_refs) {
+ prepare_replace_object(r);
+ if (hashmap_get_size(&r->objects->replace_map->map)) 8: f4ab234ed2 ! 8: a26c175897 commit-graph: close_commit_graph before shallow walk @@ -2,8 +2,6 @@
commit-graph: close_commit_graph before shallow walk
- Make close_commit_graph() work for arbitrary repositories. -
Call close_commit_graph() when about to start a rev-list walk that includes shallow commits. This is necessary in code paths that "fake" shallow commits for the sake of fetch. Specifically, test 351 in
Re: [PATCH v2 5/8] commit-graph: not compatible with replace objects
In reply to Junio C HamanoQuoted 27 lines · Junio C Hamano
Quoted 25 lines · Derrick Stolee
Create new method
commit_graph_compatible(r)to check if a given repository r is compatible with the commit-graph feature. Fill the method with a check to see if replace-objects exist. Test this interaction succeeds, including ignoring an existing commit-graph and failing to write a new commit-graph. However, we do ensure that we write a new commit-graph by settingread_replace_refsto 0, thereby ignoring the replace refs.Signed-off-by Derrick Stolee <[email protected]>--- builtin/commit-graph.c | 4 ++++ commit-graph.c | 21 +++++++++++++++++++++ replace-object.c | 2 +- replace-object.h | 2 ++ t/t5318-commit-graph.sh | 22 ++++++++++++++++++++++ 5 files changed, 50 insertions(+), 1 deletion(-)builtin/commit-graph.c +0
-0@@ -120,6 +120,8 @@ static int graph_read(int argc, const char **argv)return 0 ; }
Quoted 4 lines
builtin/commit-graph.c static int graph_read(int argc, const char **argv) · line 124
Quoted 2 lines
builtin/commit-graph.c +2
-0+extern int read_replace_refs; +Why do you need this (and also in
commit-graph.c)? I thoughtcache.hincludes it, which you can just make use of it.
You're right. I don't know how I missed that.
Quoted 41 lines
Quoted 15 lines
builtin/commit-graph.c +10
-0+static int commit_graph_compatible (struct repository *r) +{ +if (read_replace_refs) { +prepare_replace_object (r); +if (hashmap_get_size (&r->objects->replace_map->map)) +return 0 ; + } + +return 1 ; +}replace-object.c +0
-0replace-object.c static int register_replace_ref(struct repository *r, · line 38
Quoted 8 lines
replace-object.c +1
-1@@ -32,7 +32,7 @@ static int register_replace_ref(struct repository *r,return 0 ; } -static void prepare_replace_object (struct repository *r) +void prepare_replace_object (struct repository *r) {if (r->objects->replace_map)return ;The way the new caller is written, I am wondering if this function should return "we are (or, are not) using the replace object feature", making it unnecessary for callers on the reading side to know about "read-replace-refs" external variable, for example.
/* * To be called on-demand from codepaths that want to make * sure that replacement objects can be found as necessary. * * Return number of replacement defined for the repository, or * -1 when !read_replace_refs tells us not to use replacement * mechanism at all. */ int prepare_replace_object(struct repository *r) { if (!read_replace_refs) return -1;
Quoted 8 lines
if (!r->objects->replace_map) {
r->objects->replace_map =
xmalloc(...);
oidmap_init(r->objects->replace_map, 0);
for_each_refplace_ref(r, register_...);
}
return hashmap_get_size(&r->objects->replace_map->map);
}This is a good idea. I can incorporate it into v3.
Quoted 3 lines
Then, the caller side can simply become something like:
#define cgraph_compat(r) (prepare_replace_object(r) <= 0)
With the next two patches that add more conditions to commit_graph_compatible(), I'd prefer keeping it a method instead of a macro.
Quoted 7 lines
There are various writers to
read_replace_refsvariable, but I think they should first be replaced with calls to something like:void use_replace_refs(struct repository *r, int enable);which allows us to hide the global variable and later make it per repository if we wanted to.
I will incorporate this into v3 as well.
Thanks,
-Stolee