fork of go-git with some jj specific features
at main 3.5 kB view raw
1package main 2 3import ( 4 "os" 5 6 "github.com/go-git/go-git/v5" 7 "github.com/go-git/go-git/v5/plumbing" 8 "github.com/go-git/go-git/v5/plumbing/object" 9) 10 11type exitCode int 12 13const ( 14 exitCodeSuccess exitCode = iota 15 exitCodeNotFound 16 exitCodeWrongSyntax 17 exitCodeCouldNotOpenRepository 18 exitCodeCouldNotParseRevision 19 exitCodeUnexpected 20 21 cmdDesc = "Returns the merge-base between two commits:" 22 23 helpShortMsg = ` 24 usage: %_COMMAND_NAME_% <path> <commitRev> <commitRev> 25 or: %_COMMAND_NAME_% <path> --independent <commitRev>... 26 or: %_COMMAND_NAME_% <path> --is-ancestor <commitRev> <commitRev> 27 or: %_COMMAND_NAME_% --help 28 29 params: 30 <path> path to the git repository 31 <commitRev> git revision as supported by go-git 32 33options: 34 (no options) lists the best common ancestors of the two passed commits 35 --independent list commits not reachable from the others 36 --is-ancestor is the first one ancestor of the other? 37 --help show the full help message of %_COMMAND_NAME_% 38` 39) 40 41// Command that mimics `git merge-base --all <baseRev> <headRev>` 42// Command that mimics `git merge-base --is-ancestor <baseRev> <headRev>` 43// Command that mimics `git merge-base --independent <commitRev>...` 44func main() { 45 if len(os.Args) == 1 { 46 helpAndExit("Returns the merge-base between two commits:", helpShortMsg, exitCodeSuccess) 47 } 48 49 if os.Args[1] == "--help" || os.Args[1] == "-h" { 50 helpAndExit("Returns the merge-base between two commits:", helpLongMsg, exitCodeSuccess) 51 } 52 53 if len(os.Args) < 4 { 54 helpAndExit("Wrong syntax", helpShortMsg, exitCodeWrongSyntax) 55 } 56 57 path := os.Args[1] 58 59 var modeIndependent, modeAncestor bool 60 var commitRevs []string 61 var res []*object.Commit 62 63 switch os.Args[2] { 64 case "--independent": 65 modeIndependent = true 66 commitRevs = os.Args[3:] 67 case "--is-ancestor": 68 modeAncestor = true 69 commitRevs = os.Args[3:] 70 if len(commitRevs) != 2 { 71 helpAndExit("Wrong syntax", helpShortMsg, exitCodeWrongSyntax) 72 } 73 default: 74 commitRevs = os.Args[2:] 75 if len(commitRevs) != 2 { 76 helpAndExit("Wrong syntax", helpShortMsg, exitCodeWrongSyntax) 77 } 78 } 79 80 // Open a git repository from current directory 81 repo, err := git.PlainOpen(path) 82 checkIfError(err, exitCodeCouldNotOpenRepository, "not in a git repository") 83 84 // Get the hashes of the passed revisions 85 var hashes []*plumbing.Hash 86 for _, rev := range commitRevs { 87 hash, err := repo.ResolveRevision(plumbing.Revision(rev)) 88 checkIfError(err, exitCodeCouldNotParseRevision, "could not parse revision '%s'", rev) 89 hashes = append(hashes, hash) 90 } 91 92 // Get the commits identified by the passed hashes 93 var commits []*object.Commit 94 for _, hash := range hashes { 95 commit, err := repo.CommitObject(*hash) 96 checkIfError(err, exitCodeUnexpected, "could not find commit '%s'", hash.String()) 97 commits = append(commits, commit) 98 } 99 100 if modeAncestor { 101 isAncestor, err := commits[0].IsAncestor(commits[1]) 102 checkIfError(err, exitCodeUnexpected, "could not traverse the repository history") 103 104 if !isAncestor { 105 os.Exit(int(exitCodeNotFound)) 106 } 107 108 os.Exit(int(exitCodeSuccess)) 109 } 110 111 if modeIndependent { 112 res, err = object.Independents(commits) 113 checkIfError(err, exitCodeUnexpected, "could not traverse the repository history") 114 } else { 115 res, err = commits[0].MergeBase(commits[1]) 116 checkIfError(err, exitCodeUnexpected, "could not traverse the repository history") 117 118 if len(res) == 0 { 119 os.Exit(int(exitCodeNotFound)) 120 } 121 } 122 123 printCommits(res) 124}