checkout: gracefully handle files deleted from the index
Right now, when someone deletes a pointer from the index with `git rm` and then runs `git lfs checkout`, the operation fails with a message of "Could not update the index" because our invocation of `git update-index` is missing the `--add` flag. Obviously, the user does not expect an error in this case, and `git checkout` simply ignores files staged for deletation, so let's do the same thing. If a file on disk is deleted, check the index with `git diff-index` to see if it's deleted from `HEAD`. If so, ignore the file, just like Git does. Note that we use `git diff-index` specifically because it doesn't refresh the index and is therefore much cheaper than alternatives, such as `git status`, which might do that.
This commit is contained in:
parent
19a702e780
commit
d3299175ff
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/git-lfs/git-lfs/v3/config"
|
"github.com/git-lfs/git-lfs/v3/config"
|
||||||
@ -69,7 +70,19 @@ func (c *singleCheckout) Run(p *lfs.WrappedPointer) {
|
|||||||
|
|
||||||
// Check the content - either missing or still this pointer (not exist is ok)
|
// Check the content - either missing or still this pointer (not exist is ok)
|
||||||
filepointer, err := lfs.DecodePointerFromFile(cwdfilepath)
|
filepointer, err := lfs.DecodePointerFromFile(cwdfilepath)
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
output, err := git.DiffIndexWithPaths("HEAD", true, []string{p.Name})
|
||||||
|
if err != nil {
|
||||||
|
LoggedError(err, tr.Tr.Get("Checkout error trying to run diff-index: %s", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(output, ":100644 000000 ") || strings.HasPrefix(output, ":100755 000000 ") {
|
||||||
|
// This file is deleted in the index. Don't try
|
||||||
|
// to check it out.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if errors.IsNotAPointerError(err) || errors.IsBadPointerKeyError(err) {
|
if errors.IsNotAPointerError(err) || errors.IsBadPointerKeyError(err) {
|
||||||
// File has non-pointer content, leave it alone
|
// File has non-pointer content, leave it alone
|
||||||
return
|
return
|
||||||
@ -78,6 +91,7 @@ func (c *singleCheckout) Run(p *lfs.WrappedPointer) {
|
|||||||
LoggedError(err, tr.Tr.Get("Checkout error: %s", err))
|
LoggedError(err, tr.Tr.Get("Checkout error: %s", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if filepointer != nil && filepointer.Oid != p.Oid {
|
if filepointer != nil && filepointer.Oid != p.Oid {
|
||||||
// User has probably manually reset a file to another commit
|
// User has probably manually reset a file to another commit
|
||||||
|
17
git/git.go
17
git/git.go
@ -258,6 +258,23 @@ func DiffIndex(ref string, cached bool, refresh bool, workingDir string) (*bufio
|
|||||||
return bufio.NewScanner(cmd.Stdout), nil
|
return bufio.NewScanner(cmd.Stdout), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DiffIndexWithPaths(ref string, cached bool, paths []string) (string, error) {
|
||||||
|
args := []string{"diff-index"}
|
||||||
|
if cached {
|
||||||
|
args = append(args, "--cached")
|
||||||
|
}
|
||||||
|
args = append(args, ref)
|
||||||
|
args = append(args, "--")
|
||||||
|
args = append(args, paths...)
|
||||||
|
|
||||||
|
output, err := gitSimple(args...)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return output, nil
|
||||||
|
}
|
||||||
|
|
||||||
func HashObject(r io.Reader) (string, error) {
|
func HashObject(r io.Reader) (string, error) {
|
||||||
cmd, err := gitNoLFS("hash-object", "--stdin")
|
cmd, err := gitNoLFS("hash-object", "--stdin")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -51,6 +51,14 @@ begin_test "checkout"
|
|||||||
grep 'accepting "file1.dat"' checkout.log
|
grep 'accepting "file1.dat"' checkout.log
|
||||||
grep 'rejecting "file1.dat"' checkout.log && exit 1
|
grep 'rejecting "file1.dat"' checkout.log && exit 1
|
||||||
|
|
||||||
|
git rm file1.dat
|
||||||
|
|
||||||
|
echo "checkout should skip replacing files deleted in index"
|
||||||
|
git lfs checkout
|
||||||
|
[ ! -f file1.dat ]
|
||||||
|
|
||||||
|
git reset --hard
|
||||||
|
|
||||||
# Remove the working directory
|
# Remove the working directory
|
||||||
rm -rf file1.dat file2.dat file3.dat folder1/nested.dat folder2/nested.dat
|
rm -rf file1.dat file2.dat file3.dat folder1/nested.dat folder2/nested.dat
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user