From 0497d2b1146c307923b0a372dde75ace8557fe66 Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Fri, 28 Feb 2014 00:21:48 -0800 Subject: [PATCH 01/11] fix bad merge (LastError -> MakeGitError) --- repository.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/repository.go b/repository.go index bf6aee5..f6bfcb4 100644 --- a/repository.go +++ b/repository.go @@ -352,8 +352,9 @@ func (v *Repository) EnsureLog(name string) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - if ret := C.git_reference_ensure_log(v.ptr, cname); ret < 0 { - return LastError() + ret := C.git_reference_ensure_log(v.ptr, cname) + if ret < 0 { + return MakeGitError(ret) } return nil @@ -370,7 +371,7 @@ func (v *Repository) HasLog(name string) (bool, error) { ret := C.git_reference_has_log(v.ptr, cname) if ret < 0 { - return false, LastError() + return false, MakeGitError(ret) } return ret == 1, nil @@ -385,8 +386,9 @@ func (v *Repository) DwimReference(name string) (*Reference, error) { defer runtime.UnlockOSThread() var ptr *C.git_reference - if ret := C.git_reference_dwim(&ptr, v.ptr, cname); ret < 0 { - return nil, LastError() + ret := C.git_reference_dwim(&ptr, v.ptr, cname) + if ret < 0 { + return nil, MakeGitError(ret) } return newReferenceFromC(ptr), nil -- 2.45.2 From 9e7ba027086173fdf540b8ddb465a57bfa281126 Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Fri, 28 Feb 2014 01:06:41 -0800 Subject: [PATCH 02/11] wrap merge functions --- merge.go | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ wrapper.c | 15 +++++ 2 files changed, 210 insertions(+) create mode 100644 merge.go diff --git a/merge.go b/merge.go new file mode 100644 index 0000000..03ace2b --- /dev/null +++ b/merge.go @@ -0,0 +1,195 @@ +package git + +/* +#include +#include + +extern git_merge_head** _go_git_make_merge_head_array(size_t len); +extern void _go_git_merge_head_array_set(git_merge_head** array, git_merge_head* ptr, size_t n); +extern git_merge_head* _go_git_merge_head_array_get(git_merge_head** array, size_t n); + +*/ +import "C" +import ( + "runtime" + "unsafe" +) + +type MergeHead struct { + ptr *C.git_merge_head +} + +func newMergeHeadFromC(c *C.git_merge_head) *MergeHead { + mh := &MergeHead{ptr: c} + runtime.SetFinalizer(mh, (*MergeHead).Free) + return mh +} + +func (mh *MergeHead) Free() { + C.git_merge_head_free(mh.ptr) + runtime.SetFinalizer(mh, nil) +} + +func (r *Repository) MergeHeadFromFetchHead(branchName string, remoteURL string, oid *Oid) (*MergeHead, error) { + mh := &MergeHead{} + + cbranchName := C.CString(branchName) + defer C.free(unsafe.Pointer(cbranchName)) + + cremoteURL := C.CString(remoteURL) + defer C.free(unsafe.Pointer(cremoteURL)) + + ret := C.git_merge_head_from_fetchhead(&mh.ptr, r.ptr, cbranchName, cremoteURL, oid.toC()) + if ret < 0 { + return nil, MakeGitError(ret) + } + runtime.SetFinalizer(mh, (*MergeHead).Free) + return mh, nil +} + +func (r *Repository) MergeHeadFromId(oid *Oid) (*MergeHead, error) { + mh := &MergeHead{} + + ret := C.git_merge_head_from_id(&mh.ptr, r.ptr, oid.toC()) + if ret < 0 { + return nil, MakeGitError(ret) + } + runtime.SetFinalizer(mh, (*MergeHead).Free) + return mh, nil +} + +func (r *Repository) MergeHeadFromRef(ref *Reference) (*MergeHead, error) { + mh := &MergeHead{} + + ret := C.git_merge_head_from_ref(&mh.ptr, r.ptr, ref.ptr) + if ret < 0 { + return nil, MakeGitError(ret) + } + runtime.SetFinalizer(mh, (*MergeHead).Free) + return mh, nil +} + +type MergeOptions struct { +} + +func (mo *MergeOptions) toC() *C.git_merge_opts { + return nil +} + +type MergeTreeOptions struct { +} + +func (mo *MergeTreeOptions) toC() *C.git_merge_tree_opts { + return nil +} + +type MergeResult struct { + ptr *C.git_merge_result +} + +func newMergeResultFromC(c *C.git_merge_result) *MergeResult { + mr := &MergeResult{ptr: c} + runtime.SetFinalizer(mr, (*MergeResult).Free) + return mr +} + +func (mr *MergeResult) Free() { + runtime.SetFinalizer(mr, nil) + C.git_merge_result_free(mr.ptr) +} + +func (mr *MergeResult) IsFastForward() bool { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_merge_result_is_fastforward(mr.ptr) + return ret != 0 +} + +func (mr *MergeResult) IsUpToDate() bool { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_merge_result_is_uptodate(mr.ptr) + return ret != 0 +} + +func (mr *MergeResult) FastForwardId() (*Oid, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + var oid C.git_oid + ret := C.git_merge_result_fastforward_id(&oid, mr.ptr) + if ret < 0 { + return nil, MakeGitError(ret) + } + return newOidFromC(&oid), nil +} + +func (r *Repository) Merge(theirHeads []MergeHead, options MergeOptions) (*MergeResult, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + var result *C.git_merge_result + + copts := options.toC() + + cmerge_head_array := C._go_git_make_merge_head_array(C.size_t(len(theirHeads))) + defer C.free(unsafe.Pointer(cmerge_head_array)) + + for i, _ := range theirHeads { + C._go_git_merge_head_array_set(cmerge_head_array, theirHeads[i].ptr, C.size_t(i)) + } + + err := C.git_merge(&result, r.ptr, cmerge_head_array, C.size_t(len(theirHeads)), copts) + if err < 0 { + return nil, MakeGitError(err) + } + return newMergeResultFromC(result), nil +} + +func (r *Repository) MergeCommits(ours *Commit, theirs *Commit, options MergeTreeOptions) (*Index, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + copts := options.toC() + + idx := &Index{} + + ret := C.git_merge_commits(&idx.ptr, r.ptr, ours.ptr, theirs.ptr, copts) + if ret < 0 { + return nil, MakeGitError(ret) + } + runtime.SetFinalizer(idx, (*Index).Free) + return idx, nil +} + +func (r *Repository) MergeTrees(ancestor *Tree, ours *Tree, theirs *Tree, options MergeTreeOptions) (*Index, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + copts := options.toC() + + idx := &Index{} + + ret := C.git_merge_trees(&idx.ptr, r.ptr, ancestor.ptr, ours.ptr, theirs.ptr, copts) + if ret < 0 { + return nil, MakeGitError(ret) + } + runtime.SetFinalizer(idx, (*Index).Free) + return idx, 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()) + if ret < 0 { + return nil, MakeGitError(ret) + } + return newOidFromC(&oid), nil +} + +// int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[]); diff --git a/wrapper.c b/wrapper.c index 2af3974..66e10bd 100644 --- a/wrapper.c +++ b/wrapper.c @@ -24,4 +24,19 @@ int _go_git_odb_foreach(git_odb *db, void *payload) { return git_odb_foreach(db, (git_odb_foreach_cb)&odbForEachCb, payload); } + +git_merge_head** _go_git_make_merge_head_array(size_t len) +{ + return (git_merge_head**)malloc(sizeof(git_merge_head*) * len); +} + +void _go_git_merge_head_array_set(git_merge_head** array, git_merge_head* ptr, size_t n) +{ + array[n] = ptr; +} + +git_merge_head* _go_git_merge_head_array_get(git_merge_head** array, size_t n) +{ + return array[n]; +} /* EOF */ -- 2.45.2 From 5f5906878ebb6db6b1841e42c368f5b69fd7794d Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Fri, 28 Feb 2014 01:31:01 -0800 Subject: [PATCH 03/11] merge options / merge tree options --- merge.go | 50 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/merge.go b/merge.go index 03ace2b..11219e0 100644 --- a/merge.go +++ b/merge.go @@ -69,18 +69,62 @@ func (r *Repository) MergeHeadFromRef(ref *Reference) (*MergeHead, error) { return mh, nil } +type MergeFlag int + +const ( + MergeFlagDefault MergeFlag = iota + MergeNoFastForward + MergeFastForwardOnly +) + type MergeOptions struct { + Version uint + Flags MergeFlag + + TreeOptions MergeTreeOptions + //TODO: CheckoutOptions CheckoutOptions } func (mo *MergeOptions) toC() *C.git_merge_opts { - return nil + return &C.git_merge_opts{ + version: C.uint(mo.Version), + merge_flags: C.git_merge_flags_t(mo.Flags), + merge_tree_opts: *mo.TreeOptions.toC(), + } } +type MergeTreeFlag int + +const ( + MergeTreeFindRenames MergeTreeFlag = 1 << iota +) + +type MergeFileFavorType int + +const ( + MergeFileFavorNormal MergeFileFavorType = iota + MergeFileFavorOurs + MergeFileFavorTheirs + MergeFileFavorUnion +) + type MergeTreeOptions struct { + Version uint + Flags MergeTreeFlag + RenameThreshold uint + TargetLimit uint + //TODO: SimilarityMetric *DiffSimilarityMetric + FileFavor MergeFileFavorType } func (mo *MergeTreeOptions) toC() *C.git_merge_tree_opts { - return nil + return &C.git_merge_tree_opts{ + version: C.uint(mo.Version), + flags: C.git_merge_tree_flag_t(mo.Flags), + rename_threshold: C.uint(mo.RenameThreshold), + target_limit: C.uint(mo.TargetLimit), + file_favor: C.git_merge_file_favor_t(mo.FileFavor), + } } type MergeResult struct { @@ -192,4 +236,4 @@ func (r *Repository) MergeBase(one *Oid, two *Oid) (*Oid, error) { return newOidFromC(&oid), nil } -// int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[]); +//TODO: int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[]); -- 2.45.2 From da64faf8bde3de19fb9b0fc029df2bf6f5b691a1 Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Fri, 28 Feb 2014 11:13:51 -0800 Subject: [PATCH 04/11] switch from iota to explicit def --- merge.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/merge.go b/merge.go index 11219e0..82e01fb 100644 --- a/merge.go +++ b/merge.go @@ -72,9 +72,9 @@ func (r *Repository) MergeHeadFromRef(ref *Reference) (*MergeHead, error) { type MergeFlag int const ( - MergeFlagDefault MergeFlag = iota - MergeNoFastForward - MergeFastForwardOnly + MergeFlagDefault MergeFlag = 0 + MergeNoFastForward = 1 + MergeFastForwardOnly = 2 ) type MergeOptions struct { @@ -96,16 +96,16 @@ func (mo *MergeOptions) toC() *C.git_merge_opts { type MergeTreeFlag int const ( - MergeTreeFindRenames MergeTreeFlag = 1 << iota + MergeTreeFindRenames MergeTreeFlag = 1 << 0 ) type MergeFileFavorType int const ( - MergeFileFavorNormal MergeFileFavorType = iota - MergeFileFavorOurs - MergeFileFavorTheirs - MergeFileFavorUnion + MergeFileFavorNormal MergeFileFavorType = 0 + MergeFileFavorOurs = 1 + MergeFileFavorTheirs = 2 + MergeFileFavorUnion = 3 ) type MergeTreeOptions struct { -- 2.45.2 From 6af4d4a82534f0cebc3d9ac680f83da1aace884a Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Fri, 28 Feb 2014 11:15:39 -0800 Subject: [PATCH 05/11] move finalizer above free --- merge.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/merge.go b/merge.go index 82e01fb..570c361 100644 --- a/merge.go +++ b/merge.go @@ -26,8 +26,8 @@ func newMergeHeadFromC(c *C.git_merge_head) *MergeHead { } func (mh *MergeHead) Free() { - C.git_merge_head_free(mh.ptr) runtime.SetFinalizer(mh, nil) + C.git_merge_head_free(mh.ptr) } func (r *Repository) MergeHeadFromFetchHead(branchName string, remoteURL string, oid *Oid) (*MergeHead, error) { -- 2.45.2 From e5946d4009cf58f7a041f647e953a454159e2d4f Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Fri, 28 Feb 2014 14:33:38 -0800 Subject: [PATCH 06/11] Add defaults, add simple test, merge heads should be pointer array --- merge.go | 12 +++++++++++- merge_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 merge_test.go diff --git a/merge.go b/merge.go index 570c361..59cdd58 100644 --- a/merge.go +++ b/merge.go @@ -85,6 +85,12 @@ type MergeOptions struct { //TODO: CheckoutOptions CheckoutOptions } +func DefaultMergeOptions() MergeOptions { + options := MergeOptions{Version: 1} + options.TreeOptions = DefaultMergeTreeOptions() + return options +} + func (mo *MergeOptions) toC() *C.git_merge_opts { return &C.git_merge_opts{ version: C.uint(mo.Version), @@ -117,6 +123,10 @@ type MergeTreeOptions struct { FileFavor MergeFileFavorType } +func DefaultMergeTreeOptions() MergeTreeOptions { + return MergeTreeOptions{Version: 1} +} + func (mo *MergeTreeOptions) toC() *C.git_merge_tree_opts { return &C.git_merge_tree_opts{ version: C.uint(mo.Version), @@ -170,7 +180,7 @@ func (mr *MergeResult) FastForwardId() (*Oid, error) { return newOidFromC(&oid), nil } -func (r *Repository) Merge(theirHeads []MergeHead, options MergeOptions) (*MergeResult, error) { +func (r *Repository) Merge(theirHeads []*MergeHead, options MergeOptions) (*MergeResult, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() diff --git a/merge_test.go b/merge_test.go new file mode 100644 index 0000000..9bf9b9d --- /dev/null +++ b/merge_test.go @@ -0,0 +1,27 @@ +package git + +import ( + "testing" +) + +func Test_Merge_With_Self(t *testing.T) { + + repo := createTestRepo(t) + seedTestRepo(t, repo) + + master, err := repo.LookupReference("refs/heads/master") + checkFatal(t, err) + + mergeHead, err := repo.MergeHeadFromRef(master) + checkFatal(t, err) + + options := DefaultMergeOptions() + mergeHeads := make([]*MergeHead, 1) + mergeHeads[0] = mergeHead + results, err := repo.Merge(mergeHeads, options) + checkFatal(t, err) + + if !results.IsUpToDate() { + t.Fatal("Expected up to date") + } +} -- 2.45.2 From 6a068d526547effe3dd11c77c64bb81cd5d92040 Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Tue, 11 Mar 2014 12:47:56 -0700 Subject: [PATCH 07/11] remove useless wrappers --- merge.go | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/merge.go b/merge.go index 59cdd58..d5f5cb9 100644 --- a/merge.go +++ b/merge.go @@ -72,9 +72,9 @@ func (r *Repository) MergeHeadFromRef(ref *Reference) (*MergeHead, error) { type MergeFlag int const ( - MergeFlagDefault MergeFlag = 0 - MergeNoFastForward = 1 - MergeFastForwardOnly = 2 + MergeFlagDefault MergeFlag = C.GIT_MERGE_DEFAULT + MergeNoFastForward = C.GIT_MERGE_NO_FASTFORWARD + MergeFastForwardOnly = C.GIT_MERGE_FASTFORWARD_ONLY ) type MergeOptions struct { @@ -187,15 +187,12 @@ func (r *Repository) Merge(theirHeads []*MergeHead, options MergeOptions) (*Merg var result *C.git_merge_result copts := options.toC() - - cmerge_head_array := C._go_git_make_merge_head_array(C.size_t(len(theirHeads))) - defer C.free(unsafe.Pointer(cmerge_head_array)) - - for i, _ := range theirHeads { - C._go_git_merge_head_array_set(cmerge_head_array, theirHeads[i].ptr, C.size_t(i)) + gmerge_head_array := make([]*C.git_merge_head, len(theirHeads)) + for i := 0; i < len(theirHeads); i++ { + gmerge_head_array[i] = theirHeads[i].ptr } - - err := C.git_merge(&result, r.ptr, cmerge_head_array, C.size_t(len(theirHeads)), copts) + ptr := unsafe.Pointer(&gmerge_head_array[0]) + err := C.git_merge(&result, r.ptr, (**C.git_merge_head)(ptr), C.size_t(len(theirHeads)), copts) if err < 0 { return nil, MakeGitError(err) } -- 2.45.2 From 50a3c4aa0935adb798bc4ecf6612710908d227cf Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Wed, 26 Mar 2014 11:28:48 -0700 Subject: [PATCH 08/11] update to new merge API --- checkout.go | 19 +++++-- merge.go | 137 ++++++++++++++++---------------------------------- merge_test.go | 10 ++-- submodule.go | 18 +++++-- 4 files changed, 73 insertions(+), 111 deletions(-) diff --git a/checkout.go b/checkout.go index 5b72b9a..456c302 100644 --- a/checkout.go +++ b/checkout.go @@ -5,8 +5,8 @@ package git */ import "C" import ( - "runtime" "os" + "runtime" ) type CheckoutStrategy uint @@ -32,10 +32,19 @@ const ( type CheckoutOpts struct { Strategy CheckoutStrategy // Default will be a dry run - DisableFilters bool // Don't apply filters like CRLF conversion - DirMode os.FileMode // Default is 0755 - FileMode os.FileMode // Default is 0644 or 0755 as dictated by blob - FileOpenFlags int // Default is O_CREAT | O_TRUNC | O_WRONLY + DisableFilters bool // Don't apply filters like CRLF conversion + DirMode os.FileMode // Default is 0755 + FileMode os.FileMode // Default is 0644 or 0755 as dictated by blob + FileOpenFlags int // Default is O_CREAT | O_TRUNC | O_WRONLY +} + +func (opts *CheckoutOpts) toC() *C.git_checkout_options { + if opts == nil { + return nil + } + c := C.git_checkout_options{} + populateCheckoutOpts(&c, opts) + return &c } // Convert the CheckoutOpts struct to the corresponding diff --git a/merge.go b/merge.go index d5f5cb9..d8a8936 100644 --- a/merge.go +++ b/merge.go @@ -69,66 +69,47 @@ func (r *Repository) MergeHeadFromRef(ref *Reference) (*MergeHead, error) { return mh, nil } -type MergeFlag int +type MergeTreeFlag int const ( - MergeFlagDefault MergeFlag = C.GIT_MERGE_DEFAULT - MergeNoFastForward = C.GIT_MERGE_NO_FASTFORWARD - MergeFastForwardOnly = C.GIT_MERGE_FASTFORWARD_ONLY + MergeTreeFindRenames MergeTreeFlag = C.GIT_MERGE_TREE_FIND_RENAMES ) type MergeOptions struct { Version uint - Flags MergeFlag + Flags MergeTreeFlag - TreeOptions MergeTreeOptions - //TODO: CheckoutOptions CheckoutOptions + RenameThreshold uint + TargetLimit uint + FileFavor MergeFileFavorType + + //TODO: Diff similarity metric } -func DefaultMergeOptions() MergeOptions { - options := MergeOptions{Version: 1} - options.TreeOptions = DefaultMergeTreeOptions() - return options -} - -func (mo *MergeOptions) toC() *C.git_merge_opts { - return &C.git_merge_opts{ - version: C.uint(mo.Version), - merge_flags: C.git_merge_flags_t(mo.Flags), - merge_tree_opts: *mo.TreeOptions.toC(), +func mergeOptionsFromC(opts *C.git_merge_options) MergeOptions { + return MergeOptions{ + Version: uint(opts.version), + Flags: MergeTreeFlag(opts.flags), + RenameThreshold: uint(opts.rename_threshold), + TargetLimit: uint(opts.target_limit), + FileFavor: MergeFileFavorType(opts.file_favor), } } -type MergeTreeFlag int - -const ( - MergeTreeFindRenames MergeTreeFlag = 1 << 0 -) - -type MergeFileFavorType int - -const ( - MergeFileFavorNormal MergeFileFavorType = 0 - MergeFileFavorOurs = 1 - MergeFileFavorTheirs = 2 - MergeFileFavorUnion = 3 -) - -type MergeTreeOptions struct { - Version uint - Flags MergeTreeFlag - RenameThreshold uint - TargetLimit uint - //TODO: SimilarityMetric *DiffSimilarityMetric - FileFavor MergeFileFavorType +func DefaultMergeOptions() (MergeOptions, error) { + opts := C.git_merge_options{} + ecode := C.git_merge_init_options(&opts, C.GIT_MERGE_OPTIONS_VERSION) + if ecode < 0 { + return MergeOptions{}, MakeGitError(ecode) + } + return mergeOptionsFromC(&opts), nil } -func DefaultMergeTreeOptions() MergeTreeOptions { - return MergeTreeOptions{Version: 1} -} - -func (mo *MergeTreeOptions) toC() *C.git_merge_tree_opts { - return &C.git_merge_tree_opts{ +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_tree_flag_t(mo.Flags), rename_threshold: C.uint(mo.RenameThreshold), @@ -137,69 +118,35 @@ func (mo *MergeTreeOptions) toC() *C.git_merge_tree_opts { } } -type MergeResult struct { - ptr *C.git_merge_result -} +type MergeFileFavorType int -func newMergeResultFromC(c *C.git_merge_result) *MergeResult { - mr := &MergeResult{ptr: c} - runtime.SetFinalizer(mr, (*MergeResult).Free) - return mr -} +const ( + MergeFileFavorNormal MergeFileFavorType = C.GIT_MERGE_FILE_FAVOR_NORMAL + MergeFileFavorOurs = C.GIT_MERGE_FILE_FAVOR_OURS + MergeFileFavorTheirs = C.GIT_MERGE_FILE_FAVOR_THEIRS + MergeFileFavorUnion = C.GIT_MERGE_FILE_FAVOR_UNION +) -func (mr *MergeResult) Free() { - runtime.SetFinalizer(mr, nil) - C.git_merge_result_free(mr.ptr) -} - -func (mr *MergeResult) IsFastForward() bool { +func (r *Repository) Merge(theirHeads []*MergeHead, mergeOptions *MergeOptions, checkoutOptions *CheckoutOpts) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_merge_result_is_fastforward(mr.ptr) - return ret != 0 -} + cMergeOpts := mergeOptions.toC() + cCheckoutOpts := checkoutOptions.toC() -func (mr *MergeResult) IsUpToDate() bool { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_merge_result_is_uptodate(mr.ptr) - return ret != 0 -} - -func (mr *MergeResult) FastForwardId() (*Oid, error) { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - var oid C.git_oid - ret := C.git_merge_result_fastforward_id(&oid, mr.ptr) - if ret < 0 { - return nil, MakeGitError(ret) - } - return newOidFromC(&oid), nil -} - -func (r *Repository) Merge(theirHeads []*MergeHead, options MergeOptions) (*MergeResult, error) { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - var result *C.git_merge_result - - copts := options.toC() gmerge_head_array := make([]*C.git_merge_head, 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(&result, r.ptr, (**C.git_merge_head)(ptr), C.size_t(len(theirHeads)), copts) + err := C.git_merge(r.ptr, (**C.git_merge_head)(ptr), C.size_t(len(theirHeads)), cMergeOpts, cCheckoutOpts) if err < 0 { - return nil, MakeGitError(err) + return MakeGitError(err) } - return newMergeResultFromC(result), nil + return nil } -func (r *Repository) MergeCommits(ours *Commit, theirs *Commit, options MergeTreeOptions) (*Index, error) { +func (r *Repository) MergeCommits(ours *Commit, theirs *Commit, options MergeOptions) (*Index, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -215,7 +162,7 @@ func (r *Repository) MergeCommits(ours *Commit, theirs *Commit, options MergeTre return idx, nil } -func (r *Repository) MergeTrees(ancestor *Tree, ours *Tree, theirs *Tree, options MergeTreeOptions) (*Index, error) { +func (r *Repository) MergeTrees(ancestor *Tree, ours *Tree, theirs *Tree, options MergeOptions) (*Index, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() diff --git a/merge_test.go b/merge_test.go index 9bf9b9d..74f08bc 100644 --- a/merge_test.go +++ b/merge_test.go @@ -4,7 +4,7 @@ import ( "testing" ) -func Test_Merge_With_Self(t *testing.T) { +func TestMergeWithSelf(t *testing.T) { repo := createTestRepo(t) seedTestRepo(t, repo) @@ -15,13 +15,9 @@ func Test_Merge_With_Self(t *testing.T) { mergeHead, err := repo.MergeHeadFromRef(master) checkFatal(t, err) - options := DefaultMergeOptions() + options, _ := DefaultMergeOptions() mergeHeads := make([]*MergeHead, 1) mergeHeads[0] = mergeHead - results, err := repo.Merge(mergeHeads, options) + err = repo.Merge(mergeHeads, &options, nil) checkFatal(t, err) - - if !results.IsUpToDate() { - t.Fatal("Expected up to date") - } } diff --git a/submodule.go b/submodule.go index a94afd4..e192e15 100644 --- a/submodule.go +++ b/submodule.go @@ -287,22 +287,32 @@ func (sub *Submodule) Open() (*Repository, error) { return repo, nil } -func (sub *Submodule) Reload() error { +func (sub *Submodule) Reload(force bool) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_submodule_reload(sub.ptr) + cforce := C.int(0) + if force { + cforce = C.int(1) + } + + ret := C.git_submodule_reload(sub.ptr, cforce) if ret < 0 { return MakeGitError(ret) } return nil } -func (repo *Repository) ReloadAllSubmodules() error { +func (repo *Repository) ReloadAllSubmodules(force bool) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_submodule_reload_all(repo.ptr) + cforce := C.int(0) + if force { + cforce = C.int(1) + } + + ret := C.git_submodule_reload_all(repo.ptr, cforce) if ret < 0 { return MakeGitError(ret) } -- 2.45.2 From eff3a8b4d04349a5462f54c31d6f39213cecef71 Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Wed, 26 Mar 2014 12:17:23 -0700 Subject: [PATCH 09/11] support nil merge options --- merge.go | 4 ++-- merge_test.go | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/merge.go b/merge.go index d8a8936..34894f6 100644 --- a/merge.go +++ b/merge.go @@ -146,7 +146,7 @@ func (r *Repository) Merge(theirHeads []*MergeHead, mergeOptions *MergeOptions, return nil } -func (r *Repository) MergeCommits(ours *Commit, theirs *Commit, options MergeOptions) (*Index, error) { +func (r *Repository) MergeCommits(ours *Commit, theirs *Commit, options *MergeOptions) (*Index, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -162,7 +162,7 @@ func (r *Repository) MergeCommits(ours *Commit, theirs *Commit, options MergeOpt return idx, nil } -func (r *Repository) MergeTrees(ancestor *Tree, ours *Tree, theirs *Tree, options MergeOptions) (*Index, error) { +func (r *Repository) MergeTrees(ancestor *Tree, ours *Tree, theirs *Tree, options *MergeOptions) (*Index, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() diff --git a/merge_test.go b/merge_test.go index 74f08bc..1c0606e 100644 --- a/merge_test.go +++ b/merge_test.go @@ -15,9 +15,8 @@ func TestMergeWithSelf(t *testing.T) { mergeHead, err := repo.MergeHeadFromRef(master) checkFatal(t, err) - options, _ := DefaultMergeOptions() mergeHeads := make([]*MergeHead, 1) mergeHeads[0] = mergeHead - err = repo.Merge(mergeHeads, &options, nil) + err = repo.Merge(mergeHeads, nil, nil) checkFatal(t, err) } -- 2.45.2 From 0b48ba2224ec41dfc10a379ce1b7bb86441cc432 Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Wed, 26 Mar 2014 14:42:01 -0700 Subject: [PATCH 10/11] Merge analysis and merge file --- merge.go | 173 ++++++++++++++++++++++++++++++++++++++++++++++++-- merge_test.go | 53 ++++++++++++++++ push_test.go | 2 - 3 files changed, 219 insertions(+), 9 deletions(-) diff --git a/merge.go b/merge.go index 34894f6..9feb88a 100644 --- a/merge.go +++ b/merge.go @@ -81,7 +81,7 @@ type MergeOptions struct { RenameThreshold uint TargetLimit uint - FileFavor MergeFileFavorType + FileFavor MergeFileFavor //TODO: Diff similarity metric } @@ -92,7 +92,7 @@ func mergeOptionsFromC(opts *C.git_merge_options) MergeOptions { Flags: MergeTreeFlag(opts.flags), RenameThreshold: uint(opts.rename_threshold), TargetLimit: uint(opts.target_limit), - FileFavor: MergeFileFavorType(opts.file_favor), + FileFavor: MergeFileFavor(opts.file_favor), } } @@ -118,13 +118,13 @@ func (mo *MergeOptions) toC() *C.git_merge_options { } } -type MergeFileFavorType int +type MergeFileFavor int const ( - MergeFileFavorNormal MergeFileFavorType = C.GIT_MERGE_FILE_FAVOR_NORMAL - MergeFileFavorOurs = C.GIT_MERGE_FILE_FAVOR_OURS - MergeFileFavorTheirs = C.GIT_MERGE_FILE_FAVOR_THEIRS - MergeFileFavorUnion = C.GIT_MERGE_FILE_FAVOR_UNION + MergeFileFavorNormal MergeFileFavor = C.GIT_MERGE_FILE_FAVOR_NORMAL + MergeFileFavorOurs = C.GIT_MERGE_FILE_FAVOR_OURS + MergeFileFavorTheirs = C.GIT_MERGE_FILE_FAVOR_THEIRS + MergeFileFavorUnion = C.GIT_MERGE_FILE_FAVOR_UNION ) func (r *Repository) Merge(theirHeads []*MergeHead, mergeOptions *MergeOptions, checkoutOptions *CheckoutOpts) error { @@ -146,6 +146,34 @@ func (r *Repository) Merge(theirHeads []*MergeHead, mergeOptions *MergeOptions, return nil } +type MergeAnalysis int + +const ( + MergeAnalysisNone MergeAnalysis = C.GIT_MERGE_ANALYSIS_NONE + MergeAnalysisNormal = C.GIT_MERGE_ANALYSIS_NORMAL + MergeAnalysisUpToDate = C.GIT_MERGE_ANALYSIS_UP_TO_DATE + MergeAnalysisFastForward = C.GIT_MERGE_ANALYSIS_FASTFORWARD + MergeAnalysisUnborn = C.GIT_MERGE_ANALYSIS_UNBORN +) + +func (r *Repository) MergeAnalysis(theirHeads []*MergeHead) (MergeAnalysis, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + gmerge_head_array := make([]*C.git_merge_head, 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 + err := C.git_merge_analysis(&analysis, r.ptr, (**C.git_merge_head)(ptr), C.size_t(len(theirHeads))) + if err < 0 { + return MergeAnalysisNone, MakeGitError(err) + } + return MergeAnalysis(analysis), nil + +} + func (r *Repository) MergeCommits(ours *Commit, theirs *Commit, options *MergeOptions) (*Index, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -191,3 +219,134 @@ func (r *Repository) MergeBase(one *Oid, two *Oid) (*Oid, error) { } //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 // Contents of file, will be invalid after Free + + 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) + } + + r := &MergeFileResult{ + Automergeable: c.automergeable != 0, + Path: path, + Mode: uint(c.mode), + Contents: C.GoBytes(unsafe.Pointer(c.ptr), C.int(c.len)), + 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 +} + +// populate a C struct with merge file input, make sure to use freeMergeFileInput to clean up allocs +func populateCMergeFileInput(c *C.git_merge_file_input, input MergeFileInput) { + c.path = C.CString(input.Path) + c.ptr = (*C.char)(unsafe.Pointer(&input.Contents[0])) + c.size = C.size_t(len(input.Contents)) + c.mode = C.uint(input.Mode) +} + +func freeCMergeFileInput(c *C.git_merge_file_input) { + C.free(unsafe.Pointer(c.path)) +} + +type MergeFileFlags int + +const ( + MergeFileDefault MergeFileFlags = C.GIT_MERGE_FILE_DEFAULT + + MergeFileStyleMerge = C.GIT_MERGE_FILE_STYLE_MERGE + MergeFileStyleDiff = C.GIT_MERGE_FILE_STYLE_DIFF3 + MergeFileStyleSimplifyAlnum = C.GIT_MERGE_FILE_SIMPLIFY_ALNUM +) + +type MergeFileOptions struct { + AncestorLabel string + OurLabel string + TheirLabel string + Favor MergeFileFavor + Flags MergeFileFlags +} + +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), + } +} + +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_flags_t(options.Flags) +} + +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) { + + var cancestor C.git_merge_file_input + var cours C.git_merge_file_input + var ctheirs C.git_merge_file_input + + populateCMergeFileInput(&cancestor, ancestor) + defer freeCMergeFileInput(&cancestor) + populateCMergeFileInput(&cours, ours) + defer freeCMergeFileInput(&cours) + populateCMergeFileInput(&ctheirs, theirs) + defer freeCMergeFileInput(&ctheirs) + + 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) + } + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + var result C.git_merge_file_result + ecode := C.git_merge_file(&result, &cancestor, &cours, &ctheirs, copts) + 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); diff --git a/merge_test.go b/merge_test.go index 1c0606e..3f9dc33 100644 --- a/merge_test.go +++ b/merge_test.go @@ -20,3 +20,56 @@ func TestMergeWithSelf(t *testing.T) { err = repo.Merge(mergeHeads, nil, nil) checkFatal(t, err) } + +func TestMergeAnalysisWithSelf(t *testing.T) { + + repo := createTestRepo(t) + seedTestRepo(t, repo) + + master, err := repo.LookupReference("refs/heads/master") + checkFatal(t, err) + + mergeHead, err := repo.MergeHeadFromRef(master) + checkFatal(t, err) + + mergeHeads := make([]*MergeHead, 1) + mergeHeads[0] = mergeHead + a, err := repo.MergeAnalysis(mergeHeads) + checkFatal(t, err) + + if a != MergeAnalysisUpToDate { + t.Fatalf("Expected up to date merge, not %v", a) + } +} + +func TestMergeSameFile(t *testing.T) { + + file := MergeFileInput{ + Path: "test", + Mode: 33188, + Contents: []byte("hello world"), + } + + result, err := MergeFile(file, file, file, nil) + checkFatal(t, err) + if !result.Automergeable { + t.Fatal("expected automergeable") + } + if result.Path != file.Path { + t.Fatal("path was incorrect") + } + if result.Mode != file.Mode { + t.Fatal("mode was incorrect") + } + + compareBytes(t, file.Contents, result.Contents) + +} + +func compareBytes(t *testing.T, expected, actual []byte) { + for i, v := range expected { + if actual[i] != v { + t.Fatalf("Bad bytes") + } + } +} diff --git a/push_test.go b/push_test.go index c1e6a22..65f4dd2 100644 --- a/push_test.go +++ b/push_test.go @@ -1,7 +1,6 @@ package git import ( - "log" "os" "testing" "time" @@ -45,7 +44,6 @@ func Test_Push_ToRemote(t *testing.T) { checkFatal(t, err) err = push.StatusForeach(func(ref string, msg string) int { - log.Printf("%s -> %s", ref, msg) return 0 }) checkFatal(t, err) -- 2.45.2 From 9d8cbe7547d499fefb69bf42e975a2f7e8e7fca4 Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Fri, 4 Apr 2014 00:45:41 -0700 Subject: [PATCH 11/11] use cast_ptr instead of ptr for travis go tip build --- merge.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/merge.go b/merge.go index 9c51a85..48a28b0 100644 --- a/merge.go +++ b/merge.go @@ -182,7 +182,7 @@ func (r *Repository) MergeCommits(ours *Commit, theirs *Commit, options *MergeOp idx := &Index{} - ret := C.git_merge_commits(&idx.ptr, r.ptr, ours.ptr, theirs.ptr, copts) + ret := C.git_merge_commits(&idx.ptr, r.ptr, ours.cast_ptr, theirs.cast_ptr, copts) if ret < 0 { return nil, MakeGitError(ret) } @@ -198,7 +198,7 @@ func (r *Repository) MergeTrees(ancestor *Tree, ours *Tree, theirs *Tree, option idx := &Index{} - ret := C.git_merge_trees(&idx.ptr, r.ptr, ancestor.ptr, ours.ptr, theirs.ptr, copts) + ret := C.git_merge_trees(&idx.ptr, r.ptr, ancestor.cast_ptr, ours.cast_ptr, theirs.cast_ptr, copts) if ret < 0 { return nil, MakeGitError(ret) } -- 2.45.2