Skip to content
Permalink
Browse files

Allow checking out any ref in gitutils

Also changes so that shallow fetch is performed
even when a specific ref is specified.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
  • Loading branch information...
tonistiigi committed Apr 11, 2017
1 parent 2c6bcf3 commit d76062aa3ce750ecea2206c7b03f23502bf1c694
Showing with 52 additions and 37 deletions.
  1. +44 −19 pkg/gitutils/gitutils.go
  2. +8 −18 pkg/gitutils/gitutils_test.go
@@ -12,6 +12,7 @@ import (

"github.com/docker/docker/pkg/symlink"
"github.com/docker/docker/pkg/urlutil"
"github.com/pkg/errors"
)

// Clone clones a repository into a newly created directory which
@@ -30,19 +31,43 @@ func Clone(remoteURL string) (string, error) {
return "", err
}

fragment := u.Fragment
clone := cloneArgs(u, root)
if out, err := gitWithinDir(root, "init"); err != nil {
return "", errors.Wrapf(err, "failed to init repo at %s: %s", root, out)
}

ref, subdir := getRefAndSubdir(u.Fragment)
fetch := fetchArgs(u, ref)

if output, err := git(clone...); err != nil {
return "", fmt.Errorf("Error trying to use git: %s (%s)", err, output)
if u.Fragment != "" {
u.Fragment = ""
}

return checkoutGit(fragment, root)
if out, err := gitWithinDir(root, "remote", "add", "origin", u.String()); err != nil {
return "", errors.Wrapf(err, "failed add origin repo at %s: %s", u.String(), out)
}

if output, err := gitWithinDir(root, fetch...); err != nil {
return "", errors.Wrapf(err, "error fetching: %s", output)
}

return checkoutGit(root, ref, subdir)
}

func cloneArgs(remoteURL *url.URL, root string) []string {
args := []string{"clone", "--recursive"}
shallow := len(remoteURL.Fragment) == 0
func getRefAndSubdir(fragment string) (ref string, subdir string) {
refAndDir := strings.SplitN(fragment, ":", 2)
ref = "master"
if len(refAndDir[0]) != 0 {
ref = refAndDir[0]
}
if len(refAndDir) > 1 && len(refAndDir[1]) != 0 {
subdir = refAndDir[1]
}
return
}

func fetchArgs(remoteURL *url.URL, ref string) []string {
args := []string{"fetch", "--recurse-submodules=yes"}
shallow := true

if shallow && strings.HasPrefix(remoteURL.Scheme, "http") {
res, err := http.Head(fmt.Sprintf("%s/info/refs?service=git-upload-pack", remoteURL))
@@ -59,30 +84,30 @@ func cloneArgs(remoteURL *url.URL, root string) []string {
remoteURL.Fragment = ""
}

return append(args, remoteURL.String(), root)
return append(args, "origin", ref)
}

func checkoutGit(fragment, root string) (string, error) {
refAndDir := strings.SplitN(fragment, ":", 2)

if len(refAndDir[0]) != 0 {
if output, err := gitWithinDir(root, "checkout", refAndDir[0]); err != nil {
return "", fmt.Errorf("Error trying to use git: %s (%s)", err, output)
func checkoutGit(root, ref, subdir string) (string, error) {
// try a branch
if output, err := gitWithinDir(root, "checkout", ref); err != nil {
// checkout last fetched ref
if _, err2 := gitWithinDir(root, "checkout", "FETCH_HEAD"); err2 != nil {
return "", errors.Wrapf(err, "error checking out %s: %s", ref, output)
}
}

if len(refAndDir) > 1 && len(refAndDir[1]) != 0 {
newCtx, err := symlink.FollowSymlinkInScope(filepath.Join(root, refAndDir[1]), root)
if subdir != "" {
newCtx, err := symlink.FollowSymlinkInScope(filepath.Join(root, subdir), root)
if err != nil {
return "", fmt.Errorf("Error setting git context, %q not within git root: %s", refAndDir[1], err)
return "", errors.Wrapf(err, "error setting git context, %q not within git root: %s", subdir)
}

fi, err := os.Stat(newCtx)
if err != nil {
return "", err
}
if !fi.IsDir() {
return "", fmt.Errorf("Error setting git context, not a directory: %s", newCtx)
return "", errors.Errorf("error setting git context, not a directory: %s", newCtx)
}
root = newCtx
}
@@ -20,15 +20,14 @@ func TestCloneArgsSmartHttp(t *testing.T) {
serverURL, _ := url.Parse(server.URL)

serverURL.Path = "/repo.git"
gitURL := serverURL.String()

mux.HandleFunc("/repo.git/info/refs", func(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query().Get("service")
w.Header().Set("Content-Type", fmt.Sprintf("application/x-%s-advertisement", q))
})

args := cloneArgs(serverURL, "/tmp")
exp := []string{"clone", "--recursive", "--depth", "1", gitURL, "/tmp"}
args := fetchArgs(serverURL, "master")
exp := []string{"fetch", "--recurse-submodules=yes", "--depth", "1", "origin", "master"}
if !reflect.DeepEqual(args, exp) {
t.Fatalf("Expected %v, got %v", exp, args)
}
@@ -40,32 +39,22 @@ func TestCloneArgsDumbHttp(t *testing.T) {
serverURL, _ := url.Parse(server.URL)

serverURL.Path = "/repo.git"
gitURL := serverURL.String()

mux.HandleFunc("/repo.git/info/refs", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
})

args := cloneArgs(serverURL, "/tmp")
exp := []string{"clone", "--recursive", gitURL, "/tmp"}
args := fetchArgs(serverURL, "master")
exp := []string{"fetch", "--recurse-submodules=yes", "origin", "master"}
if !reflect.DeepEqual(args, exp) {
t.Fatalf("Expected %v, got %v", exp, args)
}
}

func TestCloneArgsGit(t *testing.T) {
u, _ := url.Parse("git://github.com/docker/docker")
args := cloneArgs(u, "/tmp")
exp := []string{"clone", "--recursive", "--depth", "1", "git://github.com/docker/docker", "/tmp"}
if !reflect.DeepEqual(args, exp) {
t.Fatalf("Expected %v, got %v", exp, args)
}
}

func TestCloneArgsStripFragment(t *testing.T) {
u, _ := url.Parse("git://github.com/docker/docker#test")
args := cloneArgs(u, "/tmp")
exp := []string{"clone", "--recursive", "git://github.com/docker/docker", "/tmp"}
args := fetchArgs(u, "master")
exp := []string{"fetch", "--recurse-submodules=yes", "--depth", "1", "origin", "master"}
if !reflect.DeepEqual(args, exp) {
t.Fatalf("Expected %v, got %v", exp, args)
}
@@ -198,7 +187,8 @@ func TestCheckoutGit(t *testing.T) {
}

for _, c := range cases {
r, err := checkoutGit(c.frag, gitDir)
ref, subdir := getRefAndSubdir(c.frag)
r, err := checkoutGit(gitDir, ref, subdir)

fail := err != nil
if fail != c.fail {

0 comments on commit d76062a

Please sign in to comment.
You can’t perform that action at this time.