git.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. package utils
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "net/http"
  6. "net/url"
  7. "os"
  8. "os/exec"
  9. "path/filepath"
  10. "strings"
  11. "github.com/docker/docker/pkg/urlutil"
  12. )
  13. func GitClone(remoteURL string) (string, error) {
  14. if !urlutil.IsGitTransport(remoteURL) {
  15. remoteURL = "https://" + remoteURL
  16. }
  17. root, err := ioutil.TempDir("", "docker-build-git")
  18. if err != nil {
  19. return "", err
  20. }
  21. u, err := url.Parse(remoteURL)
  22. if err != nil {
  23. return "", err
  24. }
  25. fragment := u.Fragment
  26. clone := cloneArgs(u, root)
  27. if output, err := git(clone...); err != nil {
  28. return "", fmt.Errorf("Error trying to use git: %s (%s)", err, output)
  29. }
  30. return checkoutGit(fragment, root)
  31. }
  32. func cloneArgs(remoteURL *url.URL, root string) []string {
  33. args := []string{"clone", "--recursive"}
  34. shallow := len(remoteURL.Fragment) == 0
  35. if shallow && strings.HasPrefix(remoteURL.Scheme, "http") {
  36. res, err := http.Head(fmt.Sprintf("%s/info/refs?service=git-upload-pack", remoteURL))
  37. if err != nil || res.Header.Get("Content-Type") != "application/x-git-upload-pack-advertisement" {
  38. shallow = false
  39. }
  40. }
  41. if shallow {
  42. args = append(args, "--depth", "1")
  43. }
  44. if remoteURL.Fragment != "" {
  45. remoteURL.Fragment = ""
  46. }
  47. return append(args, remoteURL.String(), root)
  48. }
  49. func checkoutGit(fragment, root string) (string, error) {
  50. refAndDir := strings.SplitN(fragment, ":", 2)
  51. if len(refAndDir[0]) != 0 {
  52. if output, err := gitWithinDir(root, "checkout", refAndDir[0]); err != nil {
  53. return "", fmt.Errorf("Error trying to use git: %s (%s)", err, output)
  54. }
  55. }
  56. if len(refAndDir) > 1 && len(refAndDir[1]) != 0 {
  57. newCtx := filepath.Join(root, refAndDir[1])
  58. fi, err := os.Stat(newCtx)
  59. if err != nil {
  60. return "", err
  61. }
  62. if !fi.IsDir() {
  63. return "", fmt.Errorf("Error setting git context, not a directory: %s", newCtx)
  64. }
  65. root = newCtx
  66. }
  67. return root, nil
  68. }
  69. func gitWithinDir(dir string, args ...string) ([]byte, error) {
  70. a := []string{"--work-tree", dir, "--git-dir", filepath.Join(dir, ".git")}
  71. return git(append(a, args...)...)
  72. }
  73. func git(args ...string) ([]byte, error) {
  74. return exec.Command("git", args...).CombinedOutput()
  75. }