go-git
go-git copied to clipboard
Depth: 1 clone has issues when pulling new commits
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))
}
}
any updates?