Code

Merge branch 'sg/maint-gitdir-in-subdir'
[git.git] / builtin-remote.c
index 47deb0a8028760a7195d11b8212904e312a8998c..abc8dd8389be4a51b467b4f6d4f74e2037d65423 100644 (file)
@@ -8,12 +8,13 @@
 #include "refs.h"
 
 static const char * const builtin_remote_usage[] = {
-       "git remote",
-       "git remote add <name> <url>",
+       "git remote [-v | --verbose]",
+       "git remote add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>",
+       "git remote rename <old> <new>",
        "git remote rm <name>",
-       "git remote show <name>",
-       "git remote prune <name>",
-       "git remote update [group]",
+       "git remote show [-n] <name>",
+       "git remote prune [-n | --dry-run] <name>",
+       "git remote [-v | --verbose] update [group]",
        NULL
 };
 
@@ -41,7 +42,11 @@ static int opt_parse_track(const struct option *opt, const char *arg, int not)
 
 static int fetch_remote(const char *name)
 {
-       const char *argv[] = { "fetch", name, NULL };
+       const char *argv[] = { "fetch", name, NULL, NULL };
+       if (verbose) {
+               argv[1] = "-v";
+               argv[2] = name;
+       }
        printf("Updating %s\n", name);
        if (run_command_v_opt(argv, RUN_GIT_CMD))
                return error("Could not fetch %s", name);
@@ -329,6 +334,191 @@ 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;
+       struct string_list_item *item;
+       int flag;
+       unsigned char orig_sha1[20];
+       const char *symref;
+
+       strbuf_addf(&buf, "refs/remotes/%s", rename->old);
+       if(!prefixcmp(refname, buf.buf)) {
+               item = string_list_append(xstrdup(refname), rename->remote_branches);
+               symref = resolve_ref(refname, orig_sha1, 1, &flag);
+               if (flag & REF_ISSYMREF)
+                       item->util = xstrdup(symref);
+               else
+                       item->util = NULL;
+       }
+
+       return 0;
+}
+
+static int migrate_file(struct remote *remote)
+{
+       struct strbuf buf = STRBUF_INIT;
+       int i;
+       char *path = NULL;
+
+       strbuf_addf(&buf, "remote.%s.url", remote->name);
+       for (i = 0; i < remote->url_nr; i++)
+               if (git_config_set_multivar(buf.buf, remote->url[i], "^$", 0))
+                       return error("Could not append '%s' to '%s'",
+                                       remote->url[i], buf.buf);
+       strbuf_reset(&buf);
+       strbuf_addf(&buf, "remote.%s.push", remote->name);
+       for (i = 0; i < remote->push_refspec_nr; i++)
+               if (git_config_set_multivar(buf.buf, remote->push_refspec[i], "^$", 0))
+                       return error("Could not append '%s' to '%s'",
+                                       remote->push_refspec[i], buf.buf);
+       strbuf_reset(&buf);
+       strbuf_addf(&buf, "remote.%s.fetch", remote->name);
+       for (i = 0; i < remote->fetch_refspec_nr; i++)
+               if (git_config_set_multivar(buf.buf, remote->fetch_refspec[i], "^$", 0))
+                       return error("Could not append '%s' to '%s'",
+                                       remote->fetch_refspec[i], buf.buf);
+       if (remote->origin == REMOTE_REMOTES)
+               path = git_path("remotes/%s", remote->name);
+       else if (remote->origin == REMOTE_BRANCHES)
+               path = git_path("branches/%s", remote->name);
+       if (path && unlink(path))
+               warning("failed to remove '%s'", path);
+       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, buf3 = 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);
+
+       if (!strcmp(rename.old, rename.new) && oldremote->origin != REMOTE_CONFIG)
+               return migrate_file(oldremote);
+
+       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);
+                       }
+               }
+       }
+
+       /*
+        * First remove symrefs, then rename the rest, finally create
+        * the new symrefs.
+        */
+       for_each_ref(read_remote_branches, &rename);
+       for (i = 0; i < remote_branches.nr; i++) {
+               struct string_list_item *item = remote_branches.items + i;
+               int flag = 0;
+               unsigned char sha1[20];
+               const char *symref;
+
+               symref = resolve_ref(item->string, sha1, 1, &flag);
+               if (!(flag & REF_ISSYMREF))
+                       continue;
+               if (delete_ref(item->string, NULL, REF_NODEREF))
+                       die("deleting '%s' failed", item->string);
+       }
+       for (i = 0; i < remote_branches.nr; i++) {
+               struct string_list_item *item = remote_branches.items + i;
+
+               if (item->util)
+                       continue;
+               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);
+       }
+       for (i = 0; i < remote_branches.nr; i++) {
+               struct string_list_item *item = remote_branches.items + i;
+
+               if (!item->util)
+                       continue;
+               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_addstr(&buf2, item->util);
+               strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old),
+                               rename.new, strlen(rename.new));
+               strbuf_reset(&buf3);
+               strbuf_addf(&buf3, "remote: renamed %s to %s",
+                               item->string, buf.buf);
+               if (create_symref(buf.buf, buf2.buf, buf3.buf))
+                       die("creating '%s' failed", buf.buf);
+       }
+       return 0;
+}
+
 static int remove_branches(struct string_list *branches)
 {
        int i, result = 0;
@@ -584,7 +774,7 @@ static int get_one_remote_for_update(struct remote *remote, void *priv)
 {
        struct string_list *list = priv;
        if (!remote->skip_default_update)
-               string_list_append(xstrdup(remote->name), list);
+               string_list_append(remote->name, list);
        return 0;
 }
 
@@ -695,6 +885,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], "rename"))
+               result = mv(argc, argv);
        else if (!strcmp(argv[0], "rm"))
                result = rm(argc, argv);
        else if (!strcmp(argv[0], "show"))