Skip to content

Commit

Permalink
commands,t: fix output of ls-files in subdirectory
Browse files Browse the repository at this point in the history
As reported in the following issue comment, the "git lfs ls-files"
command does not report the presence or absence of Git LFS object files
in the working tree correctly unless the command is run the root
directory of the working tree:

git-lfs#1189 (comment)

We resolve this problem by ensuring that the full, canonicalized path
to the working tree is prepended by the fileExistsOfSize() helper
function to the paths of the files whose presence it checks with the
os.Stat() function.

We then add two new tests which validate the correct behaviour of the
command when it is run in a subdirectory of the working tree.  These
tests would fail without the accompanying bug fix to the command.

We also add another two new tests which confirm the same behaviour when
the --json option is used.  The use of a separate test for this option was
preferred in PR git-lfs#5007 when the --json option was first introduced (rather
than overloading the existing tests, as was done for the --debug option
when it was added in PR git-lfs#2540), so we follow that model here as well.
  • Loading branch information
chrisd8088 committed Feb 22, 2024
1 parent c26ae12 commit b4f3f37
Show file tree
Hide file tree
Showing 2 changed files with 266 additions and 1 deletion.
3 changes: 2 additions & 1 deletion commands/command_ls_files.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package commands
import (
"encoding/json"
"os"
"path/filepath"
"strings"

"github.com/git-lfs/git-lfs/v3/errors"
Expand Down Expand Up @@ -181,7 +182,7 @@ func lsFilesCommand(cmd *cobra.Command, args []string) {
// Returns true if a pointer appears to be properly smudge on checkout
func fileExistsOfSize(p *lfs.WrappedPointer) bool {
path := cfg.Filesystem().DecodePathname(p.Name)
info, err := os.Stat(path)
info, err := os.Stat(filepath.Join(cfg.LocalWorkingDir(), path))
return err == nil && info.Size() == p.Size
}

Expand Down
264 changes: 264 additions & 0 deletions t/t-ls-files.sh
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,56 @@ EOF)
)
end_test
begin_test "ls-files: run within subdirectory"
(
set -e
reponame="ls-files-in-subdir"
git init "$reponame"
cd "$reponame"
git lfs track "*.dat"
git add .gitattributes
git commit -m "initial commit"
mkdir subdir
contents1="a"
oid1="$(calc_oid "$contents1")"
printf "%s" "$contents1" > a.dat
contents2="b"
oid2="$(calc_oid "$contents2")"
printf "%s" "$contents2" > subdir/b.dat
cd subdir
[ "" = "$(git lfs ls-files)" ]
git add ../a.dat b.dat
expected="${oid1:0:10} * a.dat
${oid2:0:10} * subdir/b.dat"
[ "$expected" = "$(git lfs ls-files)" ]
diff -u <(git lfs ls-files --debug) <(cat <<-EOF
filepath: a.dat
size: 1
checkout: true
download: true
oid: sha256 $oid1
version: https://git-lfs.github.com/spec/v1
filepath: subdir/b.dat
size: 1
checkout: true
download: true
oid: sha256 $oid2
version: https://git-lfs.github.com/spec/v1
EOF)
)
end_test
begin_test "ls-files: checkout and download status"
(
set -e
Expand Down Expand Up @@ -137,6 +187,86 @@ EOF)
)
end_test
begin_test "ls-files: checkout and download status (run within subdirectory)"
(
set -e
reponame="ls-files-status-in-subdir"
git init "$reponame"
cd "$reponame"
git lfs track "*.dat"
git add .gitattributes
git commit -m "initial commit"
contents1="a"
oid1="$(calc_oid "$contents1")"
printf "%s" "$contents1" > a.dat
contents2="b"
oid2="$(calc_oid "$contents2")"
printf "%s" "$contents2" > b.dat
mkdir subdir
cd subdir
contents3="c"
oid3="$(calc_oid "$contents3")"
printf "%s" "$contents3" > c.dat
contents4="d"
oid4="$(calc_oid "$contents4")"
printf "%s" "$contents4" > d.dat
[ "" = "$(git lfs ls-files)" ]
# Note that if we don't remove b.dat and d.dat from the working tree as
# well as the Git LFS object cache, Git calls (as invoked by Git LFS) may
# restore the cache copies from the working tree copies by re-invoking
# Git LFS in "clean" filter mode.
git add ../a.dat ../b.dat c.dat d.dat
rm ../a.dat ../b.dat c.dat d.dat
rm "../.git/lfs/objects/${oid2:0:2}/${oid2:2:2}/$oid2"
rm "../.git/lfs/objects/${oid4:0:2}/${oid4:2:2}/$oid4"
expected="${oid1:0:10} - a.dat
${oid2:0:10} - b.dat
${oid3:0:10} - subdir/c.dat
${oid4:0:10} - subdir/d.dat"
[ "$expected" = "$(git lfs ls-files)" ]
diff -u <(git lfs ls-files --debug) <(cat <<-EOF
filepath: a.dat
size: 1
checkout: false
download: true
oid: sha256 $oid1
version: https://git-lfs.github.com/spec/v1
filepath: b.dat
size: 1
checkout: false
download: false
oid: sha256 $oid2
version: https://git-lfs.github.com/spec/v1
filepath: subdir/c.dat
size: 1
checkout: false
download: true
oid: sha256 $oid3
version: https://git-lfs.github.com/spec/v1
filepath: subdir/d.dat
size: 1
checkout: false
download: false
oid: sha256 $oid4
version: https://git-lfs.github.com/spec/v1
EOF)
)
end_test
begin_test "ls-files: --size"
(
set -e
Expand Down Expand Up @@ -721,6 +851,57 @@ EOF)
)
end_test
begin_test "ls-files: run within subdirectory (--json)"
(
set -e
reponame="ls-files-in-subdir-json"
git init "$reponame"
cd "$reponame"
git lfs track "*.dat"
git add .gitattributes
git commit -m "initial commit"
mkdir subdir
contents1="a"
oid1="$(calc_oid "$contents1")"
printf "%s" "$contents1" > a.dat
contents2="b"
oid2="$(calc_oid "$contents2")"
printf "%s" "$contents2" > subdir/b.dat
cd subdir
git add ../a.dat b.dat
diff -u <(git lfs ls-files --json) <(cat <<-EOF
{
"files": [
{
"name": "a.dat",
"size": 1,
"checkout": true,
"downloaded": true,
"oid_type": "sha256",
"oid": "$oid1",
"version": "https://git-lfs.github.com/spec/v1"
},
{
"name": "subdir/b.dat",
"size": 1,
"checkout": true,
"downloaded": true,
"oid_type": "sha256",
"oid": "$oid2",
"version": "https://git-lfs.github.com/spec/v1"
}
]
}
EOF)
)
end_test
begin_test "ls-files: checkout and download status (--json)"
(
set -e
Expand Down Expand Up @@ -774,3 +955,86 @@ begin_test "ls-files: checkout and download status (--json)"
EOF)
)
end_test
begin_test "ls-files: checkout and download status (run within subdirectory) (--json)"
(
set -e
reponame="ls-files-status-in-subdir-json"
git init "$reponame"
cd "$reponame"
git lfs track "*.dat"
git add .gitattributes
git commit -m "initial commit"
contents1="a"
oid1="$(calc_oid "$contents1")"
printf "%s" "$contents1" > a.dat
contents2="b"
oid2="$(calc_oid "$contents2")"
printf "%s" "$contents2" > b.dat
mkdir subdir
cd subdir
contents3="c"
oid3="$(calc_oid "$contents3")"
printf "%s" "$contents3" > c.dat
contents4="d"
oid4="$(calc_oid "$contents4")"
printf "%s" "$contents4" > d.dat
# Note that if we don't remove b.dat and d.dat from the working tree as
# well as the Git LFS object cache, Git calls (as invoked by Git LFS) may
# restore the cache copies from the working tree copies by re-invoking
# Git LFS in "clean" filter mode.
git add ../a.dat ../b.dat c.dat d.dat
rm ../a.dat ../b.dat c.dat d.dat
rm "../.git/lfs/objects/${oid2:0:2}/${oid2:2:2}/$oid2"
rm "../.git/lfs/objects/${oid4:0:2}/${oid4:2:2}/$oid4"
diff -u <(git lfs ls-files --json) <(cat <<-EOF
{
"files": [
{
"name": "a.dat",
"size": 1,
"checkout": false,
"downloaded": true,
"oid_type": "sha256",
"oid": "$oid1",
"version": "https://git-lfs.github.com/spec/v1"
},
{
"name": "b.dat",
"size": 1,
"checkout": false,
"downloaded": false,
"oid_type": "sha256",
"oid": "$oid2",
"version": "https://git-lfs.github.com/spec/v1"
},
{
"name": "subdir/c.dat",
"size": 1,
"checkout": false,
"downloaded": true,
"oid_type": "sha256",
"oid": "$oid3",
"version": "https://git-lfs.github.com/spec/v1"
},
{
"name": "subdir/d.dat",
"size": 1,
"checkout": false,
"downloaded": false,
"oid_type": "sha256",
"oid": "$oid4",
"version": "https://git-lfs.github.com/spec/v1"
}
]
}
EOF)
)
end_test

0 comments on commit b4f3f37

Please sign in to comment.