Merge branch 'nd/worktree-kill-parse-ref' into maint
authorJunio C Hamano <gitster@pobox.com>
Mon, 23 Oct 2017 05:14:16 +0000 (14:14 +0900)
committerJunio C Hamano <gitster@pobox.com>
Mon, 23 Oct 2017 05:14:16 +0000 (14:14 +0900)
"git branch -M a b" while on a branch that is completely unrelated
to either branch a or branch b misbehaved when multiple worktree
was in use. This has been fixed.

* nd/worktree-kill-parse-ref:
branch: fix branch renaming not updating HEADs correctly

1  2 
branch.c
t/t3200-branch.sh
worktree.c
diff --combined branch.c
index 36541d05cd7b934bceba1af4fd4c121e51cd862b,dc5c8aabde7ad7d7c643a02774f8c257184ff3e4..86360d36b3edca403255b855b6edc8bdaf700de8
+++ b/branch.c
@@@ -1,6 -1,5 +1,6 @@@
  #include "git-compat-util.h"
  #include "cache.h"
 +#include "config.h"
  #include "branch.h"
  #include "refs.h"
  #include "remote.h"
@@@ -25,7 -24,8 +25,7 @@@ static int find_tracked_branch(struct r
                } else {
                        free(tracking->spec.src);
                        if (tracking->src) {
 -                              free(tracking->src);
 -                              tracking->src = NULL;
 +                              FREE_AND_NULL(tracking->src);
                        }
                }
                tracking->spec.src = NULL;
@@@ -191,9 -191,9 +191,9 @@@ int validate_new_branchname(const char 
  
        if (!attr_only) {
                const char *head;
 -              unsigned char sha1[20];
 +              struct object_id oid;
  
 -              head = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
 +              head = resolve_ref_unsafe("HEAD", 0, oid.hash, NULL);
                if (!is_bare_repository() && head && !strcmp(head, ref->buf))
                        die(_("Cannot force update the current branch."));
        }
@@@ -233,8 -233,8 +233,8 @@@ void create_branch(const char *name, co
                   int quiet, enum branch_track track)
  {
        struct commit *commit;
 -      unsigned char sha1[20];
 -      char *real_ref, msg[PATH_MAX + 20];
 +      struct object_id oid;
 +      char *real_ref;
        struct strbuf ref = STRBUF_INIT;
        int forcing = 0;
        int dont_change_ref = 0;
        }
  
        real_ref = NULL;
 -      if (get_sha1(start_name, sha1)) {
 +      if (get_oid(start_name, &oid)) {
                if (explicit_tracking) {
                        if (advice_set_upstream_failure) {
                                error(_(upstream_missing), start_name);
                die(_("Not a valid object name: '%s'."), start_name);
        }
  
 -      switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
 +      switch (dwim_ref(start_name, strlen(start_name), oid.hash, &real_ref)) {
        case 0:
                /* Not branching from any existing branch */
                if (explicit_tracking)
                break;
        }
  
 -      if ((commit = lookup_commit_reference(sha1)) == NULL)
 +      if ((commit = lookup_commit_reference(&oid)) == NULL)
                die(_("Not a valid branch point: '%s'."), start_name);
 -      hashcpy(sha1, commit->object.oid.hash);
 -
 -      if (forcing)
 -              snprintf(msg, sizeof msg, "branch: Reset to %s",
 -                       start_name);
 -      else if (!dont_change_ref)
 -              snprintf(msg, sizeof msg, "branch: Created from %s",
 -                       start_name);
 +      oidcpy(&oid, &commit->object.oid);
  
        if (reflog)
                log_all_ref_updates = LOG_REFS_NORMAL;
        if (!dont_change_ref) {
                struct ref_transaction *transaction;
                struct strbuf err = STRBUF_INIT;
 +              char *msg;
 +
 +              if (forcing)
 +                      msg = xstrfmt("branch: Reset to %s", start_name);
 +              else
 +                      msg = xstrfmt("branch: Created from %s", start_name);
  
                transaction = ref_transaction_begin(&err);
                if (!transaction ||
                    ref_transaction_update(transaction, ref.buf,
 -                                         sha1, forcing ? NULL : null_sha1,
 +                                         oid.hash, forcing ? NULL : null_sha1,
                                           0, msg, &err) ||
                    ref_transaction_commit(transaction, &err))
                        die("%s", err.buf);
                ref_transaction_free(transaction);
                strbuf_release(&err);
 +              free(msg);
        }
  
        if (real_ref && track)
@@@ -357,8 -357,9 +357,9 @@@ int replace_each_worktree_head_symref(c
  
                if (worktrees[i]->is_detached)
                        continue;
-               if (worktrees[i]->head_ref &&
-                   strcmp(oldref, worktrees[i]->head_ref))
+               if (!worktrees[i]->head_ref)
+                       continue;
+               if (strcmp(oldref, worktrees[i]->head_ref))
                        continue;
  
                refs = get_worktree_ref_store(worktrees[i]);
diff --combined t/t3200-branch.sh
index 9d707d2a40a457abcd85fb93d9f8195946d09d01,c5c371888bdef6caa8336eb99e935126674b5e76..5fc2fb425b007c3fec736df5cf13782b8e881f58
@@@ -100,23 -100,6 +100,23 @@@ test_expect_success 'git branch -m n/n 
        git reflog exists refs/heads/n
  '
  
 +# The topmost entry in reflog for branch bbb is about branch creation.
 +# Hence, we compare bbb@{1} (instead of bbb@{0}) with aaa@{0}.
 +
 +test_expect_success 'git branch -m bbb should rename checked out branch' '
 +      test_when_finished git branch -D bbb &&
 +      test_when_finished git checkout master &&
 +      git checkout -b aaa &&
 +      git commit --allow-empty -m "a new commit" &&
 +      git rev-parse aaa@{0} >expect &&
 +      git branch -m bbb &&
 +      git rev-parse bbb@{1} >actual &&
 +      test_cmp expect actual &&
 +      git symbolic-ref HEAD >actual &&
 +      echo refs/heads/bbb >expect &&
 +      test_cmp expect actual
 +'
 +
  test_expect_success 'git branch -m o/o o should fail when o/p exists' '
        git branch o/o &&
        git branch o/p &&
@@@ -162,16 -145,19 +162,29 @@@ test_expect_success 'git branch -M baz 
        grep "^0\{40\}.*$msg$" .git/logs/HEAD
  '
  
+ test_expect_success 'git branch -M should leave orphaned HEAD alone' '
+       git init orphan &&
+       (
+               cd orphan &&
+               test_commit initial &&
+               git checkout --orphan lonely &&
+               grep lonely .git/HEAD &&
+               test_path_is_missing .git/refs/head/lonely &&
+               git branch -M master mistress &&
+               grep lonely .git/HEAD
+       )
+ '
 +test_expect_success 'resulting reflog can be shown by log -g' '
 +      oid=$(git rev-parse HEAD) &&
 +      cat >expect <<-EOF &&
 +      HEAD@{0} $oid $msg
 +      HEAD@{2} $oid checkout: moving from foo to baz
 +      EOF
 +      git log -g --format="%gd %H %gs" -2 HEAD >actual &&
 +      test_cmp expect actual
 +'
 +
  test_expect_success 'git branch -M baz bam should succeed when baz is checked out as linked working tree' '
        git checkout master &&
        git worktree add -b baz bazdir &&
@@@ -365,7 -351,7 +378,7 @@@ test_expect_success 'git branch -m s/s 
  
  test_expect_success 'config information was renamed, too' '
        test $(git config branch.s.dummy) = Hello &&
 -      test_must_fail git config branch.s/s/dummy
 +      test_must_fail git config branch.s/s.dummy
  '
  
  test_expect_success 'deleting a symref' '
@@@ -1005,10 -991,6 +1018,10 @@@ test_expect_success '--merged catches i
        test_must_fail git branch --merged 0000000000000000000000000000000000000000
  '
  
 +test_expect_success '--merged is incompatible with --no-merged' '
 +      test_must_fail git branch --merged HEAD --no-merged HEAD
 +'
 +
  test_expect_success 'tracking with unexpected .fetch refspec' '
        rm -rf a b c d &&
        git init a &&
diff --combined worktree.c
index e28ffbeb096a85d67b2e885c48ab096340b5d295,7bc36f4343283704ca1c7918419b6be6a0bd6750..c0c5a2b3735c89fc98d7aa29863eb4a28651d808
@@@ -1,5 -1,4 +1,5 @@@
  #include "cache.h"
 +#include "repository.h"
  #include "refs.h"
  #include "strbuf.h"
  #include "worktree.h"
@@@ -30,7 -29,7 +30,7 @@@ static void add_head_info(struct worktr
  
        target = refs_resolve_ref_unsafe(get_worktree_ref_store(wt),
                                         "HEAD",
-                                        RESOLVE_REF_READING,
+                                        0,
                                         wt->head_sha1, &flags);
        if (!target)
                return;
@@@ -77,7 -76,7 +77,7 @@@ static struct worktree *get_linked_work
        if (!id)
                die("Missing linked worktree name");
  
 -      strbuf_git_common_path(&path, "worktrees/%s/gitdir", id);
 +      strbuf_git_common_path(&path, the_repository, "worktrees/%s/gitdir", id);
        if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
                /* invalid gitdir file */
                goto done;
@@@ -210,19 -209,16 +210,19 @@@ struct worktree *find_worktree(struct w
  {
        struct worktree *wt;
        char *path;
 +      char *to_free = NULL;
  
        if ((wt = find_worktree_by_suffix(list, arg)))
                return wt;
  
 -      arg = prefix_filename(prefix, strlen(prefix), arg);
 +      if (prefix)
 +              arg = to_free = prefix_filename(prefix, arg);
        path = real_pathdup(arg, 1);
        for (; *list; list++)
                if (!fspathcmp(path, real_path((*list)->path)))
                        break;
        free(path);
 +      free(to_free);
        return *list;
  }
  
@@@ -352,7 -348,6 +352,7 @@@ int submodule_uses_worktrees(const cha
  
        /* The env would be set for the superproject. */
        get_common_dir_noenv(&sb, submodule_gitdir);
 +      free(submodule_gitdir);
  
        /*
         * The check below is only known to be good for repository format
        /* See if there is any file inside the worktrees directory. */
        dir = opendir(sb.buf);
        strbuf_release(&sb);
 -      free(submodule_gitdir);
  
        if (!dir)
                return 0;