#!/bin/sh # # Copyright (c) 2012 Robert Luberda # test_description='concurrent git svn dcommit' . ./lib-git-svn.sh test_expect_success 'setup svn repository' ' svn_cmd checkout "$svnrepo" work.svn && ( cd work.svn && echo >file && echo > auto_updated_file svn_cmd add file auto_updated_file && svn_cmd commit -m "initial commit" ) && svn_cmd checkout "$svnrepo" work-auto-commits.svn ' N=0 next_N() { N=$(( $N + 1 )) } # Setup SVN repository hooks to emulate SVN failures or concurrent commits # The function adds # either pre-commit hook, which causes SVN commit given in second argument # to fail # or post-commit hook, which creates a new commit (a new line added to # auto_updated_file) after given SVN commit # The first argument contains a type of the hook # The second argument contains a number (not SVN revision) of commit # the hook should be applied for (each time the hook is run, the given # number is decreased by one until it gets 0, in which case the hook # will execute its real action) setup_hook() { hook_type="$1" # "pre-commit" or "post-commit" skip_revs="$2" [ "$hook_type" = "pre-commit" ] || [ "$hook_type" = "post-commit" ] || { echo "ERROR: invalid argument ($hook_type)" \ "passed to setup_hook" >&2 ; return 1; } echo "cnt=$skip_revs" > "$hook_type-counter" rm -f "$rawsvnrepo/hooks/"*-commit # drop previous hooks hook="$rawsvnrepo/hooks/$hook_type" cat > "$hook" <<- 'EOF1' #!/bin/sh set -e cd "$1/.." # "$1" is repository location exec >> svn-hook.log 2>&1 hook="$(basename "$0")" echo "*** Executing $hook $@" set -x . ./$hook-counter cnt="$(($cnt - 1))" echo "cnt=$cnt" > ./$hook-counter [ "$cnt" = "0" ] || exit 0 EOF1 if [ "$hook_type" = "pre-commit" ]; then echo "echo 'commit disallowed' >&2; exit 1" >> "$hook" else echo "PATH=\"$PATH\"; export PATH" >> $hook echo "svnconf=\"$svnconf\"" >> $hook cat >> "$hook" <<- 'EOF2' cd work-auto-commits.svn svn up --config-dir "$svnconf" echo "$$" >> auto_updated_file svn commit --config-dir "$svnconf" \ -m "auto-committing concurrent change" exit 0 EOF2 fi chmod 755 "$hook" } check_contents() { gitdir="$1" (cd ../work.svn && svn_cmd up) && test_cmp file ../work.svn/file && test_cmp auto_updated_file ../work.svn/auto_updated_file } test_expect_success 'check if post-commit hook creates a concurrent commit' ' setup_hook post-commit 1 && ( cd work.svn && cp auto_updated_file au_file_saved && echo 1 >> file && svn_cmd commit -m "changing file" && svn_cmd up && test_must_fail test_cmp auto_updated_file au_file_saved ) ' test_expect_success 'check if pre-commit hook fails' ' setup_hook pre-commit 2 && ( cd work.svn && echo 2 >> file && svn_cmd commit -m "changing file once again" && echo 3 >> file && test_must_fail svn_cmd commit -m "this commit should fail" && svn_cmd revert file ) ' test_expect_success 'dcommit error handling' ' setup_hook pre-commit 2 && next_N && git svn clone "$svnrepo" work$N.git && ( cd work$N.git && echo 1 >> file && git commit -am "commit change $N.1" && echo 2 >> file && git commit -am "commit change $N.2" && echo 3 >> file && git commit -am "commit change $N.3" && # should fail to dcommit 2nd and 3rd change # but still should leave the repository in reasonable state test_must_fail git svn dcommit && git update-index --refresh && git show HEAD~2 | grep -q git-svn-id && ! git show HEAD~1 | grep -q git-svn-id && ! git show HEAD | grep -q git-svn-id ) ' test_expect_success 'dcommit concurrent change in non-changed file' ' setup_hook post-commit 2 && next_N && git svn clone "$svnrepo" work$N.git && ( cd work$N.git && echo 1 >> file && git commit -am "commit change $N.1" && echo 2 >> file && git commit -am "commit change $N.2" && echo 3 >> file && git commit -am "commit change $N.3" && # should rebase and leave the repository in reasonable state git svn dcommit && git update-index --refresh && check_contents && git show HEAD~3 | grep -q git-svn-id && git show HEAD~2 | grep -q git-svn-id && git show HEAD~1 | grep -q auto-committing && git show HEAD | grep -q git-svn-id ) ' # An utility function used in the following test delete_first_line() { file="$1" && sed 1d < "$file" > "${file}.tmp" && rm "$file" && mv "${file}.tmp" "$file" } test_expect_success 'dcommit concurrent non-conflicting change' ' setup_hook post-commit 2 && next_N && git svn clone "$svnrepo" work$N.git && ( cd work$N.git && cat file >> auto_updated_file && git commit -am "commit change $N.1" && delete_first_line auto_updated_file && git commit -am "commit change $N.2" && delete_first_line auto_updated_file && git commit -am "commit change $N.3" && # should rebase and leave the repository in reasonable state git svn dcommit && git update-index --refresh && check_contents && git show HEAD~3 | grep -q git-svn-id && git show HEAD~2 | grep -q git-svn-id && git show HEAD~1 | grep -q auto-committing && git show HEAD | grep -q git-svn-id ) ' test_expect_success 'dcommit --no-rebase concurrent non-conflicting change' ' setup_hook post-commit 2 && next_N && git svn clone "$svnrepo" work$N.git && ( cd work$N.git && cat file >> auto_updated_file && git commit -am "commit change $N.1" && delete_first_line auto_updated_file && git commit -am "commit change $N.2" && delete_first_line auto_updated_file && git commit -am "commit change $N.3" && # should fail as rebase is needed test_must_fail git svn dcommit --no-rebase && # but should leave HEAD unchanged git update-index --refresh && ! git show HEAD~2 | grep -q git-svn-id && ! git show HEAD~1 | grep -q git-svn-id && ! git show HEAD | grep -q git-svn-id ) ' test_expect_success 'dcommit fails on concurrent conflicting change' ' setup_hook post-commit 1 && next_N && git svn clone "$svnrepo" work$N.git && ( cd work$N.git && echo a >> file && git commit -am "commit change $N.1" && echo b >> auto_updated_file && git commit -am "commit change $N.2" && echo c >> auto_updated_file && git commit -am "commit change $N.3" && test_must_fail git svn dcommit && # rebase should fail test_must_fail git update-index --refresh ) ' test_done