2006-05-17 09:55:40 +00:00
|
|
|
#!/bin/sh
|
|
|
|
#
|
|
|
|
# Copyright (c) 2006 Shawn Pearce
|
|
|
|
#
|
|
|
|
|
2007-07-03 05:52:14 +00:00
|
|
|
test_description='Test git update-ref and basic ref logging'
|
2006-05-17 09:55:40 +00:00
|
|
|
. ./test-lib.sh
|
|
|
|
|
2018-05-13 02:24:13 +00:00
|
|
|
Z=$ZERO_OID
|
2008-01-15 23:50:17 +00:00
|
|
|
|
2017-01-27 10:09:48 +00:00
|
|
|
m=refs/heads/master
|
|
|
|
n_dir=refs/heads/gu
|
|
|
|
n=$n_dir/fixes
|
|
|
|
outside=refs/foo
|
|
|
|
bare=bare-repo
|
2008-01-15 23:50:17 +00:00
|
|
|
|
2017-01-27 10:09:48 +00:00
|
|
|
create_test_commits ()
|
|
|
|
{
|
|
|
|
prfx="$1"
|
2008-01-15 23:50:17 +00:00
|
|
|
for name in A B C D E F
|
|
|
|
do
|
|
|
|
test_tick &&
|
|
|
|
T=$(git write-tree) &&
|
|
|
|
sha1=$(echo $name | git commit-tree $T) &&
|
2017-01-27 10:09:48 +00:00
|
|
|
eval $prfx$name=$sha1
|
2008-01-15 23:50:17 +00:00
|
|
|
done
|
2017-01-27 10:09:48 +00:00
|
|
|
}
|
2008-01-15 23:50:17 +00:00
|
|
|
|
2017-01-27 10:09:48 +00:00
|
|
|
test_expect_success setup '
|
|
|
|
create_test_commits "" &&
|
|
|
|
mkdir $bare &&
|
|
|
|
cd $bare &&
|
|
|
|
git init --bare &&
|
|
|
|
create_test_commits "bare" &&
|
|
|
|
cd -
|
2008-01-15 23:50:17 +00:00
|
|
|
'
|
|
|
|
|
2017-04-16 02:31:02 +00:00
|
|
|
test_expect_success "create $m" '
|
|
|
|
git update-ref $m $A &&
|
2020-05-20 17:36:08 +00:00
|
|
|
test $A = $(git show-ref -s --verify $m)
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
|
|
|
test_expect_success "create $m with oldvalue verification" '
|
|
|
|
git update-ref $m $B $A &&
|
2020-05-20 17:36:08 +00:00
|
|
|
test $B = $(git show-ref -s --verify $m)
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
2008-05-25 16:14:29 +00:00
|
|
|
test_expect_success "fail to delete $m with stale ref" '
|
|
|
|
test_must_fail git update-ref -d $m $A &&
|
2020-05-20 17:36:08 +00:00
|
|
|
test $B = "$(git show-ref -s --verify $m)"
|
2008-05-25 16:14:29 +00:00
|
|
|
'
|
|
|
|
test_expect_success "delete $m" '
|
2020-11-13 08:12:31 +00:00
|
|
|
test_when_finished "git update-ref -d $m" &&
|
2008-05-25 16:14:29 +00:00
|
|
|
git update-ref -d $m $B &&
|
2020-11-13 08:12:31 +00:00
|
|
|
test_must_fail git show-ref --verify -q $m
|
2008-05-25 16:14:29 +00:00
|
|
|
'
|
2006-05-17 09:55:40 +00:00
|
|
|
|
2017-03-21 00:56:16 +00:00
|
|
|
test_expect_success "delete $m without oldvalue verification" '
|
2020-11-13 08:12:31 +00:00
|
|
|
test_when_finished "git update-ref -d $m" &&
|
2008-06-02 23:34:53 +00:00
|
|
|
git update-ref $m $A &&
|
2020-05-20 17:36:08 +00:00
|
|
|
test $A = $(git show-ref -s --verify $m) &&
|
2008-06-02 23:34:53 +00:00
|
|
|
git update-ref -d $m &&
|
2020-11-13 08:12:31 +00:00
|
|
|
test_must_fail git show-ref --verify -q $m
|
2017-03-21 00:56:16 +00:00
|
|
|
'
|
2008-06-02 23:34:53 +00:00
|
|
|
|
2017-03-21 00:56:16 +00:00
|
|
|
test_expect_success "fail to create $n" '
|
|
|
|
test_when_finished "rm -f .git/$n_dir" &&
|
|
|
|
touch .git/$n_dir &&
|
|
|
|
test_must_fail git update-ref $n $A
|
|
|
|
'
|
2006-07-29 03:44:51 +00:00
|
|
|
|
2017-04-16 02:31:02 +00:00
|
|
|
test_expect_success "create $m (by HEAD)" '
|
|
|
|
git update-ref HEAD $A &&
|
2020-05-20 17:36:08 +00:00
|
|
|
test $A = $(git show-ref -s --verify $m)
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
|
|
|
test_expect_success "create $m (by HEAD) with oldvalue verification" '
|
|
|
|
git update-ref HEAD $B $A &&
|
2020-05-20 17:36:08 +00:00
|
|
|
test $B = $(git show-ref -s --verify $m)
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
2008-05-25 16:14:29 +00:00
|
|
|
test_expect_success "fail to delete $m (by HEAD) with stale ref" '
|
|
|
|
test_must_fail git update-ref -d HEAD $A &&
|
2020-05-20 17:36:08 +00:00
|
|
|
test $B = $(git show-ref -s --verify $m)
|
2008-05-25 16:14:29 +00:00
|
|
|
'
|
|
|
|
test_expect_success "delete $m (by HEAD)" '
|
2020-11-13 08:12:31 +00:00
|
|
|
test_when_finished "git update-ref -d $m" &&
|
2008-05-25 16:14:29 +00:00
|
|
|
git update-ref -d HEAD $B &&
|
2020-11-13 08:12:31 +00:00
|
|
|
test_must_fail git show-ref --verify -q $m
|
2008-05-25 16:14:29 +00:00
|
|
|
'
|
2006-05-17 09:55:40 +00:00
|
|
|
|
2017-02-21 01:10:33 +00:00
|
|
|
test_expect_success "deleting current branch adds message to HEAD's log" '
|
2020-11-13 08:12:31 +00:00
|
|
|
test_when_finished "git update-ref -d $m" &&
|
2017-02-21 01:10:33 +00:00
|
|
|
git update-ref $m $A &&
|
|
|
|
git symbolic-ref HEAD $m &&
|
|
|
|
git update-ref -m delete-$m -d $m &&
|
2020-11-13 08:12:31 +00:00
|
|
|
test_must_fail git show-ref --verify -q $m &&
|
2017-02-21 01:10:33 +00:00
|
|
|
grep "delete-$m$" .git/logs/HEAD
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success "deleting by HEAD adds message to HEAD's log" '
|
2020-11-13 08:12:31 +00:00
|
|
|
test_when_finished "git update-ref -d $m" &&
|
2017-02-21 01:10:33 +00:00
|
|
|
git update-ref $m $A &&
|
|
|
|
git symbolic-ref HEAD $m &&
|
|
|
|
git update-ref -m delete-by-head -d HEAD &&
|
2020-11-13 08:12:31 +00:00
|
|
|
test_must_fail git show-ref --verify -q $m &&
|
2017-02-21 01:10:33 +00:00
|
|
|
grep "delete-by-head$" .git/logs/HEAD
|
|
|
|
'
|
|
|
|
|
2015-07-21 21:04:55 +00:00
|
|
|
test_expect_success 'update-ref does not create reflogs by default' '
|
|
|
|
test_when_finished "git update-ref -d $outside" &&
|
|
|
|
git update-ref $outside $A &&
|
|
|
|
git rev-parse $A >expect &&
|
|
|
|
git rev-parse $outside >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
test_must_fail git reflog exists $outside
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'update-ref creates reflogs with --create-reflog' '
|
|
|
|
test_when_finished "git update-ref -d $outside" &&
|
|
|
|
git update-ref --create-reflog $outside $A &&
|
|
|
|
git rev-parse $A >expect &&
|
|
|
|
git rev-parse $outside >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git reflog exists $outside
|
|
|
|
'
|
|
|
|
|
2017-01-27 10:09:48 +00:00
|
|
|
test_expect_success 'creates no reflog in bare repository' '
|
|
|
|
git -C $bare update-ref $m $bareA &&
|
|
|
|
git -C $bare rev-parse $bareA >expect &&
|
|
|
|
git -C $bare rev-parse $m >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
test_must_fail git -C $bare reflog exists $m
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'core.logAllRefUpdates=true creates reflog in bare repository' '
|
|
|
|
test_when_finished "git -C $bare config --unset core.logAllRefUpdates && \
|
|
|
|
rm $bare/logs/$m" &&
|
|
|
|
git -C $bare config core.logAllRefUpdates true &&
|
|
|
|
git -C $bare update-ref $m $bareB &&
|
|
|
|
git -C $bare rev-parse $bareB >expect &&
|
|
|
|
git -C $bare rev-parse $m >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git -C $bare reflog exists $m
|
|
|
|
'
|
|
|
|
|
2017-01-27 10:09:47 +00:00
|
|
|
test_expect_success 'core.logAllRefUpdates=true does not create reflog by default' '
|
|
|
|
test_config core.logAllRefUpdates true &&
|
|
|
|
test_when_finished "git update-ref -d $outside" &&
|
|
|
|
git update-ref $outside $A &&
|
|
|
|
git rev-parse $A >expect &&
|
|
|
|
git rev-parse $outside >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
test_must_fail git reflog exists $outside
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'core.logAllRefUpdates=always creates reflog by default' '
|
|
|
|
test_config core.logAllRefUpdates always &&
|
|
|
|
test_when_finished "git update-ref -d $outside" &&
|
|
|
|
git update-ref $outside $A &&
|
|
|
|
git rev-parse $A >expect &&
|
|
|
|
git rev-parse $outside >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git reflog exists $outside
|
|
|
|
'
|
|
|
|
|
2020-07-27 16:25:46 +00:00
|
|
|
test_expect_success 'core.logAllRefUpdates=always creates reflog for ORIG_HEAD' '
|
2017-01-27 10:09:47 +00:00
|
|
|
test_config core.logAllRefUpdates always &&
|
|
|
|
git update-ref ORIG_HEAD $A &&
|
2020-07-27 16:25:46 +00:00
|
|
|
git reflog exists ORIG_HEAD
|
2017-01-27 10:09:47 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success '--no-create-reflog overrides core.logAllRefUpdates=always' '
|
|
|
|
test_config core.logAllRefUpdates true &&
|
|
|
|
test_when_finished "git update-ref -d $outside" &&
|
|
|
|
git update-ref --no-create-reflog $outside $A &&
|
|
|
|
git rev-parse $A >expect &&
|
|
|
|
git rev-parse $outside >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
test_must_fail git reflog exists $outside
|
|
|
|
'
|
|
|
|
|
2017-04-16 02:31:02 +00:00
|
|
|
test_expect_success "create $m (by HEAD)" '
|
|
|
|
git update-ref HEAD $A &&
|
2020-05-20 17:36:08 +00:00
|
|
|
test $A = $(git show-ref -s --verify $m)
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
|
|
|
test_expect_success 'pack refs' '
|
|
|
|
git pack-refs --all
|
|
|
|
'
|
|
|
|
test_expect_success "move $m (by HEAD)" '
|
|
|
|
git update-ref HEAD $B $A &&
|
2020-05-20 17:36:08 +00:00
|
|
|
test $B = $(git show-ref -s --verify $m)
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
2012-10-21 10:40:32 +00:00
|
|
|
test_expect_success "delete $m (by HEAD) should remove both packed and loose $m" '
|
2020-11-13 08:12:31 +00:00
|
|
|
test_when_finished "git update-ref -d $m" &&
|
2012-10-21 10:40:31 +00:00
|
|
|
git update-ref -d HEAD $B &&
|
|
|
|
! grep "$m" .git/packed-refs &&
|
2020-11-13 08:12:31 +00:00
|
|
|
test_must_fail git show-ref --verify -q $m
|
2012-10-21 10:40:31 +00:00
|
|
|
'
|
|
|
|
|
2017-04-16 02:31:02 +00:00
|
|
|
test_expect_success 'delete symref without dereference' '
|
2020-11-13 08:12:31 +00:00
|
|
|
test_when_finished "git update-ref -d $m" &&
|
|
|
|
echo foo >foo.c &&
|
|
|
|
git add foo.c &&
|
|
|
|
git commit -m foo &&
|
|
|
|
git symbolic-ref SYMREF $m &&
|
|
|
|
git update-ref --no-deref -d SYMREF &&
|
|
|
|
git show-ref --verify -q $m &&
|
|
|
|
test_must_fail git show-ref --verify -q SYMREF &&
|
|
|
|
test_must_fail git symbolic-ref SYMREF
|
2008-10-26 02:33:58 +00:00
|
|
|
'
|
|
|
|
|
2017-04-16 02:31:02 +00:00
|
|
|
test_expect_success 'delete symref without dereference when the referred ref is packed' '
|
2020-11-13 08:12:31 +00:00
|
|
|
test_when_finished "git update-ref -d $m" &&
|
2008-10-31 23:25:44 +00:00
|
|
|
echo foo >foo.c &&
|
|
|
|
git add foo.c &&
|
|
|
|
git commit -m foo &&
|
2020-11-13 08:12:31 +00:00
|
|
|
git symbolic-ref SYMREF $m &&
|
2008-10-31 23:25:44 +00:00
|
|
|
git pack-refs --all &&
|
2020-11-13 08:12:31 +00:00
|
|
|
git update-ref --no-deref -d SYMREF &&
|
|
|
|
git show-ref --verify -q $m &&
|
|
|
|
test_must_fail git show-ref --verify -q SYMREF &&
|
|
|
|
test_must_fail git symbolic-ref SYMREF
|
2008-10-31 23:25:44 +00:00
|
|
|
'
|
2017-03-21 00:56:16 +00:00
|
|
|
|
2014-09-11 01:22:48 +00:00
|
|
|
test_expect_success 'update-ref -d is not confused by self-reference' '
|
|
|
|
git symbolic-ref refs/heads/self refs/heads/self &&
|
|
|
|
test_when_finished "rm -f .git/refs/heads/self" &&
|
|
|
|
test_path_is_file .git/refs/heads/self &&
|
|
|
|
test_must_fail git update-ref -d refs/heads/self &&
|
|
|
|
test_path_is_file .git/refs/heads/self
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'update-ref --no-deref -d can delete self-reference' '
|
|
|
|
git symbolic-ref refs/heads/self refs/heads/self &&
|
|
|
|
test_when_finished "rm -f .git/refs/heads/self" &&
|
|
|
|
test_path_is_file .git/refs/heads/self &&
|
|
|
|
git update-ref --no-deref -d refs/heads/self &&
|
2020-11-13 08:12:31 +00:00
|
|
|
test_must_fail git show-ref --verify -q refs/heads/self
|
2014-09-11 01:22:48 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' '
|
|
|
|
>.git/refs/heads/bad &&
|
|
|
|
test_when_finished "rm -f .git/refs/heads/bad" &&
|
|
|
|
git symbolic-ref refs/heads/ref-to-bad refs/heads/bad &&
|
2020-11-13 08:12:31 +00:00
|
|
|
test_when_finished "git update-ref -d refs/heads/ref-to-bad" &&
|
2014-09-11 01:22:48 +00:00
|
|
|
test_path_is_file .git/refs/heads/ref-to-bad &&
|
|
|
|
git update-ref --no-deref -d refs/heads/ref-to-bad &&
|
2020-11-13 08:12:31 +00:00
|
|
|
test_must_fail git show-ref --verify -q refs/heads/ref-to-bad
|
2014-09-11 01:22:48 +00:00
|
|
|
'
|
|
|
|
|
2017-04-16 02:31:02 +00:00
|
|
|
test_expect_success '(not) create HEAD with old sha1' '
|
2008-07-12 15:47:52 +00:00
|
|
|
test_must_fail git update-ref HEAD $A $B
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
2017-03-21 00:56:16 +00:00
|
|
|
test_expect_success "(not) prior created .git/$m" '
|
2020-11-13 08:12:31 +00:00
|
|
|
test_when_finished "git update-ref -d $m" &&
|
|
|
|
test_must_fail git show-ref --verify -q $m
|
2017-03-21 00:56:16 +00:00
|
|
|
'
|
2006-05-17 09:55:40 +00:00
|
|
|
|
2017-04-16 02:31:02 +00:00
|
|
|
test_expect_success 'create HEAD' '
|
|
|
|
git update-ref HEAD $A
|
|
|
|
'
|
|
|
|
test_expect_success '(not) change HEAD with wrong SHA1' '
|
2008-07-12 15:47:52 +00:00
|
|
|
test_must_fail git update-ref HEAD $B $Z
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
2017-03-21 00:56:16 +00:00
|
|
|
test_expect_success "(not) changed .git/$m" '
|
2020-11-13 08:12:31 +00:00
|
|
|
test_when_finished "git update-ref -d $m" &&
|
2020-05-20 17:36:08 +00:00
|
|
|
! test $B = $(git show-ref -s --verify $m)
|
2008-02-01 09:50:53 +00:00
|
|
|
'
|
2006-05-17 09:55:40 +00:00
|
|
|
|
2015-07-27 22:57:08 +00:00
|
|
|
rm -f .git/logs/refs/heads/master
|
2017-04-16 02:31:02 +00:00
|
|
|
test_expect_success "create $m (logged by touch)" '
|
|
|
|
test_config core.logAllRefUpdates false &&
|
|
|
|
GIT_COMMITTER_DATE="2005-05-26 23:30" \
|
|
|
|
git update-ref --create-reflog HEAD $A -m "Initial Creation" &&
|
2020-05-20 17:36:08 +00:00
|
|
|
test $A = $(git show-ref -s --verify $m)
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
|
|
|
test_expect_success "update $m (logged by touch)" '
|
|
|
|
test_config core.logAllRefUpdates false &&
|
|
|
|
GIT_COMMITTER_DATE="2005-05-26 23:31" \
|
|
|
|
git update-ref HEAD $B $A -m "Switch" &&
|
2020-05-20 17:36:08 +00:00
|
|
|
test $B = $(git show-ref -s --verify $m)
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
|
|
|
test_expect_success "set $m (logged by touch)" '
|
|
|
|
test_config core.logAllRefUpdates false &&
|
|
|
|
GIT_COMMITTER_DATE="2005-05-26 23:41" \
|
|
|
|
git update-ref HEAD $A &&
|
2020-05-20 17:36:08 +00:00
|
|
|
test $A = $(git show-ref -s --verify $m)
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'empty directory removal' '
|
2017-01-06 16:22:43 +00:00
|
|
|
git branch d1/d2/r1 HEAD &&
|
|
|
|
git branch d1/r2 HEAD &&
|
2017-03-21 00:56:14 +00:00
|
|
|
test_path_is_file .git/refs/heads/d1/d2/r1 &&
|
|
|
|
test_path_is_file .git/logs/refs/heads/d1/d2/r1 &&
|
2017-01-06 16:22:43 +00:00
|
|
|
git branch -d d1/d2/r1 &&
|
2020-11-13 08:12:31 +00:00
|
|
|
test_must_fail git show-ref --verify -q refs/heads/d1/d2 &&
|
|
|
|
test_must_fail git show-ref --verify -q logs/refs/heads/d1/d2 &&
|
2017-03-21 00:56:14 +00:00
|
|
|
test_path_is_file .git/refs/heads/d1/r2 &&
|
|
|
|
test_path_is_file .git/logs/refs/heads/d1/r2
|
2017-01-06 16:22:43 +00:00
|
|
|
'
|
|
|
|
|
2017-04-16 02:31:02 +00:00
|
|
|
test_expect_success 'symref empty directory removal' '
|
2017-01-06 16:22:43 +00:00
|
|
|
git branch e1/e2/r1 HEAD &&
|
|
|
|
git branch e1/r2 HEAD &&
|
|
|
|
git checkout e1/e2/r1 &&
|
|
|
|
test_when_finished "git checkout master" &&
|
2017-03-21 00:56:14 +00:00
|
|
|
test_path_is_file .git/refs/heads/e1/e2/r1 &&
|
|
|
|
test_path_is_file .git/logs/refs/heads/e1/e2/r1 &&
|
2017-01-06 16:22:43 +00:00
|
|
|
git update-ref -d HEAD &&
|
2020-11-13 08:12:31 +00:00
|
|
|
test_must_fail git show-ref --verify -q refs/heads/e1/e2 &&
|
|
|
|
test_must_fail git show-ref --verify -q logs/refs/heads/e1/e2 &&
|
2017-03-21 00:56:14 +00:00
|
|
|
test_path_is_file .git/refs/heads/e1/r2 &&
|
|
|
|
test_path_is_file .git/logs/refs/heads/e1/r2 &&
|
|
|
|
test_path_is_file .git/logs/HEAD
|
2017-01-06 16:22:43 +00:00
|
|
|
'
|
|
|
|
|
2006-05-17 09:55:40 +00:00
|
|
|
cat >expect <<EOF
|
|
|
|
$Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 Initial Creation
|
|
|
|
$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000 Switch
|
|
|
|
$B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000
|
|
|
|
EOF
|
2017-03-21 00:56:16 +00:00
|
|
|
test_expect_success "verifying $m's log (logged by touch)" '
|
|
|
|
test_when_finished "rm -rf .git/$m .git/logs expect" &&
|
|
|
|
test_cmp expect .git/logs/$m
|
|
|
|
'
|
2006-05-17 09:55:40 +00:00
|
|
|
|
2017-04-16 02:31:02 +00:00
|
|
|
test_expect_success "create $m (logged by config)" '
|
|
|
|
test_config core.logAllRefUpdates true &&
|
|
|
|
GIT_COMMITTER_DATE="2005-05-26 23:32" \
|
|
|
|
git update-ref HEAD $A -m "Initial Creation" &&
|
2020-05-20 17:36:08 +00:00
|
|
|
test $A = $(git show-ref -s --verify $m)
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
|
|
|
test_expect_success "update $m (logged by config)" '
|
|
|
|
test_config core.logAllRefUpdates true &&
|
|
|
|
GIT_COMMITTER_DATE="2005-05-26 23:33" \
|
t: don't spuriously close and reopen quotes
In the test scripts, the recommended style is, e.g.:
test_expect_success 'name' '
do-something somehow &&
do-some-more testing
'
When using this style, any single quote in the multi-line test section
is actually closing the lone single quotes that surround it.
It can be a non-issue in practice:
test_expect_success 'sed a little' '
sed -e 's/hi/lo/' in >out # "ok": no whitespace in s/hi/lo/
'
Or it can be a bug in the test, e.g., because variable interpolation
happens before the test even begins executing:
v=abc
test_expect_success 'variable interpolation' '
v=def &&
echo '"$v"' # abc
'
Change several such in-test single quotes to use double quotes instead
or, in a few cases, drop them altogether. These were identified using
some crude grepping. We're not fixing any test bugs here, but we're
hopefully making these tests slightly easier to grok and to maintain.
There are legitimate use cases for closing a quote and opening a new
one, e.g., both '\'' and '"'"' can be used to produce a literal single
quote. I'm not touching any of those here.
In t9401, tuck the redirecting ">" to the filename while we're touching
those lines.
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-08-06 20:08:53 +00:00
|
|
|
git update-ref HEAD $B $A -m "Switch" &&
|
2020-05-20 17:36:08 +00:00
|
|
|
test $B = $(git show-ref -s --verify $m)
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
|
|
|
test_expect_success "set $m (logged by config)" '
|
|
|
|
test_config core.logAllRefUpdates true &&
|
|
|
|
GIT_COMMITTER_DATE="2005-05-26 23:43" \
|
|
|
|
git update-ref HEAD $A &&
|
2020-05-20 17:36:08 +00:00
|
|
|
test $A = $(git show-ref -s --verify $m)
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
2006-05-17 09:55:40 +00:00
|
|
|
|
|
|
|
cat >expect <<EOF
|
|
|
|
$Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 +0000 Initial Creation
|
|
|
|
$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 +0000 Switch
|
|
|
|
$B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000
|
|
|
|
EOF
|
2017-04-16 02:31:02 +00:00
|
|
|
test_expect_success "verifying $m's log (logged by config)" '
|
|
|
|
test_when_finished "rm -f .git/$m .git/logs/$m expect" &&
|
|
|
|
test_cmp expect .git/logs/$m
|
|
|
|
'
|
2006-05-17 09:55:40 +00:00
|
|
|
|
2019-10-28 12:57:17 +00:00
|
|
|
test_expect_success 'set up for querying the reflog' '
|
|
|
|
git update-ref $m $D &&
|
|
|
|
cat >.git/logs/$m <<-EOF
|
|
|
|
$Z $C $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 -0500
|
|
|
|
$C $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150350 -0500
|
|
|
|
$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 -0500
|
|
|
|
$F $Z $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500
|
|
|
|
$Z $E $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 -0500
|
|
|
|
EOF
|
|
|
|
'
|
2006-05-19 07:28:19 +00:00
|
|
|
|
|
|
|
ed="Thu, 26 May 2005 18:32:00 -0500"
|
|
|
|
gd="Thu, 26 May 2005 18:33:00 -0500"
|
|
|
|
ld="Thu, 26 May 2005 18:43:00 -0500"
|
2017-04-16 02:31:02 +00:00
|
|
|
test_expect_success 'Query "master@{May 25 2005}" (before history)' '
|
|
|
|
test_when_finished "rm -f o e" &&
|
|
|
|
git rev-parse --verify "master@{May 25 2005}" >o 2>e &&
|
2020-01-25 00:06:24 +00:00
|
|
|
echo "$C" >expect &&
|
|
|
|
test_cmp expect o &&
|
2020-01-25 00:13:01 +00:00
|
|
|
echo "warning: log for '\''master'\'' only goes back to $ed" >expect &&
|
|
|
|
test_i18ncmp expect e
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
|
|
|
test_expect_success 'Query master@{2005-05-25} (before history)' '
|
|
|
|
test_when_finished "rm -f o e" &&
|
|
|
|
git rev-parse --verify master@{2005-05-25} >o 2>e &&
|
2020-01-25 00:06:24 +00:00
|
|
|
echo "$C" >expect &&
|
|
|
|
test_cmp expect o &&
|
2020-01-25 00:13:01 +00:00
|
|
|
echo "warning: log for '\''master'\'' only goes back to $ed" >expect &&
|
|
|
|
test_i18ncmp expect e
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
|
|
|
test_expect_success 'Query "master@{May 26 2005 23:31:59}" (1 second before history)' '
|
|
|
|
test_when_finished "rm -f o e" &&
|
|
|
|
git rev-parse --verify "master@{May 26 2005 23:31:59}" >o 2>e &&
|
2020-01-25 00:06:24 +00:00
|
|
|
echo "$C" >expect &&
|
|
|
|
test_cmp expect o &&
|
2020-01-25 00:13:01 +00:00
|
|
|
echo "warning: log for '\''master'\'' only goes back to $ed" >expect &&
|
|
|
|
test_i18ncmp expect e
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
|
|
|
test_expect_success 'Query "master@{May 26 2005 23:32:00}" (exactly history start)' '
|
|
|
|
test_when_finished "rm -f o e" &&
|
|
|
|
git rev-parse --verify "master@{May 26 2005 23:32:00}" >o 2>e &&
|
2020-01-25 00:06:24 +00:00
|
|
|
echo "$C" >expect &&
|
|
|
|
test_cmp expect o &&
|
2019-11-26 19:41:57 +00:00
|
|
|
test_must_be_empty e
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
|
|
|
test_expect_success 'Query "master@{May 26 2005 23:32:30}" (first non-creation change)' '
|
|
|
|
test_when_finished "rm -f o e" &&
|
|
|
|
git rev-parse --verify "master@{May 26 2005 23:32:30}" >o 2>e &&
|
2020-01-25 00:06:24 +00:00
|
|
|
echo "$A" >expect &&
|
|
|
|
test_cmp expect o &&
|
2019-11-26 19:41:57 +00:00
|
|
|
test_must_be_empty e
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
|
|
|
test_expect_success 'Query "master@{2005-05-26 23:33:01}" (middle of history with gap)' '
|
|
|
|
test_when_finished "rm -f o e" &&
|
|
|
|
git rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e &&
|
2020-01-25 00:06:24 +00:00
|
|
|
echo "$B" >expect &&
|
|
|
|
test_cmp expect o &&
|
2018-07-21 07:49:35 +00:00
|
|
|
test_i18ngrep -F "warning: log for ref $m has gap after $gd" e
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
|
|
|
test_expect_success 'Query "master@{2005-05-26 23:38:00}" (middle of history)' '
|
|
|
|
test_when_finished "rm -f o e" &&
|
|
|
|
git rev-parse --verify "master@{2005-05-26 23:38:00}" >o 2>e &&
|
2020-01-25 00:06:24 +00:00
|
|
|
echo "$Z" >expect &&
|
|
|
|
test_cmp expect o &&
|
2019-11-26 19:41:57 +00:00
|
|
|
test_must_be_empty e
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
|
|
|
test_expect_success 'Query "master@{2005-05-26 23:43:00}" (exact end of history)' '
|
|
|
|
test_when_finished "rm -f o e" &&
|
|
|
|
git rev-parse --verify "master@{2005-05-26 23:43:00}" >o 2>e &&
|
2020-01-25 00:06:24 +00:00
|
|
|
echo "$E" >expect &&
|
|
|
|
test_cmp expect o &&
|
2019-11-26 19:41:57 +00:00
|
|
|
test_must_be_empty e
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
|
|
|
test_expect_success 'Query "master@{2005-05-28}" (past end of history)' '
|
|
|
|
test_when_finished "rm -f o e" &&
|
|
|
|
git rev-parse --verify "master@{2005-05-28}" >o 2>e &&
|
2020-01-25 00:06:24 +00:00
|
|
|
echo "$D" >expect &&
|
|
|
|
test_cmp expect o &&
|
2018-07-21 07:49:35 +00:00
|
|
|
test_i18ngrep -F "warning: log for ref $m unexpectedly ended on $ld" e
|
2017-04-16 02:31:02 +00:00
|
|
|
'
|
2006-05-19 07:29:43 +00:00
|
|
|
|
|
|
|
rm -f .git/$m .git/logs/$m expect
|
|
|
|
|
2017-04-16 02:31:02 +00:00
|
|
|
test_expect_success 'creating initial files' '
|
|
|
|
test_when_finished rm -f M &&
|
|
|
|
echo TEST >F &&
|
|
|
|
git add F &&
|
|
|
|
GIT_AUTHOR_DATE="2005-05-26 23:30" \
|
|
|
|
GIT_COMMITTER_DATE="2005-05-26 23:30" git commit -m add -a &&
|
|
|
|
h_TEST=$(git rev-parse --verify HEAD) &&
|
|
|
|
echo The other day this did not work. >M &&
|
|
|
|
echo And then Bob told me how to fix it. >>M &&
|
|
|
|
echo OTHER >F &&
|
|
|
|
GIT_AUTHOR_DATE="2005-05-26 23:41" \
|
|
|
|
GIT_COMMITTER_DATE="2005-05-26 23:41" git commit -F M -a &&
|
|
|
|
h_OTHER=$(git rev-parse --verify HEAD) &&
|
|
|
|
GIT_AUTHOR_DATE="2005-05-26 23:44" \
|
|
|
|
GIT_COMMITTER_DATE="2005-05-26 23:44" git commit --amend &&
|
|
|
|
h_FIXED=$(git rev-parse --verify HEAD) &&
|
|
|
|
echo Merged initial commit and a later commit. >M &&
|
|
|
|
echo $h_TEST >.git/MERGE_HEAD &&
|
|
|
|
GIT_AUTHOR_DATE="2005-05-26 23:45" \
|
|
|
|
GIT_COMMITTER_DATE="2005-05-26 23:45" git commit -F M &&
|
|
|
|
h_MERGED=$(git rev-parse --verify HEAD)
|
|
|
|
'
|
2006-05-25 03:33:18 +00:00
|
|
|
|
|
|
|
cat >expect <<EOF
|
2006-07-11 02:48:47 +00:00
|
|
|
$Z $h_TEST $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 commit (initial): add
|
2006-05-25 03:33:18 +00:00
|
|
|
$h_TEST $h_OTHER $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000 commit: The other day this did not work.
|
2006-07-11 02:48:47 +00:00
|
|
|
$h_OTHER $h_FIXED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151040 +0000 commit (amend): The other day this did not work.
|
|
|
|
$h_FIXED $h_MERGED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151100 +0000 commit (merge): Merged initial commit and a later commit.
|
2006-05-25 03:33:18 +00:00
|
|
|
EOF
|
2017-04-16 02:31:02 +00:00
|
|
|
test_expect_success 'git commit logged updates' '
|
|
|
|
test_cmp expect .git/logs/$m
|
|
|
|
'
|
2006-07-11 02:48:47 +00:00
|
|
|
unset h_TEST h_OTHER h_FIXED h_MERGED
|
2006-05-19 07:29:43 +00:00
|
|
|
|
2017-04-16 02:31:02 +00:00
|
|
|
test_expect_success 'git cat-file blob master:F (expect OTHER)' '
|
|
|
|
test OTHER = $(git cat-file blob master:F)
|
|
|
|
'
|
|
|
|
test_expect_success 'git cat-file blob master@{2005-05-26 23:30}:F (expect TEST)' '
|
|
|
|
test TEST = $(git cat-file blob "master@{2005-05-26 23:30}:F")
|
|
|
|
'
|
|
|
|
test_expect_success 'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER)' '
|
|
|
|
test OTHER = $(git cat-file blob "master@{2005-05-26 23:42}:F")
|
|
|
|
'
|
2006-05-19 07:29:43 +00:00
|
|
|
|
2018-05-10 19:29:55 +00:00
|
|
|
# Test adding and deleting pseudorefs
|
|
|
|
|
|
|
|
test_expect_success 'given old value for missing pseudoref, do not create' '
|
|
|
|
test_must_fail git update-ref PSEUDOREF $A $B 2>err &&
|
2020-07-16 18:45:39 +00:00
|
|
|
test_must_fail git rev-parse PSEUDOREF &&
|
2020-07-27 16:25:46 +00:00
|
|
|
test_i18ngrep "unable to resolve reference" err
|
2018-05-10 19:29:55 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'create pseudoref' '
|
|
|
|
git update-ref PSEUDOREF $A &&
|
2020-07-16 18:45:39 +00:00
|
|
|
test $A = $(git rev-parse PSEUDOREF)
|
2018-05-10 19:29:55 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'overwrite pseudoref with no old value given' '
|
|
|
|
git update-ref PSEUDOREF $B &&
|
2020-07-16 18:45:39 +00:00
|
|
|
test $B = $(git rev-parse PSEUDOREF)
|
2018-05-10 19:29:55 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'overwrite pseudoref with correct old value' '
|
|
|
|
git update-ref PSEUDOREF $C $B &&
|
2020-07-16 18:45:39 +00:00
|
|
|
test $C = $(git rev-parse PSEUDOREF)
|
2018-05-10 19:29:55 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'do not overwrite pseudoref with wrong old value' '
|
|
|
|
test_must_fail git update-ref PSEUDOREF $D $E 2>err &&
|
2020-07-16 18:45:39 +00:00
|
|
|
test $C = $(git rev-parse PSEUDOREF) &&
|
2020-07-27 16:25:46 +00:00
|
|
|
test_i18ngrep "cannot lock ref.*expected" err
|
2018-05-10 19:29:55 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'delete pseudoref' '
|
|
|
|
git update-ref -d PSEUDOREF &&
|
2020-07-16 18:45:39 +00:00
|
|
|
test_must_fail git rev-parse PSEUDOREF
|
2018-05-10 19:29:55 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'do not delete pseudoref with wrong old value' '
|
|
|
|
git update-ref PSEUDOREF $A &&
|
|
|
|
test_must_fail git update-ref -d PSEUDOREF $B 2>err &&
|
2020-07-16 18:45:39 +00:00
|
|
|
test $A = $(git rev-parse PSEUDOREF) &&
|
2020-07-27 16:25:46 +00:00
|
|
|
test_i18ngrep "cannot lock ref.*expected" err
|
2018-05-10 19:29:55 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'delete pseudoref with correct old value' '
|
|
|
|
git update-ref -d PSEUDOREF $A &&
|
2020-07-16 18:45:39 +00:00
|
|
|
test_must_fail git rev-parse PSEUDOREF
|
2018-05-10 19:29:55 +00:00
|
|
|
'
|
|
|
|
|
2018-05-10 19:29:56 +00:00
|
|
|
test_expect_success 'create pseudoref with old OID zero' '
|
2018-05-10 19:29:55 +00:00
|
|
|
git update-ref PSEUDOREF $A $Z &&
|
2020-07-16 18:45:39 +00:00
|
|
|
test $A = $(git rev-parse PSEUDOREF)
|
2018-05-10 19:29:55 +00:00
|
|
|
'
|
|
|
|
|
2018-05-10 19:29:56 +00:00
|
|
|
test_expect_success 'do not overwrite pseudoref with old OID zero' '
|
2018-05-10 19:29:55 +00:00
|
|
|
test_when_finished git update-ref -d PSEUDOREF &&
|
|
|
|
test_must_fail git update-ref PSEUDOREF $B $Z 2>err &&
|
2020-07-16 18:45:39 +00:00
|
|
|
test $A = $(git rev-parse PSEUDOREF) &&
|
2018-07-21 07:49:35 +00:00
|
|
|
test_i18ngrep "already exists" err
|
2018-05-10 19:29:55 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
# Test --stdin
|
|
|
|
|
2013-09-11 12:46:18 +00:00
|
|
|
a=refs/heads/a
|
|
|
|
b=refs/heads/b
|
|
|
|
c=refs/heads/c
|
|
|
|
E='""'
|
|
|
|
F='%s\0'
|
|
|
|
pws='path with space'
|
|
|
|
|
|
|
|
test_expect_success 'stdin test setup' '
|
|
|
|
echo "$pws" >"$pws" &&
|
|
|
|
git add -- "$pws" &&
|
|
|
|
git commit -m "$pws"
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success '-z fails without --stdin' '
|
|
|
|
test_must_fail git update-ref -z $m $m $m 2>err &&
|
2016-06-17 20:21:07 +00:00
|
|
|
test_i18ngrep "usage: git update-ref" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin works with no input' '
|
|
|
|
>stdin &&
|
|
|
|
git update-ref --stdin <stdin &&
|
|
|
|
git rev-parse --verify -q $m
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin fails on empty line' '
|
|
|
|
echo "" >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
|
|
|
grep "fatal: empty command in input" err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin fails on only whitespace' '
|
|
|
|
echo " " >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
|
|
|
grep "fatal: whitespace before command: " err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin fails on leading whitespace' '
|
|
|
|
echo " create $a $m" >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
|
|
|
grep "fatal: whitespace before command: create $a $m" err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin fails on unknown command' '
|
|
|
|
echo "unknown $a" >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
|
|
|
grep "fatal: unknown command: unknown $a" err
|
|
|
|
'
|
|
|
|
|
2014-04-07 13:47:55 +00:00
|
|
|
test_expect_success 'stdin fails on unbalanced quotes' '
|
2013-09-11 12:46:18 +00:00
|
|
|
echo "create $a \"master" >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
|
|
|
grep "fatal: badly quoted argument: \\\"master" err
|
|
|
|
'
|
|
|
|
|
2020-10-23 14:00:06 +00:00
|
|
|
test_expect_success PREPARE_FOR_MAIN_BRANCH 'stdin fails on invalid escape' '
|
|
|
|
echo "create $a \"ma\zn\"" >stdin &&
|
2014-04-07 13:47:55 +00:00
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2020-10-23 14:00:06 +00:00
|
|
|
grep "fatal: badly quoted argument: \\\"ma\\\\zn\\\"" err
|
2014-04-07 13:47:55 +00:00
|
|
|
'
|
|
|
|
|
2014-04-07 13:47:54 +00:00
|
|
|
test_expect_success 'stdin fails on junk after quoted argument' '
|
2013-09-11 12:46:18 +00:00
|
|
|
echo "create \"$a\"master" >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2014-04-07 13:47:54 +00:00
|
|
|
grep "fatal: unexpected character after quoted argument: \\\"$a\\\"master" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin fails create with no ref' '
|
|
|
|
echo "create " >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2014-04-07 13:48:09 +00:00
|
|
|
grep "fatal: create: missing <ref>" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin fails create with no new value' '
|
|
|
|
echo "create $a" >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2014-04-07 13:48:09 +00:00
|
|
|
grep "fatal: create $a: missing <newvalue>" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin fails create with too many arguments' '
|
|
|
|
echo "create $a $m $m" >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2014-04-07 13:48:09 +00:00
|
|
|
grep "fatal: create $a: extra input: $m" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin fails update with no ref' '
|
|
|
|
echo "update " >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2014-04-07 13:48:09 +00:00
|
|
|
grep "fatal: update: missing <ref>" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin fails update with no new value' '
|
|
|
|
echo "update $a" >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2014-04-07 13:48:09 +00:00
|
|
|
grep "fatal: update $a: missing <newvalue>" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin fails update with too many arguments' '
|
|
|
|
echo "update $a $m $m $m" >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2014-04-07 13:48:09 +00:00
|
|
|
grep "fatal: update $a: extra input: $m" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin fails delete with no ref' '
|
|
|
|
echo "delete " >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2014-04-07 13:48:09 +00:00
|
|
|
grep "fatal: delete: missing <ref>" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin fails delete with too many arguments' '
|
|
|
|
echo "delete $a $m $m" >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2014-04-07 13:48:09 +00:00
|
|
|
grep "fatal: delete $a: extra input: $m" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin fails verify with too many arguments' '
|
|
|
|
echo "verify $a $m $m" >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2014-04-07 13:48:09 +00:00
|
|
|
grep "fatal: verify $a: extra input: $m" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin fails option with unknown name' '
|
|
|
|
echo "option unknown" >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
|
|
|
grep "fatal: option unknown: unknown" err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin fails with duplicate refs' '
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
create $a $m
|
|
|
|
create $b $m
|
|
|
|
create $a $m
|
|
|
|
EOF
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2018-07-21 07:49:35 +00:00
|
|
|
test_i18ngrep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin create ref works' '
|
|
|
|
echo "create $a $m" >stdin &&
|
|
|
|
git update-ref --stdin <stdin &&
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $a >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2015-07-21 21:04:55 +00:00
|
|
|
test_expect_success 'stdin does not create reflogs by default' '
|
|
|
|
test_when_finished "git update-ref -d $outside" &&
|
|
|
|
echo "create $outside $m" >stdin &&
|
|
|
|
git update-ref --stdin <stdin &&
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $outside >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
test_must_fail git reflog exists $outside
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin creates reflogs with --create-reflog' '
|
2017-01-27 10:09:47 +00:00
|
|
|
test_when_finished "git update-ref -d $outside" &&
|
2015-07-21 21:04:55 +00:00
|
|
|
echo "create $outside $m" >stdin &&
|
|
|
|
git update-ref --create-reflog --stdin <stdin &&
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $outside >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git reflog exists $outside
|
|
|
|
'
|
|
|
|
|
2014-04-07 13:47:55 +00:00
|
|
|
test_expect_success 'stdin succeeds with quoted argument' '
|
|
|
|
git update-ref -d $a &&
|
|
|
|
echo "create $a \"$m\"" >stdin &&
|
|
|
|
git update-ref --stdin <stdin &&
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $a >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2020-10-23 14:00:06 +00:00
|
|
|
test_expect_success PREPARE_FOR_MAIN_BRANCH 'stdin succeeds with escaped character' '
|
2014-04-07 13:47:55 +00:00
|
|
|
git update-ref -d $a &&
|
2020-10-23 14:00:06 +00:00
|
|
|
echo "create $a \"ma\\151n\"" >stdin &&
|
2014-04-07 13:47:55 +00:00
|
|
|
git update-ref --stdin <stdin &&
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $a >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2013-09-11 12:46:18 +00:00
|
|
|
test_expect_success 'stdin update ref creates with zero old value' '
|
|
|
|
echo "update $b $m $Z" >stdin &&
|
|
|
|
git update-ref --stdin <stdin &&
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $b >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git update-ref -d $b
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin update ref creates with empty old value' '
|
|
|
|
echo "update $b $m $E" >stdin &&
|
|
|
|
git update-ref --stdin <stdin &&
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $b >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin create ref works with path with space to blob' '
|
|
|
|
echo "create refs/blobs/pws \"$m:$pws\"" >stdin &&
|
|
|
|
git update-ref --stdin <stdin &&
|
|
|
|
git rev-parse "$m:$pws" >expect &&
|
|
|
|
git rev-parse refs/blobs/pws >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git update-ref -d refs/blobs/pws
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin update ref fails with wrong old value' '
|
|
|
|
echo "update $c $m $m~1" >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2015-05-22 23:34:57 +00:00
|
|
|
grep "fatal: cannot lock ref '"'"'$c'"'"'" err &&
|
2013-09-11 12:46:18 +00:00
|
|
|
test_must_fail git rev-parse --verify -q $c
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin update ref fails with bad old value' '
|
|
|
|
echo "update $c $m does-not-exist" >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2014-04-07 13:48:02 +00:00
|
|
|
grep "fatal: update $c: invalid <oldvalue>: does-not-exist" err &&
|
2013-09-11 12:46:18 +00:00
|
|
|
test_must_fail git rev-parse --verify -q $c
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin create ref fails with bad new value' '
|
|
|
|
echo "create $c does-not-exist" >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2014-04-07 13:48:02 +00:00
|
|
|
grep "fatal: create $c: invalid <newvalue>: does-not-exist" err &&
|
2013-09-11 12:46:18 +00:00
|
|
|
test_must_fail git rev-parse --verify -q $c
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin create ref fails with zero new value' '
|
|
|
|
echo "create $c " >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2014-04-07 13:48:09 +00:00
|
|
|
grep "fatal: create $c: zero <newvalue>" err &&
|
2013-09-11 12:46:18 +00:00
|
|
|
test_must_fail git rev-parse --verify -q $c
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin update ref works with right old value' '
|
|
|
|
echo "update $b $m~1 $m" >stdin &&
|
|
|
|
git update-ref --stdin <stdin &&
|
|
|
|
git rev-parse $m~1 >expect &&
|
|
|
|
git rev-parse $b >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin delete ref fails with wrong old value' '
|
|
|
|
echo "delete $a $m~1" >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2015-05-22 23:34:57 +00:00
|
|
|
grep "fatal: cannot lock ref '"'"'$a'"'"'" err &&
|
2013-09-11 12:46:18 +00:00
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $a >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin delete ref fails with zero old value' '
|
|
|
|
echo "delete $a " >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2014-04-07 13:48:09 +00:00
|
|
|
grep "fatal: delete $a: zero <oldvalue>" err &&
|
2013-09-11 12:46:18 +00:00
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $a >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin update symref works option no-deref' '
|
|
|
|
git symbolic-ref TESTSYMREF $b &&
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
option no-deref
|
|
|
|
update TESTSYMREF $a $b
|
|
|
|
EOF
|
|
|
|
git update-ref --stdin <stdin &&
|
|
|
|
git rev-parse TESTSYMREF >expect &&
|
|
|
|
git rev-parse $a >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git rev-parse $m~1 >expect &&
|
|
|
|
git rev-parse $b >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin delete symref works option no-deref' '
|
|
|
|
git symbolic-ref TESTSYMREF $b &&
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
option no-deref
|
|
|
|
delete TESTSYMREF $b
|
|
|
|
EOF
|
|
|
|
git update-ref --stdin <stdin &&
|
|
|
|
test_must_fail git rev-parse --verify -q TESTSYMREF &&
|
|
|
|
git rev-parse $m~1 >expect &&
|
|
|
|
git rev-parse $b >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2018-09-05 17:25:50 +00:00
|
|
|
test_expect_success 'stdin update symref works flag --no-deref' '
|
|
|
|
git symbolic-ref TESTSYMREFONE $b &&
|
|
|
|
git symbolic-ref TESTSYMREFTWO $b &&
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
update TESTSYMREFONE $a $b
|
|
|
|
update TESTSYMREFTWO $a $b
|
|
|
|
EOF
|
|
|
|
git update-ref --no-deref --stdin <stdin &&
|
|
|
|
git rev-parse TESTSYMREFONE TESTSYMREFTWO >expect &&
|
|
|
|
git rev-parse $a $a >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git rev-parse $m~1 >expect &&
|
|
|
|
git rev-parse $b >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin delete symref works flag --no-deref' '
|
|
|
|
git symbolic-ref TESTSYMREFONE $b &&
|
|
|
|
git symbolic-ref TESTSYMREFTWO $b &&
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
delete TESTSYMREFONE $b
|
|
|
|
delete TESTSYMREFTWO $b
|
|
|
|
EOF
|
|
|
|
git update-ref --no-deref --stdin <stdin &&
|
|
|
|
test_must_fail git rev-parse --verify -q TESTSYMREFONE &&
|
|
|
|
test_must_fail git rev-parse --verify -q TESTSYMREFTWO &&
|
|
|
|
git rev-parse $m~1 >expect &&
|
|
|
|
git rev-parse $b >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2013-09-11 12:46:18 +00:00
|
|
|
test_expect_success 'stdin delete ref works with right old value' '
|
|
|
|
echo "delete $b $m~1" >stdin &&
|
|
|
|
git update-ref --stdin <stdin &&
|
|
|
|
test_must_fail git rev-parse --verify -q $b
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin update/create/verify combination works' '
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
update $a $m
|
|
|
|
create $b $m
|
|
|
|
verify $c
|
|
|
|
EOF
|
|
|
|
git update-ref --stdin <stdin &&
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $a >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git rev-parse $b >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
test_must_fail git rev-parse --verify -q $c
|
|
|
|
'
|
|
|
|
|
2014-12-10 23:47:51 +00:00
|
|
|
test_expect_success 'stdin verify succeeds for correct value' '
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
echo "verify $m $m" >stdin &&
|
|
|
|
git update-ref --stdin <stdin &&
|
|
|
|
git rev-parse $m >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin verify succeeds for missing reference' '
|
|
|
|
echo "verify refs/heads/missing $Z" >stdin &&
|
|
|
|
git update-ref --stdin <stdin &&
|
|
|
|
test_must_fail git rev-parse --verify -q refs/heads/missing
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin verify treats no value as missing' '
|
|
|
|
echo "verify refs/heads/missing" >stdin &&
|
|
|
|
git update-ref --stdin <stdin &&
|
|
|
|
test_must_fail git rev-parse --verify -q refs/heads/missing
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin verify fails for wrong value' '
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
echo "verify $m $m~1" >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin &&
|
|
|
|
git rev-parse $m >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin verify fails for mistaken null value' '
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
echo "verify $m $Z" >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin &&
|
|
|
|
git rev-parse $m >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2014-12-10 23:47:52 +00:00
|
|
|
test_expect_success 'stdin verify fails for mistaken empty value' '
|
2014-12-10 23:47:51 +00:00
|
|
|
M=$(git rev-parse $m) &&
|
|
|
|
test_when_finished "git update-ref $m $M" &&
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
echo "verify $m" >stdin &&
|
|
|
|
test_must_fail git update-ref --stdin <stdin &&
|
|
|
|
git rev-parse $m >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2013-09-11 12:46:18 +00:00
|
|
|
test_expect_success 'stdin update refs works with identity updates' '
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
update $a $m $m
|
|
|
|
update $b $m $m
|
|
|
|
update $c $Z $E
|
|
|
|
EOF
|
|
|
|
git update-ref --stdin <stdin &&
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $a >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git rev-parse $b >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
test_must_fail git rev-parse --verify -q $c
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin update refs fails with wrong old value' '
|
|
|
|
git update-ref $c $m &&
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
update $a $m $m
|
|
|
|
update $b $m $m
|
|
|
|
update $c ''
|
|
|
|
EOF
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2015-05-22 23:34:57 +00:00
|
|
|
grep "fatal: cannot lock ref '"'"'$c'"'"'" err &&
|
2013-09-11 12:46:18 +00:00
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $a >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git rev-parse $b >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git rev-parse $c >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin delete refs works with packed and loose refs' '
|
|
|
|
git pack-refs --all &&
|
|
|
|
git update-ref $c $m~1 &&
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
delete $a $m
|
|
|
|
update $b $Z $m
|
|
|
|
update $c $E $m~1
|
|
|
|
EOF
|
|
|
|
git update-ref --stdin <stdin &&
|
|
|
|
test_must_fail git rev-parse --verify -q $a &&
|
|
|
|
test_must_fail git rev-parse --verify -q $b &&
|
|
|
|
test_must_fail git rev-parse --verify -q $c
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z works on empty input' '
|
|
|
|
>stdin &&
|
|
|
|
git update-ref -z --stdin <stdin &&
|
|
|
|
git rev-parse --verify -q $m
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z fails on empty line' '
|
|
|
|
echo "" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
|
|
|
grep "fatal: whitespace before command: " err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z fails on empty command' '
|
|
|
|
printf $F "" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
|
|
|
grep "fatal: empty command in input" err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z fails on only whitespace' '
|
|
|
|
printf $F " " >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
|
|
|
grep "fatal: whitespace before command: " err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z fails on leading whitespace' '
|
|
|
|
printf $F " create $a" "$m" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
|
|
|
grep "fatal: whitespace before command: create $a" err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z fails on unknown command' '
|
|
|
|
printf $F "unknown $a" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
|
|
|
grep "fatal: unknown command: unknown $a" err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z fails create with no ref' '
|
|
|
|
printf $F "create " >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
2014-04-07 13:48:09 +00:00
|
|
|
grep "fatal: create: missing <ref>" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z fails create with no new value' '
|
|
|
|
printf $F "create $a" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
2014-04-07 13:48:08 +00:00
|
|
|
grep "fatal: create $a: unexpected end of input when reading <newvalue>" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z fails create with too many arguments' '
|
|
|
|
printf $F "create $a" "$m" "$m" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
|
|
|
grep "fatal: unknown command: $m" err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z fails update with no ref' '
|
|
|
|
printf $F "update " >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
2014-04-07 13:48:09 +00:00
|
|
|
grep "fatal: update: missing <ref>" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
2014-04-07 13:48:07 +00:00
|
|
|
test_expect_success 'stdin -z fails update with too few args' '
|
|
|
|
printf $F "update $a" "$m" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
2014-04-07 13:48:08 +00:00
|
|
|
grep "fatal: update $a: unexpected end of input when reading <oldvalue>" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
2014-04-07 13:48:06 +00:00
|
|
|
test_expect_success 'stdin -z emits warning with empty new value' '
|
2014-04-07 13:48:04 +00:00
|
|
|
git update-ref $a $m &&
|
|
|
|
printf $F "update $a" "" "" >stdin &&
|
2014-04-07 13:48:06 +00:00
|
|
|
git update-ref -z --stdin <stdin 2>err &&
|
|
|
|
grep "warning: update $a: missing <newvalue>, treating as zero" err &&
|
2014-04-07 13:48:04 +00:00
|
|
|
test_must_fail git rev-parse --verify -q $a
|
|
|
|
'
|
|
|
|
|
2013-09-11 12:46:18 +00:00
|
|
|
test_expect_success 'stdin -z fails update with no new value' '
|
|
|
|
printf $F "update $a" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
2014-04-07 13:48:08 +00:00
|
|
|
grep "fatal: update $a: unexpected end of input when reading <newvalue>" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z fails update with no old value' '
|
|
|
|
printf $F "update $a" "$m" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
2014-04-07 13:48:08 +00:00
|
|
|
grep "fatal: update $a: unexpected end of input when reading <oldvalue>" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z fails update with too many arguments' '
|
|
|
|
printf $F "update $a" "$m" "$m" "$m" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
|
|
|
grep "fatal: unknown command: $m" err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z fails delete with no ref' '
|
|
|
|
printf $F "delete " >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
2014-04-07 13:48:09 +00:00
|
|
|
grep "fatal: delete: missing <ref>" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z fails delete with no old value' '
|
|
|
|
printf $F "delete $a" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
2014-04-07 13:48:08 +00:00
|
|
|
grep "fatal: delete $a: unexpected end of input when reading <oldvalue>" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z fails delete with too many arguments' '
|
|
|
|
printf $F "delete $a" "$m" "$m" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
|
|
|
grep "fatal: unknown command: $m" err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z fails verify with too many arguments' '
|
|
|
|
printf $F "verify $a" "$m" "$m" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
|
|
|
grep "fatal: unknown command: $m" err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z fails verify with no old value' '
|
|
|
|
printf $F "verify $a" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
2014-04-07 13:48:08 +00:00
|
|
|
grep "fatal: verify $a: unexpected end of input when reading <oldvalue>" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z fails option with unknown name' '
|
|
|
|
printf $F "option unknown" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
|
|
|
grep "fatal: option unknown: unknown" err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z fails with duplicate refs' '
|
|
|
|
printf $F "create $a" "$m" "create $b" "$m" "create $a" "$m" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
2018-07-21 07:49:35 +00:00
|
|
|
test_i18ngrep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed" err
|
2013-09-11 12:46:18 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z create ref works' '
|
|
|
|
printf $F "create $a" "$m" >stdin &&
|
|
|
|
git update-ref -z --stdin <stdin &&
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $a >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z update ref creates with zero old value' '
|
|
|
|
printf $F "update $b" "$m" "$Z" >stdin &&
|
|
|
|
git update-ref -z --stdin <stdin &&
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $b >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git update-ref -d $b
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z update ref creates with empty old value' '
|
|
|
|
printf $F "update $b" "$m" "" >stdin &&
|
|
|
|
git update-ref -z --stdin <stdin &&
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $b >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z create ref works with path with space to blob' '
|
|
|
|
printf $F "create refs/blobs/pws" "$m:$pws" >stdin &&
|
|
|
|
git update-ref -z --stdin <stdin &&
|
|
|
|
git rev-parse "$m:$pws" >expect &&
|
|
|
|
git rev-parse refs/blobs/pws >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git update-ref -d refs/blobs/pws
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z update ref fails with wrong old value' '
|
|
|
|
printf $F "update $c" "$m" "$m~1" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
2015-05-22 23:34:57 +00:00
|
|
|
grep "fatal: cannot lock ref '"'"'$c'"'"'" err &&
|
2013-09-11 12:46:18 +00:00
|
|
|
test_must_fail git rev-parse --verify -q $c
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z update ref fails with bad old value' '
|
|
|
|
printf $F "update $c" "$m" "does-not-exist" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
2014-04-07 13:48:02 +00:00
|
|
|
grep "fatal: update $c: invalid <oldvalue>: does-not-exist" err &&
|
2013-09-11 12:46:18 +00:00
|
|
|
test_must_fail git rev-parse --verify -q $c
|
|
|
|
'
|
|
|
|
|
2014-04-02 08:09:54 +00:00
|
|
|
test_expect_success 'stdin -z create ref fails when ref exists' '
|
|
|
|
git update-ref $c $m &&
|
|
|
|
git rev-parse "$c" >expect &&
|
|
|
|
printf $F "create $c" "$m~1" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
2015-05-22 23:34:57 +00:00
|
|
|
grep "fatal: cannot lock ref '"'"'$c'"'"'" err &&
|
2014-04-02 08:09:54 +00:00
|
|
|
git rev-parse "$c" >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2013-09-11 12:46:18 +00:00
|
|
|
test_expect_success 'stdin -z create ref fails with bad new value' '
|
2014-04-02 08:09:54 +00:00
|
|
|
git update-ref -d "$c" &&
|
2013-09-11 12:46:18 +00:00
|
|
|
printf $F "create $c" "does-not-exist" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
2014-04-07 13:48:02 +00:00
|
|
|
grep "fatal: create $c: invalid <newvalue>: does-not-exist" err &&
|
2013-09-11 12:46:18 +00:00
|
|
|
test_must_fail git rev-parse --verify -q $c
|
|
|
|
'
|
|
|
|
|
update-ref.c: extract a new function, parse_next_sha1()
Replace three functions, update_store_new_sha1(),
update_store_old_sha1(), and parse_next_arg(), with a single function,
parse_next_sha1(). The new function takes care of a whole argument,
including checking whether it is there, converting it to an SHA-1, and
emitting errors on EOF or for invalid values. The return value
indicates whether the argument was present or absent, which requires
a bit of intelligence because absent values are represented
differently depending on whether "-z" was used.
The new interface means that the calling functions, parse_cmd_*(),
don't have to interpret the result differently based on the
line_termination mode that is in effect. It also means that
parse_cmd_create() can distinguish unambiguously between an empty new
value and a zeros new value, which fixes a failure in t1400.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-04-07 13:48:05 +00:00
|
|
|
test_expect_success 'stdin -z create ref fails with empty new value' '
|
2013-09-11 12:46:18 +00:00
|
|
|
printf $F "create $c" "" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
2014-04-07 13:48:09 +00:00
|
|
|
grep "fatal: create $c: missing <newvalue>" err &&
|
2013-09-11 12:46:18 +00:00
|
|
|
test_must_fail git rev-parse --verify -q $c
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z update ref works with right old value' '
|
|
|
|
printf $F "update $b" "$m~1" "$m" >stdin &&
|
|
|
|
git update-ref -z --stdin <stdin &&
|
|
|
|
git rev-parse $m~1 >expect &&
|
|
|
|
git rev-parse $b >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z delete ref fails with wrong old value' '
|
|
|
|
printf $F "delete $a" "$m~1" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
2015-05-22 23:34:57 +00:00
|
|
|
grep "fatal: cannot lock ref '"'"'$a'"'"'" err &&
|
2013-09-11 12:46:18 +00:00
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $a >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z delete ref fails with zero old value' '
|
|
|
|
printf $F "delete $a" "$Z" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
2014-04-07 13:48:09 +00:00
|
|
|
grep "fatal: delete $a: zero <oldvalue>" err &&
|
2013-09-11 12:46:18 +00:00
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $a >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z update symref works option no-deref' '
|
|
|
|
git symbolic-ref TESTSYMREF $b &&
|
|
|
|
printf $F "option no-deref" "update TESTSYMREF" "$a" "$b" >stdin &&
|
|
|
|
git update-ref -z --stdin <stdin &&
|
|
|
|
git rev-parse TESTSYMREF >expect &&
|
|
|
|
git rev-parse $a >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git rev-parse $m~1 >expect &&
|
|
|
|
git rev-parse $b >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z delete symref works option no-deref' '
|
|
|
|
git symbolic-ref TESTSYMREF $b &&
|
|
|
|
printf $F "option no-deref" "delete TESTSYMREF" "$b" >stdin &&
|
|
|
|
git update-ref -z --stdin <stdin &&
|
|
|
|
test_must_fail git rev-parse --verify -q TESTSYMREF &&
|
|
|
|
git rev-parse $m~1 >expect &&
|
|
|
|
git rev-parse $b >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z delete ref works with right old value' '
|
|
|
|
printf $F "delete $b" "$m~1" >stdin &&
|
|
|
|
git update-ref -z --stdin <stdin &&
|
|
|
|
test_must_fail git rev-parse --verify -q $b
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z update/create/verify combination works' '
|
|
|
|
printf $F "update $a" "$m" "" "create $b" "$m" "verify $c" "" >stdin &&
|
|
|
|
git update-ref -z --stdin <stdin &&
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $a >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git rev-parse $b >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
test_must_fail git rev-parse --verify -q $c
|
|
|
|
'
|
|
|
|
|
2014-12-10 23:47:51 +00:00
|
|
|
test_expect_success 'stdin -z verify succeeds for correct value' '
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
printf $F "verify $m" "$m" >stdin &&
|
|
|
|
git update-ref -z --stdin <stdin &&
|
|
|
|
git rev-parse $m >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z verify succeeds for missing reference' '
|
|
|
|
printf $F "verify refs/heads/missing" "$Z" >stdin &&
|
|
|
|
git update-ref -z --stdin <stdin &&
|
|
|
|
test_must_fail git rev-parse --verify -q refs/heads/missing
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z verify treats no value as missing' '
|
|
|
|
printf $F "verify refs/heads/missing" "" >stdin &&
|
|
|
|
git update-ref -z --stdin <stdin &&
|
|
|
|
test_must_fail git rev-parse --verify -q refs/heads/missing
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z verify fails for wrong value' '
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
printf $F "verify $m" "$m~1" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin &&
|
|
|
|
git rev-parse $m >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z verify fails for mistaken null value' '
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
printf $F "verify $m" "$Z" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin &&
|
|
|
|
git rev-parse $m >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2014-12-10 23:47:52 +00:00
|
|
|
test_expect_success 'stdin -z verify fails for mistaken empty value' '
|
2014-12-10 23:47:51 +00:00
|
|
|
M=$(git rev-parse $m) &&
|
|
|
|
test_when_finished "git update-ref $m $M" &&
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
printf $F "verify $m" "" >stdin &&
|
|
|
|
test_must_fail git update-ref -z --stdin <stdin &&
|
|
|
|
git rev-parse $m >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2013-09-11 12:46:18 +00:00
|
|
|
test_expect_success 'stdin -z update refs works with identity updates' '
|
|
|
|
printf $F "update $a" "$m" "$m" "update $b" "$m" "$m" "update $c" "$Z" "" >stdin &&
|
|
|
|
git update-ref -z --stdin <stdin &&
|
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $a >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git rev-parse $b >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
test_must_fail git rev-parse --verify -q $c
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z update refs fails with wrong old value' '
|
|
|
|
git update-ref $c $m &&
|
2014-04-07 13:47:53 +00:00
|
|
|
printf $F "update $a" "$m" "$m" "update $b" "$m" "$m" "update $c" "$m" "$Z" >stdin &&
|
2013-09-11 12:46:18 +00:00
|
|
|
test_must_fail git update-ref -z --stdin <stdin 2>err &&
|
2015-05-22 23:34:57 +00:00
|
|
|
grep "fatal: cannot lock ref '"'"'$c'"'"'" err &&
|
2013-09-11 12:46:18 +00:00
|
|
|
git rev-parse $m >expect &&
|
|
|
|
git rev-parse $a >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git rev-parse $b >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git rev-parse $c >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'stdin -z delete refs works with packed and loose refs' '
|
|
|
|
git pack-refs --all &&
|
|
|
|
git update-ref $c $m~1 &&
|
|
|
|
printf $F "delete $a" "$m" "update $b" "$Z" "$m" "update $c" "" "$m~1" >stdin &&
|
|
|
|
git update-ref -z --stdin <stdin &&
|
|
|
|
test_must_fail git rev-parse --verify -q $a &&
|
|
|
|
test_must_fail git rev-parse --verify -q $b &&
|
|
|
|
test_must_fail git rev-parse --verify -q $c
|
|
|
|
'
|
|
|
|
|
refs: resolve symbolic refs first
Before committing ref updates, split symbolic ref updates into two
parts: an update to the underlying ref, and a log-only update to the
symbolic ref. This ensures that both references are locked correctly
during the transaction, including while their reflogs are updated.
Similarly, if the reference pointed to by HEAD is modified directly, add
a separate log-only update to HEAD, rather than leaving the job of
updating HEAD's reflog to commit_ref_update(). This change ensures that
HEAD is locked correctly while its reflog is being modified, as well as
being cheaper (HEAD only needs to be resolved once).
This makes use of a new function, lock_raw_ref(), which is analogous to
read_raw_ref(), but acquires a lock on the reference before reading it.
This change still has two problems:
* There are redundant read_ref_full() reference lookups.
* It is still possible to get incorrect reflogs for symbolic references
if there is a concurrent update by another process, since the old_oid
of a symref is determined before the lock on the pointed-to ref is
held.
Both problems will soon be fixed.
Signed-off-by: David Turner <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
WIP
2016-04-25 13:56:07 +00:00
|
|
|
test_expect_success 'fails with duplicate HEAD update' '
|
|
|
|
git branch target1 $A &&
|
|
|
|
git checkout target1 &&
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
update refs/heads/target1 $C
|
|
|
|
option no-deref
|
|
|
|
update HEAD $B
|
|
|
|
EOF
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2018-07-21 07:49:35 +00:00
|
|
|
test_i18ngrep "fatal: multiple updates for '\''HEAD'\'' (including one via its referent .refs/heads/target1.) are not allowed" err &&
|
refs: resolve symbolic refs first
Before committing ref updates, split symbolic ref updates into two
parts: an update to the underlying ref, and a log-only update to the
symbolic ref. This ensures that both references are locked correctly
during the transaction, including while their reflogs are updated.
Similarly, if the reference pointed to by HEAD is modified directly, add
a separate log-only update to HEAD, rather than leaving the job of
updating HEAD's reflog to commit_ref_update(). This change ensures that
HEAD is locked correctly while its reflog is being modified, as well as
being cheaper (HEAD only needs to be resolved once).
This makes use of a new function, lock_raw_ref(), which is analogous to
read_raw_ref(), but acquires a lock on the reference before reading it.
This change still has two problems:
* There are redundant read_ref_full() reference lookups.
* It is still possible to get incorrect reflogs for symbolic references
if there is a concurrent update by another process, since the old_oid
of a symref is determined before the lock on the pointed-to ref is
held.
Both problems will soon be fixed.
Signed-off-by: David Turner <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
WIP
2016-04-25 13:56:07 +00:00
|
|
|
echo "refs/heads/target1" >expect &&
|
|
|
|
git symbolic-ref HEAD >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
echo "$A" >expect &&
|
|
|
|
git rev-parse refs/heads/target1 >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'fails with duplicate ref update via symref' '
|
|
|
|
git branch target2 $A &&
|
|
|
|
git symbolic-ref refs/heads/symref2 refs/heads/target2 &&
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
update refs/heads/target2 $C
|
|
|
|
update refs/heads/symref2 $B
|
|
|
|
EOF
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err &&
|
2018-07-21 07:49:35 +00:00
|
|
|
test_i18ngrep "fatal: multiple updates for '\''refs/heads/target2'\'' (including one via symref .refs/heads/symref2.) are not allowed" err &&
|
refs: resolve symbolic refs first
Before committing ref updates, split symbolic ref updates into two
parts: an update to the underlying ref, and a log-only update to the
symbolic ref. This ensures that both references are locked correctly
during the transaction, including while their reflogs are updated.
Similarly, if the reference pointed to by HEAD is modified directly, add
a separate log-only update to HEAD, rather than leaving the job of
updating HEAD's reflog to commit_ref_update(). This change ensures that
HEAD is locked correctly while its reflog is being modified, as well as
being cheaper (HEAD only needs to be resolved once).
This makes use of a new function, lock_raw_ref(), which is analogous to
read_raw_ref(), but acquires a lock on the reference before reading it.
This change still has two problems:
* There are redundant read_ref_full() reference lookups.
* It is still possible to get incorrect reflogs for symbolic references
if there is a concurrent update by another process, since the old_oid
of a symref is determined before the lock on the pointed-to ref is
held.
Both problems will soon be fixed.
Signed-off-by: David Turner <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
WIP
2016-04-25 13:56:07 +00:00
|
|
|
echo "refs/heads/target2" >expect &&
|
|
|
|
git symbolic-ref refs/heads/symref2 >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
echo "$A" >expect &&
|
|
|
|
git rev-parse refs/heads/target2 >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
ref_transaction_commit(): fix atomicity and avoid fd exhaustion
The old code was roughly
for update in updates:
acquire locks and check old_sha
for update in updates:
if changing value:
write_ref_to_lockfile()
commit_ref_update()
for update in updates:
if deleting value:
unlink()
rewrite packed-refs file
for update in updates:
if reference still locked:
unlock_ref()
This has two problems.
Non-atomic updates
==================
The atomicity of the reference transaction depends on all pre-checks
being done in the first loop, before any changes have started being
committed in the second loop. The problem is that
write_ref_to_lockfile() (previously part of write_ref_sha1()), which
is called from the second loop, contains two more checks:
* It verifies that new_sha1 is a valid object
* If the reference being updated is a branch, it verifies that
new_sha1 points at a commit object (as opposed to a tag, tree, or
blob).
If either of these checks fails, the "transaction" is aborted during
the second loop. But this might happen after some reference updates
have already been permanently committed. In other words, the
all-or-nothing promise of "git update-ref --stdin" could be violated.
So these checks have to be moved to the first loop.
File descriptor exhaustion
==========================
The old code locked all of the references in the first loop, leaving
all of the lockfiles open until later loops. Since we might be
updating a lot of references, this could result in file descriptor
exhaustion.
The solution
============
After this patch, the code looks like
for update in updates:
acquire locks and check old_sha
if changing value:
write_ref_to_lockfile()
else:
close_ref()
for update in updates:
if changing value:
commit_ref_update()
for update in updates:
if deleting value:
unlink()
rewrite packed-refs file
for update in updates:
if reference still locked:
unlock_ref()
This fixes both problems:
1. The pre-checks in write_ref_to_lockfile() are now done in the first
loop, before any changes have been committed. If any of the checks
fails, the whole transaction can now be rolled back correctly.
2. All lockfiles are closed in the first loop immediately after they
are created (either by write_ref_to_lockfile() or by close_ref()).
This means that there is never more than one open lockfile at a
time, preventing file descriptor exhaustion.
To simplify the bookkeeping across loops, add a new REF_NEEDS_COMMIT
bit to update->flags, which keeps track of whether the corresponding
lockfile needs to be committed, as opposed to just unlocked. (Since
"struct ref_update" is internal to the refs module, this change is not
visible to external callers.)
This change fixes two tests in t1400.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-24 11:35:49 +00:00
|
|
|
test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction creating branches does not burst open file limit' '
|
2015-04-14 22:25:06 +00:00
|
|
|
(
|
|
|
|
for i in $(test_seq 33)
|
|
|
|
do
|
|
|
|
echo "create refs/heads/$i HEAD"
|
|
|
|
done >large_input &&
|
|
|
|
run_with_limited_open_files git update-ref --stdin <large_input &&
|
|
|
|
git rev-parse --verify -q refs/heads/33
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
ref_transaction_commit(): fix atomicity and avoid fd exhaustion
The old code was roughly
for update in updates:
acquire locks and check old_sha
for update in updates:
if changing value:
write_ref_to_lockfile()
commit_ref_update()
for update in updates:
if deleting value:
unlink()
rewrite packed-refs file
for update in updates:
if reference still locked:
unlock_ref()
This has two problems.
Non-atomic updates
==================
The atomicity of the reference transaction depends on all pre-checks
being done in the first loop, before any changes have started being
committed in the second loop. The problem is that
write_ref_to_lockfile() (previously part of write_ref_sha1()), which
is called from the second loop, contains two more checks:
* It verifies that new_sha1 is a valid object
* If the reference being updated is a branch, it verifies that
new_sha1 points at a commit object (as opposed to a tag, tree, or
blob).
If either of these checks fails, the "transaction" is aborted during
the second loop. But this might happen after some reference updates
have already been permanently committed. In other words, the
all-or-nothing promise of "git update-ref --stdin" could be violated.
So these checks have to be moved to the first loop.
File descriptor exhaustion
==========================
The old code locked all of the references in the first loop, leaving
all of the lockfiles open until later loops. Since we might be
updating a lot of references, this could result in file descriptor
exhaustion.
The solution
============
After this patch, the code looks like
for update in updates:
acquire locks and check old_sha
if changing value:
write_ref_to_lockfile()
else:
close_ref()
for update in updates:
if changing value:
commit_ref_update()
for update in updates:
if deleting value:
unlink()
rewrite packed-refs file
for update in updates:
if reference still locked:
unlock_ref()
This fixes both problems:
1. The pre-checks in write_ref_to_lockfile() are now done in the first
loop, before any changes have been committed. If any of the checks
fails, the whole transaction can now be rolled back correctly.
2. All lockfiles are closed in the first loop immediately after they
are created (either by write_ref_to_lockfile() or by close_ref()).
This means that there is never more than one open lockfile at a
time, preventing file descriptor exhaustion.
To simplify the bookkeeping across loops, add a new REF_NEEDS_COMMIT
bit to update->flags, which keeps track of whether the corresponding
lockfile needs to be committed, as opposed to just unlocked. (Since
"struct ref_update" is internal to the refs module, this change is not
visible to external callers.)
This change fixes two tests in t1400.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-04-24 11:35:49 +00:00
|
|
|
test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction deleting branches does not burst open file limit' '
|
2015-04-14 22:25:06 +00:00
|
|
|
(
|
|
|
|
for i in $(test_seq 33)
|
|
|
|
do
|
|
|
|
echo "delete refs/heads/$i HEAD"
|
|
|
|
done >large_input &&
|
|
|
|
run_with_limited_open_files git update-ref --stdin <large_input &&
|
|
|
|
test_must_fail git rev-parse --verify -q refs/heads/33
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2015-09-01 02:13:11 +00:00
|
|
|
test_expect_success 'handle per-worktree refs in refs/bisect' '
|
|
|
|
git commit --allow-empty -m "initial commit" &&
|
|
|
|
git worktree add -b branch worktree &&
|
|
|
|
(
|
|
|
|
cd worktree &&
|
|
|
|
git commit --allow-empty -m "test commit" &&
|
|
|
|
git for-each-ref >for-each-ref.out &&
|
|
|
|
! grep refs/bisect for-each-ref.out &&
|
|
|
|
git update-ref refs/bisect/something HEAD &&
|
|
|
|
git rev-parse refs/bisect/something >../worktree-head &&
|
|
|
|
git for-each-ref | grep refs/bisect/something
|
|
|
|
) &&
|
2020-11-13 08:12:31 +00:00
|
|
|
git show-ref >actual &&
|
|
|
|
! grep 'refs/bisect' actual &&
|
2015-09-01 02:13:11 +00:00
|
|
|
test_must_fail git rev-parse refs/bisect/something &&
|
|
|
|
git update-ref refs/bisect/something HEAD &&
|
|
|
|
git rev-parse refs/bisect/something >main-head &&
|
|
|
|
! test_cmp main-head worktree-head
|
|
|
|
'
|
|
|
|
|
update-ref: implement interactive transaction handling
The git-update-ref(1) command can only handle queueing transactions
right now via its "--stdin" parameter, but there is no way for users to
handle the transaction itself in a more explicit way. E.g. in a
replicated scenario, one may imagine a coordinator that spawns
git-update-ref(1) for multiple repositories and only if all agree that
an update is possible will the coordinator send a commit. Such a
transactional session could look like
> start
< start: ok
> update refs/heads/master $OLD $NEW
> prepare
< prepare: ok
# All nodes have returned "ok"
> commit
< commit: ok
or
> start
< start: ok
> create refs/heads/master $OLD $NEW
> prepare
< fatal: cannot lock ref 'refs/heads/master': reference already exists
# On all other nodes:
> abort
< abort: ok
In order to allow for such transactional sessions, this commit
introduces four new commands for git-update-ref(1), which matches those
we have internally already with the exception of "start":
- start: start a new transaction
- prepare: prepare the transaction, that is try to lock all
references and verify their current value matches the
expected one
- commit: explicitly commit a session, that is update references to
match their new expected state
- abort: abort a session and roll back all changes
By design, git-update-ref(1) will commit as soon as standard input is
being closed. While fine in a non-transactional world, it is definitely
unexpected in a transactional world. Because of this, as soon as any of
the new transactional commands is used, the default will change to
aborting without an explicit "commit". To avoid a race between queueing
updates and the first "prepare" that starts a transaction, the "start"
command has been added to start an explicit transaction.
Add some tests to exercise this new functionality.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-02 07:10:02 +00:00
|
|
|
test_expect_success 'transaction handles empty commit' '
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
start
|
|
|
|
prepare
|
|
|
|
commit
|
|
|
|
EOF
|
|
|
|
git update-ref --stdin <stdin >actual &&
|
|
|
|
printf "%s: ok\n" start prepare commit >expect &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'transaction handles empty commit with missing prepare' '
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
start
|
|
|
|
commit
|
|
|
|
EOF
|
|
|
|
git update-ref --stdin <stdin >actual &&
|
|
|
|
printf "%s: ok\n" start commit >expect &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'transaction handles sole commit' '
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
commit
|
|
|
|
EOF
|
|
|
|
git update-ref --stdin <stdin >actual &&
|
|
|
|
printf "%s: ok\n" commit >expect &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'transaction handles empty abort' '
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
start
|
|
|
|
prepare
|
|
|
|
abort
|
|
|
|
EOF
|
|
|
|
git update-ref --stdin <stdin >actual &&
|
|
|
|
printf "%s: ok\n" start prepare abort >expect &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'transaction exits on multiple aborts' '
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
abort
|
|
|
|
abort
|
|
|
|
EOF
|
|
|
|
test_must_fail git update-ref --stdin <stdin >actual 2>err &&
|
|
|
|
printf "%s: ok\n" abort >expect &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
grep "fatal: transaction is closed" err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'transaction exits on start after prepare' '
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
prepare
|
|
|
|
start
|
|
|
|
EOF
|
|
|
|
test_must_fail git update-ref --stdin <stdin 2>err >actual &&
|
|
|
|
printf "%s: ok\n" prepare >expect &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
grep "fatal: prepared transactions can only be closed" err
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'transaction handles empty abort with missing prepare' '
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
start
|
|
|
|
abort
|
|
|
|
EOF
|
|
|
|
git update-ref --stdin <stdin >actual &&
|
|
|
|
printf "%s: ok\n" start abort >expect &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'transaction handles sole abort' '
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
abort
|
|
|
|
EOF
|
|
|
|
git update-ref --stdin <stdin >actual &&
|
|
|
|
printf "%s: ok\n" abort >expect &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'transaction can handle commit' '
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
start
|
|
|
|
create $a HEAD
|
|
|
|
commit
|
|
|
|
EOF
|
|
|
|
git update-ref --stdin <stdin >actual &&
|
|
|
|
printf "%s: ok\n" start commit >expect &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
git rev-parse HEAD >expect &&
|
|
|
|
git rev-parse $a >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'transaction can handle abort' '
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
start
|
|
|
|
create $b HEAD
|
|
|
|
abort
|
|
|
|
EOF
|
|
|
|
git update-ref --stdin <stdin >actual &&
|
|
|
|
printf "%s: ok\n" start abort >expect &&
|
|
|
|
test_cmp expect actual &&
|
2020-11-13 08:12:31 +00:00
|
|
|
test_must_fail git show-ref --verify -q $b
|
update-ref: implement interactive transaction handling
The git-update-ref(1) command can only handle queueing transactions
right now via its "--stdin" parameter, but there is no way for users to
handle the transaction itself in a more explicit way. E.g. in a
replicated scenario, one may imagine a coordinator that spawns
git-update-ref(1) for multiple repositories and only if all agree that
an update is possible will the coordinator send a commit. Such a
transactional session could look like
> start
< start: ok
> update refs/heads/master $OLD $NEW
> prepare
< prepare: ok
# All nodes have returned "ok"
> commit
< commit: ok
or
> start
< start: ok
> create refs/heads/master $OLD $NEW
> prepare
< fatal: cannot lock ref 'refs/heads/master': reference already exists
# On all other nodes:
> abort
< abort: ok
In order to allow for such transactional sessions, this commit
introduces four new commands for git-update-ref(1), which matches those
we have internally already with the exception of "start":
- start: start a new transaction
- prepare: prepare the transaction, that is try to lock all
references and verify their current value matches the
expected one
- commit: explicitly commit a session, that is update references to
match their new expected state
- abort: abort a session and roll back all changes
By design, git-update-ref(1) will commit as soon as standard input is
being closed. While fine in a non-transactional world, it is definitely
unexpected in a transactional world. Because of this, as soon as any of
the new transactional commands is used, the default will change to
aborting without an explicit "commit". To avoid a race between queueing
updates and the first "prepare" that starts a transaction, the "start"
command has been added to start an explicit transaction.
Add some tests to exercise this new functionality.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-02 07:10:02 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'transaction aborts by default' '
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
start
|
|
|
|
create $b HEAD
|
|
|
|
EOF
|
|
|
|
git update-ref --stdin <stdin >actual &&
|
|
|
|
printf "%s: ok\n" start >expect &&
|
|
|
|
test_cmp expect actual &&
|
2020-11-13 08:12:31 +00:00
|
|
|
test_must_fail git show-ref --verify -q $b
|
update-ref: implement interactive transaction handling
The git-update-ref(1) command can only handle queueing transactions
right now via its "--stdin" parameter, but there is no way for users to
handle the transaction itself in a more explicit way. E.g. in a
replicated scenario, one may imagine a coordinator that spawns
git-update-ref(1) for multiple repositories and only if all agree that
an update is possible will the coordinator send a commit. Such a
transactional session could look like
> start
< start: ok
> update refs/heads/master $OLD $NEW
> prepare
< prepare: ok
# All nodes have returned "ok"
> commit
< commit: ok
or
> start
< start: ok
> create refs/heads/master $OLD $NEW
> prepare
< fatal: cannot lock ref 'refs/heads/master': reference already exists
# On all other nodes:
> abort
< abort: ok
In order to allow for such transactional sessions, this commit
introduces four new commands for git-update-ref(1), which matches those
we have internally already with the exception of "start":
- start: start a new transaction
- prepare: prepare the transaction, that is try to lock all
references and verify their current value matches the
expected one
- commit: explicitly commit a session, that is update references to
match their new expected state
- abort: abort a session and roll back all changes
By design, git-update-ref(1) will commit as soon as standard input is
being closed. While fine in a non-transactional world, it is definitely
unexpected in a transactional world. Because of this, as soon as any of
the new transactional commands is used, the default will change to
aborting without an explicit "commit". To avoid a race between queueing
updates and the first "prepare" that starts a transaction, the "start"
command has been added to start an explicit transaction.
Add some tests to exercise this new functionality.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-02 07:10:02 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'transaction with prepare aborts by default' '
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
start
|
|
|
|
create $b HEAD
|
|
|
|
prepare
|
|
|
|
EOF
|
|
|
|
git update-ref --stdin <stdin >actual &&
|
|
|
|
printf "%s: ok\n" start prepare >expect &&
|
|
|
|
test_cmp expect actual &&
|
2020-11-13 08:12:31 +00:00
|
|
|
test_must_fail git show-ref --verify -q $b
|
update-ref: implement interactive transaction handling
The git-update-ref(1) command can only handle queueing transactions
right now via its "--stdin" parameter, but there is no way for users to
handle the transaction itself in a more explicit way. E.g. in a
replicated scenario, one may imagine a coordinator that spawns
git-update-ref(1) for multiple repositories and only if all agree that
an update is possible will the coordinator send a commit. Such a
transactional session could look like
> start
< start: ok
> update refs/heads/master $OLD $NEW
> prepare
< prepare: ok
# All nodes have returned "ok"
> commit
< commit: ok
or
> start
< start: ok
> create refs/heads/master $OLD $NEW
> prepare
< fatal: cannot lock ref 'refs/heads/master': reference already exists
# On all other nodes:
> abort
< abort: ok
In order to allow for such transactional sessions, this commit
introduces four new commands for git-update-ref(1), which matches those
we have internally already with the exception of "start":
- start: start a new transaction
- prepare: prepare the transaction, that is try to lock all
references and verify their current value matches the
expected one
- commit: explicitly commit a session, that is update references to
match their new expected state
- abort: abort a session and roll back all changes
By design, git-update-ref(1) will commit as soon as standard input is
being closed. While fine in a non-transactional world, it is definitely
unexpected in a transactional world. Because of this, as soon as any of
the new transactional commands is used, the default will change to
aborting without an explicit "commit". To avoid a race between queueing
updates and the first "prepare" that starts a transaction, the "start"
command has been added to start an explicit transaction.
Add some tests to exercise this new functionality.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-02 07:10:02 +00:00
|
|
|
'
|
|
|
|
|
2020-11-13 08:12:36 +00:00
|
|
|
test_expect_success 'transaction can commit multiple times' '
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
start
|
|
|
|
create refs/heads/branch-1 $A
|
|
|
|
commit
|
|
|
|
start
|
|
|
|
create refs/heads/branch-2 $B
|
|
|
|
commit
|
|
|
|
EOF
|
|
|
|
git update-ref --stdin <stdin >actual &&
|
|
|
|
printf "%s: ok\n" start commit start commit >expect &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
echo "$A" >expect &&
|
|
|
|
git rev-parse refs/heads/branch-1 >actual &&
|
|
|
|
test_cmp expect actual &&
|
|
|
|
echo "$B" >expect &&
|
|
|
|
git rev-parse refs/heads/branch-2 >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'transaction can create and delete' '
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
start
|
|
|
|
create refs/heads/create-and-delete $A
|
|
|
|
commit
|
|
|
|
start
|
|
|
|
delete refs/heads/create-and-delete $A
|
|
|
|
commit
|
|
|
|
EOF
|
|
|
|
git update-ref --stdin <stdin >actual &&
|
|
|
|
printf "%s: ok\n" start commit start commit >expect &&
|
|
|
|
test_must_fail git show-ref --verify refs/heads/create-and-delete
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'transaction can commit after abort' '
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
start
|
|
|
|
create refs/heads/abort $A
|
|
|
|
abort
|
|
|
|
start
|
|
|
|
create refs/heads/abort $A
|
|
|
|
commit
|
|
|
|
EOF
|
|
|
|
git update-ref --stdin <stdin >actual &&
|
|
|
|
printf "%s: ok\n" start abort start commit >expect &&
|
|
|
|
echo "$A" >expect &&
|
|
|
|
git rev-parse refs/heads/abort >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2020-11-13 08:12:45 +00:00
|
|
|
test_expect_success 'transaction cannot restart ongoing transaction' '
|
|
|
|
cat >stdin <<-EOF &&
|
|
|
|
start
|
|
|
|
create refs/heads/restart $A
|
|
|
|
start
|
|
|
|
commit
|
|
|
|
EOF
|
|
|
|
test_must_fail git update-ref --stdin <stdin >actual &&
|
|
|
|
test_must_fail git show-ref --verify refs/heads/restart
|
|
|
|
'
|
|
|
|
|
2006-05-17 09:55:40 +00:00
|
|
|
test_done
|