diff --git a/commands/command_checkout.go b/commands/command_checkout.go index 0a05795a..389570c7 100644 --- a/commands/command_checkout.go +++ b/commands/command_checkout.go @@ -68,6 +68,7 @@ func checkoutFromFetchChan(include []string, exclude []string, in chan *lfs.Wrap // Launch git update-index c := make(chan *lfs.WrappedPointer) + var wait sync.WaitGroup wait.Add(1) @@ -142,7 +143,19 @@ func checkoutAll() { // Populate the working copy with the real content of objects where the file is // either missing, or contains a matching pointer placeholder, from a list of pointers. // If the file exists but has other content it is left alone +// Callers of this function MUST NOT Panic or otherwise exit the process +// without waiting for this function to shut down. If the process exits while +// update-index is in the middle of processing a file the git index can be left +// in a locked state. func checkoutWithChan(in <-chan *lfs.WrappedPointer) { + // Get a converter from repo-relative to cwd-relative + // Since writing data & calling git update-index must be relative to cwd + repopathchan := make(chan string, 1) + cwdpathchan, err := lfs.ConvertRepoFilesRelativeToCwd(repopathchan) + if err != nil { + Panic(err, "Could not convert file paths") + } + // Fire up the update-index command cmd := exec.Command("git", "update-index", "-q", "--refresh", "--stdin") updateIdxStdin, err := cmd.StdinPipe() @@ -154,13 +167,10 @@ func checkoutWithChan(in <-chan *lfs.WrappedPointer) { Panic(err, "Could not update the index") } - // Get a converter from repo-relative to cwd-relative - // Since writing data & calling git update-index must be relative to cwd - repopathchan := make(chan string, 1) - cwdpathchan, err := lfs.ConvertRepoFilesRelativeToCwd(repopathchan) - if err != nil { - Panic(err, "Could not convert file paths") - } + // From this point on, git update-index is running. Code in this loop MUST + // NOT Panic() or otherwise cause the process to exit. If the process exits + // while update-index is in the middle of updating, the index can remain in a + // locked state. // As files come in, write them to the wd and update the index for pointer := range in { @@ -172,24 +182,27 @@ func checkoutWithChan(in <-chan *lfs.WrappedPointer) { // File has non-pointer content, leave it alone continue } - Panic(err, "Problem accessing %v", pointer.Name) + LoggedError(err, "Problem accessing %v", pointer.Name) + continue } + if filepointer != nil && filepointer.Oid != pointer.Oid { // User has probably manually reset a file to another commit // while leaving it a pointer; don't mess with this continue } - // OK now we can (over)write the file content + repopathchan <- pointer.Name cwdfilepath := <-cwdpathchan - // smudge but set auto-download to false + err = lfs.PointerSmudgeToFile(cwdfilepath, pointer.Pointer, false, nil) if err != nil { if err == lfs.DownloadDeclinedError { // acceptable error, data not local (fetch not run or include/exclude) LoggedError(err, "Skipped checkout for %v, content not local. Use fetch to download.", pointer.Name) } else { - Panic(err, "Could not checkout file") + LoggedError(err, "Could not checkout file") + continue } } @@ -201,5 +214,4 @@ func checkoutWithChan(in <-chan *lfs.WrappedPointer) { if err := cmd.Wait(); err != nil { Panic(err, "Error updating the git index") } - }