gitutils.go 2.4 KB

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