branch: record creation of renamed branch in HEAD's log

Renaming the current branch adds an event to the current branch's log and to HEAD's log. However, the logged entries differ. The entry in the branch's log represents the entire renaming operation (the old and new hash are identical), whereas the entry in HEAD's log represents the deletion only (the new sha1 is null). Extend replace_each_worktree_head_symref(), whose only caller is branch_rename(), to take a reflog message argument. This allows the creation of the new ref to be recorded in HEAD's log. As a result, the renaming event is represented by two entries (a deletion and a creation entry) in HEAD's log. It's a bit unfortunate that the branch's log and HEAD's log now represent the renaming event in different ways. Given that the renaming operation is not atomic, the two-entry form is a more accurate representation of the operation and is more useful for debugging purposes if a failure occurs between the deletion and creation events. It would make sense to move the branch's log to the two-entry form, but this would involve changes to how the rename is carried out and to how the update flags and reflogs are processed for deletions, so it may not be worth the effort. Based-on-patch-by: Jeff King <peff@peff.net> Signed-off-by: Kyle Meyer <kyle@kyleam.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Kyle Meyer committed Feb 20, 2017 at 20:10 UTC 39ee4c6c2fc80960094ae1454922c2d10c72f210
6 files changed +15 -10
branch.c
+3 -2
@@ -345,7 +345,8 @@ void die_if_checked_out(const char *branch, int ignore_current_worktree)
345 branch, wt->path);
346 }
347
348 -int replace_each_worktree_head_symref(const char *oldref, const char *newref)
348 +int replace_each_worktree_head_symref(const char *oldref, const char *newref,
349 + const char *logmsg)
350 {
351 int ret = 0;
352 struct worktree **worktrees = get_worktrees(0);
@@ -358,7 +359,7 @@ int replace_each_worktree_head_symref(const char *oldref, const char *newref)
359 continue;
360
361 if (set_worktree_head_symref(get_worktree_git_dir(worktrees[i]),
361 - newref)) {
362 + newref, logmsg)) {
363 ret = -1;
364 error(_("HEAD of working tree %s is not updated"),
365 worktrees[i]->path);
branch.h
+2 -1
@@ -71,6 +71,7 @@ extern void die_if_checked_out(const char *branch, int ignore_current_worktree);
71 * This will be used when renaming a branch. Returns 0 if successful, non-zero
72 * otherwise.
73 */
74 -extern int replace_each_worktree_head_symref(const char *oldref, const char *newref);
74 +extern int replace_each_worktree_head_symref(const char *oldref, const char *newref,
75 + const char *logmsg);
76
77 #endif
builtin/branch.c
+3 -2
@@ -579,14 +579,15 @@ static void rename_branch(const char *oldname, const char *newname, int force)
579
580 if (rename_ref(oldref.buf, newref.buf, logmsg.buf))
581 die(_("Branch rename failed"));
582 - strbuf_release(&logmsg);
582
583 if (recovery)
584 warning(_("Renamed a misnamed branch '%s' away"), oldref.buf + 11);
585
587 - if (replace_each_worktree_head_symref(oldref.buf, newref.buf))
586 + if (replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
587 die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
588
589 + strbuf_release(&logmsg);
590 +
591 strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11);
592 strbuf_release(&oldref);
593 strbuf_addf(&newsection, "branch.%s", newref.buf + 11);
refs.h
+2 -1
@@ -334,7 +334,8 @@ int create_symref(const char *refname, const char *target, const char *logmsg);
334 * $GIT_DIR points to.
335 * Return 0 if successful, non-zero otherwise.
336 * */
337 -int set_worktree_head_symref(const char *gitdir, const char *target);
337 +int set_worktree_head_symref(const char *gitdir, const char *target,
338 + const char *logmsg);
339
340 enum action_on_err {
341 UPDATE_REFS_MSG_ON_ERR,
refs/files-backend.c
+2 -2
@@ -3055,7 +3055,7 @@ static int files_create_symref(struct ref_store *ref_store,
3055 return ret;
3056 }
3057
3058 -int set_worktree_head_symref(const char *gitdir, const char *target)
3058 +int set_worktree_head_symref(const char *gitdir, const char *target, const char *logmsg)
3059 {
3060 static struct lock_file head_lock;
3061 struct ref_lock *lock;
@@ -3083,7 +3083,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target)
3083 lock->lk = &head_lock;
3084 lock->ref_name = xstrdup(head_rel);
3085
3086 - ret = create_symref_locked(lock, head_rel, target, NULL);
3086 + ret = create_symref_locked(lock, head_rel, target, logmsg);
3087
3088 unlock_ref(lock); /* will free lock */
3089 strbuf_release(&head_path);
t/t3200-branch.sh
+3 -2
@@ -139,9 +139,10 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou
139 test $(git rev-parse --abbrev-ref HEAD) = bam
140 '
141
142 -test_expect_success 'git branch -M baz bam should add entry to .git/logs/HEAD' '
142 +test_expect_success 'git branch -M baz bam should add entries to .git/logs/HEAD' '
143 msg="Branch: renamed refs/heads/baz to refs/heads/bam" &&
144 - grep " 0\{40\}.*$msg$" .git/logs/HEAD
144 + grep " 0\{40\}.*$msg$" .git/logs/HEAD &&
145 + grep "^0\{40\}.*$msg$" .git/logs/HEAD
146 '
147
148 test_expect_success 'git branch -M baz bam should succeed when baz is checked out as linked working tree' '