getdeps/git.go (110 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
package main
import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
)
// Git represents a Git repository
type Git struct {
	Label  string  `json:"label"`
	URL    string  `json:"url"`
	Dest   string  `json:"dest,omitempty"`
	Branch *string `json:"branch,omitempty"`
	Hash   *string `json:"hash,omitempty"`
}
// Get downloads a Git repository
func (g *Git) Get(projectDir string, urlOverrides *URLOverrides, hashMode HashMode) error {
	branch := defaultBranch
	if g.Branch != nil && *g.Branch != "" {
		branch = *g.Branch
	} else {
		g.Branch = &branch
	}
	dest := "."
	if g.Dest != "" {
		dest = g.Dest
	}
	hash := ""
	if g.Hash != nil {
		hash = *g.Hash
	}
	currentHash, err := gitClone(g.Label, g.URL, dest, branch, hashMode, hash, urlOverrides)
	if err != nil {
		return err
	}
	g.Hash = ¤tHash
	return nil
}
func gitCloneShallow(label, repo, ref, dest string) (err error) {
	if err = os.MkdirAll(dest, 0o755); err != nil {
		return fmt.Errorf("%s: error creating %q: %w", label, dest, err)
	}
	defer func() {
		if err != nil {
			if dest != "." {
				os.RemoveAll(dest)
			} else {
				os.RemoveAll(".git")
			}
		}
	}()
	if err = runCommand("git", "-C", dest, "init", "-q"); err != nil {
		return fmt.Errorf("%s: %w", label, err)
	}
	if err = runCommand("git", "-C", dest, "remote", "add", "origin", repo); err != nil {
		return fmt.Errorf("%s: %w", label, err)
	}
	if err = runCommand("git", "-C", dest, "fetch", "--depth=1", "origin", ref); err != nil {
		return fmt.Errorf("%s: %w", label, err)
	}
	if err = runCommand("git", "-C", dest, "checkout", "-q", ref); err != nil {
		return fmt.Errorf("%s: %w", label, err)
	}
	return nil
}
func gitClone(label, repo, dest, branch string, hashMode HashMode, hash string, urlOverrides *URLOverrides) (string, error) {
	if urlOverrides != nil {
		repo = urlOverrides.Override(repo)
	}
	if branch == "" {
		return "", fmt.Errorf("%s: branch not specified", label)
	}
	if hashMode == hashModeUpdate {
		hash = ""
	}
	log.Printf("%s: Cloning %s (%s %s)...", label, repo, branch, hash)
	// Try the shallow clone first. This is much faster but requires
	// `uploadpack.allowReachableSHA1InWant` to be enabled on the server,
	// which is not the default.
	ok := false
	ref := hash
	if ref == "" {
		ref = branch
	}
	if err := gitCloneShallow(label, repo, ref, dest); err == nil {
		ok = true
	} else {
		log.Printf(
			"%s: shallow clone failed: %s,\n"+
				"== NOTE: This is likely because uploadpack.allowReachableSHA1InWant is not enabled on the server.",
			label, err)
		// Fall back to full clone
	}
	if !ok {
		if err := runCommand("git", "clone", "-q", "-b", branch, repo, dest); err != nil {
			return "", fmt.Errorf("%s: %w", label, err)
		}
		if hash != "" {
			if err := runCommand("git", "-C", dest, "checkout", "-q", hash); err != nil {
				return "", fmt.Errorf("%s: %w", label, err)
			}
		}
	}
	cmd := exec.Command("git", "-C", dest, "rev-parse", "HEAD")
	out, err := cmd.CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("%s: error running %v: %w", label, cmd, err)
	}
	currentHash := strings.TrimSpace(string(out))
	log.Printf("%s: Current hash is %s", label, currentHash)
	if hashMode == hashModeStrict && hash == "" {
		return currentHash, fmt.Errorf("%s: %s: hash mode is strict and no hash supplied (current is %s)", label, repo, currentHash)
	}
	return currentHash, nil
}