go-git icon indicating copy to clipboard operation
go-git copied to clipboard

Depth: 1 clone has issues when pulling new commits

Open glibsm opened this issue 5 years ago • 1 comments

Depth: 1 clones fail to pull in new commits from a remote

In this example:

  • There is a repository with three commits
  • Two workers clone it with Depth: 1 at the same time
  • First worker writes a file, commits it and pushes
  • Second worker attempts to pull and gets an error: object not found

Removing the Depth: 1 from the clone options makes the code run, but sort of defeats the point of depth :(

I've tried to re-create the same scenario in the command line git and it worked just fine, so I suspect there may be a problem with how depth is handled.

It's really confusing trying to figure out if this is even a supported feature. For example, https://stackoverflow.com/a/21217267 doesn't make anything terribly conclusive.

Go-git Version

revision = "3bd5e82b2512d85becae9677fa06b5a973fd4cfb"
version = "v4.5.0"

Billy version

revision = "83cf655d40b15b427014d7875d10850f96edba14"
version = "v4.2.0"

Runnable code

Apologies for some boilerplate, not terribly familiar with go-git suites, which would probably reduce the line count.

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"

	"gopkg.in/src-d/go-billy.v4/memfs"
	"gopkg.in/src-d/go-git.v4"
	"gopkg.in/src-d/go-git.v4/plumbing/object"
	"gopkg.in/src-d/go-git.v4/storage/memory"
)

func main() {
	dir, err := ioutil.TempDir("", "depth-pull")
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(dir)

	// plan init a bare repo in temp directory
	_, err = git.PlainInit(dir, true)
	if err != nil {
		log.Fatal(err)
	}
	var opts = &git.CloneOptions{
		URL:   dir,
		Depth: 1, // <-- note the depth in cloning options
	}

	// fill bare repository with three commits that get each pushed
	// important to have more than one commit in the history for this to fail
	bareWorker, _ := git.Clone(memory.NewStorage(), memfs.New(), opts)
	writeFile(bareWorker, "./file1", []byte("one"))
	commitAndPush(bareWorker, filepath.Dir("."))
	writeFile(bareWorker, "./file2", []byte("two"))
	commitAndPush(bareWorker, filepath.Dir("."))
	writeFile(bareWorker, "./file3", []byte("three"))
	commitAndPush(bareWorker, filepath.Dir("."))

	// create two "worker" repos pointing to the same bare remote
	repo1, err := git.Clone(memory.NewStorage(), memfs.New(), opts)
	if err != nil {
		panic(err)
	}
	repo2, err := git.Clone(memory.NewStorage(), memfs.New(), opts)
	if err != nil {
		panic(err)
	}

	// write a file on one, commit and push
	writeFile(repo1, "./file4", []byte("four"))
	commitAndPush(repo1, filepath.Dir("./test0/test_file"))

	// perform a pull on the second worker
	wt2, err := repo2.Worktree()
	if err != nil {
		panic(err)
	}
	err = wt2.Pull(&git.PullOptions{})
	if err != nil {
		panic(err) // <<--------------------- PANICS HERE
	}
}

// commit all files and push the changes to default remote
func commitAndPush(r *git.Repository, dir string) {
	wt, err := r.Worktree()
	if err != nil {
		panic(fmt.Sprintf("error [commitAndPush] - creating worktree, %v", err))
	}

	if _, err := wt.Add("."); err != nil {
		panic(fmt.Sprintf("error [commitAndPush] - Adding directory, %v", err))
	}

	if hash, err := wt.Commit(
		fmt.Sprintf("test, %v", dir),
		&git.CommitOptions{
			Author: &object.Signature{
				Name:  "test",
				Email: "[email protected]",
			},
		}); err != nil {
		log.Fatal(fmt.Sprintf("error [commitAndPush] - Commit, %v", err))
	} else {
		fmt.Println("Commit succeeded, hash:", hash)
	}

	if err = r.Push(&git.PushOptions{}); err != nil {
		panic(fmt.Sprintf("error [commitAndPush] - Pushing changes, %v", err))
	}
}

// boilerplate to write into a filepath
func writeFile(r *git.Repository, file string, data []byte) {
	wt, err := r.Worktree()
	if err != nil {
		panic(fmt.Sprintf("error [writeFile] - creating worktree, %v", err))
	}

	f, err := wt.Filesystem.Create(file)
	if err != nil {
		panic(fmt.Sprintf("error [writeFile] - writing file, %v", err))
	}
	defer f.Close()

	if _, err = f.Write(data); err != nil {
		panic(fmt.Sprintf("error [writeFile] - writing data, %v", err))
	}
}

glibsm avatar Jul 23 '18 22:07 glibsm

any updates?

lelvisl avatar Dec 12 '19 16:12 lelvisl