git/t/t7522-serialized-status.sh

459 строки
15 KiB
Bash
Executable File

#!/bin/sh
test_description='git serialized status tests'
. ./test-lib.sh
# This file includes tests for serializing / deserializing
# status data. These tests cover two basic features:
#
# [1] Because users can request different types of untracked-file
# and ignored file reporting, the cache data generated by
# serialize must use either the same untracked and ignored
# parameters as the later deserialize invocation; otherwise,
# the deserialize invocation must disregard the cached data
# and run a full scan itself.
#
# To increase the number of cases where the cached status can
# be used, we have added a "--untracked-file=complete" option
# that reports a superset or union of the results from the
# "-u normal" and "-u all". We combine this with a filter in
# deserialize to filter the results.
#
# Ignored file reporting is simpler in that is an all or
# nothing; there are no subsets.
#
# The tests here (in addition to confirming that a cache
# file can be generated and used by a subsequent status
# command) need to test this untracked-file filtering.
#
# [2] ensuring the status calls are using data from the status
# cache as expected. This includes verifying cached data
# is used when appropriate as well as falling back to
# performing a new status scan when the data in the cache
# is insufficient/known stale.
test_expect_success 'setup' '
git branch -M main &&
cat >.gitignore <<-\EOF &&
*.ign
ignored_dir/
EOF
mkdir tracked ignored_dir &&
touch tracked_1.txt tracked/tracked_1.txt &&
git add . &&
test_tick &&
git commit -m"Adding original file." &&
mkdir untracked &&
touch ignored.ign ignored_dir/ignored_2.txt \
untracked_1.txt untracked/untracked_2.txt untracked/untracked_3.txt &&
test_oid_cache <<-EOF
branch_oid sha1:68d4a437ea4c2de65800f48c053d4d543b55c410
x_base sha1:587be6b4c3f93f93c489c0111bba5596147a26cb
x_ours sha1:b68025345d5301abad4d9ec9166f455243a0d746
x_theirs sha1:975fbec8256d3e8a3797e7a3611380f27c49f4ac
branch_oid sha256:6b95e4b1ea911dad213f2020840f5e92d3066cf9e38cf35f79412ec58d409ce4
x_base sha256:14f5162e2fe3d240d0d37aaab0f90e4af9a7cfa79639f3bab005b5bfb4174d9f
x_ours sha256:3a404ba030a4afa912155c476a48a253d4b3a43d0098431b6d6ca6e554bd78fb
x_theirs sha256:44dc634218adec09e34f37839b3840bad8c6103693e9216626b32d00e093fa35
EOF
'
test_expect_success 'verify untracked-files=complete with no conversion' '
test_when_finished "rm serialized_status.dat new_change.txt output" &&
cat >expect <<-\EOF &&
? expect
? serialized_status.dat
? untracked/
? untracked/untracked_2.txt
? untracked/untracked_3.txt
? untracked_1.txt
! ignored.ign
! ignored_dir/
EOF
git status --untracked-files=complete --ignored=matching --serialize >serialized_status.dat &&
touch new_change.txt &&
git status --porcelain=v2 --untracked-files=complete --ignored=matching --deserialize=serialized_status.dat >output &&
test_cmp expect output
'
test_expect_success 'verify untracked-files=complete to untracked-files=normal conversion' '
test_when_finished "rm serialized_status.dat new_change.txt output" &&
cat >expect <<-\EOF &&
? expect
? serialized_status.dat
? untracked/
? untracked_1.txt
EOF
git status --untracked-files=complete --ignored=matching --serialize >serialized_status.dat &&
touch new_change.txt &&
git status --porcelain=v2 --deserialize=serialized_status.dat >output &&
test_cmp expect output
'
test_expect_success 'verify untracked-files=complete to untracked-files=all conversion' '
test_when_finished "rm serialized_status.dat new_change.txt output" &&
cat >expect <<-\EOF &&
? expect
? serialized_status.dat
? untracked/untracked_2.txt
? untracked/untracked_3.txt
? untracked_1.txt
! ignored.ign
! ignored_dir/
EOF
git status --untracked-files=complete --ignored=matching --serialize >serialized_status.dat &&
touch new_change.txt &&
git status --porcelain=v2 --untracked-files=all --ignored=matching --deserialize=serialized_status.dat >output &&
test_cmp expect output
'
test_expect_success 'verify serialized status with non-convertible ignore mode does new scan' '
test_when_finished "rm serialized_status.dat new_change.txt output" &&
cat >expect <<-\EOF &&
? expect
? new_change.txt
? output
? serialized_status.dat
? untracked/
? untracked_1.txt
! ignored.ign
! ignored_dir/
EOF
git status --untracked-files=complete --ignored=matching --serialize >serialized_status.dat &&
touch new_change.txt &&
git status --porcelain=v2 --ignored --deserialize=serialized_status.dat >output &&
test_cmp expect output
'
test_expect_success 'verify serialized status handles path scopes' '
test_when_finished "rm serialized_status.dat new_change.txt output" &&
cat >expect <<-\EOF &&
? untracked/
EOF
git status --untracked-files=complete --ignored=matching --serialize >serialized_status.dat &&
touch new_change.txt &&
git status --porcelain=v2 --deserialize=serialized_status.dat untracked >output &&
test_cmp expect output
'
test_expect_success 'verify no-ahead-behind and serialized status integration' '
test_when_finished "rm serialized_status.dat new_change.txt output" &&
cat >expect <<-EOF &&
# branch.oid $(test_oid branch_oid)
# branch.head alt_branch
# branch.upstream main
# branch.ab +1 -0
? expect
? serialized_status.dat
? untracked/
? untracked_1.txt
EOF
git checkout -b alt_branch main --track >/dev/null &&
touch alt_branch_changes.txt &&
git add alt_branch_changes.txt &&
test_tick &&
git commit -m"New commit on alt branch" &&
git status --untracked-files=complete --ignored=matching --serialize >serialized_status.dat &&
touch new_change.txt &&
git -c status.aheadBehind=false status --porcelain=v2 --branch --ahead-behind --deserialize=serialized_status.dat >output &&
test_cmp expect output
'
test_expect_success 'verify new --serialize=path mode' '
test_when_finished "rm serialized_status.dat expect new_change.txt output.1 output.2" &&
cat >expect <<-\EOF &&
? expect
? output.1
? untracked/
? untracked_1.txt
EOF
git checkout -b serialize_path_branch main --track >/dev/null &&
touch alt_branch_changes.txt &&
git add alt_branch_changes.txt &&
test_tick &&
git commit -m"New commit on serialize_path_branch" &&
git status --porcelain=v2 --serialize=serialized_status.dat >output.1 &&
touch new_change.txt &&
git status --porcelain=v2 --deserialize=serialized_status.dat >output.2 &&
test_cmp expect output.1 &&
test_cmp expect output.2
'
test_expect_success 'try deserialize-wait feature' '
test_when_finished "rm -f serialized_status.dat dirt expect.* output.* trace.*" &&
git status --serialize=serialized_status.dat >output.1 &&
# make status cache stale by updating the mtime on the index. confirm that
# deserialize fails when requested.
sleep 1 &&
touch .git/index &&
test_must_fail git status --deserialize=serialized_status.dat --deserialize-wait=fail &&
test_must_fail git -c status.deserializeWait=fail status --deserialize=serialized_status.dat &&
cat >expect.1 <<-\EOF &&
? expect.1
? output.1
? serialized_status.dat
? untracked/
? untracked_1.txt
EOF
# refresh the status cache.
git status --porcelain=v2 --serialize=serialized_status.dat >output.1 &&
test_cmp expect.1 output.1 &&
# create some dirt. confirm deserialize used the existing status cache.
echo x >dirt &&
git status --porcelain=v2 --deserialize=serialized_status.dat >output.2 &&
test_cmp output.1 output.2 &&
# make the cache stale and try the timeout feature and wait upto
# 2 tenths of a second. confirm deserialize timed out and rejected
# the status cache and did a normal scan.
cat >expect.2 <<-\EOF &&
? dirt
? expect.1
? expect.2
? output.1
? output.2
? serialized_status.dat
? trace.2
? untracked/
? untracked_1.txt
EOF
sleep 1 &&
touch .git/index &&
GIT_TRACE_DESERIALIZE=1 git status --porcelain=v2 --deserialize=serialized_status.dat --deserialize-wait=2 >output.2 2>trace.2 &&
test_cmp expect.2 output.2 &&
grep "wait polled=2 result=1" trace.2 >trace.2g
'
test_expect_success 'merge conflicts' '
# create a merge conflict.
git init -b main conflicts &&
echo x >conflicts/x.txt &&
git -C conflicts add x.txt &&
git -C conflicts commit -m x &&
git -C conflicts branch a &&
git -C conflicts branch b &&
git -C conflicts checkout a &&
echo y >conflicts/x.txt &&
git -C conflicts add x.txt &&
git -C conflicts commit -m a &&
git -C conflicts checkout b &&
echo z >conflicts/x.txt &&
git -C conflicts add x.txt &&
git -C conflicts commit -m b &&
test_must_fail git -C conflicts merge --no-commit a &&
# verify that regular status correctly identifies it
# in each format.
cat >expect.v2 <<EOF &&
u UU N... 100644 100644 100644 100644 $(test_oid x_base) $(test_oid x_ours) $(test_oid x_theirs) x.txt
EOF
git -C conflicts status --porcelain=v2 >observed.v2 &&
test_cmp expect.v2 observed.v2 &&
cat >expect.long <<EOF &&
On branch b
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: x.txt
no changes added to commit (use "git add" and/or "git commit -a")
EOF
git -C conflicts status --long >observed.long &&
test_cmp expect.long observed.long &&
cat >expect.short <<EOF &&
UU x.txt
EOF
git -C conflicts status --short >observed.short &&
test_cmp expect.short observed.short &&
# save status data in serialized cache.
git -C conflicts status --serialize >serialized &&
# make some dirt in the worktree so we can tell whether subsequent
# status commands used the cached data or did a fresh status.
echo dirt >conflicts/dirt.txt &&
# run status using the cached data.
git -C conflicts status --long --deserialize=../serialized >observed.long &&
test_cmp expect.long observed.long &&
git -C conflicts status --short --deserialize=../serialized >observed.short &&
test_cmp expect.short observed.short &&
# currently, the cached data does not have enough information about
# merge conflicts for porcelain V2 format. (And V2 format looks at
# the index to get that data, but the whole point of the serialization
# is to avoid reading the index unnecessarily.) So V2 always rejects
# the cached data when there is an unresolved conflict.
cat >expect.v2.dirty <<EOF &&
u UU N... 100644 100644 100644 100644 $(test_oid x_base) $(test_oid x_ours) $(test_oid x_theirs) x.txt
? dirt.txt
EOF
git -C conflicts status --porcelain=v2 --deserialize=../serialized >observed.v2 &&
test_cmp expect.v2.dirty observed.v2
'
test_expect_success 'renames' '
git init -b main rename_test &&
echo OLDNAME >rename_test/OLDNAME &&
git -C rename_test add OLDNAME &&
git -C rename_test commit -m OLDNAME &&
git -C rename_test mv OLDNAME NEWNAME &&
git -C rename_test status --serialize=renamed.dat >output.1 &&
echo DIRT >rename_test/DIRT &&
git -C rename_test status --deserialize=renamed.dat >output.2 &&
test_cmp output.1 output.2
'
test_expect_success 'hint message when cached with u=complete' '
git init -b main hint &&
echo xxx >hint/xxx &&
git -C hint add xxx &&
git -C hint commit -m xxx &&
cat >expect.clean <<EOF &&
On branch main
nothing to commit, working tree clean
EOF
cat >expect.use_u <<EOF &&
On branch main
nothing to commit (use -u to show untracked files)
EOF
# Capture long format output from "no", "normal", and "all"
# (without using status cache) and verify it matches expected
# output.
git -C hint status --untracked-files=normal >hint.output_normal &&
test_cmp expect.clean hint.output_normal &&
git -C hint status --untracked-files=all >hint.output_all &&
test_cmp expect.clean hint.output_all &&
git -C hint status --untracked-files=no >hint.output_no &&
test_cmp expect.use_u hint.output_no &&
# Create long format output for "complete" and create status cache.
git -C hint status --untracked-files=complete --ignored=matching --serialize=../hint.dat >hint.output_complete &&
test_cmp expect.clean hint.output_complete &&
# Capture long format output using the status cache and verify
# that the output matches the non-cached version. There are 2
# ways to specify untracked-files, so do them both.
git -C hint status --deserialize=../hint.dat -unormal >hint.d1_normal &&
test_cmp expect.clean hint.d1_normal &&
git -C hint -c status.showuntrackedfiles=normal status --deserialize=../hint.dat >hint.d2_normal &&
test_cmp expect.clean hint.d2_normal &&
git -C hint status --deserialize=../hint.dat -uall >hint.d1_all &&
test_cmp expect.clean hint.d1_all &&
git -C hint -c status.showuntrackedfiles=all status --deserialize=../hint.dat >hint.d2_all &&
test_cmp expect.clean hint.d2_all &&
git -C hint status --deserialize=../hint.dat -uno >hint.d1_no &&
test_cmp expect.use_u hint.d1_no &&
git -C hint -c status.showuntrackedfiles=no status --deserialize=../hint.dat >hint.d2_no &&
test_cmp expect.use_u hint.d2_no
'
test_expect_success 'ensure deserialize -v does not crash' '
git init -b main verbose_test &&
touch verbose_test/a &&
touch verbose_test/b &&
touch verbose_test/c &&
git -C verbose_test add a b c &&
git -C verbose_test commit -m abc &&
echo green >>verbose_test/a &&
git -C verbose_test add a &&
echo red_1 >>verbose_test/b &&
echo red_2 >verbose_test/dirt &&
git -C verbose_test status >output.ref &&
git -C verbose_test status -v >output.ref_v &&
git -C verbose_test --no-optional-locks status --serialize=../verbose_test.dat >output.ser.long &&
git -C verbose_test --no-optional-locks status --serialize=../verbose_test.dat_v -v >output.ser.long_v &&
# Verify that serialization does not affect the status output itself.
test_cmp output.ref output.ser.long &&
test_cmp output.ref_v output.ser.long_v &&
GIT_TRACE2_PERF="$(pwd)"/verbose_test.log \
git -C verbose_test status --deserialize=../verbose_test.dat >output.des.long &&
# Verify that normal deserialize was actually used and produces the same result.
test_cmp output.ser.long output.des.long &&
grep -q "deserialize/result:ok" verbose_test.log &&
GIT_TRACE2_PERF="$(pwd)"/verbose_test.log_v \
git -C verbose_test status --deserialize=../verbose_test.dat_v -v >output.des.long_v &&
# Verify that vebose mode produces the same result because verbose was rejected.
test_cmp output.ser.long_v output.des.long_v &&
grep -q "deserialize/reject:args/verbose" verbose_test.log_v
'
test_expect_success 'fallback when implicit' '
git init -b main implicit_fallback_test &&
git -C implicit_fallback_test -c status.deserializepath=foobar status
'
test_expect_success 'fallback when explicit' '
git init -b main explicit_fallback_test &&
git -C explicit_fallback_test status --deserialize=foobar
'
test_expect_success 'deserialize from stdin' '
git init -b main stdin_test &&
git -C stdin_test status --serialize >serialized_status.dat &&
cat serialize_status.dat | git -C stdin_test status --deserialize
'
test_done