func MergePoints()

in pkg/git/mainline.go [68:136]


func MergePoints(r *gogit.Repository, mainLine []*object.Commit) (map[plumbing.Hash]*object.Commit, error) {
	// create lookup table for the position in mainLine
	mainLinePos := map[plumbing.Hash]int{}
	for i, c := range mainLine {
		mainLinePos[c.Hash] = i
	}

	earliestMergePoints := map[plumbing.Hash]int{} // the earlist mainline commit index a given commit was merged into the mainline
	seen := map[plumbing.Hash]*object.Commit{}

	// pos is the position of the current mainline commit, h
	var visit func(pos int, h plumbing.Hash) error
	visit = func(pos int, h plumbing.Hash) error {
		// stop if we reached the mainline
		if _, isOnMainLine := mainLinePos[h]; isOnMainLine {
			return nil
		}

		// was h seen before as descendent of a mainline commit? It must have had
		// a better position as we saw it earlier.
		if _, seenBefore := earliestMergePoints[h]; seenBefore {
			return nil
		}

		earliestMergePoints[h] = pos

		// resolve hash
		c := seen[h]
		if c == nil {
			var err error
			c, err = cache.CommitObject(r, h)
			if err != nil {
				return fmt.Errorf("failed to get %s: %v", h.String(), err)
			}
			seen[h] = c
		}

		// recurse into parents
		for _, ph := range c.ParentHashes {
			err := visit(pos, ph)
			if err != nil {
				return err
			}
		}

		return nil
	}

	// recursively enumerate all reachable commits
	for pos := len(mainLine) - 1; pos >= 0; pos-- {
		c := mainLine[pos]
		earliestMergePoints[c.Hash] = pos
		seen[c.Hash] = c
		for _, ph := range c.ParentHashes {
			err := visit(pos, ph)
			if err != nil {
				return nil, err
			}
		}
	}

	// map commit hash to best merge point on mainline
	result := map[plumbing.Hash]*object.Commit{}
	for _, c := range seen {
		result[c.Hash] = mainLine[earliestMergePoints[c.Hash]]
	}

	return result, nil
}