The new rename subcommand does the followings: 1) Renames the remote.foo configuration section to remote.bar 2) Updates the remote.bar.fetch refspecs 3) Updates the branch.*.remote settings 4) Renames the tracking branches. Signed-off-by: Miklos Vajna <vmiklos@xxxxxxxxxxxxxx> --- On Wed, Oct 22, 2008 at 11:52:54AM -0500, Brandon Casey <casey@xxxxxxxxxxxxxxx> wrote: > > + struct rename_info rename = { argv[1], argv[2], &remote_branches > > }; > > I think some non-c99 compilers would have issues with this run-time > initialization from function arguments. Plus, what if argv doesn't > have > 3 elements? I see you have a check for that below... Hm true. Here is a corrected version. Documentation/git-remote.txt | 6 ++ builtin-remote.c | 106 ++++++++++++++++++++++++++++++++++++++++++ t/t5505-remote.sh | 14 ++++++ 3 files changed, 126 insertions(+), 0 deletions(-) diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt index bb99810..4b5542a 100644 --- a/Documentation/git-remote.txt +++ b/Documentation/git-remote.txt @@ -11,6 +11,7 @@ SYNOPSIS [verse] 'git remote' [-v | --verbose] 'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url> +'git remote mv' <old> <new> 'git remote rm' <name> 'git remote show' [-n] <name> 'git remote prune' [-n | --dry-run] <name> @@ -61,6 +62,11 @@ only makes sense in bare repositories. If a remote uses mirror mode, furthermore, `git push` will always behave as if `\--mirror` was passed. +'mv':: + +Rename the remote named <old> to <new>. All remote tracking branches and +configuration settings for the remote are updated. + 'rm':: Remove the remote named <name>. All remote tracking branches and diff --git a/builtin-remote.c b/builtin-remote.c index 6b3325d..8a54bca 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -10,6 +10,7 @@ static const char * const builtin_remote_usage[] = { "git remote", "git remote add <name> <url>", + "git remote mv <old> <new>", "git remote rm <name>", "git remote show <name>", "git remote prune <name>", @@ -329,6 +330,109 @@ static int add_branch_for_removal(const char *refname, return 0; } +struct rename_info { + const char *old; + const char *new; + struct string_list *remote_branches; +}; + +static int read_remote_branches(const char *refname, + const unsigned char *sha1, int flags, void *cb_data) +{ + struct rename_info *rename = cb_data; + struct strbuf buf = STRBUF_INIT; + + strbuf_addf(&buf, "refs/remotes/%s", rename->old); + if(!prefixcmp(refname, buf.buf)) + string_list_append(xstrdup(refname), rename->remote_branches); + + return 0; +} + +static int mv(int argc, const char **argv) +{ + struct option options[] = { + OPT_END() + }; + struct remote *oldremote, *newremote; + struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT; + struct string_list remote_branches = { NULL, 0, 0, 0 }; + struct rename_info rename; + int i; + + if (argc != 3) + usage_with_options(builtin_remote_usage, options); + + rename.old = argv[1]; + rename.new = argv[2]; + rename.remote_branches = &remote_branches; + + oldremote = remote_get(rename.old); + if (!oldremote) + die("No such remote: %s", rename.old); + + newremote = remote_get(rename.new); + if (newremote && (newremote->url_nr > 1 || newremote->fetch_refspec_nr)) + die("remote %s already exists.", rename.new); + + strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new); + if (!valid_fetch_refspec(buf.buf)) + die("'%s' is not a valid remote name", rename.new); + + strbuf_reset(&buf); + strbuf_addf(&buf, "remote.%s", rename.old); + strbuf_addf(&buf2, "remote.%s", rename.new); + if (git_config_rename_section(buf.buf, buf2.buf) < 1) + return error("Could not rename config section '%s' to '%s'", + buf.buf, buf2.buf); + + strbuf_reset(&buf); + strbuf_addf(&buf, "remote.%s.fetch", rename.new); + if (git_config_set_multivar(buf.buf, NULL, NULL, 1)) + return error("Could not remove config section '%s'", buf.buf); + for (i = 0; i < oldremote->fetch_refspec_nr; i++) { + char *ptr; + + strbuf_reset(&buf2); + strbuf_addstr(&buf2, oldremote->fetch_refspec[i]); + ptr = strstr(buf2.buf, rename.old); + if (ptr) + strbuf_splice(&buf2, ptr-buf2.buf, strlen(rename.old), + rename.new, strlen(rename.new)); + if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0)) + return error("Could not append '%s'", buf.buf); + } + + read_branches(); + for (i = 0; i < branch_list.nr; i++) { + struct string_list_item *item = branch_list.items + i; + struct branch_info *info = item->util; + if (info->remote && !strcmp(info->remote, rename.old)) { + strbuf_reset(&buf); + strbuf_addf(&buf, "branch.%s.remote", item->string); + if (git_config_set(buf.buf, rename.new)) { + return error("Could not set '%s'", buf.buf); + } + } + } + + for_each_ref(read_remote_branches, &rename); + for (i = 0; i < remote_branches.nr; i++) { + struct string_list_item *item = remote_branches.items + i; + strbuf_reset(&buf); + strbuf_addstr(&buf, item->string); + strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old), + rename.new, strlen(rename.new)); + strbuf_reset(&buf2); + strbuf_addf(&buf2, "remote: renamed %s to %s", + item->string, buf.buf); + if (rename_ref(item->string, buf.buf, buf2.buf)) + die("renaming '%s' failed", item->string); + } + + return 0; +} + static int remove_branches(struct string_list *branches) { int i, result = 0; @@ -696,6 +800,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix) result = show_all(); else if (!strcmp(argv[0], "add")) result = add(argc, argv); + else if (!strcmp(argv[0], "mv")) + result = mv(argc, argv); else if (!strcmp(argv[0], "rm")) result = rm(argc, argv); else if (!strcmp(argv[0], "show")) diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index c449663..9b11fd3 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -324,4 +324,18 @@ test_expect_success 'reject adding remote with an invalid name' ' ' +# The first three tests if the config is properly updated, the last one +# checks if the branches are renamed. + +test_expect_success 'rename a remote' ' + + git clone one four && + (cd four && + git remote mv origin upstream && + git remote show |grep -q upstream && + git config remote.upstream.fetch |grep -q upstream && + test $(git config branch.master.remote) = "upstream" && + git for-each-ref|grep -q refs/remotes/upstream) + +' test_done -- 1.6.0.2 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html