Commit Diff


commit - 7ccf82596168ce763ab082e181aae83836ebdc23
commit + a1fb16d88f6904f90545c864be8d12c6ea9753b0
blob - ebb4bbf8fb93d5fdded2331a037f242e4bb76119
blob + f18db31b991c8c0ff10fc8f0d7545aa15e6ce2d2
--- got/got.1
+++ got/got.1
@@ -469,24 +469,12 @@ This step currently requires
 .Dl $ git checkout unified-buffer-cache
 .Dl $ git rebase master
 .Dl $ git push -f
-.Pp
-Create a new branch reference for a work tree which has lost
-linear ancestry with its recorded branch reference and thus
-can no longer be updated:
-.Pp
-.Dl $ got ref refs/heads/old-branch $(cat .got/base-commit)
-.Dl $ got update -b old-branch
 .Pp
 Update the work tree to the newly rebased
 .Dq unified-buffer-cache
 branch:
 .Pp
 .Dl $ got update -b unified-buffer-cache
-.Pp
-Delete a reference which is no longer needed:
-.Pp
-.Dl $ got ref -d refs/heads/old-branch
-.Pp
 .Sh SEE ALSO
 .Xr git-repository 5
 .Xr got-worktree 5
blob - 4d034c8af70fd54a7de2623002e97255dca49eaf
blob + 16eef0039dc6d2261b9556a71753ea90319564b7
--- got/got.c
+++ got/got.c
@@ -565,6 +565,46 @@ update_progress(void *arg, unsigned char status, const
 }
 
 static const struct got_error *
+switch_head_ref(struct got_reference *head_ref,
+    struct got_object_id *commit_id, struct got_worktree *worktree,
+    struct got_repository *repo)
+{
+	const struct got_error *err = NULL;
+	char *base_id_str;
+	int ref_has_moved = 0;
+
+	/* Trivial case: switching between two different references. */
+	if (strcmp(got_ref_get_name(head_ref),
+	    got_worktree_get_head_ref_name(worktree)) != 0) {
+		printf("Switching work tree from %s to %s\n",
+		    got_worktree_get_head_ref_name(worktree),
+		    got_ref_get_name(head_ref));
+		return got_worktree_set_head_ref(worktree, head_ref);
+	}
+
+	err = check_linear_ancestry(commit_id,
+	    got_worktree_get_base_commit_id(worktree), repo);
+	if (err) {
+		if (err->code != GOT_ERR_ANCESTRY)
+			return err;
+		ref_has_moved = 1;
+	}
+	if (!ref_has_moved)
+		return NULL;
+
+	/* Switching to a rebased branch with the same reference name. */
+	err = got_object_id_str(&base_id_str,
+	    got_worktree_get_base_commit_id(worktree));
+	if (err)
+		return err;
+	printf("Reference %s now points at a different branch\n",
+	    got_worktree_get_head_ref_name(worktree));
+	printf("Switching work tree from %s to %s\n", base_id_str,
+	    got_worktree_get_head_ref_name(worktree));
+	return NULL;
+}
+
+static const struct got_error *
 cmd_update(int argc, char *argv[])
 {
 	const struct got_error *error = NULL;
@@ -632,9 +672,8 @@ cmd_update(int argc, char *argv[])
 	if (error)
 		goto done;
 
-	if (branch_name == NULL)
-		branch_name = got_worktree_get_head_ref_name(worktree);
-	error = got_ref_open(&head_ref, repo, branch_name, 0);
+	error = got_ref_open(&head_ref, repo, branch_name ? branch_name :
+	    got_worktree_get_head_ref_name(worktree), 0);
 	if (error != NULL)
 		goto done;
 	if (commit_id_str == NULL) {
@@ -651,12 +690,11 @@ cmd_update(int argc, char *argv[])
 			goto done;
 	}
 
-	if (strcmp(got_ref_get_name(head_ref),
-	    got_worktree_get_head_ref_name(worktree)) != 0) {
+	if (branch_name) {
 		struct got_object_id *head_commit_id;
 		if (strlen(path) != 0) {
-			fprintf(stderr, "%s: switching to a different "
-			    "branch requires that the entire work tree "
+			fprintf(stderr, "%s: switching between branches "
+			    "requires that the entire work tree "
 			    "gets updated, not just '%s'\n",
 			    getprogname(), path);
 			error = got_error(GOT_ERR_BAD_PATH);
@@ -672,10 +710,7 @@ cmd_update(int argc, char *argv[])
 		error = check_same_branch(commit_id, head_ref, repo);
 		if (error)
 			goto done;
-		printf("Switching work tree from %s to %s\n",
-		    got_worktree_get_head_ref_name(worktree),
-		    got_ref_get_name(head_ref));
-		error = got_worktree_set_head_ref(worktree, head_ref);
+		error = switch_head_ref(head_ref, commit_id, worktree, repo);
 		if (error)
 			goto done;
 	} else {
blob - 21b84dec550e11df0837e1b7f9dc20217e2463d3
blob + b5d808017aca17881058fa3c0276cc3ec7ed3381
--- include/got_error.h
+++ include/got_error.h
@@ -174,8 +174,8 @@ static const struct got_error {
 	{ GOT_ERR_COMMIT_MSG_EMPTY, "commit message cannot be empty" },
 	{ GOT_ERR_DIR_NOT_EMPTY, "directory exists and is not empty" },
 	{ GOT_ERR_COMMIT_NO_CHANGES, "no changes to commit" },
-	{ GOT_ERR_BRANCH_MOVED,	"work tree's branch reference has moved; "
-	    "new branch reference or rebase required" },
+	{ GOT_ERR_BRANCH_MOVED,	"work tree's head reference now points to a "
+	    "different branch; new head reference and/or update -b required" },
 	{ GOT_ERR_OBJ_TOO_LARGE,	"object too large" },
 };
 
blob - 1e92b37d93b14ff994cbfaf3cf87b14e5ff846c5
blob + ab518679ea7cb4197f1beebbdeb2d17ff873976f
--- regress/cmdline/update.sh
+++ regress/cmdline/update.sh
@@ -1291,9 +1291,10 @@ function test_update_moved_branch_ref {
 	(cd $testroot/repo2 && git fetch -q --all)
 
 	echo -n > $testroot/stdout.expected
-	echo -n "got: work tree's branch reference has moved; " \
+	echo -n "got: work tree's head reference now points to a different " \
 		> $testroot/stderr.expected
-	echo "new branch reference or rebase required" >> $testroot/stderr.expected
+	echo "branch; new head reference and/or update -b required"  \
+		>> $testroot/stderr.expected
 
 	(cd $testroot/wt && got update > $testroot/stdout 2> $testroot/stderr)