package main import ( "os" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" ) type exitCode int const ( exitCodeSuccess exitCode = iota exitCodeNotFound exitCodeWrongSyntax exitCodeCouldNotOpenRepository exitCodeCouldNotParseRevision exitCodeUnexpected cmdDesc = "Returns the merge-base between two commits:" helpShortMsg = ` usage: %_COMMAND_NAME_% or: %_COMMAND_NAME_% --independent ... or: %_COMMAND_NAME_% --is-ancestor or: %_COMMAND_NAME_% --help params: path to the git repository git revision as supported by go-git options: (no options) lists the best common ancestors of the two passed commits --independent list commits not reachable from the others --is-ancestor is the first one ancestor of the other? --help show the full help message of %_COMMAND_NAME_% ` ) // Command that mimics `git merge-base --all ` // Command that mimics `git merge-base --is-ancestor ` // Command that mimics `git merge-base --independent ...` func main() { if len(os.Args) == 1 { helpAndExit("Returns the merge-base between two commits:", helpShortMsg, exitCodeSuccess) } if os.Args[1] == "--help" || os.Args[1] == "-h" { helpAndExit("Returns the merge-base between two commits:", helpLongMsg, exitCodeSuccess) } if len(os.Args) < 4 { helpAndExit("Wrong syntax", helpShortMsg, exitCodeWrongSyntax) } path := os.Args[1] var modeIndependent, modeAncestor bool var commitRevs []string var res []*object.Commit switch os.Args[2] { case "--independent": modeIndependent = true commitRevs = os.Args[3:] case "--is-ancestor": modeAncestor = true commitRevs = os.Args[3:] if len(commitRevs) != 2 { helpAndExit("Wrong syntax", helpShortMsg, exitCodeWrongSyntax) } default: commitRevs = os.Args[2:] if len(commitRevs) != 2 { helpAndExit("Wrong syntax", helpShortMsg, exitCodeWrongSyntax) } } // Open a git repository from current directory repo, err := git.PlainOpen(path) checkIfError(err, exitCodeCouldNotOpenRepository, "not in a git repository") // Get the hashes of the passed revisions var hashes []*plumbing.Hash for _, rev := range commitRevs { hash, err := repo.ResolveRevision(plumbing.Revision(rev)) checkIfError(err, exitCodeCouldNotParseRevision, "could not parse revision '%s'", rev) hashes = append(hashes, hash) } // Get the commits identified by the passed hashes var commits []*object.Commit for _, hash := range hashes { commit, err := repo.CommitObject(*hash) checkIfError(err, exitCodeUnexpected, "could not find commit '%s'", hash.String()) commits = append(commits, commit) } if modeAncestor { isAncestor, err := commits[0].IsAncestor(commits[1]) checkIfError(err, exitCodeUnexpected, "could not traverse the repository history") if !isAncestor { os.Exit(int(exitCodeNotFound)) } os.Exit(int(exitCodeSuccess)) } if modeIndependent { res, err = object.Independents(commits) checkIfError(err, exitCodeUnexpected, "could not traverse the repository history") } else { res, err = commits[0].MergeBase(commits[1]) checkIfError(err, exitCodeUnexpected, "could not traverse the repository history") if len(res) == 0 { os.Exit(int(exitCodeNotFound)) } } printCommits(res) }