From b4f3f371f58b962497d36ec453ac18336e239be0 Mon Sep 17 00:00:00 2001 From: Chris Darroch Date: Mon, 19 Feb 2024 23:43:56 -0800 Subject: [PATCH] commands,t: fix output of ls-files in subdirectory 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: https://github.com/git-lfs/git-lfs/issues/1189#issuecomment-1950664657 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 #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 #2540), so we follow that model here as well. --- commands/command_ls_files.go | 3 +- t/t-ls-files.sh | 264 +++++++++++++++++++++++++++++++++++ 2 files changed, 266 insertions(+), 1 deletion(-) diff --git a/commands/command_ls_files.go b/commands/command_ls_files.go index 4647b8d093..0381dc83d3 100644 --- a/commands/command_ls_files.go +++ b/commands/command_ls_files.go @@ -3,6 +3,7 @@ package commands import ( "encoding/json" "os" + "path/filepath" "strings" "github.com/git-lfs/git-lfs/v3/errors" @@ -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 } diff --git a/t/t-ls-files.sh b/t/t-ls-files.sh index bd13ed03eb..749c27cc6f 100755 --- a/t/t-ls-files.sh +++ b/t/t-ls-files.sh @@ -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 @@ -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 @@ -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 @@ -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