github.com/driusan/dgit@v0.0.0-20221118233547-f39f0c15edbb/git/mergebase.go (about) 1 package git 2 3 import ( 4 "fmt" 5 ) 6 7 type MergeBaseOptions struct { 8 IsAncestor bool 9 Octopus bool 10 } 11 12 func MergeBase(c *Client, options MergeBaseOptions, commits []Commitish) (CommitID, error) { 13 if len(commits) == 2 { 14 // If there's only two commits specified, and one is 15 // an ancestor of the other, than that one is the merge-base 16 // (ie it's a fast-forward, so the earlier ancestor is always 17 // the merge-base) 18 cmt0, err := commits[0].CommitID(c) 19 if err != nil { 20 return CommitID{}, err 21 } 22 cmt1, err := commits[1].CommitID(c) 23 if err != nil { 24 return CommitID{}, err 25 } 26 27 if options.IsAncestor { 28 if cmt0.IsAncestor(c, cmt1) { 29 return cmt0, nil 30 } else { 31 return CommitID{}, fmt.Errorf("Not an ancestor") 32 } 33 } 34 35 if cmt0.IsAncestor(c, cmt1) { 36 return cmt0, nil 37 } else if cmt1.IsAncestor(c, cmt0) { 38 return cmt1, nil 39 } 40 } 41 if options.Octopus { 42 return MergeBaseOctopus(c, options, commits) 43 } 44 if len(commits) <= 1 { 45 return CommitID{}, fmt.Errorf("MergeBase requires at least 2 commits") 46 } 47 48 // Starting with commits[0]'s parents, perform a bread-first search 49 // looking for a commit who's an ancestor of commits[1:] 50 tip := commits[0] 51 cmt, err := tip.CommitID(c) 52 if err != nil { 53 return CommitID{}, err 54 } 55 56 // the first level is commit[0]'s parents. 57 nextlevel, err := cmt.Parents(c) 58 if err != nil { 59 return CommitID{}, err 60 } 61 62 // Keep looking until there's no commits left. 63 for len(nextlevel) > 0 { 64 // Check if we've found a commit who's an ancestor 65 // of another commit that was passed. If so, this is 66 // the common ancestor of the "hypothetical merge commit" 67 // that git-merge-base(1) talks about. 68 for _, level := range nextlevel { 69 check, err := level.CommitID(c) 70 if err != nil { 71 return CommitID{}, err 72 } 73 for _, otherhead := range commits[1:] { 74 othercmt, err := otherhead.CommitID(c) 75 if err != nil { 76 return CommitID{}, err 77 } 78 if check.IsAncestor(c, othercmt) { 79 return check, nil 80 } 81 } 82 } 83 84 // Found nothing, so create a new queue of the 85 // next level of parents to check. 86 newnextlevel := make([]CommitID, 0) 87 for _, parent := range nextlevel { 88 parents, err := parent.Parents(c) 89 if err != nil { 90 return CommitID{}, err 91 } 92 newnextlevel = append(newnextlevel, parents...) 93 } 94 nextlevel = newnextlevel 95 } 96 97 // If nothing was found it's not an error, it just means the 98 // merge-base is 00000000000000000000 99 return CommitID{}, nil 100 } 101 102 func MergeBaseOctopus(c *Client, options MergeBaseOptions, commits []Commitish) (CommitID, error) { 103 var bestSoFar Commitish = commits[0] 104 for _, commit := range commits[1:] { 105 closest, err := NearestCommonParent(c, bestSoFar, commit) 106 if err != nil { 107 return CommitID{}, err 108 } 109 bestSoFar = closest 110 } 111 return bestSoFar.CommitID(c) 112 }