Last active
March 15, 2023 16:17
-
-
Save sithembiso/d890d3f751029a73f2c5 to your computer and use it in GitHub Desktop.
git pull using git2go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func (repo *Repo) Pull() error { | |
branch, err := repo.Branch() | |
if err != nil { | |
return err | |
} | |
// Get the name | |
name, err := branch.Name() | |
if err != nil { | |
return err | |
} | |
// Locate remote | |
remote, err := repo.Remotes.Lookup("origin") | |
if err != nil { | |
return err | |
} | |
// Fetch changes from remote | |
if err := remote.Fetch([]string{}, nil, ""); err != nil { | |
return err | |
} | |
// Get remote master | |
remoteBranch, err := repo.References.Lookup("refs/remotes/origin/"+name) | |
if err != nil { | |
return err | |
} | |
remoteBranchID := remoteBranch.Target() | |
// Get annotated commit | |
annotatedCommit, err := repo.AnnotatedCommitFromRef(remoteBranch) | |
if err != nil { | |
return err | |
} | |
// Do the merge analysis | |
mergeHeads := make([]*git.AnnotatedCommit, 1) | |
mergeHeads[0] = annotatedCommit | |
analysis, _, err := repo.MergeAnalysis(mergeHeads) | |
if err != nil { | |
return err | |
} | |
// Get repo head | |
head, err := repo.Head() | |
if err != nil { | |
return err | |
} | |
if analysis & git.MergeAnalysisUpToDate != 0 { | |
return nil | |
} else if analysis & git.MergeAnalysisNormal != 0 { | |
// Just merge changes | |
if err := repo.Merge([]*git.AnnotatedCommit{annotatedCommit}, nil, nil); err != nil { | |
return err | |
} | |
// Check for conflicts | |
index, err := repo.Index() | |
if err != nil { | |
return err | |
} | |
if index.HasConflicts() { | |
return errors.New("Conflicts encountered. Please resolve them.") | |
} | |
// Make the merge commit | |
sig, err := repo.DefaultSignature() | |
if err != nil { | |
return err | |
} | |
// Get Write Tree | |
treeId, err := index.WriteTree() | |
if err != nil { | |
return err | |
} | |
tree, err := repo.LookupTree(treeId) | |
if err != nil { | |
return err | |
} | |
localCommit, err := repo.LookupCommit(head.Target()) | |
if err != nil { | |
return err | |
} | |
remoteCommit, err := repo.LookupCommit(remoteBranchID) | |
if err != nil { | |
return err | |
} | |
repo.CreateCommit("HEAD", sig, sig, "", tree, localCommit, remoteCommit) | |
// Clean up | |
repo.StateCleanup() | |
} else if analysis & git.MergeAnalysisFastForward != 0 { | |
// Fast-forward changes | |
// Get remote tree | |
remoteTree, err := repo.LookupTree(remoteBranchID) | |
if err != nil { | |
return err | |
} | |
// Checkout | |
if err := repo.CheckoutTree(remoteTree, nil); err != nil { | |
return err | |
} | |
branchRef, err := repo.References.Lookup("refs/heads/"+name) | |
if err != nil { | |
return err | |
} | |
// Point branch to the object | |
branchRef.SetTarget(remoteBranchID, "") | |
if _, err := head.SetTarget(remoteBranchID, ""); err != nil { | |
return err | |
} | |
} else { | |
return fmt.Errorf("Unexpected merge analysis result %d", analysis) | |
} | |
return nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi and thanks for this snippet. It really saved me a lot of time and hassle!
I think I've found a logical problem with the conditional expressions of applying AND bitwise as demonstrated here.
Problem is that in libgit2 (merge.c), an analysis value representing a valid fast-forward is the result of
GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_NORMAL
as evident here.Meaning is that both conditionals (
analysis & git.MergeAnalysisFastForward != 0
andanalysis & git.MergeAnalysisNormal != 0
) are true and the entrance to either one of them is just a matter of ordering of the conditional (here normal will be always entered although FF is possible)Also note that
repo.CheckoutTree(remoteTree, nil)
only does a 'dry-run' which is the default behavior when passing nil opts.This should be changed to something like
repo.CheckoutTree(tree, &git.CheckoutOpts{Strategy: git.CheckoutSafe})
to effectively checkout the remote tree into the current repo