{"thread":{"id":"15999","subject":"[PATCH] Implement git remote mv","startedAt":"2008-10-22T00:23:14Z","lastAt":"2008-11-12T04:22:38Z","messageCount":15,"participants":["Miklos Vajna","Brandon Casey","Jeff King","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"93643","messageId":"1224634994-1664-1-git-send-email-vmiklos@frugalware.org","threadId":"15999","inReplyTo":null,"subject":"[PATCH] Implement git remote mv","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-10-22T00:23:14Z","receivedAt":"2008-10-22T00:23:14Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"The new rename subcommand does the followings:\n\n1) Renames the remote.foo configuration section to remote.bar\n\n2) Updates the remote.bar.fetch refspecs\n\n3) Updates the branch.*.remote settings\n\n4) Renames the tracking branches.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n Documentation/git-remote.txt |    6 +++\n builtin-remote.c             |  102 ++++++++++++++++++++++++++++++++++++++++++\n t/t5505-remote.sh            |   14 ++++++\n 3 files changed, 122 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex bb99810..4b5542a 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -11,6 +11,7 @@ SYNOPSIS\n [verse]\n 'git remote' [-v | --verbose]\n 'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>\n+'git remote mv' <old> <new>\n 'git remote rm' <name>\n 'git remote show' [-n] <name>\n 'git remote prune' [-n | --dry-run] <name>\n@@ -61,6 +62,11 @@ only makes sense in bare repositories.  If a remote uses mirror\n mode, furthermore, `git push` will always behave as if `\\--mirror`\n was passed.\n \n+'mv'::\n+\n+Rename the remote named <old> to <new>. All remote tracking branches and\n+configuration settings for the remote are updated.\n+\n 'rm'::\n \n Remove the remote named <name>. All remote tracking branches and\ndiff --git a/builtin-remote.c b/builtin-remote.c\nindex 6b3325d..4a23738 100644\n--- a/builtin-remote.c\n+++ b/builtin-remote.c\n@@ -10,6 +10,7 @@\n static const char * const builtin_remote_usage[] = {\n \t\"git remote\",\n \t\"git remote add <name> <url>\",\n+\t\"git remote mv <old> <new>\",\n \t\"git remote rm <name>\",\n \t\"git remote show <name>\",\n \t\"git remote prune <name>\",\n@@ -329,6 +330,105 @@ static int add_branch_for_removal(const char *refname,\n \treturn 0;\n }\n \n+struct rename_info {\n+\tconst char *old;\n+\tconst char *new;\n+\tstruct string_list *remote_branches;\n+};\n+\n+static int read_remote_branches(const char *refname,\n+\tconst unsigned char *sha1, int flags, void *cb_data)\n+{\n+\tstruct rename_info *rename = cb_data;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\tstrbuf_addf(&buf, \"refs/remotes/%s\", rename->old);\n+\tif(!prefixcmp(refname, buf.buf))\n+\t\tstring_list_append(xstrdup(refname), rename->remote_branches);\n+\n+\treturn 0;\n+}\n+\n+static int mv(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\tstruct remote *oldremote, *newremote;\n+\tstruct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;\n+\tstruct string_list remote_branches = { NULL, 0, 0, 0 };\n+\tstruct rename_info rename = { argv[1], argv[2], &remote_branches };\n+\tint i;\n+\n+\tif (argc != 3)\n+\t\tusage_with_options(builtin_remote_usage, options);\n+\n+\toldremote = remote_get(rename.old);\n+\tif (!oldremote)\n+\t\tdie(\"No such remote: %s\", rename.old);\n+\n+\tnewremote = remote_get(rename.new);\n+\tif (newremote && (newremote->url_nr > 1 || newremote->fetch_refspec_nr))\n+\t\tdie(\"remote %s already exists.\", rename.new);\n+\n+\tstrbuf_addf(&buf, \"refs/heads/test:refs/remotes/%s/test\", rename.new);\n+\tif (!valid_fetch_refspec(buf.buf))\n+\t\tdie(\"'%s' is not a valid remote name\", rename.new);\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"remote.%s\", rename.old);\n+\tstrbuf_addf(&buf2, \"remote.%s\", rename.new);\n+\tif (git_config_rename_section(buf.buf, buf2.buf) < 1)\n+\t\treturn error(\"Could not rename config section '%s' to '%s'\",\n+\t\t\t\tbuf.buf, buf2.buf);\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"remote.%s.fetch\", rename.new);\n+\tif (git_config_set_multivar(buf.buf, NULL, NULL, 1))\n+\t\treturn error(\"Could not remove config section '%s'\", buf.buf);\n+\tfor (i = 0; i < oldremote->fetch_refspec_nr; i++) {\n+\t\tchar *ptr;\n+\n+\t\tstrbuf_reset(&buf2);\n+\t\tstrbuf_addstr(&buf2, oldremote->fetch_refspec[i]);\n+\t\tptr = strstr(buf2.buf, rename.old);\n+\t\tif (ptr)\n+\t\t\tstrbuf_splice(&buf2, ptr-buf2.buf, strlen(rename.old),\n+\t\t\t\t\trename.new, strlen(rename.new));\n+\t\tif (git_config_set_multivar(buf.buf, buf2.buf, \"^$\", 0))\n+\t\t\treturn error(\"Could not append '%s'\", buf.buf);\n+\t}\n+\n+\tread_branches();\n+\tfor (i = 0; i < branch_list.nr; i++) {\n+\t\tstruct string_list_item *item = branch_list.items + i;\n+\t\tstruct branch_info *info = item->util;\n+\t\tif (info->remote && !strcmp(info->remote, rename.old)) {\n+\t\t\tstrbuf_reset(&buf);\n+\t\t\tstrbuf_addf(&buf, \"branch.%s.remote\", item->string);\n+\t\t\tif (git_config_set(buf.buf, rename.new)) {\n+\t\t\t\treturn error(\"Could not set '%s'\", buf.buf);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tfor_each_ref(read_remote_branches, &rename);\n+\tfor (i = 0; i < remote_branches.nr; i++) {\n+\t\tstruct string_list_item *item = remote_branches.items + i;\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_addstr(&buf, item->string);\n+\t\tstrbuf_splice(&buf, strlen(\"refs/remotes/\"), strlen(rename.old),\n+\t\t\t\trename.new, strlen(rename.new));\n+\t\tstrbuf_reset(&buf2);\n+\t\tstrbuf_addf(&buf2, \"remote: renamed %s to %s\",\n+\t\t\t\titem->string, buf.buf);\n+\t\tif (rename_ref(item->string, buf.buf, buf2.buf))\n+\t\t\tdie(\"renaming '%s' failed\", item->string);\n+\t}\n+\n+\treturn 0;\n+}\n+\n static int remove_branches(struct string_list *branches)\n {\n \tint i, result = 0;\n@@ -696,6 +796,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix)\n \t\tresult = show_all();\n \telse if (!strcmp(argv[0], \"add\"))\n \t\tresult = add(argc, argv);\n+\telse if (!strcmp(argv[0], \"mv\"))\n+\t\tresult = mv(argc, argv);\n \telse if (!strcmp(argv[0], \"rm\"))\n \t\tresult = rm(argc, argv);\n \telse if (!strcmp(argv[0], \"show\"))\ndiff --git a/t/t5505-remote.sh b/t/t5505-remote.sh\nindex c449663..9b11fd3 100755\n--- a/t/t5505-remote.sh\n+++ b/t/t5505-remote.sh\n@@ -324,4 +324,18 @@ test_expect_success 'reject adding remote with an invalid name' '\n \n '\n \n+# The first three tests if the config is properly updated, the last one\n+# checks if the branches are renamed.\n+\n+test_expect_success 'rename a remote' '\n+\n+\tgit clone one four &&\n+\t(cd four &&\n+\t git remote mv origin upstream &&\n+\t git remote show |grep -q upstream &&\n+\t git config remote.upstream.fetch |grep -q upstream &&\n+\t test $(git config branch.master.remote) = \"upstream\" &&\n+\t git for-each-ref|grep -q refs/remotes/upstream)\n+\n+'\n test_done\n-- \n1.6.0.2\n"},{"id":"93700","messageId":"JQScKsOjmj-C7kmMPApAxZ3UDmaTZ1j0HkObQuCveJ9n4G-4o6Zxbw@cipher.nrlssc.navy.mil","threadId":"15999","inReplyTo":"1224634994-1664-1-git-send-email-vmiklos@frugalware.org","subject":"Re: [PATCH] Implement git remote mv","fromName":"Brandon Casey","fromEmail":"casey@nrlssc.navy.mil","sentAt":"2008-10-22T16:52:54Z","receivedAt":"2008-10-22T16:52:54Z","isPatch":true,"sender":{"key":"drafnel@gmail.com","avatar":"https://avatars.githubusercontent.com/u/921167?v=4"},"body":"Miklos Vajna wrote:\n\n> +static int mv(int argc, const char **argv)\n> +{\n> +\tstruct option options[] = {\n> +\t\tOPT_END()\n> +\t};\n> +\tstruct remote *oldremote, *newremote;\n> +\tstruct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;\n> +\tstruct string_list remote_branches = { NULL, 0, 0, 0 };\n> +\tstruct rename_info rename = { argv[1], argv[2], &remote_branches };\n\nI think some non-c99 compilers would have issues with this run-time\ninitialization from function arguments. Plus, what if argv doesn't have\n3 elements? I see you have a check for that below...\n\n> +\tint i;\n> +\n> +\tif (argc != 3)\n> +\t\tusage_with_options(builtin_remote_usage, options);\n\n-brandon\n"},{"id":"93753","messageId":"1224724704-12700-1-git-send-email-vmiklos@frugalware.org","threadId":"15999","inReplyTo":"JQScKsOjmj-C7kmMPApAxZ3UDmaTZ1j0HkObQuCveJ9n4G-4o6Zxbw@cipher.nrlssc.navy.mil","subject":"[PATCH] Implement git remote mv","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-10-23T01:18:24Z","receivedAt":"2008-10-23T01:18:24Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"The new rename subcommand does the followings:\n\n1) Renames the remote.foo configuration section to remote.bar\n\n2) Updates the remote.bar.fetch refspecs\n\n3) Updates the branch.*.remote settings\n\n4) Renames the tracking branches.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n\nOn Wed, Oct 22, 2008 at 11:52:54AM -0500, Brandon Casey <casey@nrlssc.navy.mil> wrote:\n> > +   struct rename_info rename = { argv[1], argv[2], &remote_branches\n> > };\n>\n> I think some non-c99 compilers would have issues with this run-time\n> initialization from function arguments. Plus, what if argv doesn't\n> have\n> 3 elements? I see you have a check for that below...\n\nHm true. Here is a corrected version.\n\n Documentation/git-remote.txt |    6 ++\n builtin-remote.c             |  106 ++++++++++++++++++++++++++++++++++++++++++\n t/t5505-remote.sh            |   14 ++++++\n 3 files changed, 126 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex bb99810..4b5542a 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -11,6 +11,7 @@ SYNOPSIS\n [verse]\n 'git remote' [-v | --verbose]\n 'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>\n+'git remote mv' <old> <new>\n 'git remote rm' <name>\n 'git remote show' [-n] <name>\n 'git remote prune' [-n | --dry-run] <name>\n@@ -61,6 +62,11 @@ only makes sense in bare repositories.  If a remote uses mirror\n mode, furthermore, `git push` will always behave as if `\\--mirror`\n was passed.\n \n+'mv'::\n+\n+Rename the remote named <old> to <new>. All remote tracking branches and\n+configuration settings for the remote are updated.\n+\n 'rm'::\n \n Remove the remote named <name>. All remote tracking branches and\ndiff --git a/builtin-remote.c b/builtin-remote.c\nindex 6b3325d..8a54bca 100644\n--- a/builtin-remote.c\n+++ b/builtin-remote.c\n@@ -10,6 +10,7 @@\n static const char * const builtin_remote_usage[] = {\n \t\"git remote\",\n \t\"git remote add <name> <url>\",\n+\t\"git remote mv <old> <new>\",\n \t\"git remote rm <name>\",\n \t\"git remote show <name>\",\n \t\"git remote prune <name>\",\n@@ -329,6 +330,109 @@ static int add_branch_for_removal(const char *refname,\n \treturn 0;\n }\n \n+struct rename_info {\n+\tconst char *old;\n+\tconst char *new;\n+\tstruct string_list *remote_branches;\n+};\n+\n+static int read_remote_branches(const char *refname,\n+\tconst unsigned char *sha1, int flags, void *cb_data)\n+{\n+\tstruct rename_info *rename = cb_data;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\tstrbuf_addf(&buf, \"refs/remotes/%s\", rename->old);\n+\tif(!prefixcmp(refname, buf.buf))\n+\t\tstring_list_append(xstrdup(refname), rename->remote_branches);\n+\n+\treturn 0;\n+}\n+\n+static int mv(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\tstruct remote *oldremote, *newremote;\n+\tstruct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;\n+\tstruct string_list remote_branches = { NULL, 0, 0, 0 };\n+\tstruct rename_info rename;\n+\tint i;\n+\n+\tif (argc != 3)\n+\t\tusage_with_options(builtin_remote_usage, options);\n+\n+\trename.old = argv[1];\n+\trename.new = argv[2];\n+\trename.remote_branches = &remote_branches;\n+\n+\toldremote = remote_get(rename.old);\n+\tif (!oldremote)\n+\t\tdie(\"No such remote: %s\", rename.old);\n+\n+\tnewremote = remote_get(rename.new);\n+\tif (newremote && (newremote->url_nr > 1 || newremote->fetch_refspec_nr))\n+\t\tdie(\"remote %s already exists.\", rename.new);\n+\n+\tstrbuf_addf(&buf, \"refs/heads/test:refs/remotes/%s/test\", rename.new);\n+\tif (!valid_fetch_refspec(buf.buf))\n+\t\tdie(\"'%s' is not a valid remote name\", rename.new);\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"remote.%s\", rename.old);\n+\tstrbuf_addf(&buf2, \"remote.%s\", rename.new);\n+\tif (git_config_rename_section(buf.buf, buf2.buf) < 1)\n+\t\treturn error(\"Could not rename config section '%s' to '%s'\",\n+\t\t\t\tbuf.buf, buf2.buf);\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"remote.%s.fetch\", rename.new);\n+\tif (git_config_set_multivar(buf.buf, NULL, NULL, 1))\n+\t\treturn error(\"Could not remove config section '%s'\", buf.buf);\n+\tfor (i = 0; i < oldremote->fetch_refspec_nr; i++) {\n+\t\tchar *ptr;\n+\n+\t\tstrbuf_reset(&buf2);\n+\t\tstrbuf_addstr(&buf2, oldremote->fetch_refspec[i]);\n+\t\tptr = strstr(buf2.buf, rename.old);\n+\t\tif (ptr)\n+\t\t\tstrbuf_splice(&buf2, ptr-buf2.buf, strlen(rename.old),\n+\t\t\t\t\trename.new, strlen(rename.new));\n+\t\tif (git_config_set_multivar(buf.buf, buf2.buf, \"^$\", 0))\n+\t\t\treturn error(\"Could not append '%s'\", buf.buf);\n+\t}\n+\n+\tread_branches();\n+\tfor (i = 0; i < branch_list.nr; i++) {\n+\t\tstruct string_list_item *item = branch_list.items + i;\n+\t\tstruct branch_info *info = item->util;\n+\t\tif (info->remote && !strcmp(info->remote, rename.old)) {\n+\t\t\tstrbuf_reset(&buf);\n+\t\t\tstrbuf_addf(&buf, \"branch.%s.remote\", item->string);\n+\t\t\tif (git_config_set(buf.buf, rename.new)) {\n+\t\t\t\treturn error(\"Could not set '%s'\", buf.buf);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tfor_each_ref(read_remote_branches, &rename);\n+\tfor (i = 0; i < remote_branches.nr; i++) {\n+\t\tstruct string_list_item *item = remote_branches.items + i;\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_addstr(&buf, item->string);\n+\t\tstrbuf_splice(&buf, strlen(\"refs/remotes/\"), strlen(rename.old),\n+\t\t\t\trename.new, strlen(rename.new));\n+\t\tstrbuf_reset(&buf2);\n+\t\tstrbuf_addf(&buf2, \"remote: renamed %s to %s\",\n+\t\t\t\titem->string, buf.buf);\n+\t\tif (rename_ref(item->string, buf.buf, buf2.buf))\n+\t\t\tdie(\"renaming '%s' failed\", item->string);\n+\t}\n+\n+\treturn 0;\n+}\n+\n static int remove_branches(struct string_list *branches)\n {\n \tint i, result = 0;\n@@ -696,6 +800,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix)\n \t\tresult = show_all();\n \telse if (!strcmp(argv[0], \"add\"))\n \t\tresult = add(argc, argv);\n+\telse if (!strcmp(argv[0], \"mv\"))\n+\t\tresult = mv(argc, argv);\n \telse if (!strcmp(argv[0], \"rm\"))\n \t\tresult = rm(argc, argv);\n \telse if (!strcmp(argv[0], \"show\"))\ndiff --git a/t/t5505-remote.sh b/t/t5505-remote.sh\nindex c449663..9b11fd3 100755\n--- a/t/t5505-remote.sh\n+++ b/t/t5505-remote.sh\n@@ -324,4 +324,18 @@ test_expect_success 'reject adding remote with an invalid name' '\n \n '\n \n+# The first three tests if the config is properly updated, the last one\n+# checks if the branches are renamed.\n+\n+test_expect_success 'rename a remote' '\n+\n+\tgit clone one four &&\n+\t(cd four &&\n+\t git remote mv origin upstream &&\n+\t git remote show |grep -q upstream &&\n+\t git config remote.upstream.fetch |grep -q upstream &&\n+\t test $(git config branch.master.remote) = \"upstream\" &&\n+\t git for-each-ref|grep -q refs/remotes/upstream)\n+\n+'\n test_done\n-- \n1.6.0.2\n"},{"id":"93757","messageId":"20081023035213.GA8396@coredump.intra.peff.net","threadId":"15999","inReplyTo":"1224724704-12700-1-git-send-email-vmiklos@frugalware.org","subject":"Re: [PATCH] Implement git remote mv","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2008-10-23T03:52:14Z","receivedAt":"2008-10-23T03:52:14Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Oct 23, 2008 at 03:18:24AM +0200, Miklos Vajna wrote:\n\n> The new rename subcommand does the followings:\n> \n> 1) Renames the remote.foo configuration section to remote.bar\n> \n> 2) Updates the remote.bar.fetch refspecs\n> \n> 3) Updates the branch.*.remote settings\n> \n> 4) Renames the tracking branches.\n\nI can't help but notice that the word \"rename\" appears all over the\ncommit description and in the code, but not in the user interface. Maybe\n\"rename\" would be a better name for the command instead of (or in\naddition to) \"mv\"?\n\n-Peff\n"},{"id":"93776","messageId":"1224766597-16034-1-git-send-email-vmiklos@frugalware.org","threadId":"15999","inReplyTo":"20081023035213.GA8396@coredump.intra.peff.net","subject":"[PATCH] Implement git remote rename","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-10-23T12:56:37Z","receivedAt":"2008-10-23T12:56:37Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"The new rename subcommand does the followings:\n\n1) Renames the remote.foo configuration section to remote.bar\n\n2) Updates the remote.bar.fetch refspecs\n\n3) Updates the branch.*.remote settings\n\n4) Renames the tracking branches.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n\nOn Wed, Oct 22, 2008 at 11:52:14PM -0400, Jeff King <peff@peff.net> wrote:\n> I can't help but notice that the word \"rename\" appears all over the\n> commit description and in the code, but not in the user interface.\n> Maybe\n> \"rename\" would be a better name for the command instead of (or in\n> addition to) \"mv\"?\n\nI called it \"mv\" because of \"rm\" (it is not \"remove\") and\ngit-mv/git-add, but I don't think it's a problem if it's called\n\"rename\". Here is an updated patch.\n\nThe function name is still \"mv\" because of rename(2).\n\n Documentation/git-remote.txt |    6 ++\n builtin-remote.c             |  106 ++++++++++++++++++++++++++++++++++++++++++\n t/t5505-remote.sh            |   14 ++++++\n 3 files changed, 126 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex bb99810..7b227b3 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -11,6 +11,7 @@ SYNOPSIS\n [verse]\n 'git remote' [-v | --verbose]\n 'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>\n+'git remote rename' <old> <new>\n 'git remote rm' <name>\n 'git remote show' [-n] <name>\n 'git remote prune' [-n | --dry-run] <name>\n@@ -61,6 +62,11 @@ only makes sense in bare repositories.  If a remote uses mirror\n mode, furthermore, `git push` will always behave as if `\\--mirror`\n was passed.\n \n+'rename'::\n+\n+Rename the remote named <old> to <new>. All remote tracking branches and\n+configuration settings for the remote are updated.\n+\n 'rm'::\n \n Remove the remote named <name>. All remote tracking branches and\ndiff --git a/builtin-remote.c b/builtin-remote.c\nindex 6b3325d..106d6f6 100644\n--- a/builtin-remote.c\n+++ b/builtin-remote.c\n@@ -10,6 +10,7 @@\n static const char * const builtin_remote_usage[] = {\n \t\"git remote\",\n \t\"git remote add <name> <url>\",\n+\t\"git remote rename <old> <new>\",\n \t\"git remote rm <name>\",\n \t\"git remote show <name>\",\n \t\"git remote prune <name>\",\n@@ -329,6 +330,109 @@ static int add_branch_for_removal(const char *refname,\n \treturn 0;\n }\n \n+struct rename_info {\n+\tconst char *old;\n+\tconst char *new;\n+\tstruct string_list *remote_branches;\n+};\n+\n+static int read_remote_branches(const char *refname,\n+\tconst unsigned char *sha1, int flags, void *cb_data)\n+{\n+\tstruct rename_info *rename = cb_data;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\tstrbuf_addf(&buf, \"refs/remotes/%s\", rename->old);\n+\tif(!prefixcmp(refname, buf.buf))\n+\t\tstring_list_append(xstrdup(refname), rename->remote_branches);\n+\n+\treturn 0;\n+}\n+\n+static int mv(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\tstruct remote *oldremote, *newremote;\n+\tstruct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;\n+\tstruct string_list remote_branches = { NULL, 0, 0, 0 };\n+\tstruct rename_info rename;\n+\tint i;\n+\n+\tif (argc != 3)\n+\t\tusage_with_options(builtin_remote_usage, options);\n+\n+\trename.old = argv[1];\n+\trename.new = argv[2];\n+\trename.remote_branches = &remote_branches;\n+\n+\toldremote = remote_get(rename.old);\n+\tif (!oldremote)\n+\t\tdie(\"No such remote: %s\", rename.old);\n+\n+\tnewremote = remote_get(rename.new);\n+\tif (newremote && (newremote->url_nr > 1 || newremote->fetch_refspec_nr))\n+\t\tdie(\"remote %s already exists.\", rename.new);\n+\n+\tstrbuf_addf(&buf, \"refs/heads/test:refs/remotes/%s/test\", rename.new);\n+\tif (!valid_fetch_refspec(buf.buf))\n+\t\tdie(\"'%s' is not a valid remote name\", rename.new);\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"remote.%s\", rename.old);\n+\tstrbuf_addf(&buf2, \"remote.%s\", rename.new);\n+\tif (git_config_rename_section(buf.buf, buf2.buf) < 1)\n+\t\treturn error(\"Could not rename config section '%s' to '%s'\",\n+\t\t\t\tbuf.buf, buf2.buf);\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"remote.%s.fetch\", rename.new);\n+\tif (git_config_set_multivar(buf.buf, NULL, NULL, 1))\n+\t\treturn error(\"Could not remove config section '%s'\", buf.buf);\n+\tfor (i = 0; i < oldremote->fetch_refspec_nr; i++) {\n+\t\tchar *ptr;\n+\n+\t\tstrbuf_reset(&buf2);\n+\t\tstrbuf_addstr(&buf2, oldremote->fetch_refspec[i]);\n+\t\tptr = strstr(buf2.buf, rename.old);\n+\t\tif (ptr)\n+\t\t\tstrbuf_splice(&buf2, ptr-buf2.buf, strlen(rename.old),\n+\t\t\t\t\trename.new, strlen(rename.new));\n+\t\tif (git_config_set_multivar(buf.buf, buf2.buf, \"^$\", 0))\n+\t\t\treturn error(\"Could not append '%s'\", buf.buf);\n+\t}\n+\n+\tread_branches();\n+\tfor (i = 0; i < branch_list.nr; i++) {\n+\t\tstruct string_list_item *item = branch_list.items + i;\n+\t\tstruct branch_info *info = item->util;\n+\t\tif (info->remote && !strcmp(info->remote, rename.old)) {\n+\t\t\tstrbuf_reset(&buf);\n+\t\t\tstrbuf_addf(&buf, \"branch.%s.remote\", item->string);\n+\t\t\tif (git_config_set(buf.buf, rename.new)) {\n+\t\t\t\treturn error(\"Could not set '%s'\", buf.buf);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tfor_each_ref(read_remote_branches, &rename);\n+\tfor (i = 0; i < remote_branches.nr; i++) {\n+\t\tstruct string_list_item *item = remote_branches.items + i;\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_addstr(&buf, item->string);\n+\t\tstrbuf_splice(&buf, strlen(\"refs/remotes/\"), strlen(rename.old),\n+\t\t\t\trename.new, strlen(rename.new));\n+\t\tstrbuf_reset(&buf2);\n+\t\tstrbuf_addf(&buf2, \"remote: renamed %s to %s\",\n+\t\t\t\titem->string, buf.buf);\n+\t\tif (rename_ref(item->string, buf.buf, buf2.buf))\n+\t\t\tdie(\"renaming '%s' failed\", item->string);\n+\t}\n+\n+\treturn 0;\n+}\n+\n static int remove_branches(struct string_list *branches)\n {\n \tint i, result = 0;\n@@ -696,6 +800,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix)\n \t\tresult = show_all();\n \telse if (!strcmp(argv[0], \"add\"))\n \t\tresult = add(argc, argv);\n+\telse if (!strcmp(argv[0], \"rename\"))\n+\t\tresult = mv(argc, argv);\n \telse if (!strcmp(argv[0], \"rm\"))\n \t\tresult = rm(argc, argv);\n \telse if (!strcmp(argv[0], \"show\"))\ndiff --git a/t/t5505-remote.sh b/t/t5505-remote.sh\nindex c449663..58bd7bf 100755\n--- a/t/t5505-remote.sh\n+++ b/t/t5505-remote.sh\n@@ -324,4 +324,18 @@ test_expect_success 'reject adding remote with an invalid name' '\n \n '\n \n+# The first three tests if the config is properly updated, the last one\n+# checks if the branches are renamed.\n+\n+test_expect_success 'rename a remote' '\n+\n+\tgit clone one four &&\n+\t(cd four &&\n+\t git remote rename origin upstream &&\n+\t git remote show |grep -q upstream &&\n+\t git config remote.upstream.fetch |grep -q upstream &&\n+\t test $(git config branch.master.remote) = \"upstream\" &&\n+\t git for-each-ref|grep -q refs/remotes/upstream)\n+\n+'\n test_done\n-- \n1.6.0.2\n"},{"id":"93900","messageId":"7v63nh1sc7.fsf@gitster.siamese.dyndns.org","threadId":"15999","inReplyTo":"1224766597-16034-1-git-send-email-vmiklos@frugalware.org","subject":"Re: [PATCH] Implement git remote rename","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-10-24T23:33:28Z","receivedAt":"2008-10-24T23:33:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miklos Vajna <vmiklos@frugalware.org> writes:\n\n> +static int mv(int argc, const char **argv)\n> +{\n> +\tstruct option options[] = {\n> +\t\tOPT_END()\n> +\t};\n> +\tstruct remote *oldremote, *newremote;\n> +\tstruct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;\n> +\tstruct string_list remote_branches = { NULL, 0, 0, 0 };\n> +\tstruct rename_info rename;\n> +\tint i;\n> +\n> +\tif (argc != 3)\n> +\t\tusage_with_options(builtin_remote_usage, options);\n> +\n> +\trename.old = argv[1];\n> +\trename.new = argv[2];\n> +\trename.remote_branches = &remote_branches;\n> +\n> +\toldremote = remote_get(rename.old);\n> +\tif (!oldremote)\n> +\t\tdie(\"No such remote: %s\", rename.old);\n> +\n> +\tnewremote = remote_get(rename.new);\n> +\tif (newremote && (newremote->url_nr > 1 || newremote->fetch_refspec_nr))\n> +\t\tdie(\"remote %s already exists.\", rename.new);\n> +\n> +\tstrbuf_addf(&buf, \"refs/heads/test:refs/remotes/%s/test\", rename.new);\n> +\tif (!valid_fetch_refspec(buf.buf))\n> +\t\tdie(\"'%s' is not a valid remote name\", rename.new);\n> +\n> +\tstrbuf_reset(&buf);\n> +\tstrbuf_addf(&buf, \"remote.%s\", rename.old);\n> +\tstrbuf_addf(&buf2, \"remote.%s\", rename.new);\n> +\tif (git_config_rename_section(buf.buf, buf2.buf) < 1)\n> +\t\treturn error(\"Could not rename config section '%s' to '%s'\",\n> +\t\t\t\tbuf.buf, buf2.buf);\n\nHmm, remote_get() can read from all three supported places that you can\ndefine remotes.  Could you explain what happens if the old remote is read\nfrom say $GIT_DIR/remotes/origin and you are renaming it to \"upstream\"\nwith \"git remote rename origin upstream\"?\n\nI suspect that if you record where you read the configuration from in\n\"struct remote\" and add necessary code to remove the original when\nrename.old is *not* coming from in-config definition, you would make it\npossible for repositories initialized with older git that has either\n$GIT_DIR/branches/origin or $GIT_DIR/remotes/origin to be migrated to the\nin-config format using \"git remote rename origin origin\".\n"},{"id":"94787","messageId":"1225736778-12556-1-git-send-email-vmiklos@frugalware.org","threadId":"15999","inReplyTo":"7v63nh1sc7.fsf@gitster.siamese.dyndns.org","subject":"[PATCH] Implement git remote rename","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-11-03T18:26:18Z","receivedAt":"2008-11-03T18:26:18Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"The new rename subcommand does the followings:\n\n1) Renames the remote.foo configuration section to remote.bar\n\n2) Updates the remote.bar.fetch refspecs\n\n3) Updates the branch.*.remote settings\n\n4) Renames the tracking branches: renames the normal refs and rewrites\n   the symrefs to point to the new refs.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n\nOn Fri, Oct 24, 2008 at 04:33:28PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> Hmm, remote_get() can read from all three supported places that you\n> can define remotes.  Could you explain what happens if the old remote\n> is read from say $GIT_DIR/remotes/origin and you are renaming it to\n> \"upstream\" with \"git remote rename origin upstream\"?\n\nCurrently it dies because it can't rename config section 'remote.origin'\nto 'remote.upstream'.\n\n> I suspect that if you record where you read the configuration from in\n> \"struct remote\" and add necessary code to remove the original when\n> rename.old is *not* coming from in-config definition, you would make\n> it possible for repositories initialized with older git that has\n> either $GIT_DIR/branches/origin or $GIT_DIR/remotes/origin to be\n> migrated to the in-config format using \"git remote rename origin\n> origin\".\n\nYes, that's possible. I think the patch is already large enough that it\nwould be better to do it as a separate patch, but I like the idea.\n\nOne important step is that currently we have to care about the\nremote.upstream.fetch variable, but once migration is supported, we have\nto pay attention to remote.upstream.url/push as well.\n\nIn the meantime here is an updated patch which applies on top of\nmv/maint-branch-m-symref. However, it is not meant for 'maint', of\ncourse. :)\n\nThe old version did not \"rename\" (in fact it is not just a rename but it\nreplaces origin with upstream in the contents) symrefs properly, now the\ntestcases are extended to check for this, too.\n\n Documentation/git-remote.txt |    6 ++\n builtin-remote.c             |  153 ++++++++++++++++++++++++++++++++++++++++++\n t/t5505-remote.sh            |   15 ++++\n 3 files changed, 174 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex bb99810..7b227b3 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -11,6 +11,7 @@ SYNOPSIS\n [verse]\n 'git remote' [-v | --verbose]\n 'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>\n+'git remote rename' <old> <new>\n 'git remote rm' <name>\n 'git remote show' [-n] <name>\n 'git remote prune' [-n | --dry-run] <name>\n@@ -61,6 +62,11 @@ only makes sense in bare repositories.  If a remote uses mirror\n mode, furthermore, `git push` will always behave as if `\\--mirror`\n was passed.\n \n+'rename'::\n+\n+Rename the remote named <old> to <new>. All remote tracking branches and\n+configuration settings for the remote are updated.\n+\n 'rm'::\n \n Remove the remote named <name>. All remote tracking branches and\ndiff --git a/builtin-remote.c b/builtin-remote.c\nindex e396a3a..1ca6cdb 100644\n--- a/builtin-remote.c\n+++ b/builtin-remote.c\n@@ -10,6 +10,7 @@\n static const char * const builtin_remote_usage[] = {\n \t\"git remote\",\n \t\"git remote add <name> <url>\",\n+\t\"git remote rename <old> <new>\",\n \t\"git remote rm <name>\",\n \t\"git remote show <name>\",\n \t\"git remote prune <name>\",\n@@ -329,6 +330,156 @@ static int add_branch_for_removal(const char *refname,\n \treturn 0;\n }\n \n+struct rename_info {\n+\tconst char *old;\n+\tconst char *new;\n+\tstruct string_list *remote_branches;\n+};\n+\n+static int read_remote_branches(const char *refname,\n+\tconst unsigned char *sha1, int flags, void *cb_data)\n+{\n+\tstruct rename_info *rename = cb_data;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct string_list_item *item;\n+\tint flag;\n+\tunsigned char orig_sha1[20];\n+\tconst char *symref;\n+\n+\tstrbuf_addf(&buf, \"refs/remotes/%s\", rename->old);\n+\tif(!prefixcmp(refname, buf.buf)) {\n+\t\titem = string_list_append(xstrdup(refname), rename->remote_branches);\n+\t\tsymref = resolve_ref(refname, orig_sha1, 1, &flag);\n+\t\tif (flag & REF_ISSYMREF)\n+\t\t\titem->util = xstrdup(symref);\n+\t\telse\n+\t\t\titem->util = NULL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int mv(int argc, const char **argv)\n+{\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\tstruct remote *oldremote, *newremote;\n+\tstruct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT;\n+\tstruct string_list remote_branches = { NULL, 0, 0, 0 };\n+\tstruct rename_info rename;\n+\tint i;\n+\n+\tif (argc != 3)\n+\t\tusage_with_options(builtin_remote_usage, options);\n+\n+\trename.old = argv[1];\n+\trename.new = argv[2];\n+\trename.remote_branches = &remote_branches;\n+\n+\toldremote = remote_get(rename.old);\n+\tif (!oldremote)\n+\t\tdie(\"No such remote: %s\", rename.old);\n+\n+\tnewremote = remote_get(rename.new);\n+\tif (newremote && (newremote->url_nr > 1 || newremote->fetch_refspec_nr))\n+\t\tdie(\"remote %s already exists.\", rename.new);\n+\n+\tstrbuf_addf(&buf, \"refs/heads/test:refs/remotes/%s/test\", rename.new);\n+\tif (!valid_fetch_refspec(buf.buf))\n+\t\tdie(\"'%s' is not a valid remote name\", rename.new);\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"remote.%s\", rename.old);\n+\tstrbuf_addf(&buf2, \"remote.%s\", rename.new);\n+\tif (git_config_rename_section(buf.buf, buf2.buf) < 1)\n+\t\treturn error(\"Could not rename config section '%s' to '%s'\",\n+\t\t\t\tbuf.buf, buf2.buf);\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"remote.%s.fetch\", rename.new);\n+\tif (git_config_set_multivar(buf.buf, NULL, NULL, 1))\n+\t\treturn error(\"Could not remove config section '%s'\", buf.buf);\n+\tfor (i = 0; i < oldremote->fetch_refspec_nr; i++) {\n+\t\tchar *ptr;\n+\n+\t\tstrbuf_reset(&buf2);\n+\t\tstrbuf_addstr(&buf2, oldremote->fetch_refspec[i]);\n+\t\tptr = strstr(buf2.buf, rename.old);\n+\t\tif (ptr)\n+\t\t\tstrbuf_splice(&buf2, ptr-buf2.buf, strlen(rename.old),\n+\t\t\t\t\trename.new, strlen(rename.new));\n+\t\tif (git_config_set_multivar(buf.buf, buf2.buf, \"^$\", 0))\n+\t\t\treturn error(\"Could not append '%s'\", buf.buf);\n+\t}\n+\n+\tread_branches();\n+\tfor (i = 0; i < branch_list.nr; i++) {\n+\t\tstruct string_list_item *item = branch_list.items + i;\n+\t\tstruct branch_info *info = item->util;\n+\t\tif (info->remote && !strcmp(info->remote, rename.old)) {\n+\t\t\tstrbuf_reset(&buf);\n+\t\t\tstrbuf_addf(&buf, \"branch.%s.remote\", item->string);\n+\t\t\tif (git_config_set(buf.buf, rename.new)) {\n+\t\t\t\treturn error(\"Could not set '%s'\", buf.buf);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * First remove symrefs, then rename the rest, finally create\n+\t * the new symrefs.\n+\t */\n+\tfor_each_ref(read_remote_branches, &rename);\n+\tfor (i = 0; i < remote_branches.nr; i++) {\n+\t\tstruct string_list_item *item = remote_branches.items + i;\n+\t\tint flag = 0;\n+\t\tunsigned char sha1[20];\n+\t\tconst char *symref;\n+\n+\t\tsymref = resolve_ref(item->string, sha1, 1, &flag);\n+\t\tif (!(flag & REF_ISSYMREF))\n+\t\t\tcontinue;\n+\t\tif (delete_ref(item->string, NULL, REF_NODEREF))\n+\t\t\tdie(\"deleting '%s' failed\", item->string);\n+\t}\n+\tfor (i = 0; i < remote_branches.nr; i++) {\n+\t\tstruct string_list_item *item = remote_branches.items + i;\n+\n+\t\tif (item->util)\n+\t\t\tcontinue;\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_addstr(&buf, item->string);\n+\t\tstrbuf_splice(&buf, strlen(\"refs/remotes/\"), strlen(rename.old),\n+\t\t\t\trename.new, strlen(rename.new));\n+\t\tstrbuf_reset(&buf2);\n+\t\tstrbuf_addf(&buf2, \"remote: renamed %s to %s\",\n+\t\t\t\titem->string, buf.buf);\n+\t\tif (rename_ref(item->string, buf.buf, buf2.buf))\n+\t\t\tdie(\"renaming '%s' failed\", item->string);\n+\t}\n+\tfor (i = 0; i < remote_branches.nr; i++) {\n+\t\tstruct string_list_item *item = remote_branches.items + i;\n+\n+\t\tif (!item->util)\n+\t\t\tcontinue;\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_addstr(&buf, item->string);\n+\t\tstrbuf_splice(&buf, strlen(\"refs/remotes/\"), strlen(rename.old),\n+\t\t\t\trename.new, strlen(rename.new));\n+\t\tstrbuf_reset(&buf2);\n+\t\tstrbuf_addstr(&buf2, item->util);\n+\t\tstrbuf_splice(&buf2, strlen(\"refs/remotes/\"), strlen(rename.old),\n+\t\t\t\trename.new, strlen(rename.new));\n+\t\tstrbuf_reset(&buf3);\n+\t\tstrbuf_addf(&buf3, \"remote: renamed %s to %s\",\n+\t\t\t\titem->string, buf.buf);\n+\t\tif (create_symref(buf.buf, buf2.buf, buf3.buf))\n+\t\t\tdie(\"creating '%s' failed\", buf.buf);\n+\t}\n+\treturn 0;\n+}\n+\n static int remove_branches(struct string_list *branches)\n {\n \tint i, result = 0;\n@@ -695,6 +846,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix)\n \t\tresult = show_all();\n \telse if (!strcmp(argv[0], \"add\"))\n \t\tresult = add(argc, argv);\n+\telse if (!strcmp(argv[0], \"rename\"))\n+\t\tresult = mv(argc, argv);\n \telse if (!strcmp(argv[0], \"rm\"))\n \t\tresult = rm(argc, argv);\n \telse if (!strcmp(argv[0], \"show\"))\ndiff --git a/t/t5505-remote.sh b/t/t5505-remote.sh\nindex c4380c7..0c956ba 100755\n--- a/t/t5505-remote.sh\n+++ b/t/t5505-remote.sh\n@@ -328,4 +328,19 @@ test_expect_success 'reject adding remote with an invalid name' '\n \n '\n \n+# The first three test if the tracking branches are properly renamed,\n+# the last two ones check if the config is updated.\n+\n+test_expect_success 'rename a remote' '\n+\n+\tgit clone one four &&\n+\t(cd four &&\n+\t git remote rename origin upstream &&\n+\t rmdir .git/refs/remotes/origin &&\n+\t test \"$(git symbolic-ref refs/remotes/upstream/HEAD)\" = \"refs/remotes/upstream/master\" &&\n+\t test \"$(git rev-parse upstream/master)\" = \"$(git rev-parse master)\" &&\n+\t test \"$(git config remote.upstream.fetch)\" = \"+refs/heads/*:refs/remotes/upstream/*\" &&\n+\t test \"$(git config branch.master.remote)\" = \"upstream\")\n+\n+'\n test_done\n-- \n1.6.0.2\n"},{"id":"95378","messageId":"cover.1226349595.git.vmiklos@frugalware.org","threadId":"15999","inReplyTo":"7v63nh1sc7.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] Implement git remote rename","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-11-10T20:42:59Z","receivedAt":"2008-11-10T20:42:59Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Fri, Oct 24, 2008 at 04:33:28PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> I suspect that if you record where you read the configuration from in\n> \"struct remote\" and add necessary code to remove the original when\n> rename.old is *not* coming from in-config definition, you would make\n> it possible for repositories initialized with older git that has\n> either $GIT_DIR/branches/origin or $GIT_DIR/remotes/origin to be\n> migrated to the in-config format using \"git remote rename origin\n> origin\".\n\nHere are 4 patches to implement this + add the related\ntestcases/documentation.\n\nMiklos Vajna (4):\n  remote: add a new 'origin' variable to the struct\n  git-remote rename: support remotes->config migration\n  git-remote rename: support branches->config migration\n  git-remote: document the migration feature of the rename subcommand\n\n Documentation/git-remote.txt |    4 ++++\n builtin-remote.c             |   35 +++++++++++++++++++++++++++++++++++\n remote.c                     |    3 +++\n remote.h                     |    7 +++++++\n t/t5505-remote.sh            |   33 +++++++++++++++++++++++++++++++++\n 5 files changed, 82 insertions(+), 0 deletions(-)\n"},{"id":"95380","messageId":"95e56b46e30b41af31da86789625c93511f1faef.1226349595.git.vmiklos@frugalware.org","threadId":"15999","inReplyTo":"cover.1226349595.git.vmiklos@frugalware.org","subject":"[PATCH 1/4] remote: add a new 'origin' variable to the struct","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-11-10T20:43:00Z","receivedAt":"2008-11-10T20:43:00Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"This allows one to track where was the remote's original source, so that\nit's possible to decide if it makes sense to migrate it to the config\nformat or not.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n remote.c |    3 +++\n remote.h |    7 +++++++\n 2 files changed, 10 insertions(+), 0 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex e530a21..cbb3e48 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -201,6 +201,7 @@ static void read_remotes_file(struct remote *remote)\n \n \tif (!f)\n \t\treturn;\n+\tremote->origin = REMOTE_REMOTES;\n \twhile (fgets(buffer, BUF_SIZE, f)) {\n \t\tint value_list;\n \t\tchar *s, *p;\n@@ -261,6 +262,7 @@ static void read_branches_file(struct remote *remote)\n \t\ts++;\n \tif (!*s)\n \t\treturn;\n+\tremote->origin = REMOTE_BRANCHES;\n \tp = s + strlen(s);\n \twhile (isspace(p[-1]))\n \t\t*--p = 0;\n@@ -350,6 +352,7 @@ static int handle_config(const char *key, const char *value, void *cb)\n \tif (!subkey)\n \t\treturn error(\"Config with no key for remote %s\", name);\n \tremote = make_remote(name, subkey - name);\n+\tremote->origin = REMOTE_CONFIG;\n \tif (!strcmp(subkey, \".mirror\"))\n \t\tremote->mirror = git_config_bool(key, value);\n \telse if (!strcmp(subkey, \".skipdefaultupdate\"))\ndiff --git a/remote.h b/remote.h\nindex d2e170c..a46a5be 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -1,8 +1,15 @@\n #ifndef REMOTE_H\n #define REMOTE_H\n \n+enum {\n+\tREMOTE_CONFIG,\n+\tREMOTE_REMOTES,\n+\tREMOTE_BRANCHES\n+};\n+\n struct remote {\n \tconst char *name;\n+\tint origin;\n \n \tconst char **url;\n \tint url_nr;\n-- \n1.6.0.2\n"},{"id":"95382","messageId":"033bc63195299e494791e4e6d8a41f142d848bba.1226349595.git.vmiklos@frugalware.org","threadId":"15999","inReplyTo":"95e56b46e30b41af31da86789625c93511f1faef.1226349595.git.vmiklos@frugalware.org","subject":"[PATCH 2/4] git-remote rename: support remotes->config migration","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-11-10T20:43:01Z","receivedAt":"2008-11-10T20:43:01Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"This patch makes it possible to migrate a remote stored in a\n$GIT_DIR/remotes/nick file to the configuration file format.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n builtin-remote.c  |   33 +++++++++++++++++++++++++++++++++\n t/t5505-remote.sh |   21 +++++++++++++++++++++\n 2 files changed, 54 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-remote.c b/builtin-remote.c\nindex 1ca6cdb..d9d0ba3 100644\n--- a/builtin-remote.c\n+++ b/builtin-remote.c\n@@ -359,6 +359,36 @@ static int read_remote_branches(const char *refname,\n \treturn 0;\n }\n \n+static int migrate_file(struct remote *remote)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint i;\n+\tchar *path = NULL;\n+\n+\tstrbuf_addf(&buf, \"remote.%s.url\", remote->name);\n+\tfor (i = 0; i < remote->url_nr; i++)\n+\t\tif (git_config_set_multivar(buf.buf, remote->url[i], \"^$\", 0))\n+\t\t\treturn error(\"Could not append '%s' to '%s'\",\n+\t\t\t\t\tremote->url[i], buf.buf);\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"remote.%s.push\", remote->name);\n+\tfor (i = 0; i < remote->push_refspec_nr; i++)\n+\t\tif (git_config_set_multivar(buf.buf, remote->push_refspec[i], \"^$\", 0))\n+\t\t\treturn error(\"Could not append '%s' to '%s'\",\n+\t\t\t\t\tremote->push_refspec[i], buf.buf);\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"remote.%s.fetch\", remote->name);\n+\tfor (i = 0; i < remote->fetch_refspec_nr; i++)\n+\t\tif (git_config_set_multivar(buf.buf, remote->fetch_refspec[i], \"^$\", 0))\n+\t\t\treturn error(\"Could not append '%s' to '%s'\",\n+\t\t\t\t\tremote->fetch_refspec[i], buf.buf);\n+\tif (remote->origin == REMOTE_REMOTES)\n+\t\tpath = git_path(\"remotes/%s\", remote->name);\n+\tif (path && unlink(path))\n+\t\twarning(\"failed to remove '%s'\", path);\n+\treturn 0;\n+}\n+\n static int mv(int argc, const char **argv)\n {\n \tstruct option options[] = {\n@@ -381,6 +411,9 @@ static int mv(int argc, const char **argv)\n \tif (!oldremote)\n \t\tdie(\"No such remote: %s\", rename.old);\n \n+\tif (!strcmp(rename.old, rename.new) && oldremote->origin != REMOTE_CONFIG)\n+\t\treturn migrate_file(oldremote);\n+\n \tnewremote = remote_get(rename.new);\n \tif (newremote && (newremote->url_nr > 1 || newremote->fetch_refspec_nr))\n \t\tdie(\"remote %s already exists.\", rename.new);\ndiff --git a/t/t5505-remote.sh b/t/t5505-remote.sh\nindex 0c956ba..1567631 100755\n--- a/t/t5505-remote.sh\n+++ b/t/t5505-remote.sh\n@@ -343,4 +343,25 @@ test_expect_success 'rename a remote' '\n \t test \"$(git config branch.master.remote)\" = \"upstream\")\n \n '\n+\n+cat > remotes_origin << EOF\n+URL: $(pwd)/one\n+Push: refs/heads/master:refs/heads/upstream\n+Pull: refs/heads/master:refs/heads/origin\n+EOF\n+\n+test_expect_success 'migrate a remote from named file in $GIT_DIR/remotes' '\n+\tgit clone one five &&\n+\torigin_url=$(pwd)/one &&\n+\t(cd five &&\n+\t git remote rm origin &&\n+\t mkdir -p .git/remotes &&\n+\t cat ../remotes_origin > .git/remotes/origin &&\n+\t git remote rename origin origin &&\n+\t ! test -f .git/remotes/origin &&\n+\t test \"$(git config remote.origin.url)\" = \"$origin_url\" &&\n+\t test \"$(git config remote.origin.push)\" = \"refs/heads/master:refs/heads/upstream\" &&\n+\t test \"$(git config remote.origin.fetch)\" = \"refs/heads/master:refs/heads/origin\")\n+'\n+\n test_done\n-- \n1.6.0.2\n"},{"id":"95381","messageId":"b32cf68df41e417079a49dc02e46ffc0c571029b.1226349595.git.vmiklos@frugalware.org","threadId":"15999","inReplyTo":"033bc63195299e494791e4e6d8a41f142d848bba.1226349595.git.vmiklos@frugalware.org","subject":"[PATCH 3/4] git-remote rename: support branches->config migration","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-11-10T20:43:02Z","receivedAt":"2008-11-10T20:43:02Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"This is similar to the remotes->config one, but it makes the\nbranches->config conversion possible.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n builtin-remote.c  |    2 ++\n t/t5505-remote.sh |   12 ++++++++++++\n 2 files changed, 14 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-remote.c b/builtin-remote.c\nindex d9d0ba3..3af1876 100644\n--- a/builtin-remote.c\n+++ b/builtin-remote.c\n@@ -384,6 +384,8 @@ static int migrate_file(struct remote *remote)\n \t\t\t\t\tremote->fetch_refspec[i], buf.buf);\n \tif (remote->origin == REMOTE_REMOTES)\n \t\tpath = git_path(\"remotes/%s\", remote->name);\n+\telse if (remote->origin == REMOTE_BRANCHES)\n+\t\tpath = git_path(\"branches/%s\", remote->name);\n \tif (path && unlink(path))\n \t\twarning(\"failed to remove '%s'\", path);\n \treturn 0;\ndiff --git a/t/t5505-remote.sh b/t/t5505-remote.sh\nindex 1567631..1f59960 100755\n--- a/t/t5505-remote.sh\n+++ b/t/t5505-remote.sh\n@@ -364,4 +364,16 @@ test_expect_success 'migrate a remote from named file in $GIT_DIR/remotes' '\n \t test \"$(git config remote.origin.fetch)\" = \"refs/heads/master:refs/heads/origin\")\n '\n \n+test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '\n+\tgit clone one six &&\n+\torigin_url=$(pwd)/one &&\n+\t(cd six &&\n+\t git remote rm origin &&\n+\t echo \"$origin_url\" > .git/branches/origin &&\n+\t git remote rename origin origin &&\n+\t ! test -f .git/branches/origin &&\n+\t test \"$(git config remote.origin.url)\" = \"$origin_url\" &&\n+\t test \"$(git config remote.origin.fetch)\" = \"refs/heads/master:refs/heads/origin\")\n+'\n+\n test_done\n-- \n1.6.0.2\n"},{"id":"95379","messageId":"ebcd8165be4bf4444692f6552aeb4d2ae6c32579.1226349595.git.vmiklos@frugalware.org","threadId":"15999","inReplyTo":"b32cf68df41e417079a49dc02e46ffc0c571029b.1226349595.git.vmiklos@frugalware.org","subject":"[PATCH 4/4] git-remote: document the migration feature of the rename subcommand","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-11-10T20:43:03Z","receivedAt":"2008-11-10T20:43:03Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n Documentation/git-remote.txt |    4 ++++\n 1 files changed, 4 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 7b227b3..fad983e 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -66,6 +66,10 @@ was passed.\n \n Rename the remote named <old> to <new>. All remote tracking branches and\n configuration settings for the remote are updated.\n++\n+In case <old> and <new> are the same, and <old> is a file under\n+`$GIT_DIR/remotes` or `$GIT_DIR/branches`, the remote is converted to\n+the configuration file format.\n \n 'rm'::\n \n-- \n1.6.0.2\n"},{"id":"95515","messageId":"7v63mtvkdx.fsf@gitster.siamese.dyndns.org","threadId":"15999","inReplyTo":"b32cf68df41e417079a49dc02e46ffc0c571029b.1226349595.git.vmiklos@frugalware.org","subject":"Re: [PATCH 3/4] git-remote rename: support branches->config migration","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-11-12T00:49:14Z","receivedAt":"2008-11-12T00:49:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miklos Vajna <vmiklos@frugalware.org> writes:\n\n> This is similar to the remotes->config one, but it makes the\n> branches->config conversion possible.\n>\n> Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>\n> ---\n>  builtin-remote.c  |    2 ++\n>  t/t5505-remote.sh |   12 ++++++++++++\n>  2 files changed, 14 insertions(+), 0 deletions(-)\n>\n> diff --git a/builtin-remote.c b/builtin-remote.c\n> index d9d0ba3..3af1876 100644\n> --- a/builtin-remote.c\n> +++ b/builtin-remote.c\n> @@ -384,6 +384,8 @@ static int migrate_file(struct remote *remote)\n>  \t\t\t\t\tremote->fetch_refspec[i], buf.buf);\n>  \tif (remote->origin == REMOTE_REMOTES)\n>  \t\tpath = git_path(\"remotes/%s\", remote->name);\n> +\telse if (remote->origin == REMOTE_BRANCHES)\n> +\t\tpath = git_path(\"branches/%s\", remote->name);\n\nThere is something fishy going on between 2/4 and 3/4.  2/4 was advertised\nto migrate remotes to config and had a call to migrate_file() for that\npurpose.  Here this one now allows to convert branches but there is no\nchange to the callsite of migrate_file().\n\nWhich would mean that 2/4 would convert branches/foo too.  And this one is\nonly to remove the leftover branches/foo file.\n\nOr am I utterly confused?\n"},{"id":"95524","messageId":"20081112020158.GK24201@genesis.frugalware.org","threadId":"15999","inReplyTo":"7v63mtvkdx.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 3/4] git-remote rename: support branches->config migration","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-11-12T02:01:58Z","receivedAt":"2008-11-12T02:01:58Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Tue, Nov 11, 2008 at 04:49:14PM -0800, Junio C Hamano <gitster@pobox.com> wrote:\n> There is something fishy going on between 2/4 and 3/4.  2/4 was advertised\n> to migrate remotes to config and had a call to migrate_file() for that\n> purpose.  Here this one now allows to convert branches but there is no\n> change to the callsite of migrate_file().\n> \n> Which would mean that 2/4 would convert branches/foo too.  And this one is\n> only to remove the leftover branches/foo file.\n> \n> Or am I utterly confused?\n\nThe trick is that 2/4 already added support for remotes/foo as it uses\nremote_get() and that detects remotes/foo as well, but that is\ncompletely unintentional. If you think it makes sense, just squash 3/4\nto 2/4, I made it two separate patches because I think these are\nlogically separate changes.\n"},{"id":"95526","messageId":"7viqqtshdd.fsf@gitster.siamese.dyndns.org","threadId":"15999","inReplyTo":"20081112020158.GK24201@genesis.frugalware.org","subject":"Re: [PATCH 3/4] git-remote rename: support branches->config migration","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-11-12T04:22:38Z","receivedAt":"2008-11-12T04:22:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miklos Vajna <vmiklos@frugalware.org> writes:\n\n> On Tue, Nov 11, 2008 at 04:49:14PM -0800, Junio C Hamano <gitster@pobox.com> wrote:\n>> There is something fishy going on between 2/4 and 3/4.  2/4 was advertised\n>> to migrate remotes to config and had a call to migrate_file() for that\n>> purpose.  Here this one now allows to convert branches but there is no\n>> change to the callsite of migrate_file().\n>> \n>> Which would mean that 2/4 would convert branches/foo too.  And this one is\n>> only to remove the leftover branches/foo file.\n>> \n>> Or am I utterly confused?\n>\n> The trick is that 2/4 already added support for remotes/foo as it uses\n> remote_get() and that detects remotes/foo as well, but that is\n> completely unintentional.\n\nThat is not a trick; it merely is a broken code.\n\nThe function migrate_file() introduced by [2/4] is called for any remote\ndefinition that did not come from config (by definition, it either came\nfrom remotes/foo or branches/foo).  The function adds the entries for the\ngiven remote definition to the config file, and then removes remotes/foo\nfile if the remote definition came from it.  So it is a logically\nconsistent change if you only called this function only for remote\ndefinitions that came from remotes/foo.\n\nBut the function is called for a remote definition that originally came\nfrom branches/foo as well.  It happily adds the definition to the config,\neven though it *fails to remove* branches/foo file.\n\nDo you still think 2/4 is a logically contained good change?\n\nIf you apply this to 5505 (taken from 3/4, but removing the check for\nbranches/origin file), and look at resulting t/trash*/six/.git/config\nfile, you will see you have already migrated the remote definition to the\nconfig.\n\ndiff --git i/t/t5505-remote.sh w/t/t5505-remote.sh\nindex 1567631..60bb9e5 100755\n--- i/t/t5505-remote.sh\n+++ w/t/t5505-remote.sh\n@@ -364,4 +364,15 @@ test_expect_success 'migrate a remote from named file in $GIT_DIR/remotes' '\n \t test \"$(git config remote.origin.fetch)\" = \"refs/heads/master:refs/heads/origin\")\n '\n \n-test_done\n+test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '\n+\tgit clone one six &&\n+\torigin_url=$(pwd)/one &&\n+\t(cd six &&\n+\t git remote rm origin &&\n+\t echo \"$origin_url\" > .git/branches/origin &&\n+\t git remote rename origin origin &&\n+\t test \"$(git config remote.origin.url)\" = \"$origin_url\" &&\n+\t test \"$(git config remote.origin.fetch)\" = \"refs/heads/master:refs/heads/origin\")\n+'\n+\n+: test_done\n"}]}