builtin/rerere: convert to use subcommands
cover builtin/rerere: convert to use subcommands
Hi,
while reviewing some patch series around git-rerere(1) recently I was a bit appalled by our custom subcommand parsing that we do here. This patch series here thus cleans up and modernizes that part a bit so that git-rerere(1) behaves like every other Git command that happens to have subcommands.
This series is based on 37b3475fb53 (The 9th batch, 2026-10-25) with ps/rerere-remove-global-state at b948c10c8b3 (rerere: get rid of global rerere cache state, 2026-10-23) merged into it.
Thanks!
Patrick
---
Patrick Steinhardt (12):
rerere: rename repo_rerere()
rerere: convert flags into an enum
builtin/rerere: drop the_repository
builtin/rerere: stop passing flags to subcommands that ignore them builtin/rerere: turn "forget" into a proper subcommand builtin/rerere: turn "clear" into a proper subcommand builtin/rerere: turn "status" into a proper subcommand builtin/rerere: turn "remaining" into a proper subcommand builtin/rerere: turn "diff" into a proper subcommand builtin/rerere: turn "gc" into a proper subcommand builtin/rerere: use OPT_SUBCOMMAND()
builtin/rerere: introduce proper usage strings for subcommands
Documentation/git-rerere.adoc | 8 +- apply.c | 2 +- builtin/am.c | 4 +- builtin/commit.c | 2 +- builtin/merge.c | 2 +- builtin/rerere.c | 319 ++++++++++++++++++++++++++++++++---------- builtin/stash.c | 2 +- rerere.c | 7 +- rerere.h | 21 +-- sequencer.c | 4 +- t/t4200-rerere.sh | 5 - 11 files changed, 275 insertions(+), 101 deletions(-)
---
base-commit: 710910c03a22ac663885049213b9fef5853ea8e0
change-id: 20261027-pks-rerere-subcommands-318d41553438
PATCH 1/12 rerere: rename `repo_rerere()`
The repo_rerere() function is the only function in "rerere.h" that does not have a rerere_ prefix. And while it does indeed receive a repository as first parameter, that's not a good-enough reason to give it a repo_ prefix.
Rename the function to rerere_record() to be consistent with all the other functions.
--- apply.c | 2 +- builtin/am.c | 4 ++-- builtin/commit.c | 2 +- builtin/merge.c | 2 +- builtin/rerere.c | 2 +- builtin/stash.c | 2 +- rerere.c | 2 +- rerere.h | 2 +- sequencer.c | 4 ++-- 9 files changed, 11 insertions(+), 11 deletions(-)
apply.c +1 -1
@@ -4866,7 +4866,7 @@ static int write_out_results(struct apply_state *state, struct patch *list)* tree with conflict markers, but that isn't written with --cached. */ if (!state->cached) -repo_rerere (state->repo,0 ); +rerere_record (state->repo,0 ); }return errs;
builtin/am.c +2 -2
@@ -1649,7 +1649,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa o.verbosity =0 ;if (merge_ort_generic (&o, &our_tree, &their_tree,1 , bases, &result)) { -repo_rerere (the_repository, state->allow_rerere_autoupdate); +rerere_record (the_repository, state->allow_rerere_autoupdate);free (their_tree_name);return error (_ ("Failed to merge in the changes." )); }
@@ -1979,7 +1979,7 @@ static void am_resolve(struct am_state *state, int allow_empty) goto next; } -repo_rerere (the_repository,0 ); +rerere_record (the_repository,0 );do_commit (state);
builtin/commit.c +1 -1
@@ -1962,7 +1962,7 @@ int cmd_commit(int argc,git_test_write_commit_graph_or_die (the_repository); -repo_rerere (the_repository,0 ); +rerere_record (the_repository,0 );run_auto_maintenance (the_repository, quiet);run_commit_hook (use_editor,repo_get_index_file (the_repository),NULL ,"post-commit" ,NULL );
builtin/merge.c +1 -1
@@ -1061,7 +1061,7 @@ static int suggest_conflicts(void)fputs (msgbuf.buf, fp);strbuf_release (&msgbuf);fclose (fp); -repo_rerere (the_repository, allow_rerere_auto); +rerere_record (the_repository, allow_rerere_auto);printf (_ ("Automatic merge failed; " "fix conflicts and then commit the result.\n" ));return 1 ;
builtin/rerere.c +1 -1
@@ -83,7 +83,7 @@ int cmd_rerere(int argc, flags =RERERE_NOAUTOUPDATE ;if (argc <1 ) -return repo_rerere (the_repository, flags); +return rerere_record (the_repository, flags);if (!strcmp (argv[0 ],"forget" )) {struct pathspec pathspec;
builtin/stash.c +1 -1
@@ -688,7 +688,7 @@ static enum stash_apply_result do_apply_stash(const char *prefix, ret =error (_ ("could not write index" ));if (ret) { -repo_rerere (the_repository,0 ); +rerere_record (the_repository,0 );if (index)fprintf_ln (stderr,_ ("Index was not unstashed." ));
rerere.c +1 -1
@@ -956,7 +956,7 @@ int rerere_cache_setup(struct rerere_cache *cache,* perform mergy operations, possibly leaving conflicted index entries * and working tree files. */ -int repo_rerere (struct repository *r,int flags) +int rerere_record (struct repository *r,int flags) {struct string_list merge_rr =STRING_LIST_INIT_DUP ;struct rerere_cache cache;
rerere.h +1 -1
@@ -45,7 +45,7 @@ int rerere_cache_setup(struct rerere_cache *cache, int flags);void rerere_cache_release (struct rerere_cache *cache); -int repo_rerere (struct repository *,int ); +int rerere_record (struct repository *,int );/* * Given the conflict ID and the name of a "file" used for replaying * the recorded resolution (e.g. "preimage", "postimage"), return the
sequencer.c +2 -2
@@ -2556,7 +2556,7 @@ static enum pick_result do_pick_commit(struct repository *r, : _("could not apply %s... %s"), short_commit_name(r, commit), msg.subject);print_advice (r, res ==1 , opts); -repo_rerere (r, opts->allow_rerere_auto); +rerere_record (r, opts->allow_rerere_auto); goto leave; }
@@ -4488,7 +4488,7 @@ static int do_merge(struct repository *r,rollback_lock_file (&lock);if (ret) -repo_rerere (r, opts->allow_rerere_auto); +rerere_record (r, opts->allow_rerere_auto);else /* * In case of problems, we now want to return a positive
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
Re: [PATCH 01/12] rerere: rename `repo_rerere()`
Quoted 7 lines · Patrick Steinhardt
The
repo_rerere()function is the only function in "rerere.h" that does not have arerere_prefix. And while it does indeed receive a repository as first parameter, that's not a good-enough reason to give it arepo_prefix.Rename the function to
rerere_record()to be consistent with all the other functions.
I do not think the convention to give "repo_" prefix to variants of functions that worked only on the_repository should stay with us forever, and losing repo prefix from this function would be an excellent first move towards that future.
I howevr take isseus with the verb "record" in its new name. What the function does is not just record. It autonomously finds out what situation it is in and does the logical next thing, which varies. It resolves when a recorded resolution exist. it records when seeing a particular conflict for the first time. There was no single good verb to express that. Perhaps "to rerere" or "to perform rerere" can be invented as a verb to describe such an intelligent operation?
Re: [PATCH 01/12] rerere: rename `repo_rerere()`
Quoted 15 lines · Junio C Hamano
Quoted 7 lines · Patrick Steinhardt
The
repo_rerere()function is the only function in "rerere.h" that does not have arerere_prefix. And while it does indeed receive a repository as first parameter, that's not a good-enough reason to give it arepo_prefix.Rename the function to
rerere_record()to be consistent with all the other functions.I do not think the convention to give "
repo_" prefix to variants of functions that worked only onthe_repositoryshould stay with us forever, and losing repo prefix from this function would be an excellent first move towards that future.
Quoted 8 lines
I howevr take isseus with the verb "record" in its new name. What the function does is not just record. It autonomously finds out what situation it is in and does the logical next thing, which varies. It resolves when a recorded resolution exist. it records when seeing a particular conflict for the first time. There was no single good verb to express that. Perhaps "to rerere" or "to perform rerere" can be invented as a verb to describe such an intelligent operation?
I was picking "record" because that's also what git-rerere(1) says:
This command assists the developer in this process by recording conflicted automerge results and corresponding hand resolve results on the initial manual merge, and applying previously recorded hand resolutions to their corresponding automerge results.
But I guess your point is valid, as we don't only do that, but also update merge conflicts locally. I'd pick your "perform" proposal in that case and rename it to rerere_perform() instead. That name is somewhat vague, but I guess that cannot be helped because it actually does a bunch of things at once.
I guess that can be helped with documentation though.
Patrick
Re: [PATCH 01/12] rerere: rename `repo_rerere()`
Quoted 9 lines · Patrick Steinhardt
I was picking "record" because that's also what git-rerere(1) says:
This command assists the developer in this process by recording conflicted automerge results and corresponding hand resolve results on the initial manual merge, and applying previously recorded hand resolutions to their corresponding automerge results.
But I guess your point is valid, as we don't only do that, but also update merge conflicts locally. I'd pick your "perform" proposal in that
Yup, the description above says "recording" and "applying".
Quoted 3 lines
case and rename it to
rerere_perform()instead. That name is somewhat vague, but I guess that cannot be helped because it actually does a bunch of things at once.
Let's just call it rerere(), then.
It was the original name for the operation before it was renamed in 35843b1123 (rerere.c: remove implicit dependency on the_index, 2018-09-21), which taught it to take an instance of a repository structure.
Going back to that original name is in line with what I said earlier. The "repo_" prefix is a mistaken trend, and once everything becomes free from the_repository, it would become redundant and unnecessary.
Thanks.
PATCH 2/12 rerere: convert flags into an enum
In reply to Patrick SteinhardtOur modern coding guidelines say that flags should typically be passed around as enums so that it becomes trivial to see what kind of flags are expected. Adapt the rerere flags accordingly.
--- rerere.c | 7 ++++--- rerere.h | 21 ++++++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-)
rerere.c +4 -3
@@ -908,7 +908,7 @@ static int is_rerere_enabled(struct repository *r)int rerere_cache_setup (struct rerere_cache *cache,struct repository *r,struct string_list *merge_rr, -int flags) +enum rerere_flags flags) {int lock_timeout_ms =1000 ;int fd;
@@ -956,7 +956,7 @@ int rerere_cache_setup(struct rerere_cache *cache,* perform mergy operations, possibly leaving conflicted index entries * and working tree files. */ -int rerere_record (struct repository *r,int flags) +int rerere_record (struct repository *r,enum rerere_flags flags) {struct string_list merge_rr =STRING_LIST_INIT_DUP ;struct rerere_cache cache;
@@ -1341,7 +1341,8 @@ bool rerere_gc_needed(struct repository *r, size_t limit)return needed; } -void rerere_gc (struct repository *r,struct string_list *rr,int flags) +void rerere_gc (struct repository *r,struct string_list *rr, +enum rerere_flags flags) {struct rerere_cache cache;struct string_list to_remove =STRING_LIST_INIT_DUP ;
rerere.h +12 -9
@@ -9,12 +9,6 @@struct pathspec ;struct repository ; -#define RERERE_AUTOUPDATE 01 -#define RERERE_NOAUTOUPDATE 02 -#define RERERE_READONLY 04 -/* If MERGE_RR.lock is taken, return -1 as if rerere were disabled */ -#define RERERE_NOWAIT 010 -/* * Marks paths that have been hand-resolved and added to the * index. Set in the util field of such paths after calling
@@ -39,13 +33,21 @@ struct rerere_id {int variant; }; +enum rerere_flags { +RERERE_AUTOUPDATE = (1 <<0 ), +RERERE_NOAUTOUPDATE = (1 <<1 ), +RERERE_READONLY = (1 <<2 ), +/* If MERGE_RR.lock is taken, return -1 as if rerere were disabled */ +RERERE_NOWAIT = (1 <<3 ), +}; +int rerere_cache_setup (struct rerere_cache *cache,struct repository *r,struct string_list *merge_rr, -int flags); +enum rerere_flags flags);void rerere_cache_release (struct rerere_cache *cache); -int rerere_record (struct repository *,int ); +int rerere_record (struct repository *r,enum rerere_flags flags);/* * Given the conflict ID and the name of a "file" used for replaying * the recorded resolution (e.g. "preimage", "postimage"), return the
@@ -58,7 +60,8 @@ const char *rerere_path(struct repository *,int rerere_forget (struct repository *,struct pathspec *);int rerere_remaining (struct repository *,struct string_list *);void rerere_clear (struct repository *,struct string_list *); -void rerere_gc (struct repository *,struct string_list *,int ); +void rerere_gc (struct repository *,struct string_list *, +enum rerere_flags flags);/* * Check whether garbage collection for rerere entries is needed, which is
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
Re: [PATCH 02/12] rerere: convert flags into an enum
rerere.h struct rerere_id { · line 42
Quoted 12 lines · Patrick Steinhardt
Our modern coding guidelines say that flags should typically be passed around as enums so that it becomes trivial to see what kind of flags are expected. Adapt the rerere flags accordingly.
<snip>
rerere.h +7
-0+enum rerere_flags { +RERERE_AUTOUPDATE = (1 <<0 ), +RERERE_NOAUTOUPDATE = (1 <<1 ), +RERERE_READONLY = (1 <<2 ), +/* If MERGE_RR.lock is taken, return -1 as if rerere were disabled */ +RERERE_NOWAIT = (1 <<3 ), +};
If we're moving to using enum flags, then wouldn't it be better to explicitly add a "RERE_MERGE_LOCK_TOKEN" to the enum for that case, rather than have this value only documented by a comment?
-Mark
Re: [PATCH 02/12] rerere: convert flags into an enum
Quoted 5 lines · Mark C. Chu-Carroll
Quoted 4 lines · Patrick Steinhardt
Our modern coding guidelines say that flags should typically be passed around as enums so that it becomes trivial to see what kind of flags are expected. Adapt the rerere flags accordingly.
Quoted 12 lines
rerere.h struct rerere_id { · line 42
Quoted 8 lines
<snip>
rerere.h +7
-0+enum rerere_flags { +RERERE_AUTOUPDATE = (1 <<0 ), +RERERE_NOAUTOUPDATE = (1 <<1 ), +RERERE_READONLY = (1 <<2 ), +/* If MERGE_RR.lock is taken, return -1 as if rerere were disabled */ +RERERE_NOWAIT = (1 <<3 ), +};If we're moving to using enum flags, then wouldn't it be better to explicitly add a "
RERE_MERGE_LOCK_TOKEN" to the enum for that case, rather than have this value only documented by a comment?
Perhaps you misunderstand what the comment says. It is an explanation for the next line, i.e., it tells the API users "If you want the 'immediate failure when locked' behaviour, pass the _NOWAIT bit".
Re: [PATCH 02/12] rerere: convert flags into an enum
Quoted 20 lines · Junio C Hamano
Quoted 5 lines · Mark C. Chu-Carroll
Quoted 4 lines · Patrick Steinhardt
Our modern coding guidelines say that flags should typically be passed around as enums so that it becomes trivial to see what kind of flags are expected. Adapt the rerere flags accordingly.
Quoted 12 lines
rerere.h struct rerere_id { · line 42
Quoted 8 lines
<snip>
rerere.h +7
-0+enum rerere_flags { +RERERE_AUTOUPDATE = (1 <<0 ), +RERERE_NOAUTOUPDATE = (1 <<1 ), +RERERE_READONLY = (1 <<2 ), +/* If MERGE_RR.lock is taken, return -1 as if rerere were disabled */ +RERERE_NOWAIT = (1 <<3 ), +};If we're moving to using enum flags, then wouldn't it be better to explicitly add a "
RERE_MERGE_LOCK_TOKEN" to the enum for that case, rather than have this value only documented by a comment?
Quoted 4 lines
Perhaps you misunderstand what the comment says. It is an explanation for the next line, i.e., it tells the API users "If you want the 'immediate failure when locked' behaviour, pass the
_NOWAITbit".
Yeah, this. I guess we can help that though by documenting all of the flags while at it.
Patrick
Re: [PATCH 02/12] rerere: convert flags into an enum
Quoted 21 lines · Patrick Steinhardt
Quoted 20 lines · Junio C Hamano
Quoted 17 lines · Mark C. Chu-Carroll
rerere.h struct rerere_id { · line 42
Quoted 12 lines · Patrick Steinhardt
Our modern coding guidelines say that flags should typically be passed around as enums so that it becomes trivial to see what kind of flags are expected. Adapt the rerere flags accordingly.
<snip>
rerere.h +7
-0+enum rerere_flags { +RERERE_AUTOUPDATE = (1 <<0 ), +RERERE_NOAUTOUPDATE = (1 <<1 ), +RERERE_READONLY = (1 <<2 ), +/* If MERGE_RR.lock is taken, return -1 as if rerere were disabled */ +RERERE_NOWAIT = (1 <<3 ), +};If we're moving to using enum flags, then wouldn't it be better to explicitly add a "
RERE_MERGE_LOCK_TOKEN" to the enum for that case, rather than have this value only documented by a comment?
Quoted 7 lines
Quoted 4 lines
Perhaps you misunderstand what the comment says. It is an explanation for the next line, i.e., it tells the API users "If you want the 'immediate failure when locked' behaviour, pass the
_NOWAITbit".Yeah, this. I guess we can help that though by documenting all of the flags while at it.
Ah, yeah. I misunderstood that. Obvious in retrospect, but I was puzzling over that for a while.
-Mark
Re: [PATCH 02/12] rerere: convert flags into an enum
In reply to Patrick SteinhardtQuoted 21 lines · Patrick Steinhardt
Quoted 20 lines · Junio C Hamano
Quoted 17 lines · Mark C. Chu-Carroll
rerere.h struct rerere_id { · line 42
Quoted 12 lines · Patrick Steinhardt
Our modern coding guidelines say that flags should typically be passed around as enums so that it becomes trivial to see what kind of flags are expected. Adapt the rerere flags accordingly.
<snip>
rerere.h +7
-0+enum rerere_flags { +RERERE_AUTOUPDATE = (1 <<0 ), +RERERE_NOAUTOUPDATE = (1 <<1 ), +RERERE_READONLY = (1 <<2 ), +/* If MERGE_RR.lock is taken, return -1 as if rerere were disabled */ +RERERE_NOWAIT = (1 <<3 ), +};If we're moving to using enum flags, then wouldn't it be better to explicitly add a "
RERE_MERGE_LOCK_TOKEN" to the enum for that case, rather than have this value only documented by a comment?
Quoted 7 lines
Quoted 4 lines
Perhaps you misunderstand what the comment says. It is an explanation for the next line, i.e., it tells the API users "If you want the 'immediate failure when locked' behaviour, pass the
_NOWAITbit".Yeah, this. I guess we can help that though by documenting all of the flags while at it.
Great.
PATCH 3/12 builtin/rerere: drop `the_repository`
In reply to Patrick SteinhardtThere are a couple of references to the_repository in git-rerere(1). As that command cannot run outside of a repository these references can be trivially replaced by the repository that's passed in as parameter to cmd_rerere(). We don't have to worry about the repository being NULL when called with "-h", either, as there are no references to the repo before we call parse_options().
--- builtin/rerere.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-)
builtin/rerere.c +11 -13
@@ -1,5 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE -#include "builtin.h" #include "config.h" #include "gettext.h"
@@ -57,7 +55,7 @@ static int diff_two(const char *file1, const char *label1,int cmd_rerere (int argc,const char **argv,const char *prefix, - struct repository *repo UNUSED) +struct repository *repo) {struct string_list merge_rr =STRING_LIST_INIT_DUP ;int autoupdate =-1 , skip_locked =0 , flags =0 ;
@@ -75,7 +73,7 @@ int cmd_rerere(int argc,if (skip_locked && (argc <1 ||strcmp (argv[0 ],"gc" )))die (_ ("the option '%s' requires '%s'" ),"--skip-locked" ,"gc" ); -repo_config (the_repository, git_xmerge_config,NULL ); +repo_config (repo, git_xmerge_config,NULL );if (autoupdate ==1 ) flags =RERERE_AUTOUPDATE ;
@@ -83,7 +81,7 @@ int cmd_rerere(int argc, flags =RERERE_NOAUTOUPDATE ;if (argc <1 ) -return rerere_record (the_repository, flags); +return rerere_record (repo, flags);if (!strcmp (argv[0 ],"forget" )) {struct pathspec pathspec;
@@ -94,21 +92,21 @@ int cmd_rerere(int argc,parse_pathspec (&pathspec,0 ,PATHSPEC_PREFER_CWD , prefix, argv +1 ); - ret =rerere_forget (the_repository, &pathspec); + ret =rerere_forget (repo, &pathspec);clear_pathspec (&pathspec);return ret; }if (!strcmp (argv[0 ],"clear" )) { -rerere_clear (the_repository, &merge_rr); +rerere_clear (repo, &merge_rr); }else if (!strcmp (argv[0 ],"gc" )) -rerere_gc (the_repository, &merge_rr, +rerere_gc (repo, &merge_rr, skip_locked ?RERERE_NOWAIT :0 );else if (!strcmp (argv[0 ],"status" )) {struct rerere_cache cache; -if (rerere_cache_setup (&cache, the_repository, &merge_rr, +if (rerere_cache_setup (&cache, repo, &merge_rr, flags |RERERE_READONLY ) <0 )return 0 ;for (size_t i =0 ; i < merge_rr.nr; i++)
@@ -116,7 +114,7 @@ int cmd_rerere(int argc,rerere_cache_release (&cache); }else if (!strcmp (argv[0 ],"remaining" )) { -rerere_remaining (the_repository, &merge_rr); +rerere_remaining (repo, &merge_rr);for (size_t i =0 ; i < merge_rr.nr; i++) {if (merge_rr.items[i].util !=RERERE_RESOLVED )printf ("%s\n" , merge_rr.items[i].string);
@@ -129,16 +127,16 @@ int cmd_rerere(int argc,struct strbuf buf =STRBUF_INIT ;struct rerere_cache cache; -if (rerere_cache_setup (&cache, the_repository, &merge_rr, +if (rerere_cache_setup (&cache, repo, &merge_rr, flags |RERERE_READONLY ) <0 )return 0 ;for (size_t i =0 ; i < merge_rr.nr; i++) {const char *path = merge_rr.items[i].string;const struct rerere_id *id = merge_rr.items[i].util; -if (diff_two (rerere_path (the_repository, &buf, id,"preimage" ), +if (diff_two (rerere_path (repo, &buf, id,"preimage" ), path, path, path))die (_ ("unable to generate diff for '%s'" ), -rerere_path (the_repository, &buf, id,NULL )); +rerere_path (repo, &buf, id,NULL )); }rerere_cache_release (&cache);
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
PATCH 4/12 builtin/rerere: stop passing flags to subcommands that ignore them
In reply to Patrick SteinhardtThe git-rerere(1) command has a "--[no-]rerere-autoupdate" flag. If passed, we explicitly enable or disable recording clean merge resolutions that are staged in the index. This flag only matters to git-rerere(1) when not executed with any subcommand, but we also forward the flag to git rerere status and git rerere diff.
This is mostly harmless as we would ignore those flags anyway, but it makes subsequent refactorings harder to understand. Prepare for these refactorings by not using the flag in those subcommands anymore.
--- builtin/rerere.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
builtin/rerere.c +2 -2
@@ -107,7 +107,7 @@ int cmd_rerere(int argc,struct rerere_cache cache;if (rerere_cache_setup (&cache, repo, &merge_rr, - flags |RERERE_READONLY ) <0 ) +RERERE_READONLY ) <0 )return 0 ;for (size_t i =0 ; i < merge_rr.nr; i++)printf ("%s\n" , merge_rr.items[i].string);
@@ -128,7 +128,7 @@ int cmd_rerere(int argc,struct rerere_cache cache;if (rerere_cache_setup (&cache, repo, &merge_rr, - flags |RERERE_READONLY ) <0 ) +RERERE_READONLY ) <0 )return 0 ;for (size_t i =0 ; i < merge_rr.nr; i++) {const char *path = merge_rr.items[i].string;
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
PATCH 5/12 builtin/rerere: turn "forget" into a proper subcommand
In reply to Patrick SteinhardtThe git-rerere(1) command has its own, home-grown implementation of subcommands. Of course this implementation is inconsistent with how subcommands typically work:
- Options from other subcommands apply to each other.
- We don't have proper usage strings.
- Running git rerere <subcmd> -h doesn't print the options that are supported by that subcommand, but instead it just prints the help of git-rerere(1) itself.
A subsequent commit will thus introduce proper subcommands as supported by our parse-options subsystem. Refactor the "forget" subcommand to use parse-options as a preparatory step. The other commands will follow in subsequent commits until all of them have been converted, and then we switch over to OPT_SUBCOMMAND().
--- builtin/rerere.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-)
builtin/rerere.c +24 -14
@@ -52,6 +52,28 @@ static int diff_two(const char *file1, const char *label1,return ret; } +static int cmd_rerere_forget (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct pathspec pathspec; +int ret; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (!argc) +warning (_ ("'git rerere forget' without paths is deprecated" )); +parse_pathspec (&pathspec,0 ,PATHSPEC_PREFER_CWD , prefix, argv); + + ret =rerere_forget (repo, &pathspec); + +clear_pathspec (&pathspec); +return ret; +} + int cmd_rerere(int argc, const char **argv, const char *prefix,
@@ -83,20 +105,8 @@ int cmd_rerere(int argc,if (argc <1 )return rerere_record (repo, flags); -if (!strcmp (argv[0 ],"forget" )) { -struct pathspec pathspec; -int ret; - -if (argc <2 ) -warning (_ ("'git rerere forget' without paths is deprecated" )); -parse_pathspec (&pathspec,0 ,PATHSPEC_PREFER_CWD , - prefix, argv +1 ); - - ret =rerere_forget (repo, &pathspec); - -clear_pathspec (&pathspec); -return ret; - } +if (!strcmp (argv[0 ],"forget" )) +return cmd_rerere_forget (argc, argv, prefix, repo);if (!strcmp (argv[0 ],"clear" )) {rerere_clear (repo, &merge_rr);
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
PATCH 6/12 builtin/rerere: turn "clear" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "clear" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-)
builtin/rerere.c +22 -3
@@ -52,6 +52,26 @@ static int diff_two(const char *file1, const char *label1,return ret; } +static int cmd_rerere_clear (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +rerere_clear (repo, &merge_rr); + +string_list_clear (&merge_rr,1 ); +return 0 ; +} + static int cmd_rerere_forget(int argc, const char **argv, const char *prefix,
@@ -107,9 +127,8 @@ int cmd_rerere(int argc,if (!strcmp (argv[0 ],"forget" ))return cmd_rerere_forget (argc, argv, prefix, repo); - -if (!strcmp (argv[0 ],"clear" )) { -rerere_clear (repo, &merge_rr); +else if (!strcmp (argv[0 ],"clear" )) { +return cmd_rerere_clear (argc, argv, prefix, repo); }else if (!strcmp (argv[0 ],"gc" ))rerere_gc (repo, &merge_rr, skip_locked ?RERERE_NOWAIT :0 );
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
PATCH 7/12 builtin/rerere: turn "status" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "status" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-)
builtin/rerere.c +29 -11
@@ -94,6 +94,32 @@ static int cmd_rerere_forget(int argc,return ret; } +static int cmd_rerere_status (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; +struct rerere_cache cache; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +if (rerere_cache_setup (&cache, repo, &merge_rr, +RERERE_READONLY ) <0 ) +return 0 ; +for (size_t i =0 ; i < merge_rr.nr; i++) +printf ("%s\n" , merge_rr.items[i].string); + +string_list_clear (&merge_rr,1 ); +rerere_cache_release (&cache); +return 0 ; +} + int cmd_rerere(int argc, const char **argv, const char *prefix,
@@ -132,17 +158,9 @@ int cmd_rerere(int argc, }else if (!strcmp (argv[0 ],"gc" ))rerere_gc (repo, &merge_rr, skip_locked ?RERERE_NOWAIT :0 ); -else if (!strcmp (argv[0 ],"status" )) { -struct rerere_cache cache; - -if (rerere_cache_setup (&cache, repo, &merge_rr, -RERERE_READONLY ) <0 ) -return 0 ; -for (size_t i =0 ; i < merge_rr.nr; i++) -printf ("%s\n" , merge_rr.items[i].string); - -rerere_cache_release (&cache); - }else if (!strcmp (argv[0 ],"remaining" )) { +else if (!strcmp (argv[0 ],"status" )) +return cmd_rerere_status (argc, argv, prefix, repo); +else if (!strcmp (argv[0 ],"remaining" )) {rerere_remaining (repo, &merge_rr);for (size_t i =0 ; i < merge_rr.nr; i++) {if (merge_rr.items[i].util !=RERERE_RESOLVED )
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
PATCH 8/12 builtin/rerere: turn "remaining" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "remaining" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-)
builtin/rerere.c +31 -11
@@ -94,6 +94,34 @@ static int cmd_rerere_forget(int argc,return ret; } +static int cmd_rerere_remaining (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +rerere_remaining (repo, &merge_rr); +for (size_t i =0 ; i < merge_rr.nr; i++) { +if (merge_rr.items[i].util !=RERERE_RESOLVED ) +printf ("%s\n" , merge_rr.items[i].string); +else +/* prepare for later call to +* string_list_clear() */ + merge_rr.items[i].util =NULL ; + } + +string_list_clear (&merge_rr,1 ); +return 0 ; +} + static int cmd_rerere_status(int argc, const char **argv, const char *prefix,
@@ -160,17 +188,9 @@ int cmd_rerere(int argc, skip_locked ? RERERE_NOWAIT : 0); else if (!strcmp(argv[0], "status")) return cmd_rerere_status(argc, argv, prefix, repo); - else if (!strcmp(argv[0], "remaining")) { -rerere_remaining (repo, &merge_rr); -for (size_t i =0 ; i < merge_rr.nr; i++) { -if (merge_rr.items[i].util !=RERERE_RESOLVED ) -printf ("%s\n" , merge_rr.items[i].string); -else -/* prepare for later call to -* string_list_clear() */ - merge_rr.items[i].util =NULL ; - } - } else if (!strcmp(argv[0], "diff")) { + else if (!strcmp(argv[0], "remaining")) + return cmd_rerere_remaining(argc, argv, prefix, repo); + else if (!strcmp(argv[0], "diff")) {struct strbuf buf =STRBUF_INIT ;struct rerere_cache cache;
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
PATCH 9/12 builtin/rerere: turn "diff" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "diff" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 56 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 19 deletions(-)
builtin/rerere.c +37 -19
@@ -72,6 +72,40 @@ static int cmd_rerere_clear(int argc,return 0 ; } +static int cmd_rerere_diff (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; +struct strbuf buf =STRBUF_INIT ; +struct rerere_cache cache; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +if (rerere_cache_setup (&cache, repo, &merge_rr, +RERERE_READONLY ) <0 ) +return 0 ; + +for (size_t i =0 ; i < merge_rr.nr; i++) { +const char *path = merge_rr.items[i].string; +const struct rerere_id *id = merge_rr.items[i].util; +if (diff_two (rerere_path (repo, &buf, id,"preimage" ), path, path, path)) +die (_ ("unable to generate diff for '%s'" ), +rerere_path (repo, &buf, id,NULL )); + } + +string_list_clear (&merge_rr,1 ); +rerere_cache_release (&cache); +strbuf_release (&buf); +return 0 ; +} + static int cmd_rerere_forget(int argc, const char **argv, const char *prefix,
@@ -190,25 +224,9 @@ int cmd_rerere(int argc,return cmd_rerere_status (argc, argv, prefix, repo); else if (!strcmp(argv[0], "remaining")) return cmd_rerere_remaining(argc, argv, prefix, repo); - else if (!strcmp(argv[0], "diff")) { -struct strbuf buf =STRBUF_INIT ; -struct rerere_cache cache; - -if (rerere_cache_setup (&cache, repo, &merge_rr, -RERERE_READONLY ) <0 ) -return 0 ; -for (size_t i =0 ; i < merge_rr.nr; i++) { -const char *path = merge_rr.items[i].string; -const struct rerere_id *id = merge_rr.items[i].util; -if (diff_two (rerere_path (repo, &buf, id,"preimage" ), - path, path, path)) -die (_ ("unable to generate diff for '%s'" ), -rerere_path (repo, &buf, id,NULL )); - } - -rerere_cache_release (&cache); -strbuf_release (&buf); - } else + else if (!strcmp(argv[0], "diff")) + return cmd_rerere_diff(argc, argv, prefix, repo); + elseusage_with_options (rerere_usage ,options );string_list_clear (&merge_rr,1 );
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
PATCH 10/12 builtin/rerere: turn "gc" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "gc" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 43 ++++++++++++++++++++++++++++++------------- t/t4200-rerere.sh | 5 ----- 2 files changed, 30 insertions(+), 18 deletions(-)
builtin/rerere.c +30 -13
@@ -128,6 +128,30 @@ static int cmd_rerere_forget(int argc,return ret; } +static int cmd_rerere_gc (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +int skip_locked =0 ; +struct option options[] = { +OPT_HIDDEN_BOOL (0 ,"skip-locked" , &skip_locked, +N_ ("skip gc while another process holds the lock" )), +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +rerere_gc (repo, &merge_rr, + skip_locked ?RERERE_NOWAIT :0 ); + +string_list_clear (&merge_rr,1 ); +return 0 ; +} + static int cmd_rerere_remaining(int argc, const char **argv, const char *prefix,
@@ -187,21 +211,16 @@ int cmd_rerere(int argc,const char *prefix, struct repository *repo) { - struct string_list merge_rr = STRING_LIST_INIT_DUP; -int autoupdate =-1 , skip_locked =0 , flags =0 ; + int autoupdate = -1, flags = 0;struct option options[] = {OPT_SET_INT (0 ,"rerere-autoupdate" , &autoupdate,N_ ("register clean resolutions in index" ),1 ), -OPT_HIDDEN_BOOL (0 ,"skip-locked" , &skip_locked, -N_ ("skip gc while another process holds the lock" )),OPT_END (), }; - argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); - -if (skip_locked && (argc <1 ||strcmp (argv[0 ],"gc" ))) -die (_ ("the option '%s' requires '%s'" ),"--skip-locked" ,"gc" ); + argc =parse_options (argc, argv, prefix, options, rerere_usage, +PARSE_OPT_KEEP_UNKNOWN_OPT );repo_config (repo, git_xmerge_config,NULL );
@@ -215,11 +234,10 @@ int cmd_rerere(int argc,if (!strcmp (argv[0 ],"forget" ))return cmd_rerere_forget (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"clear" )) { +else if (!strcmp (argv[0 ],"clear" ))return cmd_rerere_clear (argc, argv, prefix, repo); - }else if (!strcmp (argv[0 ],"gc" )) -rerere_gc (repo, &merge_rr, - skip_locked ?RERERE_NOWAIT :0 ); +else if (!strcmp (argv[0 ],"gc" )) +return cmd_rerere_gc (argc, argv, prefix, repo);else if (!strcmp (argv[0 ],"status" ))return cmd_rerere_status (argc, argv, prefix, repo);else if (!strcmp (argv[0 ],"remaining" ))
@@ -229,6 +247,5 @@ int cmd_rerere(int argc,else usage_with_options (rerere_usage, options); -string_list_clear (&merge_rr,1 );return 0 ; }
t/t4200-rerere.sh +0 -5
@@ -263,11 +263,6 @@ test_expect_success 'gc --skip-locked prunes while MERGE_RR is not locked' ' test_path_is_missing $rr2/preimage ' -test_expect_success '--skip-locked is only accepted by gc' ' - test_must_fail git rerere --skip-locked clear 2>err && - test_grep "option .--skip-locked. requires .gc." err -' - test_expect_success 'a held lock is waited out within rerere.lockTimeout' ' git reset --hard && rm -rf $rr &&
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
PATCH 11/12 builtin/rerere: use `OPT_SUBCOMMAND()`
In reply to Patrick SteinhardtIn git-rerere(1) we have our own custom logic to parse subcommands. In preceding commits we have refactored all of those subcommands so that they are ready to be used as OPT_SUBCOMMAND()s. Switch over to use these so that we get rid of our custom subcommand parsing.
Unfortunately this means that we have a backwards-incompatible change because we no longer accept the "--[no-]rerere-autoupdate" flag for all subcommands. But this was arguably broken and misleading as we ignore this flag for any other subcommand aynway. So we cheat a bit and simply call it a bug that those flags were accepted in the first place.
--- builtin/rerere.c | 84 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 28 deletions(-)
builtin/rerere.c +56 -28
@@ -5,6 +5,7 @@#include "rerere.h" #include "strbuf.h" #include "string-list.h" +#include "strvec.h" #include "xdiff/xdiff.h" #include "xdiff-interface.h" #include "pathspec.h"
@@ -152,6 +153,31 @@ static int cmd_rerere_gc(int argc,return 0 ; } +static int cmd_rerere_record (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +int autoupdate =-1 ; +struct option options[] = { +OPT_SET_INT (0 ,"rerere-autoupdate" , &autoupdate, +N_ ("register clean resolutions in index" ),1 ), +OPT_END (), + }; +enum rerere_flags flags =0 ; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +if (autoupdate ==1 ) + flags =RERERE_AUTOUPDATE ; +if (autoupdate ==0 ) + flags =RERERE_NOAUTOUPDATE ; + +return rerere_record (repo, flags); +} + static int cmd_rerere_remaining(int argc, const char **argv, const char *prefix,
@@ -211,41 +237,43 @@ int cmd_rerere(int argc,const char *prefix, struct repository *repo) { - int autoupdate = -1, flags = 0; - + parse_opt_subcommand_fn *fn = NULL;struct option options[] = { -OPT_SET_INT (0 ,"rerere-autoupdate" , &autoupdate, -N_ ("register clean resolutions in index" ),1 ), +OPT_SUBCOMMAND ("clear" , &fn, cmd_rerere_clear), +OPT_SUBCOMMAND ("diff" , &fn, cmd_rerere_diff), +OPT_SUBCOMMAND ("forget" , &fn, cmd_rerere_forget), +OPT_SUBCOMMAND ("gc" , &fn, cmd_rerere_gc), +OPT_SUBCOMMAND ("remaining" , &fn, cmd_rerere_remaining), +OPT_SUBCOMMAND ("status" , &fn, cmd_rerere_status),OPT_END (), }; argc =parse_options (argc, argv, prefix, options, rerere_usage, -PARSE_OPT_KEEP_UNKNOWN_OPT ); - +PARSE_OPT_KEEP_UNKNOWN_OPT |PARSE_OPT_SUBCOMMAND_OPTIONAL );repo_config (repo, git_xmerge_config,NULL ); -if (autoupdate ==1 ) - flags =RERERE_AUTOUPDATE ; -if (autoupdate ==0 ) - flags =RERERE_NOAUTOUPDATE ; +if (!fn) { +struct strvec args =STRVEC_INIT ; +const char **args_copy; +int ret; -if (argc <1 ) -return rerere_record (repo, flags); - -if (!strcmp (argv[0 ],"forget" )) -return cmd_rerere_forget (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"clear" )) -return cmd_rerere_clear (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"gc" )) -return cmd_rerere_gc (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"status" )) -return cmd_rerere_status (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"remaining" )) -return cmd_rerere_remaining (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"diff" )) -return cmd_rerere_diff (argc, argv, prefix, repo); -else -usage_with_options (rerere_usage, options); +strvec_push (&args,"record" ); +strvec_pushv (&args, argv); -return 0 ; +/* +* We need to copy the array as it will be modified by +* `parse_options()`, and if so we leak the strings +* referenced by `args`. +*/ +CALLOC_ARRAY (args_copy, args.nr); +COPY_ARRAY (args_copy, args.v, args.nr); + + ret =cmd_rerere_record (args.nr, args_copy, prefix, repo); + +strvec_clear (&args); +free (args_copy); +return ret; + } + +return fn (argc, argv, prefix, repo); }
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
PATCH 12/12 builtin/rerere: introduce proper usage strings for subcommands
In reply to Patrick SteinhardtAdapt git-rerere(1) to have proper usage strings for each of the subcommands.
--- Documentation/git-rerere.adoc | 8 +++++- builtin/rerere.c | 61 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 11 deletions(-)
Documentation/git-rerere.adoc +7 -1
@@ -8,7 +8,13 @@ git-rerere - Reuse recorded resolution of conflicted merges SYNOPSIS -------- [verse] -'git rerere' [clear | forget <pathspec>... | diff | status | remaining | gc] +'git rerere' [--[no-]rerere-autoupdate] +'git rerere' clear +'git rerere' diff +'git rerere' forget <pathspec>... +'git rerere' gc +'git rerere' remaining +'git rerere' status DESCRIPTION -----------
builtin/rerere.c +51 -10
131 lines not on this page · raw message
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
Quoted 9 lines · Patrick Steinhardt
while reviewing some patch series around git-rerere(1) recently I was a bit appalled by our custom subcommand parsing that we do here. This patch series here thus cleans up and modernizes that part a bit so that git-rerere(1) behaves like every other Git command that happens to have subcommands.
This series is based on
37b3475fb53(The 9th batch, 2026-10-25) with ps/rerere-remove-global-state atb948c10c8b3(rerere: get rid of global rerere cache state, 2026-10-23) merged into it.
As the inventor of the mechanism, I took special interest in the topic, and overall found them well done. I wasn't even aware that we were so close to get rid of USE_THE_REPOSITORY_VARIABLE in builtin/rerere.c with such a small change (even though as I've said I do not think it is high priority to make cmd_foo() not rely on the_repository). Very nicely done.
Thanks.
v2 cover builtin/rerere: convert to use subcommands
In reply to Patrick SteinhardtHi,
while reviewing some patch series around git-rerere(1) recently I was a bit appalled by our custom subcommand parsing that we do here. This patch series here thus cleans up and modernizes that part a bit so that git-rerere(1) behaves like every other Git command that happens to have subcommands.
This series is based on 37b3475fb53 (The 9th batch, 2026-10-25) with ps/rerere-remove-global-state at b948c10c8b3 (rerere: get rid of global rerere cache state, 2026-10-23) merged into it.
Changes in v2:
- Document rerere flags.
- Rename rerere_record() to just rerere().
- Link to v1: https://patch.msgid.link/[email protected]
Thanks!
Patrick
---
Patrick Steinhardt (12):
rerere: rename repo_rerere()
rerere: convert flags into an enum
builtin/rerere: drop the_repository
builtin/rerere: stop passing flags to subcommands that ignore them builtin/rerere: turn "forget" into a proper subcommand builtin/rerere: turn "clear" into a proper subcommand builtin/rerere: turn "status" into a proper subcommand builtin/rerere: turn "remaining" into a proper subcommand builtin/rerere: turn "diff" into a proper subcommand builtin/rerere: turn "gc" into a proper subcommand builtin/rerere: use OPT_SUBCOMMAND()
builtin/rerere: introduce proper usage strings for subcommands
Documentation/git-rerere.adoc | 8 +- apply.c | 2 +- builtin/am.c | 4 +- builtin/commit.c | 2 +- builtin/merge.c | 2 +- builtin/rerere.c | 319 ++++++++++++++++++++++++++++++++---------- builtin/stash.c | 2 +- rerere.c | 12 +- rerere.h | 37 +++-- sequencer.c | 4 +- t/t4200-rerere.sh | 5 - 11 files changed, 291 insertions(+), 106 deletions(-)
Range-diff versus v1:
1: ce75cde848d ! 1: 1f3fbbad575 rerere: rename repo_rerere()
@@ Commit message
repository as first parameter, that's not a good-enough reason to give
it a `repo_` prefix.
- Rename the function to `rerere_record()` to be consistent with all the
- other functions.
+ Rename the function to just `rerere()`. Move around and extend
+ documentation of this function to better explain what it does.
Signed-off-by: Patrick Steinhardt <[email protected]>
@@ apply.c: static int write_out_results(struct apply_state *state, struct patch *l
*/
if (!state->cached)
- repo_rerere(state->repo, 0);
-+ rerere_record(state->repo, 0);
++ rerere(state->repo, 0);
}
return errs;
@@ builtin/am.c: static int fall_back_threeway(const struct am_state *state, const
if (merge_ort_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {
- repo_rerere(the_repository, state->allow_rerere_autoupdate);
-+ rerere_record(the_repository, state->allow_rerere_autoupdate);
++ rerere(the_repository, state->allow_rerere_autoupdate);
free(their_tree_name);
return error(_("Failed to merge in the changes."));
}
@@ builtin/am.c: static void am_resolve(struct am_state *state, int allow_empty)
}
- repo_rerere(the_repository, 0);
-+ rerere_record(the_repository, 0);
++ rerere(the_repository, 0);
do_commit(state);
@@ builtin/commit.c: int cmd_commit(int argc,
git_test_write_commit_graph_or_die(the_repository);
- repo_rerere(the_repository, 0);
-+ rerere_record(the_repository, 0);
++ rerere(the_repository, 0);
run_auto_maintenance(the_repository, quiet);
run_commit_hook(use_editor, repo_get_index_file(the_repository),
NULL, "post-commit", NULL);
@@ builtin/merge.c: static int suggest_conflicts(void)
strbuf_release(&msgbuf);
fclose(fp);
- repo_rerere(the_repository, allow_rerere_auto);
-+ rerere_record(the_repository, allow_rerere_auto);
++ rerere(the_repository, allow_rerere_auto);
printf(_("Automatic merge failed; "
"fix conflicts and then commit the result.\n"));
return 1;
@@ builtin/rerere.c: int cmd_rerere(int argc,
if (argc < 1)
- return repo_rerere(the_repository, flags);
-+ return rerere_record(the_repository, flags);
++ return rerere(the_repository, flags);
if (!strcmp(argv[0], "forget")) {
struct pathspec pathspec;
@@ builtin/stash.c: static enum stash_apply_result do_apply_stash(const char *prefi
if (ret) {
- repo_rerere(the_repository, 0);
-+ rerere_record(the_repository, 0);
++ rerere(the_repository, 0);
if (index)
fprintf_ln(stderr, _("Index was not unstashed."));
## rerere.c ##
@@ rerere.c: int rerere_cache_setup(struct rerere_cache *cache,
- * perform mergy operations, possibly leaving conflicted index entries
- * and working tree files.
- */
+ return fd;
+ }
+
+-/*
+- * The main entry point that is called internally from codepaths that
+- * perform mergy operations, possibly leaving conflicted index entries
+- * and working tree files.
+- */
-int repo_rerere(struct repository *r, int flags)
-+int rerere_record(struct repository *r, int flags)
++int rerere(struct repository *r, int flags)
{
struct string_list merge_rr = STRING_LIST_INIT_DUP;
struct rerere_cache cache;
@@ rerere.h: int rerere_cache_setup(struct rerere_cache *cache,
void rerere_cache_release(struct rerere_cache *cache);
-int repo_rerere(struct repository *, int);
-+int rerere_record(struct repository *, int);
++/*
++ * The main entry point that is called internally from codepaths that
++ * perform mergy operations, possibly leaving conflicted index entries
++ * and working tree files. It records (and optionally updates) resolved
++ * conflicts and reuses recorded resolutions for unresolved conflicts.
++ */
++int rerere(struct repository *r, int flags);
++
/*
* Given the conflict ID and the name of a "file" used for replaying
* the recorded resolution (e.g. "preimage", "postimage"), return the
@@ sequencer.c: static enum pick_result do_pick_commit(struct repository *r,
short_commit_name(r, commit), msg.subject);
print_advice(r, res == 1, opts);
- repo_rerere(r, opts->allow_rerere_auto);
-+ rerere_record(r, opts->allow_rerere_auto);
++ rerere(r, opts->allow_rerere_auto);
goto leave;
}
@@ sequencer.c: static int do_merge(struct repository *r,
rollback_lock_file(&lock);
if (ret)
- repo_rerere(r, opts->allow_rerere_auto);
-+ rerere_record(r, opts->allow_rerere_auto);
++ rerere(r, opts->allow_rerere_auto);
else
/*
* In case of problems, we now want to return a positive
2: 18259e1556a ! 2: e910458a47c rerere: convert flags into an enum
@@ Commit message
Our modern coding guidelines say that flags should typically be passed
around as enums so that it becomes trivial to see what kind of flags are
- expected. Adapt the rerere flags accordingly.
+ expected. Adapt the rerere flags accordingly and document them.
Signed-off-by: Patrick Steinhardt <[email protected]>
@@ rerere.c: static int is_rerere_enabled(struct repository *r)
int lock_timeout_ms = 1000;
int fd;
@@ rerere.c: int rerere_cache_setup(struct rerere_cache *cache,
- * perform mergy operations, possibly leaving conflicted index entries
- * and working tree files.
- */
--int rerere_record(struct repository *r, int flags)
-+int rerere_record(struct repository *r, enum rerere_flags flags)
+ return fd;
+ }
+
+-int rerere(struct repository *r, int flags)
++int rerere(struct repository *r, enum rerere_flags flags)
{
struct string_list merge_rr = STRING_LIST_INIT_DUP;
struct rerere_cache cache;
@@ rerere.h: struct rerere_id {
};
+enum rerere_flags {
++ /*
++ * Auto-update the index with previously recorded resolutions. This can
++ * be used to explicitly override "rerere.autoUpdate".
++ */
+ RERERE_AUTOUPDATE = (1 << 0),
++
++ /* Reverse of RERERE_AUTOUPDATE. */
+ RERERE_NOAUTOUPDATE = (1 << 1),
++
++ /* Skip acquiring the MERGE_RR.lock for a read-only operation. */
+ RERERE_READONLY = (1 << 2),
-+ /* If MERGE_RR.lock is taken, return -1 as if rerere were disabled */
++
++ /* If MERGE_RR.lock is taken, return -1 as if rerere were disabled. */
+ RERERE_NOWAIT = (1 << 3),
+};
+
@@ rerere.h: struct rerere_id {
+ enum rerere_flags flags);
void rerere_cache_release(struct rerere_cache *cache);
--int rerere_record(struct repository *, int);
-+int rerere_record(struct repository *r, enum rerere_flags flags);
+ /*
+@@ rerere.h: void rerere_cache_release(struct rerere_cache *cache);
+ * and working tree files. It records (and optionally updates) resolved
+ * conflicts and reuses recorded resolutions for unresolved conflicts.
+ */
+-int rerere(struct repository *r, int flags);
++int rerere(struct repository *r, enum rerere_flags flags);
+
/*
* Given the conflict ID and the name of a "file" used for replaying
- * the recorded resolution (e.g. "preimage", "postimage"), return the
@@ rerere.h: const char *rerere_path(struct repository *,
int rerere_forget(struct repository *, struct pathspec *);
int rerere_remaining(struct repository *, struct string_list *);
3: 89ae664d90d ! 3: 5254d1ac3ba builtin/rerere: drop `the_repository`
@@ builtin/rerere.c: int cmd_rerere(int argc,
flags = RERERE_NOAUTOUPDATE;
if (argc < 1)
-- return rerere_record(the_repository, flags);
-+ return rerere_record(repo, flags);
+- return rerere(the_repository, flags);
++ return rerere(repo, flags);
if (!strcmp(argv[0], "forget")) {
struct pathspec pathspec;
4: 5eff73de7bc = 4: 8b24e253e6b builtin/rerere: stop passing flags to subcommands that ignore them
5: 621c5f6ab1b ! 5: 43dde532e63 builtin/rerere: turn "forget" into a proper subcommand @@ builtin/rerere.c: static int diff_two(const char *file1, const char *label1,
const char *prefix,
@@ builtin/rerere.c: int cmd_rerere(int argc,
if (argc < 1)
- return rerere_record(repo, flags);
+ return rerere(repo, flags);
- if (!strcmp(argv[0], "forget")) {
- struct pathspec pathspec;
6: d01fa02064c = 6: 13fd07a2f53 builtin/rerere: turn "clear" into a proper subcommand
7: 2bebea6449e = 7: c5d48081cc9 builtin/rerere: turn "status" into a proper subcommand
8: 5594019f31f = 8: 5c6891f6a48 builtin/rerere: turn "remaining" into a proper subcommand
9: e8a2f8ce209 = 9: b7ed9ef810b builtin/rerere: turn "diff" into a proper subcommand
10: deebcee0379 = 10: 0ceff6d241b builtin/rerere: turn "gc" into a proper subcommand
11: 56037e9a761 ! 11: 3b8b4b65a53 builtin/rerere: use `OPT_SUBCOMMAND()`
@@ builtin/rerere.c: static int cmd_rerere_gc(int argc,
return 0;
}
-+static int cmd_rerere_record(int argc,
-+ const char **argv,
-+ const char *prefix,
-+ struct repository *repo)
++static int cmd_rerere_perform(int argc,
++ const char **argv,
++ const char *prefix,
++ struct repository *repo)
+{
+ int autoupdate = -1;
+ struct option options[] = {
@@ builtin/rerere.c: static int cmd_rerere_gc(int argc,
+ if (autoupdate == 0)
+ flags = RERERE_NOAUTOUPDATE;
+
-+ return rerere_record(repo, flags);
++ return rerere(repo, flags);
+}
+
static int cmd_rerere_remaining(int argc,
@@ builtin/rerere.c: int cmd_rerere(int argc,
+ int ret;
- if (argc < 1)
-- return rerere_record(repo, flags);
+- return rerere(repo, flags);
-
- if (!strcmp(argv[0], "forget"))
- return cmd_rerere_forget(argc, argv, prefix, repo);
@@ builtin/rerere.c: int cmd_rerere(int argc,
- return cmd_rerere_diff(argc, argv, prefix, repo);
- else
- usage_with_options(rerere_usage, options);
-+ strvec_push(&args, "record");
++ strvec_push(&args, "perform");
+ strvec_pushv(&args, argv);
- return 0;
@@ builtin/rerere.c: int cmd_rerere(int argc,
+ CALLOC_ARRAY(args_copy, args.nr);
+ COPY_ARRAY(args_copy, args.v, args.nr);
+
-+ ret = cmd_rerere_record(args.nr, args_copy, prefix, repo);
++ ret = cmd_rerere_perform(args.nr, args_copy, prefix, repo);
+
+ strvec_clear(&args);
+ free(args_copy);12: 102223da27b ! 12: 05adadc9dd8 builtin/rerere: introduce proper usage strings for subcommands
@@ builtin/rerere.c: static int cmd_rerere_gc(int argc,
if (argc)
usage(_("too many arguments"));
-@@ builtin/rerere.c: static int cmd_rerere_record(int argc, +@@ builtin/rerere.c: static int cmd_rerere_perform(int argc,
N_("register clean resolutions in index"), 1),
OPT_END(),
};
---
base-commit: 710910c03a22ac663885049213b9fef5853ea8e0
change-id: 20261027-pks-rerere-subcommands-318d41553438
PATCH v2 1/12 rerere: rename `repo_rerere()`
The repo_rerere() function is the only function in "rerere.h" that does not have a rerere_ prefix. And while it does indeed receive a repository as first parameter, that's not a good-enough reason to give it a repo_ prefix.
Rename the function to just rerere(). Move around and extend documentation of this function to better explain what it does.
--- apply.c | 2 +- builtin/am.c | 4 ++-- builtin/commit.c | 2 +- builtin/merge.c | 2 +- builtin/rerere.c | 2 +- builtin/stash.c | 2 +- rerere.c | 7 +------ rerere.h | 9 ++++++++- sequencer.c | 4 ++-- 9 files changed, 18 insertions(+), 16 deletions(-)
apply.c +1 -1
@@ -4866,7 +4866,7 @@ static int write_out_results(struct apply_state *state, struct patch *list)* tree with conflict markers, but that isn't written with --cached. */ if (!state->cached) -repo_rerere (state->repo,0 ); +rerere (state->repo,0 ); }return errs;
builtin/am.c +2 -2
@@ -1649,7 +1649,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa o.verbosity =0 ;if (merge_ort_generic (&o, &our_tree, &their_tree,1 , bases, &result)) { -repo_rerere (the_repository, state->allow_rerere_autoupdate); +rerere (the_repository, state->allow_rerere_autoupdate);free (their_tree_name);return error (_ ("Failed to merge in the changes." )); }
@@ -1979,7 +1979,7 @@ static void am_resolve(struct am_state *state, int allow_empty) goto next; } -repo_rerere (the_repository,0 ); +rerere (the_repository,0 );do_commit (state);
builtin/commit.c +1 -1
@@ -1962,7 +1962,7 @@ int cmd_commit(int argc,git_test_write_commit_graph_or_die (the_repository); -repo_rerere (the_repository,0 ); +rerere (the_repository,0 );run_auto_maintenance (the_repository, quiet);run_commit_hook (use_editor,repo_get_index_file (the_repository),NULL ,"post-commit" ,NULL );
builtin/merge.c +1 -1
@@ -1061,7 +1061,7 @@ static int suggest_conflicts(void)fputs (msgbuf.buf, fp);strbuf_release (&msgbuf);fclose (fp); -repo_rerere (the_repository, allow_rerere_auto); +rerere (the_repository, allow_rerere_auto);printf (_ ("Automatic merge failed; " "fix conflicts and then commit the result.\n" ));return 1 ;
builtin/rerere.c +1 -1
@@ -83,7 +83,7 @@ int cmd_rerere(int argc, flags =RERERE_NOAUTOUPDATE ;if (argc <1 ) -return repo_rerere (the_repository, flags); +return rerere (the_repository, flags);if (!strcmp (argv[0 ],"forget" )) {struct pathspec pathspec;
builtin/stash.c +1 -1
@@ -688,7 +688,7 @@ static enum stash_apply_result do_apply_stash(const char *prefix, ret =error (_ ("could not write index" ));if (ret) { -repo_rerere (the_repository,0 ); +rerere (the_repository,0 );if (index)fprintf_ln (stderr,_ ("Index was not unstashed." ));
rerere.c +1 -6
@@ -951,12 +951,7 @@ int rerere_cache_setup(struct rerere_cache *cache,return fd; } -/* -* The main entry point that is called internally from codepaths that -* perform mergy operations, possibly leaving conflicted index entries -* and working tree files. -*/ -int repo_rerere (struct repository *r,int flags) +int rerere (struct repository *r,int flags) {struct string_list merge_rr =STRING_LIST_INIT_DUP ;struct rerere_cache cache;
rerere.h +8 -1
@@ -45,7 +45,14 @@ int rerere_cache_setup(struct rerere_cache *cache, int flags);void rerere_cache_release (struct rerere_cache *cache); -int repo_rerere (struct repository *,int ); +/* +* The main entry point that is called internally from codepaths that +* perform mergy operations, possibly leaving conflicted index entries +* and working tree files. It records (and optionally updates) resolved +* conflicts and reuses recorded resolutions for unresolved conflicts. +*/ +int rerere (struct repository *r,int flags); +/* * Given the conflict ID and the name of a "file" used for replaying * the recorded resolution (e.g. "preimage", "postimage"), return the
sequencer.c +2 -2
@@ -2556,7 +2556,7 @@ static enum pick_result do_pick_commit(struct repository *r, : _("could not apply %s... %s"), short_commit_name(r, commit), msg.subject);print_advice (r, res ==1 , opts); -repo_rerere (r, opts->allow_rerere_auto); +rerere (r, opts->allow_rerere_auto); goto leave; }
@@ -4488,7 +4488,7 @@ static int do_merge(struct repository *r,rollback_lock_file (&lock);if (ret) -repo_rerere (r, opts->allow_rerere_auto); +rerere (r, opts->allow_rerere_auto);else /* * In case of problems, we now want to return a positive
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
PATCH v2 2/12 rerere: convert flags into an enum
In reply to Patrick SteinhardtOur modern coding guidelines say that flags should typically be passed around as enums so that it becomes trivial to see what kind of flags are expected. Adapt the rerere flags accordingly and document them.
--- rerere.c | 7 ++++--- rerere.h | 30 +++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 12 deletions(-)
rerere.c +4 -3
@@ -908,7 +908,7 @@ static int is_rerere_enabled(struct repository *r)int rerere_cache_setup (struct rerere_cache *cache,struct repository *r,struct string_list *merge_rr, -int flags) +enum rerere_flags flags) {int lock_timeout_ms =1000 ;int fd;
@@ -951,7 +951,7 @@ int rerere_cache_setup(struct rerere_cache *cache,return fd; } -int rerere (struct repository *r,int flags) +int rerere (struct repository *r,enum rerere_flags flags) {struct string_list merge_rr =STRING_LIST_INIT_DUP ;struct rerere_cache cache;
@@ -1336,7 +1336,8 @@ bool rerere_gc_needed(struct repository *r, size_t limit)return needed; } -void rerere_gc (struct repository *r,struct string_list *rr,int flags) +void rerere_gc (struct repository *r,struct string_list *rr, +enum rerere_flags flags) {struct rerere_cache cache;struct string_list to_remove =STRING_LIST_INIT_DUP ;
rerere.h +21 -9
@@ -9,12 +9,6 @@struct pathspec ;struct repository ; -#define RERERE_AUTOUPDATE 01 -#define RERERE_NOAUTOUPDATE 02 -#define RERERE_READONLY 04 -/* If MERGE_RR.lock is taken, return -1 as if rerere were disabled */ -#define RERERE_NOWAIT 010 -/* * Marks paths that have been hand-resolved and added to the * index. Set in the util field of such paths after calling
@@ -39,10 +33,27 @@ struct rerere_id {int variant; }; +enum rerere_flags { +/* +* Auto-update the index with previously recorded resolutions. This can +* be used to explicitly override "rerere.autoUpdate". +*/ +RERERE_AUTOUPDATE = (1 <<0 ), + +/* Reverse of RERERE_AUTOUPDATE. */ +RERERE_NOAUTOUPDATE = (1 <<1 ), + +/* Skip acquiring the MERGE_RR.lock for a read-only operation. */ +RERERE_READONLY = (1 <<2 ), + +/* If MERGE_RR.lock is taken, return -1 as if rerere were disabled. */ +RERERE_NOWAIT = (1 <<3 ), +}; +int rerere_cache_setup (struct rerere_cache *cache,struct repository *r,struct string_list *merge_rr, -int flags); +enum rerere_flags flags);void rerere_cache_release (struct rerere_cache *cache);/*
@@ -51,7 +62,7 @@ void rerere_cache_release(struct rerere_cache *cache);* and working tree files. It records (and optionally updates) resolved * conflicts and reuses recorded resolutions for unresolved conflicts. */ -int rerere (struct repository *r,int flags); +int rerere (struct repository *r,enum rerere_flags flags);/* * Given the conflict ID and the name of a "file" used for replaying
@@ -65,7 +76,8 @@ const char *rerere_path(struct repository *,int rerere_forget (struct repository *,struct pathspec *);int rerere_remaining (struct repository *,struct string_list *);void rerere_clear (struct repository *,struct string_list *); -void rerere_gc (struct repository *,struct string_list *,int ); +void rerere_gc (struct repository *,struct string_list *, +enum rerere_flags flags);/* * Check whether garbage collection for rerere entries is needed, which is
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
Re: [PATCH v2 02/12] rerere: convert flags into an enum
Quoted 8 lines · Patrick Steinhardt
rerere.h +0
-0@@ -39,10 +33,27 @@ struct rerere_id {int variant; };
rerere.h struct rerere_id { · line 45
Quoted 10 lines
rerere.h +10
-0+enum rerere_flags { +/* +* Auto-update the index with previously recorded resolutions. This can +* be used to explicitly override "rerere.autoUpdate". +*/ +RERERE_AUTOUPDATE = (1 <<0 ), + +/* Reverse of RERERE_AUTOUPDATE. */ +RERERE_NOAUTOUPDATE = (1 <<1 ), +
Nit: From my understanding, RERERE_NOAUTOUPDATE outputs the conflict resolution information to the stderr instead of updating the index. Perhaps that's something to document?
Quoted 15 lines
rerere.h +8
-1+/* Skip acquiring the MERGE_RR.lock for a read-only operation. */ +RERERE_READONLY = (1 <<2 ), + +/* If MERGE_RR.lock is taken, return -1 as if rerere were disabled. */ + RERERE_NOWAIT = (1 << 3), +}; +int rerere_cache_setup (struct rerere_cache *cache,struct repository *r,struct string_list *merge_rr, -int flags); +enum rerere_flags flags);void rerere_cache_release (struct rerere_cache *cache);/*
Re: [PATCH v2 02/12] rerere: convert flags into an enum
Quoted 10 lines · Karthik Nayak
Quoted 8 lines · Patrick Steinhardt
rerere.h +0
-0@@ -39,10 +33,27 @@ struct rerere_id {int variant; };
Quoted 14 lines
rerere.h struct rerere_id { · line 45
Quoted 10 lines
rerere.h +10
-0+enum rerere_flags { +/* +* Auto-update the index with previously recorded resolutions. This can +* be used to explicitly override "rerere.autoUpdate". +*/ +RERERE_AUTOUPDATE = (1 <<0 ), + +/* Reverse of RERERE_AUTOUPDATE. */ +RERERE_NOAUTOUPDATE = (1 <<1 ), +Nit: From my understanding,
RERERE_NOAUTOUPDATEoutputs the conflict resolution information to the stderr instead of updating the index. Perhaps that's something to document?
Well, the fact that it prints to stderr is not specific to NOAUTOUPDATE. We print messages in both cases, just at different times:
- When RERERE_AUTOUPDATE is set we print the messages in
`update_paths()`.
- When RERERE_NOAUTOUPDATE is set (or RERERE_AUTOUPDATE is simply unset) then we print the message in do_rerere_one_path().
The messages are different of course, but I don't really think that this matters for the flag here. What we might want to say though is that we don't update the index, but the working tree does get updated.
Will adapt, thanks.
Patrick
Re: [PATCH v2 02/12] rerere: convert flags into an enum
Quoted 26 lines · Patrick Steinhardt
Quoted 10 lines · Karthik Nayak
Quoted 8 lines · Patrick Steinhardt
rerere.h +0
-0@@ -39,10 +33,27 @@ struct rerere_id {int variant; };Quoted 14 lines
rerere.h struct rerere_id { · line 45
Quoted 10 lines
rerere.h +10
-0+enum rerere_flags { +/* +* Auto-update the index with previously recorded resolutions. This can +* be used to explicitly override "rerere.autoUpdate". +*/ +RERERE_AUTOUPDATE = (1 <<0 ), + +/* Reverse of RERERE_AUTOUPDATE. */ +RERERE_NOAUTOUPDATE = (1 <<1 ), +Nit: From my understanding,
RERERE_NOAUTOUPDATEoutputs the conflict resolution information to the stderr instead of updating the index. Perhaps that's something to document?
Quoted 9 lines
Well, the fact that it prints to stderr is not specific to NOAUTOUPDATE. We print messages in both cases, just at different times:
- When
RERERE_AUTOUPDATEis set we print the messages in`update_paths()`.- When
RERERE_NOAUTOUPDATEis set (orRERERE_AUTOUPDATEis simply unset) then we print the message indo_rerere_one_path().
I didn't dig enough to find the former.
Quoted 7 lines
The messages are different of course, but I don't really think that this matters for the flag here. What we might want to say though is that we don't update the index, but the working tree does get updated.
Will adapt, thanks.
Patrick
Sounds good.
PATCH v2 3/12 builtin/rerere: drop `the_repository`
In reply to Patrick SteinhardtThere are a couple of references to the_repository in git-rerere(1). As that command cannot run outside of a repository these references can be trivially replaced by the repository that's passed in as parameter to cmd_rerere(). We don't have to worry about the repository being NULL when called with "-h", either, as there are no references to the repo before we call parse_options().
--- builtin/rerere.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-)
builtin/rerere.c +11 -13
@@ -1,5 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE -#include "builtin.h" #include "config.h" #include "gettext.h"
@@ -57,7 +55,7 @@ static int diff_two(const char *file1, const char *label1,int cmd_rerere (int argc,const char **argv,const char *prefix, - struct repository *repo UNUSED) +struct repository *repo) {struct string_list merge_rr =STRING_LIST_INIT_DUP ;int autoupdate =-1 , skip_locked =0 , flags =0 ;
@@ -75,7 +73,7 @@ int cmd_rerere(int argc,if (skip_locked && (argc <1 ||strcmp (argv[0 ],"gc" )))die (_ ("the option '%s' requires '%s'" ),"--skip-locked" ,"gc" ); -repo_config (the_repository, git_xmerge_config,NULL ); +repo_config (repo, git_xmerge_config,NULL );if (autoupdate ==1 ) flags =RERERE_AUTOUPDATE ;
@@ -83,7 +81,7 @@ int cmd_rerere(int argc, flags =RERERE_NOAUTOUPDATE ;if (argc <1 ) -return rerere (the_repository, flags); +return rerere (repo, flags);if (!strcmp (argv[0 ],"forget" )) {struct pathspec pathspec;
@@ -94,21 +92,21 @@ int cmd_rerere(int argc,parse_pathspec (&pathspec,0 ,PATHSPEC_PREFER_CWD , prefix, argv +1 ); - ret =rerere_forget (the_repository, &pathspec); + ret =rerere_forget (repo, &pathspec);clear_pathspec (&pathspec);return ret; }if (!strcmp (argv[0 ],"clear" )) { -rerere_clear (the_repository, &merge_rr); +rerere_clear (repo, &merge_rr); }else if (!strcmp (argv[0 ],"gc" )) -rerere_gc (the_repository, &merge_rr, +rerere_gc (repo, &merge_rr, skip_locked ?RERERE_NOWAIT :0 );else if (!strcmp (argv[0 ],"status" )) {struct rerere_cache cache; -if (rerere_cache_setup (&cache, the_repository, &merge_rr, +if (rerere_cache_setup (&cache, repo, &merge_rr, flags |RERERE_READONLY ) <0 )return 0 ;for (size_t i =0 ; i < merge_rr.nr; i++)
@@ -116,7 +114,7 @@ int cmd_rerere(int argc,rerere_cache_release (&cache); }else if (!strcmp (argv[0 ],"remaining" )) { -rerere_remaining (the_repository, &merge_rr); +rerere_remaining (repo, &merge_rr);for (size_t i =0 ; i < merge_rr.nr; i++) {if (merge_rr.items[i].util !=RERERE_RESOLVED )printf ("%s\n" , merge_rr.items[i].string);
@@ -129,16 +127,16 @@ int cmd_rerere(int argc,struct strbuf buf =STRBUF_INIT ;struct rerere_cache cache; -if (rerere_cache_setup (&cache, the_repository, &merge_rr, +if (rerere_cache_setup (&cache, repo, &merge_rr, flags |RERERE_READONLY ) <0 )return 0 ;for (size_t i =0 ; i < merge_rr.nr; i++) {const char *path = merge_rr.items[i].string;const struct rerere_id *id = merge_rr.items[i].util; -if (diff_two (rerere_path (the_repository, &buf, id,"preimage" ), +if (diff_two (rerere_path (repo, &buf, id,"preimage" ), path, path, path))die (_ ("unable to generate diff for '%s'" ), -rerere_path (the_repository, &buf, id,NULL )); +rerere_path (repo, &buf, id,NULL )); }rerere_cache_release (&cache);
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
PATCH v2 4/12 builtin/rerere: stop passing flags to subcommands that ignore them
In reply to Patrick SteinhardtThe git-rerere(1) command has a "--[no-]rerere-autoupdate" flag. If passed, we explicitly enable or disable recording clean merge resolutions that are staged in the index. This flag only matters to git-rerere(1) when not executed with any subcommand, but we also forward the flag to git rerere status and git rerere diff.
This is mostly harmless as we would ignore those flags anyway, but it makes subsequent refactorings harder to understand. Prepare for these refactorings by not using the flag in those subcommands anymore.
--- builtin/rerere.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
builtin/rerere.c +2 -2
@@ -107,7 +107,7 @@ int cmd_rerere(int argc,struct rerere_cache cache;if (rerere_cache_setup (&cache, repo, &merge_rr, - flags |RERERE_READONLY ) <0 ) +RERERE_READONLY ) <0 )return 0 ;for (size_t i =0 ; i < merge_rr.nr; i++)printf ("%s\n" , merge_rr.items[i].string);
@@ -128,7 +128,7 @@ int cmd_rerere(int argc,struct rerere_cache cache;if (rerere_cache_setup (&cache, repo, &merge_rr, - flags |RERERE_READONLY ) <0 ) +RERERE_READONLY ) <0 )return 0 ;for (size_t i =0 ; i < merge_rr.nr; i++) {const char *path = merge_rr.items[i].string;
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
PATCH v2 5/12 builtin/rerere: turn "forget" into a proper subcommand
In reply to Patrick SteinhardtThe git-rerere(1) command has its own, home-grown implementation of subcommands. Of course this implementation is inconsistent with how subcommands typically work:
- Options from other subcommands apply to each other.
- We don't have proper usage strings.
- Running git rerere <subcmd> -h doesn't print the options that are supported by that subcommand, but instead it just prints the help of git-rerere(1) itself.
A subsequent commit will thus introduce proper subcommands as supported by our parse-options subsystem. Refactor the "forget" subcommand to use parse-options as a preparatory step. The other commands will follow in subsequent commits until all of them have been converted, and then we switch over to OPT_SUBCOMMAND().
--- builtin/rerere.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-)
builtin/rerere.c +24 -14
@@ -52,6 +52,28 @@ static int diff_two(const char *file1, const char *label1,return ret; } +static int cmd_rerere_forget (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct pathspec pathspec; +int ret; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (!argc) +warning (_ ("'git rerere forget' without paths is deprecated" )); +parse_pathspec (&pathspec,0 ,PATHSPEC_PREFER_CWD , prefix, argv); + + ret =rerere_forget (repo, &pathspec); + +clear_pathspec (&pathspec); +return ret; +} + int cmd_rerere(int argc, const char **argv, const char *prefix,
@@ -83,20 +105,8 @@ int cmd_rerere(int argc,if (argc <1 )return rerere (repo, flags); -if (!strcmp (argv[0 ],"forget" )) { -struct pathspec pathspec; -int ret; - -if (argc <2 ) -warning (_ ("'git rerere forget' without paths is deprecated" )); -parse_pathspec (&pathspec,0 ,PATHSPEC_PREFER_CWD , - prefix, argv +1 ); - - ret =rerere_forget (repo, &pathspec); - -clear_pathspec (&pathspec); -return ret; - } +if (!strcmp (argv[0 ],"forget" )) +return cmd_rerere_forget (argc, argv, prefix, repo);if (!strcmp (argv[0 ],"clear" )) {rerere_clear (repo, &merge_rr);
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
PATCH v2 6/12 builtin/rerere: turn "clear" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "clear" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-)
builtin/rerere.c +22 -3
@@ -52,6 +52,26 @@ static int diff_two(const char *file1, const char *label1,return ret; } +static int cmd_rerere_clear (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +rerere_clear (repo, &merge_rr); + +string_list_clear (&merge_rr,1 ); +return 0 ; +} + static int cmd_rerere_forget(int argc, const char **argv, const char *prefix,
@@ -107,9 +127,8 @@ int cmd_rerere(int argc,if (!strcmp (argv[0 ],"forget" ))return cmd_rerere_forget (argc, argv, prefix, repo); - -if (!strcmp (argv[0 ],"clear" )) { -rerere_clear (repo, &merge_rr); +else if (!strcmp (argv[0 ],"clear" )) { +return cmd_rerere_clear (argc, argv, prefix, repo); }else if (!strcmp (argv[0 ],"gc" ))rerere_gc (repo, &merge_rr, skip_locked ?RERERE_NOWAIT :0 );
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
PATCH v2 7/12 builtin/rerere: turn "status" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "status" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-)
builtin/rerere.c +29 -11
@@ -94,6 +94,32 @@ static int cmd_rerere_forget(int argc,return ret; } +static int cmd_rerere_status (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; +struct rerere_cache cache; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +if (rerere_cache_setup (&cache, repo, &merge_rr, +RERERE_READONLY ) <0 ) +return 0 ; +for (size_t i =0 ; i < merge_rr.nr; i++) +printf ("%s\n" , merge_rr.items[i].string); + +string_list_clear (&merge_rr,1 ); +rerere_cache_release (&cache); +return 0 ; +} + int cmd_rerere(int argc, const char **argv, const char *prefix,
@@ -132,17 +158,9 @@ int cmd_rerere(int argc, }else if (!strcmp (argv[0 ],"gc" ))rerere_gc (repo, &merge_rr, skip_locked ?RERERE_NOWAIT :0 ); -else if (!strcmp (argv[0 ],"status" )) { -struct rerere_cache cache; - -if (rerere_cache_setup (&cache, repo, &merge_rr, -RERERE_READONLY ) <0 ) -return 0 ; -for (size_t i =0 ; i < merge_rr.nr; i++) -printf ("%s\n" , merge_rr.items[i].string); - -rerere_cache_release (&cache); - }else if (!strcmp (argv[0 ],"remaining" )) { +else if (!strcmp (argv[0 ],"status" )) +return cmd_rerere_status (argc, argv, prefix, repo); +else if (!strcmp (argv[0 ],"remaining" )) {rerere_remaining (repo, &merge_rr);for (size_t i =0 ; i < merge_rr.nr; i++) {if (merge_rr.items[i].util !=RERERE_RESOLVED )
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
PATCH v2 8/12 builtin/rerere: turn "remaining" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "remaining" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-)
builtin/rerere.c +31 -11
@@ -94,6 +94,34 @@ static int cmd_rerere_forget(int argc,return ret; } +static int cmd_rerere_remaining (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +rerere_remaining (repo, &merge_rr); +for (size_t i =0 ; i < merge_rr.nr; i++) { +if (merge_rr.items[i].util !=RERERE_RESOLVED ) +printf ("%s\n" , merge_rr.items[i].string); +else +/* prepare for later call to +* string_list_clear() */ + merge_rr.items[i].util =NULL ; + } + +string_list_clear (&merge_rr,1 ); +return 0 ; +} + static int cmd_rerere_status(int argc, const char **argv, const char *prefix,
@@ -160,17 +188,9 @@ int cmd_rerere(int argc, skip_locked ? RERERE_NOWAIT : 0); else if (!strcmp(argv[0], "status")) return cmd_rerere_status(argc, argv, prefix, repo); - else if (!strcmp(argv[0], "remaining")) { -rerere_remaining (repo, &merge_rr); -for (size_t i =0 ; i < merge_rr.nr; i++) { -if (merge_rr.items[i].util !=RERERE_RESOLVED ) -printf ("%s\n" , merge_rr.items[i].string); -else -/* prepare for later call to -* string_list_clear() */ - merge_rr.items[i].util =NULL ; - } - } else if (!strcmp(argv[0], "diff")) { + else if (!strcmp(argv[0], "remaining")) + return cmd_rerere_remaining(argc, argv, prefix, repo); + else if (!strcmp(argv[0], "diff")) {struct strbuf buf =STRBUF_INIT ;struct rerere_cache cache;
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
PATCH v2 9/12 builtin/rerere: turn "diff" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "diff" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 56 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 19 deletions(-)
builtin/rerere.c +37 -19
@@ -72,6 +72,40 @@ static int cmd_rerere_clear(int argc,return 0 ; } +static int cmd_rerere_diff (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; +struct strbuf buf =STRBUF_INIT ; +struct rerere_cache cache; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +if (rerere_cache_setup (&cache, repo, &merge_rr, +RERERE_READONLY ) <0 ) +return 0 ; + +for (size_t i =0 ; i < merge_rr.nr; i++) { +const char *path = merge_rr.items[i].string; +const struct rerere_id *id = merge_rr.items[i].util; +if (diff_two (rerere_path (repo, &buf, id,"preimage" ), path, path, path)) +die (_ ("unable to generate diff for '%s'" ), +rerere_path (repo, &buf, id,NULL )); + } + +string_list_clear (&merge_rr,1 ); +rerere_cache_release (&cache); +strbuf_release (&buf); +return 0 ; +} + static int cmd_rerere_forget(int argc, const char **argv, const char *prefix,
@@ -190,25 +224,9 @@ int cmd_rerere(int argc,return cmd_rerere_status (argc, argv, prefix, repo); else if (!strcmp(argv[0], "remaining")) return cmd_rerere_remaining(argc, argv, prefix, repo); - else if (!strcmp(argv[0], "diff")) { -struct strbuf buf =STRBUF_INIT ; -struct rerere_cache cache; - -if (rerere_cache_setup (&cache, repo, &merge_rr, -RERERE_READONLY ) <0 ) -return 0 ; -for (size_t i =0 ; i < merge_rr.nr; i++) { -const char *path = merge_rr.items[i].string; -const struct rerere_id *id = merge_rr.items[i].util; -if (diff_two (rerere_path (repo, &buf, id,"preimage" ), - path, path, path)) -die (_ ("unable to generate diff for '%s'" ), -rerere_path (repo, &buf, id,NULL )); - } - -rerere_cache_release (&cache); -strbuf_release (&buf); - } else + else if (!strcmp(argv[0], "diff")) + return cmd_rerere_diff(argc, argv, prefix, repo); + elseusage_with_options (rerere_usage ,options );string_list_clear (&merge_rr,1 );
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
PATCH v2 10/12 builtin/rerere: turn "gc" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "gc" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 43 ++++++++++++++++++++++++++++++------------- t/t4200-rerere.sh | 5 ----- 2 files changed, 30 insertions(+), 18 deletions(-)
builtin/rerere.c +30 -13
@@ -128,6 +128,30 @@ static int cmd_rerere_forget(int argc,return ret; } +static int cmd_rerere_gc (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +int skip_locked =0 ; +struct option options[] = { +OPT_HIDDEN_BOOL (0 ,"skip-locked" , &skip_locked, +N_ ("skip gc while another process holds the lock" )), +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +rerere_gc (repo, &merge_rr, + skip_locked ?RERERE_NOWAIT :0 ); + +string_list_clear (&merge_rr,1 ); +return 0 ; +} + static int cmd_rerere_remaining(int argc, const char **argv, const char *prefix,
@@ -187,21 +211,16 @@ int cmd_rerere(int argc,const char *prefix, struct repository *repo) { - struct string_list merge_rr = STRING_LIST_INIT_DUP; -int autoupdate =-1 , skip_locked =0 , flags =0 ; + int autoupdate = -1, flags = 0;struct option options[] = {OPT_SET_INT (0 ,"rerere-autoupdate" , &autoupdate,N_ ("register clean resolutions in index" ),1 ), -OPT_HIDDEN_BOOL (0 ,"skip-locked" , &skip_locked, -N_ ("skip gc while another process holds the lock" )),OPT_END (), }; - argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); - -if (skip_locked && (argc <1 ||strcmp (argv[0 ],"gc" ))) -die (_ ("the option '%s' requires '%s'" ),"--skip-locked" ,"gc" ); + argc =parse_options (argc, argv, prefix, options, rerere_usage, +PARSE_OPT_KEEP_UNKNOWN_OPT );repo_config (repo, git_xmerge_config,NULL );
@@ -215,11 +234,10 @@ int cmd_rerere(int argc,if (!strcmp (argv[0 ],"forget" ))return cmd_rerere_forget (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"clear" )) { +else if (!strcmp (argv[0 ],"clear" ))return cmd_rerere_clear (argc, argv, prefix, repo); - }else if (!strcmp (argv[0 ],"gc" )) -rerere_gc (repo, &merge_rr, - skip_locked ?RERERE_NOWAIT :0 ); +else if (!strcmp (argv[0 ],"gc" )) +return cmd_rerere_gc (argc, argv, prefix, repo);else if (!strcmp (argv[0 ],"status" ))return cmd_rerere_status (argc, argv, prefix, repo);else if (!strcmp (argv[0 ],"remaining" ))
@@ -229,6 +247,5 @@ int cmd_rerere(int argc,else usage_with_options (rerere_usage, options); -string_list_clear (&merge_rr,1 );return 0 ; }
t/t4200-rerere.sh +0 -5
@@ -263,11 +263,6 @@ test_expect_success 'gc --skip-locked prunes while MERGE_RR is not locked' ' test_path_is_missing $rr2/preimage ' -test_expect_success '--skip-locked is only accepted by gc' ' - test_must_fail git rerere --skip-locked clear 2>err && - test_grep "option .--skip-locked. requires .gc." err -' - test_expect_success 'a held lock is waited out within rerere.lockTimeout' ' git reset --hard && rm -rf $rr &&
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
Re: [PATCH v2 10/12] builtin/rerere: turn "gc" into a proper subcommand
Quoted 4 lines · Patrick Steinhardt
As explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "gc" subcommand to use parse-options as a preparatory step.
Okay so the commits till here are mostly straightforward extraction to prepare for the subcommand introduction. Reading on....
PATCH v2 11/12 builtin/rerere: use `OPT_SUBCOMMAND()`
In reply to Patrick SteinhardtIn git-rerere(1) we have our own custom logic to parse subcommands. In preceding commits we have refactored all of those subcommands so that they are ready to be used as OPT_SUBCOMMAND()s. Switch over to use these so that we get rid of our custom subcommand parsing.
Unfortunately this means that we have a backwards-incompatible change because we no longer accept the "--[no-]rerere-autoupdate" flag for all subcommands. But this was arguably broken and misleading as we ignore this flag for any other subcommand aynway. So we cheat a bit and simply call it a bug that those flags were accepted in the first place.
--- builtin/rerere.c | 84 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 28 deletions(-)
builtin/rerere.c +56 -28
@@ -5,6 +5,7 @@#include "rerere.h" #include "strbuf.h" #include "string-list.h" +#include "strvec.h" #include "xdiff/xdiff.h" #include "xdiff-interface.h" #include "pathspec.h"
@@ -152,6 +153,31 @@ static int cmd_rerere_gc(int argc,return 0 ; } +static int cmd_rerere_perform (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +int autoupdate =-1 ; +struct option options[] = { +OPT_SET_INT (0 ,"rerere-autoupdate" , &autoupdate, +N_ ("register clean resolutions in index" ),1 ), +OPT_END (), + }; +enum rerere_flags flags =0 ; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +if (autoupdate ==1 ) + flags =RERERE_AUTOUPDATE ; +if (autoupdate ==0 ) + flags =RERERE_NOAUTOUPDATE ; + +return rerere (repo, flags); +} + static int cmd_rerere_remaining(int argc, const char **argv, const char *prefix,
@@ -211,41 +237,43 @@ int cmd_rerere(int argc,const char *prefix, struct repository *repo) { - int autoupdate = -1, flags = 0; - + parse_opt_subcommand_fn *fn = NULL;struct option options[] = { -OPT_SET_INT (0 ,"rerere-autoupdate" , &autoupdate, -N_ ("register clean resolutions in index" ),1 ), +OPT_SUBCOMMAND ("clear" , &fn, cmd_rerere_clear), +OPT_SUBCOMMAND ("diff" , &fn, cmd_rerere_diff), +OPT_SUBCOMMAND ("forget" , &fn, cmd_rerere_forget), +OPT_SUBCOMMAND ("gc" , &fn, cmd_rerere_gc), +OPT_SUBCOMMAND ("remaining" , &fn, cmd_rerere_remaining), +OPT_SUBCOMMAND ("status" , &fn, cmd_rerere_status),OPT_END (), }; argc =parse_options (argc, argv, prefix, options, rerere_usage, -PARSE_OPT_KEEP_UNKNOWN_OPT ); - +PARSE_OPT_KEEP_UNKNOWN_OPT |PARSE_OPT_SUBCOMMAND_OPTIONAL );repo_config (repo, git_xmerge_config,NULL ); -if (autoupdate ==1 ) - flags =RERERE_AUTOUPDATE ; -if (autoupdate ==0 ) - flags =RERERE_NOAUTOUPDATE ; +if (!fn) { +struct strvec args =STRVEC_INIT ; +const char **args_copy; +int ret; -if (argc <1 ) -return rerere (repo, flags); - -if (!strcmp (argv[0 ],"forget" )) -return cmd_rerere_forget (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"clear" )) -return cmd_rerere_clear (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"gc" )) -return cmd_rerere_gc (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"status" )) -return cmd_rerere_status (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"remaining" )) -return cmd_rerere_remaining (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"diff" )) -return cmd_rerere_diff (argc, argv, prefix, repo); -else -usage_with_options (rerere_usage, options); +strvec_push (&args,"perform" ); +strvec_pushv (&args, argv); -return 0 ; +/* +* We need to copy the array as it will be modified by +* `parse_options()`, and if so we leak the strings +* referenced by `args`. +*/ +CALLOC_ARRAY (args_copy, args.nr); +COPY_ARRAY (args_copy, args.v, args.nr); + + ret =cmd_rerere_perform (args.nr, args_copy, prefix, repo); + +strvec_clear (&args); +free (args_copy); +return ret; + } + +return fn (argc, argv, prefix, repo); }
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
Re: [PATCH v2 11/12] builtin/rerere: use `OPT_SUBCOMMAND()`
Quoted 11 lines · Patrick Steinhardt
In git-rerere(1) we have our own custom logic to parse subcommands. In preceding commits we have refactored all of those subcommands so that they are ready to be used as
OPT_SUBCOMMAND()s. Switch over to use these so that we get rid of our custom subcommand parsing.Unfortunately this means that we have a backwards-incompatible change because we no longer accept the "--[no-]rerere-autoupdate" flag for all subcommands. But this was arguably broken and misleading as we ignore this flag for any other subcommand aynway. So we cheat a bit and simply call it a bug that those flags were accepted in the first place.
Okay.
Quoted 36 lines
Signed-off-by Patrick Steinhardt <[email protected]>--- builtin/rerere.c | 84 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 28 deletions(-)builtin/rerere.c +16
-0@@ -5,6 +5,7 @@#include "rerere.h" #include "strbuf.h" #include "string-list.h" +#include "strvec.h" #include "xdiff/xdiff.h" #include "xdiff-interface.h" #include "pathspec.h" @@ -152,6 +153,31 @@ static int cmd_rerere_gc(int argc,return 0 ; } +static int cmd_rerere_perform (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +int autoupdate =-1 ; +struct option options[] = { +OPT_SET_INT (0 ,"rerere-autoupdate" , &autoupdate, +N_ ("register clean resolutions in index" ),1 ), +OPT_END (), + }; +enum rerere_flags flags =0 ; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc)
builtin/rerere.c static int cmd_rerere_gc(int argc, · line 180
Quoted 10 lines
builtin/rerere.c +10
-0+usage (_ ("too many arguments" )); + +if (autoupdate ==1 ) + flags =RERERE_AUTOUPDATE ; +if (autoupdate ==0 ) + flags =RERERE_NOAUTOUPDATE ; + +return rerere (repo, flags); +} +
So I guess this would be the default case if no subcommand is used, meaning the subcommands should be optional.
Quoted 16 lines
builtin/rerere.c +4
-4static int cmd_rerere_remaining(int argc, const char **argv, const char *prefix,@@ -211,41 +237,43 @@ int cmd_rerere(int argc,const char *prefix, struct repository *repo) { - int autoupdate = -1, flags = 0; - + parse_opt_subcommand_fn *fn = NULL; struct option options[] = { - OPT_SET_INT(0, "rerere-autoupdate", &autoupdate, - N_("register clean resolutions in index"), 1), + OPT_SUBCOMMAND("clear", &fn, cmd_rerere_clear), + OPT_SUBCOMMAND("diff", &fn, cmd_rerere_diff), + OPT_SUBCOMMAND("forget", &fn, cmd_rerere_forget),
builtin/rerere.c int cmd_rerere(int argc, · line 252
Quoted 10 lines
builtin/rerere.c +4
-2+OPT_SUBCOMMAND ("gc" , &fn, cmd_rerere_gc), +OPT_SUBCOMMAND ("remaining" , &fn, cmd_rerere_remaining), +OPT_SUBCOMMAND ("status" , &fn, cmd_rerere_status),OPT_END (), }; argc =parse_options (argc, argv, prefix, options, rerere_usage, -PARSE_OPT_KEEP_UNKNOWN_OPT ); - +PARSE_OPT_KEEP_UNKNOWN_OPT |PARSE_OPT_SUBCOMMAND_OPTIONAL );
So this matches what I thought.
builtin/rerere.c int cmd_rerere(int argc, · line 255
Quoted 7 lines
builtin/rerere.c +1
-4repo_config (repo, git_xmerge_config,NULL ); -if (autoupdate ==1 ) - flags =RERERE_AUTOUPDATE ; -if (autoupdate ==0 ) - flags =RERERE_NOAUTOUPDATE ; +if (!fn) {
Okay if there's no function we call the default case. I wonder if we could've done the inverse to reduce code block. if (fn) reutrn fn(...). Doesn't matter much anyway.
Quoted 12 lines
builtin/rerere.c +3
-8+struct strvec args =STRVEC_INIT ; +const char **args_copy; +int ret; -if (argc <1 ) -return rerere (repo, flags); - -if (!strcmp (argv[0 ],"forget" )) -return cmd_rerere_forget (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"clear" )) -return cmd_rerere_clear (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"gc" ))
builtin/rerere.c int cmd_rerere(int argc, · line 260
Quoted 10 lines
builtin/rerere.c +1
-9-return cmd_rerere_gc (argc, argv, prefix, repo); - else if (!strcmp(argv[0], "status")) - return cmd_rerere_status(argc, argv, prefix, repo); - else if (!strcmp(argv[0], "remaining")) - return cmd_rerere_remaining(argc, argv, prefix, repo); - else if (!strcmp(argv[0], "diff")) - return cmd_rerere_diff(argc, argv, prefix, repo); - else -usage_with_options (rerere_usage ,options ); +strvec_push (&args,"perform" );
Worthwhile adding a comment that this is a dummy placeholder subcommand?
builtin/rerere.c int cmd_rerere(int argc, · line 270
Quoted 11 lines
builtin/rerere.c +9
-1+strvec_pushv (&args, argv); -return 0 ; +/* +* We need to copy the array as it will be modified by +* `parse_options()`, and if so we leak the strings +* referenced by `args`. +*/ +CALLOC_ARRAY (args_copy, args.nr); +COPY_ARRAY (args_copy, args.v, args.nr); +
Since we need to add the dummy 'perform' and don't want to clear the actual argv, we copy it over. makes sense.
Quoted 12 lines
builtin/rerere.c +8
-1+ ret =cmd_rerere_perform (args.nr, args_copy, prefix, repo); + +strvec_clear (&args); +free (args_copy); +return ret; + } + +return fn (argc, argv, prefix, repo); } --2.56.0.341.g393f52fd96.dirty
Re: [PATCH v2 11/12] builtin/rerere: use `OPT_SUBCOMMAND()`
Quoted 33 lines · Karthik Nayak
Quoted 17 lines · Patrick Steinhardt
builtin/rerere.c +4
-4@@ -211,41 +237,43 @@ int cmd_rerere(int argc,const char *prefix, struct repository *repo) { - int autoupdate = -1, flags = 0; - + parse_opt_subcommand_fn *fn = NULL; struct option options[] = { - OPT_SET_INT(0, "rerere-autoupdate", &autoupdate, - N_("register clean resolutions in index"), 1), + OPT_SUBCOMMAND("clear", &fn, cmd_rerere_clear), + OPT_SUBCOMMAND("diff", &fn, cmd_rerere_diff), + OPT_SUBCOMMAND("forget", &fn, cmd_rerere_forget),builtin/rerere.c int cmd_rerere(int argc, · line 252
Quoted 10 lines
builtin/rerere.c +4
-2+OPT_SUBCOMMAND ("gc" , &fn, cmd_rerere_gc), +OPT_SUBCOMMAND ("remaining" , &fn, cmd_rerere_remaining), +OPT_SUBCOMMAND ("status" , &fn, cmd_rerere_status),OPT_END (), }; argc =parse_options (argc, argv, prefix, options, rerere_usage, -PARSE_OPT_KEEP_UNKNOWN_OPT ); - +PARSE_OPT_KEEP_UNKNOWN_OPT |PARSE_OPT_SUBCOMMAND_OPTIONAL );So this matches what I thought.
Quoted 2 lines
builtin/rerere.c +0
-0repo_config (repo, git_xmerge_config,NULL );
Quoted 9 lines
builtin/rerere.c int cmd_rerere(int argc, · line 255
Quoted 5 lines
builtin/rerere.c +1
-4-if (autoupdate ==1 ) - flags =RERERE_AUTOUPDATE ; -if (autoupdate ==0 ) - flags =RERERE_NOAUTOUPDATE ; +if (!fn) {Okay if there's no function we call the default case. I wonder if we could've done the inverse to reduce code block. if (fn) reutrn fn(...). Doesn't matter much anyway.
I think that the result is actually harder to read in that case, even though that might just be my personal opinion. But having a return in the middle of the function is awkward, and I like that the variables that are specific to the default subcommand are nicely scoped to the block so that they obviously cannot be used by any other subcommand.
So I prefer to keep this as-is.
Quoted 16 lines
Quoted 16 lines
builtin/rerere.c +3
-12+struct strvec args =STRVEC_INIT ; +const char **args_copy; +int ret; -if (argc <1 ) -return rerere (repo, flags); - -if (!strcmp (argv[0 ],"forget" )) -return cmd_rerere_forget (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"clear" )) -return cmd_rerere_clear (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"gc" )) -return cmd_rerere_gc (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"status" )) -return cmd_rerere_status (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"remaining" ))
Quoted 8 lines
builtin/rerere.c int cmd_rerere(int argc, · line 260
Quoted 6 lines
builtin/rerere.c +1
-5-return cmd_rerere_remaining (argc, argv, prefix, repo); - else if (!strcmp(argv[0], "diff")) - return cmd_rerere_diff(argc, argv, prefix, repo); - else -usage_with_options (rerere_usage ,options ); +strvec_push (&args,"perform" );Worthwhile adding a comment that this is a dummy placeholder subcommand?
Hmm. I could add something like this:
builtin/rerere.c +1 -0
@@ -257,6 +257,7 @@ int cmd_rerere(int argc,const char **args_copy;int ret; +/* This is a dummy subcommand name. */ strvec_push (&args,"perform" );strvec_pushv (&args, argv);
But does that really add much value? I myself don't think so, but if you feel that it does I'm happy to add it in.
Thanks!
Patrick
PATCH v2 12/12 builtin/rerere: introduce proper usage strings for subcommands
In reply to Patrick SteinhardtAdapt git-rerere(1) to have proper usage strings for each of the subcommands.
--- Documentation/git-rerere.adoc | 8 +++++- builtin/rerere.c | 61 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 11 deletions(-)
Documentation/git-rerere.adoc +7 -1
@@ -8,7 +8,13 @@ git-rerere - Reuse recorded resolution of conflicted merges SYNOPSIS -------- [verse] -'git rerere' [clear | forget <pathspec>... | diff | status | remaining | gc] +'git rerere' [--[no-]rerere-autoupdate] +'git rerere' clear +'git rerere' diff +'git rerere' forget <pathspec>... +'git rerere' gc +'git rerere' remaining +'git rerere' status DESCRIPTION -----------
builtin/rerere.c +51 -10
131 lines not on this page · raw message
Signature · 1 line
2.56.0.341.g393f52fd96.dirty
Quoted 8 lines · Patrick Steinhardt
Hi,
while reviewing some patch series around git-rerere(1) recently I was a bit appalled by our custom subcommand parsing that we do here. This patch series here thus cleans up and modernizes that part a bit so that git-rerere(1) behaves like every other Git command that happens to have subcommands.
Quoted 4 lines
This series is based on
37b3475fb53(The 9th batch, 2026-10-25) with ps/rerere-remove-global-state atb948c10c8b3(rerere: get rid of global rerere cache state, 2026-10-23) merged into it.
Looks to be in good shape!
Thanks
v3 cover builtin/rerere: convert to use subcommands
In reply to Patrick SteinhardtHi,
while reviewing some patch series around git-rerere(1) recently I was a bit appalled by our custom subcommand parsing that we do here. This patch series here thus cleans up and modernizes that part a bit so that git-rerere(1) behaves like every other Git command that happens to have subcommands.
This series is based on 37b3475fb53 (The 9th batch, 2026-10-25) with ps/rerere-remove-global-state at b948c10c8b3 (rerere: get rid of global rerere cache state, 2026-10-23) merged into it.
Changes in v3:
- Clarify what RERERE_NOAUTOUPDATE does.
- Link to v2: https://patch.msgid.link/[email protected]
Changes in v2:
- Document rerere flags.
- Rename rerere_record() to just rerere().
- Link to v1: https://patch.msgid.link/[email protected]
Thanks!
Patrick
---
Patrick Steinhardt (12):
rerere: rename repo_rerere()
rerere: convert flags into an enum
builtin/rerere: drop the_repository
builtin/rerere: stop passing flags to subcommands that ignore them builtin/rerere: turn "forget" into a proper subcommand builtin/rerere: turn "clear" into a proper subcommand builtin/rerere: turn "status" into a proper subcommand builtin/rerere: turn "remaining" into a proper subcommand builtin/rerere: turn "diff" into a proper subcommand builtin/rerere: turn "gc" into a proper subcommand builtin/rerere: use OPT_SUBCOMMAND()
builtin/rerere: introduce proper usage strings for subcommands
Documentation/git-rerere.adoc | 8 +- apply.c | 2 +- builtin/am.c | 4 +- builtin/commit.c | 2 +- builtin/merge.c | 2 +- builtin/rerere.c | 319 ++++++++++++++++++++++++++++++++---------- builtin/stash.c | 2 +- rerere.c | 12 +- rerere.h | 40 ++++-- sequencer.c | 4 +- t/t4200-rerere.sh | 5 - 11 files changed, 294 insertions(+), 106 deletions(-)
Range-diff versus v2:
1: 8d6b21c1ea2 = 1: 022fe75450a rerere: rename `repo_rerere()`
2: 3dd2a9cc7e0 ! 2: 1cb50bf5937 rerere: convert flags into an enum
@@ rerere.h: struct rerere_id {
+ */
+ RERERE_AUTOUPDATE = (1 << 0),
+
-+ /* Reverse of RERERE_AUTOUPDATE. */
++ /*
++ * Reverse of RERERE_AUTOUPDATE. That is, update the file in the
++ * working tree but don't touch the index.
++ */
+ RERERE_NOAUTOUPDATE = (1 << 1),
+
+ /* Skip acquiring the MERGE_RR.lock for a read-only operation. */
3: a69e7f43d2b = 3: d18adf008c9 builtin/rerere: drop `the_repository`
4: 86654ba3606 = 4: bc2eea0ba84 builtin/rerere: stop passing flags to subcommands that ignore them
5: f532910d986 = 5: 2b45e209b08 builtin/rerere: turn "forget" into a proper subcommand
6: 6340e1db614 = 6: 8e1a5cf4239 builtin/rerere: turn "clear" into a proper subcommand
7: 1c623efc48e = 7: aede0d76e54 builtin/rerere: turn "status" into a proper subcommand
8: debe0999ecb = 8: 114e40d5a51 builtin/rerere: turn "remaining" into a proper subcommand
9: 00bcc60a186 = 9: 7c523f60339 builtin/rerere: turn "diff" into a proper subcommand
10: 9cc14c726eb = 10: 6962d3ae764 builtin/rerere: turn "gc" into a proper subcommand
11: 8c90e572751 = 11: d9021a91b8e builtin/rerere: use `OPT_SUBCOMMAND()`
12: 11baa9ee058 = 12: 3b59da3d583 builtin/rerere: introduce proper usage strings for subcommands---
base-commit: 710910c03a22ac663885049213b9fef5853ea8e0
change-id: 20261027-pks-rerere-subcommands-318d41553438
PATCH v3 1/12 rerere: rename `repo_rerere()`
The repo_rerere() function is the only function in "rerere.h" that does not have a rerere_ prefix. And while it does indeed receive a repository as first parameter, that's not a good-enough reason to give it a repo_ prefix.
Rename the function to just rerere(). Move around and extend documentation of this function to better explain what it does.
--- apply.c | 2 +- builtin/am.c | 4 ++-- builtin/commit.c | 2 +- builtin/merge.c | 2 +- builtin/rerere.c | 2 +- builtin/stash.c | 2 +- rerere.c | 7 +------ rerere.h | 9 ++++++++- sequencer.c | 4 ++-- 9 files changed, 18 insertions(+), 16 deletions(-)
apply.c +1 -1
@@ -4866,7 +4866,7 @@ static int write_out_results(struct apply_state *state, struct patch *list)* tree with conflict markers, but that isn't written with --cached. */ if (!state->cached) -repo_rerere (state->repo,0 ); +rerere (state->repo,0 ); }return errs;
builtin/am.c +2 -2
@@ -1649,7 +1649,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa o.verbosity =0 ;if (merge_ort_generic (&o, &our_tree, &their_tree,1 , bases, &result)) { -repo_rerere (the_repository, state->allow_rerere_autoupdate); +rerere (the_repository, state->allow_rerere_autoupdate);free (their_tree_name);return error (_ ("Failed to merge in the changes." )); }
@@ -1979,7 +1979,7 @@ static void am_resolve(struct am_state *state, int allow_empty) goto next; } -repo_rerere (the_repository,0 ); +rerere (the_repository,0 );do_commit (state);
builtin/commit.c +1 -1
@@ -1962,7 +1962,7 @@ int cmd_commit(int argc,git_test_write_commit_graph_or_die (the_repository); -repo_rerere (the_repository,0 ); +rerere (the_repository,0 );run_auto_maintenance (the_repository, quiet);run_commit_hook (use_editor,repo_get_index_file (the_repository),NULL ,"post-commit" ,NULL );
builtin/merge.c +1 -1
@@ -1061,7 +1061,7 @@ static int suggest_conflicts(void)fputs (msgbuf.buf, fp);strbuf_release (&msgbuf);fclose (fp); -repo_rerere (the_repository, allow_rerere_auto); +rerere (the_repository, allow_rerere_auto);printf (_ ("Automatic merge failed; " "fix conflicts and then commit the result.\n" ));return 1 ;
builtin/rerere.c +1 -1
@@ -83,7 +83,7 @@ int cmd_rerere(int argc, flags =RERERE_NOAUTOUPDATE ;if (argc <1 ) -return repo_rerere (the_repository, flags); +return rerere (the_repository, flags);if (!strcmp (argv[0 ],"forget" )) {struct pathspec pathspec;
builtin/stash.c +1 -1
@@ -688,7 +688,7 @@ static enum stash_apply_result do_apply_stash(const char *prefix, ret =error (_ ("could not write index" ));if (ret) { -repo_rerere (the_repository,0 ); +rerere (the_repository,0 );if (index)fprintf_ln (stderr,_ ("Index was not unstashed." ));
rerere.c +1 -6
@@ -951,12 +951,7 @@ int rerere_cache_setup(struct rerere_cache *cache,return fd; } -/* -* The main entry point that is called internally from codepaths that -* perform mergy operations, possibly leaving conflicted index entries -* and working tree files. -*/ -int repo_rerere (struct repository *r,int flags) +int rerere (struct repository *r,int flags) {struct string_list merge_rr =STRING_LIST_INIT_DUP ;struct rerere_cache cache;
rerere.h +8 -1
@@ -45,7 +45,14 @@ int rerere_cache_setup(struct rerere_cache *cache, int flags);void rerere_cache_release (struct rerere_cache *cache); -int repo_rerere (struct repository *,int ); +/* +* The main entry point that is called internally from codepaths that +* perform mergy operations, possibly leaving conflicted index entries +* and working tree files. It records (and optionally updates) resolved +* conflicts and reuses recorded resolutions for unresolved conflicts. +*/ +int rerere (struct repository *r,int flags); +/* * Given the conflict ID and the name of a "file" used for replaying * the recorded resolution (e.g. "preimage", "postimage"), return the
sequencer.c +2 -2
@@ -2556,7 +2556,7 @@ static enum pick_result do_pick_commit(struct repository *r, : _("could not apply %s... %s"), short_commit_name(r, commit), msg.subject);print_advice (r, res ==1 , opts); -repo_rerere (r, opts->allow_rerere_auto); +rerere (r, opts->allow_rerere_auto); goto leave; }
@@ -4488,7 +4488,7 @@ static int do_merge(struct repository *r,rollback_lock_file (&lock);if (ret) -repo_rerere (r, opts->allow_rerere_auto); +rerere (r, opts->allow_rerere_auto);else /* * In case of problems, we now want to return a positive
Signature · 1 line
2.56.0.500.g5d06dc02cd.dirty
PATCH v3 2/12 rerere: convert flags into an enum
In reply to Patrick SteinhardtOur modern coding guidelines say that flags should typically be passed around as enums so that it becomes trivial to see what kind of flags are expected. Adapt the rerere flags accordingly and document them.
--- rerere.c | 7 ++++--- rerere.h | 33 ++++++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 12 deletions(-)
rerere.c +4 -3
@@ -908,7 +908,7 @@ static int is_rerere_enabled(struct repository *r)int rerere_cache_setup (struct rerere_cache *cache,struct repository *r,struct string_list *merge_rr, -int flags) +enum rerere_flags flags) {int lock_timeout_ms =1000 ;int fd;
@@ -951,7 +951,7 @@ int rerere_cache_setup(struct rerere_cache *cache,return fd; } -int rerere (struct repository *r,int flags) +int rerere (struct repository *r,enum rerere_flags flags) {struct string_list merge_rr =STRING_LIST_INIT_DUP ;struct rerere_cache cache;
@@ -1336,7 +1336,8 @@ bool rerere_gc_needed(struct repository *r, size_t limit)return needed; } -void rerere_gc (struct repository *r,struct string_list *rr,int flags) +void rerere_gc (struct repository *r,struct string_list *rr, +enum rerere_flags flags) {struct rerere_cache cache;struct string_list to_remove =STRING_LIST_INIT_DUP ;
rerere.h +24 -9
@@ -9,12 +9,6 @@struct pathspec ;struct repository ; -#define RERERE_AUTOUPDATE 01 -#define RERERE_NOAUTOUPDATE 02 -#define RERERE_READONLY 04 -/* If MERGE_RR.lock is taken, return -1 as if rerere were disabled */ -#define RERERE_NOWAIT 010 -/* * Marks paths that have been hand-resolved and added to the * index. Set in the util field of such paths after calling
@@ -39,10 +33,30 @@ struct rerere_id {int variant; }; +enum rerere_flags { +/* +* Auto-update the index with previously recorded resolutions. This can +* be used to explicitly override "rerere.autoUpdate". +*/ +RERERE_AUTOUPDATE = (1 <<0 ), + +/* +* Reverse of RERERE_AUTOUPDATE. That is, update the file in the +* working tree but don't touch the index. +*/ +RERERE_NOAUTOUPDATE = (1 <<1 ), + +/* Skip acquiring the MERGE_RR.lock for a read-only operation. */ +RERERE_READONLY = (1 <<2 ), + +/* If MERGE_RR.lock is taken, return -1 as if rerere were disabled. */ +RERERE_NOWAIT = (1 <<3 ), +}; +int rerere_cache_setup (struct rerere_cache *cache,struct repository *r,struct string_list *merge_rr, -int flags); +enum rerere_flags flags);void rerere_cache_release (struct rerere_cache *cache);/*
@@ -51,7 +65,7 @@ void rerere_cache_release(struct rerere_cache *cache);* and working tree files. It records (and optionally updates) resolved * conflicts and reuses recorded resolutions for unresolved conflicts. */ -int rerere (struct repository *r,int flags); +int rerere (struct repository *r,enum rerere_flags flags);/* * Given the conflict ID and the name of a "file" used for replaying
@@ -65,7 +79,8 @@ const char *rerere_path(struct repository *,int rerere_forget (struct repository *,struct pathspec *);int rerere_remaining (struct repository *,struct string_list *);void rerere_clear (struct repository *,struct string_list *); -void rerere_gc (struct repository *,struct string_list *,int ); +void rerere_gc (struct repository *,struct string_list *, +enum rerere_flags flags);/* * Check whether garbage collection for rerere entries is needed, which is
Signature · 1 line
2.56.0.500.g5d06dc02cd.dirty
Re: [PATCH v3 02/12] rerere: convert flags into an enum
rerere.h struct rerere_id { · line 47
Quoted 12 lines · Patrick Steinhardt
rerere.h +12
-0+enum rerere_flags { +/* +* Auto-update the index with previously recorded resolutions. This can +* be used to explicitly override "rerere.autoUpdate". +*/ +RERERE_AUTOUPDATE = (1 <<0 ), + +/* +* Reverse of RERERE_AUTOUPDATE. That is, update the file in the +* working tree but don't touch the index. +*/ +RERERE_NOAUTOUPDATE = (1 <<1 ),
As you have updated the comment in this iteration, I'll throw a bit of review in. The new comment is not wrong per-se, but the one from the previous iteration was sufficient to convey the same thing. In other words, I see no improvement in v3 over what you had in v2 for this bit.
What BOTH versions lack is an explanation why RERERE_NOAUTOUPDATE is needed in the first place. In other words, "How is it different from the lack of RERERE_AUTOUPDATE bit?" is not answered here. And I think that is a lot more important thing to clarify.
Quoted 6 lines
rerere.h +6
-0+/* Skip acquiring the MERGE_RR.lock for a read-only operation. */ +RERERE_READONLY = (1 <<2 ), + +/* If MERGE_RR.lock is taken, return -1 as if rerere were disabled. */ + RERERE_NOWAIT = (1 << 3), +};
Re: [PATCH v3 02/12] rerere: convert flags into an enum
Quoted 12 lines · Junio C Hamano
Quoted 10 lines · Patrick Steinhardt
rerere.h +10
-0+enum rerere_flags { +/* +* Auto-update the index with previously recorded resolutions. This can +* be used to explicitly override "rerere.autoUpdate". +*/ +RERERE_AUTOUPDATE = (1 <<0 ), + +/* +* Reverse of RERERE_AUTOUPDATE. That is, update the file in the +* working tree but don't touch the index.
Quoted 8 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ */ + RERERE_NOAUTOUPDATE = (1 << 1),As you have updated the comment in this iteration, I'll throw a bit of review in. The new comment is not wrong per-se, but the one from the previous iteration was sufficient to convey the same thing. In other words, I see no improvement in v3 over what you had in v2 for this bit.
I wouldn't necessarily say so, as an unknowing reader might not know that we still update the working tree. I myself certainly didn't, and I had to dig a bit to figure out what happens with this flag, mostly because I am not familiar with the rerere subsystem.
Quoted 4 lines
What BOTH versions lack is an explanation why
RERERE_NOAUTOUPDATEis needed in the first place. In other words, "How is it different from the lack ofRERERE_AUTOUPDATEbit?" is not answered here. And I think that is a lot more important thing to clarify.
Heh, in this case _I_ thought that it's implicitly clear from the comment of RERERE_AUTOUPDATE: it overrides "rerere.autoUpdate". I figured that updating these comments would cause some discussions, and that's exactly why I didn't dare to update them at all in v1 :)
How about this instead:
Only update the file in the working tree, but don't touch the index. This can be passed to explicitly override "rerere.autoUpdate".
Patrick
Re: [PATCH v3 02/12] rerere: convert flags into an enum
Quoted 9 lines · Patrick Steinhardt
Quoted 4 lines
What BOTH versions lack is an explanation why
RERERE_NOAUTOUPDATEis needed in the first place. In other words, "How is it different from the lack ofRERERE_AUTOUPDATEbit?" is not answered here. And I think that is a lot more important thing to clarify.Heh, in this case _I_ thought that it's implicitly clear from the comment of
RERERE_AUTOUPDATE: it overrides "rerere.autoUpdate". I figured that updating these comments would cause some discussions, and that's exactly why I didn't dare to update them at all in v1 :)
;-)
Re: [PATCH v3 02/12] rerere: convert flags into an enum
Quoted 7 lines · Junio C Hamano
Quoted 5 lines · Patrick Steinhardt
Quoted 4 lines
What BOTH versions lack is an explanation why
RERERE_NOAUTOUPDATEis needed in the first place. In other words, "How is it different from the lack ofRERERE_AUTOUPDATEbit?" is not answered here. And I think that is a lot more important thing to clarify.
Quoted 6 lines
Quoted 4 lines
Heh, in this case _I_ thought that it's implicitly clear from the comment of
RERERE_AUTOUPDATE: it overrides "rerere.autoUpdate". I figured that updating these comments would cause some discussions, and that's exactly why I didn't dare to update them at all in v1 :);-)
The command line can give us not two (which could be represented with a single bit) but three choices, (0) the command line has no preference on --[no-]autoupdate and follow the configured default,
(1) --autoupdate, or (2) --no-autoupdate, and that is exactly why we have these two independent-looking bits.
But two bits can express another combination (i.e., both bits on), which probably is invalid. The underlying OPT_UYN() calls parse_opt_tertiary() which only chooses between 1 and 2 so we should be safe on the setting side, but I offhand do not remember if we detect when both bits are on and BUG() out where we consume the value.
Thanks.
Re: [PATCH v3 02/12] rerere: convert flags into an enum
Quoted 22 lines · Junio C Hamano
Quoted 7 lines · Junio C Hamano
Quoted 5 lines · Patrick Steinhardt
Quoted 4 lines
What BOTH versions lack is an explanation why
RERERE_NOAUTOUPDATEis needed in the first place. In other words, "How is it different from the lack ofRERERE_AUTOUPDATEbit?" is not answered here. And I think that is a lot more important thing to clarify.Quoted 6 lines
Quoted 4 lines
Heh, in this case _I_ thought that it's implicitly clear from the comment of
RERERE_AUTOUPDATE: it overrides "rerere.autoUpdate". I figured that updating these comments would cause some discussions, and that's exactly why I didn't dare to update them at all in v1 :);-)
The command line can give us not two (which could be represented with a single bit) but three choices, (0) the command line has no preference on --[no-]autoupdate and follow the configured default,
(1) --autoupdate, or (2) --no-autoupdate, and that is exactly why we have these two independent-looking bits.
Quoted 6 lines
But two bits can express another combination (i.e., both bits on), which probably is invalid. The underlying
OPT_UYN()callsparse_opt_tertiary()which only chooses between 1 and 2 so we should be safe on the setting side, but I offhand do not remember if we detect when both bits are on andBUG()out where we consume the value.
We have this bit in rerere_cache_setup():
if (flags & (RERERE_AUTOUPDATE|RERERE_NOAUTOUPDATE)) cache->autoupdate = !!(flags & RERERE_AUTOUPDATE);
So RERERE_AUTOUPDATE takes precedence over RERERE_NOAUTOUPDATE, but all callers know to only pass one of these flags. I agree though that we should detect this misuse and complain about it, it's certainly not obvious at all.
Patrick
PATCH v3 3/12 builtin/rerere: drop `the_repository`
In reply to Patrick SteinhardtThere are a couple of references to the_repository in git-rerere(1). As that command cannot run outside of a repository these references can be trivially replaced by the repository that's passed in as parameter to cmd_rerere(). We don't have to worry about the repository being NULL when called with "-h", either, as there are no references to the repo before we call parse_options().
--- builtin/rerere.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-)
builtin/rerere.c +11 -13
@@ -1,5 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE -#include "builtin.h" #include "config.h" #include "gettext.h"
@@ -57,7 +55,7 @@ static int diff_two(const char *file1, const char *label1,int cmd_rerere (int argc,const char **argv,const char *prefix, - struct repository *repo UNUSED) +struct repository *repo) {struct string_list merge_rr =STRING_LIST_INIT_DUP ;int autoupdate =-1 , skip_locked =0 , flags =0 ;
@@ -75,7 +73,7 @@ int cmd_rerere(int argc,if (skip_locked && (argc <1 ||strcmp (argv[0 ],"gc" )))die (_ ("the option '%s' requires '%s'" ),"--skip-locked" ,"gc" ); -repo_config (the_repository, git_xmerge_config,NULL ); +repo_config (repo, git_xmerge_config,NULL );if (autoupdate ==1 ) flags =RERERE_AUTOUPDATE ;
@@ -83,7 +81,7 @@ int cmd_rerere(int argc, flags =RERERE_NOAUTOUPDATE ;if (argc <1 ) -return rerere (the_repository, flags); +return rerere (repo, flags);if (!strcmp (argv[0 ],"forget" )) {struct pathspec pathspec;
@@ -94,21 +92,21 @@ int cmd_rerere(int argc,parse_pathspec (&pathspec,0 ,PATHSPEC_PREFER_CWD , prefix, argv +1 ); - ret =rerere_forget (the_repository, &pathspec); + ret =rerere_forget (repo, &pathspec);clear_pathspec (&pathspec);return ret; }if (!strcmp (argv[0 ],"clear" )) { -rerere_clear (the_repository, &merge_rr); +rerere_clear (repo, &merge_rr); }else if (!strcmp (argv[0 ],"gc" )) -rerere_gc (the_repository, &merge_rr, +rerere_gc (repo, &merge_rr, skip_locked ?RERERE_NOWAIT :0 );else if (!strcmp (argv[0 ],"status" )) {struct rerere_cache cache; -if (rerere_cache_setup (&cache, the_repository, &merge_rr, +if (rerere_cache_setup (&cache, repo, &merge_rr, flags |RERERE_READONLY ) <0 )return 0 ;for (size_t i =0 ; i < merge_rr.nr; i++)
@@ -116,7 +114,7 @@ int cmd_rerere(int argc,rerere_cache_release (&cache); }else if (!strcmp (argv[0 ],"remaining" )) { -rerere_remaining (the_repository, &merge_rr); +rerere_remaining (repo, &merge_rr);for (size_t i =0 ; i < merge_rr.nr; i++) {if (merge_rr.items[i].util !=RERERE_RESOLVED )printf ("%s\n" , merge_rr.items[i].string);
@@ -129,16 +127,16 @@ int cmd_rerere(int argc,struct strbuf buf =STRBUF_INIT ;struct rerere_cache cache; -if (rerere_cache_setup (&cache, the_repository, &merge_rr, +if (rerere_cache_setup (&cache, repo, &merge_rr, flags |RERERE_READONLY ) <0 )return 0 ;for (size_t i =0 ; i < merge_rr.nr; i++) {const char *path = merge_rr.items[i].string;const struct rerere_id *id = merge_rr.items[i].util; -if (diff_two (rerere_path (the_repository, &buf, id,"preimage" ), +if (diff_two (rerere_path (repo, &buf, id,"preimage" ), path, path, path))die (_ ("unable to generate diff for '%s'" ), -rerere_path (the_repository, &buf, id,NULL )); +rerere_path (repo, &buf, id,NULL )); }rerere_cache_release (&cache);
Signature · 1 line
2.56.0.500.g5d06dc02cd.dirty
PATCH v3 4/12 builtin/rerere: stop passing flags to subcommands that ignore them
In reply to Patrick SteinhardtThe git-rerere(1) command has a "--[no-]rerere-autoupdate" flag. If passed, we explicitly enable or disable recording clean merge resolutions that are staged in the index. This flag only matters to git-rerere(1) when not executed with any subcommand, but we also forward the flag to git rerere status and git rerere diff.
This is mostly harmless as we would ignore those flags anyway, but it makes subsequent refactorings harder to understand. Prepare for these refactorings by not using the flag in those subcommands anymore.
--- builtin/rerere.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
builtin/rerere.c +2 -2
@@ -107,7 +107,7 @@ int cmd_rerere(int argc,struct rerere_cache cache;if (rerere_cache_setup (&cache, repo, &merge_rr, - flags |RERERE_READONLY ) <0 ) +RERERE_READONLY ) <0 )return 0 ;for (size_t i =0 ; i < merge_rr.nr; i++)printf ("%s\n" , merge_rr.items[i].string);
@@ -128,7 +128,7 @@ int cmd_rerere(int argc,struct rerere_cache cache;if (rerere_cache_setup (&cache, repo, &merge_rr, - flags |RERERE_READONLY ) <0 ) +RERERE_READONLY ) <0 )return 0 ;for (size_t i =0 ; i < merge_rr.nr; i++) {const char *path = merge_rr.items[i].string;
Signature · 1 line
2.56.0.500.g5d06dc02cd.dirty
PATCH v3 5/12 builtin/rerere: turn "forget" into a proper subcommand
In reply to Patrick SteinhardtThe git-rerere(1) command has its own, home-grown implementation of subcommands. Of course this implementation is inconsistent with how subcommands typically work:
- Options from other subcommands apply to each other.
- We don't have proper usage strings.
- Running git rerere <subcmd> -h doesn't print the options that are supported by that subcommand, but instead it just prints the help of git-rerere(1) itself.
A subsequent commit will thus introduce proper subcommands as supported by our parse-options subsystem. Refactor the "forget" subcommand to use parse-options as a preparatory step. The other commands will follow in subsequent commits until all of them have been converted, and then we switch over to OPT_SUBCOMMAND().
--- builtin/rerere.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-)
builtin/rerere.c +24 -14
@@ -52,6 +52,28 @@ static int diff_two(const char *file1, const char *label1,return ret; } +static int cmd_rerere_forget (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct pathspec pathspec; +int ret; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (!argc) +warning (_ ("'git rerere forget' without paths is deprecated" )); +parse_pathspec (&pathspec,0 ,PATHSPEC_PREFER_CWD , prefix, argv); + + ret =rerere_forget (repo, &pathspec); + +clear_pathspec (&pathspec); +return ret; +} + int cmd_rerere(int argc, const char **argv, const char *prefix,
@@ -83,20 +105,8 @@ int cmd_rerere(int argc,if (argc <1 )return rerere (repo, flags); -if (!strcmp (argv[0 ],"forget" )) { -struct pathspec pathspec; -int ret; - -if (argc <2 ) -warning (_ ("'git rerere forget' without paths is deprecated" )); -parse_pathspec (&pathspec,0 ,PATHSPEC_PREFER_CWD , - prefix, argv +1 ); - - ret =rerere_forget (repo, &pathspec); - -clear_pathspec (&pathspec); -return ret; - } +if (!strcmp (argv[0 ],"forget" )) +return cmd_rerere_forget (argc, argv, prefix, repo);if (!strcmp (argv[0 ],"clear" )) {rerere_clear (repo, &merge_rr);
Signature · 1 line
2.56.0.500.g5d06dc02cd.dirty
PATCH v3 6/12 builtin/rerere: turn "clear" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "clear" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-)
builtin/rerere.c +22 -3
@@ -52,6 +52,26 @@ static int diff_two(const char *file1, const char *label1,return ret; } +static int cmd_rerere_clear (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +rerere_clear (repo, &merge_rr); + +string_list_clear (&merge_rr,1 ); +return 0 ; +} + static int cmd_rerere_forget(int argc, const char **argv, const char *prefix,
@@ -107,9 +127,8 @@ int cmd_rerere(int argc,if (!strcmp (argv[0 ],"forget" ))return cmd_rerere_forget (argc, argv, prefix, repo); - -if (!strcmp (argv[0 ],"clear" )) { -rerere_clear (repo, &merge_rr); +else if (!strcmp (argv[0 ],"clear" )) { +return cmd_rerere_clear (argc, argv, prefix, repo); }else if (!strcmp (argv[0 ],"gc" ))rerere_gc (repo, &merge_rr, skip_locked ?RERERE_NOWAIT :0 );
Signature · 1 line
2.56.0.500.g5d06dc02cd.dirty
PATCH v3 7/12 builtin/rerere: turn "status" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "status" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-)
builtin/rerere.c +29 -11
@@ -94,6 +94,32 @@ static int cmd_rerere_forget(int argc,return ret; } +static int cmd_rerere_status (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; +struct rerere_cache cache; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +if (rerere_cache_setup (&cache, repo, &merge_rr, +RERERE_READONLY ) <0 ) +return 0 ; +for (size_t i =0 ; i < merge_rr.nr; i++) +printf ("%s\n" , merge_rr.items[i].string); + +string_list_clear (&merge_rr,1 ); +rerere_cache_release (&cache); +return 0 ; +} + int cmd_rerere(int argc, const char **argv, const char *prefix,
@@ -132,17 +158,9 @@ int cmd_rerere(int argc, }else if (!strcmp (argv[0 ],"gc" ))rerere_gc (repo, &merge_rr, skip_locked ?RERERE_NOWAIT :0 ); -else if (!strcmp (argv[0 ],"status" )) { -struct rerere_cache cache; - -if (rerere_cache_setup (&cache, repo, &merge_rr, -RERERE_READONLY ) <0 ) -return 0 ; -for (size_t i =0 ; i < merge_rr.nr; i++) -printf ("%s\n" , merge_rr.items[i].string); - -rerere_cache_release (&cache); - }else if (!strcmp (argv[0 ],"remaining" )) { +else if (!strcmp (argv[0 ],"status" )) +return cmd_rerere_status (argc, argv, prefix, repo); +else if (!strcmp (argv[0 ],"remaining" )) {rerere_remaining (repo, &merge_rr);for (size_t i =0 ; i < merge_rr.nr; i++) {if (merge_rr.items[i].util !=RERERE_RESOLVED )
Signature · 1 line
2.56.0.500.g5d06dc02cd.dirty
PATCH v3 8/12 builtin/rerere: turn "remaining" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "remaining" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-)
builtin/rerere.c +31 -11
@@ -94,6 +94,34 @@ static int cmd_rerere_forget(int argc,return ret; } +static int cmd_rerere_remaining (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +rerere_remaining (repo, &merge_rr); +for (size_t i =0 ; i < merge_rr.nr; i++) { +if (merge_rr.items[i].util !=RERERE_RESOLVED ) +printf ("%s\n" , merge_rr.items[i].string); +else +/* prepare for later call to +* string_list_clear() */ + merge_rr.items[i].util =NULL ; + } + +string_list_clear (&merge_rr,1 ); +return 0 ; +} + static int cmd_rerere_status(int argc, const char **argv, const char *prefix,
@@ -160,17 +188,9 @@ int cmd_rerere(int argc, skip_locked ? RERERE_NOWAIT : 0); else if (!strcmp(argv[0], "status")) return cmd_rerere_status(argc, argv, prefix, repo); - else if (!strcmp(argv[0], "remaining")) { -rerere_remaining (repo, &merge_rr); -for (size_t i =0 ; i < merge_rr.nr; i++) { -if (merge_rr.items[i].util !=RERERE_RESOLVED ) -printf ("%s\n" , merge_rr.items[i].string); -else -/* prepare for later call to -* string_list_clear() */ - merge_rr.items[i].util =NULL ; - } - } else if (!strcmp(argv[0], "diff")) { + else if (!strcmp(argv[0], "remaining")) + return cmd_rerere_remaining(argc, argv, prefix, repo); + else if (!strcmp(argv[0], "diff")) {struct strbuf buf =STRBUF_INIT ;struct rerere_cache cache;
Signature · 1 line
2.56.0.500.g5d06dc02cd.dirty
PATCH v3 9/12 builtin/rerere: turn "diff" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "diff" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 56 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 19 deletions(-)
builtin/rerere.c +37 -19
@@ -72,6 +72,40 @@ static int cmd_rerere_clear(int argc,return 0 ; } +static int cmd_rerere_diff (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; +struct strbuf buf =STRBUF_INIT ; +struct rerere_cache cache; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +if (rerere_cache_setup (&cache, repo, &merge_rr, +RERERE_READONLY ) <0 ) +return 0 ; + +for (size_t i =0 ; i < merge_rr.nr; i++) { +const char *path = merge_rr.items[i].string; +const struct rerere_id *id = merge_rr.items[i].util; +if (diff_two (rerere_path (repo, &buf, id,"preimage" ), path, path, path)) +die (_ ("unable to generate diff for '%s'" ), +rerere_path (repo, &buf, id,NULL )); + } + +string_list_clear (&merge_rr,1 ); +rerere_cache_release (&cache); +strbuf_release (&buf); +return 0 ; +} + static int cmd_rerere_forget(int argc, const char **argv, const char *prefix,
@@ -190,25 +224,9 @@ int cmd_rerere(int argc,return cmd_rerere_status (argc, argv, prefix, repo); else if (!strcmp(argv[0], "remaining")) return cmd_rerere_remaining(argc, argv, prefix, repo); - else if (!strcmp(argv[0], "diff")) { -struct strbuf buf =STRBUF_INIT ; -struct rerere_cache cache; - -if (rerere_cache_setup (&cache, repo, &merge_rr, -RERERE_READONLY ) <0 ) -return 0 ; -for (size_t i =0 ; i < merge_rr.nr; i++) { -const char *path = merge_rr.items[i].string; -const struct rerere_id *id = merge_rr.items[i].util; -if (diff_two (rerere_path (repo, &buf, id,"preimage" ), - path, path, path)) -die (_ ("unable to generate diff for '%s'" ), -rerere_path (repo, &buf, id,NULL )); - } - -rerere_cache_release (&cache); -strbuf_release (&buf); - } else + else if (!strcmp(argv[0], "diff")) + return cmd_rerere_diff(argc, argv, prefix, repo); + elseusage_with_options (rerere_usage ,options );string_list_clear (&merge_rr,1 );
Signature · 1 line
2.56.0.500.g5d06dc02cd.dirty
PATCH v3 10/12 builtin/rerere: turn "gc" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "gc" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 43 ++++++++++++++++++++++++++++++------------- t/t4200-rerere.sh | 5 ----- 2 files changed, 30 insertions(+), 18 deletions(-)
builtin/rerere.c +30 -13
@@ -128,6 +128,30 @@ static int cmd_rerere_forget(int argc,return ret; } +static int cmd_rerere_gc (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +int skip_locked =0 ; +struct option options[] = { +OPT_HIDDEN_BOOL (0 ,"skip-locked" , &skip_locked, +N_ ("skip gc while another process holds the lock" )), +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +rerere_gc (repo, &merge_rr, + skip_locked ?RERERE_NOWAIT :0 ); + +string_list_clear (&merge_rr,1 ); +return 0 ; +} + static int cmd_rerere_remaining(int argc, const char **argv, const char *prefix,
@@ -187,21 +211,16 @@ int cmd_rerere(int argc,const char *prefix, struct repository *repo) { - struct string_list merge_rr = STRING_LIST_INIT_DUP; -int autoupdate =-1 , skip_locked =0 , flags =0 ; + int autoupdate = -1, flags = 0;struct option options[] = {OPT_SET_INT (0 ,"rerere-autoupdate" , &autoupdate,N_ ("register clean resolutions in index" ),1 ), -OPT_HIDDEN_BOOL (0 ,"skip-locked" , &skip_locked, -N_ ("skip gc while another process holds the lock" )),OPT_END (), }; - argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); - -if (skip_locked && (argc <1 ||strcmp (argv[0 ],"gc" ))) -die (_ ("the option '%s' requires '%s'" ),"--skip-locked" ,"gc" ); + argc =parse_options (argc, argv, prefix, options, rerere_usage, +PARSE_OPT_KEEP_UNKNOWN_OPT );repo_config (repo, git_xmerge_config,NULL );
@@ -215,11 +234,10 @@ int cmd_rerere(int argc,if (!strcmp (argv[0 ],"forget" ))return cmd_rerere_forget (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"clear" )) { +else if (!strcmp (argv[0 ],"clear" ))return cmd_rerere_clear (argc, argv, prefix, repo); - }else if (!strcmp (argv[0 ],"gc" )) -rerere_gc (repo, &merge_rr, - skip_locked ?RERERE_NOWAIT :0 ); +else if (!strcmp (argv[0 ],"gc" )) +return cmd_rerere_gc (argc, argv, prefix, repo);else if (!strcmp (argv[0 ],"status" ))return cmd_rerere_status (argc, argv, prefix, repo);else if (!strcmp (argv[0 ],"remaining" ))
@@ -229,6 +247,5 @@ int cmd_rerere(int argc,else usage_with_options (rerere_usage, options); -string_list_clear (&merge_rr,1 );return 0 ; }
t/t4200-rerere.sh +0 -5
@@ -263,11 +263,6 @@ test_expect_success 'gc --skip-locked prunes while MERGE_RR is not locked' ' test_path_is_missing $rr2/preimage ' -test_expect_success '--skip-locked is only accepted by gc' ' - test_must_fail git rerere --skip-locked clear 2>err && - test_grep "option .--skip-locked. requires .gc." err -' - test_expect_success 'a held lock is waited out within rerere.lockTimeout' ' git reset --hard && rm -rf $rr &&
Signature · 1 line
2.56.0.500.g5d06dc02cd.dirty
PATCH v3 11/12 builtin/rerere: use `OPT_SUBCOMMAND()`
In reply to Patrick SteinhardtIn git-rerere(1) we have our own custom logic to parse subcommands. In preceding commits we have refactored all of those subcommands so that they are ready to be used as OPT_SUBCOMMAND()s. Switch over to use these so that we get rid of our custom subcommand parsing.
Unfortunately this means that we have a backwards-incompatible change because we no longer accept the "--[no-]rerere-autoupdate" flag for all subcommands. But this was arguably broken and misleading as we ignore this flag for any other subcommand aynway. So we cheat a bit and simply call it a bug that those flags were accepted in the first place.
--- builtin/rerere.c | 84 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 28 deletions(-)
builtin/rerere.c +56 -28
@@ -5,6 +5,7 @@#include "rerere.h" #include "strbuf.h" #include "string-list.h" +#include "strvec.h" #include "xdiff/xdiff.h" #include "xdiff-interface.h" #include "pathspec.h"
@@ -152,6 +153,31 @@ static int cmd_rerere_gc(int argc,return 0 ; } +static int cmd_rerere_perform (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +int autoupdate =-1 ; +struct option options[] = { +OPT_SET_INT (0 ,"rerere-autoupdate" , &autoupdate, +N_ ("register clean resolutions in index" ),1 ), +OPT_END (), + }; +enum rerere_flags flags =0 ; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +if (autoupdate ==1 ) + flags =RERERE_AUTOUPDATE ; +if (autoupdate ==0 ) + flags =RERERE_NOAUTOUPDATE ; + +return rerere (repo, flags); +} + static int cmd_rerere_remaining(int argc, const char **argv, const char *prefix,
@@ -211,41 +237,43 @@ int cmd_rerere(int argc,const char *prefix, struct repository *repo) { - int autoupdate = -1, flags = 0; - + parse_opt_subcommand_fn *fn = NULL;struct option options[] = { -OPT_SET_INT (0 ,"rerere-autoupdate" , &autoupdate, -N_ ("register clean resolutions in index" ),1 ), +OPT_SUBCOMMAND ("clear" , &fn, cmd_rerere_clear), +OPT_SUBCOMMAND ("diff" , &fn, cmd_rerere_diff), +OPT_SUBCOMMAND ("forget" , &fn, cmd_rerere_forget), +OPT_SUBCOMMAND ("gc" , &fn, cmd_rerere_gc), +OPT_SUBCOMMAND ("remaining" , &fn, cmd_rerere_remaining), +OPT_SUBCOMMAND ("status" , &fn, cmd_rerere_status),OPT_END (), }; argc =parse_options (argc, argv, prefix, options, rerere_usage, -PARSE_OPT_KEEP_UNKNOWN_OPT ); - +PARSE_OPT_KEEP_UNKNOWN_OPT |PARSE_OPT_SUBCOMMAND_OPTIONAL );repo_config (repo, git_xmerge_config,NULL ); -if (autoupdate ==1 ) - flags =RERERE_AUTOUPDATE ; -if (autoupdate ==0 ) - flags =RERERE_NOAUTOUPDATE ; +if (!fn) { +struct strvec args =STRVEC_INIT ; +const char **args_copy; +int ret; -if (argc <1 ) -return rerere (repo, flags); - -if (!strcmp (argv[0 ],"forget" )) -return cmd_rerere_forget (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"clear" )) -return cmd_rerere_clear (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"gc" )) -return cmd_rerere_gc (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"status" )) -return cmd_rerere_status (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"remaining" )) -return cmd_rerere_remaining (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"diff" )) -return cmd_rerere_diff (argc, argv, prefix, repo); -else -usage_with_options (rerere_usage, options); +strvec_push (&args,"perform" ); +strvec_pushv (&args, argv); -return 0 ; +/* +* We need to copy the array as it will be modified by +* `parse_options()`, and if so we leak the strings +* referenced by `args`. +*/ +CALLOC_ARRAY (args_copy, args.nr); +COPY_ARRAY (args_copy, args.v, args.nr); + + ret =cmd_rerere_perform (args.nr, args_copy, prefix, repo); + +strvec_clear (&args); +free (args_copy); +return ret; + } + +return fn (argc, argv, prefix, repo); }
Signature · 1 line
2.56.0.500.g5d06dc02cd.dirty
PATCH v3 12/12 builtin/rerere: introduce proper usage strings for subcommands
In reply to Patrick SteinhardtAdapt git-rerere(1) to have proper usage strings for each of the subcommands.
--- Documentation/git-rerere.adoc | 8 +++++- builtin/rerere.c | 61 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 11 deletions(-)
Documentation/git-rerere.adoc +7 -1
@@ -8,7 +8,13 @@ git-rerere - Reuse recorded resolution of conflicted merges SYNOPSIS -------- [verse] -'git rerere' [clear | forget <pathspec>... | diff | status | remaining | gc] +'git rerere' [--[no-]rerere-autoupdate] +'git rerere' clear +'git rerere' diff +'git rerere' forget <pathspec>... +'git rerere' gc +'git rerere' remaining +'git rerere' status DESCRIPTION -----------
builtin/rerere.c +51 -10
131 lines not on this page · raw message
Signature · 1 line
2.56.0.500.g5d06dc02cd.dirty
v4 cover builtin/rerere: convert to use subcommands
In reply to Patrick SteinhardtHi,
while reviewing some patch series around git-rerere(1) recently I was a bit appalled by our custom subcommand parsing that we do here. This patch series here thus cleans up and modernizes that part a bit so that git-rerere(1) behaves like every other Git command that happens to have subcommands.
This series is based on 37b3475fb53 (The 9th batch, 2026-10-25) with ps/rerere-remove-global-state at b948c10c8b3 (rerere: get rid of global rerere cache state, 2026-10-23) merged into it.
Changes in v4:
- Some more polishing for RERERE_{NO,}AUTOUPDATE documentation.
- Die in case both of these flags are passed.
- Link to v3: https://patch.msgid.link/[email protected]
Changes in v3:
- Clarify what RERERE_NOAUTOUPDATE does.
- Link to v2: https://patch.msgid.link/[email protected]
Changes in v2:
- Document rerere flags.
- Rename rerere_record() to just rerere().
- Link to v1: https://patch.msgid.link/[email protected]
Thanks!
Patrick
---
Patrick Steinhardt (12):
rerere: rename repo_rerere()
rerere: convert flags into an enum
builtin/rerere: drop the_repository
builtin/rerere: stop passing flags to subcommands that ignore them builtin/rerere: turn "forget" into a proper subcommand builtin/rerere: turn "clear" into a proper subcommand builtin/rerere: turn "status" into a proper subcommand builtin/rerere: turn "remaining" into a proper subcommand builtin/rerere: turn "diff" into a proper subcommand builtin/rerere: turn "gc" into a proper subcommand builtin/rerere: use OPT_SUBCOMMAND()
builtin/rerere: introduce proper usage strings for subcommands
Documentation/git-rerere.adoc | 8 +- apply.c | 2 +- builtin/am.c | 4 +- builtin/commit.c | 2 +- builtin/merge.c | 2 +- builtin/rerere.c | 319 ++++++++++++++++++++++++++++++++---------- builtin/stash.c | 2 +- rerere.c | 14 +- rerere.h | 43 ++++-- sequencer.c | 4 +- t/t4200-rerere.sh | 5 - 11 files changed, 299 insertions(+), 106 deletions(-)
Range-diff versus v3:
1: e9237465706 = 1: f669dc6eddf rerere: rename `repo_rerere()` 2: 94d91a0620d ! 2: 4fd3f6aa650 rerere: convert flags into an enum
@@ Commit message
around as enums so that it becomes trivial to see what kind of flags are expected. Adapt the rerere flags accordingly and document them.
+ While at it, detect the case where both RERERE_AUTOUPDATE and
+ RERERE_NOAUTOUPDATE are passed. This combination does not make a lot
+ of sense, and it's not obvious which of those flags takes precedence
+ (it's RERERE_AUTOUPDATE). All current callers know to only pass one of
+ those flags, but let's be safe rather than sorry and BUG() in case
+ both are passed.
+
## rerere.c ##
@@ rerere.c: static int is_rerere_enabled(struct repository *r)
{
int lock_timeout_ms = 1000;
int fd;
+@@ rerere.c: int rerere_cache_setup(struct rerere_cache *cache,
+ repo_config_get_bool(r, "rerere.autoupdate", &cache->autoupdate);
+ repo_config_get_int(r, "rerere.locktimeout", &lock_timeout_ms);
+
++ if ((flags & RERERE_AUTOUPDATE) && (flags & RERERE_NOAUTOUPDATE))
++ BUG("RERERE_AUTOUPDATE cannot be used with RERERE_NOAUTOUPDATE");
+ if (flags & (RERERE_AUTOUPDATE|RERERE_NOAUTOUPDATE))
+ cache->autoupdate = !!(flags & RERERE_AUTOUPDATE);
+ if ((flags & RERERE_READONLY) && (flags & RERERE_NOWAIT))
@@ rerere.c: int rerere_cache_setup(struct rerere_cache *cache,
return fd;
}
@@ rerere.h: struct rerere_id {
+ RERERE_AUTOUPDATE = (1 << 0),
+
+ /*
-+ * Reverse of RERERE_AUTOUPDATE. That is, update the file in the
-+ * working tree but don't touch the index.
++ * update the file in the working tree but don't touch the index. This
++ * can be used to explicitly override "rerere.autoUpdate".
++ *
++ * This flag is essentially the reverse of RERERE_AUTOUPDATE. It is
++ * invalid to pass both flags at the same time.
+ */
+ RERERE_NOAUTOUPDATE = (1 << 1),
+
3: 99faad47068 = 3: 48692c2c88d builtin/rerere: drop `the_repository` 4: f164c482227 = 4: e76991dd39d builtin/rerere: stop passing flags to subcommands that ignore them 5: 0573d2323e8 = 5: 2474dca4713 builtin/rerere: turn "forget" into a proper subcommand 6: 2098dedf4fd = 6: cecdbb1c63b builtin/rerere: turn "clear" into a proper subcommand 7: e8eaa2b4092 = 7: 54fc766f9d2 builtin/rerere: turn "status" into a proper subcommand 8: a5781284b8b = 8: fa2c839b2cb builtin/rerere: turn "remaining" into a proper subcommand 9: 9a4278df0ea = 9: 416b8311ca9 builtin/rerere: turn "diff" into a proper subcommand 10: e63b9be0d74 = 10: adb2b68f833 builtin/rerere: turn "gc" into a proper subcommand 11: 47889896ecb = 11: a12e19cb2e8 builtin/rerere: use `OPT_SUBCOMMAND()` 12: 5e16a247b49 = 12: 4b1b6170ba3 builtin/rerere: introduce proper usage strings for subcommands
---
base-commit: 710910c03a22ac663885049213b9fef5853ea8e0
change-id: 20261027-pks-rerere-subcommands-318d41553438
PATCH v4 1/12 rerere: rename `repo_rerere()`
The repo_rerere() function is the only function in "rerere.h" that does not have a rerere_ prefix. And while it does indeed receive a repository as first parameter, that's not a good-enough reason to give it a repo_ prefix.
Rename the function to just rerere(). Move around and extend documentation of this function to better explain what it does.
--- apply.c | 2 +- builtin/am.c | 4 ++-- builtin/commit.c | 2 +- builtin/merge.c | 2 +- builtin/rerere.c | 2 +- builtin/stash.c | 2 +- rerere.c | 7 +------ rerere.h | 9 ++++++++- sequencer.c | 4 ++-- 9 files changed, 18 insertions(+), 16 deletions(-)
apply.c +1 -1
@@ -4866,7 +4866,7 @@ static int write_out_results(struct apply_state *state, struct patch *list)* tree with conflict markers, but that isn't written with --cached. */ if (!state->cached) -repo_rerere (state->repo,0 ); +rerere (state->repo,0 ); }return errs;
builtin/am.c +2 -2
@@ -1649,7 +1649,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa o.verbosity =0 ;if (merge_ort_generic (&o, &our_tree, &their_tree,1 , bases, &result)) { -repo_rerere (the_repository, state->allow_rerere_autoupdate); +rerere (the_repository, state->allow_rerere_autoupdate);free (their_tree_name);return error (_ ("Failed to merge in the changes." )); }
@@ -1979,7 +1979,7 @@ static void am_resolve(struct am_state *state, int allow_empty) goto next; } -repo_rerere (the_repository,0 ); +rerere (the_repository,0 );do_commit (state);
builtin/commit.c +1 -1
@@ -1962,7 +1962,7 @@ int cmd_commit(int argc,git_test_write_commit_graph_or_die (the_repository); -repo_rerere (the_repository,0 ); +rerere (the_repository,0 );run_auto_maintenance (the_repository, quiet);run_commit_hook (use_editor,repo_get_index_file (the_repository),NULL ,"post-commit" ,NULL );
builtin/merge.c +1 -1
@@ -1061,7 +1061,7 @@ static int suggest_conflicts(void)fputs (msgbuf.buf, fp);strbuf_release (&msgbuf);fclose (fp); -repo_rerere (the_repository, allow_rerere_auto); +rerere (the_repository, allow_rerere_auto);printf (_ ("Automatic merge failed; " "fix conflicts and then commit the result.\n" ));return 1 ;
builtin/rerere.c +1 -1
@@ -83,7 +83,7 @@ int cmd_rerere(int argc, flags =RERERE_NOAUTOUPDATE ;if (argc <1 ) -return repo_rerere (the_repository, flags); +return rerere (the_repository, flags);if (!strcmp (argv[0 ],"forget" )) {struct pathspec pathspec;
builtin/stash.c +1 -1
@@ -688,7 +688,7 @@ static enum stash_apply_result do_apply_stash(const char *prefix, ret =error (_ ("could not write index" ));if (ret) { -repo_rerere (the_repository,0 ); +rerere (the_repository,0 );if (index)fprintf_ln (stderr,_ ("Index was not unstashed." ));
rerere.c +1 -6
@@ -951,12 +951,7 @@ int rerere_cache_setup(struct rerere_cache *cache,return fd; } -/* -* The main entry point that is called internally from codepaths that -* perform mergy operations, possibly leaving conflicted index entries -* and working tree files. -*/ -int repo_rerere (struct repository *r,int flags) +int rerere (struct repository *r,int flags) {struct string_list merge_rr =STRING_LIST_INIT_DUP ;struct rerere_cache cache;
rerere.h +8 -1
@@ -45,7 +45,14 @@ int rerere_cache_setup(struct rerere_cache *cache, int flags);void rerere_cache_release (struct rerere_cache *cache); -int repo_rerere (struct repository *,int ); +/* +* The main entry point that is called internally from codepaths that +* perform mergy operations, possibly leaving conflicted index entries +* and working tree files. It records (and optionally updates) resolved +* conflicts and reuses recorded resolutions for unresolved conflicts. +*/ +int rerere (struct repository *r,int flags); +/* * Given the conflict ID and the name of a "file" used for replaying * the recorded resolution (e.g. "preimage", "postimage"), return the
sequencer.c +2 -2
@@ -2556,7 +2556,7 @@ static enum pick_result do_pick_commit(struct repository *r, : _("could not apply %s... %s"), short_commit_name(r, commit), msg.subject);print_advice (r, res ==1 , opts); -repo_rerere (r, opts->allow_rerere_auto); +rerere (r, opts->allow_rerere_auto); goto leave; }
@@ -4488,7 +4488,7 @@ static int do_merge(struct repository *r,rollback_lock_file (&lock);if (ret) -repo_rerere (r, opts->allow_rerere_auto); +rerere (r, opts->allow_rerere_auto);else /* * In case of problems, we now want to return a positive
Signature · 1 line
2.56.0.611.g1030f0b940.dirty
PATCH v4 2/12 rerere: convert flags into an enum
In reply to Patrick SteinhardtOur modern coding guidelines say that flags should typically be passed around as enums so that it becomes trivial to see what kind of flags are expected. Adapt the rerere flags accordingly and document them.
While at it, detect the case where both RERERE_AUTOUPDATE and RERERE_NOAUTOUPDATE are passed. This combination does not make a lot of sense, and it's not obvious which of those flags takes precedence (it's RERERE_AUTOUPDATE). All current callers know to only pass one of those flags, but let's be safe rather than sorry and BUG() in case both are passed.
--- rerere.c | 9 ++++++--- rerere.h | 36 +++++++++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 12 deletions(-)
rerere.c +6 -3
@@ -908,7 +908,7 @@ static int is_rerere_enabled(struct repository *r)int rerere_cache_setup (struct rerere_cache *cache,struct repository *r,struct string_list *merge_rr, -int flags) +enum rerere_flags flags) {int lock_timeout_ms =1000 ;int fd;
@@ -922,6 +922,8 @@ int rerere_cache_setup(struct rerere_cache *cache,repo_config_get_bool (r,"rerere.autoupdate" , &cache->autoupdate);repo_config_get_int (r,"rerere.locktimeout" , &lock_timeout_ms); +if ((flags &RERERE_AUTOUPDATE ) && (flags &RERERE_NOAUTOUPDATE )) +BUG ("RERERE_AUTOUPDATE cannot be used with RERERE_NOAUTOUPDATE" );if (flags & (RERERE_AUTOUPDATE |RERERE_NOAUTOUPDATE )) cache->autoupdate = !!(flags &RERERE_AUTOUPDATE );if ((flags &RERERE_READONLY ) && (flags &RERERE_NOWAIT ))
@@ -951,7 +953,7 @@ int rerere_cache_setup(struct rerere_cache *cache,return fd; } -int rerere (struct repository *r,int flags) +int rerere (struct repository *r,enum rerere_flags flags) {struct string_list merge_rr =STRING_LIST_INIT_DUP ;struct rerere_cache cache;
@@ -1336,7 +1338,8 @@ bool rerere_gc_needed(struct repository *r, size_t limit)return needed; } -void rerere_gc (struct repository *r,struct string_list *rr,int flags) +void rerere_gc (struct repository *r,struct string_list *rr, +enum rerere_flags flags) {struct rerere_cache cache;struct string_list to_remove =STRING_LIST_INIT_DUP ;
rerere.h +27 -9
@@ -9,12 +9,6 @@struct pathspec ;struct repository ; -#define RERERE_AUTOUPDATE 01 -#define RERERE_NOAUTOUPDATE 02 -#define RERERE_READONLY 04 -/* If MERGE_RR.lock is taken, return -1 as if rerere were disabled */ -#define RERERE_NOWAIT 010 -/* * Marks paths that have been hand-resolved and added to the * index. Set in the util field of such paths after calling
@@ -39,10 +33,33 @@ struct rerere_id {int variant; }; +enum rerere_flags { +/* +* Auto-update the index with previously recorded resolutions. This can +* be used to explicitly override "rerere.autoUpdate". +*/ +RERERE_AUTOUPDATE = (1 <<0 ), + +/* +* update the file in the working tree but don't touch the index. This +* can be used to explicitly override "rerere.autoUpdate". +* +* This flag is essentially the reverse of `RERERE_AUTOUPDATE`. It is +* invalid to pass both flags at the same time. +*/ +RERERE_NOAUTOUPDATE = (1 <<1 ), + +/* Skip acquiring the MERGE_RR.lock for a read-only operation. */ +RERERE_READONLY = (1 <<2 ), + +/* If MERGE_RR.lock is taken, return -1 as if rerere were disabled. */ +RERERE_NOWAIT = (1 <<3 ), +}; +int rerere_cache_setup (struct rerere_cache *cache,struct repository *r,struct string_list *merge_rr, -int flags); +enum rerere_flags flags);void rerere_cache_release (struct rerere_cache *cache);/*
@@ -51,7 +68,7 @@ void rerere_cache_release(struct rerere_cache *cache);* and working tree files. It records (and optionally updates) resolved * conflicts and reuses recorded resolutions for unresolved conflicts. */ -int rerere (struct repository *r,int flags); +int rerere (struct repository *r,enum rerere_flags flags);/* * Given the conflict ID and the name of a "file" used for replaying
@@ -65,7 +82,8 @@ const char *rerere_path(struct repository *,int rerere_forget (struct repository *,struct pathspec *);int rerere_remaining (struct repository *,struct string_list *);void rerere_clear (struct repository *,struct string_list *); -void rerere_gc (struct repository *,struct string_list *,int ); +void rerere_gc (struct repository *,struct string_list *, +enum rerere_flags flags);/* * Check whether garbage collection for rerere entries is needed, which is
Signature · 1 line
2.56.0.611.g1030f0b940.dirty
PATCH v4 3/12 builtin/rerere: drop `the_repository`
In reply to Patrick SteinhardtThere are a couple of references to the_repository in git-rerere(1). As that command cannot run outside of a repository these references can be trivially replaced by the repository that's passed in as parameter to cmd_rerere(). We don't have to worry about the repository being NULL when called with "-h", either, as there are no references to the repo before we call parse_options().
--- builtin/rerere.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-)
builtin/rerere.c +11 -13
@@ -1,5 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE -#include "builtin.h" #include "config.h" #include "gettext.h"
@@ -57,7 +55,7 @@ static int diff_two(const char *file1, const char *label1,int cmd_rerere (int argc,const char **argv,const char *prefix, - struct repository *repo UNUSED) +struct repository *repo) {struct string_list merge_rr =STRING_LIST_INIT_DUP ;int autoupdate =-1 , skip_locked =0 , flags =0 ;
@@ -75,7 +73,7 @@ int cmd_rerere(int argc,if (skip_locked && (argc <1 ||strcmp (argv[0 ],"gc" )))die (_ ("the option '%s' requires '%s'" ),"--skip-locked" ,"gc" ); -repo_config (the_repository, git_xmerge_config,NULL ); +repo_config (repo, git_xmerge_config,NULL );if (autoupdate ==1 ) flags =RERERE_AUTOUPDATE ;
@@ -83,7 +81,7 @@ int cmd_rerere(int argc, flags =RERERE_NOAUTOUPDATE ;if (argc <1 ) -return rerere (the_repository, flags); +return rerere (repo, flags);if (!strcmp (argv[0 ],"forget" )) {struct pathspec pathspec;
@@ -94,21 +92,21 @@ int cmd_rerere(int argc,parse_pathspec (&pathspec,0 ,PATHSPEC_PREFER_CWD , prefix, argv +1 ); - ret =rerere_forget (the_repository, &pathspec); + ret =rerere_forget (repo, &pathspec);clear_pathspec (&pathspec);return ret; }if (!strcmp (argv[0 ],"clear" )) { -rerere_clear (the_repository, &merge_rr); +rerere_clear (repo, &merge_rr); }else if (!strcmp (argv[0 ],"gc" )) -rerere_gc (the_repository, &merge_rr, +rerere_gc (repo, &merge_rr, skip_locked ?RERERE_NOWAIT :0 );else if (!strcmp (argv[0 ],"status" )) {struct rerere_cache cache; -if (rerere_cache_setup (&cache, the_repository, &merge_rr, +if (rerere_cache_setup (&cache, repo, &merge_rr, flags |RERERE_READONLY ) <0 )return 0 ;for (size_t i =0 ; i < merge_rr.nr; i++)
@@ -116,7 +114,7 @@ int cmd_rerere(int argc,rerere_cache_release (&cache); }else if (!strcmp (argv[0 ],"remaining" )) { -rerere_remaining (the_repository, &merge_rr); +rerere_remaining (repo, &merge_rr);for (size_t i =0 ; i < merge_rr.nr; i++) {if (merge_rr.items[i].util !=RERERE_RESOLVED )printf ("%s\n" , merge_rr.items[i].string);
@@ -129,16 +127,16 @@ int cmd_rerere(int argc,struct strbuf buf =STRBUF_INIT ;struct rerere_cache cache; -if (rerere_cache_setup (&cache, the_repository, &merge_rr, +if (rerere_cache_setup (&cache, repo, &merge_rr, flags |RERERE_READONLY ) <0 )return 0 ;for (size_t i =0 ; i < merge_rr.nr; i++) {const char *path = merge_rr.items[i].string;const struct rerere_id *id = merge_rr.items[i].util; -if (diff_two (rerere_path (the_repository, &buf, id,"preimage" ), +if (diff_two (rerere_path (repo, &buf, id,"preimage" ), path, path, path))die (_ ("unable to generate diff for '%s'" ), -rerere_path (the_repository, &buf, id,NULL )); +rerere_path (repo, &buf, id,NULL )); }rerere_cache_release (&cache);
Signature · 1 line
2.56.0.611.g1030f0b940.dirty
PATCH v4 4/12 builtin/rerere: stop passing flags to subcommands that ignore them
In reply to Patrick SteinhardtThe git-rerere(1) command has a "--[no-]rerere-autoupdate" flag. If passed, we explicitly enable or disable recording clean merge resolutions that are staged in the index. This flag only matters to git-rerere(1) when not executed with any subcommand, but we also forward the flag to git rerere status and git rerere diff.
This is mostly harmless as we would ignore those flags anyway, but it makes subsequent refactorings harder to understand. Prepare for these refactorings by not using the flag in those subcommands anymore.
--- builtin/rerere.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
builtin/rerere.c +2 -2
@@ -107,7 +107,7 @@ int cmd_rerere(int argc,struct rerere_cache cache;if (rerere_cache_setup (&cache, repo, &merge_rr, - flags |RERERE_READONLY ) <0 ) +RERERE_READONLY ) <0 )return 0 ;for (size_t i =0 ; i < merge_rr.nr; i++)printf ("%s\n" , merge_rr.items[i].string);
@@ -128,7 +128,7 @@ int cmd_rerere(int argc,struct rerere_cache cache;if (rerere_cache_setup (&cache, repo, &merge_rr, - flags |RERERE_READONLY ) <0 ) +RERERE_READONLY ) <0 )return 0 ;for (size_t i =0 ; i < merge_rr.nr; i++) {const char *path = merge_rr.items[i].string;
Signature · 1 line
2.56.0.611.g1030f0b940.dirty
PATCH v4 5/12 builtin/rerere: turn "forget" into a proper subcommand
In reply to Patrick SteinhardtThe git-rerere(1) command has its own, home-grown implementation of subcommands. Of course this implementation is inconsistent with how subcommands typically work:
- Options from other subcommands apply to each other.
- We don't have proper usage strings.
- Running git rerere <subcmd> -h doesn't print the options that are supported by that subcommand, but instead it just prints the help of git-rerere(1) itself.
A subsequent commit will thus introduce proper subcommands as supported by our parse-options subsystem. Refactor the "forget" subcommand to use parse-options as a preparatory step. The other commands will follow in subsequent commits until all of them have been converted, and then we switch over to OPT_SUBCOMMAND().
--- builtin/rerere.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-)
builtin/rerere.c +24 -14
@@ -52,6 +52,28 @@ static int diff_two(const char *file1, const char *label1,return ret; } +static int cmd_rerere_forget (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct pathspec pathspec; +int ret; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (!argc) +warning (_ ("'git rerere forget' without paths is deprecated" )); +parse_pathspec (&pathspec,0 ,PATHSPEC_PREFER_CWD , prefix, argv); + + ret =rerere_forget (repo, &pathspec); + +clear_pathspec (&pathspec); +return ret; +} + int cmd_rerere(int argc, const char **argv, const char *prefix,
@@ -83,20 +105,8 @@ int cmd_rerere(int argc,if (argc <1 )return rerere (repo, flags); -if (!strcmp (argv[0 ],"forget" )) { -struct pathspec pathspec; -int ret; - -if (argc <2 ) -warning (_ ("'git rerere forget' without paths is deprecated" )); -parse_pathspec (&pathspec,0 ,PATHSPEC_PREFER_CWD , - prefix, argv +1 ); - - ret =rerere_forget (repo, &pathspec); - -clear_pathspec (&pathspec); -return ret; - } +if (!strcmp (argv[0 ],"forget" )) +return cmd_rerere_forget (argc, argv, prefix, repo);if (!strcmp (argv[0 ],"clear" )) {rerere_clear (repo, &merge_rr);
Signature · 1 line
2.56.0.611.g1030f0b940.dirty
PATCH v4 6/12 builtin/rerere: turn "clear" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "clear" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-)
builtin/rerere.c +22 -3
@@ -52,6 +52,26 @@ static int diff_two(const char *file1, const char *label1,return ret; } +static int cmd_rerere_clear (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +rerere_clear (repo, &merge_rr); + +string_list_clear (&merge_rr,1 ); +return 0 ; +} + static int cmd_rerere_forget(int argc, const char **argv, const char *prefix,
@@ -107,9 +127,8 @@ int cmd_rerere(int argc,if (!strcmp (argv[0 ],"forget" ))return cmd_rerere_forget (argc, argv, prefix, repo); - -if (!strcmp (argv[0 ],"clear" )) { -rerere_clear (repo, &merge_rr); +else if (!strcmp (argv[0 ],"clear" )) { +return cmd_rerere_clear (argc, argv, prefix, repo); }else if (!strcmp (argv[0 ],"gc" ))rerere_gc (repo, &merge_rr, skip_locked ?RERERE_NOWAIT :0 );
Signature · 1 line
2.56.0.611.g1030f0b940.dirty
PATCH v4 7/12 builtin/rerere: turn "status" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "status" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-)
builtin/rerere.c +29 -11
@@ -94,6 +94,32 @@ static int cmd_rerere_forget(int argc,return ret; } +static int cmd_rerere_status (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; +struct rerere_cache cache; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +if (rerere_cache_setup (&cache, repo, &merge_rr, +RERERE_READONLY ) <0 ) +return 0 ; +for (size_t i =0 ; i < merge_rr.nr; i++) +printf ("%s\n" , merge_rr.items[i].string); + +string_list_clear (&merge_rr,1 ); +rerere_cache_release (&cache); +return 0 ; +} + int cmd_rerere(int argc, const char **argv, const char *prefix,
@@ -132,17 +158,9 @@ int cmd_rerere(int argc, }else if (!strcmp (argv[0 ],"gc" ))rerere_gc (repo, &merge_rr, skip_locked ?RERERE_NOWAIT :0 ); -else if (!strcmp (argv[0 ],"status" )) { -struct rerere_cache cache; - -if (rerere_cache_setup (&cache, repo, &merge_rr, -RERERE_READONLY ) <0 ) -return 0 ; -for (size_t i =0 ; i < merge_rr.nr; i++) -printf ("%s\n" , merge_rr.items[i].string); - -rerere_cache_release (&cache); - }else if (!strcmp (argv[0 ],"remaining" )) { +else if (!strcmp (argv[0 ],"status" )) +return cmd_rerere_status (argc, argv, prefix, repo); +else if (!strcmp (argv[0 ],"remaining" )) {rerere_remaining (repo, &merge_rr);for (size_t i =0 ; i < merge_rr.nr; i++) {if (merge_rr.items[i].util !=RERERE_RESOLVED )
Signature · 1 line
2.56.0.611.g1030f0b940.dirty
PATCH v4 8/12 builtin/rerere: turn "remaining" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "remaining" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-)
builtin/rerere.c +31 -11
@@ -94,6 +94,34 @@ static int cmd_rerere_forget(int argc,return ret; } +static int cmd_rerere_remaining (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +rerere_remaining (repo, &merge_rr); +for (size_t i =0 ; i < merge_rr.nr; i++) { +if (merge_rr.items[i].util !=RERERE_RESOLVED ) +printf ("%s\n" , merge_rr.items[i].string); +else +/* prepare for later call to +* string_list_clear() */ + merge_rr.items[i].util =NULL ; + } + +string_list_clear (&merge_rr,1 ); +return 0 ; +} + static int cmd_rerere_status(int argc, const char **argv, const char *prefix,
@@ -160,17 +188,9 @@ int cmd_rerere(int argc, skip_locked ? RERERE_NOWAIT : 0); else if (!strcmp(argv[0], "status")) return cmd_rerere_status(argc, argv, prefix, repo); - else if (!strcmp(argv[0], "remaining")) { -rerere_remaining (repo, &merge_rr); -for (size_t i =0 ; i < merge_rr.nr; i++) { -if (merge_rr.items[i].util !=RERERE_RESOLVED ) -printf ("%s\n" , merge_rr.items[i].string); -else -/* prepare for later call to -* string_list_clear() */ - merge_rr.items[i].util =NULL ; - } - } else if (!strcmp(argv[0], "diff")) { + else if (!strcmp(argv[0], "remaining")) + return cmd_rerere_remaining(argc, argv, prefix, repo); + else if (!strcmp(argv[0], "diff")) {struct strbuf buf =STRBUF_INIT ;struct rerere_cache cache;
Signature · 1 line
2.56.0.611.g1030f0b940.dirty
PATCH v4 9/12 builtin/rerere: turn "diff" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "diff" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 56 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 19 deletions(-)
builtin/rerere.c +37 -19
@@ -72,6 +72,40 @@ static int cmd_rerere_clear(int argc,return 0 ; } +static int cmd_rerere_diff (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +struct option options[] = { +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; +struct strbuf buf =STRBUF_INIT ; +struct rerere_cache cache; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +if (rerere_cache_setup (&cache, repo, &merge_rr, +RERERE_READONLY ) <0 ) +return 0 ; + +for (size_t i =0 ; i < merge_rr.nr; i++) { +const char *path = merge_rr.items[i].string; +const struct rerere_id *id = merge_rr.items[i].util; +if (diff_two (rerere_path (repo, &buf, id,"preimage" ), path, path, path)) +die (_ ("unable to generate diff for '%s'" ), +rerere_path (repo, &buf, id,NULL )); + } + +string_list_clear (&merge_rr,1 ); +rerere_cache_release (&cache); +strbuf_release (&buf); +return 0 ; +} + static int cmd_rerere_forget(int argc, const char **argv, const char *prefix,
@@ -190,25 +224,9 @@ int cmd_rerere(int argc,return cmd_rerere_status (argc, argv, prefix, repo); else if (!strcmp(argv[0], "remaining")) return cmd_rerere_remaining(argc, argv, prefix, repo); - else if (!strcmp(argv[0], "diff")) { -struct strbuf buf =STRBUF_INIT ; -struct rerere_cache cache; - -if (rerere_cache_setup (&cache, repo, &merge_rr, -RERERE_READONLY ) <0 ) -return 0 ; -for (size_t i =0 ; i < merge_rr.nr; i++) { -const char *path = merge_rr.items[i].string; -const struct rerere_id *id = merge_rr.items[i].util; -if (diff_two (rerere_path (repo, &buf, id,"preimage" ), - path, path, path)) -die (_ ("unable to generate diff for '%s'" ), -rerere_path (repo, &buf, id,NULL )); - } - -rerere_cache_release (&cache); -strbuf_release (&buf); - } else + else if (!strcmp(argv[0], "diff")) + return cmd_rerere_diff(argc, argv, prefix, repo); + elseusage_with_options (rerere_usage ,options );string_list_clear (&merge_rr,1 );
Signature · 1 line
2.56.0.611.g1030f0b940.dirty
PATCH v4 10/12 builtin/rerere: turn "gc" into a proper subcommand
In reply to Patrick SteinhardtAs explained in a preceding commit, a subsequent commit will introduce proper subcommands as supported by our parse-options subsystem. Refactor the "gc" subcommand to use parse-options as a preparatory step.
--- builtin/rerere.c | 43 ++++++++++++++++++++++++++++++------------- t/t4200-rerere.sh | 5 ----- 2 files changed, 30 insertions(+), 18 deletions(-)
builtin/rerere.c +30 -13
@@ -128,6 +128,30 @@ static int cmd_rerere_forget(int argc,return ret; } +static int cmd_rerere_gc (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +int skip_locked =0 ; +struct option options[] = { +OPT_HIDDEN_BOOL (0 ,"skip-locked" , &skip_locked, +N_ ("skip gc while another process holds the lock" )), +OPT_END (), + }; +struct string_list merge_rr =STRING_LIST_INIT_DUP ; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +rerere_gc (repo, &merge_rr, + skip_locked ?RERERE_NOWAIT :0 ); + +string_list_clear (&merge_rr,1 ); +return 0 ; +} + static int cmd_rerere_remaining(int argc, const char **argv, const char *prefix,
@@ -187,21 +211,16 @@ int cmd_rerere(int argc,const char *prefix, struct repository *repo) { - struct string_list merge_rr = STRING_LIST_INIT_DUP; -int autoupdate =-1 , skip_locked =0 , flags =0 ; + int autoupdate = -1, flags = 0;struct option options[] = {OPT_SET_INT (0 ,"rerere-autoupdate" , &autoupdate,N_ ("register clean resolutions in index" ),1 ), -OPT_HIDDEN_BOOL (0 ,"skip-locked" , &skip_locked, -N_ ("skip gc while another process holds the lock" )),OPT_END (), }; - argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); - -if (skip_locked && (argc <1 ||strcmp (argv[0 ],"gc" ))) -die (_ ("the option '%s' requires '%s'" ),"--skip-locked" ,"gc" ); + argc =parse_options (argc, argv, prefix, options, rerere_usage, +PARSE_OPT_KEEP_UNKNOWN_OPT );repo_config (repo, git_xmerge_config,NULL );
@@ -215,11 +234,10 @@ int cmd_rerere(int argc,if (!strcmp (argv[0 ],"forget" ))return cmd_rerere_forget (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"clear" )) { +else if (!strcmp (argv[0 ],"clear" ))return cmd_rerere_clear (argc, argv, prefix, repo); - }else if (!strcmp (argv[0 ],"gc" )) -rerere_gc (repo, &merge_rr, - skip_locked ?RERERE_NOWAIT :0 ); +else if (!strcmp (argv[0 ],"gc" )) +return cmd_rerere_gc (argc, argv, prefix, repo);else if (!strcmp (argv[0 ],"status" ))return cmd_rerere_status (argc, argv, prefix, repo);else if (!strcmp (argv[0 ],"remaining" ))
@@ -229,6 +247,5 @@ int cmd_rerere(int argc,else usage_with_options (rerere_usage, options); -string_list_clear (&merge_rr,1 );return 0 ; }
t/t4200-rerere.sh +0 -5
@@ -263,11 +263,6 @@ test_expect_success 'gc --skip-locked prunes while MERGE_RR is not locked' ' test_path_is_missing $rr2/preimage ' -test_expect_success '--skip-locked is only accepted by gc' ' - test_must_fail git rerere --skip-locked clear 2>err && - test_grep "option .--skip-locked. requires .gc." err -' - test_expect_success 'a held lock is waited out within rerere.lockTimeout' ' git reset --hard && rm -rf $rr &&
Signature · 1 line
2.56.0.611.g1030f0b940.dirty
PATCH v4 11/12 builtin/rerere: use `OPT_SUBCOMMAND()`
In reply to Patrick SteinhardtIn git-rerere(1) we have our own custom logic to parse subcommands. In preceding commits we have refactored all of those subcommands so that they are ready to be used as OPT_SUBCOMMAND()s. Switch over to use these so that we get rid of our custom subcommand parsing.
Unfortunately this means that we have a backwards-incompatible change because we no longer accept the "--[no-]rerere-autoupdate" flag for all subcommands. But this was arguably broken and misleading as we ignore this flag for any other subcommand aynway. So we cheat a bit and simply call it a bug that those flags were accepted in the first place.
--- builtin/rerere.c | 84 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 28 deletions(-)
builtin/rerere.c +56 -28
@@ -5,6 +5,7 @@#include "rerere.h" #include "strbuf.h" #include "string-list.h" +#include "strvec.h" #include "xdiff/xdiff.h" #include "xdiff-interface.h" #include "pathspec.h"
@@ -152,6 +153,31 @@ static int cmd_rerere_gc(int argc,return 0 ; } +static int cmd_rerere_perform (int argc, +const char **argv, +const char *prefix, +struct repository *repo) +{ +int autoupdate =-1 ; +struct option options[] = { +OPT_SET_INT (0 ,"rerere-autoupdate" , &autoupdate, +N_ ("register clean resolutions in index" ),1 ), +OPT_END (), + }; +enum rerere_flags flags =0 ; + + argc =parse_options (argc, argv, prefix, options, rerere_usage,0 ); +if (argc) +usage (_ ("too many arguments" )); + +if (autoupdate ==1 ) + flags =RERERE_AUTOUPDATE ; +if (autoupdate ==0 ) + flags =RERERE_NOAUTOUPDATE ; + +return rerere (repo, flags); +} + static int cmd_rerere_remaining(int argc, const char **argv, const char *prefix,
@@ -211,41 +237,43 @@ int cmd_rerere(int argc,const char *prefix, struct repository *repo) { - int autoupdate = -1, flags = 0; - + parse_opt_subcommand_fn *fn = NULL;struct option options[] = { -OPT_SET_INT (0 ,"rerere-autoupdate" , &autoupdate, -N_ ("register clean resolutions in index" ),1 ), +OPT_SUBCOMMAND ("clear" , &fn, cmd_rerere_clear), +OPT_SUBCOMMAND ("diff" , &fn, cmd_rerere_diff), +OPT_SUBCOMMAND ("forget" , &fn, cmd_rerere_forget), +OPT_SUBCOMMAND ("gc" , &fn, cmd_rerere_gc), +OPT_SUBCOMMAND ("remaining" , &fn, cmd_rerere_remaining), +OPT_SUBCOMMAND ("status" , &fn, cmd_rerere_status),OPT_END (), }; argc =parse_options (argc, argv, prefix, options, rerere_usage, -PARSE_OPT_KEEP_UNKNOWN_OPT ); - +PARSE_OPT_KEEP_UNKNOWN_OPT |PARSE_OPT_SUBCOMMAND_OPTIONAL );repo_config (repo, git_xmerge_config,NULL ); -if (autoupdate ==1 ) - flags =RERERE_AUTOUPDATE ; -if (autoupdate ==0 ) - flags =RERERE_NOAUTOUPDATE ; +if (!fn) { +struct strvec args =STRVEC_INIT ; +const char **args_copy; +int ret; -if (argc <1 ) -return rerere (repo, flags); - -if (!strcmp (argv[0 ],"forget" )) -return cmd_rerere_forget (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"clear" )) -return cmd_rerere_clear (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"gc" )) -return cmd_rerere_gc (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"status" )) -return cmd_rerere_status (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"remaining" )) -return cmd_rerere_remaining (argc, argv, prefix, repo); -else if (!strcmp (argv[0 ],"diff" )) -return cmd_rerere_diff (argc, argv, prefix, repo); -else -usage_with_options (rerere_usage, options); +strvec_push (&args,"perform" ); +strvec_pushv (&args, argv); -return 0 ; +/* +* We need to copy the array as it will be modified by +* `parse_options()`, and if so we leak the strings +* referenced by `args`. +*/ +CALLOC_ARRAY (args_copy, args.nr); +COPY_ARRAY (args_copy, args.v, args.nr); + + ret =cmd_rerere_perform (args.nr, args_copy, prefix, repo); + +strvec_clear (&args); +free (args_copy); +return ret; + } + +return fn (argc, argv, prefix, repo); }
Signature · 1 line
2.56.0.611.g1030f0b940.dirty
PATCH v4 12/12 builtin/rerere: introduce proper usage strings for subcommands
In reply to Patrick SteinhardtAdapt git-rerere(1) to have proper usage strings for each of the subcommands.
--- Documentation/git-rerere.adoc | 8 +++++- builtin/rerere.c | 61 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 11 deletions(-)
Documentation/git-rerere.adoc +7 -1
@@ -8,7 +8,13 @@ git-rerere - Reuse recorded resolution of conflicted merges SYNOPSIS -------- [verse] -'git rerere' [clear | forget <pathspec>... | diff | status | remaining | gc] +'git rerere' [--[no-]rerere-autoupdate] +'git rerere' clear +'git rerere' diff +'git rerere' forget <pathspec>... +'git rerere' gc +'git rerere' remaining +'git rerere' status DESCRIPTION -----------
builtin/rerere.c +51 -10
131 lines not on this page · raw message
Signature · 1 line
2.56.0.611.g1030f0b940.dirty