예제 #1
0
// FullHash gives the full commit hash for the given ref.
func (g *GitInfo) FullHash(ref string) (string, error) {
	output, err := exec.RunCwd(g.dir, "git", "rev-parse", fmt.Sprintf("%s^{commit}", ref))
	if err != nil {
		return "", fmt.Errorf("Failed to obtain full hash: %s", err)
	}
	return strings.Trim(output, "\n"), nil
}
예제 #2
0
// Update refreshes the history that GitInfo stores for the repo. If pull is
// true then git pull is performed before refreshing.
func (g *GitInfo) Update(pull, allBranches bool) error {
	g.mutex.Lock()
	defer g.mutex.Unlock()
	glog.Info("Beginning Update.")
	if pull {
		if _, err := exec.RunCwd(g.dir, "git", "pull"); err != nil {
			return fmt.Errorf("Failed to sync to HEAD: %s", err)
		}
	}
	glog.Info("Finished pull.")
	var hashes []string
	var timestamps map[string]time.Time
	var err error
	if allBranches {
		hashes, timestamps, err = readCommitsFromGitAllBranches(g.dir)
	} else {
		hashes, timestamps, err = readCommitsFromGit(g.dir, "HEAD")
	}
	glog.Infof("Finished reading commits: %s", g.dir)
	if err != nil {
		return fmt.Errorf("Failed to read commits from: %s : %s", g.dir, err)
	}
	g.hashes = hashes
	g.timestamps = timestamps
	return nil
}
예제 #3
0
// GetBranches returns the list of branch heads in a Git repository.
// In order to separate local working branches from published branches, only
// remote branches in 'origin' are returned.
func GetBranches(dir string) ([]*GitBranch, error) {
	output, err := exec.RunCwd(dir, "git", "show-ref")
	if err != nil {
		return nil, fmt.Errorf("Failed to get branch list: %v", err)
	}
	branches := []*GitBranch{}
	lines := strings.Split(output, "\n")
	for _, line := range lines {
		if line == "" {
			continue
		}
		parts := strings.SplitN(line, " ", 2)
		if len(parts) != 2 {
			return nil, fmt.Errorf("Could not parse output of 'git show-ref'.")
		}

		for _, prefix := range includeBranchPrefixes {
			if strings.HasPrefix(parts[1], prefix) {
				name := parts[1][len(prefix):]
				branches = append(branches, &GitBranch{
					Name: name,
					Head: parts[0],
				})
			}
		}
	}
	return branches, nil
}
예제 #4
0
// readCommitsFromGit reads the commit history from a Git repository.
func readCommitsFromGit(dir, branch string) ([]string, map[string]time.Time, error) {
	output, err := exec.RunCwd(dir, "git", "log", "--format=format:%H%x20%ci", branch)
	if err != nil {
		return nil, nil, fmt.Errorf("Failed to execute git log: %s", err)
	}
	lines := strings.Split(output, "\n")
	gitHashes := make([]*gitHash, 0, len(lines))
	timestamps := map[string]time.Time{}
	for _, line := range lines {
		parts := strings.SplitN(line, " ", 2)
		if len(parts) == 2 {
			t, err := time.Parse("2006-01-02 15:04:05 -0700", parts[1])
			if err != nil {
				return nil, nil, fmt.Errorf("Failed parsing Git log timestamp: %s", err)
			}
			hash := parts[0]
			gitHashes = append(gitHashes, &gitHash{hash: hash, timeStamp: t})
			timestamps[hash] = t
		}
	}
	sort.Sort(gitHashSlice(gitHashes))
	hashes := make([]string, len(gitHashes), len(gitHashes))
	for i, h := range gitHashes {
		hashes[i] = h.hash
	}
	return hashes, timestamps, nil
}
예제 #5
0
// InitalCommit returns the hash of the initial commit.
func (g *GitInfo) InitialCommit() (string, error) {
	output, err := exec.RunCwd(g.dir, "git", "rev-list", "--max-parents=0", "HEAD")
	if err != nil {
		return "", fmt.Errorf("Failed to determine initial commit: %v", err)
	}
	return strings.Trim(output, "\n"), nil
}
예제 #6
0
// ShortList returns a slice of ShortCommit for every commit in (begin, end].
func (g *GitInfo) ShortList(begin, end string) (*ShortCommits, error) {
	command := []string{"git", "log", "--pretty='%H,%an,%s", begin + ".." + end}
	output, err := exec.RunCwd(g.dir, command...)
	if err != nil {
		return nil, err
	}
	ret := &ShortCommits{
		Commits: []*vcsinfo.ShortCommit{},
	}
	for _, line := range strings.Split(output, "\n") {
		match := commitLineRe.FindStringSubmatch(line)
		if match == nil {
			// This could happen if the subject has new line, in which case we truncate it and ignore the remainder.
			continue
		}
		commit := &vcsinfo.ShortCommit{
			Hash:    match[1],
			Author:  match[2],
			Subject: match[3],
		}
		ret.Commits = append(ret.Commits, commit)
	}

	return ret, nil
}
예제 #7
0
func (g *GitInfo) SetToCommit(hash string) error {
	_, err := exec.RunCwd(g.dir, "git", "reset", "--hard", hash)
	if err != nil {
		return fmt.Errorf("Failed to roll back/forward to commit %s: %s", hash, err)
	}
	return nil
}
예제 #8
0
// GetFile returns the contents of the given file at the given commit.
func (g *GitInfo) GetFile(fileName, commit string) (string, error) {
	output, err := exec.RunCwd(g.dir, "git", "show", commit+":"+fileName)
	if err != nil {
		return "", err
	}
	return output, nil
}
예제 #9
0
// RolledPast determines whether DEPS has rolled past the given commit.
func (r *repoManager) RolledPast(hash string) bool {
	r.mtx.RLock()
	defer r.mtx.RUnlock()
	if _, err := exec.RunCwd(r.childDir, "git", "merge-base", "--is-ancestor", hash, r.lastRollRev); err != nil {
		return false
	}
	return true
}
예제 #10
0
// cleanChromium forces the Chromium checkout into a clean state.
func (r *repoManager) cleanChromium() error {
	if _, err := exec.RunCwd(r.chromiumDir, "git", "clean", "-d", "-f", "-f"); err != nil {
		return err
	}
	_, _ = exec.RunCwd(r.chromiumDir, "git", "rebase", "--abort")
	if _, err := exec.RunCwd(r.chromiumDir, "git", "checkout", "origin/master", "-f"); err != nil {
		return err
	}
	_, _ = exec.RunCwd(r.chromiumDir, "git", "branch", "-D", DEPS_ROLL_BRANCH)
	if _, err := exec.RunCommand(&exec.Command{
		Dir:  r.chromiumDir,
		Env:  []string{fmt.Sprintf("PATH=%s:%s", r.depot_tools, os.Getenv("PATH"))},
		Name: r.gclient,
		Args: []string{"revert", "--nohooks"},
	}); err != nil {
		return err
	}
	return nil
}
예제 #11
0
// RevList returns the results of "git rev-list".
func (g *GitInfo) RevList(args ...string) ([]string, error) {
	g.mutex.Lock()
	defer g.mutex.Unlock()
	output, err := exec.RunCwd(g.dir, append([]string{"git", "rev-list"}, args...)...)
	if err != nil {
		return nil, fmt.Errorf("git rev-list failed: %v", err)
	}
	res := strings.Trim(output, "\n")
	if res == "" {
		return []string{}, nil
	}
	return strings.Split(res, "\n"), nil
}
예제 #12
0
// Log returns a --name-only short log for every commit in (begin, end].
//
// If end is "" then it returns just the short log for the single commit at
// begin.
//
// Example response:
//
//    commit b7988a21fdf23cc4ace6145a06ea824aa85db099
//    Author: Joe Gregorio <*****@*****.**>
//    Date:   Tue Aug 5 16:19:48 2014 -0400
//
//        A description of the commit.
//
//    perf/go/skiaperf/perf.go
//    perf/go/types/types.go
//    perf/res/js/logic.js
//
func (g *GitInfo) Log(begin, end string) (string, error) {
	command := []string{"git", "log", "--name-only"}
	hashrange := begin
	if end != "" {
		hashrange += ".." + end
		command = append(command, hashrange)
	} else {
		command = append(command, "-n", "1", hashrange)
	}
	output, err := exec.RunCwd(g.dir, command...)
	if err != nil {
		return "", err
	}
	return output, nil
}
예제 #13
0
// LastSkpCommit returns the time of the last change to the SKP_VERSION file.
func (g *GitInfo) LastSkpCommit() (time.Time, error) {
	// Executes a git log command that looks like:
	//
	// git log --format=format:%ct -n 1 SKP_VERSION
	//
	// The output should be a single unix timestamp.
	output, err := exec.RunCwd(g.dir, "git", "log", "--format=format:%ct", "-n", "1", "SKP_VERSION")
	if err != nil {
		return time.Time{}, fmt.Errorf("LastSkpCommit: Failed to read git log: %s", err)
	}
	ts, err := strconv.ParseInt(output, 10, 64)
	if err != nil {
		return time.Time{}, fmt.Errorf("LastSkpCommit: Failed to parse timestamp: %s", err)
	}
	return time.Unix(ts, 0), nil
}
예제 #14
0
// getLastRollRev returns the commit hash of the last-completed DEPS roll.
func (r *repoManager) getLastRollRev() (string, error) {
	output, err := exec.RunCwd(r.chromiumDir, r.gclient, "revinfo")
	if err != nil {
		return "", err
	}
	split := strings.Split(output, "\n")
	for _, s := range split {
		if strings.HasPrefix(s, r.childPath) {
			subs := strings.Split(s, "@")
			if len(subs) != 2 {
				return "", fmt.Errorf("Failed to parse output of `gclient revinfo`")
			}
			return subs[1], nil
		}
	}
	return "", fmt.Errorf("Failed to parse output of `gclient revinfo`")
}
예제 #15
0
// getBranchesForCommit returns a string set with all the branches that can reach
// the commit with the given hash.
// TODO(stephana): Speed up this method, there are either better ways to do this
// in git or the results can be cached.
func (g *GitInfo) getBranchesForCommit(hash string) (map[string]bool, error) {
	output, err := exec.RunCwd(g.dir, "git", "branch", "--list", "--contains", hash)
	if err != nil {
		return nil, fmt.Errorf("Failed to get branches for commit %s: %s", hash, err)
	}

	lines := strings.Split(strings.TrimSpace(output), "\n")
	ret := map[string]bool{}
	for _, line := range lines {
		l := strings.TrimSpace(line)
		if l != "" {
			// Splitting the line to filter out the '*' that marks the active branch.
			parts := strings.Split(l, " ")
			ret[parts[len(parts)-1]] = true
		}
	}
	return ret, nil
}
예제 #16
0
// SkpCommits returns the indices for all the commits that contain SKP updates.
func (g *GitInfo) SkpCommits(tile *tiling.Tile) ([]int, error) {
	// Executes a git log command that looks like:
	//
	//   git log --format=format:%H  32956400b4d8f33394e2cdef9b66e8369ba2a0f3..e7416bfc9858bde8fc6eb5f3bfc942bc3350953a SKP_VERSION
	//
	// The output should be a \n separated list of hashes that match.
	first, last := tile.CommitRange()
	output, err := exec.RunCwd(g.dir, "git", "log", "--format=format:%H", first+".."+last, "SKP_VERSION")
	if err != nil {
		return nil, fmt.Errorf("SkpCommits: Failed to find git log of SKP_VERSION: %s", err)
	}
	hashes := strings.Split(output, "\n")

	ret := []int{}
	for i, c := range tile.Commits {
		if c.CommitTime != 0 && util.In(c.Hash, hashes) {
			ret = append(ret, i)
		}
	}
	return ret, nil
}
예제 #17
0
// Details returns more information than ShortCommit about a given commit.
// See the vcsinfo.VCS interface for details.
func (g *GitInfo) Details(hash string, includeBranchInfo bool) (*vcsinfo.LongCommit, error) {
	g.mutex.Lock()
	defer g.mutex.Unlock()
	if c, ok := g.detailsCache[hash]; ok {
		return c, nil
	}
	output, err := exec.RunCwd(g.dir, "git", "log", "-n", "1", "--format=format:%H%n%P%n%an%x20(%ae)%n%s%n%b", hash)
	if err != nil {
		return nil, fmt.Errorf("Failed to execute Git: %s", err)
	}
	lines := strings.SplitN(output, "\n", 5)
	if len(lines) != 5 {
		return nil, fmt.Errorf("Failed to parse output of 'git log'.")
	}
	branches := map[string]bool{}
	if includeBranchInfo {
		branches, err = g.getBranchesForCommit(hash)
		if err != nil {
			return nil, err
		}
	}

	c := vcsinfo.LongCommit{
		ShortCommit: &vcsinfo.ShortCommit{
			Hash:    lines[0],
			Author:  lines[2],
			Subject: lines[3],
		},
		Parents:   strings.Split(lines[1], " "),
		Body:      lines[4],
		Timestamp: g.timestamps[hash],
		Branches:  branches,
	}
	g.detailsCache[hash] = &c
	return &c, nil
}
예제 #18
0
// CreateNewRoll creates and uploads a new DEPS roll to the given commit.
// Returns the issue number of the uploaded roll.
func (r *repoManager) CreateNewRoll(emails, cqExtraTrybots []string, dryRun bool) (int64, error) {
	r.mtx.Lock()
	defer r.mtx.Unlock()

	// Clean the checkout, get onto a fresh branch.
	if err := r.cleanChromium(); err != nil {
		return 0, err
	}
	if _, err := exec.RunCwd(r.chromiumDir, "git", "checkout", "-b", DEPS_ROLL_BRANCH, "-t", "origin/master", "-f"); err != nil {
		return 0, err
	}

	// Defer some more cleanup.
	defer func() {
		util.LogErr(r.cleanChromium())
	}()

	// Create the roll CL.
	if _, err := exec.RunCwd(r.chromiumDir, "git", "config", "user.name", autoroll.ROLL_AUTHOR); err != nil {
		return 0, err
	}
	if _, err := exec.RunCwd(r.chromiumDir, "git", "config", "user.email", autoroll.ROLL_AUTHOR); err != nil {
		return 0, err
	}
	if _, err := exec.RunCommand(&exec.Command{
		Dir:  r.chromiumDir,
		Env:  []string{fmt.Sprintf("PATH=%s:%s", r.depot_tools, os.Getenv("PATH"))},
		Name: r.rollDep,
		Args: []string{r.childPath, r.childHead},
	}); err != nil {
		return 0, err
	}
	// Build the commit message, starting with the message provided by roll-dep.
	commitMsg, err := exec.RunCwd(r.chromiumDir, "git", "log", "-n1", "--format=%B", "HEAD")
	if err != nil {
		return 0, err
	}
	if cqExtraTrybots != nil && len(cqExtraTrybots) > 0 {
		commitMsg += "\n" + fmt.Sprintf(TMPL_CQ_INCLUDE_TRYBOTS, strings.Join(cqExtraTrybots, ","))
	}
	uploadCmd := &exec.Command{
		Dir:  r.chromiumDir,
		Env:  []string{fmt.Sprintf("PATH=%s:%s", r.depot_tools, os.Getenv("PATH"))},
		Name: "git",
		Args: []string{"cl", "upload", "--bypass-hooks", "-f"},
	}
	if dryRun {
		uploadCmd.Args = append(uploadCmd.Args, "--cq-dry-run")
	} else {
		uploadCmd.Args = append(uploadCmd.Args, "--use-commit-queue")
	}
	tbr := "\nTBR="
	if emails != nil && len(emails) > 0 {
		emailStr := strings.Join(emails, ",")
		tbr += emailStr
		uploadCmd.Args = append(uploadCmd.Args, "--send-mail", "--cc", emailStr)
	}
	commitMsg += tbr
	uploadCmd.Args = append(uploadCmd.Args, "-m", commitMsg)

	// Upload the CL.
	uploadOutput, err := exec.RunCommand(uploadCmd)
	if err != nil {
		return 0, err
	}
	issues := ISSUE_CREATED_REGEX.FindStringSubmatch(uploadOutput)
	if len(issues) != 2 {
		return 0, fmt.Errorf("Failed to find newly-uploaded issue number!")
	}
	return strconv.ParseInt(issues[1], 10, 64)
}