make moved branch head references easier to deal with
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
diff --git a/got/got.1 b/got/got.1
index ebb4bbf..f18db31 100644
--- a/got/got.1
+++ b/got/got.1
@@ -470,23 +470,11 @@ This step currently requires
.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
diff --git a/got/got.c b/got/got.c
index 4d034c8..16eef00 100644
--- a/got/got.c
+++ b/got/got.c
@@ -565,6 +565,46 @@ update_progress(void *arg, unsigned char status, const char *path)
}
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 {
diff --git a/include/got_error.h b/include/got_error.h
index 21b84de..b5d8080 100644
--- a/include/got_error.h
+++ b/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" },
};
diff --git a/regress/cmdline/update.sh b/regress/cmdline/update.sh
index 1e92b37..ab51867 100755
--- a/regress/cmdline/update.sh
+++ b/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)