commit 64c6d99023a672f220d1de2662dfe52300f51ea2 from: Stefan Sperling date: Thu Jul 11 14:37:36 2019 UTC prevent rebase in a work tree with an incompatible path prefix commit - 69844fbab193c41fa361eefe874f37ed2f7d26a3 commit + 64c6d99023a672f220d1de2662dfe52300f51ea2 blob - 1780abc217ffcc573874381bf38a884d642ed56e blob + f5918cd2aab9138de84e3108140aac04a83037df --- got/got.1 +++ got/got.1 @@ -581,6 +581,10 @@ to a single base commit with .Cm got update . If the work tree contains local changes, these changes must be committed or reverted first. +If the +.Ar branch +contains changes to files outside of the work tree's path prefix, +the work tree cannot be used to rebase this branch. .Pp The .Cm got update blob - 178e89a6d7544bc1ba03c425048f3307dd462312 blob + 26e1d49401834d67cbff109f6d3e09cd46be8449 --- got/got.c +++ got/got.c @@ -3309,6 +3309,73 @@ rebase_commit(struct got_worktree *worktree, struct go done: got_object_commit_close(commit); return error; +} + +struct check_path_prefix_arg { + const char *path_prefix; + size_t len; +}; + +static const struct got_error * +check_path_prefix(void *arg, struct got_blob_object *blob1, + struct got_blob_object *blob2, struct got_object_id *id1, + struct got_object_id *id2, const char *path1, const char *path2, + struct got_repository *repo) +{ + struct check_path_prefix_arg *a = arg; + + if ((path1 && !got_path_is_child(path1, a->path_prefix, a->len)) || + (path2 && !got_path_is_child(path2, a->path_prefix, a->len))) + return got_error(GOT_ERR_REBASE_PATH); + + return NULL; +} + +static const struct got_error * +rebase_check_path_prefix(struct got_object_id *parent_id, + struct got_object_id *commit_id, const char *path_prefix, + struct got_repository *repo) +{ + const struct got_error *err; + struct got_tree_object *tree1 = NULL, *tree2 = NULL; + struct got_commit_object *commit = NULL, *parent_commit = NULL; + struct check_path_prefix_arg cpp_arg; + + if (got_path_is_root_dir(path_prefix)) + return NULL; + + err = got_object_open_as_commit(&commit, repo, commit_id); + if (err) + goto done; + + err = got_object_open_as_commit(&parent_commit, repo, parent_id); + if (err) + goto done; + + err = got_object_open_as_tree(&tree1, repo, + got_object_commit_get_tree_id(parent_commit)); + if (err) + goto done; + + err = got_object_open_as_tree(&tree2, repo, + got_object_commit_get_tree_id(commit)); + if (err) + goto done; + + cpp_arg.path_prefix = path_prefix; + cpp_arg.len = strlen(path_prefix); + err = got_diff_tree(tree1, tree2, "", "", repo, check_path_prefix, + &cpp_arg); +done: + if (tree1) + got_object_tree_close(tree1); + if (tree2) + got_object_tree_close(tree2); + if (commit) + got_object_commit_close(commit); + if (parent_commit) + got_object_commit_close(parent_commit); + return err; } static const struct got_error * @@ -3487,6 +3554,11 @@ cmd_rebase(int argc, char *argv[]) if (error) goto done; } else { + error = rebase_check_path_prefix(parent_id, commit_id, + got_worktree_get_path_prefix(worktree), repo); + if (error) + goto done; + error = got_object_qid_alloc(&qid, commit_id); if (error) goto done; blob - 4a4ef0bdd9c32b7b02ae4cccbc6f721e54dfcc43 blob + 48ede3e31fd5acad44260af32cff4957fb97fdad --- include/got_error.h +++ include/got_error.h @@ -102,6 +102,7 @@ #define GOT_ERR_EMPTY_REBASE 86 #define GOT_ERR_REBASE_COMMITID 87 #define GOT_ERR_REBASING 88 +#define GOT_ERR_REBASE_PATH 89 static const struct got_error { int code; @@ -201,6 +202,8 @@ static const struct got_error { { GOT_ERR_REBASE_COMMITID,"rebase commit ID mismatch" }, { GOT_ERR_REBASING, "a rebase operation is in progress in this " "work tree and must be continued or aborted first" }, + { GOT_ERR_REBASE_PATH, "cannot rebase branch which contains " + "changes outside of this work tree's path prefix" }, }; /* blob - 91b9ba8be58e42753c9f676acbdaa9514e14bddb blob + 7ea64d7ebe0847c750649789dc27c2f0d8a27428 --- regress/cmdline/rebase.sh +++ regress/cmdline/rebase.sh @@ -585,7 +585,52 @@ function test_rebase_in_progress { return 1 fi done + + test_done "$testroot" "$ret" +} + +function test_rebase_path_prefix { + local testroot=`test_init rebase_path_prefix` + + (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" + + 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_commit=`git_show_head $testroot/repo` + + got checkout -p epsilon $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: cannot rebase branch which contains changes outside " \ + > $testroot/stderr.expected + echo "of this work tree's path prefix" >> $testroot/stderr.expected + cmp -s $testroot/stderr.expected $testroot/stderr + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stderr.expected $testroot/stderr + fi test_done "$testroot" "$ret" } @@ -595,3 +640,4 @@ run_test test_rebase_continue run_test test_rebase_abort run_test test_rebase_no_op_change run_test test_rebase_in_progress +run_test test_rebase_path_prefix