prevent rebase with an out-of-date work tree
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
diff --git a/include/got_error.h b/include/got_error.h
index d0df125..577bc08 100644
--- a/include/got_error.h
+++ b/include/got_error.h
@@ -128,6 +128,7 @@
#define GOT_ERR_REGEX 112
#define GOT_ERR_REF_NAME_MINUS 113
#define GOT_ERR_GITCONFIG_SYNTAX 114
+#define GOT_ERR_REBASE_OUT_OF_DATE 115
static const struct got_error {
int code;
@@ -262,6 +263,8 @@ static const struct got_error {
{ GOT_ERR_REGEX, "regular expression error" },
{ GOT_ERR_REF_NAME_MINUS, "reference name may not start with '-'" },
{ GOT_ERR_GITCONFIG_SYNTAX, "gitconfig syntax error" },
+ { GOT_ERR_REBASE_OUT_OF_DATE, "work tree must be updated before it "
+ "can be used to rebase a branch" },
};
/*
diff --git a/lib/worktree.c b/lib/worktree.c
index ba58428..3f75e03 100644
--- a/lib/worktree.c
+++ b/lib/worktree.c
@@ -4679,6 +4679,7 @@ got_worktree_rebase_prepare(struct got_reference **new_base_branch_ref,
char *fileindex_path = NULL;
struct check_rebase_ok_arg ok_arg;
struct got_reference *wt_branch = NULL, *branch_ref = NULL;
+ struct got_object_id *wt_branch_tip = NULL;
*new_base_branch_ref = NULL;
*tmp_branch = NULL;
@@ -4716,6 +4717,14 @@ got_worktree_rebase_prepare(struct got_reference **new_base_branch_ref,
if (err)
goto done;
+ err = got_ref_resolve(&wt_branch_tip, repo, wt_branch);
+ if (err)
+ goto done;
+ if (got_object_id_cmp(worktree->base_commit_id, wt_branch_tip) != 0) {
+ err = got_error(GOT_ERR_REBASE_OUT_OF_DATE);
+ goto done;
+ }
+
err = got_ref_alloc_symref(new_base_branch_ref,
new_base_branch_ref_name, wt_branch);
if (err)
@@ -4754,6 +4763,7 @@ done:
got_ref_close(branch_ref);
if (wt_branch)
got_ref_close(wt_branch);
+ free(wt_branch_tip);
if (err) {
if (*new_base_branch_ref) {
got_ref_close(*new_base_branch_ref);
diff --git a/regress/cmdline/rebase.sh b/regress/cmdline/rebase.sh
index e504cd7..57da5df 100755
--- a/regress/cmdline/rebase.sh
+++ b/regress/cmdline/rebase.sh
@@ -870,6 +870,76 @@ function test_rebase_forward {
test_done "$testroot" "$ret"
}
+function test_rebase_out_of_date {
+ local testroot=`test_init rebase_out_of_date`
+ local initial_commit=`git_show_head $testroot/repo`
+
+ (cd $testroot/repo && git checkout -q -b newbranch)
+ echo "modified delta on branch" > $testroot/repo/gamma/delta
+ git_commit $testroot/repo -m "committing to delta on newbranch"
+
+ echo "modified alpha on branch" > $testroot/repo/alpha
+ (cd $testroot/repo && git rm -q beta)
+ echo "new file on branch" > $testroot/repo/epsilon/new
+ (cd $testroot/repo && git add epsilon/new)
+ git_commit $testroot/repo -m "committing more changes on newbranch"
+
+ local orig_commit1=`git_show_parent_commit $testroot/repo`
+ local orig_commit2=`git_show_head $testroot/repo`
+
+ (cd $testroot/repo && git checkout -q master)
+ echo "modified zeta on master" > $testroot/repo/epsilon/zeta
+ git_commit $testroot/repo -m "committing to zeta on master"
+ local master_commit1=`git_show_head $testroot/repo`
+
+ (cd $testroot/repo && git checkout -q master)
+ echo "modified beta on master" > $testroot/repo/beta
+ git_commit $testroot/repo -m "committing to beta on master"
+ local master_commit2=`git_show_head $testroot/repo`
+
+ got checkout -c $master_commit1 $testroot/repo $testroot/wt \
+ > /dev/null
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ (cd $testroot/wt && got rebase newbranch > $testroot/stdout \
+ 2> $testroot/stderr)
+
+ echo -n > $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ echo -n "got: work tree must be updated before it can be " \
+ > $testroot/stderr.expected
+ echo "used to rebase a branch" >> $testroot/stderr.expected
+ cmp -s $testroot/stderr.expected $testroot/stderr
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stderr.expected $testroot/stderr
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ (cd $testroot/wt && got log -l3 | grep ^commit > $testroot/stdout)
+ echo "commit $master_commit2 (master)" > $testroot/stdout.expected
+ echo "commit $master_commit1" >> $testroot/stdout.expected
+ echo "commit $initial_commit" >> $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ fi
+ test_done "$testroot" "$ret"
+}
+
run_test test_rebase_basic
run_test test_rebase_ancestry_check
run_test test_rebase_continue
@@ -880,3 +950,4 @@ run_test test_rebase_path_prefix
run_test test_rebase_preserves_logmsg
run_test test_rebase_no_commits_to_rebase
run_test test_rebase_forward
+run_test test_rebase_out_of_date