go-git
go-git copied to clipboard
Unable to clone from local repository
Host: Windows 10 Go Version: 1.10.1 windows/amd64
I am trying a simple flow
- Init a new repository in a local folder
- Clone that local repository to another location
However I seem to be unable to do so
Here's a reproducer
package main
import (
"fmt"
"io/ioutil"
"log"
"net/url"
"os"
git "gopkg.in/src-d/go-git.v4"
)
func main() {
srcPathName := "srcRepo"
dstPathName := "dstRepo"
// Create a local folder to store a temporary repository
srcPath, err := ioutil.TempDir("", srcPathName)
if err != nil {
log.Fatalf("Unable to create source folder: %s", err)
}
defer os.RemoveAll(srcPath)
// Init a new repo in the src folder
_, err = git.PlainInit(srcPath, false)
if err != nil {
log.Fatalf("Unable to init source repository: %s\n", err)
}
// Create destination clone directory
dstPath, err := ioutil.TempDir("", dstPathName)
if err != nil {
log.Fatalf("Unable to create destination repository: %s\n", err)
}
defer os.RemoveAll(dstPath)
// Get srcPath as a file: url to clone it
srcURL, _ := url.Parse(srcPath)
srcURL.Scheme = "file"
if err != nil {
log.Fatalf("Unable to parse source repo path: %s\n", err)
}
fmt.Printf("Attempting to clone from %s\n", srcURL.String())
// Attempt to clone local repo
_, err = git.PlainClone(dstPath, false, &git.CloneOptions{
URL: srcURL.String(),
RecurseSubmodules: git.DefaultSubmoduleRecursionDepth,
})
if err != nil {
log.Fatalf("Unable to clone repo: %s", err)
}
}
When I run this I get
Attempting to clone from file:\Users\USERNA~1\AppData\Local\Temp\dstRepo768183330
2018/05/17 23:33:39 Unable to clone repo: repository not found
I have also tried the following variations of the code to make sure I am not doing something wrong
- Init as bare
- Making the url in the format of
file:///C:\Users\USERNA~1\AppData\Local\Temp\dstRepo768183330
It is my understanding from looking at the compatibility matrix that the file: protocol is supported.
Am I doing something wrong here, is this a bug or just not supported?
I've had the same problem, If you dive into the created repo, does git see it as a repository?
It seems so. If I jump inside the folder and do a git status
PS C:\Users\Username\AppData\Local\Temp\dstRepo005704362> git status
On branch master
No commits yet
nothing to commit (create/copy files and use "git add" to track)
I am also able to commit to it with git.
I also attempted cloning a repository created with Git and get the same error
I found a small bug in my reproducer code above where I was attempting to clone from destination rather than source, I fixed it but it did not change the outcome
After some more digging I found the issue
Turns out go-git does not like the file:\some\path format returned by url.Parse when used with the path returned by ioutil.TempDir directly so we need to make the source path into a more friendly format for go-git. This can be done using filepath.ToSlash on the path value returned by ioutil.TempDir
Also go-git complains about not being able to clone an empty repository so we need to commit a new file to it for the use case above to work.
Here follows the full solution
package main
import (
"fmt"
"io/ioutil"
"log"
"net/url"
"os"
"path/filepath"
"time"
git "gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing/object"
)
func main() {
srcPathName := "srcRepo"
dstPathName := "dstRepo"
// Create a local folder to store a temporary repository
srcPath, err := ioutil.TempDir("", srcPathName)
if err != nil {
log.Fatalf("Unable to create source folder: %s", err)
}
defer os.RemoveAll(srcPath)
// Windows hosts return a path using \ so we need to make this
// into a path format go-git can understand with /
srcPath = filepath.ToSlash(srcPath)
// Create a dummy file to commit into the dummy repo
tf, err := ioutil.TempFile(srcPath, "test")
if err != nil {
log.Fatalf("Unable to create test file: %s\n", err)
}
// Init a new repo in the src folder
repo, err := git.PlainInit(srcPath, false)
if err != nil {
log.Fatalf("Unable to init source repository: %s\n", err)
}
wt, err := repo.Worktree()
if err != nil {
log.Fatalf("Unable to retrieve source repo working tree: %s\n", err)
}
// Stage our test file
_, err = wt.Add(filepath.Base(tf.Name()))
if err != nil {
log.Fatalf("Unable to stage path: %s\n", err)
}
// Commit the file
_, err = wt.Commit("Initial commit", &git.CommitOptions{
Author: &object.Signature{
Name: "John Doe",
Email: "[email protected]",
When: time.Now(),
},
})
if err != nil {
log.Fatalf("Unable to commit: %s\n", err)
}
// Create destination clone directory
dstPath, err := ioutil.TempDir("", dstPathName)
if err != nil {
log.Fatalf("Unable to create test repository: %s\n", err)
}
defer os.RemoveAll(dstPath)
// Get srcPath as a file: url to clone it
srcURL, _ := url.Parse(srcPath)
srcURL.Scheme = "file"
if err != nil {
log.Fatalf("Unable to unescape path: %s\n", err)
}
fmt.Printf("Attempting to clone from %s\n", srcURL.String())
// Attempt to clone local repo
_, err = git.PlainClone(dstPath, false, &git.CloneOptions{
URL: srcURL.String(),
RecurseSubmodules: git.DefaultSubmoduleRecursionDepth,
})
if err != nil {
log.Fatalf("Unable to clone repo: %s", err)
}
fmt.Printf("Cloned repository into %s\n", dstPath)
}
Can this be considered a bug in go-git or maybe an enhancement in go-git? Or is this just by design?
@ByteFlinger I think it can be considered a bug.
I have found that incorrect file url's seem to work. If you try file://C:/Users/USERNA~1/AppData/Local/Temp/dstRepo768183330 it would probably work as expected. That said, it seems to be a url parsing problem. Not exactly sure where...
Traced this into go net/url Parse itself... Then checked the reported issues, and they explicitly chose this behavior. So, even though windows expects urls of the form file:///C:/foo, golang decided to only parse file://C:/foo... For better or worse, this seems to be behaving as designed.