2016-10-26 17:19:16 +00:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
2018-07-10 18:48:02 +00:00
|
|
|
. "$(dirname "$0")/testlib.sh"
|
2016-10-26 17:19:16 +00:00
|
|
|
|
|
|
|
# HACK(taylor): git uses ".g<hash>" in the version name to signal that it is
|
|
|
|
# from the "next" branch, which is the only (current) version of Git that has
|
|
|
|
# support for the filter protocol.
|
|
|
|
#
|
2016-11-22 09:01:14 +00:00
|
|
|
ensure_git_version_isnt $VERSION_LOWER "2.11.0"
|
2016-10-26 17:19:16 +00:00
|
|
|
|
|
|
|
begin_test "filter process: checking out a branch"
|
|
|
|
(
|
|
|
|
set -e
|
|
|
|
|
|
|
|
reponame="filter_process_checkout"
|
|
|
|
setup_remote_repo "$reponame"
|
|
|
|
clone_repo "$reponame" repo
|
|
|
|
|
|
|
|
git lfs track "*.dat"
|
|
|
|
git add .gitattributes
|
|
|
|
git commit -m "initial commit"
|
|
|
|
|
|
|
|
contents_a="contents_a"
|
|
|
|
contents_a_oid="$(calc_oid $contents_a)"
|
2018-09-10 14:57:10 +00:00
|
|
|
printf "%s" "$contents_a" > a.dat
|
2016-10-26 17:19:16 +00:00
|
|
|
|
|
|
|
git add a.dat
|
|
|
|
git commit -m "add a.dat"
|
|
|
|
|
|
|
|
git checkout -b b
|
|
|
|
|
|
|
|
contents_b="contents_b"
|
|
|
|
contents_b_oid="$(calc_oid $contents_b)"
|
2018-09-10 14:57:10 +00:00
|
|
|
printf "%s" "$contents_b" > b.dat
|
2016-10-26 17:19:16 +00:00
|
|
|
|
|
|
|
git add b.dat
|
|
|
|
git commit -m "add b.dat"
|
|
|
|
|
|
|
|
git push origin --all
|
|
|
|
|
|
|
|
pushd ..
|
2016-11-10 18:26:07 +00:00
|
|
|
# Git will choose filter.lfs.process over `filter.lfs.clean` and
|
|
|
|
# `filter.lfs.smudge`
|
2017-08-12 04:18:56 +00:00
|
|
|
GIT_TRACE_PACKET=1 git \
|
2016-11-08 20:05:12 +00:00
|
|
|
-c "filter.lfs.process=git-lfs filter-process" \
|
2016-11-10 18:26:07 +00:00
|
|
|
-c "filter.lfs.clean=false"\
|
|
|
|
-c "filter.lfs.smudge=false" \
|
2016-10-26 17:19:16 +00:00
|
|
|
-c "filter.lfs.required=true" \
|
|
|
|
clone "$GITSERVER/$reponame" "$reponame-assert"
|
|
|
|
|
|
|
|
cd "$reponame-assert"
|
|
|
|
|
2020-06-29 16:18:57 +00:00
|
|
|
# Assert that we are on the "main" branch, and have a.dat
|
|
|
|
[ "main" = "$(git rev-parse --abbrev-ref HEAD)" ]
|
2016-10-26 17:19:16 +00:00
|
|
|
[ "$contents_a" = "$(cat a.dat)" ]
|
2020-06-29 16:18:57 +00:00
|
|
|
assert_pointer "main" "a.dat" "$contents_a_oid" 10
|
2016-10-26 17:19:16 +00:00
|
|
|
|
|
|
|
git checkout b
|
|
|
|
|
|
|
|
# Assert that we are on the "b" branch, and have b.dat
|
|
|
|
[ "b" = "$(git rev-parse --abbrev-ref HEAD)" ]
|
|
|
|
[ "$contents_b" = "$(cat b.dat)" ]
|
|
|
|
assert_pointer "b" "b.dat" "$contents_b_oid" 10
|
|
|
|
popd
|
|
|
|
)
|
|
|
|
end_test
|
2016-11-02 23:07:35 +00:00
|
|
|
|
commands,t: gitignore matching for fetch filters
The "lfs.fetchinclude" and "lfs.fetchexclude" Git configuration
options, if set, are used to control the action of a number of Git
LFS commands. Since PR #4556, the "git lfs clone", "git lfs fetch",
and "git lfs pull" commands have strictly applied gitignore(5)-style
matching rules to these configuration options.
However, other commands including "git lfs filter-process" and
"git lfs smudge" now apply gitattributes(5)-style matching
rules to these same configuration options, leading to confusion.
We therefore revise all remaining uses of these configuration
options to also use gitignore-style matching rules.
We also add new tests for the "git lfs filter-process" and "git lfs
fsck" commands and adjust or expand existing tests for the "git lfs
prune" and "git lfs smudge" commands in order to confirm that
gitignore-style matching is used for all of them. These new and
updated tests fail if gitattributes-style matching is used instead.
(Note that the "git lfs migrate" command does not require any changes
because it does not read the "lfs.fetch*" configuration options.
Instead, it supplies a "false" value for the "useFetchOptions" flag
to the determineIncludeExcludePaths() function, so any "lfs.fetch*"
configuration values are ignored. This is significant because
"git lfs migrate" deliberately uses gitattributes-style matching
for any path patterns supplied via its -I/-X command-line arguments,
unlike all other commands that accept -I/-X arguments as overrides
for the "lfs.fetch*" configuration options.)
2022-04-18 07:24:53 +00:00
|
|
|
begin_test "filter process: include/exclude"
|
|
|
|
(
|
|
|
|
set -e
|
|
|
|
|
|
|
|
reponame="$(basename "$0" ".sh")-includeexclude"
|
|
|
|
setup_remote_repo "$reponame"
|
|
|
|
clone_repo "$reponame" "$reponame"
|
|
|
|
|
|
|
|
git lfs track "*.dat"
|
|
|
|
mkdir -p foo/bar
|
|
|
|
|
|
|
|
contents_a="contents_a"
|
|
|
|
contents_a_oid="$(calc_oid $contents_a)"
|
|
|
|
printf "%s" "$contents_a" > a.dat
|
|
|
|
cp a.dat foo
|
|
|
|
cp a.dat foo/bar
|
|
|
|
|
|
|
|
git add .gitattributes a.dat foo
|
|
|
|
git commit -m "initial commit"
|
|
|
|
|
|
|
|
git push origin main
|
|
|
|
|
|
|
|
# The Git LFS objects for a.dat and foo/bar/a.dat would both download except
|
|
|
|
# we're going to prevent them from doing so with include/exclude.
|
|
|
|
# We also need to prevent MSYS from rewriting /foo into a Windows path.
|
|
|
|
MSYS_NO_PATHCONV=1 git config --global "lfs.fetchinclude" "/foo"
|
|
|
|
MSYS_NO_PATHCONV=1 git config --global "lfs.fetchexclude" "/foo/bar"
|
|
|
|
|
|
|
|
pushd ..
|
|
|
|
# Git will choose filter.lfs.process over `filter.lfs.clean` and
|
|
|
|
# `filter.lfs.smudge`
|
|
|
|
GIT_TRACE_PACKET=1 git \
|
|
|
|
-c "filter.lfs.process=git-lfs filter-process" \
|
|
|
|
-c "filter.lfs.clean=false"\
|
|
|
|
-c "filter.lfs.smudge=false" \
|
|
|
|
-c "filter.lfs.required=true" \
|
|
|
|
clone "$GITSERVER/$reponame" "$reponame-assert"
|
|
|
|
|
|
|
|
cd "$reponame-assert"
|
|
|
|
|
|
|
|
pointer="$(pointer "$contents_a_oid" 10)"
|
|
|
|
|
|
|
|
[ "$pointer" = "$(cat a.dat)" ]
|
|
|
|
assert_pointer "main" "a.dat" "$contents_a_oid" 10
|
|
|
|
|
|
|
|
[ "$contents_a" = "$(cat foo/a.dat)" ]
|
|
|
|
assert_pointer "main" "foo/a.dat" "$contents_a_oid" 10
|
|
|
|
|
|
|
|
[ "$pointer" = "$(cat foo/bar/a.dat)" ]
|
|
|
|
assert_pointer "main" "foo/bar/a.dat" "$contents_a_oid" 10
|
|
|
|
popd
|
|
|
|
)
|
|
|
|
end_test
|
|
|
|
|
2016-11-02 23:07:35 +00:00
|
|
|
begin_test "filter process: adding a file"
|
|
|
|
(
|
|
|
|
set -e
|
|
|
|
|
|
|
|
reponame="filter_process_add"
|
|
|
|
setup_remote_repo "$reponame"
|
|
|
|
clone_repo "$reponame" "$reponame"
|
|
|
|
|
|
|
|
git lfs track "*.dat"
|
|
|
|
git add .gitattributes
|
|
|
|
git commit -m "initial commit"
|
|
|
|
|
|
|
|
contents="contents"
|
|
|
|
contents_oid="$(calc_oid "$contents")"
|
2018-09-10 14:57:10 +00:00
|
|
|
printf "%s" "$contents" > a.dat
|
2016-11-02 23:07:35 +00:00
|
|
|
|
|
|
|
git add a.dat
|
|
|
|
|
|
|
|
expected="$(pointer "$contents_oid" "${#contents}")"
|
|
|
|
got="$(git cat-file -p :a.dat)"
|
|
|
|
|
|
|
|
diff -u <(echo "$expected") <(echo "$got")
|
|
|
|
)
|
|
|
|
end_test
|
2016-11-22 08:51:55 +00:00
|
|
|
|
|
|
|
# https://github.com/git-lfs/git-lfs/issues/1697
|
|
|
|
begin_test "filter process: add a file with 1024 bytes"
|
|
|
|
(
|
|
|
|
set -e
|
|
|
|
|
2016-11-22 11:32:39 +00:00
|
|
|
mkdir repo-issue-1697
|
|
|
|
cd repo-issue-1697
|
2016-11-22 08:51:55 +00:00
|
|
|
git init
|
|
|
|
git lfs track "*.dat"
|
|
|
|
dd if=/dev/zero of=first.dat bs=1024 count=1
|
|
|
|
printf "any contents" > second.dat
|
|
|
|
git add .
|
|
|
|
)
|
|
|
|
end_test
|
|
|
|
|
filter-process: avoid hang when using git hash-object --stdin
When we use git hash-object --stdin with the --path option, Git applies
filters to the object, so Git LFS is invoked. However, if the object
provided is less than 1024 bytes in size, we would hang. This occurred
because of our packet reader didn't quite implement the io.Reader
interface completely: if it returned a non-zero value and io.EOF, the
next call to Read would not return 0 and io.EOF. Instead, it would try
to read from stdin, which would not be sending us more data until we
provided a response, so we would hang.
To solve this, keep track of the EOF and always return it on subsequent
Read calls. In addition, don't process the callback to write the file
in this case, since we don't actually want to write into the working
tree.
2019-11-04 19:20:45 +00:00
|
|
|
begin_test "filter process: hash-object --stdin --path does not hang"
|
|
|
|
(
|
|
|
|
set -e
|
2016-11-22 08:51:55 +00:00
|
|
|
|
filter-process: avoid hang when using git hash-object --stdin
When we use git hash-object --stdin with the --path option, Git applies
filters to the object, so Git LFS is invoked. However, if the object
provided is less than 1024 bytes in size, we would hang. This occurred
because of our packet reader didn't quite implement the io.Reader
interface completely: if it returned a non-zero value and io.EOF, the
next call to Read would not return 0 and io.EOF. Instead, it would try
to read from stdin, which would not be sending us more data until we
provided a response, so we would hang.
To solve this, keep track of the EOF and always return it on subsequent
Read calls. In addition, don't process the callback to write the file
in this case, since we don't actually want to write into the working
tree.
2019-11-04 19:20:45 +00:00
|
|
|
mkdir repo-hash-object
|
|
|
|
cd repo-hash-object
|
|
|
|
git init
|
|
|
|
git lfs track "*.dat"
|
|
|
|
contents="test"
|
|
|
|
contents_oid="$(calc_oid "$contents")"
|
|
|
|
expected=$(pointer "$contents_oid" 4 | git hash-object --stdin)
|
|
|
|
|
|
|
|
dd if=/dev/zero of=first.dat bs=1000 count=1
|
|
|
|
echo a > second.dat
|
|
|
|
# Works for existing file longer than this one.
|
|
|
|
output=$(printf test | git hash-object --path first.dat --stdin)
|
|
|
|
[ "$expected" = "$output" ]
|
|
|
|
# Works for existing file shorter than this one.
|
|
|
|
output=$(printf test | git hash-object --path second.dat --stdin)
|
|
|
|
[ "$expected" = "$output" ]
|
|
|
|
# Works for absent file.
|
|
|
|
output=$(printf test | git hash-object --path third.dat --stdin)
|
|
|
|
[ "$expected" = "$output" ]
|
|
|
|
|
|
|
|
dd if=/dev/zero of=large.dat bs=65537 count=1
|
|
|
|
oid=$(calc_oid_file large.dat)
|
|
|
|
expected=$(pointer "$oid" 65537 | git hash-object --stdin)
|
|
|
|
output=$(git hash-object --path third.dat --stdin <large.dat)
|
|
|
|
[ "$expected" = "$output" ]
|
|
|
|
git add .
|
|
|
|
)
|
|
|
|
end_test
|
smudge: honor GIT_LFS_SKIP_SMUDGE with checkout-index
In most cases, users use a relatively modern version of Git which
supports the filter-process code and delayed smudging. This is valuable
because it makes things much faster. However, it also uses a different
code path from the non-delayed path. This non-delayed path is also used
by git checkout-index.
The non-delayed path doesn't work properly, however, if the data is
already on disk. In such a case, we simply ask the smudge filter not to
download the data, but since it's already on disk, we feed it out
anyway.
Let's solve this by simply not invoking the filter and emitting the
pointer if we've asked to skip. We do the same thing if the file
doesn't match the filter, since the comment at the top of the function
states that's what's supposed to happen here as well.
Once we've done that, the variable download is always true, so remove
all the code which considers that it might be false to simplify the code
somewhat.
2022-02-07 16:00:01 +00:00
|
|
|
|
|
|
|
begin_test "filter process: checking out a branch with --skip-smudge and checkout-index"
|
|
|
|
(
|
|
|
|
set -e
|
|
|
|
|
|
|
|
reponame="filter-process-skip-smudge-checkout-index"
|
|
|
|
setup_remote_repo "$reponame"
|
|
|
|
clone_repo "$reponame" "$reponame"
|
|
|
|
|
|
|
|
git lfs track "*.dat"
|
|
|
|
git add .gitattributes
|
|
|
|
git commit -m "initial commit"
|
|
|
|
|
|
|
|
contents_a="contents_a"
|
|
|
|
contents_a_oid="$(calc_oid $contents_a)"
|
|
|
|
printf "%s" "$contents_a" > a.dat
|
|
|
|
|
|
|
|
git add a.dat
|
|
|
|
git commit -m "add a.dat"
|
|
|
|
|
|
|
|
git checkout -b b
|
|
|
|
|
|
|
|
contents_b="contents_b"
|
|
|
|
contents_b_oid="$(calc_oid $contents_b)"
|
|
|
|
printf "%s" "$contents_b" > b.dat
|
|
|
|
|
|
|
|
git add b.dat
|
|
|
|
git commit -m "add b.dat"
|
|
|
|
|
|
|
|
git lfs install --local --skip-smudge
|
|
|
|
|
|
|
|
git checkout main
|
|
|
|
|
|
|
|
rm a.dat
|
|
|
|
git checkout-index -af
|
|
|
|
git lfs pointer --check --file a.dat
|
|
|
|
|
|
|
|
assert_pointer "main" "a.dat" "$contents_a_oid" 10
|
|
|
|
|
|
|
|
git checkout b
|
|
|
|
|
|
|
|
rm *.dat
|
|
|
|
git checkout-index -af
|
|
|
|
git lfs pointer --check --file a.dat
|
|
|
|
git lfs pointer --check --file b.dat
|
|
|
|
|
|
|
|
# Assert that we are on the "b" branch, and have b.dat
|
|
|
|
assert_pointer "b" "b.dat" "$contents_b_oid" 10
|
|
|
|
)
|
|
|
|
end_test
|
tq: avoid spawning SSH process needlessly
When a user invokes `git archive` with LFS files, `git lfs
filter-process` is invoked to smudge the LFS files. However, currently
when we instantiate the manifest object as part of that, an attempt is
made to connect to the remote using SSH, which we don't want to do
unless necessary. For example, if the user already has all the files
locally, the network connection is needless and serves only to waste
resources.
In the previous commit, we made our manifest an abstract interface with
a single implementing type: a concrete manifest. Now, introduce a lazy
manifest, which can upgrade to a concrete manifest but doesn't
instantiate one until that happens. This allows us to instantiate a
manifest without making the SSH connection, and we can delay the SSH
connection until it's really needed, if at all.
Add a test for this case as well.
2023-03-10 16:18:00 +00:00
|
|
|
|
|
|
|
begin_test "filter process: git archive does not invoke SSH"
|
|
|
|
(
|
|
|
|
set -e
|
|
|
|
|
|
|
|
setup_pure_ssh
|
|
|
|
|
|
|
|
reponame="filter-process-archive"
|
|
|
|
setup_remote_repo "$reponame"
|
|
|
|
clone_repo "$reponame" "$reponame"
|
|
|
|
|
|
|
|
sshurl=$(ssh_remote "$reponame")
|
|
|
|
git config lfs.url "$sshurl"
|
|
|
|
|
|
|
|
contents="test"
|
|
|
|
git lfs track "*.dat"
|
|
|
|
printf "%s" "$contents" > test.dat
|
|
|
|
git add .gitattributes test.dat
|
|
|
|
git commit -m "initial commit"
|
|
|
|
|
|
|
|
git push origin main 2>&1
|
|
|
|
cd ..
|
|
|
|
GIT_TRACE=1 git clone "$sshurl" "$reponame-2" 2>&1 | tee trace.log
|
|
|
|
grep "lfs-ssh-echo.*git-lfs-transfer .*$reponame.git download" trace.log
|
|
|
|
cd "$reponame-2"
|
|
|
|
GIT_TRACE=1 GIT_TRACE_PACKET=1 git archive -o foo.tar HEAD 2>&1 | tee archive.log
|
|
|
|
grep 'pure SSH' archive.log && exit 1
|
|
|
|
true
|
|
|
|
)
|
|
|
|
end_test
|