Exemple #1
0
func (r *Repository) makeCommit(rec *hg_revlog.Rec) (*vcs.Commit, error) {
	fb := hg_revlog.NewFileBuilder()
	ce, err := hg_changelog.BuildEntry(rec, fb)
	if err != nil {
		return nil, err
	}

	addr, err := mail.ParseAddress(ce.Committer)
	if err != nil {
		// This occurs when the commit author specifier is
		// malformed. Fall back to just using the whole committer
		// string as the name.
		addr = &mail.Address{
			Name:    ce.Committer,
			Address: "",
		}
	}

	var parents []vcs.CommitID
	if !rec.IsStartOfBranch() {
		if p := rec.Parent(); p != nil {
			parents = append(parents, vcs.CommitID(hex.EncodeToString(rec.Parent().Id())))
		}
		if rec.Parent2Present() {
			parents = append(parents, vcs.CommitID(hex.EncodeToString(rec.Parent2().Id())))
		}
	}

	return &vcs.Commit{
		ID:      vcs.CommitID(ce.Id),
		Author:  vcs.Signature{addr.Name, addr.Address, pbtypes.NewTimestamp(ce.Date)},
		Message: ce.Comment,
		Parents: parents,
	}, nil
}
Exemple #2
0
func (r *Repository) getParents(revSpec vcs.CommitID) ([]vcs.CommitID, error) {
	var parents []vcs.CommitID

	cmd := exec.Command("hg", "parents", "-r", string(revSpec), "--template",
		`{node}\x00{author|person}\x00{author|email}\x00{date|rfc3339date}\x00{desc}\x00{p1node}\x00{p2node}\x00`)
	cmd.Dir = r.Dir
	out, err := cmd.CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("exec `hg parents` failed: %s. Output was:\n\n%s", err, out)
	}

	const partsPerCommit = 7 // number of \x00-separated fields per commit
	allParts := bytes.Split(out, []byte{'\x00'})
	numCommits := len(allParts) / partsPerCommit
	for i := 0; i < numCommits; i++ {
		parts := allParts[partsPerCommit*i : partsPerCommit*(i+1)]

		if p1 := parts[0]; len(p1) > 0 && !bytes.Equal(p1, hgNullParentNodeID) {
			parents = append(parents, vcs.CommitID(p1))
		}
		if p2 := parts[5]; len(p2) > 0 && !bytes.Equal(p2, hgNullParentNodeID) {
			parents = append(parents, vcs.CommitID(p2))
		}
		if p3 := parts[6]; len(p3) > 0 && !bytes.Equal(p3, hgNullParentNodeID) {
			parents = append(parents, vcs.CommitID(p3))
		}
	}

	return parents, nil
}
Exemple #3
0
func (r *Repository) Branches(_ vcs.BranchesOptions) ([]*vcs.Branch, error) {
	r.editLock.RLock()
	defer r.editLock.RUnlock()

	refs, err := r.u.NewReferenceIterator()
	if err != nil {
		return nil, err
	}

	var bs []*vcs.Branch
	for {
		ref, err := refs.Next()
		if isErrIterOver(err) {
			break
		}
		if err != nil {
			return nil, err
		}
		if ref.IsBranch() {
			bs = append(bs, &vcs.Branch{Name: ref.Shorthand(), Head: vcs.CommitID(ref.Target().String())})
		}
	}

	sort.Sort(vcs.Branches(bs))
	return bs, nil
}
Exemple #4
0
func (r *Repository) Tags() ([]*vcs.Tag, error) {
	r.editLock.RLock()
	defer r.editLock.RUnlock()

	refs, err := r.u.NewReferenceIterator()
	if err != nil {
		return nil, err
	}

	var ts []*vcs.Tag
	for {
		ref, err := refs.Next()
		if isErrIterOver(err) {
			break
		}
		if err != nil {
			return nil, err
		}
		if ref.IsTag() {
			ts = append(ts, &vcs.Tag{Name: ref.Shorthand(), CommitID: vcs.CommitID(ref.Target().String())})
		}
	}

	sort.Sort(vcs.Tags(ts))
	return ts, nil
}
Exemple #5
0
func (fs *gitFSLibGit2) makeFileInfo(path string, e *git2go.TreeEntry) (*util.FileInfo, error) {
	switch e.Type {
	case git2go.ObjectBlob:
		return fs.fileInfo(e)
	case git2go.ObjectTree:
		return fs.dirInfo(e), nil
	case git2go.ObjectCommit:
		submod, err := fs.repo.LookupSubmodule(path)
		if err != nil {
			return nil, err
		}

		// TODO(sqs): add (*Submodule).Free to git2go and defer submod.Free()
		// below when that method has been added.
		//
		// defer submod.Free()

		return &util.FileInfo{
			Name_: e.Name,
			Mode_: vcs.ModeSubmodule,
			Sys_: vcs.SubmoduleInfo{
				URL:      submod.Url(),
				CommitID: vcs.CommitID(e.Id.String()),
			},
		}, nil
	}

	return nil, fmt.Errorf("unexpected object type %v while making file info (expected blob, tree, or commit)", e.Type)
}
Exemple #6
0
func TestRepository_ResolveRevision(t *testing.T) {
	setup()
	defer teardown()

	repoPath := "a.b/c"
	repo_, _ := vcsclient.Repository(repoPath)
	repo := repo_.(*repository)

	want := vcs.CommitID("abcd")

	var called bool
	mux.HandleFunc(urlPath(t, RouteRepoRevision, repo, map[string]string{"RepoPath": repoPath, "RevSpec": "myrevspec"}), func(w http.ResponseWriter, r *http.Request) {
		called = true
		testMethod(t, r, "GET")

		http.Redirect(w, r, urlPath(t, RouteRepoCommit, repo, map[string]string{"CommitID": "abcd"}), http.StatusFound)
	})

	commitID, err := repo.ResolveRevision("myrevspec")
	if err != nil {
		t.Errorf("Repository.ResolveRevision returned error: %v", err)
	}

	if !called {
		t.Fatal("!called")
	}

	if commitID != want {
		t.Errorf("Repository.ResolveRevision returned %+v, want %+v", commitID, want)
	}
}
Exemple #7
0
func (r *Repository) Branches(opt vcs.BranchesOptions) ([]*vcs.Branch, error) {
	r.editLock.RLock()
	defer r.editLock.RUnlock()

	refs, err := r.showRef("--heads")
	if err != nil {
		return nil, err
	}

	branches := make([]*vcs.Branch, len(refs))
	for i, ref := range refs {
		branches[i] = &vcs.Branch{
			Name: strings.TrimPrefix(ref[1], "refs/heads/"),
			Head: vcs.CommitID(ref[0]),
		}
	}

	// Fetch behind/ahead counts for each branch.
	if opt.BehindAheadBranch != "" {
		for i, branch := range branches {
			behind, ahead, err := r.branchCounts(branch.Name, opt.BehindAheadBranch)
			if err != nil {
				return nil, err
			}
			branches[i].Counts = &vcs.BehindAhead{Behind: uint32(behind), Ahead: uint32(ahead)}
		}
	}

	return branches, nil
}
Exemple #8
0
func TestRepository_CrossRepoMergeBase(t *testing.T) {
	setup()
	defer teardown()

	repoPath := "a.b/c"
	repo_, _ := vcsclient.Repository(repoPath)
	repo := repo_.(*repository)

	want := vcs.CommitID("abcd")

	var called bool
	mux.HandleFunc(urlPath(t, RouteRepoCrossRepoMergeBase, repo, map[string]string{"RepoPath": repoPath, "CommitIDA": "a", "BRepoPath": "x.com/y", "CommitIDB": "b"}), func(w http.ResponseWriter, r *http.Request) {
		called = true
		testMethod(t, r, "GET")

		http.Redirect(w, r, urlPath(t, RouteRepoCommit, repo, map[string]string{"CommitID": "abcd"}), http.StatusFound)
	})

	bRepoPath := "x.com/y"
	bRepo, _ := vcsclient.Repository(bRepoPath)

	commitID, err := repo.CrossRepoMergeBase("a", bRepo, "b")
	if err != nil {
		t.Errorf("Repository.CrossRepoMergeBase returned error: %v", err)
	}

	if !called {
		t.Fatal("!called")
	}

	if commitID != want {
		t.Errorf("Repository.CrossRepoMergeBase returned %+v, want %+v", commitID, want)
	}
}
Exemple #9
0
func (r *Repository) makeCommit(c *git2go.Commit) *vcs.Commit {
	var parents []vcs.CommitID
	if pc := c.ParentCount(); pc > 0 {
		parents = make([]vcs.CommitID, pc)
		for i := 0; i < int(pc); i++ {
			parents[i] = vcs.CommitID(c.ParentId(uint(i)).String())
		}
	}

	au, cm := c.Author(), c.Committer()
	return &vcs.Commit{
		ID:        vcs.CommitID(c.Id().String()),
		Author:    vcs.Signature{au.Name, au.Email, pbtypes.NewTimestamp(au.When)},
		Committer: &vcs.Signature{cm.Name, cm.Email, pbtypes.NewTimestamp(cm.When)},
		Message:   strings.TrimSuffix(c.Message(), "\n"),
		Parents:   parents,
	}
}
Exemple #10
0
func (r *Repository) Tags() ([]*vcs.Tag, error) {
	ts := make([]*vcs.Tag, len(r.allTags.IdByName))
	i := 0
	for name, id := range r.allTags.IdByName {
		ts[i] = &vcs.Tag{Name: name, CommitID: vcs.CommitID(id)}
		i++
	}
	sort.Sort(vcs.Tags(ts))
	return ts, nil
}
Exemple #11
0
func (r *Repository) Branches(_ vcs.BranchesOptions) ([]*vcs.Branch, error) {
	bs := make([]*vcs.Branch, len(r.branchHeads.IdByName))
	i := 0
	for name, id := range r.branchHeads.IdByName {
		bs[i] = &vcs.Branch{Name: name, Head: vcs.CommitID(id)}
		i++
	}
	sort.Sort(vcs.Branches(bs))
	return bs, nil
}
Exemple #12
0
func (r *Repository) MergeBase(a, b vcs.CommitID) (vcs.CommitID, error) {
	r.editLock.RLock()
	defer r.editLock.RUnlock()

	cmd := exec.Command("git", "merge-base", "--", string(a), string(b))
	cmd.Dir = r.Dir
	out, err := cmd.CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("exec %v failed: %s. Output was:\n\n%s", cmd.Args, err, out)
	}
	return vcs.CommitID(bytes.TrimSpace(out)), nil
}
Exemple #13
0
func (r *Repository) ResolveRevision(spec string) (vcs.CommitID, error) {
	cmd := exec.Command("hg", "identify", "--debug", "-i", "--rev="+spec)
	cmd.Dir = r.Dir
	out, err := cmd.CombinedOutput()
	if err != nil {
		out = bytes.TrimSpace(out)
		if isUnknownRevisionError(string(out), spec) {
			return "", vcs.ErrRevisionNotFound
		}
		return "", fmt.Errorf("exec `hg identify` failed: %s. Output was:\n\n%s", err, out)
	}
	return vcs.CommitID(bytes.TrimSpace(out)), nil
}
Exemple #14
0
func (r *Repository) ResolveBranch(name string) (vcs.CommitID, error) {
	r.editLock.RLock()
	defer r.editLock.RUnlock()

	b, err := r.u.LookupBranch(name, git2go.BranchLocal)
	if err != nil {
		if err.Error() == fmt.Sprintf("Cannot locate local branch '%s'", name) {
			return "", vcs.ErrBranchNotFound
		}
		return "", err
	}
	return vcs.CommitID(b.Target().String()), nil
}
Exemple #15
0
func (r *repository) parseCommitIDInURL(urlStr string) (vcs.CommitID, error) {
	url, err := url.Parse(urlStr)
	if err != nil {
		return "", err
	}

	var info muxpkg.RouteMatch
	match := (*muxpkg.Router)(router).Match(&http.Request{Method: "GET", URL: url}, &info)
	if !match || info.Vars["CommitID"] == "" {
		return "", errors.New("failed to determine CommitID from URL")
	}

	return vcs.CommitID(info.Vars["CommitID"]), nil
}
Exemple #16
0
func (r *Repository) ResolveRevision(spec string) (vcs.CommitID, error) {
	r.editLock.RLock()
	defer r.editLock.RUnlock()

	o, err := r.u.RevparseSingle(spec)
	if err != nil {
		if err.Error() == fmt.Sprintf("Revspec '%s' not found.", spec) {
			return "", vcs.ErrRevisionNotFound
		}
		return "", err
	}
	defer o.Free()
	return vcs.CommitID(o.Id().String()), nil
}
Exemple #17
0
func (r *Repository) Tags() ([]*vcs.Tag, error) {
	refs, err := r.execAndParseCols("tags")
	if err != nil {
		return nil, err
	}

	tags := make([]*vcs.Tag, len(refs))
	for i, ref := range refs {
		tags[i] = &vcs.Tag{
			Name:     ref[1],
			CommitID: vcs.CommitID(ref[0]),
		}
	}
	return tags, nil
}
Exemple #18
0
func (r *Repository) Branches(_ vcs.BranchesOptions) ([]*vcs.Branch, error) {
	refs, err := r.execAndParseCols("branches")
	if err != nil {
		return nil, err
	}

	branches := make([]*vcs.Branch, len(refs))
	for i, ref := range refs {
		branches[i] = &vcs.Branch{
			Name: ref[1],
			Head: vcs.CommitID(ref[0]),
		}
	}
	return branches, nil
}
Exemple #19
0
// mergeBaseHoldingEditLock performs a merge-base. Callers must hold
// the r.editLock (either as a reader or writer).
func (r *Repository) mergeBaseHoldingEditLock(a, b vcs.CommitID) (vcs.CommitID, error) {
	ao, err := git2go.NewOid(string(a))
	if err != nil {
		return "", err
	}
	bo, err := git2go.NewOid(string(b))
	if err != nil {
		return "", err
	}
	mb, err := r.u.MergeBase(ao, bo)
	if err != nil {
		return "", err
	}
	return vcs.CommitID(mb.String()), nil
}
Exemple #20
0
func (r *Repository) ResolveRevision(spec string) (vcs.CommitID, error) {
	if id, err := r.ResolveBranch(spec); err == nil {
		return id, nil
	}
	if id, err := r.ResolveTag(spec); err == nil {
		return id, nil
	}

	rec, err := r.parseRevisionSpec(spec).Lookup(r.cl)
	if err != nil {
		if err == hg_revlog.ErrRevNotFound || err == hex.ErrLength {
			return "", vcs.ErrRevisionNotFound
		}
		return "", err
	}
	return vcs.CommitID(hex.EncodeToString(rec.Id())), nil
}
Exemple #21
0
func (r *Repository) Tags() ([]*vcs.Tag, error) {
	r.editLock.RLock()
	defer r.editLock.RUnlock()

	refs, err := r.showRef("--tags")
	if err != nil {
		return nil, err
	}

	tags := make([]*vcs.Tag, len(refs))
	for i, ref := range refs {
		tags[i] = &vcs.Tag{
			Name:     strings.TrimPrefix(ref[1], "refs/tags/"),
			CommitID: vcs.CommitID(ref[0]),
		}
	}
	return tags, nil
}
Exemple #22
0
func (r *Repository) ResolveRevision(spec string) (vcs.CommitID, error) {
	r.editLock.RLock()
	defer r.editLock.RUnlock()

	if err := checkSpecArgSafety(spec); err != nil {
		return "", err
	}

	cmd := exec.Command("git", "rev-parse", spec+"^{commit}")
	cmd.Dir = r.Dir
	stdout, stderr, err := dividedOutput(cmd)
	if err != nil {
		if bytes.Contains(stderr, []byte("unknown revision")) {
			return "", vcs.ErrRevisionNotFound
		}
		return "", fmt.Errorf("exec `git rev-parse` failed: %s. Stderr was:\n\n%s", err, stderr)
	}
	return vcs.CommitID(bytes.TrimSpace(stdout)), nil
}
Exemple #23
0
func (r *Repository) ResolveTag(name string) (vcs.CommitID, error) {
	r.editLock.RLock()
	defer r.editLock.RUnlock()

	// TODO(sqs): slow way to iterate through tags because git_tag_lookup is not
	// in git2go yet
	refs, err := r.u.NewReferenceIterator()
	if err != nil {
		return "", err
	}

	for {
		ref, err := refs.Next()
		if err != nil {
			break
		}
		if ref.IsTag() && ref.Shorthand() == name {
			return vcs.CommitID(ref.Target().String()), nil
		}
	}

	return "", vcs.ErrTagNotFound
}
Exemple #24
0
func (r *Repository) BlameFile(path string, opt *vcs.BlameOptions) ([]*vcs.Hunk, error) {
	r.editLock.RLock()
	defer r.editLock.RUnlock()

	if opt == nil {
		opt = &vcs.BlameOptions{}
	}
	if opt.OldestCommit != "" {
		return nil, fmt.Errorf("OldestCommit not implemented")
	}
	if err := checkSpecArgSafety(string(opt.NewestCommit)); err != nil {
		return nil, err
	}
	if err := checkSpecArgSafety(string(opt.OldestCommit)); err != nil {
		return nil, err
	}

	args := []string{"blame", "-w", "--porcelain"}
	if opt.StartLine != 0 || opt.EndLine != 0 {
		args = append(args, fmt.Sprintf("-L%d,%d", opt.StartLine, opt.EndLine))
	}
	args = append(args, string(opt.NewestCommit), "--", path)
	cmd := exec.Command("git", args...)
	cmd.Dir = r.Dir
	out, err := cmd.CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("exec `git blame` failed: %s. Output was:\n\n%s", err, out)
	}
	if len(out) < 1 {
		// go 1.8.5 changed the behavior of `git blame` on empty files.
		// previously, it returned a boundary commit. now, it returns nothing.
		// TODO(sqs) TODO(beyang): make `git blame` return the boundary commit
		// on an empty file somehow, or come up with some other workaround.
		st, err := os.Stat(filepath.Join(r.Dir, path))
		if err == nil && st.Size() == 0 {
			return nil, nil
		}
		return nil, fmt.Errorf("Expected git output of length at least 1")
	}

	commits := make(map[string]vcs.Commit)
	hunks := make([]*vcs.Hunk, 0)
	remainingLines := strings.Split(string(out[:len(out)-1]), "\n")
	byteOffset := 0
	for len(remainingLines) > 0 {
		// Consume hunk
		hunkHeader := strings.Split(remainingLines[0], " ")
		if len(hunkHeader) != 4 {
			fmt.Printf("Remaining lines: %+v, %d, '%s'\n", remainingLines, len(remainingLines), remainingLines[0])
			return nil, fmt.Errorf("Expected at least 4 parts to hunkHeader, but got: '%s'", hunkHeader)
		}
		commitID := hunkHeader[0]
		lineNoCur, _ := strconv.Atoi(hunkHeader[2])
		nLines, _ := strconv.Atoi(hunkHeader[3])
		hunk := &vcs.Hunk{
			CommitID:  vcs.CommitID(commitID),
			StartLine: int(lineNoCur),
			EndLine:   int(lineNoCur + nLines),
			StartByte: byteOffset,
		}

		if _, in := commits[commitID]; in {
			// Already seen commit
			byteOffset += len(remainingLines[1])
			remainingLines = remainingLines[2:]
		} else {
			// New commit
			author := strings.Join(strings.Split(remainingLines[1], " ")[1:], " ")
			email := strings.Join(strings.Split(remainingLines[2], " ")[1:], " ")
			if len(email) >= 2 && email[0] == '<' && email[len(email)-1] == '>' {
				email = email[1 : len(email)-1]
			}
			authorTime, err := strconv.ParseInt(strings.Join(strings.Split(remainingLines[3], " ")[1:], " "), 10, 64)
			if err != nil {
				return nil, fmt.Errorf("Failed to parse author-time %q", remainingLines[3])
			}
			summary := strings.Join(strings.Split(remainingLines[9], " ")[1:], " ")
			commit := vcs.Commit{
				ID:      vcs.CommitID(commitID),
				Message: summary,
				Author: vcs.Signature{
					Name:  author,
					Email: email,
					Date:  pbtypes.NewTimestamp(time.Unix(authorTime, 0).In(time.UTC)),
				},
			}

			if len(remainingLines) >= 13 && strings.HasPrefix(remainingLines[10], "previous ") {
				byteOffset += len(remainingLines[12])
				remainingLines = remainingLines[13:]
			} else if len(remainingLines) >= 13 && remainingLines[10] == "boundary" {
				byteOffset += len(remainingLines[12])
				remainingLines = remainingLines[13:]
			} else if len(remainingLines) >= 12 {
				byteOffset += len(remainingLines[11])
				remainingLines = remainingLines[12:]
			} else if len(remainingLines) == 11 {
				// Empty file
				remainingLines = remainingLines[11:]
			} else {
				return nil, fmt.Errorf("Unexpected number of remaining lines (%d):\n%s", len(remainingLines), "  "+strings.Join(remainingLines, "\n  "))
			}

			commits[commitID] = commit
		}

		if commit, present := commits[commitID]; present {
			// Should always be present, but check just to avoid
			// panicking in case of a (somewhat likely) bug in our
			// git-blame parser above.
			hunk.CommitID = commit.ID
			hunk.Author = commit.Author
		}

		// Consume remaining lines in hunk
		for i := 1; i < nLines; i++ {
			byteOffset += len(remainingLines[1])
			remainingLines = remainingLines[2:]
		}

		hunk.EndByte = byteOffset
		hunks = append(hunks, hunk)
	}

	return hunks, nil
}
Exemple #25
0
func (r *Repository) commitLog(opt vcs.CommitsOptions) ([]*vcs.Commit, uint, error) {
	args := []string{"log", `--format=format:%H%x00%aN%x00%aE%x00%at%x00%cN%x00%cE%x00%ct%x00%B%x00%P%x00`}
	if opt.N != 0 {
		args = append(args, "-n", strconv.FormatUint(uint64(opt.N), 10))
	}
	if opt.Skip != 0 {
		args = append(args, "--skip="+strconv.FormatUint(uint64(opt.Skip), 10))
	}

	// Range
	rng := string(opt.Head)
	if opt.Base != "" {
		rng += "..." + string(opt.Base)
	}
	args = append(args, rng)

	cmd := exec.Command("git", args...)
	cmd.Dir = r.Dir
	out, err := cmd.CombinedOutput()
	if err != nil {
		out = bytes.TrimSpace(out)
		if isBadObjectErr(string(out), string(opt.Head)) {
			return nil, 0, vcs.ErrCommitNotFound
		}
		return nil, 0, fmt.Errorf("exec `git log` failed: %s. Output was:\n\n%s", err, out)
	}

	const partsPerCommit = 9 // number of \x00-separated fields per commit
	allParts := bytes.Split(out, []byte{'\x00'})
	numCommits := len(allParts) / partsPerCommit
	commits := make([]*vcs.Commit, numCommits)
	for i := 0; i < numCommits; i++ {
		parts := allParts[partsPerCommit*i : partsPerCommit*(i+1)]

		// log outputs are newline separated, so all but the 1st commit ID part
		// has an erroneous leading newline.
		parts[0] = bytes.TrimPrefix(parts[0], []byte{'\n'})

		authorTime, err := strconv.ParseInt(string(parts[3]), 10, 64)
		if err != nil {
			return nil, 0, fmt.Errorf("parsing git commit author time: %s", err)
		}
		committerTime, err := strconv.ParseInt(string(parts[6]), 10, 64)
		if err != nil {
			return nil, 0, fmt.Errorf("parsing git commit committer time: %s", err)
		}

		var parents []vcs.CommitID
		if parentPart := parts[8]; len(parentPart) > 0 {
			parentIDs := bytes.Split(parentPart, []byte{' '})
			parents = make([]vcs.CommitID, len(parentIDs))
			for i, id := range parentIDs {
				parents[i] = vcs.CommitID(id)
			}
		}

		commits[i] = &vcs.Commit{
			ID:        vcs.CommitID(parts[0]),
			Author:    vcs.Signature{string(parts[1]), string(parts[2]), pbtypes.NewTimestamp(time.Unix(authorTime, 0))},
			Committer: &vcs.Signature{string(parts[4]), string(parts[5]), pbtypes.NewTimestamp(time.Unix(committerTime, 0))},
			Message:   string(bytes.TrimSuffix(parts[7], []byte{'\n'})),
			Parents:   parents,
		}
	}

	// Count commits.
	cmd = exec.Command("git", "rev-list", "--count", rng)
	cmd.Dir = r.Dir
	out, err = cmd.CombinedOutput()
	if err != nil {
		return nil, 0, fmt.Errorf("exec `git rev-list --count` failed: %s. Output was:\n\n%s", err, out)
	}
	out = bytes.TrimSpace(out)
	total, err := strconv.ParseUint(string(out), 10, 64)
	if err != nil {
		return nil, 0, err
	}

	return commits, uint(total), nil
}
Exemple #26
0
func (r *Repository) ResolveBranch(name string) (vcs.CommitID, error) {
	if id, ok := r.branchHeads.IdByName[name]; ok {
		return vcs.CommitID(id), nil
	}
	return "", vcs.ErrBranchNotFound
}
Exemple #27
0
func (r *Repository) commitLog(revSpec string, n uint) ([]*vcs.Commit, uint, error) {
	args := []string{"log", `--template={node}\x00{author|person}\x00{author|email}\x00{date|rfc3339date}\x00{desc}\x00{p1node}\x00{p2node}\x00`}
	if n != 0 {
		args = append(args, "--limit", strconv.FormatUint(uint64(n), 10))
	}
	args = append(args, "--rev="+revSpec+":0")

	cmd := exec.Command("hg", args...)
	cmd.Dir = r.Dir
	out, err := cmd.CombinedOutput()
	if err != nil {
		out = bytes.TrimSpace(out)
		if isUnknownRevisionError(string(out), revSpec) {
			return nil, 0, vcs.ErrCommitNotFound
		}
		return nil, 0, fmt.Errorf("exec `hg log` failed: %s. Output was:\n\n%s", err, out)
	}

	const partsPerCommit = 7 // number of \x00-separated fields per commit
	allParts := bytes.Split(out, []byte{'\x00'})
	numCommits := len(allParts) / partsPerCommit
	commits := make([]*vcs.Commit, numCommits)
	for i := 0; i < numCommits; i++ {
		parts := allParts[partsPerCommit*i : partsPerCommit*(i+1)]
		id := vcs.CommitID(parts[0])

		authorTime, err := time.Parse(time.RFC3339, string(parts[3]))
		if err != nil {
			log.Println(err)
			//return nil, 0, err
		}

		parents, err := r.getParents(id)
		if err != nil {
			return nil, 0, fmt.Errorf("r.GetParents failed: %s. Output was:\n\n%s", err, out)
		}

		commits[i] = &vcs.Commit{
			ID:      id,
			Author:  vcs.Signature{string(parts[1]), string(parts[2]), pbtypes.NewTimestamp(authorTime)},
			Message: string(parts[4]),
			Parents: parents,
		}
	}

	// Count.
	cmd = exec.Command("hg", "id", "--num", "--rev="+revSpec)
	cmd.Dir = r.Dir
	out, err = cmd.CombinedOutput()
	if err != nil {
		return nil, 0, fmt.Errorf("exec `hg id --num` failed: %s. Output was:\n\n%s", err, out)
	}
	out = bytes.TrimSpace(out)
	total, err := strconv.ParseUint(string(out), 10, 64)
	if err != nil {
		return nil, 0, err
	}
	total++ // sequence number is 1 less than total number of commits

	return commits, uint(total), nil
}
Exemple #28
0
func (r *Repository) BlameFile(path string, opt *vcs.BlameOptions) ([]*vcs.Hunk, error) {
	r.editLock.RLock()
	defer r.editLock.RUnlock()

	gopt := git2go.BlameOptions{}
	if opt != nil {
		var err error
		if opt.NewestCommit != "" {
			gopt.NewestCommit, err = git2go.NewOid(string(opt.NewestCommit))
			if err != nil {
				return nil, err
			}
		}
		if opt.OldestCommit != "" {
			gopt.OldestCommit, err = git2go.NewOid(string(opt.OldestCommit))
			if err != nil {
				return nil, err
			}
		}
		gopt.MinLine = uint32(opt.StartLine)
		gopt.MaxLine = uint32(opt.EndLine)
	}

	blame, err := r.u.BlameFile(path, &gopt)
	if err != nil {
		return nil, err
	}
	defer blame.Free()

	// Read file contents so we can set hunk byte start and end.
	fs, err := r.FileSystem(vcs.CommitID(gopt.NewestCommit.String()))
	if err != nil {
		return nil, err
	}
	b, err := fs.(*gitFSLibGit2).readFileBytes(path)
	if err != nil {
		return nil, err
	}
	lines := bytes.SplitAfter(b, []byte{'\n'})

	byteOffset := 0
	hunks := make([]*vcs.Hunk, blame.HunkCount())
	for i := 0; i < len(hunks); i++ {
		hunk, err := blame.HunkByIndex(i)
		if err != nil {
			return nil, err
		}

		hunkBytes := 0
		for j := uint16(0); j < hunk.LinesInHunk; j++ {
			hunkBytes += len(lines[j])
		}
		endByteOffset := byteOffset + hunkBytes

		hunks[i] = &vcs.Hunk{
			StartLine: int(hunk.FinalStartLineNumber),
			EndLine:   int(hunk.FinalStartLineNumber + hunk.LinesInHunk),
			StartByte: byteOffset,
			EndByte:   endByteOffset,
			CommitID:  vcs.CommitID(hunk.FinalCommitId.String()),
			Author: vcs.Signature{
				Name:  hunk.FinalSignature.Name,
				Email: hunk.FinalSignature.Email,
				Date:  pbtypes.NewTimestamp(hunk.FinalSignature.When.In(time.UTC)),
			},
		}
		byteOffset = endByteOffset
		lines = lines[hunk.LinesInHunk:]
	}

	return hunks, nil
}
Exemple #29
0
func (fs *gitFSCmd) lsTree(path string) ([]os.FileInfo, error) {
	fs.repoEditLock.RLock()
	defer fs.repoEditLock.RUnlock()

	// Don't call filepath.Clean(path) because ReadDir needs to pass
	// path with a trailing slash.

	if err := checkSpecArgSafety(path); err != nil {
		return nil, err
	}

	cmd := exec.Command("git", "ls-tree", "-z", "--full-name", "--long", string(fs.at), "--", path)
	cmd.Dir = fs.dir
	out, err := cmd.CombinedOutput()
	if err != nil {
		if bytes.Contains(out, []byte("exists on disk, but not in")) {
			return nil, &os.PathError{Op: "ls-tree", Path: path, Err: os.ErrNotExist}
		}
		return nil, fmt.Errorf("exec `git ls-files` failed: %s. Output was:\n\n%s", err, out)
	}

	if len(out) == 0 {
		return nil, os.ErrNotExist
	}

	lines := bytes.Split(out, []byte{'\x00'})
	fis := make([]os.FileInfo, len(lines)-1)
	for i, line := range lines {
		if i == len(lines)-1 {
			// last entry is empty
			continue
		}

		// Format of `git ls-tree --long` is:
		// "MODE TYPE COMMITID      SIZE    NAME"
		// For example:
		// "100644 blob cfea37f3df073e40c52b61efcd8f94af750346c7     73   mydir/myfile"
		parts := bytes.SplitN(line, []byte(" "), 4)
		if len(parts) != 4 {
			return nil, fmt.Errorf("invalid `git ls-tree --long` output: %q", out)
		}

		typ := string(parts[1])
		oid := parts[2]
		if len(oid) != 40 {
			return nil, fmt.Errorf("invalid `git ls-tree --long` oid output: %q", oid)
		}

		rest := bytes.TrimLeft(parts[3], " ")
		restParts := bytes.SplitN(rest, []byte{'\t'}, 2)
		if len(restParts) != 2 {
			return nil, fmt.Errorf("invalid `git ls-tree --long` size and/or name: %q", rest)
		}
		sizeB := restParts[0]
		var size int64
		if len(sizeB) != 0 && sizeB[0] != '-' {
			size, err = strconv.ParseInt(string(sizeB), 10, 64)
			if err != nil {
				return nil, err
			}
		}
		name := string(restParts[1])

		var sys interface{}

		mode, err := strconv.ParseInt(string(parts[0]), 8, 32)
		if err != nil {
			return nil, err
		}
		switch typ {
		case "blob":
			const gitModeSymlink = 020000
			if mode&gitModeSymlink != 0 {
				// Dereference symlink.
				b, err := fs.readFileBytes(name)
				if err != nil {
					return nil, err
				}
				mode = int64(os.ModeSymlink)
				sys = vcs.SymlinkInfo{Dest: string(b)}
			} else {
				// Regular file.
				mode = mode | 0644
			}
		case "commit":
			mode = mode | vcs.ModeSubmodule
			cmd := exec.Command("git", "config", "--get", "submodule."+name+".url")
			cmd.Dir = fs.dir
			out, err := cmd.CombinedOutput()
			if err != nil {
				return nil, fmt.Errorf("invalid `git config` output: %q", out)
			}
			sys = vcs.SubmoduleInfo{
				URL:      string(bytes.TrimSpace(out)),
				CommitID: vcs.CommitID(oid),
			}
		case "tree":
			mode = mode | int64(os.ModeDir)
		}

		mtime, err := fs.getModTimeFromGitLog(name)
		if err != nil {
			return nil, err
		}

		fis[i] = &util.FileInfo{
			Name_:    filepath.Base(name),
			Mode_:    os.FileMode(mode),
			Size_:    size,
			ModTime_: mtime,
			Sys_:     sys,
		}
	}
	util.SortFileInfosByName(fis)

	return fis, nil
}
Exemple #30
0
func (r *Repository) BlameFile(path string, opt *vcs.BlameOptions) ([]*vcs.Hunk, error) {
	if opt == nil {
		opt = &vcs.BlameOptions{}
	}

	// TODO(sqs): implement OldestCommit
	cmd := exec.Command("python", "-", r.Dir, string(opt.NewestCommit), path)
	cmd.Dir = r.Dir
	cmd.Stdin = strings.NewReader(hgRepoAnnotatePy)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return nil, err
	}

	in := bufio.NewReader(stdout)
	if err := cmd.Start(); err != nil {
		return nil, err
	}

	var data struct {
		Commits map[string]struct {
			Author     struct{ Name, Email string }
			AuthorDate time.Time
		}
		Hunks map[string][]struct {
			CommitID           string
			StartLine, EndLine int
			StartByte, EndByte int
		}
	}
	jsonErr := json.NewDecoder(in).Decode(&data)
	errOut, _ := ioutil.ReadAll(stderr)
	if jsonErr != nil {
		cmd.Wait()
		return nil, fmt.Errorf("%s (stderr: %s)", jsonErr, errOut)
	}
	if err := cmd.Wait(); err != nil {
		return nil, fmt.Errorf("%s (stderr: %s)", err, errOut)
	}

	hunks := make([]*vcs.Hunk, len(data.Hunks[path]))
	for i, hunk := range data.Hunks[path] {
		c := data.Commits[hunk.CommitID]
		hunks[i] = &vcs.Hunk{
			StartLine: hunk.StartLine,
			EndLine:   hunk.EndLine,
			StartByte: hunk.StartByte,
			EndByte:   hunk.EndByte,
			CommitID:  vcs.CommitID(hunk.CommitID),
			Author: vcs.Signature{
				Name:  c.Author.Name,
				Email: c.Author.Email,
				Date:  pbtypes.NewTimestamp(c.AuthorDate.In(time.UTC)),
			},
		}
	}
	return hunks, nil
}