Skip to content

Commit 47ccbff

Browse files
chrisd8088hswong3i
authored andcommitted
t: test index is updated on checkout and pull
After our "git lfs checkout" and "git lfs pull" commands write the contents of a Git LFS object into a file in the current working tree, they pass the file's relative path to the "git update-index" command, so the entries in Git's index for these files reflect their current state. If we did not run the "git update-index" command, Git commands like "git diff-index" would report the files we updated as having been modified in the working tree. Notably, though, the "git status" command would normally not report the files as modified, because it refreshes the Git index in the same way as the "git update-index" command we run. We invoke the "git update-index" command with the --refresh option, so it runs the refresh_index() function, which is also executed by the "git status" command. The refresh_index() function checks the state of all the cached entries in Git's index, and so it will detect that the files in the working tree have been modified, read their content and pass it to the "git lfs filter-process" command, which returns the same Git LFS pointer data as should already be in the Git index. The end result is that the index entries remain unchanged, but the cached modification timestamps are updated. As a consequence, even if we never invoked the "git update-index" command while running our "git lfs checkout" and "git lfs pull" commands, a subsequent "git status" command would typically not show the working tree files we update as modified. However, in our tests of the "git lfs pull" command, we rely exclusively on the "git status" command to try to verify that the "git update-index" command has been executed by the "git lfs pull" command. Moreover, the assert_clean_status() test helper function we invoke to run the "git status" command has a bug which means it always succeeds, even if the "git status" command were to report that the current working tree was not clean. We added the assert_clean_status() assertion function in commit 7158e3b of PR git-lfs#2641, along with calls to that function in the initial "pull" test of what is now our t/t-pull.sh test script. In the same commit we also revised the "git lfs checkout" and "git lfs pull" commands so that they pass paths relative to the current working directory to the "git update-index" command, rather than paths relative to the root of the repository. This latter change resolved the problem reported in git-lfs#2639, because by passing file paths relative to the repository root instead of the current working directory to the "git update-index" command, the "git lfs checkout" and "git lfs pull" commands were leaving the Git index and working tree in a confusing state. At the time when git-lfs#2639 was reported, running "git lfs pull" in a subdirectory might result in a subsequent "git status" command finding modifications to both the Git index and the working tree. To check that the problem was resolved, in PR git-lfs#2641 we introduced the assert_clean_status() test helper function, and expanded the "pull" test to call this function after performing several "git lfs pull" commands in subdirectories of the working tree. Unfortunately, the assert_clean_status() function does not behave as an assertion, because it always returns a successful (i.e., zero) exit code. The function tests whether the output from a "git status" command includes the message "working tree clean", and if not, reports the command's output along with that from a "git lfs status" command. However, the function does not cause the current test's sub-shell to stop execution and return a non-zero exit code to signal a test failure condition. We address this problem now by adding a call to the "exit" shell built-in command with a non-zero exit code at the end of the brace-delimited group command which executes if the assert_clean_status() function does not find the "working tree clean" message in the output from the "git status" command. This change ensures that if Git determines that the working tree is not clean, our assert_clean_status() function will cause the test which called it to fail. As well, we update the assert_clean_status() function so that when checking for the "working tree clean" message in the output of the "git status" command, it will also accept the older "working directory clean" message output by the "git status" command prior to Git v2.10.0. This allows our assertion function to succeed and the "pull" test to pass in our GitHub Actions CI jobs that run our test suite against Git v2.0.0, the oldest Git version we currently support. As a further problem, though, checking that a "git status" command finds a clean working tree is no longer sufficient to guarantee that the "git lfs pull" command has actually called the "git update-index" command with the appropriate file paths. To be certain that the Git LFS client has invoked the "git update-index" command and passed valid file paths, we also need to check the output of the "git diff-index" command, and do so before we run the "git status" command, which will cause Git to reset the file modification timestamps it has cached. We therefore define a new assert_clean_worktree() test helper function which runs a "git diff-index HEAD" command and confirms no unexpected changes are detected in the working tree, and we call this function at the start of the assert_clean_status() function before the "git status" command is executed. This ensures that we check the state of the working tree before the "git status" command refreshes the file modification timestamps Git has previously cached. As noted above, if we reversed the order of these checks, the "git diff-index" command would always return an empty list and so our check of the working tree's state would be defeated. We also define a new assert_clean_worktree_with_exceptions() test helper function, which acts like the assert_clean_worktree() function but filters the output of the "git diff-index HEAD" command with an extended regular expression pattern provided to the function as its only argument. This allows us to use the assert_clean_worktree_with_exceptions() function in instances where we expect certain files in the working tree to be absent or modified and so we want to ignore them when they appear in the "git diff-index" command's output. We then update the initial "pull" test in the t/t-pull.sh test script so that it calls either the assert_clean_status() function or the assert_clean_worktree_with_exceptions() function after each invocation of the "git lfs pull" command, thereby making the checks in this test as thorough and consistent with each other as possible. Finally, we update the initial "checkout" test in the t/t-checkout.sh test script so that it makes use of the assert_clean_status() and assert_clean_worktree_with_exceptions() functions in the same manner as the "pull" test in the t/t-pull.sh script now does. Both tests perform similar operations, and like the "git lfs pull" command, the "git lfs checkout" command also invokes the "git update-index" command. However, the "checkout" test was not updated in PR git-lfs#2641 when the assert_clean_status() was introduced and the "pull" test was updated to call it. Since we want our "checkout" test to verify that the "git lfs checkout" command successfully runs the "git update-index" command, we update the test so that it mirrors the "pull" test and consistently uses the assert_clean_worktree() and assert_clean_worktree_with_exceptions() functions to check the state of the working tree. To ensure that the "checkout" test still passes, though, we also need to adjust the test so that it removes any log files it creates because the assert_clean_status() function now behaves as was originally intended and will cause the calling test to fail if the working tree is not clean.
1 parent 23ae097 commit 47ccbff

File tree

3 files changed

+34
-2
lines changed

3 files changed

+34
-2
lines changed

t/t-checkout.sh

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ begin_test "checkout"
1010
setup_remote_repo "$reponame"
1111

1212
clone_repo "$reponame" repo
13+
rm -f clone.log
1314

1415
git lfs track "*.dat" 2>&1 | tee track.log
1516
grep "Tracking \"\*.dat\"" track.log
17+
rm -f track.log
1618

1719
contents="something something"
1820
contentsize=19
@@ -50,12 +52,15 @@ begin_test "checkout"
5052
grep "Checking out LFS objects: 100% (5/5), 95 B" checkout.log
5153
grep 'accepting "file1.dat"' checkout.log
5254
grep 'rejecting "file1.dat"' checkout.log && exit 1
55+
rm -f checkout.log
56+
assert_clean_status
5357

5458
git rm file1.dat
5559

5660
echo "checkout should skip replacing files deleted in index"
5761
git lfs checkout
5862
[ ! -f file1.dat ]
63+
assert_clean_worktree_with_exceptions "file1\.dat"
5964

6065
git reset --hard
6166

@@ -69,13 +74,15 @@ begin_test "checkout"
6974
[ ! -f file3.dat ]
7075
[ ! -f folder1/nested.dat ]
7176
[ ! -f folder2/nested.dat ]
77+
assert_clean_worktree_with_exceptions "(file[13]|nested)\.dat"
7278

7379
echo "quotes to avoid shell globbing"
7480
git lfs checkout "file*.dat"
7581
[ "$contents" = "$(cat file1.dat)" ]
7682
[ "$contents" = "$(cat file3.dat)" ]
7783
[ ! -f folder1/nested.dat ]
7884
[ ! -f folder2/nested.dat ]
85+
assert_clean_worktree_with_exceptions "nested\.dat"
7986

8087
echo "test subdir context"
8188
rm file1.dat
@@ -84,40 +91,47 @@ begin_test "checkout"
8491
[ "$contents" = "$(cat nested.dat)" ]
8592
[ ! -f ../file1.dat ]
8693
[ ! -f ../folder2/nested.dat ]
94+
assert_clean_worktree_with_exceptions "(file1|folder2/nested)\.dat"
8795

8896
# test '.' in current dir
8997
rm nested.dat
90-
git lfs checkout . 2>&1 | tee checkout.log
98+
git lfs checkout .
9199
[ "$contents" = "$(cat nested.dat)" ]
92100
[ ! -f ../file1.dat ]
93101
[ ! -f ../folder2/nested.dat ]
102+
assert_clean_worktree_with_exceptions "(file1|folder2/nested)\.dat"
94103

95104
# test '..' in current dir
96105
git lfs checkout ..
97106
[ "$contents" = "$(cat ../file1.dat)" ]
98107
[ "$contents" = "$(cat ../folder2/nested.dat)" ]
108+
assert_clean_status
99109

100110
# test glob match with '..' in current dir
101111
rm -rf ../folder2
102112
git lfs checkout '../folder2/**'
103113
[ "$contents" = "$(cat ../folder2/nested.dat)" ]
114+
assert_clean_status
104115
popd
105116

106117
echo "test folder param"
107118
rm -rf folder2
108119
git lfs checkout folder2
109120
[ "$contents" = "$(cat folder2/nested.dat)" ]
121+
assert_clean_status
110122

111123
echo "test folder param with pre-existing directory"
112124
rm -rf folder2
113125
mkdir folder2
114126
git lfs checkout folder2
115127
[ "$contents" = "$(cat folder2/nested.dat)" ]
128+
assert_clean_status
116129

117130
echo "test folder param with glob match"
118131
rm -rf folder2
119132
git lfs checkout 'folder2/**'
120133
[ "$contents" = "$(cat folder2/nested.dat)" ]
134+
assert_clean_status
121135

122136
echo "test '.' in current dir"
123137
rm -rf file1.dat file2.dat file3.dat folder1/nested.dat folder2/nested.dat
@@ -127,6 +141,7 @@ begin_test "checkout"
127141
[ "$contents" = "$(cat file3.dat)" ]
128142
[ "$contents" = "$(cat folder1/nested.dat)" ]
129143
[ "$contents" = "$(cat folder2/nested.dat)" ]
144+
assert_clean_status
130145

131146
echo "test checkout with missing data doesn't fail"
132147
git push origin main
@@ -138,6 +153,7 @@ begin_test "checkout"
138153
[ "$(pointer $contents_oid $contentsize)" = "$(cat file3.dat)" ]
139154
[ "$contents" = "$(cat folder1/nested.dat)" ]
140155
[ "$contents" = "$(cat folder2/nested.dat)" ]
156+
assert_clean_worktree_with_exceptions "file[123]\.dat"
141157
)
142158
end_test
143159

t/t-pull.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ begin_test "pull"
8282
assert_local_object "$contents_oid" 1
8383
assert_local_object "$contents2_oid" 1
8484
assert_local_object "$contents3_oid" 3
85+
assert_clean_status
8586
git lfs fsck
8687

8788
echo "lfs pull with remote"
@@ -154,6 +155,7 @@ begin_test "pull"
154155
assert_local_object "$contents_oid" 1
155156
refute_local_object "$contents2_oid"
156157
assert_local_object "$contents3_oid" 3
158+
assert_clean_worktree_with_exceptions '\\303\\241\.dat'
157159

158160
git lfs pull -I "*.dat"
159161
[ "A" = "$(cat "á.dat")" ]
@@ -198,6 +200,7 @@ begin_test "pull"
198200
refute_local_object "$contents_oid"
199201
assert_local_object "$contents2_oid" 1
200202
assert_local_object "$contents3_oid" 3
203+
assert_clean_worktree_with_exceptions "a\.dat"
201204

202205
pushd dir
203206
git lfs pull -I "*.dat"

t/testhelpers.sh

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,11 +376,24 @@ assert_hooks() {
376376
[ -x "$git_root/hooks/pre-push" ]
377377
}
378378

379+
assert_clean_worktree() {
380+
[ -z "$(git diff-index HEAD)" ]
381+
}
382+
383+
assert_clean_worktree_with_exceptions() {
384+
local exceptions="$1"
385+
386+
[ -z "$(git diff-index HEAD | grep -v -E "$exceptions")" ]
387+
}
388+
379389
assert_clean_status() {
390+
assert_clean_worktree
391+
380392
status="$(git status)"
381-
echo "$status" | grep "working tree clean" || {
393+
echo "$status" | grep "working \(directory\|tree\) clean" || {
382394
echo $status
383395
git lfs status
396+
exit 1
384397
}
385398
}
386399

0 commit comments

Comments
 (0)