diff options
Diffstat (limited to 'builtin-remote.c')
-rw-r--r-- | builtin-remote.c | 217 |
1 files changed, 200 insertions, 17 deletions
diff --git a/builtin-remote.c b/builtin-remote.c index 5af4e643eb..14774e36c4 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -8,11 +8,12 @@ #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 show [-n] <name>", + "git remote prune [-n | --dry-run] <name>", "git remote update [group]", NULL }; @@ -54,7 +55,7 @@ static int add(int argc, const char **argv) struct string_list track = { NULL, 0, 0 }; const char *master = NULL; struct remote *remote; - struct strbuf buf, buf2; + struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT; const char *name, *url; int i; @@ -81,9 +82,6 @@ static int add(int argc, const char **argv) remote->fetch_refspec_nr)) die("remote %s already exists.", name); - strbuf_init(&buf, 0); - strbuf_init(&buf2, 0); - strbuf_addf(&buf2, "refs/heads/test:refs/remotes/%s/test", name); if (!valid_fetch_refspec(buf2.buf)) die("'%s' is not a valid remote name", name); @@ -332,6 +330,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; @@ -352,7 +535,7 @@ static int rm(int argc, const char **argv) OPT_END() }; struct remote *remote; - struct strbuf buf; + struct strbuf buf = STRBUF_INIT; struct known_remotes known_remotes = { NULL, NULL }; struct string_list branches = { NULL, 0, 0, 1 }; struct branches_for_remote cb_data = { NULL, &branches, &known_remotes }; @@ -368,7 +551,6 @@ static int rm(int argc, const char **argv) known_remotes.to_delete = remote; for_each_remote(add_known_remote, &known_remotes); - strbuf_init(&buf, 0); strbuf_addf(&buf, "remote.%s", remote->name); if (git_config_rename_section(buf.buf, NULL) < 1) return error("Could not remove config section '%s'", buf.buf); @@ -416,10 +598,9 @@ static void show_list(const char *title, struct string_list *list, return; printf(title, list->nr > 1 ? "es" : "", extra_arg); - printf("\n "); - for (i = 0; i < list->nr; i++) - printf("%s%s", i ? " " : "", list->items[i].string); printf("\n"); + for (i = 0; i < list->nr; i++) + printf(" %s\n", list->items[i].string); } static int get_remote_ref_states(const char *name, @@ -515,17 +696,17 @@ static int show(int argc, const char **argv) show_list(" Tracked remote branch%s", &states.tracked, ""); if (states.remote->push_refspec_nr) { - printf(" Local branch%s pushed with 'git push'\n ", + printf(" Local branch%s pushed with 'git push'\n", states.remote->push_refspec_nr > 1 ? "es" : ""); for (i = 0; i < states.remote->push_refspec_nr; i++) { struct refspec *spec = states.remote->push + i; - printf(" %s%s%s%s", spec->force ? "+" : "", + printf(" %s%s%s%s\n", + spec->force ? "+" : "", abbrev_branch(spec->src), spec->dst ? ":" : "", spec->dst ? abbrev_branch(spec->dst) : ""); } - printf("\n"); } /* NEEDSWORK: free remote */ @@ -589,7 +770,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; } @@ -700,6 +881,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")) |