Skip to content

Instantly share code, notes, and snippets.

@danielfbm
Last active June 1, 2020 01:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save danielfbm/37b0ca88b745503557b2b3f16865d8c3 to your computer and use it in GitHub Desktop.
Save danielfbm/37b0ca88b745503557b2b3f16865d8c3 to your computer and use it in GitHub Desktop.
how to merge two local branches using git2go
package main
import (
"errors"
"log"
"github.com/libgit2/git2go"
)
func mergeBranches(repo *git.Repository, sourceBranchName string, destinationBranchName string) error {
// Assuming that these two branches are local already
sourceBranch, err := repo.LookupBranch(sourceBranchName, git.BranchLocal)
if err != nil {
log.Print("Failed lookup source branch " + sourceBranchName)
return err
}
defer sourceBranch.Free()
destinationBranch, err := repo.LookupBranch(destinationBranchName, git.BranchLocal)
if err != nil {
log.Print("Failed lookup destination branch " + destinationBranchName)
return err
}
defer destinationBranch.Free()
//Assuming we are already checkout as the destination branch
sourceAnnCommit, err := repo.AnnotatedCommitFromRef(sourceBranch.Reference)
if err != nil {
log.Print("Failed get annotated commit from source branch " + sourceBranchName)
return err
}
defer sourceAnnCommit.Free()
//Getting repo HEAD
head, err := repo.Head()
if err != nil {
log.Print("Failed get head ")
return err
}
// Do merge analysis
mergeHeads := make([]*git.AnnotatedCommit, 1)
mergeHeads[0] = sourceAnnCommit
analysis, something, err := repo.MergeAnalysis(mergeHeads)
// Branches are already merged?
if analysis&git.MergeAnalysisNone != 0 || analysis&git.MergeAnalysisUpToDate != 0 {
log.Print("Nothing to merge?")
return nil
}
// Should merge
if analysis&git.MergeAnalysisNormal == 0 {
log.Print("Some issue and should not merge?")
return errors.New("Merge analysis returned as not normal merge...")
}
//Options for merge
mergeOpts, _ := git.DefaultMergeOptions()
mergeOpts.FileFavor = git.MergeFileFavorNormal
mergeOpts.TreeFlags = git.MergeTreeFailOnConflict
//Options for checkout
checkoutOpts := &git.CheckoutOpts{
Strategy: git.CheckoutSafe | git.CheckoutRecreateMissing | git.CheckoutUseTheirs,
}
//Merge action
if err = repo.Merge(mergeHeads, &mergeOpts, checkoutOpts); err != nil {
log.Print("Failed to merge heads")
return err
}
//Getting repo Index
index, err := repo.Index()
if err != nil {
log.Print("Failed to get repo index")
return err
}
defer index.Free()
//Checking for conflicts
if index.HasConflicts() {
return errors.New("Merge resulted in conflicts. Please solve the conflicts before merging.")
}
// Getting last commit from source
commit, err := repo.LookupCommit(sourceBranch.Target())
if err != nil {
log.Print("Failed to get last commit from source branch")
return err
}
defer commit.Free()
//Getting signature
signature := commit.Author()
// Writting tree to index
treeId, err := index.WriteTree()
if err != nil {
log.Print("Failed to write tree")
return err
}
// Getting the created tree
tree, err := repo.LookupTree(treeId)
if err != nil {
log.Print("Failed to lookup tree after writting")
return err
}
defer tree.Free()
//Getting HEAD's commit
currentDestinationCommit, err := repo.LookupCommit(head.Target())
if err != nil {
log.Print("Failed to get HEAD's commit")
return err
}
//Commit
_, err = repo.CreateCommit("HEAD", signature, signature, "Merged "+sourceBranchName+" into "+destinationBranchName,
tree, currentDestinationCommit, commit)
if err != nil {
log.Print("Failed to commit")
return err
}
err = repo.StateCleanup()
if err != nil {
log.Print("Error while cleaning up state")
return err
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment