Skip to content

Commit 56710a7

Browse files
ttaylorrgitster
authored andcommitted
builtin/remote.c: show progress when renaming remote references
When renaming a remote, Git needs to rename all remote tracking references to the remote's new name (e.g., renaming "refs/remotes/old/foo" to "refs/remotes/new/foo" when renaming a remote from "old" to "new"). This can be somewhat slow when there are many references to rename, since each rename is done in a separate call to rename_ref() as opposed to grouping all renames together into the same transaction. It would be nice to execute all renames as a single transaction, but there is a snag: the reference transaction backend doesn't support renames during a transaction (only individually, via rename_ref()). The reasons there are described in more detail in [1], but the main problem is that in order to preserve the existing reflog, it must be moved while holding both locks (i.e., on "oldname" and "newname"), and the ref transaction code doesn't support inserting arbitrary actions into the middle of a transaction like that. As an aside, adding support for this to the ref transaction code is less straightforward than inserting both a ref_update() and ref_delete() call into the same transaction. rename_ref()'s special handling to detect D/F conflicts would need to be rewritten for the transaction code if we wanted to proactively catch D/F conflicts when renaming a reference during a transaction. The reftable backend could support this much more readily because of its lack of D/F conflicts. Instead of a more complex modification to the ref transaction code, display a progress meter when running verbosely in order to convince the user that Git is doing work while renaming a remote. This is mostly done as-expected, with the minor caveat that we intentionally count symrefs renames twice, since renaming a symref takes place over two separate calls (one to delete the old one, and another to create the new one). [1]: https://lore.kernel.org/git/[email protected]/ Suggested-by: brian m. carlson <[email protected]> Signed-off-by: Taylor Blau <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent c6dddb3 commit 56710a7

File tree

3 files changed

+29
-7
lines changed

3 files changed

+29
-7
lines changed

Documentation/git-remote.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ SYNOPSIS
1111
[verse]
1212
'git remote' [-v | --verbose]
1313
'git remote add' [-t <branch>] [-m <master>] [-f] [--[no-]tags] [--mirror=(fetch|push)] <name> <URL>
14-
'git remote rename' <old> <new>
14+
'git remote rename' [--[no-]progress] <old> <new>
1515
'git remote remove' <name>
1616
'git remote set-head' <name> (-a | --auto | -d | --delete | <branch>)
1717
'git remote set-branches' [--add] <name> <branch>...

builtin/remote.c

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@
1212
#include "object-store.h"
1313
#include "strvec.h"
1414
#include "commit-reach.h"
15+
#include "progress.h"
1516

1617
static const char * const builtin_remote_usage[] = {
1718
"git remote [-v | --verbose]",
1819
N_("git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--mirror=<fetch|push>] <name> <url>"),
19-
N_("git remote rename <old> <new>"),
20+
N_("git remote rename [--[no-]progress] <old> <new>"),
2021
N_("git remote remove <name>"),
2122
N_("git remote set-head <name> (-a | --auto | -d | --delete | <branch>)"),
2223
N_("git remote [-v | --verbose] show [-n] <name>"),
@@ -36,7 +37,7 @@ static const char * const builtin_remote_add_usage[] = {
3637
};
3738

3839
static const char * const builtin_remote_rename_usage[] = {
39-
N_("git remote rename <old> <new>"),
40+
N_("git remote rename [--[no-]progress] <old> <new>"),
4041
NULL
4142
};
4243

@@ -571,6 +572,7 @@ struct rename_info {
571572
const char *old_name;
572573
const char *new_name;
573574
struct string_list *remote_branches;
575+
uint32_t symrefs_nr;
574576
};
575577

576578
static int read_remote_branches(const char *refname,
@@ -587,10 +589,12 @@ static int read_remote_branches(const char *refname,
587589
item = string_list_append(rename->remote_branches, refname);
588590
symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
589591
NULL, &flag);
590-
if (symref && (flag & REF_ISSYMREF))
592+
if (symref && (flag & REF_ISSYMREF)) {
591593
item->util = xstrdup(symref);
592-
else
594+
rename->symrefs_nr++;
595+
} else {
593596
item->util = NULL;
597+
}
594598
}
595599
strbuf_release(&buf);
596600

@@ -674,15 +678,18 @@ static void handle_push_default(const char* old_name, const char* new_name)
674678

675679
static int mv(int argc, const char **argv)
676680
{
681+
int show_progress = isatty(2);
677682
struct option options[] = {
683+
OPT_BOOL(0, "progress", &show_progress, N_("force progress reporting")),
678684
OPT_END()
679685
};
680686
struct remote *oldremote, *newremote;
681687
struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT,
682688
old_remote_context = STRBUF_INIT;
683689
struct string_list remote_branches = STRING_LIST_INIT_DUP;
684690
struct rename_info rename;
685-
int i, refspec_updated = 0;
691+
int i, refs_renamed_nr = 0, refspec_updated = 0;
692+
struct progress *progress = NULL;
686693

687694
argc = parse_options(argc, argv, NULL, options,
688695
builtin_remote_rename_usage, 0);
@@ -693,6 +700,7 @@ static int mv(int argc, const char **argv)
693700
rename.old_name = argv[0];
694701
rename.new_name = argv[1];
695702
rename.remote_branches = &remote_branches;
703+
rename.symrefs_nr = 0;
696704

697705
oldremote = remote_get(rename.old_name);
698706
if (!remote_is_configured(oldremote, 1)) {
@@ -767,6 +775,14 @@ static int mv(int argc, const char **argv)
767775
* the new symrefs.
768776
*/
769777
for_each_ref(read_remote_branches, &rename);
778+
if (show_progress) {
779+
/*
780+
* Count symrefs twice, since "renaming" them is done by
781+
* deleting and recreating them in two separate passes.
782+
*/
783+
progress = start_progress(_("Renaming remote references"),
784+
rename.remote_branches->nr + rename.symrefs_nr);
785+
}
770786
for (i = 0; i < remote_branches.nr; i++) {
771787
struct string_list_item *item = remote_branches.items + i;
772788
int flag = 0;
@@ -776,6 +792,7 @@ static int mv(int argc, const char **argv)
776792
continue;
777793
if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF))
778794
die(_("deleting '%s' failed"), item->string);
795+
display_progress(progress, ++refs_renamed_nr);
779796
}
780797
for (i = 0; i < remote_branches.nr; i++) {
781798
struct string_list_item *item = remote_branches.items + i;
@@ -791,6 +808,7 @@ static int mv(int argc, const char **argv)
791808
item->string, buf.buf);
792809
if (rename_ref(item->string, buf.buf, buf2.buf))
793810
die(_("renaming '%s' failed"), item->string);
811+
display_progress(progress, ++refs_renamed_nr);
794812
}
795813
for (i = 0; i < remote_branches.nr; i++) {
796814
struct string_list_item *item = remote_branches.items + i;
@@ -810,7 +828,9 @@ static int mv(int argc, const char **argv)
810828
item->string, buf.buf);
811829
if (create_symref(buf.buf, buf2.buf, buf3.buf))
812830
die(_("creating '%s' failed"), buf.buf);
831+
display_progress(progress, ++refs_renamed_nr);
813832
}
833+
stop_progress(&progress);
814834
string_list_clear(&remote_branches, 1);
815835

816836
handle_push_default(rename.old_name, rename.new_name);

t/t5505-remote.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,9 @@ test_expect_success 'rename a remote' '
753753
(
754754
cd four &&
755755
git config branch.main.pushRemote origin &&
756-
git remote rename origin upstream &&
756+
GIT_TRACE2_EVENT=$(pwd)/trace \
757+
git remote rename --progress origin upstream &&
758+
test_region progress "Renaming remote references" trace &&
757759
grep "pushRemote" .git/config &&
758760
test -z "$(git for-each-ref refs/remotes/origin)" &&
759761
test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/main" &&

0 commit comments

Comments
 (0)