// GetFirstCommit returns the first commit that is included in the review
func (review differentialReview) GetFirstCommit(repo repository.Repo) *repository.Revision {
	var commits []string
	for _, hashPair := range review.Hashes {
		// We only care about the hashes for commits, which have exactly two
		// elements, the first of which is "gtcm".
		if len(hashPair) == 2 && hashPair[0] == commitHashType {
			commits = append(commits, hashPair[1])
		}
	}
	var commitTimestamps []int
	commitsByTimestamp := make(map[int]string)
	for _, commit := range commits {
		details, err := repo.GetDetails(repository.Revision(commit))
		if err == nil {
			timestamp, err := strconv.Atoi(details.Time)
			if err == nil {
				commitTimestamps = append(commitTimestamps, timestamp)
				// If there are multiple, equally old commits, then the last one wins.
				commitsByTimestamp[timestamp] = commit
			}
		}
	}
	if len(commitTimestamps) == 0 {
		return nil
	}
	sort.Ints(commitTimestamps)
	revision := repository.Revision(commitsByTimestamp[commitTimestamps[0]])
	return &revision
}
// EnsureRequestExists runs the "arcanist" command-line tool to create a Differential diff for the given request, if one does not already exist.
func (arc Arcanist) EnsureRequestExists(repo repository.Repo, revision repository.Revision, req request.Request, comments map[string]comment.Comment) {

	// If this revision has been previously closed shortcut all processing
	if closedRevisionsMap[revision] {
		return
	}

	mergeBase, err := repo.GetMergeBase(repository.Revision(req.TargetRef), revision)
	if err != nil {
		// There are lots of reasons that we might not be able to compute a merge base,
		// (e.g. the revision already being merged in, or being dropped and garbage collected),
		// but they all indicate that the review request is no longer valid.
		log.Printf("Ignoring review request '%v', because we could not compute a merge base", req)
		return
	}

	existingReviews := arc.listDifferentialReviewsOrDie(req.ReviewRef, revision)
	if mergeBase == revision {
		// The change has already been merged in, so we should simply close any open reviews.
		for _, review := range existingReviews {
			if !review.isClosed() {
				review.close()
			}
		}
		closedRevisionsMap[revision] = true
		return
	}

	headDetails, err := repo.GetDetails(repository.Revision(req.ReviewRef))
	if err != nil {
		// The given review ref has been deleted (or never existed), but the change wasn't merged.
		// TODO(ojarjur): We should mark the existing reviews as abandoned.
		log.Printf("Ignoring review because the review ref '%s' does not exist", req.ReviewRef)
		return
	}

	if len(existingReviews) > 0 {
		// The change is still pending, but we already have existing reviews, so we should just update those.
		for _, review := range existingReviews {
			arc.updateReviewDiffs(repo, review, headDetails.Commit, req, comments)
		}
		return
	}

	diff, err := arc.createDifferentialDiff(repo, mergeBase, revision, req, []string{})
	if err != nil {
		log.Fatal(err)
	}
	if diff == nil {
		// The revision is already merged in, ignore it.
		return
	}
	rev, err := arc.createDifferentialRevision(repo, revision, diff.ID, req)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Created diff %v and revision %v for the review of %s", diff, rev, revision)

	// If the review already contains multiple commits by the time we mirror it, then
	// we need to ensure that at least the first and last ones are added.
	existingReviews = arc.listDifferentialReviewsOrDie(req.ReviewRef, revision)
	for _, review := range existingReviews {
		arc.updateReviewDiffs(repo, review, headDetails.Commit, req, comments)
	}
}
Esempio n. 3
0
// createDifferentialDiff generates a Phabricator resource that represents a diff between two revisions.
//
// The generated resource includes metadata about how the diff was generated, and a JSON representation
// of the changes from the diff, as parsed by Phabricator.
func (arc Arcanist) createDifferentialDiff(repo repository.Repo, mergeBase, revision repository.Revision, req request.Request, priorDiffs []string) (*differentialDiff, error) {
	revisionDetails, err := repo.GetDetails(revision)
	if err != nil {
		return nil, err
	}
	changes, err := arc.getDiffChanges(repo, mergeBase, revision)
	if err != nil {
		return nil, err
	}
	createRequest := differentialCreateDiffRequest{
		Branch:                    abbreviateRefName(req.ReviewRef),
		SourceControlSystem:       "git",
		SourceControlBaseRevision: string(mergeBase),
		SourcePath:                repo.GetPath(),
		LintStatus:                "4", // Status code 5 means "linter skipped"
		UnitStatus:                "4", // Status code 5 means "unit tests have been skipped"
		Changes:                   changes,
	}
	var createResponse differentialCreateDiffResponse
	runArcCommandOrDie("differential.creatediff", createRequest, &createResponse)
	if createResponse.Error != "" {
		return nil, fmt.Errorf(createResponse.ErrorMessage)
	}

	localCommits := make(map[string]interface{})
	for _, priorDiff := range priorDiffs {
		diffID, err := strconv.Atoi(priorDiff)
		if err != nil {
			return nil, err
		}
		queryRequest := differentialQueryDiffsRequest{[]int{diffID}}
		var queryResponse differentialQueryDiffsResponse
		runArcCommandOrDie("differential.querydiffs", queryRequest, &queryResponse)
		if queryResponse.Error != "" {
			return nil, fmt.Errorf(queryResponse.ErrorMessage)
		}
		priorProperty := queryResponse.Response[priorDiff].Properties
		if priorPropertyMap, ok := priorProperty.(map[string]interface{}); ok {
			if localCommitsProperty, ok := priorPropertyMap["local:commits"]; ok {
				if priorLocalCommits, ok := localCommitsProperty.(map[string]interface{}); ok {
					for id, val := range priorLocalCommits {
						localCommits[id] = val
					}
				}
			}
		}
	}
	localCommits[string(revision)] = *revisionDetails
	localCommitsProperty, err := json.Marshal(localCommits)
	if err != nil {
		return nil, err
	}
	if err := arc.setDiffProperty(createResponse.Response.ID, "local:commits", string(localCommitsProperty)); err != nil {
		return nil, err
	}
	if err := arc.setDiffProperty(createResponse.Response.ID, "arc:unit", "{}"); err != nil {
		return nil, err
	}

	return &createResponse.Response, nil
}