2009-01-29 11:33:02 +03:00
|
|
|
#!/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' '
|
2010-10-31 04:46:54 +03:00
|
|
|
echo content >file && git add file && git commit -m one &&
|
2015-12-22 18:05:47 +03:00
|
|
|
test_must_fail git symbolic-ref HEAD $(git rev-parse HEAD)
|
2009-01-29 11:33:02 +03:00
|
|
|
'
|
|
|
|
reset_to_sane
|
|
|
|
|
2012-10-21 15:32:33 +04:00
|
|
|
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
|
|
|
|
|
2015-09-25 00:07:22 +03:00
|
|
|
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
|
|
|
|
'
|
|
|
|
|
2015-12-20 10:27:18 +03:00
|
|
|
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
|
|
|
|
'
|
|
|
|
|
2015-12-20 10:27:23 +03:00
|
|
|
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
|
2016-06-03 23:42:35 +03:00
|
|
|
git log --format=%gs -g -2 >actual &&
|
2015-12-20 10:27:23 +03:00
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
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 08:57:01 +03:00
|
|
|
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
|
|
|
|
'
|
|
|
|
|
lock_ref_sha1_basic: handle REF_NODEREF with invalid refs
We sometimes call lock_ref_sha1_basic with REF_NODEREF
to operate directly on a symbolic ref. This is used, for
example, to move to a detached HEAD, or when updating
the contents of HEAD via checkout or symbolic-ref.
However, the first step of the function is to resolve the
refname to get the "old" sha1, and we do so without telling
resolve_ref_unsafe() that we are only interested in the
symref. As a result, we may detect a problem there not with
the symref itself, but with something it points to.
The real-world example I found (and what is used in the test
suite) is a HEAD pointing to a ref that cannot exist,
because it would cause a directory/file conflict with other
existing refs. This situation is somewhat broken, of
course, as trying to _commit_ on that HEAD would fail. But
it's not explicitly forbidden, and we should be able to move
away from it. However, neither "git checkout" nor "git
symbolic-ref" can do so. We try to take the lock on HEAD,
which is pointing to a non-existent ref. We bail from
resolve_ref_unsafe() with errno set to EISDIR, and the lock
code thinks we are attempting to create a d/f conflict.
Of course we're not. The problem is that the lock code has
no idea what level we were at when we got EISDIR, so trying
to diagnose or remove empty directories for HEAD is not
useful.
To make things even more complicated, we only get EISDIR in
the loose-ref case. If the refs are packed, the resolution
may "succeed", giving us the pointed-to ref in "refname",
but a null oid. Later, we say "ah, the null oid means we are
creating; let's make sure there is room for it", but
mistakenly check against the _resolved_ refname, not the
original.
We can fix this by making two tweaks:
1. Call resolve_ref_unsafe() with RESOLVE_REF_NO_RECURSE
when REF_NODEREF is set. This means any errors
we get will be from the orig_refname, and we can act
accordingly.
We already do this in the REF_DELETING case, but we
should do it for update, too.
2. If we do get a "refname" return from
resolve_ref_unsafe(), even with RESOLVE_REF_NO_RECURSE
it may be the name of the ref pointed-to by a symref.
We already normalize this back to orig_refname before
taking the lockfile, but we need to do so before the
null_oid check.
While we're rearranging the REF_NODEREF handling, we can
also bump the initialization of lflags to the top of the
function, where we are setting up other flags. This saves us
from having yet another conditional block on REF_NODEREF
just to set it later.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-01-13 00:45:09 +03:00
|
|
|
test_expect_success 'symbolic-ref handles existing pointer to invalid name' '
|
|
|
|
head=$(git rev-parse HEAD) &&
|
|
|
|
git symbolic-ref HEAD refs/heads/outer &&
|
|
|
|
git update-ref refs/heads/outer/inner $head &&
|
|
|
|
git symbolic-ref HEAD refs/heads/unrelated
|
|
|
|
'
|
|
|
|
|
2009-01-29 11:33:02 +03:00
|
|
|
test_done
|