package gitutils import ( "fmt" "io/ioutil" "net/http" "net/url" "os" "os/exec" "path/filepath" "strings" "github.com/docker/docker/pkg/symlink" "github.com/docker/docker/pkg/urlutil" ) // Clone clones a repository into a newly created directory which // will be under "docker-build-git" func Clone(remoteURL string) (string, error) { if !urlutil.IsGitTransport(remoteURL) { remoteURL = "https://" + remoteURL } root, err := ioutil.TempDir("", "docker-build-git") if err != nil { return "", err } u, err := url.Parse(remoteURL) if err != nil { return "", err } fragment := u.Fragment clone := cloneArgs(u, root) if output, err := git(clone...); err != nil { return "", fmt.Errorf("Error trying to use git: %s (%s)", err, output) } return checkoutGit(fragment, root) } func cloneArgs(remoteURL *url.URL, root string) []string { args := []string{"clone", "--recursive"} shallow := len(remoteURL.Fragment) == 0 if shallow && strings.HasPrefix(remoteURL.Scheme, "http") { res, err := http.Head(fmt.Sprintf("%s/info/refs?service=git-upload-pack", remoteURL)) if err != nil || res.Header.Get("Content-Type") != "application/x-git-upload-pack-advertisement" { shallow = false } } if shallow { args = append(args, "--depth", "1") } if remoteURL.Fragment != "" { remoteURL.Fragment = "" } return append(args, remoteURL.String(), root) } 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) } } if len(refAndDir) > 1 && len(refAndDir[1]) != 0 { newCtx, err := symlink.FollowSymlinkInScope(filepath.Join(root, refAndDir[1]), root) if err != nil { return "", fmt.Errorf("Error setting git context, %q not within git root: %s", refAndDir[1], err) } 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) } root = newCtx } return root, nil } func gitWithinDir(dir string, args ...string) ([]byte, error) { a := []string{"--work-tree", dir, "--git-dir", filepath.Join(dir, ".git")} return git(append(a, args...)...) } func git(args ...string) ([]byte, error) { return exec.Command("git", args...).CombinedOutput() }