Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/libgit2/git2go/merge.go')
-rw-r--r--vendor/github.com/libgit2/git2go/merge.go441
1 files changed, 441 insertions, 0 deletions
diff --git a/vendor/github.com/libgit2/git2go/merge.go b/vendor/github.com/libgit2/git2go/merge.go
new file mode 100644
index 000000000..adc521a2b
--- /dev/null
+++ b/vendor/github.com/libgit2/git2go/merge.go
@@ -0,0 +1,441 @@
+package git
+
+/*
+#include <git2.h>
+
+extern git_annotated_commit** _go_git_make_merge_head_array(size_t len);
+extern void _go_git_annotated_commit_array_set(git_annotated_commit** array, git_annotated_commit* ptr, size_t n);
+extern git_annotated_commit* _go_git_annotated_commit_array_get(git_annotated_commit** array, size_t n);
+extern int _go_git_merge_file(git_merge_file_result*, char*, size_t, char*, unsigned int, char*, size_t, char*, unsigned int, char*, size_t, char*, unsigned int, git_merge_file_options*);
+
+*/
+import "C"
+import (
+ "reflect"
+ "runtime"
+ "unsafe"
+)
+
+type AnnotatedCommit struct {
+ ptr *C.git_annotated_commit
+ r *Repository
+}
+
+func newAnnotatedCommitFromC(ptr *C.git_annotated_commit, r *Repository) *AnnotatedCommit {
+ mh := &AnnotatedCommit{ptr: ptr, r: r}
+ runtime.SetFinalizer(mh, (*AnnotatedCommit).Free)
+ return mh
+}
+
+func (mh *AnnotatedCommit) Free() {
+ runtime.SetFinalizer(mh, nil)
+ C.git_annotated_commit_free(mh.ptr)
+}
+
+func (r *Repository) AnnotatedCommitFromFetchHead(branchName string, remoteURL string, oid *Oid) (*AnnotatedCommit, error) {
+ cbranchName := C.CString(branchName)
+ defer C.free(unsafe.Pointer(cbranchName))
+
+ cremoteURL := C.CString(remoteURL)
+ defer C.free(unsafe.Pointer(cremoteURL))
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ var ptr *C.git_annotated_commit
+ ret := C.git_annotated_commit_from_fetchhead(&ptr, r.ptr, cbranchName, cremoteURL, oid.toC())
+ runtime.KeepAlive(oid)
+ if ret < 0 {
+ return nil, MakeGitError(ret)
+ }
+
+ return newAnnotatedCommitFromC(ptr, r), nil
+}
+
+func (r *Repository) LookupAnnotatedCommit(oid *Oid) (*AnnotatedCommit, error) {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ var ptr *C.git_annotated_commit
+ ret := C.git_annotated_commit_lookup(&ptr, r.ptr, oid.toC())
+ runtime.KeepAlive(oid)
+ if ret < 0 {
+ return nil, MakeGitError(ret)
+ }
+ return newAnnotatedCommitFromC(ptr, r), nil
+}
+
+func (r *Repository) AnnotatedCommitFromRef(ref *Reference) (*AnnotatedCommit, error) {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ var ptr *C.git_annotated_commit
+ ret := C.git_annotated_commit_from_ref(&ptr, r.ptr, ref.ptr)
+ runtime.KeepAlive(r)
+ runtime.KeepAlive(ref)
+ if ret < 0 {
+ return nil, MakeGitError(ret)
+ }
+ return newAnnotatedCommitFromC(ptr, r), nil
+}
+
+type MergeTreeFlag int
+
+const (
+ // Detect renames that occur between the common ancestor and the "ours"
+ // side or the common ancestor and the "theirs" side. This will enable
+ // the ability to merge between a modified and renamed file.
+ MergeTreeFindRenames MergeTreeFlag = C.GIT_MERGE_FIND_RENAMES
+ // If a conflict occurs, exit immediately instead of attempting to
+ // continue resolving conflicts. The merge operation will fail with
+ // GIT_EMERGECONFLICT and no index will be returned.
+ MergeTreeFailOnConflict MergeTreeFlag = C.GIT_MERGE_FAIL_ON_CONFLICT
+)
+
+type MergeOptions struct {
+ Version uint
+ TreeFlags MergeTreeFlag
+
+ RenameThreshold uint
+ TargetLimit uint
+ FileFavor MergeFileFavor
+
+ //TODO: Diff similarity metric
+}
+
+func mergeOptionsFromC(opts *C.git_merge_options) MergeOptions {
+ return MergeOptions{
+ Version: uint(opts.version),
+ TreeFlags: MergeTreeFlag(opts.flags),
+ RenameThreshold: uint(opts.rename_threshold),
+ TargetLimit: uint(opts.target_limit),
+ FileFavor: MergeFileFavor(opts.file_favor),
+ }
+}
+
+func DefaultMergeOptions() (MergeOptions, error) {
+ opts := C.git_merge_options{}
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ecode := C.git_merge_init_options(&opts, C.GIT_MERGE_OPTIONS_VERSION)
+ if ecode < 0 {
+ return MergeOptions{}, MakeGitError(ecode)
+ }
+ return mergeOptionsFromC(&opts), nil
+}
+
+func (mo *MergeOptions) toC() *C.git_merge_options {
+ if mo == nil {
+ return nil
+ }
+ return &C.git_merge_options{
+ version: C.uint(mo.Version),
+ flags: C.git_merge_flag_t(mo.TreeFlags),
+ rename_threshold: C.uint(mo.RenameThreshold),
+ target_limit: C.uint(mo.TargetLimit),
+ file_favor: C.git_merge_file_favor_t(mo.FileFavor),
+ }
+}
+
+type MergeFileFavor int
+
+const (
+ MergeFileFavorNormal MergeFileFavor = C.GIT_MERGE_FILE_FAVOR_NORMAL
+ MergeFileFavorOurs MergeFileFavor = C.GIT_MERGE_FILE_FAVOR_OURS
+ MergeFileFavorTheirs MergeFileFavor = C.GIT_MERGE_FILE_FAVOR_THEIRS
+ MergeFileFavorUnion MergeFileFavor = C.GIT_MERGE_FILE_FAVOR_UNION
+)
+
+func (r *Repository) Merge(theirHeads []*AnnotatedCommit, mergeOptions *MergeOptions, checkoutOptions *CheckoutOpts) error {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ cMergeOpts := mergeOptions.toC()
+ cCheckoutOpts := checkoutOptions.toC()
+ defer freeCheckoutOpts(cCheckoutOpts)
+
+ gmerge_head_array := make([]*C.git_annotated_commit, len(theirHeads))
+ for i := 0; i < len(theirHeads); i++ {
+ gmerge_head_array[i] = theirHeads[i].ptr
+ }
+ ptr := unsafe.Pointer(&gmerge_head_array[0])
+ err := C.git_merge(r.ptr, (**C.git_annotated_commit)(ptr), C.size_t(len(theirHeads)), cMergeOpts, cCheckoutOpts)
+ runtime.KeepAlive(theirHeads)
+ if err < 0 {
+ return MakeGitError(err)
+ }
+ return nil
+}
+
+type MergeAnalysis int
+
+const (
+ MergeAnalysisNone MergeAnalysis = C.GIT_MERGE_ANALYSIS_NONE
+ MergeAnalysisNormal MergeAnalysis = C.GIT_MERGE_ANALYSIS_NORMAL
+ MergeAnalysisUpToDate MergeAnalysis = C.GIT_MERGE_ANALYSIS_UP_TO_DATE
+ MergeAnalysisFastForward MergeAnalysis = C.GIT_MERGE_ANALYSIS_FASTFORWARD
+ MergeAnalysisUnborn MergeAnalysis = C.GIT_MERGE_ANALYSIS_UNBORN
+)
+
+type MergePreference int
+
+const (
+ MergePreferenceNone MergePreference = C.GIT_MERGE_PREFERENCE_NONE
+ MergePreferenceNoFastForward MergePreference = C.GIT_MERGE_PREFERENCE_NO_FASTFORWARD
+ MergePreferenceFastForwardOnly MergePreference = C.GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY
+)
+
+// MergeAnalysis returns the possible actions which could be taken by
+// a 'git-merge' command. There may be multiple answers, so the first
+// return value is a bitmask of MergeAnalysis values.
+func (r *Repository) MergeAnalysis(theirHeads []*AnnotatedCommit) (MergeAnalysis, MergePreference, error) {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ gmerge_head_array := make([]*C.git_annotated_commit, len(theirHeads))
+ for i := 0; i < len(theirHeads); i++ {
+ gmerge_head_array[i] = theirHeads[i].ptr
+ }
+ ptr := unsafe.Pointer(&gmerge_head_array[0])
+ var analysis C.git_merge_analysis_t
+ var preference C.git_merge_preference_t
+ err := C.git_merge_analysis(&analysis, &preference, r.ptr, (**C.git_annotated_commit)(ptr), C.size_t(len(theirHeads)))
+ runtime.KeepAlive(theirHeads)
+ if err < 0 {
+ return MergeAnalysisNone, MergePreferenceNone, MakeGitError(err)
+ }
+ return MergeAnalysis(analysis), MergePreference(preference), nil
+
+}
+
+func (r *Repository) MergeCommits(ours *Commit, theirs *Commit, options *MergeOptions) (*Index, error) {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ copts := options.toC()
+
+ var ptr *C.git_index
+ ret := C.git_merge_commits(&ptr, r.ptr, ours.cast_ptr, theirs.cast_ptr, copts)
+ runtime.KeepAlive(ours)
+ runtime.KeepAlive(theirs)
+ if ret < 0 {
+ return nil, MakeGitError(ret)
+ }
+
+ return newIndexFromC(ptr, r), nil
+}
+
+func (r *Repository) MergeTrees(ancestor *Tree, ours *Tree, theirs *Tree, options *MergeOptions) (*Index, error) {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ copts := options.toC()
+
+ var ancestor_ptr *C.git_tree
+ if ancestor != nil {
+ ancestor_ptr = ancestor.cast_ptr
+ }
+ var ptr *C.git_index
+ ret := C.git_merge_trees(&ptr, r.ptr, ancestor_ptr, ours.cast_ptr, theirs.cast_ptr, copts)
+ runtime.KeepAlive(ancestor)
+ runtime.KeepAlive(ours)
+ runtime.KeepAlive(theirs)
+ if ret < 0 {
+ return nil, MakeGitError(ret)
+ }
+
+ return newIndexFromC(ptr, r), nil
+}
+
+func (r *Repository) MergeBase(one *Oid, two *Oid) (*Oid, error) {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ var oid C.git_oid
+ ret := C.git_merge_base(&oid, r.ptr, one.toC(), two.toC())
+ runtime.KeepAlive(one)
+ runtime.KeepAlive(two)
+ runtime.KeepAlive(r)
+ if ret < 0 {
+ return nil, MakeGitError(ret)
+ }
+ return newOidFromC(&oid), nil
+}
+
+// MergeBases retrieves the list of merge bases between two commits.
+//
+// If none are found, an empty slice is returned and the error is set
+// approprately
+func (r *Repository) MergeBases(one, two *Oid) ([]*Oid, error) {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ var coids C.git_oidarray
+ ret := C.git_merge_bases(&coids, r.ptr, one.toC(), two.toC())
+ runtime.KeepAlive(one)
+ runtime.KeepAlive(two)
+ if ret < 0 {
+ return make([]*Oid, 0), MakeGitError(ret)
+ }
+
+ oids := make([]*Oid, coids.count)
+ hdr := reflect.SliceHeader{
+ Data: uintptr(unsafe.Pointer(coids.ids)),
+ Len: int(coids.count),
+ Cap: int(coids.count),
+ }
+
+ goSlice := *(*[]C.git_oid)(unsafe.Pointer(&hdr))
+
+ for i, cid := range goSlice {
+ oids[i] = newOidFromC(&cid)
+ }
+
+ return oids, nil
+}
+
+//TODO: int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[]);
+//TODO: GIT_EXTERN(int) git_merge_base_octopus(git_oid *out,git_repository *repo,size_t length,const git_oid input_array[]);
+
+type MergeFileResult struct {
+ Automergeable bool
+ Path string
+ Mode uint
+ Contents []byte
+ ptr *C.git_merge_file_result
+}
+
+func newMergeFileResultFromC(c *C.git_merge_file_result) *MergeFileResult {
+ var path string
+ if c.path != nil {
+ path = C.GoString(c.path)
+ }
+
+ originalBytes := C.GoBytes(unsafe.Pointer(c.ptr), C.int(c.len))
+ gobytes := make([]byte, len(originalBytes))
+ copy(gobytes, originalBytes)
+ r := &MergeFileResult{
+ Automergeable: c.automergeable != 0,
+ Path: path,
+ Mode: uint(c.mode),
+ Contents: gobytes,
+ ptr: c,
+ }
+
+ runtime.SetFinalizer(r, (*MergeFileResult).Free)
+ return r
+}
+
+func (r *MergeFileResult) Free() {
+ runtime.SetFinalizer(r, nil)
+ C.git_merge_file_result_free(r.ptr)
+}
+
+type MergeFileInput struct {
+ Path string
+ Mode uint
+ Contents []byte
+}
+
+type MergeFileFlags int
+
+const (
+ MergeFileDefault MergeFileFlags = C.GIT_MERGE_FILE_DEFAULT
+
+ MergeFileStyleMerge MergeFileFlags = C.GIT_MERGE_FILE_STYLE_MERGE
+ MergeFileStyleDiff MergeFileFlags = C.GIT_MERGE_FILE_STYLE_DIFF3
+ MergeFileStyleSimplifyAlnum MergeFileFlags = C.GIT_MERGE_FILE_SIMPLIFY_ALNUM
+)
+
+type MergeFileOptions struct {
+ AncestorLabel string
+ OurLabel string
+ TheirLabel string
+ Favor MergeFileFavor
+ Flags MergeFileFlags
+ MarkerSize uint16
+}
+
+func mergeFileOptionsFromC(c C.git_merge_file_options) MergeFileOptions {
+ return MergeFileOptions{
+ AncestorLabel: C.GoString(c.ancestor_label),
+ OurLabel: C.GoString(c.our_label),
+ TheirLabel: C.GoString(c.their_label),
+ Favor: MergeFileFavor(c.favor),
+ Flags: MergeFileFlags(c.flags),
+ MarkerSize: uint16(c.marker_size),
+ }
+}
+
+func populateCMergeFileOptions(c *C.git_merge_file_options, options MergeFileOptions) {
+ c.ancestor_label = C.CString(options.AncestorLabel)
+ c.our_label = C.CString(options.OurLabel)
+ c.their_label = C.CString(options.TheirLabel)
+ c.favor = C.git_merge_file_favor_t(options.Favor)
+ c.flags = C.git_merge_file_flag_t(options.Flags)
+ c.marker_size = C.ushort(options.MarkerSize)
+}
+
+func freeCMergeFileOptions(c *C.git_merge_file_options) {
+ C.free(unsafe.Pointer(c.ancestor_label))
+ C.free(unsafe.Pointer(c.our_label))
+ C.free(unsafe.Pointer(c.their_label))
+}
+
+func MergeFile(ancestor MergeFileInput, ours MergeFileInput, theirs MergeFileInput, options *MergeFileOptions) (*MergeFileResult, error) {
+
+ ancestorPath := C.CString(ancestor.Path)
+ defer C.free(unsafe.Pointer(ancestorPath))
+ var ancestorContents *byte
+ if len(ancestor.Contents) > 0 {
+ ancestorContents = &ancestor.Contents[0]
+ }
+
+ oursPath := C.CString(ours.Path)
+ defer C.free(unsafe.Pointer(oursPath))
+ var oursContents *byte
+ if len(ours.Contents) > 0 {
+ oursContents = &ours.Contents[0]
+ }
+
+ theirsPath := C.CString(theirs.Path)
+ defer C.free(unsafe.Pointer(theirsPath))
+ var theirsContents *byte
+ if len(theirs.Contents) > 0 {
+ theirsContents = &theirs.Contents[0]
+ }
+
+ var copts *C.git_merge_file_options
+ if options != nil {
+ copts = &C.git_merge_file_options{}
+ ecode := C.git_merge_file_init_options(copts, C.GIT_MERGE_FILE_OPTIONS_VERSION)
+ if ecode < 0 {
+ return nil, MakeGitError(ecode)
+ }
+ populateCMergeFileOptions(copts, *options)
+ defer freeCMergeFileOptions(copts)
+ }
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ var result C.git_merge_file_result
+ ecode := C._go_git_merge_file(&result,
+ (*C.char)(unsafe.Pointer(ancestorContents)), C.size_t(len(ancestor.Contents)), ancestorPath, C.uint(ancestor.Mode),
+ (*C.char)(unsafe.Pointer(oursContents)), C.size_t(len(ours.Contents)), oursPath, C.uint(ours.Mode),
+ (*C.char)(unsafe.Pointer(theirsContents)), C.size_t(len(theirs.Contents)), theirsPath, C.uint(theirs.Mode),
+ copts)
+ runtime.KeepAlive(ancestor)
+ runtime.KeepAlive(ours)
+ runtime.KeepAlive(theirs)
+ if ecode < 0 {
+ return nil, MakeGitError(ecode)
+ }
+
+ return newMergeFileResultFromC(&result), nil
+
+}
+
+// TODO: GIT_EXTERN(int) git_merge_file_from_index(git_merge_file_result *out,git_repository *repo,const git_index_entry *ancestor, const git_index_entry *ours, const git_index_entry *theirs, const git_merge_file_options *opts);