2006-09-28 04:58:03 +00:00
|
|
|
#!/bin/sh
|
|
|
|
#
|
|
|
|
# Copyright (c) 2005 Amos Waterland
|
|
|
|
# Copyright (c) 2006 Christian Couder
|
|
|
|
#
|
|
|
|
|
|
|
|
test_description='git pack-refs should not change the branch semantic
|
|
|
|
|
|
|
|
This test runs git pack-refs and git show-ref and checks that the branch
|
|
|
|
semantic is still the same.
|
|
|
|
'
|
2024-04-12 04:44:48 +00:00
|
|
|
|
2020-11-18 23:44:23 +00:00
|
|
|
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
|
tests: mark tests relying on the current default for `init.defaultBranch`
In addition to the manual adjustment to let the `linux-gcc` CI job run
the test suite with `master` and then with `main`, this patch makes sure
that GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME is set in all test scripts
that currently rely on the initial branch name being `master by default.
To determine which test scripts to mark up, the first step was to
force-set the default branch name to `master` in
- all test scripts that contain the keyword `master`,
- t4211, which expects `t/t4211/history.export` with a hard-coded ref to
initialize the default branch,
- t5560 because it sources `t/t556x_common` which uses `master`,
- t8002 and t8012 because both source `t/annotate-tests.sh` which also
uses `master`)
This trick was performed by this command:
$ sed -i '/^ *\. \.\/\(test-lib\|lib-\(bash\|cvs\|git-svn\)\|gitweb-lib\)\.sh$/i\
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master\
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\
' $(git grep -l master t/t[0-9]*.sh) \
t/t4211*.sh t/t5560*.sh t/t8002*.sh t/t8012*.sh
After that, careful, manual inspection revealed that some of the test
scripts containing the needle `master` do not actually rely on a
specific default branch name: either they mention `master` only in a
comment, or they initialize that branch specificially, or they do not
actually refer to the current default branch. Therefore, the
aforementioned modification was undone in those test scripts thusly:
$ git checkout HEAD -- \
t/t0027-auto-crlf.sh t/t0060-path-utils.sh \
t/t1011-read-tree-sparse-checkout.sh \
t/t1305-config-include.sh t/t1309-early-config.sh \
t/t1402-check-ref-format.sh t/t1450-fsck.sh \
t/t2024-checkout-dwim.sh \
t/t2106-update-index-assume-unchanged.sh \
t/t3040-subprojects-basic.sh t/t3301-notes.sh \
t/t3308-notes-merge.sh t/t3423-rebase-reword.sh \
t/t3436-rebase-more-options.sh \
t/t4015-diff-whitespace.sh t/t4257-am-interactive.sh \
t/t5323-pack-redundant.sh t/t5401-update-hooks.sh \
t/t5511-refspec.sh t/t5526-fetch-submodules.sh \
t/t5529-push-errors.sh t/t5530-upload-pack-error.sh \
t/t5548-push-porcelain.sh \
t/t5552-skipping-fetch-negotiator.sh \
t/t5572-pull-submodule.sh t/t5608-clone-2gb.sh \
t/t5614-clone-submodules-shallow.sh \
t/t7508-status.sh t/t7606-merge-custom.sh \
t/t9302-fast-import-unpack-limit.sh
We excluded one set of test scripts in these commands, though: the range
of `git p4` tests. The reason? `git p4` stores the (foreign) remote
branch in the branch called `p4/master`, which is obviously not the
default branch. Manual analysis revealed that only five of these tests
actually require a specific default branch name to pass; They were
modified thusly:
$ sed -i '/^ *\. \.\/lib-git-p4\.sh$/i\
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master\
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\
' t/t980[0167]*.sh t/t9811*.sh
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-11-18 23:44:19 +00:00
|
|
|
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
|
2024-04-12 04:44:48 +00:00
|
|
|
GIT_TEST_DEFAULT_REF_FORMAT=files
|
|
|
|
export GIT_TEST_DEFAULT_REF_FORMAT
|
tests: mark tests relying on the current default for `init.defaultBranch`
In addition to the manual adjustment to let the `linux-gcc` CI job run
the test suite with `master` and then with `main`, this patch makes sure
that GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME is set in all test scripts
that currently rely on the initial branch name being `master by default.
To determine which test scripts to mark up, the first step was to
force-set the default branch name to `master` in
- all test scripts that contain the keyword `master`,
- t4211, which expects `t/t4211/history.export` with a hard-coded ref to
initialize the default branch,
- t5560 because it sources `t/t556x_common` which uses `master`,
- t8002 and t8012 because both source `t/annotate-tests.sh` which also
uses `master`)
This trick was performed by this command:
$ sed -i '/^ *\. \.\/\(test-lib\|lib-\(bash\|cvs\|git-svn\)\|gitweb-lib\)\.sh$/i\
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master\
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\
' $(git grep -l master t/t[0-9]*.sh) \
t/t4211*.sh t/t5560*.sh t/t8002*.sh t/t8012*.sh
After that, careful, manual inspection revealed that some of the test
scripts containing the needle `master` do not actually rely on a
specific default branch name: either they mention `master` only in a
comment, or they initialize that branch specificially, or they do not
actually refer to the current default branch. Therefore, the
aforementioned modification was undone in those test scripts thusly:
$ git checkout HEAD -- \
t/t0027-auto-crlf.sh t/t0060-path-utils.sh \
t/t1011-read-tree-sparse-checkout.sh \
t/t1305-config-include.sh t/t1309-early-config.sh \
t/t1402-check-ref-format.sh t/t1450-fsck.sh \
t/t2024-checkout-dwim.sh \
t/t2106-update-index-assume-unchanged.sh \
t/t3040-subprojects-basic.sh t/t3301-notes.sh \
t/t3308-notes-merge.sh t/t3423-rebase-reword.sh \
t/t3436-rebase-more-options.sh \
t/t4015-diff-whitespace.sh t/t4257-am-interactive.sh \
t/t5323-pack-redundant.sh t/t5401-update-hooks.sh \
t/t5511-refspec.sh t/t5526-fetch-submodules.sh \
t/t5529-push-errors.sh t/t5530-upload-pack-error.sh \
t/t5548-push-porcelain.sh \
t/t5552-skipping-fetch-negotiator.sh \
t/t5572-pull-submodule.sh t/t5608-clone-2gb.sh \
t/t5614-clone-submodules-shallow.sh \
t/t7508-status.sh t/t7606-merge-custom.sh \
t/t9302-fast-import-unpack-limit.sh
We excluded one set of test scripts in these commands, though: the range
of `git p4` tests. The reason? `git p4` stores the (foreign) remote
branch in the branch called `p4/master`, which is obviously not the
default branch. Manual analysis revealed that only five of these tests
actually require a specific default branch name to pass; They were
modified thusly:
$ sed -i '/^ *\. \.\/lib-git-p4\.sh$/i\
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master\
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\
' t/t980[0167]*.sh t/t9811*.sh
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-11-18 23:44:19 +00:00
|
|
|
|
tests: mark tests as passing with SANITIZE=leak
When the "ab/various-leak-fixes" topic was merged in [1] only t6021
would fail if the tests were run in the
"GIT_TEST_PASSING_SANITIZE_LEAK=check" mode, i.e. to check whether we
marked all leak-free tests with "TEST_PASSES_SANITIZE_LEAK=true".
Since then we've had various tests starting to pass under
SANITIZE=leak. Let's mark those as passing, this is when they started
to pass, narrowed down with "git bisect":
- t5317-pack-objects-filter-objects.sh: In
faebba436e6 (list-objects-filter: plug pattern_list leak, 2022-12-01).
- t3210-pack-refs.sh, t5613-info-alternate.sh,
t7403-submodule-sync.sh: In 189e97bc4ba (diff: remove parseopts member
from struct diff_options, 2022-12-01).
- t1408-packed-refs.sh: In ab91f6b7c42 (Merge branch
'rs/diff-parseopts', 2022-12-19).
- t0023-crlf-am.sh, t4152-am-subjects.sh, t4254-am-corrupt.sh,
t4256-am-format-flowed.sh, t4257-am-interactive.sh,
t5403-post-checkout-hook.sh: In a658e881c13 (am: don't pass strvec to
apply_parse_options(), 2022-12-13)
- t1301-shared-repo.sh, t1302-repo-version.sh: In b07a819c05f (reflog:
clear leftovers in reflog_expiry_cleanup(), 2022-12-13).
- t1304-default-acl.sh, t1410-reflog.sh,
t5330-no-lazy-fetch-with-commit-graph.sh, t5502-quickfetch.sh,
t5604-clone-reference.sh, t6014-rev-list-all.sh,
t7701-repack-unpack-unreachable.sh: In b0c61be3209 (Merge branch
'rs/reflog-expiry-cleanup', 2022-12-26)
- t3800-mktag.sh, t5302-pack-index.sh, t5306-pack-nobase.sh,
t5573-pull-verify-signatures.sh, t7612-merge-verify-signatures.sh: In
69bbbe484ba (hash-object: use fsck for object checks, 2023-01-18).
- t1451-fsck-buffer.sh: In 8e4309038f0 (fsck: do not assume
NUL-termination of buffers, 2023-01-19).
- t6501-freshen-objects.sh: In abf2bb895b4 (Merge branch
'jk/hash-object-fsck', 2023-01-30)
1. 9ea1378d046 (Merge branch 'ab/various-leak-fixes', 2022-12-14)
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-02-06 23:07:36 +00:00
|
|
|
TEST_PASSES_SANITIZE_LEAK=true
|
2006-09-28 04:58:03 +00:00
|
|
|
. ./test-lib.sh
|
|
|
|
|
refs: speed up is_refname_available
Our filesystem ref storage does not allow D/F conflicts; so
if "refs/heads/a/b" exists, we do not allow "refs/heads/a"
to exist (and vice versa). This falls out naturally for
loose refs, where the filesystem enforces the condition. But
for packed-refs, we have to make the check ourselves.
We do so by iterating over the entire packed-refs namespace
and checking whether each name creates a conflict. If you
have a very large number of refs, this is quite inefficient,
as you end up doing a large number of comparisons with
uninteresting bits of the ref tree (e.g., we know that all
of "refs/tags" is uninteresting in the example above, yet we
check each entry in it).
Instead, let's take advantage of the fact that we have the
packed refs stored as a trie of ref_entry structs. We can
find each component of the proposed refname as we walk
through the trie, checking for D/F conflicts as we go. For a
refname of depth N (i.e., 4 in the above example), we only
have to visit N nodes. And at each visit, we can binary
search the M names at that level, for a total complexity of
O(N lg M). ("M" is different at each level, of course, but
we can take the worst-case "M" as a bound).
In a pathological case of fetching 30,000 fresh refs into a
repository with 8.5 million refs, this dropped the time to
run "git fetch" from tens of minutes to ~30s.
This may also help smaller cases in which we check against
loose refs (which we do when renaming a ref), as we may
avoid a disk access for unrelated loose directories.
Note that the tests we add appear at first glance to be
redundant with what is already in t3210. However, the early
tests are not robust; they are run with reflogs turned on,
meaning that we are not actually testing
is_refname_available at all! The operations will still fail
because the reflogs will hit D/F conflicts in the
filesystem. To get a true test, we must turn off reflogs
(but we don't want to do so for the entire script, because
the point of turning them on was to cover some other cases).
Reviewed-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-09-10 11:11:55 +00:00
|
|
|
test_expect_success 'enable reflogs' '
|
|
|
|
git config core.logallrefupdates true
|
|
|
|
'
|
2006-10-19 08:28:47 +00:00
|
|
|
|
2023-05-18 20:03:08 +00:00
|
|
|
test_expect_success 'prepare a trivial repository' '
|
|
|
|
echo Hello > A &&
|
|
|
|
git update-index --add A &&
|
|
|
|
git commit -m "Initial commit." &&
|
|
|
|
HEAD=$(git rev-parse --verify HEAD)
|
|
|
|
'
|
2006-09-28 04:58:03 +00:00
|
|
|
|
2024-03-25 10:02:59 +00:00
|
|
|
test_expect_success 'pack-refs --prune --all' '
|
|
|
|
test_path_is_missing .git/packed-refs &&
|
|
|
|
git pack-refs --no-prune --all &&
|
|
|
|
test_path_is_file .git/packed-refs &&
|
|
|
|
N=$(find .git/refs -type f | wc -l) &&
|
2024-01-19 20:18:53 +00:00
|
|
|
test "$N" != 0 &&
|
2024-03-25 10:02:59 +00:00
|
|
|
|
|
|
|
git pack-refs --prune --all &&
|
|
|
|
test_path_is_file .git/packed-refs &&
|
|
|
|
N=$(find .git/refs -type f) &&
|
2024-01-19 20:18:53 +00:00
|
|
|
test -z "$N"
|
|
|
|
'
|
|
|
|
|
2006-09-28 04:58:03 +00:00
|
|
|
SHA1=
|
|
|
|
|
2023-05-18 20:03:08 +00:00
|
|
|
test_expect_success 'see if git show-ref works as expected' '
|
|
|
|
git branch a &&
|
|
|
|
SHA1=$(cat .git/refs/heads/a) &&
|
|
|
|
echo "$SHA1 refs/heads/a" >expect &&
|
|
|
|
git show-ref a >result &&
|
|
|
|
test_cmp expect result
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'see if a branch still exists when packed' '
|
|
|
|
git branch b &&
|
|
|
|
git pack-refs --all &&
|
|
|
|
rm -f .git/refs/heads/b &&
|
|
|
|
echo "$SHA1 refs/heads/b" >expect &&
|
|
|
|
git show-ref b >result &&
|
|
|
|
test_cmp expect result
|
|
|
|
'
|
2006-09-28 04:58:03 +00:00
|
|
|
|
2008-02-01 09:50:53 +00:00
|
|
|
test_expect_success 'git branch c/d should barf if branch c exists' '
|
2023-05-18 20:03:08 +00:00
|
|
|
git branch c &&
|
|
|
|
git pack-refs --all &&
|
|
|
|
rm -f .git/refs/heads/c &&
|
|
|
|
test_must_fail git branch c/d
|
2008-02-01 09:50:53 +00:00
|
|
|
'
|
2006-09-28 04:58:03 +00:00
|
|
|
|
2023-05-18 20:03:08 +00:00
|
|
|
test_expect_success 'see if a branch still exists after git pack-refs --prune' '
|
|
|
|
git branch e &&
|
|
|
|
git pack-refs --all --prune &&
|
|
|
|
echo "$SHA1 refs/heads/e" >expect &&
|
|
|
|
git show-ref e >result &&
|
|
|
|
test_cmp expect result
|
|
|
|
'
|
2006-09-28 04:58:03 +00:00
|
|
|
|
2008-02-01 09:50:53 +00:00
|
|
|
test_expect_success 'see if git pack-refs --prune remove ref files' '
|
2023-05-18 20:03:08 +00:00
|
|
|
git branch f &&
|
|
|
|
git pack-refs --all --prune &&
|
|
|
|
! test -f .git/refs/heads/f
|
2008-02-01 09:50:53 +00:00
|
|
|
'
|
2006-09-28 04:58:03 +00:00
|
|
|
|
2010-07-06 23:29:19 +00:00
|
|
|
test_expect_success 'see if git pack-refs --prune removes empty dirs' '
|
2023-05-18 20:03:08 +00:00
|
|
|
git branch r/s/t &&
|
|
|
|
git pack-refs --all --prune &&
|
|
|
|
! test -e .git/refs/heads/r
|
2010-07-06 23:29:19 +00:00
|
|
|
'
|
|
|
|
|
2023-05-18 20:03:08 +00:00
|
|
|
test_expect_success 'git branch g should work when git branch g/h has been deleted' '
|
|
|
|
git branch g/h &&
|
|
|
|
git pack-refs --all --prune &&
|
|
|
|
git branch -d g/h &&
|
|
|
|
git branch g &&
|
|
|
|
git pack-refs --all &&
|
|
|
|
git branch -d g
|
|
|
|
'
|
2006-09-28 04:58:03 +00:00
|
|
|
|
2008-02-01 09:50:53 +00:00
|
|
|
test_expect_success 'git branch i/j/k should barf if branch i exists' '
|
2023-05-18 20:03:08 +00:00
|
|
|
git branch i &&
|
|
|
|
git pack-refs --all --prune &&
|
|
|
|
test_must_fail git branch i/j/k
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'test git branch k after branch k/l/m and k/lm have been deleted' '
|
|
|
|
git branch k/l &&
|
|
|
|
git branch k/lm &&
|
|
|
|
git branch -d k/l &&
|
|
|
|
git branch k/l/m &&
|
|
|
|
git branch -d k/l/m &&
|
|
|
|
git branch -d k/lm &&
|
|
|
|
git branch k
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'test git branch n after some branch deletion and pruning' '
|
|
|
|
git branch n/o &&
|
|
|
|
git branch n/op &&
|
|
|
|
git branch -d n/o &&
|
|
|
|
git branch n/o/p &&
|
|
|
|
git branch -d n/op &&
|
|
|
|
git pack-refs --all --prune &&
|
|
|
|
git branch -d n/o/p &&
|
|
|
|
git branch n
|
|
|
|
'
|
2006-10-01 12:38:18 +00:00
|
|
|
|
2023-05-12 21:34:41 +00:00
|
|
|
test_expect_success 'test excluded refs are not packed' '
|
|
|
|
git branch dont_pack1 &&
|
|
|
|
git branch dont_pack2 &&
|
|
|
|
git branch pack_this &&
|
|
|
|
git pack-refs --all --exclude "refs/heads/dont_pack*" &&
|
|
|
|
test -f .git/refs/heads/dont_pack1 &&
|
|
|
|
test -f .git/refs/heads/dont_pack2 &&
|
|
|
|
! test -f .git/refs/heads/pack_this'
|
|
|
|
|
|
|
|
test_expect_success 'test --no-exclude refs clears excluded refs' '
|
|
|
|
git branch dont_pack3 &&
|
|
|
|
git branch dont_pack4 &&
|
|
|
|
git pack-refs --all --exclude "refs/heads/dont_pack*" --no-exclude &&
|
|
|
|
! test -f .git/refs/heads/dont_pack3 &&
|
|
|
|
! test -f .git/refs/heads/dont_pack4'
|
|
|
|
|
2023-05-12 21:34:42 +00:00
|
|
|
test_expect_success 'test only included refs are packed' '
|
|
|
|
git branch pack_this1 &&
|
|
|
|
git branch pack_this2 &&
|
|
|
|
git tag dont_pack5 &&
|
|
|
|
git pack-refs --include "refs/heads/pack_this*" &&
|
|
|
|
test -f .git/refs/tags/dont_pack5 &&
|
|
|
|
! test -f .git/refs/heads/pack_this1 &&
|
|
|
|
! test -f .git/refs/heads/pack_this2'
|
|
|
|
|
|
|
|
test_expect_success 'test --no-include refs clears included refs' '
|
|
|
|
git branch pack1 &&
|
|
|
|
git branch pack2 &&
|
|
|
|
git pack-refs --include "refs/heads/pack*" --no-include &&
|
|
|
|
test -f .git/refs/heads/pack1 &&
|
|
|
|
test -f .git/refs/heads/pack2'
|
|
|
|
|
|
|
|
test_expect_success 'test --exclude takes precedence over --include' '
|
|
|
|
git branch dont_pack5 &&
|
|
|
|
git pack-refs --include "refs/heads/pack*" --exclude "refs/heads/pack*" &&
|
|
|
|
test -f .git/refs/heads/dont_pack5'
|
|
|
|
|
2024-03-25 10:03:20 +00:00
|
|
|
test_expect_success '--auto packs and prunes refs as usual' '
|
|
|
|
git branch auto &&
|
|
|
|
test_path_is_file .git/refs/heads/auto &&
|
|
|
|
git pack-refs --auto --all &&
|
|
|
|
test_path_is_missing .git/refs/heads/auto
|
|
|
|
'
|
|
|
|
|
2023-05-18 20:03:08 +00:00
|
|
|
test_expect_success 'see if up-to-date packed refs are preserved' '
|
|
|
|
git branch q &&
|
|
|
|
git pack-refs --all --prune &&
|
|
|
|
git update-ref refs/heads/q refs/heads/q &&
|
|
|
|
! test -f .git/refs/heads/q
|
|
|
|
'
|
2008-11-05 20:55:53 +00:00
|
|
|
|
2007-01-26 00:51:21 +00:00
|
|
|
test_expect_success 'pack, prune and repack' '
|
2008-09-03 08:59:27 +00:00
|
|
|
git tag foo &&
|
2007-07-03 05:52:14 +00:00
|
|
|
git pack-refs --all --prune &&
|
|
|
|
git show-ref >all-of-them &&
|
|
|
|
git pack-refs &&
|
|
|
|
git show-ref >again &&
|
2010-05-14 09:31:37 +00:00
|
|
|
test_cmp all-of-them again
|
2007-01-26 00:51:21 +00:00
|
|
|
'
|
|
|
|
|
2013-04-22 19:52:24 +00:00
|
|
|
test_expect_success 'explicit pack-refs with dangling packed reference' '
|
|
|
|
git commit --allow-empty -m "soon to be garbage-collected" &&
|
|
|
|
git pack-refs --all &&
|
|
|
|
git reset --hard HEAD^ &&
|
|
|
|
git reflog expire --expire=all --all &&
|
|
|
|
git prune --expire=all &&
|
|
|
|
git pack-refs --all 2>result &&
|
2018-08-19 21:57:24 +00:00
|
|
|
test_must_be_empty result
|
2013-04-22 19:52:24 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'delete ref with dangling packed version' '
|
|
|
|
git checkout -b lamb &&
|
|
|
|
git commit --allow-empty -m "future garbage" &&
|
|
|
|
git pack-refs --all &&
|
|
|
|
git reset --hard HEAD^ &&
|
2020-11-18 23:44:23 +00:00
|
|
|
git checkout main &&
|
2013-04-22 19:52:24 +00:00
|
|
|
git reflog expire --expire=all --all &&
|
|
|
|
git prune --expire=all &&
|
|
|
|
git branch -d lamb 2>result &&
|
2018-08-19 21:57:24 +00:00
|
|
|
test_must_be_empty result
|
2013-04-22 19:52:24 +00:00
|
|
|
'
|
|
|
|
|
2013-04-22 19:52:25 +00:00
|
|
|
test_expect_success 'delete ref while another dangling packed ref' '
|
2013-04-22 19:52:24 +00:00
|
|
|
git branch lamb &&
|
|
|
|
git commit --allow-empty -m "future garbage" &&
|
|
|
|
git pack-refs --all &&
|
|
|
|
git reset --hard HEAD^ &&
|
|
|
|
git reflog expire --expire=all --all &&
|
|
|
|
git prune --expire=all &&
|
|
|
|
git branch -d lamb 2>result &&
|
2018-08-19 21:57:24 +00:00
|
|
|
test_must_be_empty result
|
2013-04-22 19:52:24 +00:00
|
|
|
'
|
|
|
|
|
2014-08-23 05:27:07 +00:00
|
|
|
test_expect_success 'pack ref directly below refs/' '
|
|
|
|
git update-ref refs/top HEAD &&
|
|
|
|
git pack-refs --all --prune &&
|
|
|
|
grep refs/top .git/packed-refs &&
|
|
|
|
test_path_is_missing .git/refs/top
|
|
|
|
'
|
|
|
|
|
2015-09-01 02:13:11 +00:00
|
|
|
test_expect_success 'do not pack ref in refs/bisect' '
|
|
|
|
git update-ref refs/bisect/local HEAD &&
|
|
|
|
git pack-refs --all --prune &&
|
|
|
|
! grep refs/bisect/local .git/packed-refs >/dev/null &&
|
|
|
|
test_path_is_file .git/refs/bisect/local
|
|
|
|
'
|
|
|
|
|
refs: speed up is_refname_available
Our filesystem ref storage does not allow D/F conflicts; so
if "refs/heads/a/b" exists, we do not allow "refs/heads/a"
to exist (and vice versa). This falls out naturally for
loose refs, where the filesystem enforces the condition. But
for packed-refs, we have to make the check ourselves.
We do so by iterating over the entire packed-refs namespace
and checking whether each name creates a conflict. If you
have a very large number of refs, this is quite inefficient,
as you end up doing a large number of comparisons with
uninteresting bits of the ref tree (e.g., we know that all
of "refs/tags" is uninteresting in the example above, yet we
check each entry in it).
Instead, let's take advantage of the fact that we have the
packed refs stored as a trie of ref_entry structs. We can
find each component of the proposed refname as we walk
through the trie, checking for D/F conflicts as we go. For a
refname of depth N (i.e., 4 in the above example), we only
have to visit N nodes. And at each visit, we can binary
search the M names at that level, for a total complexity of
O(N lg M). ("M" is different at each level, of course, but
we can take the worst-case "M" as a bound).
In a pathological case of fetching 30,000 fresh refs into a
repository with 8.5 million refs, this dropped the time to
run "git fetch" from tens of minutes to ~30s.
This may also help smaller cases in which we check against
loose refs (which we do when renaming a ref), as we may
avoid a disk access for unrelated loose directories.
Note that the tests we add appear at first glance to be
redundant with what is already in t3210. However, the early
tests are not robust; they are run with reflogs turned on,
meaning that we are not actually testing
is_refname_available at all! The operations will still fail
because the reflogs will hit D/F conflicts in the
filesystem. To get a true test, we must turn off reflogs
(but we don't want to do so for the entire script, because
the point of turning them on was to cover some other cases).
Reviewed-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-09-10 11:11:55 +00:00
|
|
|
test_expect_success 'disable reflogs' '
|
|
|
|
git config core.logallrefupdates false &&
|
|
|
|
rm -rf .git/logs
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'create packed foo/bar/baz branch' '
|
|
|
|
git branch foo/bar/baz &&
|
|
|
|
git pack-refs --all --prune &&
|
|
|
|
test_path_is_missing .git/refs/heads/foo/bar/baz &&
|
2015-07-27 22:57:08 +00:00
|
|
|
test_must_fail git reflog exists refs/heads/foo/bar/baz
|
refs: speed up is_refname_available
Our filesystem ref storage does not allow D/F conflicts; so
if "refs/heads/a/b" exists, we do not allow "refs/heads/a"
to exist (and vice versa). This falls out naturally for
loose refs, where the filesystem enforces the condition. But
for packed-refs, we have to make the check ourselves.
We do so by iterating over the entire packed-refs namespace
and checking whether each name creates a conflict. If you
have a very large number of refs, this is quite inefficient,
as you end up doing a large number of comparisons with
uninteresting bits of the ref tree (e.g., we know that all
of "refs/tags" is uninteresting in the example above, yet we
check each entry in it).
Instead, let's take advantage of the fact that we have the
packed refs stored as a trie of ref_entry structs. We can
find each component of the proposed refname as we walk
through the trie, checking for D/F conflicts as we go. For a
refname of depth N (i.e., 4 in the above example), we only
have to visit N nodes. And at each visit, we can binary
search the M names at that level, for a total complexity of
O(N lg M). ("M" is different at each level, of course, but
we can take the worst-case "M" as a bound).
In a pathological case of fetching 30,000 fresh refs into a
repository with 8.5 million refs, this dropped the time to
run "git fetch" from tens of minutes to ~30s.
This may also help smaller cases in which we check against
loose refs (which we do when renaming a ref), as we may
avoid a disk access for unrelated loose directories.
Note that the tests we add appear at first glance to be
redundant with what is already in t3210. However, the early
tests are not robust; they are run with reflogs turned on,
meaning that we are not actually testing
is_refname_available at all! The operations will still fail
because the reflogs will hit D/F conflicts in the
filesystem. To get a true test, we must turn off reflogs
(but we don't want to do so for the entire script, because
the point of turning them on was to cover some other cases).
Reviewed-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-09-10 11:11:55 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'notice d/f conflict with existing directory' '
|
|
|
|
test_must_fail git branch foo &&
|
|
|
|
test_must_fail git branch foo/bar
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'existing directory reports concrete ref' '
|
|
|
|
test_must_fail git branch foo 2>stderr &&
|
2023-10-31 05:23:30 +00:00
|
|
|
test_grep refs/heads/foo/bar/baz stderr
|
refs: speed up is_refname_available
Our filesystem ref storage does not allow D/F conflicts; so
if "refs/heads/a/b" exists, we do not allow "refs/heads/a"
to exist (and vice versa). This falls out naturally for
loose refs, where the filesystem enforces the condition. But
for packed-refs, we have to make the check ourselves.
We do so by iterating over the entire packed-refs namespace
and checking whether each name creates a conflict. If you
have a very large number of refs, this is quite inefficient,
as you end up doing a large number of comparisons with
uninteresting bits of the ref tree (e.g., we know that all
of "refs/tags" is uninteresting in the example above, yet we
check each entry in it).
Instead, let's take advantage of the fact that we have the
packed refs stored as a trie of ref_entry structs. We can
find each component of the proposed refname as we walk
through the trie, checking for D/F conflicts as we go. For a
refname of depth N (i.e., 4 in the above example), we only
have to visit N nodes. And at each visit, we can binary
search the M names at that level, for a total complexity of
O(N lg M). ("M" is different at each level, of course, but
we can take the worst-case "M" as a bound).
In a pathological case of fetching 30,000 fresh refs into a
repository with 8.5 million refs, this dropped the time to
run "git fetch" from tens of minutes to ~30s.
This may also help smaller cases in which we check against
loose refs (which we do when renaming a ref), as we may
avoid a disk access for unrelated loose directories.
Note that the tests we add appear at first glance to be
redundant with what is already in t3210. However, the early
tests are not robust; they are run with reflogs turned on,
meaning that we are not actually testing
is_refname_available at all! The operations will still fail
because the reflogs will hit D/F conflicts in the
filesystem. To get a true test, we must turn off reflogs
(but we don't want to do so for the entire script, because
the point of turning them on was to cover some other cases).
Reviewed-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-09-10 11:11:55 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'notice d/f conflict with existing ref' '
|
|
|
|
test_must_fail git branch foo/bar/baz/extra &&
|
|
|
|
test_must_fail git branch foo/bar/baz/lots/of/extra/components
|
|
|
|
'
|
|
|
|
|
2017-07-01 18:31:08 +00:00
|
|
|
test_expect_success 'reject packed-refs with unterminated line' '
|
2017-07-01 18:31:07 +00:00
|
|
|
cp .git/packed-refs .git/packed-refs.bak &&
|
|
|
|
test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
|
|
|
|
printf "%s" "$HEAD refs/zzzzz" >>.git/packed-refs &&
|
|
|
|
echo "fatal: unterminated line in .git/packed-refs: $HEAD refs/zzzzz" >expected_err &&
|
|
|
|
test_must_fail git for-each-ref >out 2>err &&
|
|
|
|
test_cmp expected_err err
|
|
|
|
'
|
|
|
|
|
2017-07-01 18:31:08 +00:00
|
|
|
test_expect_success 'reject packed-refs containing junk' '
|
2017-07-01 18:31:07 +00:00
|
|
|
cp .git/packed-refs .git/packed-refs.bak &&
|
|
|
|
test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
|
|
|
|
printf "%s\n" "bogus content" >>.git/packed-refs &&
|
|
|
|
echo "fatal: unexpected line in .git/packed-refs: bogus content" >expected_err &&
|
|
|
|
test_must_fail git for-each-ref >out 2>err &&
|
|
|
|
test_cmp expected_err err
|
|
|
|
'
|
|
|
|
|
2017-07-01 18:31:08 +00:00
|
|
|
test_expect_success 'reject packed-refs with a short SHA-1' '
|
2017-07-01 18:31:07 +00:00
|
|
|
cp .git/packed-refs .git/packed-refs.bak &&
|
|
|
|
test_when_finished "mv .git/packed-refs.bak .git/packed-refs" &&
|
|
|
|
printf "%.7s %s\n" $HEAD refs/zzzzz >>.git/packed-refs &&
|
|
|
|
printf "fatal: unexpected line in .git/packed-refs: %.7s %s\n" $HEAD refs/zzzzz >expected_err &&
|
|
|
|
test_must_fail git for-each-ref >out 2>err &&
|
|
|
|
test_cmp expected_err err
|
|
|
|
'
|
|
|
|
|
lock_packed_refs(): allow retries when acquiring the packed-refs lock
Currently, there is only one attempt to acquire any lockfile, and if
the lock is held by another process, the locking attempt fails
immediately.
This is not such a limitation for loose reference files. First, they
don't take long to rewrite. Second, most reference updates have a
known "old" value, so if another process is updating a reference at
the same moment that we are trying to lock it, then probably the
expected "old" value will not longer be valid, and the update will
fail anyway.
But these arguments do not hold for packed-refs:
* The packed-refs file can be large and take significant time to
rewrite.
* Many references are stored in a single packed-refs file, so it could
be that the other process was changing a different reference than
the one that we are interested in.
Therefore, it is much more likely for there to be spurious lock
conflicts in connection to the packed-refs file, resulting in
unnecessary command failures.
So, if the first attempt to lock the packed-refs file fails, continue
retrying for a configurable length of time before giving up. The
default timeout is 1 second.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-05-11 10:35:26 +00:00
|
|
|
test_expect_success 'timeout if packed-refs.lock exists' '
|
|
|
|
LOCK=.git/packed-refs.lock &&
|
|
|
|
>"$LOCK" &&
|
|
|
|
test_when_finished "rm -f $LOCK" &&
|
|
|
|
test_must_fail git pack-refs --all --prune
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'retry acquiring packed-refs.lock' '
|
|
|
|
LOCK=.git/packed-refs.lock &&
|
|
|
|
>"$LOCK" &&
|
2018-07-02 00:23:58 +00:00
|
|
|
test_when_finished "wait && rm -f $LOCK" &&
|
lock_packed_refs(): allow retries when acquiring the packed-refs lock
Currently, there is only one attempt to acquire any lockfile, and if
the lock is held by another process, the locking attempt fails
immediately.
This is not such a limitation for loose reference files. First, they
don't take long to rewrite. Second, most reference updates have a
known "old" value, so if another process is updating a reference at
the same moment that we are trying to lock it, then probably the
expected "old" value will not longer be valid, and the update will
fail anyway.
But these arguments do not hold for packed-refs:
* The packed-refs file can be large and take significant time to
rewrite.
* Many references are stored in a single packed-refs file, so it could
be that the other process was changing a different reference than
the one that we are interested in.
Therefore, it is much more likely for there to be spurious lock
conflicts in connection to the packed-refs file, resulting in
unnecessary command failures.
So, if the first attempt to lock the packed-refs file fails, continue
retrying for a configurable length of time before giving up. The
default timeout is 1 second.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-05-11 10:35:26 +00:00
|
|
|
{
|
2018-07-02 00:23:58 +00:00
|
|
|
( sleep 1 && rm -f $LOCK ) &
|
lock_packed_refs(): allow retries when acquiring the packed-refs lock
Currently, there is only one attempt to acquire any lockfile, and if
the lock is held by another process, the locking attempt fails
immediately.
This is not such a limitation for loose reference files. First, they
don't take long to rewrite. Second, most reference updates have a
known "old" value, so if another process is updating a reference at
the same moment that we are trying to lock it, then probably the
expected "old" value will not longer be valid, and the update will
fail anyway.
But these arguments do not hold for packed-refs:
* The packed-refs file can be large and take significant time to
rewrite.
* Many references are stored in a single packed-refs file, so it could
be that the other process was changing a different reference than
the one that we are interested in.
Therefore, it is much more likely for there to be spurious lock
conflicts in connection to the packed-refs file, resulting in
unnecessary command failures.
So, if the first attempt to lock the packed-refs file fails, continue
retrying for a configurable length of time before giving up. The
default timeout is 1 second.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-05-11 10:35:26 +00:00
|
|
|
} &&
|
|
|
|
git -c core.packedrefstimeout=3000 pack-refs --all --prune
|
|
|
|
'
|
|
|
|
|
2017-07-26 23:39:42 +00:00
|
|
|
test_expect_success SYMLINKS 'pack symlinked packed-refs' '
|
|
|
|
# First make sure that symlinking works when reading:
|
2020-11-18 23:44:23 +00:00
|
|
|
git update-ref refs/heads/lossy refs/heads/main &&
|
2017-07-26 23:39:42 +00:00
|
|
|
git for-each-ref >all-refs-before &&
|
|
|
|
mv .git/packed-refs .git/my-deviant-packed-refs &&
|
|
|
|
ln -s my-deviant-packed-refs .git/packed-refs &&
|
|
|
|
git for-each-ref >all-refs-linked &&
|
|
|
|
test_cmp all-refs-before all-refs-linked &&
|
|
|
|
git pack-refs --all --prune &&
|
|
|
|
git for-each-ref >all-refs-packed &&
|
|
|
|
test_cmp all-refs-before all-refs-packed &&
|
|
|
|
test -h .git/packed-refs &&
|
2021-06-18 16:32:22 +00:00
|
|
|
test "$(test_readlink .git/packed-refs)" = "my-deviant-packed-refs"
|
2017-07-26 23:39:42 +00:00
|
|
|
'
|
|
|
|
|
2024-01-19 20:18:56 +00:00
|
|
|
# The 'packed-refs' file is stored directly in .git/. This means it is global
|
|
|
|
# to the repository, and can only contain refs that are shared across all
|
|
|
|
# worktrees.
|
|
|
|
test_expect_success 'refs/worktree must not be packed' '
|
|
|
|
test_commit initial &&
|
|
|
|
test_commit wt1 &&
|
|
|
|
test_commit wt2 &&
|
|
|
|
git worktree add wt1 wt1 &&
|
|
|
|
git worktree add wt2 wt2 &&
|
|
|
|
git checkout initial &&
|
|
|
|
git update-ref refs/worktree/foo HEAD &&
|
|
|
|
git -C wt1 update-ref refs/worktree/foo HEAD &&
|
|
|
|
git -C wt2 update-ref refs/worktree/foo HEAD &&
|
|
|
|
git pack-refs --all &&
|
|
|
|
test_path_is_missing .git/refs/tags/wt1 &&
|
|
|
|
test_path_is_file .git/refs/worktree/foo &&
|
|
|
|
test_path_is_file .git/worktrees/wt1/refs/worktree/foo &&
|
|
|
|
test_path_is_file .git/worktrees/wt2/refs/worktree/foo
|
|
|
|
'
|
|
|
|
|
2024-01-19 20:19:00 +00:00
|
|
|
# we do not want to count on running pack-refs to
|
|
|
|
# actually pack it, as it is perfectly reasonable to
|
|
|
|
# skip processing a broken ref
|
|
|
|
test_expect_success 'create packed-refs file with broken ref' '
|
|
|
|
test_tick && git commit --allow-empty -m one &&
|
|
|
|
recoverable=$(git rev-parse HEAD) &&
|
|
|
|
test_tick && git commit --allow-empty -m two &&
|
|
|
|
missing=$(git rev-parse HEAD) &&
|
|
|
|
rm -f .git/refs/heads/main &&
|
|
|
|
cat >.git/packed-refs <<-EOF &&
|
|
|
|
$missing refs/heads/main
|
|
|
|
$recoverable refs/heads/other
|
|
|
|
EOF
|
|
|
|
echo $missing >expect &&
|
|
|
|
git rev-parse refs/heads/main >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'pack-refs does not silently delete broken packed ref' '
|
|
|
|
git pack-refs --all --prune &&
|
|
|
|
git rev-parse refs/heads/main >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'pack-refs does not drop broken refs during deletion' '
|
|
|
|
git update-ref -d refs/heads/other &&
|
|
|
|
git rev-parse refs/heads/main >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
builtin/gc: pack refs when using `git maintenance run --auto`
When running `git maintenance run --auto`, then the various subtasks
will only run as needed. Thus, we for example end up only packing loose
objects if we hit a certain threshold.
Interestingly enough, the "pack-refs" task is actually _never_ executed
when the auto-flag is set because it does not have a condition at all.
As 41abfe15d9 (maintenance: add pack-refs task, 2021-02-09) mentions:
The 'auto_condition' function pointer is left NULL for now. We could
extend this in the future to have a condition check if pack-refs
should be run during 'git maintenance run --auto'.
It is not quite clear from that quote whether it is actually intended
that the task doesn't run at all in this mode. Also, no test was added
to verify this behaviour. Ultimately though, it feels quite surprising
that `git maintenance run --auto --task=pack-refs` would quietly never
do anything at all.
In any case, now that we do have the logic in place to let ref backends
decide whether or not to repack refs, it does make sense to wire it up
accordingly. With the "reftable" backend we will thus now perform
auto-compaction, which optimizes the refdb as needed.
But for the "files" backend we now unconditionally pack refs as it does
not yet know to handle the "auto" flag. Arguably, this can be seen as a
bug fix given that previously the task never did anything at all.
Eventually though we should amend the "files" backend to use some
heuristics for auto compaction, as well.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-03-25 10:03:36 +00:00
|
|
|
test_expect_success 'maintenance --auto unconditionally packs loose refs' '
|
|
|
|
git update-ref refs/heads/something HEAD &&
|
|
|
|
test_path_is_file .git/refs/heads/something &&
|
|
|
|
git rev-parse refs/heads/something >expect &&
|
|
|
|
git maintenance run --task=pack-refs --auto &&
|
|
|
|
test_path_is_missing .git/refs/heads/something &&
|
|
|
|
git rev-parse refs/heads/something >actual &&
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
2006-09-28 04:58:03 +00:00
|
|
|
test_done
|