mirror of
https://github.com/git/git.git
synced 2024-12-12 11:24:02 +08:00
e48fb750f5
dcommit didn't handle errors returned by SVN and coped very poorly with concurrent commits that appear in SVN repository while dcommit was running. In both cases it left git repository in inconsistent state: index (which was reset with `git reset --mixed' after a successful commit to SVN) no longer matched the checkouted tree, when the following commit failed or needed to be rebased. See http://bugs.debian.org/676904 for examples. This patch fixes the issues by: - introducing error handler for dcommit. The handler will try to rebase or reset working tree before returning error to the end user. dcommit_rebase function was extracted out of cmd_dcommit to ensure consistency between cmd_dcommit and the error handler. - calling `git reset --mixed' only once after all patches are successfully committed to SVN. This ensures index is not touched for most of the time of dcommit run. Signed-off-by: Eric Wong <normalperson@yhbt.net>
217 lines
6.2 KiB
Bash
Executable File
217 lines
6.2 KiB
Bash
Executable File
#!/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
|