Example #1
func TestDecodeTinyFile(t *testing.T) {
	ex := "this is not a git-lfs file!"
	p, err := DecodePointer(bytes.NewBufferString(ex))
	if p != nil {
		t.Errorf("pointer was decoded: %v", p)

	if !errors.IsNotAPointerError(err) {
		t.Errorf("error is not a NotAPointerError: %s: '%v'", reflect.TypeOf(err), err)
Example #2
// 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")

	// Don't fire up the update-index command until we have at least one file to
	// give it. Otherwise git interprets the lack of arguments to mean param-less update-index
	// which can trigger entire working copy to be re-examined, which triggers clean filters
	// and which has unexpected side effects (e.g. downloading filtered-out files)
	var cmd *exec.Cmd
	var updateIdxStdin io.WriteCloser
	var updateIdxOut bytes.Buffer

	// 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

	manifest := TransferManifest()

	for pointer := range in {

		// Check the content - either missing or still this pointer (not exist is ok)
		filepointer, err := lfs.DecodePointerFromFile(pointer.Name)
		if err != nil && !os.IsNotExist(err) {
			if errors.IsNotAPointerError(err) {
				// File has non-pointer content, leave it alone
			LoggedError(err, "Problem accessing %v", pointer.Name)

		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

		repopathchan <- pointer.Name
		cwdfilepath := <-cwdpathchan

		err = lfs.PointerSmudgeToFile(cwdfilepath, pointer.Pointer, false, manifest, nil)
		if err != nil {
			if errors.IsDownloadDeclinedError(err) {
				// 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 {
				LoggedError(err, "Could not checkout file")

		if cmd == nil {
			// Fire up the update-index command
			cmd = exec.Command("git", "update-index", "-q", "--refresh", "--stdin")
			cmd.Stdout = &updateIdxOut
			cmd.Stderr = &updateIdxOut
			updateIdxStdin, err = cmd.StdinPipe()
			if err != nil {
				Panic(err, "Could not update the index")

			if err := cmd.Start(); err != nil {
				Panic(err, "Could not update the index")


		updateIdxStdin.Write([]byte(cwdfilepath + "\n"))

	if cmd != nil && updateIdxStdin != nil {
		if err := cmd.Wait(); err != nil {
			LoggedError(err, "Error updating the git index:\n%s", updateIdxOut.String())