Browse Source

Merge pull request #12502 from calavera/shallow_git_history

Shallow clone using git to build images.
Alexander Morozov 10 years ago
parent
commit
790c63a5ef
5 changed files with 113 additions and 25 deletions
  1. 1 10
      api/client/build.go
  2. 2 9
      builder/job.go
  3. 7 6
      docs/sources/reference/commandline/cli.md
  4. 47 0
      utils/git.go
  5. 56 0
      utils/git_test.go

+ 1 - 10
api/client/build.go

@@ -96,20 +96,11 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 	} else {
 		root := cmd.Arg(0)
 		if urlutil.IsGitURL(root) {
-			remoteURL := cmd.Arg(0)
-			if !urlutil.IsGitTransport(remoteURL) {
-				remoteURL = "https://" + remoteURL
-			}
-
-			root, err = ioutil.TempDir("", "docker-build-git")
+			root, err = utils.GitClone(root)
 			if err != nil {
 				return err
 			}
 			defer os.RemoveAll(root)
-
-			if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil {
-				return fmt.Errorf("Error trying to use git: %s (%s)", err, output)
-			}
 		}
 		if _, err := os.Stat(root); err != nil {
 			return err

+ 2 - 9
builder/job.go

@@ -6,7 +6,6 @@ import (
 	"io"
 	"io/ioutil"
 	"os"
-	"os/exec"
 	"strings"
 	"sync"
 
@@ -22,6 +21,7 @@ import (
 	"github.com/docker/docker/pkg/urlutil"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/runconfig"
+	"github.com/docker/docker/utils"
 )
 
 // whitelist of commands allowed for a commit/import
@@ -106,19 +106,12 @@ func Build(d *daemon.Daemon, buildConfig *Config) error {
 	if buildConfig.RemoteURL == "" {
 		context = ioutil.NopCloser(buildConfig.Context)
 	} else if urlutil.IsGitURL(buildConfig.RemoteURL) {
-		if !urlutil.IsGitTransport(buildConfig.RemoteURL) {
-			buildConfig.RemoteURL = "https://" + buildConfig.RemoteURL
-		}
-		root, err := ioutil.TempDir("", "docker-build-git")
+		root, err := utils.GitClone(buildConfig.RemoteURL)
 		if err != nil {
 			return err
 		}
 		defer os.RemoveAll(root)
 
-		if output, err := exec.Command("git", "clone", "--recursive", buildConfig.RemoteURL, root).CombinedOutput(); err != nil {
-			return fmt.Errorf("Error trying to use git: %s (%s)", err, output)
-		}
-
 		c, err := archive.Tar(root, archive.Uncompressed)
 		if err != nil {
 			return err

+ 7 - 6
docs/sources/reference/commandline/cli.md

@@ -636,12 +636,13 @@ refer to any of the files in the context. For example, your build can use
 an [*ADD*](/reference/builder/#add) instruction to reference a file in the
 context.
 
-The `URL` parameter can specify the location of a Git repository; in this
-case,  the repository is the context. The Git repository is recursively
-cloned with its submodules.  The system does a fresh `git clone -recursive`
-in a temporary directory on your local host. Then, this clone is sent to
-the Docker daemon as the context. Local clones give you the ability to
-access private repositories using local user credentials, VPN's, and so forth.
+The `URL` parameter can specify the location of a Git repository;
+the repository acts as the build context.  The system recursively clones the repository
+and its submodules using a `git clone --depth 1 --recursive` command.
+This command runs in a temporary directory on your local host.
+After the command succeeds, the directory is sent to the Docker daemon as the context.
+Local clones give you the ability to access private repositories using
+local user credentials, VPN's, and so forth.
 
 Instead of specifying a context, you can pass a single Dockerfile in the
 `URL` or pipe the file in via `STDIN`.  To pipe a Dockerfile from `STDIN`:

+ 47 - 0
utils/git.go

@@ -0,0 +1,47 @@
+package utils
+
+import (
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"os/exec"
+	"strings"
+
+	"github.com/docker/docker/pkg/urlutil"
+)
+
+func GitClone(remoteURL string) (string, error) {
+	if !urlutil.IsGitTransport(remoteURL) {
+		remoteURL = "https://" + remoteURL
+	}
+	root, err := ioutil.TempDir("", "docker-build-git")
+	if err != nil {
+		return "", err
+	}
+
+	clone := cloneArgs(remoteURL, root)
+
+	if output, err := exec.Command("git", clone...).CombinedOutput(); err != nil {
+		return "", fmt.Errorf("Error trying to use git: %s (%s)", err, output)
+	}
+
+	return root, nil
+}
+
+func cloneArgs(remoteURL, root string) []string {
+	args := []string{"clone", "--recursive"}
+	shallow := true
+
+	if strings.HasPrefix(remoteURL, "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")
+	}
+
+	return append(args, remoteURL, root)
+}

+ 56 - 0
utils/git_test.go

@@ -0,0 +1,56 @@
+package utils
+
+import (
+	"fmt"
+	"net/http"
+	"net/http/httptest"
+	"net/url"
+	"reflect"
+	"testing"
+)
+
+func TestCloneArgsSmartHttp(t *testing.T) {
+	mux := http.NewServeMux()
+	server := httptest.NewServer(mux)
+	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(gitURL, "/tmp")
+	exp := []string{"clone", "--recursive", "--depth", "1", gitURL, "/tmp"}
+	if !reflect.DeepEqual(args, exp) {
+		t.Fatalf("Expected %v, got %v", exp, args)
+	}
+}
+
+func TestCloneArgsDumbHttp(t *testing.T) {
+	mux := http.NewServeMux()
+	server := httptest.NewServer(mux)
+	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(gitURL, "/tmp")
+	exp := []string{"clone", "--recursive", gitURL, "/tmp"}
+	if !reflect.DeepEqual(args, exp) {
+		t.Fatalf("Expected %v, got %v", exp, args)
+	}
+}
+func TestCloneArgsGit(t *testing.T) {
+	args := cloneArgs("git://github.com/docker/docker", "/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)
+	}
+}