git-commit-vandalism/t/t1401-symbolic-ref.sh
Jeff King 370e5ad65e create_symref: use existing ref-lock code
The create_symref() function predates the existence of
"struct lock_file", let alone the more recent "struct
ref_lock". Instead, it just does its own manual dot-locking.
Besides being more code, this has a few downsides:

 - if git is interrupted while holding the lock, we don't
   clean up the lockfile

 - we don't do the usual directory/filename conflict check.
   So you can sometimes create a symref "refs/heads/foo/bar",
   even if "refs/heads/foo" exists (namely, if the refs are
   packed and we do not hit the d/f conflict in the
   filesystem).

This patch refactors create_symref() to use the "struct
ref_lock" interface, which handles both of these things.
There are a few bonus cleanups that come along with it:

 - we leaked ref_path in some error cases

 - the symref contents were stored in a fixed-size buffer,
   putting an artificial (albeit large) limitation on the
   length of the refname. We now write through fprintf, and
   handle refnames of any size.

 - we called adjust_shared_perm only after the file was
   renamed into place, creating a potential race with
   readers in a shared repository. The lockfile code now
   handles this when creating the lockfile, making it
   atomic.

 - the legacy prefer_symlink_refs path did not do any
   locking at all. Admittedly, it is not atomic from a
   reader's perspective (as it unlinks and re-creates the
   symlink to overwrite), but at least it cannot conflict
   with other writers now.

 - the result of this patch is hopefully more readable. It
   eliminates three goto labels. Two were for error checking
   that is now simplified, and the third was to reach shared
   code that has been pulled into its own function.

Signed-off-by: Jeff King <peff@peff.net>
Reviewed-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-12-29 10:33:31 -08:00

126 lines
3.4 KiB
Bash
Executable File

#!/bin/sh
test_description='basic symbolic-ref tests'
. ./test-lib.sh
# If the tests munging HEAD fail, they can break detection of
# the git repo, meaning that further tests will operate on
# the surrounding git repo instead of the trash directory.
reset_to_sane() {
echo ref: refs/heads/foo >.git/HEAD
}
test_expect_success 'symbolic-ref writes HEAD' '
git symbolic-ref HEAD refs/heads/foo &&
echo ref: refs/heads/foo >expect &&
test_cmp expect .git/HEAD
'
test_expect_success 'symbolic-ref reads HEAD' '
echo refs/heads/foo >expect &&
git symbolic-ref HEAD >actual &&
test_cmp expect actual
'
test_expect_success 'symbolic-ref refuses non-ref for HEAD' '
test_must_fail git symbolic-ref HEAD foo
'
reset_to_sane
test_expect_success 'symbolic-ref refuses bare sha1' '
echo content >file && git add file && git commit -m one &&
test_must_fail git symbolic-ref HEAD `git rev-parse HEAD`
'
reset_to_sane
test_expect_success 'symbolic-ref deletes HEAD' '
git symbolic-ref -d HEAD &&
test_path_is_file .git/refs/heads/foo &&
test_path_is_missing .git/HEAD
'
reset_to_sane
test_expect_success 'symbolic-ref deletes dangling HEAD' '
git symbolic-ref HEAD refs/heads/missing &&
git symbolic-ref -d HEAD &&
test_path_is_missing .git/refs/heads/missing &&
test_path_is_missing .git/HEAD
'
reset_to_sane
test_expect_success 'symbolic-ref fails to delete missing FOO' '
echo "fatal: Cannot delete FOO, not a symbolic ref" >expect &&
test_must_fail git symbolic-ref -d FOO >actual 2>&1 &&
test_cmp expect actual
'
reset_to_sane
test_expect_success 'symbolic-ref fails to delete real ref' '
echo "fatal: Cannot delete refs/heads/foo, not a symbolic ref" >expect &&
test_must_fail git symbolic-ref -d refs/heads/foo >actual 2>&1 &&
test_path_is_file .git/refs/heads/foo &&
test_cmp expect actual
'
reset_to_sane
test_expect_success 'create large ref name' '
# make 256+ character ref; some systems may not handle that,
# so be gentle
long=0123456789abcdef &&
long=$long/$long/$long/$long &&
long=$long/$long/$long/$long &&
long_ref=refs/heads/$long &&
tree=$(git write-tree) &&
commit=$(echo foo | git commit-tree $tree) &&
if git update-ref $long_ref $commit; then
test_set_prereq LONG_REF
else
echo >&2 "long refs not supported"
fi
'
test_expect_success LONG_REF 'symbolic-ref can point to large ref name' '
git symbolic-ref HEAD $long_ref &&
echo $long_ref >expect &&
git symbolic-ref HEAD >actual &&
test_cmp expect actual
'
test_expect_success LONG_REF 'we can parse long symbolic ref' '
echo $commit >expect &&
git rev-parse --verify HEAD >actual &&
test_cmp expect actual
'
test_expect_success 'symbolic-ref reports failure in exit code' '
test_when_finished "rm -f .git/HEAD.lock" &&
>.git/HEAD.lock &&
test_must_fail git symbolic-ref HEAD refs/heads/whatever
'
test_expect_success 'symbolic-ref writes reflog entry' '
git checkout -b log1 &&
test_commit one &&
git checkout -b log2 &&
test_commit two &&
git checkout --orphan orphan &&
git symbolic-ref -m create HEAD refs/heads/log1 &&
git symbolic-ref -m update HEAD refs/heads/log2 &&
cat >expect <<-\EOF &&
update
create
EOF
git log --format=%gs -g >actual &&
test_cmp expect actual
'
test_expect_success 'symbolic-ref does not create ref d/f conflicts' '
git checkout -b df &&
test_commit df &&
test_must_fail git symbolic-ref refs/heads/df/conflict refs/heads/df &&
git pack-refs --all --prune &&
test_must_fail git symbolic-ref refs/heads/df/conflict refs/heads/df
'
test_done