@matheusbsbs / matheus-bagliano / commits / bf98421a33
Fork 0

Implement git remote rename

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: renames the normal refs and rewrites the symrefs to point to the new refs. Signed-off-by: Miklos Vajna <vmiklos@frugalware.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Miklos Vajna committed Nov 3, 2008 at 19:26 UTC bf98421a3363696f3b4c8aedc242bfaadc1ce6f6
3 files changed +174
Documentation/git-remote.txt
+6
@@ -11,6 +11,7 @@ SYNOPSIS
11 [verse]
12 'git remote' [-v | --verbose]
13 'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>
14 +'git remote rename' <old> <new>
15 'git remote rm' <name>
16 'git remote show' [-n] <name>
17 'git remote prune' [-n | --dry-run] <name>
@@ -61,6 +62,11 @@ only makes sense in bare repositories. If a remote uses mirror
62 mode, furthermore, `git push` will always behave as if `\--mirror`
63 was passed.
64
65 +'rename'::
66 +
67 +Rename the remote named <old> to <new>. All remote tracking branches and
68 +configuration settings for the remote are updated.
69 +
70 'rm'::
71
72 Remove the remote named <name>. All remote tracking branches and
builtin-remote.c
+153
@@ -10,6 +10,7 @@
10 static const char * const builtin_remote_usage[] = {
11 "git remote",
12 "git remote add <name> <url>",
13 + "git remote rename <old> <new>",
14 "git remote rm <name>",
15 "git remote show <name>",
16 "git remote prune <name>",
@@ -329,6 +330,156 @@ static int add_branch_for_removal(const char *refname,
330 return 0;
331 }
332
333 +struct rename_info {
334 + const char *old;
335 + const char *new;
336 + struct string_list *remote_branches;
337 +};
338 +
339 +static int read_remote_branches(const char *refname,
340 + const unsigned char *sha1, int flags, void *cb_data)
341 +{
342 + struct rename_info *rename = cb_data;
343 + struct strbuf buf = STRBUF_INIT;
344 + struct string_list_item *item;
345 + int flag;
346 + unsigned char orig_sha1[20];
347 + const char *symref;
348 +
349 + strbuf_addf(&buf, "refs/remotes/%s", rename->old);
350 + if(!prefixcmp(refname, buf.buf)) {
351 + item = string_list_append(xstrdup(refname), rename->remote_branches);
352 + symref = resolve_ref(refname, orig_sha1, 1, &flag);
353 + if (flag & REF_ISSYMREF)
354 + item->util = xstrdup(symref);
355 + else
356 + item->util = NULL;
357 + }
358 +
359 + return 0;
360 +}
361 +
362 +static int mv(int argc, const char **argv)
363 +{
364 + struct option options[] = {
365 + OPT_END()
366 + };
367 + struct remote *oldremote, *newremote;
368 + struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT;
369 + struct string_list remote_branches = { NULL, 0, 0, 0 };
370 + struct rename_info rename;
371 + int i;
372 +
373 + if (argc != 3)
374 + usage_with_options(builtin_remote_usage, options);
375 +
376 + rename.old = argv[1];
377 + rename.new = argv[2];
378 + rename.remote_branches = &remote_branches;
379 +
380 + oldremote = remote_get(rename.old);
381 + if (!oldremote)
382 + die("No such remote: %s", rename.old);
383 +
384 + newremote = remote_get(rename.new);
385 + if (newremote && (newremote->url_nr > 1 || newremote->fetch_refspec_nr))
386 + die("remote %s already exists.", rename.new);
387 +
388 + strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new);
389 + if (!valid_fetch_refspec(buf.buf))
390 + die("'%s' is not a valid remote name", rename.new);
391 +
392 + strbuf_reset(&buf);
393 + strbuf_addf(&buf, "remote.%s", rename.old);
394 + strbuf_addf(&buf2, "remote.%s", rename.new);
395 + if (git_config_rename_section(buf.buf, buf2.buf) < 1)
396 + return error("Could not rename config section '%s' to '%s'",
397 + buf.buf, buf2.buf);
398 +
399 + strbuf_reset(&buf);
400 + strbuf_addf(&buf, "remote.%s.fetch", rename.new);
401 + if (git_config_set_multivar(buf.buf, NULL, NULL, 1))
402 + return error("Could not remove config section '%s'", buf.buf);
403 + for (i = 0; i < oldremote->fetch_refspec_nr; i++) {
404 + char *ptr;
405 +
406 + strbuf_reset(&buf2);
407 + strbuf_addstr(&buf2, oldremote->fetch_refspec[i]);
408 + ptr = strstr(buf2.buf, rename.old);
409 + if (ptr)
410 + strbuf_splice(&buf2, ptr-buf2.buf, strlen(rename.old),
411 + rename.new, strlen(rename.new));
412 + if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
413 + return error("Could not append '%s'", buf.buf);
414 + }
415 +
416 + read_branches();
417 + for (i = 0; i < branch_list.nr; i++) {
418 + struct string_list_item *item = branch_list.items + i;
419 + struct branch_info *info = item->util;
420 + if (info->remote && !strcmp(info->remote, rename.old)) {
421 + strbuf_reset(&buf);
422 + strbuf_addf(&buf, "branch.%s.remote", item->string);
423 + if (git_config_set(buf.buf, rename.new)) {
424 + return error("Could not set '%s'", buf.buf);
425 + }
426 + }
427 + }
428 +
429 + /*
430 + * First remove symrefs, then rename the rest, finally create
431 + * the new symrefs.
432 + */
433 + for_each_ref(read_remote_branches, &rename);
434 + for (i = 0; i < remote_branches.nr; i++) {
435 + struct string_list_item *item = remote_branches.items + i;
436 + int flag = 0;
437 + unsigned char sha1[20];
438 + const char *symref;
439 +
440 + symref = resolve_ref(item->string, sha1, 1, &flag);
441 + if (!(flag & REF_ISSYMREF))
442 + continue;
443 + if (delete_ref(item->string, NULL, REF_NODEREF))
444 + die("deleting '%s' failed", item->string);
445 + }
446 + for (i = 0; i < remote_branches.nr; i++) {
447 + struct string_list_item *item = remote_branches.items + i;
448 +
449 + if (item->util)
450 + continue;
451 + strbuf_reset(&buf);
452 + strbuf_addstr(&buf, item->string);
453 + strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
454 + rename.new, strlen(rename.new));
455 + strbuf_reset(&buf2);
456 + strbuf_addf(&buf2, "remote: renamed %s to %s",
457 + item->string, buf.buf);
458 + if (rename_ref(item->string, buf.buf, buf2.buf))
459 + die("renaming '%s' failed", item->string);
460 + }
461 + for (i = 0; i < remote_branches.nr; i++) {
462 + struct string_list_item *item = remote_branches.items + i;
463 +
464 + if (!item->util)
465 + continue;
466 + strbuf_reset(&buf);
467 + strbuf_addstr(&buf, item->string);
468 + strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
469 + rename.new, strlen(rename.new));
470 + strbuf_reset(&buf2);
471 + strbuf_addstr(&buf2, item->util);
472 + strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old),
473 + rename.new, strlen(rename.new));
474 + strbuf_reset(&buf3);
475 + strbuf_addf(&buf3, "remote: renamed %s to %s",
476 + item->string, buf.buf);
477 + if (create_symref(buf.buf, buf2.buf, buf3.buf))
478 + die("creating '%s' failed", buf.buf);
479 + }
480 + return 0;
481 +}
482 +
483 static int remove_branches(struct string_list *branches)
484 {
485 int i, result = 0;
@@ -695,6 +846,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix)
846 result = show_all();
847 else if (!strcmp(argv[0], "add"))
848 result = add(argc, argv);
849 + else if (!strcmp(argv[0], "rename"))
850 + result = mv(argc, argv);
851 else if (!strcmp(argv[0], "rm"))
852 result = rm(argc, argv);
853 else if (!strcmp(argv[0], "show"))
t/t5505-remote.sh
+15
@@ -328,4 +328,19 @@ test_expect_success 'reject adding remote with an invalid name' '
328
329 '
330
331 +# The first three test if the tracking branches are properly renamed,
332 +# the last two ones check if the config is updated.
333 +
334 +test_expect_success 'rename a remote' '
335 +
336 + git clone one four &&
337 + (cd four &&
338 + git remote rename origin upstream &&
339 + rmdir .git/refs/remotes/origin &&
340 + test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/master" &&
341 + test "$(git rev-parse upstream/master)" = "$(git rev-parse master)" &&
342 + test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" &&
343 + test "$(git config branch.master.remote)" = "upstream")
344 +
345 +'
346 test_done