From 65ff78deadeeea5e801ccbaa428f9721c1f3177b Mon Sep 17 00:00:00 2001 From: Quinn Slack Date: Mon, 17 Nov 2014 19:44:21 -0800 Subject: [PATCH 01/41] Add BlameFile func and options for git-blaming files --- blame.go | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++ blame_test.go | 63 +++++++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 blame.go create mode 100644 blame_test.go diff --git a/blame.go b/blame.go new file mode 100644 index 0000000..713f83e --- /dev/null +++ b/blame.go @@ -0,0 +1,138 @@ +package git + +/* +#include +#include +*/ +import "C" +import "runtime" + +type BlameOptions struct { + Flags BlameOptionsFlag + MinMatchCharacters uint16 + NewestCommit *Oid + OldestCommit *Oid + MinLine uint32 + MaxLine uint32 +} + +func DefaultBlameOptions() (BlameOptions, error) { + opts := C.git_blame_options{} + ecode := C.git_blame_init_options(&opts, C.GIT_BLAME_OPTIONS_VERSION) + if ecode < 0 { + return BlameOptions{}, MakeGitError(ecode) + } + + return BlameOptions{ + Flags: BlameOptionsFlag(opts.flags), + MinMatchCharacters: uint16(opts.min_match_characters), + NewestCommit: newOidFromC(&opts.newest_commit), + OldestCommit: newOidFromC(&opts.oldest_commit), + MinLine: uint32(opts.min_line), + MaxLine: uint32(opts.max_line), + }, nil +} + +type BlameOptionsFlag uint32 + +const ( + BlameNormal BlameOptionsFlag = C.GIT_BLAME_NORMAL + BlameTrackCopiesSameFile BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_FILE + BlameTrackCopiesSameCommitMoves BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES + BlameTrackCopiesSameCommitCopies BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES + BlameTrackCopiesAnyCommitCopies BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES + BlameFirstParent BlameOptionsFlag = C.GIT_BLAME_FIRST_PARENT +) + +func (v *Repository) BlameFile(path string, opts *BlameOptions) (*Blame, error) { + var blamePtr *C.git_blame + + var copts *C.git_blame_options + if opts != nil { + copts = &C.git_blame_options{ + version: C.GIT_BLAME_OPTIONS_VERSION, + flags: C.uint32_t(opts.Flags), + min_match_characters: C.uint16_t(opts.MinMatchCharacters), + min_line: C.uint32_t(opts.MinLine), + max_line: C.uint32_t(opts.MaxLine), + } + if opts.NewestCommit != nil { + copts.newest_commit = *opts.NewestCommit.toC() + } + if opts.OldestCommit != nil { + copts.oldest_commit = *opts.OldestCommit.toC() + } + } + + ecode := C.git_blame_file(&blamePtr, v.ptr, C.CString(path), copts) + if ecode < 0 { + return nil, MakeGitError(ecode) + } + + return newBlameFromC(blamePtr), nil +} + +type Blame struct { + ptr *C.git_blame +} + +func (blame *Blame) HunkCount() int { + return int(C.git_blame_get_hunk_count(blame.ptr)) +} + +func (blame *Blame) HunkByIndex(index int) (BlameHunk, error) { + ptr := C.git_blame_get_hunk_byindex(blame.ptr, C.uint32_t(index)) + if ptr == nil { + return BlameHunk{}, ErrInvalid + } + return blameHunkFromC(ptr), nil +} + +func newBlameFromC(ptr *C.git_blame) *Blame { + if ptr == nil { + return nil + } + + blame := &Blame{ + ptr: ptr, + } + + runtime.SetFinalizer(blame, (*Blame).Free) + return blame +} + +func (blame *Blame) Free() error { + if blame.ptr == nil { + return ErrInvalid + } + runtime.SetFinalizer(blame, nil) + C.git_blame_free(blame.ptr) + blame.ptr = nil + return nil +} + +type BlameHunk struct { + LinesInHunk uint16 + FinalCommitId *Oid + FinalStartLineNumber uint16 + FinalSignature *Signature + OrigCommitId *Oid + OrigPath string + OrigStartLineNumber uint16 + OrigSignature *Signature + Boundary bool +} + +func blameHunkFromC(hunk *C.git_blame_hunk) BlameHunk { + return BlameHunk{ + LinesInHunk: uint16(hunk.lines_in_hunk), + FinalCommitId: newOidFromC(&hunk.final_commit_id), + FinalStartLineNumber: uint16(hunk.final_start_line_number), + FinalSignature: newSignatureFromC(hunk.final_signature), + OrigCommitId: newOidFromC(&hunk.orig_commit_id), + OrigPath: C.GoString(hunk.orig_path), + OrigStartLineNumber: uint16(hunk.orig_start_line_number), + OrigSignature: newSignatureFromC(hunk.orig_signature), + Boundary: hunk.boundary == 1, + } +} diff --git a/blame_test.go b/blame_test.go new file mode 100644 index 0000000..7c730a0 --- /dev/null +++ b/blame_test.go @@ -0,0 +1,63 @@ +package git + +import ( + "os" + "reflect" + "testing" +) + +func TestBlame(t *testing.T) { + repo := createTestRepo(t) + defer repo.Free() + defer os.RemoveAll(repo.Workdir()) + + commitId1, _ := seedTestRepo(t, repo) + commitId2, _ := updateReadme(t, repo, "foo\nbar\nbaz\n") + + opts := BlameOptions{ + NewestCommit: commitId2, + OldestCommit: nil, + MinLine: 1, + MaxLine: 3, + } + blame, err := repo.BlameFile("README", &opts) + checkFatal(t, err) + defer blame.Free() + if blame.HunkCount() != 2 { + t.Errorf("got hunk count %d, want 2", blame.HunkCount()) + } + + hunk1, err := blame.HunkByIndex(0) + checkFatal(t, err) + checkHunk(t, hunk1, BlameHunk{ + LinesInHunk: 1, + FinalCommitId: commitId1, + FinalStartLineNumber: 1, + OrigCommitId: commitId1, + OrigPath: "README", + OrigStartLineNumber: 1, + Boundary: true, + }) + + hunk2, err := blame.HunkByIndex(1) + checkFatal(t, err) + checkHunk(t, hunk2, BlameHunk{ + LinesInHunk: 2, + FinalCommitId: commitId2, + FinalStartLineNumber: 2, + OrigCommitId: commitId2, + OrigPath: "README", + OrigStartLineNumber: 2, + Boundary: false, + }) +} + +func checkHunk(t *testing.T, hunk, want BlameHunk) { + hunk.FinalSignature = nil + want.FinalSignature = nil + hunk.OrigSignature = nil + want.OrigSignature = nil + if !reflect.DeepEqual(hunk, want) { + t.Fatalf("got hunk %+v, want %+v", hunk, want) + } +} -- 2.45.2 From 609a9a3cdf9e99d7d11e271a8838f9db6b40573a Mon Sep 17 00:00:00 2001 From: Quinn Slack Date: Tue, 18 Nov 2014 04:55:10 -0800 Subject: [PATCH 02/41] omit unnecessary #include --- blame.go | 1 - 1 file changed, 1 deletion(-) diff --git a/blame.go b/blame.go index 713f83e..5384038 100644 --- a/blame.go +++ b/blame.go @@ -2,7 +2,6 @@ package git /* #include -#include */ import "C" import "runtime" -- 2.45.2 From 8b39eb795340dff00f16f93d6e9d154fabc98b14 Mon Sep 17 00:00:00 2001 From: Quinn Slack Date: Tue, 18 Nov 2014 04:57:26 -0800 Subject: [PATCH 03/41] lock OS thread when MakeGitError might be called --- blame.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/blame.go b/blame.go index 5384038..55100a4 100644 --- a/blame.go +++ b/blame.go @@ -16,6 +16,9 @@ type BlameOptions struct { } func DefaultBlameOptions() (BlameOptions, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + opts := C.git_blame_options{} ecode := C.git_blame_init_options(&opts, C.GIT_BLAME_OPTIONS_VERSION) if ecode < 0 { @@ -63,6 +66,9 @@ func (v *Repository) BlameFile(path string, opts *BlameOptions) (*Blame, error) } } + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ecode := C.git_blame_file(&blamePtr, v.ptr, C.CString(path), copts) if ecode < 0 { return nil, MakeGitError(ecode) -- 2.45.2 From ebb657ce2f8fdeac32dda6fb88d3a20548e034a7 Mon Sep 17 00:00:00 2001 From: Quinn Slack Date: Tue, 18 Nov 2014 04:58:23 -0800 Subject: [PATCH 04/41] free C string --- blame.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/blame.go b/blame.go index 55100a4..cd5665a 100644 --- a/blame.go +++ b/blame.go @@ -4,7 +4,10 @@ package git #include */ import "C" -import "runtime" +import ( + "runtime" + "unsafe" +) type BlameOptions struct { Flags BlameOptionsFlag @@ -66,10 +69,13 @@ func (v *Repository) BlameFile(path string, opts *BlameOptions) (*Blame, error) } } + cpath := C.CString(path) + defer C.free(unsafe.Pointer(cpath)) + runtime.LockOSThread() defer runtime.UnlockOSThread() - ecode := C.git_blame_file(&blamePtr, v.ptr, C.CString(path), copts) + ecode := C.git_blame_file(&blamePtr, v.ptr, cpath, copts) if ecode < 0 { return nil, MakeGitError(ecode) } -- 2.45.2 From 3268bdbeb2314d696ea608b439c3c98927a9db1a Mon Sep 17 00:00:00 2001 From: Quinn Slack Date: Tue, 18 Nov 2014 05:06:03 -0800 Subject: [PATCH 05/41] add (*Blame).HunkByLine (git_blame_get_hunk_byline) and test --- blame.go | 8 ++++++++ blame_test.go | 33 ++++++++++++++++++++++----------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/blame.go b/blame.go index cd5665a..c24c934 100644 --- a/blame.go +++ b/blame.go @@ -99,6 +99,14 @@ func (blame *Blame) HunkByIndex(index int) (BlameHunk, error) { return blameHunkFromC(ptr), nil } +func (blame *Blame) HunkByLine(lineno int) (BlameHunk, error) { + ptr := C.git_blame_get_hunk_byline(blame.ptr, C.uint32_t(lineno)) + if ptr == nil { + return BlameHunk{}, ErrInvalid + } + return blameHunkFromC(ptr), nil +} + func newBlameFromC(ptr *C.git_blame) *Blame { if ptr == nil { return nil diff --git a/blame_test.go b/blame_test.go index 7c730a0..1785042 100644 --- a/blame_test.go +++ b/blame_test.go @@ -27,9 +27,7 @@ func TestBlame(t *testing.T) { t.Errorf("got hunk count %d, want 2", blame.HunkCount()) } - hunk1, err := blame.HunkByIndex(0) - checkFatal(t, err) - checkHunk(t, hunk1, BlameHunk{ + wantHunk1 := BlameHunk{ LinesInHunk: 1, FinalCommitId: commitId1, FinalStartLineNumber: 1, @@ -37,11 +35,8 @@ func TestBlame(t *testing.T) { OrigPath: "README", OrigStartLineNumber: 1, Boundary: true, - }) - - hunk2, err := blame.HunkByIndex(1) - checkFatal(t, err) - checkHunk(t, hunk2, BlameHunk{ + } + wantHunk2 := BlameHunk{ LinesInHunk: 2, FinalCommitId: commitId2, FinalStartLineNumber: 2, @@ -49,15 +44,31 @@ func TestBlame(t *testing.T) { OrigPath: "README", OrigStartLineNumber: 2, Boundary: false, - }) + } + + hunk1, err := blame.HunkByIndex(0) + checkFatal(t, err) + checkHunk(t, "index 0", hunk1, wantHunk1) + + hunk2, err := blame.HunkByIndex(1) + checkFatal(t, err) + checkHunk(t, "index 1", hunk2, wantHunk2) + + hunkLine1, err := blame.HunkByLine(1) + checkFatal(t, err) + checkHunk(t, "line 1", hunkLine1, wantHunk1) + + hunkLine2, err := blame.HunkByLine(3) + checkFatal(t, err) + checkHunk(t, "line 2", hunkLine2, wantHunk2) } -func checkHunk(t *testing.T, hunk, want BlameHunk) { +func checkHunk(t *testing.T, label string, hunk, want BlameHunk) { hunk.FinalSignature = nil want.FinalSignature = nil hunk.OrigSignature = nil want.OrigSignature = nil if !reflect.DeepEqual(hunk, want) { - t.Fatalf("got hunk %+v, want %+v", hunk, want) + t.Fatalf("%s: got hunk %+v, want %+v", label, hunk, want) } } -- 2.45.2 From a4ae68783d92a46b8d8bbd7e2aab3700bde10597 Mon Sep 17 00:00:00 2001 From: Jochen Hilgers Date: Wed, 26 Nov 2014 17:22:15 +0100 Subject: [PATCH 06/41] Integrated git_diff_find_similar --- diff.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ diff_test.go | 75 +++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 140 insertions(+), 6 deletions(-) diff --git a/diff.go b/diff.go index f762a56..661a9a7 100644 --- a/diff.go +++ b/diff.go @@ -164,6 +164,29 @@ func (diff *Diff) Free() error { return nil } +func (diff *Diff) FindSimilar(opts *DiffFindOptions) error { + + var copts *C.git_diff_find_options + if opts != nil { + copts = &C.git_diff_find_options{ + version: C.GIT_DIFF_FIND_OPTIONS_VERSION, + flags: C.uint32_t(opts.Flags), + rename_threshold: C.uint16_t(opts.RenameThreshold), + copy_threshold: C.uint16_t(opts.CopyThreshold), + rename_from_rewrite_threshold: C.uint16_t(opts.RenameFromRewriteThreshold), + break_rewrite_threshold: C.uint16_t(opts.BreakRewriteThreshold), + rename_limit: C.size_t(opts.RenameLimit), + } + } + + ecode := C.git_diff_find_similar(diff.ptr, copts) + if ecode < 0 { + return MakeGitError(ecode) + } + + return nil +} + type diffForEachData struct { FileCallback DiffForEachFileCallback HunkCallback DiffForEachHunkCallback @@ -341,6 +364,54 @@ func DefaultDiffOptions() (DiffOptions, error) { }, nil } +type DiffFindOptionsFlag int + +const ( + DiffFindByConfig DiffFindOptionsFlag = C.GIT_DIFF_FIND_BY_CONFIG + DiffFindRenames DiffFindOptionsFlag = C.GIT_DIFF_FIND_RENAMES + DiffFindRenamesFromRewrites DiffFindOptionsFlag = C.GIT_DIFF_FIND_RENAMES_FROM_REWRITES + DiffFindCopies DiffFindOptionsFlag = C.GIT_DIFF_FIND_COPIES + DiffFindCopiesFromUnmodified DiffFindOptionsFlag = C.GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED + DiffFindRewrites DiffFindOptionsFlag = C.GIT_DIFF_FIND_REWRITES + DiffFindBreakRewrites DiffFindOptionsFlag = C.GIT_DIFF_BREAK_REWRITES + DiffFindAndBreakRewrites DiffFindOptionsFlag = C.GIT_DIFF_FIND_AND_BREAK_REWRITES + DiffFindForUntracked DiffFindOptionsFlag = C.GIT_DIFF_FIND_FOR_UNTRACKED + DiffFindAll DiffFindOptionsFlag = C.GIT_DIFF_FIND_ALL + DiffFindIgnoreLeadingWhitespace DiffFindOptionsFlag = C.GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE + DiffFindIgnoreWhitespace DiffFindOptionsFlag = C.GIT_DIFF_FIND_IGNORE_WHITESPACE + DiffFindDontIgnoreWhitespace DiffFindOptionsFlag = C.GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE + DiffFindExactMatchOnly DiffFindOptionsFlag = C.GIT_DIFF_FIND_EXACT_MATCH_ONLY + DiffFindBreakRewritesForRenamesOnly DiffFindOptionsFlag = C.GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY + DiffFindRemoveUnmodified DiffFindOptionsFlag = C.GIT_DIFF_FIND_REMOVE_UNMODIFIED +) + +//TODO implement git_diff_similarity_metric +type DiffFindOptions struct { + Flags DiffFindOptionsFlag + RenameThreshold uint16 + CopyThreshold uint16 + RenameFromRewriteThreshold uint16 + BreakRewriteThreshold uint16 + RenameLimit uint +} + +func DefaultDiffFindOptions() (DiffFindOptions, error) { + opts := C.git_diff_find_options{} + ecode := C.git_diff_find_init_options(&opts, C.GIT_DIFF_FIND_OPTIONS_VERSION) + if ecode < 0 { + return DiffFindOptions{}, MakeGitError(ecode) + } + + return DiffFindOptions{ + Flags: DiffFindOptionsFlag(opts.flags), + RenameThreshold: uint16(opts.rename_threshold), + CopyThreshold: uint16(opts.copy_threshold), + RenameFromRewriteThreshold: uint16(opts.rename_from_rewrite_threshold), + BreakRewriteThreshold: uint16(opts.break_rewrite_threshold), + RenameLimit: uint(opts.rename_limit), + }, nil +} + var ( ErrDeltaSkip = errors.New("Skip delta") ) diff --git a/diff_test.go b/diff_test.go index b688294..84d72db 100644 --- a/diff_test.go +++ b/diff_test.go @@ -6,20 +6,68 @@ import ( "testing" ) -func TestDiffTreeToTree(t *testing.T) { +func TestFindSimilar(t *testing.T) { repo := createTestRepo(t) defer repo.Free() defer os.RemoveAll(repo.Workdir()) - _, originalTreeId := seedTestRepo(t, repo) - originalTree, err := repo.LookupTree(originalTreeId) + originalTree, newTree := createTestTrees(t, repo) + diffOpt, _ := DefaultDiffOptions() + + diff, err := repo.DiffTreeToTree(originalTree, newTree, &diffOpt) + checkFatal(t, err) + if diff == nil { + t.Fatal("no diff returned") + } + + findOpts, err := DefaultDiffFindOptions() + checkFatal(t, err) + findOpts.Flags = DiffFindBreakRewrites + + err = diff.FindSimilar(&findOpts) checkFatal(t, err) - _, newTreeId := updateReadme(t, repo, "file changed\n") + numDiffs := 0 + numAdded := 0 + numDeleted := 0 - newTree, err := repo.LookupTree(newTreeId) - checkFatal(t, err) + err = diff.ForEach(func(file DiffDelta, progress float64) (DiffForEachHunkCallback, error) { + numDiffs++ + + switch file.Status { + case DeltaAdded: + numAdded++ + case DeltaDeleted: + numDeleted++ + } + + return func(hunk DiffHunk) (DiffForEachLineCallback, error) { + return func(line DiffLine) error { + return nil + }, nil + }, nil + }, DiffDetailLines) + + if numDiffs != 2 { + t.Fatal("Incorrect number of files in diff") + } + if numAdded != 1 { + t.Fatal("Incorrect number of new files in diff") + } + if numDeleted != 1 { + t.Fatal("Incorrect number of deleted files in diff") + } + +} + +func TestDiffTreeToTree(t *testing.T) { + + repo := createTestRepo(t) + defer repo.Free() + defer os.RemoveAll(repo.Workdir()) + + originalTree, newTree := createTestTrees(t, repo) callbackInvoked := false opts := DiffOptions{ @@ -94,3 +142,18 @@ func TestDiffTreeToTree(t *testing.T) { } } + +func createTestTrees(t *testing.T, repo *Repository) (originalTree *Tree, newTree *Tree) { + var err error + _, originalTreeId := seedTestRepo(t, repo) + originalTree, err = repo.LookupTree(originalTreeId) + + checkFatal(t, err) + + _, newTreeId := updateReadme(t, repo, "file changed\n") + + newTree, err = repo.LookupTree(newTreeId) + checkFatal(t, err) + + return originalTree, newTree +} -- 2.45.2 From 5b3bc2dd1f4eb0b9d6eaf1db27f8d6ea89f2190f Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Wed, 26 Nov 2014 22:05:21 +0100 Subject: [PATCH 07/41] Add (*Repository).DeleteRemote --- remote.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/remote.go b/remote.go index a2288fa..ed27c4c 100644 --- a/remote.go +++ b/remote.go @@ -278,6 +278,20 @@ func (repo *Repository) CreateRemote(name string, url string) (*Remote, error) { return remote, nil } +func (repo *Repository) DeleteRemote(name string) error { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_delete(repo.ptr, cname) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + func (repo *Repository) CreateRemoteWithFetchspec(name string, url string, fetch string) (*Remote, error) { remote := &Remote{} -- 2.45.2 From 1d759e3697c9236916ffb782b3721a96760bfc07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 3 Dec 2014 01:50:37 +0100 Subject: [PATCH 08/41] Update to master --- README.md | 5 +++++ diff.go | 16 +++++++-------- git.go | 9 +++++++- merge.go | 56 +++++++++++++++++++++++++------------------------- merge_test.go | 8 ++++---- remote.go | 4 ++-- vendor/libgit2 | 2 +- 7 files changed, 56 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index d10c46e..b0af2f9 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,11 @@ Run `go get -d github.com/libgit2/git2go` to download the code and go to your `$ will compile libgit2 and run `go install` such that it's statically linked to the git2go package. +Paralellism and network operations +---------------------------------- + +libgit2 uses OpenSSL and LibSSH2 for performing encrypted network connections. For now, git2go asks libgit2 to set locking for OpenSSL. This makes HTTPS connections thread-safe, but it is fragile and will likely stop doing it soon. This may also make SSH connections thread-safe if your copy of libssh2 is linked against OpenSSL. Check libgit2's `THREADSAFE.md` for more information. + Running the tests ----------------- diff --git a/diff.go b/diff.go index f762a56..d89a2f9 100644 --- a/diff.go +++ b/diff.go @@ -313,8 +313,8 @@ type DiffOptions struct { Pathspec []string NotifyCallback DiffNotifyCallback - ContextLines uint16 - InterhunkLines uint16 + ContextLines uint32 + InterhunkLines uint32 IdAbbrev uint16 MaxSize int @@ -334,8 +334,8 @@ func DefaultDiffOptions() (DiffOptions, error) { Flags: DiffOptionsFlag(opts.flags), IgnoreSubmodules: SubmoduleIgnore(opts.ignore_submodules), Pathspec: makeStringsFromCStrings(opts.pathspec.strings, int(opts.pathspec.count)), - ContextLines: uint16(opts.context_lines), - InterhunkLines: uint16(opts.interhunk_lines), + ContextLines: uint32(opts.context_lines), + InterhunkLines: uint32(opts.interhunk_lines), IdAbbrev: uint16(opts.id_abbrev), MaxSize: int(opts.max_size), }, nil @@ -404,8 +404,8 @@ func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree, opts *DiffOptions) ( flags: C.uint32_t(opts.Flags), ignore_submodules: C.git_submodule_ignore_t(opts.IgnoreSubmodules), pathspec: cpathspec, - context_lines: C.uint16_t(opts.ContextLines), - interhunk_lines: C.uint16_t(opts.InterhunkLines), + context_lines: C.uint32_t(opts.ContextLines), + interhunk_lines: C.uint32_t(opts.InterhunkLines), id_abbrev: C.uint16_t(opts.IdAbbrev), max_size: C.git_off_t(opts.MaxSize), } @@ -453,8 +453,8 @@ func (v *Repository) DiffTreeToWorkdir(oldTree *Tree, opts *DiffOptions) (*Diff, flags: C.uint32_t(opts.Flags), ignore_submodules: C.git_submodule_ignore_t(opts.IgnoreSubmodules), pathspec: cpathspec, - context_lines: C.uint16_t(opts.ContextLines), - interhunk_lines: C.uint16_t(opts.InterhunkLines), + context_lines: C.uint32_t(opts.ContextLines), + interhunk_lines: C.uint32_t(opts.InterhunkLines), id_abbrev: C.uint16_t(opts.IdAbbrev), max_size: C.git_off_t(opts.MaxSize), } diff --git a/git.go b/git.go index adfa3b0..72d8d03 100644 --- a/git.go +++ b/git.go @@ -93,7 +93,14 @@ var ( ) func init() { - C.git_threads_init() + C.git_libgit2_init() + + // This is not something we should be doing, as we may be + // stomping all over someone else's setup. The user should do + // this themselves or use some binding/wrapper which does it + // in such a way that they can be sure they're the only ones + // setting it up. + C.git_openssl_set_locking() } // Oid represents the id for a Git object. diff --git a/merge.go b/merge.go index 93ac71b..83a682c 100644 --- a/merge.go +++ b/merge.go @@ -4,9 +4,9 @@ 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); +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); */ import "C" @@ -15,23 +15,23 @@ import ( "unsafe" ) -type MergeHead struct { - ptr *C.git_merge_head +type AnnotatedCommit struct { + ptr *C.git_annotated_commit } -func newMergeHeadFromC(c *C.git_merge_head) *MergeHead { - mh := &MergeHead{ptr: c} - runtime.SetFinalizer(mh, (*MergeHead).Free) +func newAnnotatedCommitFromC(c *C.git_annotated_commit) *AnnotatedCommit { + mh := &AnnotatedCommit{ptr: c} + runtime.SetFinalizer(mh, (*AnnotatedCommit).Free) return mh } -func (mh *MergeHead) Free() { +func (mh *AnnotatedCommit) Free() { runtime.SetFinalizer(mh, nil) - C.git_merge_head_free(mh.ptr) + C.git_annotated_commit_free(mh.ptr) } -func (r *Repository) MergeHeadFromFetchHead(branchName string, remoteURL string, oid *Oid) (*MergeHead, error) { - mh := &MergeHead{} +func (r *Repository) AnnotatedCommitFromFetchHead(branchName string, remoteURL string, oid *Oid) (*AnnotatedCommit, error) { + mh := &AnnotatedCommit{} cbranchName := C.CString(branchName) defer C.free(unsafe.Pointer(cbranchName)) @@ -39,33 +39,33 @@ func (r *Repository) MergeHeadFromFetchHead(branchName string, remoteURL string, 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()) + ret := C.git_annotated_commit_from_fetchhead(&mh.ptr, r.ptr, cbranchName, cremoteURL, oid.toC()) if ret < 0 { return nil, MakeGitError(ret) } - runtime.SetFinalizer(mh, (*MergeHead).Free) + runtime.SetFinalizer(mh, (*AnnotatedCommit).Free) return mh, nil } -func (r *Repository) MergeHeadFromId(oid *Oid) (*MergeHead, error) { - mh := &MergeHead{} +func (r *Repository) LookupAnnotatedCommit(oid *Oid) (*AnnotatedCommit, error) { + mh := &AnnotatedCommit{} - ret := C.git_merge_head_from_id(&mh.ptr, r.ptr, oid.toC()) + ret := C.git_annotated_commit_lookup(&mh.ptr, r.ptr, oid.toC()) if ret < 0 { return nil, MakeGitError(ret) } - runtime.SetFinalizer(mh, (*MergeHead).Free) + runtime.SetFinalizer(mh, (*AnnotatedCommit).Free) return mh, nil } -func (r *Repository) MergeHeadFromRef(ref *Reference) (*MergeHead, error) { - mh := &MergeHead{} +func (r *Repository) AnnotatedCommitFromRef(ref *Reference) (*AnnotatedCommit, error) { + mh := &AnnotatedCommit{} - ret := C.git_merge_head_from_ref(&mh.ptr, r.ptr, ref.ptr) + ret := C.git_annotated_commit_from_ref(&mh.ptr, r.ptr, ref.ptr) if ret < 0 { return nil, MakeGitError(ret) } - runtime.SetFinalizer(mh, (*MergeHead).Free) + runtime.SetFinalizer(mh, (*AnnotatedCommit).Free) return mh, nil } @@ -127,19 +127,19 @@ const ( MergeFileFavorUnion MergeFileFavor = C.GIT_MERGE_FILE_FAVOR_UNION ) -func (r *Repository) Merge(theirHeads []*MergeHead, mergeOptions *MergeOptions, checkoutOptions *CheckoutOpts) error { +func (r *Repository) Merge(theirHeads []*AnnotatedCommit, mergeOptions *MergeOptions, checkoutOptions *CheckoutOpts) error { runtime.LockOSThread() defer runtime.UnlockOSThread() cMergeOpts := mergeOptions.toC() cCheckoutOpts := checkoutOptions.toC() - gmerge_head_array := make([]*C.git_merge_head, len(theirHeads)) + 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_merge_head)(ptr), C.size_t(len(theirHeads)), cMergeOpts, cCheckoutOpts) + err := C.git_merge(r.ptr, (**C.git_annotated_commit)(ptr), C.size_t(len(theirHeads)), cMergeOpts, cCheckoutOpts) if err < 0 { return MakeGitError(err) } @@ -164,18 +164,18 @@ const ( MergePreferenceFastForwardOnly MergePreference = C.GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY ) -func (r *Repository) MergeAnalysis(theirHeads []*MergeHead) (MergeAnalysis, MergePreference, error) { +func (r *Repository) MergeAnalysis(theirHeads []*AnnotatedCommit) (MergeAnalysis, MergePreference, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() - gmerge_head_array := make([]*C.git_merge_head, len(theirHeads)) + 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_merge_head)(ptr), C.size_t(len(theirHeads))) + err := C.git_merge_analysis(&analysis, &preference, r.ptr, (**C.git_annotated_commit)(ptr), C.size_t(len(theirHeads))) if err < 0 { return MergeAnalysisNone, MergePreferenceNone, MakeGitError(err) } diff --git a/merge_test.go b/merge_test.go index 7e884c0..1eba806 100644 --- a/merge_test.go +++ b/merge_test.go @@ -13,10 +13,10 @@ func TestMergeWithSelf(t *testing.T) { master, err := repo.LookupReference("refs/heads/master") checkFatal(t, err) - mergeHead, err := repo.MergeHeadFromRef(master) + mergeHead, err := repo.AnnotatedCommitFromRef(master) checkFatal(t, err) - mergeHeads := make([]*MergeHead, 1) + mergeHeads := make([]*AnnotatedCommit, 1) mergeHeads[0] = mergeHead err = repo.Merge(mergeHeads, nil, nil) checkFatal(t, err) @@ -30,10 +30,10 @@ func TestMergeAnalysisWithSelf(t *testing.T) { master, err := repo.LookupReference("refs/heads/master") checkFatal(t, err) - mergeHead, err := repo.MergeHeadFromRef(master) + mergeHead, err := repo.AnnotatedCommitFromRef(master) checkFatal(t, err) - mergeHeads := make([]*MergeHead, 1) + mergeHeads := make([]*AnnotatedCommit, 1) mergeHeads[0] = mergeHead a, _, err := repo.MergeAnalysis(mergeHeads) checkFatal(t, err) diff --git a/remote.go b/remote.go index a2288fa..afa3808 100644 --- a/remote.go +++ b/remote.go @@ -318,7 +318,7 @@ func (repo *Repository) CreateAnonymousRemote(url, fetch string) (*Remote, error return remote, nil } -func (repo *Repository) LoadRemote(name string) (*Remote, error) { +func (repo *Repository) LookupRemote(name string) (*Remote, error) { remote := &Remote{} cname := C.CString(name) @@ -327,7 +327,7 @@ func (repo *Repository) LoadRemote(name string) (*Remote, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_load(&remote.ptr, repo.ptr, cname) + ret := C.git_remote_lookup(&remote.ptr, repo.ptr, cname) if ret < 0 { return nil, MakeGitError(ret) } diff --git a/vendor/libgit2 b/vendor/libgit2 index d09458f..169497d 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit d09458f3e9f24afa0689ce90b7d4191872372634 +Subproject commit 169497d1e7c238d2925577d1af3dc03e9a507cd3 -- 2.45.2 From 9b0ba12c34fc21eb82db15f4db237a6c2e26d220 Mon Sep 17 00:00:00 2001 From: Steven Wilkin Date: Thu, 4 Dec 2014 19:51:41 +0000 Subject: [PATCH 09/41] Add build status to README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d10c46e..b1be00b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ git2go ====== -[![GoDoc](https://godoc.org/github.com/libgit2/git2go?status.svg)](http://godoc.org/github.com/libgit2/git2go) +[![GoDoc](https://godoc.org/github.com/libgit2/git2go?status.svg)](http://godoc.org/github.com/libgit2/git2go) [![Build Status](https://travis-ci.org/libgit2/git2go.svg?branch=master)](https://travis-ci.org/libgit2/git2go) Go bindings for [libgit2](http://libgit2.github.com/). The master branch follows the latest libgit2 release. -- 2.45.2 From 8c631b0c25c8de616afa2fd89378299c9d9a1439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 6 Dec 2014 02:44:57 +0100 Subject: [PATCH 10/41] Add missing thread locking --- config.go | 7 +++++++ diff.go | 13 +++++++++++++ merge.go | 13 +++++++++++++ odb.go | 14 ++++++++++++++ packbuilder.go | 3 +++ patch.go | 4 ++++ refdb.go | 6 ++++++ reference.go | 4 ++++ remote.go | 7 +++++++ repository.go | 6 ++++++ 10 files changed, 77 insertions(+) diff --git a/config.go b/config.go index 5bcb0f8..bbf03e2 100644 --- a/config.go +++ b/config.go @@ -235,6 +235,10 @@ func (c *Config) SetInt32(name string, value int32) (err error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_config_set_int32(c.ptr, cname, C.int32_t(value)) if ret < 0 { return MakeGitError(ret) @@ -350,6 +354,9 @@ type ConfigIterator struct { func (iter *ConfigIterator) Next() (*ConfigEntry, error) { var centry *C.git_config_entry + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_config_next(¢ry, iter.ptr) if ret < 0 { return nil, MakeGitError(ret) diff --git a/diff.go b/diff.go index 9ce275c..d7d8118 100644 --- a/diff.go +++ b/diff.go @@ -287,6 +287,9 @@ func (diff *Diff) Patch(deltaIndex int) (*Patch, error) { } var patchPtr *C.git_patch + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ecode := C.git_patch_from_diff(&patchPtr, diff.ptr, C.size_t(deltaIndex)) if ecode < 0 { return nil, MakeGitError(ecode) @@ -348,6 +351,10 @@ type DiffOptions struct { func DefaultDiffOptions() (DiffOptions, error) { opts := C.git_diff_options{} + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ecode := C.git_diff_init_options(&opts, C.GIT_DIFF_OPTIONS_VERSION) if ecode < 0 { return DiffOptions{}, MakeGitError(ecode) @@ -487,6 +494,9 @@ func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree, opts *DiffOptions) ( } } + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ecode := C.git_diff_tree_to_tree(&diffPtr, v.ptr, oldPtr, newPtr, copts) if ecode < 0 { return nil, MakeGitError(ecode) @@ -536,6 +546,9 @@ func (v *Repository) DiffTreeToWorkdir(oldTree *Tree, opts *DiffOptions) (*Diff, } } + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ecode := C.git_diff_tree_to_workdir(&diffPtr, v.ptr, oldPtr, copts) if ecode < 0 { return nil, MakeGitError(ecode) diff --git a/merge.go b/merge.go index 83a682c..285c9c5 100644 --- a/merge.go +++ b/merge.go @@ -39,6 +39,9 @@ func (r *Repository) AnnotatedCommitFromFetchHead(branchName string, remoteURL s cremoteURL := C.CString(remoteURL) defer C.free(unsafe.Pointer(cremoteURL)) + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_annotated_commit_from_fetchhead(&mh.ptr, r.ptr, cbranchName, cremoteURL, oid.toC()) if ret < 0 { return nil, MakeGitError(ret) @@ -50,6 +53,9 @@ func (r *Repository) AnnotatedCommitFromFetchHead(branchName string, remoteURL s func (r *Repository) LookupAnnotatedCommit(oid *Oid) (*AnnotatedCommit, error) { mh := &AnnotatedCommit{} + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_annotated_commit_lookup(&mh.ptr, r.ptr, oid.toC()) if ret < 0 { return nil, MakeGitError(ret) @@ -61,6 +67,9 @@ func (r *Repository) LookupAnnotatedCommit(oid *Oid) (*AnnotatedCommit, error) { func (r *Repository) AnnotatedCommitFromRef(ref *Reference) (*AnnotatedCommit, error) { mh := &AnnotatedCommit{} + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_annotated_commit_from_ref(&mh.ptr, r.ptr, ref.ptr) if ret < 0 { return nil, MakeGitError(ret) @@ -98,6 +107,10 @@ func mergeOptionsFromC(opts *C.git_merge_options) MergeOptions { 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) diff --git a/odb.go b/odb.go index 7076e20..9ea151b 100644 --- a/odb.go +++ b/odb.go @@ -25,6 +25,9 @@ type OdbBackend struct { func NewOdb() (odb *Odb, err error) { odb = new(Odb) + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_odb_new(&odb.ptr) if ret < 0 { return nil, MakeGitError(ret) @@ -40,6 +43,10 @@ func NewOdbBackendFromC(ptr *C.git_odb_backend) (backend *OdbBackend) { } func (v *Odb) AddBackend(backend *OdbBackend, priority int) (err error) { + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_odb_add_backend(v.ptr, backend.ptr, C.int(priority)) if ret < 0 { backend.Free() @@ -110,6 +117,9 @@ func (v *Odb) ForEach(callback OdbForEachCallback) error { err: nil, } + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C._go_git_odb_foreach(v.ptr, unsafe.Pointer(&data)) if ret == C.GIT_EUSER { return data.err @@ -140,6 +150,10 @@ func (v *Odb) Hash(data []byte, otype ObjectType) (oid *Oid, err error) { // contents of the object. func (v *Odb) NewReadStream(id *Oid) (*OdbReadStream, error) { stream := new(OdbReadStream) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_odb_open_rstream(&stream.ptr, v.ptr, id.toC()) if ret < 0 { return nil, MakeGitError(ret) diff --git a/packbuilder.go b/packbuilder.go index 666f5c4..24d2f6d 100644 --- a/packbuilder.go +++ b/packbuilder.go @@ -132,6 +132,9 @@ func (pb *Packbuilder) ForEach(callback PackbuilderForeachCallback) error { err: nil, } + runtime.LockOSThread() + defer runtime.UnlockOSThread() + err := C._go_git_packbuilder_foreach(pb.ptr, unsafe.Pointer(&data)) if err == C.GIT_EUSER { return data.err diff --git a/patch.go b/patch.go index 0665501..0fe1c04 100644 --- a/patch.go +++ b/patch.go @@ -40,6 +40,10 @@ func (patch *Patch) String() (string, error) { return "", ErrInvalid } var buf C.git_buf + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ecode := C.git_patch_to_buf(&buf, patch.ptr) if ecode < 0 { return "", MakeGitError(ecode) diff --git a/refdb.go b/refdb.go index 46fbb63..36e2f0a 100644 --- a/refdb.go +++ b/refdb.go @@ -23,6 +23,9 @@ type RefdbBackend struct { func (v *Repository) NewRefdb() (refdb *Refdb, err error) { refdb = new(Refdb) + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_refdb_new(&refdb.ptr, v.ptr) if ret < 0 { return nil, MakeGitError(ret) @@ -38,6 +41,9 @@ func NewRefdbBackendFromC(ptr *C.git_refdb_backend) (backend *RefdbBackend) { } func (v *Refdb) SetBackend(backend *RefdbBackend) (err error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_refdb_set_backend(v.ptr, backend.ptr) if ret < 0 { backend.Free() diff --git a/reference.go b/reference.go index ce9d722..5cac436 100644 --- a/reference.go +++ b/reference.go @@ -294,6 +294,10 @@ func (v *ReferenceNameIterator) Next() (string, error) { // returned error is git.ErrIterOver func (v *ReferenceIterator) Next() (*Reference, error) { var ptr *C.git_reference + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_reference_next(&ptr, v.ptr) if ret < 0 { return nil, MakeGitError(ret) diff --git a/remote.go b/remote.go index 96cc4c1..faff9c5 100644 --- a/remote.go +++ b/remote.go @@ -249,6 +249,10 @@ func (r *Remote) Free() { func (repo *Repository) ListRemotes() ([]string, error) { var r C.git_strarray + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ecode := C.git_remote_list(&r, repo.ptr) if ecode < 0 { return nil, MakeGitError(ecode) @@ -573,6 +577,9 @@ func (o *Remote) Fetch(refspecs []string, sig *Signature, msg string) error { crefspecs.strings = makeCStringsFromStrings(refspecs) defer freeStrarray(&crefspecs) + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_remote_fetch(o.ptr, &crefspecs, csig, cmsg) if ret < 0 { return MakeGitError(ret) diff --git a/repository.go b/repository.go index 09f5fef..2baeb4a 100644 --- a/repository.go +++ b/repository.go @@ -72,6 +72,9 @@ func InitRepository(path string, isbare bool) (*Repository, error) { func NewRepositoryWrapOdb(odb *Odb) (repo *Repository, err error) { repo = new(Repository) + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_repository_wrap_odb(&repo.ptr, odb.ptr) if ret < 0 { return nil, MakeGitError(ret) @@ -386,6 +389,9 @@ func (v *Repository) CreateTag( ctarget := commit.gitObject.ptr + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_tag_create(oid.toC(), v.ptr, cname, ctarget, taggerSig, cmessage, 0) if ret < 0 { return nil, MakeGitError(ret) -- 2.45.2 From 520a0425c736c7d584d21d073b08a8735dd2464f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 6 Dec 2014 02:58:28 +0100 Subject: [PATCH 11/41] Add the newer missing thread-locking instances --- diff.go | 7 +++++++ odb.go | 14 ++++++++++++++ remote.go | 3 +++ 3 files changed, 24 insertions(+) diff --git a/diff.go b/diff.go index d7d8118..00f4bd5 100644 --- a/diff.go +++ b/diff.go @@ -179,6 +179,9 @@ func (diff *Diff) FindSimilar(opts *DiffFindOptions) error { } } + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ecode := C.git_diff_find_similar(diff.ptr, copts) if ecode < 0 { return MakeGitError(ecode) @@ -404,6 +407,10 @@ type DiffFindOptions struct { func DefaultDiffFindOptions() (DiffFindOptions, error) { opts := C.git_diff_find_options{} + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ecode := C.git_diff_find_init_options(&opts, C.GIT_DIFF_FIND_OPTIONS_VERSION) if ecode < 0 { return DiffFindOptions{}, MakeGitError(ecode) diff --git a/odb.go b/odb.go index 9ea151b..9191656 100644 --- a/odb.go +++ b/odb.go @@ -168,6 +168,10 @@ func (v *Odb) NewReadStream(id *Oid) (*OdbReadStream, error) { // known in advance func (v *Odb) NewWriteStream(size int, otype ObjectType) (*OdbWriteStream, error) { stream := new(OdbWriteStream) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_odb_open_wstream(&stream.ptr, v.ptr, C.size_t(size), C.git_otype(otype)) if ret < 0 { return nil, MakeGitError(ret) @@ -221,6 +225,10 @@ func (stream *OdbReadStream) Read(data []byte) (int, error) { header := (*reflect.SliceHeader)(unsafe.Pointer(&data)) ptr := (*C.char)(unsafe.Pointer(header.Data)) size := C.size_t(header.Cap) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_odb_stream_read(stream.ptr, ptr, size) if ret < 0 { return 0, MakeGitError(ret) @@ -253,6 +261,9 @@ func (stream *OdbWriteStream) Write(data []byte) (int, error) { ptr := (*C.char)(unsafe.Pointer(header.Data)) size := C.size_t(header.Len) + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_odb_stream_write(stream.ptr, ptr, size) if ret < 0 { return 0, MakeGitError(ret) @@ -264,6 +275,9 @@ func (stream *OdbWriteStream) Write(data []byte) (int, error) { // Close signals that all the data has been written and stores the // resulting object id in the stream's Id field. func (stream *OdbWriteStream) Close() error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_odb_stream_finalize_write(stream.Id.toC(), stream.ptr) if ret < 0 { return MakeGitError(ret) diff --git a/remote.go b/remote.go index faff9c5..604ef40 100644 --- a/remote.go +++ b/remote.go @@ -610,6 +610,9 @@ func (o *Remote) Ls(filterRefs ...string) ([]RemoteHead, error) { var refs **C.git_remote_head var length C.size_t + runtime.LockOSThread() + defer runtime.UnlockOSThread() + if ret := C.git_remote_ls(&refs, &length, o.ptr); ret != 0 { return nil, MakeGitError(ret) } -- 2.45.2 From db17135a30100c698d3efa1c98ba717201f426ce Mon Sep 17 00:00:00 2001 From: Jose Alvarez Date: Fri, 5 Dec 2014 16:52:15 -0500 Subject: [PATCH 12/41] Export PatchFromBuffers function. This change also factor out diffOptionsToC function to remove duplicated code. --- diff.go | 72 +++++++++++++++++++++----------------------------------- patch.go | 32 +++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 45 deletions(-) diff --git a/diff.go b/diff.go index 00f4bd5..999f067 100644 --- a/diff.go +++ b/diff.go @@ -459,21 +459,8 @@ func diffNotifyCb(_diff_so_far unsafe.Pointer, delta_to_add *C.git_diff_delta, m return 0 } -func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree, opts *DiffOptions) (*Diff, error) { - var diffPtr *C.git_diff - var oldPtr, newPtr *C.git_tree - - if oldTree != nil { - oldPtr = oldTree.cast_ptr - } - - if newTree != nil { - newPtr = newTree.cast_ptr - } - +func diffOptionsToC(opts *DiffOptions) (copts *C.git_diff_options, notifyData *diffNotifyData) { cpathspec := C.git_strarray{} - var copts *C.git_diff_options - var notifyData *diffNotifyData if opts != nil { notifyData = &diffNotifyData{ Callback: opts.NotifyCallback, @@ -481,7 +468,6 @@ func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree, opts *DiffOptions) ( if opts.Pathspec != nil { cpathspec.count = C.size_t(len(opts.Pathspec)) cpathspec.strings = makeCStringsFromStrings(opts.Pathspec) - defer freeStrarray(&cpathspec) } copts = &C.git_diff_options{ @@ -500,6 +486,30 @@ func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree, opts *DiffOptions) ( copts.notify_payload = unsafe.Pointer(notifyData) } } + return +} + +func freeDiffOptions(copts *C.git_diff_options) { + if copts != nil { + cpathspec := copts.pathspec + freeStrarray(&cpathspec) + } +} + +func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree, opts *DiffOptions) (*Diff, error) { + var diffPtr *C.git_diff + var oldPtr, newPtr *C.git_tree + + if oldTree != nil { + oldPtr = oldTree.cast_ptr + } + + if newTree != nil { + newPtr = newTree.cast_ptr + } + + copts, notifyData := diffOptionsToC(opts) + defer freeDiffOptions(copts) runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -523,35 +533,8 @@ func (v *Repository) DiffTreeToWorkdir(oldTree *Tree, opts *DiffOptions) (*Diff, oldPtr = oldTree.cast_ptr } - cpathspec := C.git_strarray{} - var copts *C.git_diff_options - var notifyData *diffNotifyData - if opts != nil { - notifyData = &diffNotifyData{ - Callback: opts.NotifyCallback, - } - if opts.Pathspec != nil { - cpathspec.count = C.size_t(len(opts.Pathspec)) - cpathspec.strings = makeCStringsFromStrings(opts.Pathspec) - defer freeStrarray(&cpathspec) - } - - copts = &C.git_diff_options{ - version: C.GIT_DIFF_OPTIONS_VERSION, - flags: C.uint32_t(opts.Flags), - ignore_submodules: C.git_submodule_ignore_t(opts.IgnoreSubmodules), - pathspec: cpathspec, - context_lines: C.uint32_t(opts.ContextLines), - interhunk_lines: C.uint32_t(opts.InterhunkLines), - id_abbrev: C.uint16_t(opts.IdAbbrev), - max_size: C.git_off_t(opts.MaxSize), - } - - if opts.NotifyCallback != nil { - C._go_git_setup_diff_notify_callbacks(copts) - copts.notify_payload = unsafe.Pointer(notifyData) - } - } + copts, notifyData := diffOptionsToC(opts) + defer freeDiffOptions(copts) runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -565,5 +548,4 @@ func (v *Repository) DiffTreeToWorkdir(oldTree *Tree, opts *DiffOptions) (*Diff, return notifyData.Diff, nil } return newDiffFromC(diffPtr), nil - } diff --git a/patch.go b/patch.go index 0fe1c04..ea01b87 100644 --- a/patch.go +++ b/patch.go @@ -6,6 +6,7 @@ package git import "C" import ( "runtime" + "unsafe" ) type Patch struct { @@ -50,3 +51,34 @@ func (patch *Patch) String() (string, error) { } return C.GoString(buf.ptr), nil } + +func toPointer(data []byte) (ptr unsafe.Pointer) { + if len(data) > 0 { + ptr = unsafe.Pointer(&data[0]) + } else { + ptr = unsafe.Pointer(nil) + } + return +} + +func (v *Repository) PatchFromBuffers(oldPath, newPath string, oldBuf, newBuf []byte, opts *DiffOptions) (*Patch, error) { + var patchPtr *C.git_patch + + oldPtr := toPointer(oldBuf) + newPtr := (*C.char)(toPointer(newBuf)) + + cOldPath := C.CString(oldPath) + defer C.free(unsafe.Pointer(cOldPath)) + + cNewPath := C.CString(newPath) + defer C.free(unsafe.Pointer(cNewPath)) + + copts, _ := diffOptionsToC(opts) + defer freeDiffOptions(copts) + + ecode := C.git_patch_from_buffers(&patchPtr, oldPtr, C.size_t(len(oldBuf)), cOldPath, newPtr, C.size_t(len(newBuf)), cNewPath, copts) + if ecode < 0 { + return nil, MakeGitError(ecode) + } + return newPatchFromC(patchPtr), nil +} -- 2.45.2 From 3087e610fbceeeab8ad963a19aaac6fa61d10ccb Mon Sep 17 00:00:00 2001 From: Quinn Slack Date: Tue, 18 Nov 2014 05:23:22 -0800 Subject: [PATCH 13/41] add script for checking thread locks in funcs that call MakeGitError --- Makefile | 1 + script/check-MakeGitError-thread-lock.go | 69 ++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 script/check-MakeGitError-thread-lock.go diff --git a/Makefile b/Makefile index 4ecc8a4..3040857 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ build-libgit2: ./script/build-libgit2-static.sh test: build-libgit2 + go run script/check-MakeGitError-thread-lock.go ./script/with-static.sh go test ./... install: build-libgit2 diff --git a/script/check-MakeGitError-thread-lock.go b/script/check-MakeGitError-thread-lock.go new file mode 100644 index 0000000..10ab39f --- /dev/null +++ b/script/check-MakeGitError-thread-lock.go @@ -0,0 +1,69 @@ +package main + +import ( + "bytes" + "fmt" + "go/ast" + "go/build" + "go/parser" + "go/printer" + "go/token" + "log" + "strings" +) + +var ( + fset = token.NewFileSet() +) + +func main() { + log.SetFlags(0) + + bpkg, err := build.ImportDir(".", 0) + if err != nil { + log.Fatal(err) + } + + pkgs, err := parser.ParseDir(fset, bpkg.Dir, nil, 0) + if err != nil { + log.Fatal(err) + } + + for _, pkg := range pkgs { + if err := checkPkg(pkg); err != nil { + log.Fatal(err) + } + } + if len(pkgs) == 0 { + log.Fatal("No packages to check.") + } +} + +var ignoreViolationsInFunc = map[string]bool{ + "MakeGitError": true, + "MakeGitError2": true, +} + +func checkPkg(pkg *ast.Package) error { + var violations []string + ast.Inspect(pkg, func(node ast.Node) bool { + switch node := node.(type) { + case *ast.FuncDecl: + var b bytes.Buffer + if err := printer.Fprint(&b, fset, node); err != nil { + log.Fatal(err) + } + src := b.String() + + if strings.Contains(src, "MakeGitError") && !strings.Contains(src, "runtime.LockOSThread()") && !strings.Contains(src, "defer runtime.UnlockOSThread()") && !ignoreViolationsInFunc[node.Name.Name] { + pos := fset.Position(node.Pos()) + violations = append(violations, fmt.Sprintf("%s at %s:%d", node.Name.Name, pos.Filename, pos.Line)) + } + } + return true + }) + if len(violations) > 0 { + return fmt.Errorf("%d non-thread-locked calls to MakeGitError found. To fix, add the following to each func below that calls MakeGitError, before the cgo call that might produce the error:\n\n\truntime.LockOSThread()\n\tdefer runtime.UnlockOSThread()\n\n%s", len(violations), strings.Join(violations, "\n")) + } + return nil +} -- 2.45.2 From 57095bafe72ffd80d22469215db026be3c5d4242 Mon Sep 17 00:00:00 2001 From: Quinn Slack Date: Mon, 8 Dec 2014 11:54:04 -0800 Subject: [PATCH 14/41] only check Go source files for non-thread-locked MakeGitError calls --- script/check-MakeGitError-thread-lock.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/script/check-MakeGitError-thread-lock.go b/script/check-MakeGitError-thread-lock.go index 10ab39f..f6b01b3 100644 --- a/script/check-MakeGitError-thread-lock.go +++ b/script/check-MakeGitError-thread-lock.go @@ -9,6 +9,8 @@ import ( "go/printer" "go/token" "log" + "os" + "path/filepath" "strings" ) @@ -24,7 +26,7 @@ func main() { log.Fatal(err) } - pkgs, err := parser.ParseDir(fset, bpkg.Dir, nil, 0) + pkgs, err := parser.ParseDir(fset, bpkg.Dir, func(fi os.FileInfo) bool { return filepath.Ext(fi.Name()) == ".go" }, 0) if err != nil { log.Fatal(err) } -- 2.45.2 From cb6201b6336366651dcf146c626a4a1a44d20d79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 11 Dec 2014 02:46:42 +0100 Subject: [PATCH 15/41] Add missing thread-locking --- patch.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/patch.go b/patch.go index ea01b87..45e14ac 100644 --- a/patch.go +++ b/patch.go @@ -76,6 +76,9 @@ func (v *Repository) PatchFromBuffers(oldPath, newPath string, oldBuf, newBuf [] copts, _ := diffOptionsToC(opts) defer freeDiffOptions(copts) + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ecode := C.git_patch_from_buffers(&patchPtr, oldPtr, C.size_t(len(oldBuf)), cOldPath, newPtr, C.size_t(len(newBuf)), cNewPath, copts) if ecode < 0 { return nil, MakeGitError(ecode) -- 2.45.2 From a9d993f3d1400970bfba572747edbee1b57fd221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 11 Dec 2014 02:59:07 +0100 Subject: [PATCH 16/41] Remove useless includes --- blob.go | 1 - branch.go | 1 - clone.go | 1 - commit.go | 1 - config.go | 1 - credentials.go | 1 - git.go | 1 - index.go | 1 - merge.go | 1 - object.go | 1 - odb.go | 1 - packbuilder.go | 1 - push.go | 1 - refdb.go | 1 - reference.go | 1 - revparse.go | 1 - status.go | 1 - submodule.go | 1 - tree.go | 1 - walk.go | 1 - 20 files changed, 20 deletions(-) diff --git a/blob.go b/blob.go index 58258fd..5a33bd8 100644 --- a/blob.go +++ b/blob.go @@ -2,7 +2,6 @@ package git /* #include -#include #include extern int _go_git_blob_create_fromchunks(git_oid *id, diff --git a/branch.go b/branch.go index 95c3807..bb231c3 100644 --- a/branch.go +++ b/branch.go @@ -2,7 +2,6 @@ package git /* #include -#include */ import "C" diff --git a/clone.go b/clone.go index 958e65d..2e0fcce 100644 --- a/clone.go +++ b/clone.go @@ -2,7 +2,6 @@ package git /* #include -#include */ import "C" diff --git a/commit.go b/commit.go index ed026a4..559a1bd 100644 --- a/commit.go +++ b/commit.go @@ -2,7 +2,6 @@ package git /* #include -#include extern int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr); */ diff --git a/config.go b/config.go index bbf03e2..2965a69 100644 --- a/config.go +++ b/config.go @@ -2,7 +2,6 @@ package git /* #include -#include */ import "C" import ( diff --git a/credentials.go b/credentials.go index b04bf98..bb0ec41 100644 --- a/credentials.go +++ b/credentials.go @@ -2,7 +2,6 @@ package git /* #include -#include */ import "C" import "unsafe" diff --git a/git.go b/git.go index 72d8d03..89bd561 100644 --- a/git.go +++ b/git.go @@ -2,7 +2,6 @@ package git /* #include -#include */ import "C" import ( diff --git a/index.go b/index.go index 230e159..6b90758 100644 --- a/index.go +++ b/index.go @@ -2,7 +2,6 @@ package git /* #include -#include extern int _go_git_index_add_all(git_index*, const git_strarray*, unsigned int, void*); extern int _go_git_index_update_all(git_index*, const git_strarray*, void*); diff --git a/merge.go b/merge.go index 285c9c5..3742f97 100644 --- a/merge.go +++ b/merge.go @@ -2,7 +2,6 @@ package git /* #include -#include 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); diff --git a/object.go b/object.go index a3bf652..7428e0f 100644 --- a/object.go +++ b/object.go @@ -2,7 +2,6 @@ package git /* #include -#include */ import "C" import "runtime" diff --git a/odb.go b/odb.go index 9191656..19bb71c 100644 --- a/odb.go +++ b/odb.go @@ -2,7 +2,6 @@ package git /* #include -#include extern int _go_git_odb_foreach(git_odb *db, void *payload); extern void _go_git_odb_backend_free(git_odb_backend *backend); diff --git a/packbuilder.go b/packbuilder.go index 24d2f6d..7c94926 100644 --- a/packbuilder.go +++ b/packbuilder.go @@ -2,7 +2,6 @@ package git /* #include -#include #include #include diff --git a/push.go b/push.go index 5fb7f07..e6fe51f 100644 --- a/push.go +++ b/push.go @@ -2,7 +2,6 @@ package git /* #include -#include int _go_git_push_status_foreach(git_push *push, void *data); int _go_git_push_set_callbacks(git_push *push, void *packbuilder_progress_data, void *transfer_progress_data); diff --git a/refdb.go b/refdb.go index 36e2f0a..0d1e241 100644 --- a/refdb.go +++ b/refdb.go @@ -2,7 +2,6 @@ package git /* #include -#include #include extern void _go_git_refdb_backend_free(git_refdb_backend *backend); diff --git a/reference.go b/reference.go index 5cac436..46436a6 100644 --- a/reference.go +++ b/reference.go @@ -2,7 +2,6 @@ package git /* #include -#include */ import "C" import ( diff --git a/revparse.go b/revparse.go index 308da4c..7eb04f1 100644 --- a/revparse.go +++ b/revparse.go @@ -2,7 +2,6 @@ package git /* #include -#include extern void _go_git_revspec_free(git_revspec *revspec); */ diff --git a/status.go b/status.go index 1d1d098..c849aca 100644 --- a/status.go +++ b/status.go @@ -2,7 +2,6 @@ package git /* #include -#include */ import "C" diff --git a/submodule.go b/submodule.go index 35ceeb0..d5ab69f 100644 --- a/submodule.go +++ b/submodule.go @@ -2,7 +2,6 @@ package git /* #include -#include extern int _go_git_visit_submodule(git_repository *repo, void *fct); */ diff --git a/tree.go b/tree.go index 45de9f1..efabce5 100644 --- a/tree.go +++ b/tree.go @@ -2,7 +2,6 @@ package git /* #include -#include extern int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr); */ diff --git a/walk.go b/walk.go index f243e21..d02044a 100644 --- a/walk.go +++ b/walk.go @@ -2,7 +2,6 @@ package git /* #include -#include */ import "C" -- 2.45.2 From 63116ea57e6920b25d7410eda2fc1c786be8a819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 13 Dec 2014 00:25:11 +0100 Subject: [PATCH 17/41] Update to master This deprecates the Push struct in favour of Remote.Push() --- push.go | 11 +---------- push_test.go | 25 +++++++++++++++++++++---- remote.go | 37 +++++++++++++++++++++++++++++++++++++ vendor/libgit2 | 2 +- 4 files changed, 60 insertions(+), 15 deletions(-) diff --git a/push.go b/push.go index e6fe51f..ff96b08 100644 --- a/push.go +++ b/push.go @@ -31,6 +31,7 @@ func (p *Push) Free() { C.git_push_free(p.ptr) } +// This class is deprecated. Please use Remote.Push() instead func (remote *Remote) NewPush() (*Push, error) { runtime.LockOSThread() @@ -56,16 +57,6 @@ func (p *Push) Finish() error { return nil } -func (p *Push) UnpackOk() bool { - - ret := C.git_push_unpack_ok(p.ptr) - if ret == 0 { - return false - } - return true - -} - func (p *Push) UpdateTips(sig *Signature, msg string) error { var csig *C.git_signature = nil diff --git a/push_test.go b/push_test.go index 65f4dd2..3911875 100644 --- a/push_test.go +++ b/push_test.go @@ -48,10 +48,27 @@ func Test_Push_ToRemote(t *testing.T) { }) checkFatal(t, err) - if !push.UnpackOk() { - t.Fatalf("unable to unpack") - } - defer remote.Free() defer repo.Free() } + +func TestRemotePush(t *testing.T) { + repo := createBareTestRepo(t) + defer os.RemoveAll(repo.Path()) + localRepo := createTestRepo(t) + defer os.RemoveAll(localRepo.Workdir()) + + remote, err := localRepo.CreateRemote("test_push", repo.Path()) + checkFatal(t, err) + + seedTestRepo(t, localRepo) + + err = remote.Push([]string{"refs/heads/master"}, nil, nil, "") + checkFatal(t, err) + + _, err = localRepo.LookupReference("refs/remotes/test_push/master") + checkFatal(t, err) + + _, err = repo.LookupReference("refs/heads/master") + checkFatal(t, err) +} diff --git a/remote.go b/remote.go index 604ef40..021e207 100644 --- a/remote.go +++ b/remote.go @@ -650,3 +650,40 @@ func (o *Remote) Ls(filterRefs ...string) ([]RemoteHead, error) { return heads, nil } + +func (o *Remote) Push(refspecs []string, opts *PushOptions, sig *Signature, msg string) error { + var csig *C.git_signature = nil + if sig != nil { + csig = sig.toC() + defer C.free(unsafe.Pointer(csig)) + } + + var cmsg *C.char + if msg == "" { + cmsg = nil + } else { + cmsg = C.CString(msg) + defer C.free(unsafe.Pointer(cmsg)) + } + + var copts C.git_push_options + C.git_push_init_options(&copts, C.GIT_PUSH_OPTIONS_VERSION) + if opts != nil { + copts.version = C.uint(opts.Version) + copts.pb_parallelism = C.uint(opts.PbParallelism) + } + + crefspecs := C.git_strarray{} + crefspecs.count = C.size_t(len(refspecs)) + crefspecs.strings = makeCStringsFromStrings(refspecs) + defer freeStrarray(&crefspecs) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_push(o.ptr, &crefspecs, &copts, csig, cmsg) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} diff --git a/vendor/libgit2 b/vendor/libgit2 index 169497d..4eb97ef 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit 169497d1e7c238d2925577d1af3dc03e9a507cd3 +Subproject commit 4eb97ef3bf18403fbce351ae4cac673655d2886a -- 2.45.2 From 0202f152ac515aacf38b210df7d77ca82e146663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 13 Dec 2014 01:23:40 +0100 Subject: [PATCH 18/41] Add the new callbacks for Remote.Push() This unifies the types with the Push struct, in preparation for its deletion. --- push.go | 11 ++++------- remote.go | 41 +++++++++++++++++++++++++++++++++++++++++ wrapper.c | 7 ++++++- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/push.go b/push.go index ff96b08..f5299c4 100644 --- a/push.go +++ b/push.go @@ -146,17 +146,14 @@ type PushCallbacks struct { TransferProgress *PushTransferProgressCallback } -type PackbuilderProgressCallback func(stage int, current uint, total uint) int -type PushTransferProgressCallback func(current uint, total uint, bytes uint) int - //export packbuilderProgress func packbuilderProgress(stage C.int, current C.uint, total C.uint, data unsafe.Pointer) C.int { - return C.int((*(*PackbuilderProgressCallback)(data))(int(stage), uint(current), uint(total))) + return C.int((*(*PackbuilderProgressCallback)(data))(int32(stage), uint32(current), uint32(total))) } -//export pushTransferProgress -func pushTransferProgress(current C.uint, total C.uint, bytes C.size_t, data unsafe.Pointer) C.int { - return C.int((*(*PushTransferProgressCallback)(data))(uint(current), uint(total), uint(bytes))) +//export pushStructTransferProgress +func pushStructTransferProgress(current C.uint, total C.uint, bytes C.size_t, data unsafe.Pointer) C.int { + return C.int((*(*PushTransferProgressCallback)(data))(uint32(current), uint32(total), uint(bytes))) } func (p *Push) SetCallbacks(callbacks PushCallbacks) { diff --git a/remote.go b/remote.go index 021e207..563aba5 100644 --- a/remote.go +++ b/remote.go @@ -53,6 +53,9 @@ type CredentialsCallback func(url string, username_from_url string, allowed_type type TransferProgressCallback func(stats TransferProgress) ErrorCode type UpdateTipsCallback func(refname string, a *Oid, b *Oid) ErrorCode type CertificateCheckCallback func(cert *Certificate, valid bool, hostname string) ErrorCode +type PackbuilderProgressCallback func(stage int32, current, total uint32) ErrorCode +type PushTransferProgressCallback func(current, total uint32, bytes uint) ErrorCode +type PushUpdateReferenceCallback func(refname, status string) ErrorCode type RemoteCallbacks struct { SidebandProgressCallback TransportMessageCallback @@ -61,8 +64,12 @@ type RemoteCallbacks struct { TransferProgressCallback UpdateTipsCallback CertificateCheckCallback + PackProgressCallback PackbuilderProgressCallback + PushTransferProgressCallback + PushUpdateReferenceCallback } + type Remote struct { ptr *C.git_remote callbacks RemoteCallbacks @@ -216,6 +223,40 @@ func certificateCheckCallback(_cert *C.git_cert, _valid C.int, _host *C.char, da return int(callbacks.CertificateCheckCallback(&cert, valid, host)) } +//export packProgressCallback +func packProgressCallback(stage C.int, current, total C.uint, data unsafe.Pointer) int { + callbacks := (*RemoteCallbacks)(data) + + if callbacks.PackProgressCallback == nil { + return 0 + } + + return int(callbacks.PackProgressCallback(int32(stage), uint32(current), uint32(total))) +} + +//export pushTransferProgressCallback +func pushTransferProgressCallback(current, total C.uint, bytes C.size_t, data unsafe.Pointer) int { + callbacks := (*RemoteCallbacks)(data) + if callbacks.PushTransferProgressCallback == nil { + return 0 + } + + return int(callbacks.PushTransferProgressCallback(uint32(current), uint32(total), uint(bytes))) +} + +//export pushUpdateReferenceCallback +func pushUpdateReferenceCallback(refname, status *C.char, data unsafe.Pointer) int { + callbacks := (*RemoteCallbacks)(data) + + if callbacks.PushUpdateReferenceCallback == nil { + return 0 + } + + return int(callbacks.PushUpdateReferenceCallback(C.GoString(refname), C.GoString(status))) +} + + + func RemoteIsValidName(name string) bool { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) diff --git a/wrapper.c b/wrapper.c index 6e33fa2..7832d7f 100644 --- a/wrapper.c +++ b/wrapper.c @@ -71,12 +71,17 @@ void _go_git_setup_diff_notify_callbacks(git_diff_options *opts) { void _go_git_setup_callbacks(git_remote_callbacks *callbacks) { typedef int (*completion_cb)(git_remote_completion_type type, void *data); typedef int (*update_tips_cb)(const char *refname, const git_oid *a, const git_oid *b, void *data); + typedef (*push_update_reference_cb)(const char *refname, const char *status, void *data); + callbacks->sideband_progress = (git_transport_message_cb)sidebandProgressCallback; callbacks->completion = (completion_cb)completionCallback; callbacks->credentials = (git_cred_acquire_cb)credentialsCallback; callbacks->transfer_progress = (git_transfer_progress_cb)transferProgressCallback; callbacks->update_tips = (update_tips_cb)updateTipsCallback; callbacks->certificate_check = (git_transport_certificate_check_cb) certificateCheckCallback; + callbacks->pack_progress = (git_packbuilder_progress) packProgressCallback; + callbacks->push_transfer_progress = (git_push_transfer_progress) pushTransferProgressCallback; + callbacks->push_update_reference = (push_update_reference_cb) pushUpdateReferenceCallback; } typedef int (*status_foreach_cb)(const char *ref, const char *msg, void *data); @@ -88,7 +93,7 @@ int _go_git_push_status_foreach(git_push *push, void *data) int _go_git_push_set_callbacks(git_push *push, void *packbuilder_progress_data, void *transfer_progress_data) { - return git_push_set_callbacks(push, packbuilderProgress, packbuilder_progress_data, pushTransferProgress, transfer_progress_data); + return git_push_set_callbacks(push, packbuilderProgress, packbuilder_progress_data, pushStructTransferProgress, transfer_progress_data); } int _go_blob_chunk_cb(char *buffer, size_t maxLen, void *payload) -- 2.45.2 From d69c7714530f12ec9b45578d5f06813d974f7ef2 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Thu, 18 Dec 2014 14:08:11 -0800 Subject: [PATCH 19/41] Update libgit2 Fix calls to C.git_treebuilder_create and C.git_treebuilder_write. --- repository.go | 4 ++-- tree.go | 2 +- vendor/libgit2 | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/repository.go b/repository.go index 2baeb4a..b9fb4b5 100644 --- a/repository.go +++ b/repository.go @@ -456,7 +456,7 @@ func (v *Repository) TreeBuilder() (*TreeBuilder, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() - if ret := C.git_treebuilder_create(&bld.ptr, nil); ret < 0 { + if ret := C.git_treebuilder_create(&bld.ptr, v.ptr, nil); ret < 0 { return nil, MakeGitError(ret) } runtime.SetFinalizer(bld, (*TreeBuilder).Free) @@ -471,7 +471,7 @@ func (v *Repository) TreeBuilderFromTree(tree *Tree) (*TreeBuilder, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() - if ret := C.git_treebuilder_create(&bld.ptr, tree.cast_ptr); ret < 0 { + if ret := C.git_treebuilder_create(&bld.ptr, v.ptr, tree.cast_ptr); ret < 0 { return nil, MakeGitError(ret) } runtime.SetFinalizer(bld, (*TreeBuilder).Free) diff --git a/tree.go b/tree.go index efabce5..c18d02a 100644 --- a/tree.go +++ b/tree.go @@ -161,7 +161,7 @@ func (v *TreeBuilder) Write() (*Oid, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() - err := C.git_treebuilder_write(oid.toC(), v.repo.ptr, v.ptr) + err := C.git_treebuilder_write(oid.toC(), v.ptr) if err < 0 { return nil, MakeGitError(err) diff --git a/vendor/libgit2 b/vendor/libgit2 index 4eb97ef..247b3f4 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit 4eb97ef3bf18403fbce351ae4cac673655d2886a +Subproject commit 247b3f4ee5d08ee5f5f88eb1c0f6ed03cbd21fc9 -- 2.45.2 From 74957c2ae6dc852a50de54fc788f207d8f385d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 18 Dec 2014 23:02:53 +0000 Subject: [PATCH 20/41] Add missing return type --- wrapper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrapper.c b/wrapper.c index 7832d7f..3779653 100644 --- a/wrapper.c +++ b/wrapper.c @@ -71,7 +71,7 @@ void _go_git_setup_diff_notify_callbacks(git_diff_options *opts) { void _go_git_setup_callbacks(git_remote_callbacks *callbacks) { typedef int (*completion_cb)(git_remote_completion_type type, void *data); typedef int (*update_tips_cb)(const char *refname, const git_oid *a, const git_oid *b, void *data); - typedef (*push_update_reference_cb)(const char *refname, const char *status, void *data); + typedef int (*push_update_reference_cb)(const char *refname, const char *status, void *data); callbacks->sideband_progress = (git_transport_message_cb)sidebandProgressCallback; callbacks->completion = (completion_cb)completionCallback; -- 2.45.2 From 8f6e13bd08d2f37027d34c0f379a79a6c5263e8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 27 Dec 2014 10:59:19 +0000 Subject: [PATCH 21/41] Make the cgo tool do more linking work The cgo directives let us do a lot more than I previously thought, so we can use this to make the building process of git2go go through the go tool directly rather than via the script. libgit2 still needs to be built manually, so we do still require make, but only for building libgit2. Once that's built, any modifications to git2go's own code can be built with go build --- Makefile | 4 ++-- git.go | 2 ++ script/build-libgit2-static.sh | 4 ++-- script/with-static.sh | 12 ------------ 4 files changed, 6 insertions(+), 16 deletions(-) delete mode 100755 script/with-static.sh diff --git a/Makefile b/Makefile index 3040857..39fc558 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ build-libgit2: test: build-libgit2 go run script/check-MakeGitError-thread-lock.go - ./script/with-static.sh go test ./... + go test ./... install: build-libgit2 - ./script/with-static.sh go install ./... + go install ./... diff --git a/git.go b/git.go index 89bd561..6a5562a 100644 --- a/git.go +++ b/git.go @@ -1,6 +1,8 @@ package git /* +#cgo pkg-config: --static --define-variable=libdir=vendor/libgit2/build --define-variable=includedir=vendor/libgit2/include vendor/libgit2/build/libgit2.pc +#cgo LDFLAGS: -lgit2 #include */ import "C" diff --git a/script/build-libgit2-static.sh b/script/build-libgit2-static.sh index 5723721..0a6c39f 100755 --- a/script/build-libgit2-static.sh +++ b/script/build-libgit2-static.sh @@ -4,7 +4,7 @@ set -ex VENDORED_PATH=vendor/libgit2 -cd $VENDORED_PATH && +cd "$VENDORED_PATH" && mkdir -p install/lib && mkdir -p build && cd build && @@ -13,7 +13,7 @@ cmake -DTHREADSAFE=ON \ -DBUILD_SHARED_LIBS=OFF \ -DCMAKE_C_FLAGS=-fPIC \ -DCMAKE_BUILD_TYPE="RelWithDebInfo" \ - -DCMAKE_INSTALL_PREFIX=../install \ + -DCMAKE_INSTALL_PREFIX=. \ .. && cmake --build . diff --git a/script/with-static.sh b/script/with-static.sh deleted file mode 100755 index 3f60e31..0000000 --- a/script/with-static.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -set -ex - -export BUILD="$PWD/vendor/libgit2/build" -export PCFILE="$BUILD/libgit2.pc" - -FLAGS=$(pkg-config --static --libs $PCFILE) || exit 1 -export CGO_LDFLAGS="$BUILD/libgit2.a -L$BUILD ${FLAGS}" -export CGO_CFLAGS="-I$PWD/vendor/libgit2/include" - -$@ -- 2.45.2 From d2a9d7768bb76719ac4506715ec8037fea3609a2 Mon Sep 17 00:00:00 2001 From: Quinn Slack Date: Tue, 30 Dec 2014 02:03:20 -0800 Subject: [PATCH 22/41] heed DiffOptions fields OldPrefix and NewPrefix --- diff.go | 6 ++++++ diff_test.go | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/diff.go b/diff.go index 999f067..63f3453 100644 --- a/diff.go +++ b/diff.go @@ -371,6 +371,8 @@ func DefaultDiffOptions() (DiffOptions, error) { InterhunkLines: uint32(opts.interhunk_lines), IdAbbrev: uint16(opts.id_abbrev), MaxSize: int(opts.max_size), + OldPrefix: "a", + NewPrefix: "b", }, nil } @@ -479,6 +481,8 @@ func diffOptionsToC(opts *DiffOptions) (copts *C.git_diff_options, notifyData *d interhunk_lines: C.uint32_t(opts.InterhunkLines), id_abbrev: C.uint16_t(opts.IdAbbrev), max_size: C.git_off_t(opts.MaxSize), + old_prefix: C.CString(opts.OldPrefix), + new_prefix: C.CString(opts.NewPrefix), } if opts.NotifyCallback != nil { @@ -493,6 +497,8 @@ func freeDiffOptions(copts *C.git_diff_options) { if copts != nil { cpathspec := copts.pathspec freeStrarray(&cpathspec) + C.free(unsafe.Pointer(copts.old_prefix)) + C.free(unsafe.Pointer(copts.new_prefix)) } } diff --git a/diff_test.go b/diff_test.go index 84d72db..7c54f4e 100644 --- a/diff_test.go +++ b/diff_test.go @@ -3,6 +3,7 @@ package git import ( "errors" "os" + "strings" "testing" ) @@ -75,6 +76,8 @@ func TestDiffTreeToTree(t *testing.T) { callbackInvoked = true return nil }, + OldPrefix: "x1/", + NewPrefix: "y1/", } diff, err := repo.DiffTreeToTree(originalTree, newTree, &opts) @@ -90,7 +93,19 @@ func TestDiffTreeToTree(t *testing.T) { files := make([]string, 0) hunks := make([]DiffHunk, 0) lines := make([]DiffLine, 0) + patches := make([]string, 0) err = diff.ForEach(func(file DiffDelta, progress float64) (DiffForEachHunkCallback, error) { + patch, err := diff.Patch(len(patches)) + if err != nil { + return nil, err + } + defer patch.Free() + patchStr, err := patch.String() + if err != nil { + return nil, err + } + patches = append(patches, patchStr) + files = append(files, file.OldFile.Path) return func(hunk DiffHunk) (DiffForEachLineCallback, error) { hunks = append(hunks, hunk) @@ -131,6 +146,10 @@ func TestDiffTreeToTree(t *testing.T) { t.Fatal("Incorrect lines in diff") } + if want1, want2 := "x1/README", "y1/README"; !strings.Contains(patches[0], want1) || !strings.Contains(patches[0], want2) { + t.Errorf("Diff patch doesn't contain %q or %q\n\n%s", want1, want2, patches[0]) + } + errTest := errors.New("test error") err = diff.ForEach(func(file DiffDelta, progress float64) (DiffForEachHunkCallback, error) { -- 2.45.2 From ef839080596bd9fbead5db1e91f698bebfd4024a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 31 Dec 2014 19:43:20 +0000 Subject: [PATCH 23/41] Revert "Make the cgo tool do more linking work" --- Makefile | 4 ++-- git.go | 2 -- script/build-libgit2-static.sh | 4 ++-- script/with-static.sh | 12 ++++++++++++ 4 files changed, 16 insertions(+), 6 deletions(-) create mode 100755 script/with-static.sh diff --git a/Makefile b/Makefile index 39fc558..3040857 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ build-libgit2: test: build-libgit2 go run script/check-MakeGitError-thread-lock.go - go test ./... + ./script/with-static.sh go test ./... install: build-libgit2 - go install ./... + ./script/with-static.sh go install ./... diff --git a/git.go b/git.go index 6a5562a..89bd561 100644 --- a/git.go +++ b/git.go @@ -1,8 +1,6 @@ package git /* -#cgo pkg-config: --static --define-variable=libdir=vendor/libgit2/build --define-variable=includedir=vendor/libgit2/include vendor/libgit2/build/libgit2.pc -#cgo LDFLAGS: -lgit2 #include */ import "C" diff --git a/script/build-libgit2-static.sh b/script/build-libgit2-static.sh index 0a6c39f..5723721 100755 --- a/script/build-libgit2-static.sh +++ b/script/build-libgit2-static.sh @@ -4,7 +4,7 @@ set -ex VENDORED_PATH=vendor/libgit2 -cd "$VENDORED_PATH" && +cd $VENDORED_PATH && mkdir -p install/lib && mkdir -p build && cd build && @@ -13,7 +13,7 @@ cmake -DTHREADSAFE=ON \ -DBUILD_SHARED_LIBS=OFF \ -DCMAKE_C_FLAGS=-fPIC \ -DCMAKE_BUILD_TYPE="RelWithDebInfo" \ - -DCMAKE_INSTALL_PREFIX=. \ + -DCMAKE_INSTALL_PREFIX=../install \ .. && cmake --build . diff --git a/script/with-static.sh b/script/with-static.sh new file mode 100755 index 0000000..3f60e31 --- /dev/null +++ b/script/with-static.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +set -ex + +export BUILD="$PWD/vendor/libgit2/build" +export PCFILE="$BUILD/libgit2.pc" + +FLAGS=$(pkg-config --static --libs $PCFILE) || exit 1 +export CGO_LDFLAGS="$BUILD/libgit2.a -L$BUILD ${FLAGS}" +export CGO_CFLAGS="-I$PWD/vendor/libgit2/include" + +$@ -- 2.45.2 From 18aea4bfe89b83c5e2d6d55daa68efa6180655cc Mon Sep 17 00:00:00 2001 From: Henning Perl Date: Sun, 28 Dec 2014 23:07:33 +0100 Subject: [PATCH 24/41] Add git_diff_get_stats() This commit adds git_diff_get_stats() as well as functions to query the stats for insertions, deletions, and changed files. --- diff.go | 40 ++++++++++++++++++++++++++++++++++++++++ diff_test.go | 14 ++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/diff.go b/diff.go index 63f3453..63fa867 100644 --- a/diff.go +++ b/diff.go @@ -190,6 +190,46 @@ func (diff *Diff) FindSimilar(opts *DiffFindOptions) error { return nil } +type DiffStats struct { + ptr *C.git_diff_stats +} + +func (stats *DiffStats) Free() error { + if stats.ptr == nil { + return ErrInvalid + } + runtime.SetFinalizer(stats, nil) + C.git_diff_stats_free(stats.ptr) + stats.ptr = nil + return nil +} + +func (stats *DiffStats) Insertions() int { + return int(C.git_diff_stats_insertions(stats.ptr)) +} + +func (stats *DiffStats) Deletions() int { + return int(C.git_diff_stats_deletions(stats.ptr)) +} + +func (stats *DiffStats) FilesChanged() int { + return int(C.git_diff_stats_files_changed(stats.ptr)) +} + +func (diff *Diff) Stats() (*DiffStats, error) { + stats := new(DiffStats) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if ecode := C.git_diff_get_stats(&stats.ptr, diff.ptr); ecode < 0 { + return nil, MakeGitError(ecode) + } + runtime.SetFinalizer(stats, (*DiffStats).Free) + + return stats, nil +} + type diffForEachData struct { FileCallback DiffForEachFileCallback HunkCallback DiffForEachHunkCallback diff --git a/diff_test.go b/diff_test.go index 7c54f4e..fc6fed9 100644 --- a/diff_test.go +++ b/diff_test.go @@ -148,6 +148,20 @@ func TestDiffTreeToTree(t *testing.T) { if want1, want2 := "x1/README", "y1/README"; !strings.Contains(patches[0], want1) || !strings.Contains(patches[0], want2) { t.Errorf("Diff patch doesn't contain %q or %q\n\n%s", want1, want2, patches[0]) + + } + + stats, err := diff.Stats() + checkFatal(t, err) + + if stats.Insertions() != 1 { + t.Fatal("Incorrect number of insertions in diff") + } + if stats.Deletions() != 1 { + t.Fatal("Incorrect number of deletions in diff") + } + if stats.FilesChanged() != 1 { + t.Fatal("Incorrect number of changed files in diff") } errTest := errors.New("test error") -- 2.45.2 From d57246fb74404ed8b18d0d26d2b02b326ffd2cbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 4 Jan 2015 17:05:11 +0000 Subject: [PATCH 25/41] Update to libgit2 master This gets rid of the Push object. All network now goes through the Remote object. --- git.go | 1 + push.go | 169 ------------------------------------------------- push_test.go | 47 -------------- remote.go | 5 +- repository.go | 4 +- vendor/libgit2 | 2 +- wrapper.c | 20 +----- 7 files changed, 11 insertions(+), 237 deletions(-) delete mode 100644 push.go diff --git a/git.go b/git.go index 89bd561..8e78710 100644 --- a/git.go +++ b/git.go @@ -2,6 +2,7 @@ package git /* #include +#include */ import "C" import ( diff --git a/push.go b/push.go deleted file mode 100644 index f5299c4..0000000 --- a/push.go +++ /dev/null @@ -1,169 +0,0 @@ -package git - -/* -#include - -int _go_git_push_status_foreach(git_push *push, void *data); -int _go_git_push_set_callbacks(git_push *push, void *packbuilder_progress_data, void *transfer_progress_data); - -*/ -import "C" -import ( - "runtime" - "unsafe" -) - -type Push struct { - ptr *C.git_push - - packbuilderProgress *PackbuilderProgressCallback - transferProgress *PushTransferProgressCallback -} - -func newPushFromC(cpush *C.git_push) *Push { - p := &Push{ptr: cpush} - runtime.SetFinalizer(p, (*Push).Free) - return p -} - -func (p *Push) Free() { - runtime.SetFinalizer(p, nil) - C.git_push_free(p.ptr) -} - -// This class is deprecated. Please use Remote.Push() instead -func (remote *Remote) NewPush() (*Push, error) { - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - var cpush *C.git_push - ret := C.git_push_new(&cpush, remote.ptr) - if ret < 0 { - return nil, MakeGitError(ret) - } - return newPushFromC(cpush), nil -} - -func (p *Push) Finish() error { - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_push_finish(p.ptr) - if ret < 0 { - return MakeGitError(ret) - } - return nil -} - -func (p *Push) UpdateTips(sig *Signature, msg string) error { - - var csig *C.git_signature = nil - if sig != nil { - csig = sig.toC() - defer C.free(unsafe.Pointer(csig)) - } - - var cmsg *C.char - if msg == "" { - cmsg = nil - } else { - cmsg = C.CString(msg) - defer C.free(unsafe.Pointer(cmsg)) - } - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_push_update_tips(p.ptr, csig, cmsg) - if ret < 0 { - return MakeGitError(ret) - } - return nil -} - -func (p *Push) AddRefspec(refspec string) error { - - crefspec := C.CString(refspec) - defer C.free(unsafe.Pointer(crefspec)) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_push_add_refspec(p.ptr, crefspec) - if ret < 0 { - return MakeGitError(ret) - } - return nil -} - -type PushOptions struct { - Version uint - PbParallelism uint -} - -func (p *Push) SetOptions(opts PushOptions) error { - copts := C.git_push_options{version: C.uint(opts.Version), pb_parallelism: C.uint(opts.PbParallelism)} - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_push_set_options(p.ptr, &copts) - if ret < 0 { - return MakeGitError(ret) - } - return nil -} - -type StatusForeachFunc func(ref string, msg string) int - -//export statusForeach -func statusForeach(_ref *C.char, _msg *C.char, _data unsafe.Pointer) C.int { - ref := C.GoString(_ref) - msg := C.GoString(_msg) - - cb := (*StatusForeachFunc)(_data) - - return C.int((*cb)(ref, msg)) -} - -func (p *Push) StatusForeach(callback StatusForeachFunc) error { - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C._go_git_push_status_foreach(p.ptr, unsafe.Pointer(&callback)) - if ret < 0 { - return MakeGitError(ret) - } - return nil - -} - -type PushCallbacks struct { - PackbuilderProgress *PackbuilderProgressCallback - TransferProgress *PushTransferProgressCallback -} - -//export packbuilderProgress -func packbuilderProgress(stage C.int, current C.uint, total C.uint, data unsafe.Pointer) C.int { - return C.int((*(*PackbuilderProgressCallback)(data))(int32(stage), uint32(current), uint32(total))) -} - -//export pushStructTransferProgress -func pushStructTransferProgress(current C.uint, total C.uint, bytes C.size_t, data unsafe.Pointer) C.int { - return C.int((*(*PushTransferProgressCallback)(data))(uint32(current), uint32(total), uint(bytes))) -} - -func (p *Push) SetCallbacks(callbacks PushCallbacks) { - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - // save callbacks so they don't get GC'd - p.packbuilderProgress = callbacks.PackbuilderProgress - p.transferProgress = callbacks.TransferProgress - - C._go_git_push_set_callbacks(p.ptr, unsafe.Pointer(p.packbuilderProgress), unsafe.Pointer(p.transferProgress)) -} diff --git a/push_test.go b/push_test.go index 3911875..cd708c6 100644 --- a/push_test.go +++ b/push_test.go @@ -3,55 +3,8 @@ package git import ( "os" "testing" - "time" ) -func Test_Push_ToRemote(t *testing.T) { - repo := createBareTestRepo(t) - defer os.RemoveAll(repo.Path()) - repo2 := createTestRepo(t) - defer os.RemoveAll(repo2.Workdir()) - - remote, err := repo2.CreateRemote("test_push", repo.Path()) - checkFatal(t, err) - - index, err := repo2.Index() - checkFatal(t, err) - - index.AddByPath("README") - - err = index.Write() - checkFatal(t, err) - - newTreeId, err := index.WriteTree() - checkFatal(t, err) - - tree, err := repo2.LookupTree(newTreeId) - checkFatal(t, err) - - sig := &Signature{Name: "Rand Om Hacker", Email: "random@hacker.com", When: time.Now()} - // this should cause master branch to be created if it does not already exist - _, err = repo2.CreateCommit("HEAD", sig, sig, "message", tree) - checkFatal(t, err) - - push, err := remote.NewPush() - checkFatal(t, err) - - err = push.AddRefspec("refs/heads/master") - checkFatal(t, err) - - err = push.Finish() - checkFatal(t, err) - - err = push.StatusForeach(func(ref string, msg string) int { - return 0 - }) - checkFatal(t, err) - - defer remote.Free() - defer repo.Free() -} - func TestRemotePush(t *testing.T) { repo := createBareTestRepo(t) defer os.RemoveAll(repo.Path()) diff --git a/remote.go b/remote.go index 563aba5..3dc22a1 100644 --- a/remote.go +++ b/remote.go @@ -108,6 +108,10 @@ type HostkeyCertificate struct { HashSHA1 [20]byte } +type PushOptions struct { + PbParallelism uint +} + type RemoteHead struct { Id *Oid Name string @@ -710,7 +714,6 @@ func (o *Remote) Push(refspecs []string, opts *PushOptions, sig *Signature, msg var copts C.git_push_options C.git_push_init_options(&copts, C.GIT_PUSH_OPTIONS_VERSION) if opts != nil { - copts.version = C.uint(opts.Version) copts.pb_parallelism = C.uint(opts.PbParallelism) } diff --git a/repository.go b/repository.go index b9fb4b5..222ede3 100644 --- a/repository.go +++ b/repository.go @@ -456,7 +456,7 @@ func (v *Repository) TreeBuilder() (*TreeBuilder, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() - if ret := C.git_treebuilder_create(&bld.ptr, v.ptr, nil); ret < 0 { + if ret := C.git_treebuilder_new(&bld.ptr, v.ptr, nil); ret < 0 { return nil, MakeGitError(ret) } runtime.SetFinalizer(bld, (*TreeBuilder).Free) @@ -471,7 +471,7 @@ func (v *Repository) TreeBuilderFromTree(tree *Tree) (*TreeBuilder, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() - if ret := C.git_treebuilder_create(&bld.ptr, v.ptr, tree.cast_ptr); ret < 0 { + if ret := C.git_treebuilder_new(&bld.ptr, v.ptr, tree.cast_ptr); ret < 0 { return nil, MakeGitError(ret) } runtime.SetFinalizer(bld, (*TreeBuilder).Free) diff --git a/vendor/libgit2 b/vendor/libgit2 index 247b3f4..55d9c29 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit 247b3f4ee5d08ee5f5f88eb1c0f6ed03cbd21fc9 +Subproject commit 55d9c29aa0c69cdd766c5100fc012d8e0b486e23 diff --git a/wrapper.c b/wrapper.c index 3779653..938fd17 100644 --- a/wrapper.c +++ b/wrapper.c @@ -1,9 +1,7 @@ #include "_cgo_export.h" -#include "git2.h" -#include "git2/sys/odb_backend.h" -#include "git2/sys/refdb_backend.h" -#include "git2/submodule.h" -#include "git2/pack.h" +#include +#include +#include typedef int (*gogit_submodule_cbk)(git_submodule *sm, const char *name, void *payload); @@ -84,18 +82,6 @@ void _go_git_setup_callbacks(git_remote_callbacks *callbacks) { callbacks->push_update_reference = (push_update_reference_cb) pushUpdateReferenceCallback; } -typedef int (*status_foreach_cb)(const char *ref, const char *msg, void *data); - -int _go_git_push_status_foreach(git_push *push, void *data) -{ - return git_push_status_foreach(push, (status_foreach_cb)statusForeach, data); -} - -int _go_git_push_set_callbacks(git_push *push, void *packbuilder_progress_data, void *transfer_progress_data) -{ - return git_push_set_callbacks(push, packbuilderProgress, packbuilder_progress_data, pushStructTransferProgress, transfer_progress_data); -} - int _go_blob_chunk_cb(char *buffer, size_t maxLen, void *payload) { return blobChunkCb(buffer, maxLen, payload); -- 2.45.2 From dbddb88a8cf1302049bcfaccf25aba21b2224f10 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 5 Jan 2015 11:58:36 -0800 Subject: [PATCH 26/41] Add prune methods to Remote. --- remote.go | 24 +++++++++++---- remote_test.go | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 6 deletions(-) diff --git a/remote.go b/remote.go index 3dc22a1..2684c20 100644 --- a/remote.go +++ b/remote.go @@ -69,7 +69,6 @@ type RemoteCallbacks struct { PushUpdateReferenceCallback } - type Remote struct { ptr *C.git_remote callbacks RemoteCallbacks @@ -259,8 +258,6 @@ func pushUpdateReferenceCallback(refname, status *C.char, data unsafe.Pointer) i return int(callbacks.PushUpdateReferenceCallback(C.GoString(refname), C.GoString(status))) } - - func RemoteIsValidName(name string) bool { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) @@ -330,7 +327,7 @@ func (repo *Repository) CreateRemote(name string, url string) (*Remote, error) { func (repo *Repository) DeleteRemote(name string) error { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) - + runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -722,8 +719,8 @@ func (o *Remote) Push(refspecs []string, opts *PushOptions, sig *Signature, msg crefspecs.strings = makeCStringsFromStrings(refspecs) defer freeStrarray(&crefspecs) - runtime.LockOSThread() - defer runtime.UnlockOSThread() + runtime.LockOSThread() + defer runtime.UnlockOSThread() ret := C.git_remote_push(o.ptr, &crefspecs, &copts, csig, cmsg) if ret < 0 { @@ -731,3 +728,18 @@ func (o *Remote) Push(refspecs []string, opts *PushOptions, sig *Signature, msg } return nil } + +func (o *Remote) PruneRefs() bool { + return C.git_remote_prune_refs(o.ptr) > 0 +} + +func (o *Remote) Prune() error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_prune(o.ptr) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} diff --git a/remote_test.go b/remote_test.go index 631a6cd..54a66ed 100644 --- a/remote_test.go +++ b/remote_test.go @@ -1,8 +1,10 @@ package git import ( + "fmt" "os" "testing" + "time" ) func TestRefspecs(t *testing.T) { @@ -132,3 +134,84 @@ func TestRemoteLsFiltering(t *testing.T) { t.Fatalf("Expected head to have a name, but it's empty") } } + +func TestRemotePruneRefs(t *testing.T) { + repo := createTestRepo(t) + defer os.RemoveAll(repo.Workdir()) + defer repo.Free() + + config, err := repo.Config() + checkFatal(t, err) + defer config.Free() + + err = config.SetBool("remote.origin.prune", true) + checkFatal(t, err) + + _, err = repo.CreateRemote("origin", "https://github.com/libgit2/TestGitRepository") + checkFatal(t, err) + + remote, err := repo.LookupRemote("origin") + checkFatal(t, err) + + if !remote.PruneRefs() { + t.Fatal("Expected remote to be configured to prune references") + } +} + +func TestRemotePrune(t *testing.T) { + remoteRepo := createTestRepo(t) + defer os.RemoveAll(remoteRepo.Workdir()) + defer remoteRepo.Free() + + head, _ := seedTestRepo(t, remoteRepo) + commit, err := remoteRepo.LookupCommit(head) + checkFatal(t, err) + defer commit.Free() + + sig := &Signature{ + Name: "Rand Om Hacker", + Email: "random@hacker.com", + When: time.Now(), + } + + remoteRef, err := remoteRepo.CreateBranch("test-prune", commit, true, sig, "branch test-prune") + checkFatal(t, err) + + repo := createTestRepo(t) + defer os.RemoveAll(repo.Workdir()) + defer repo.Free() + + config, err := repo.Config() + checkFatal(t, err) + defer config.Free() + + remoteUrl := fmt.Sprintf("file://%s", remoteRepo.Workdir()) + remote, err := repo.CreateRemote("origin", remoteUrl) + checkFatal(t, err) + + err = remote.Fetch([]string{"test-prune"}, sig, "") + checkFatal(t, err) + + _, err = repo.CreateReference("refs/remotes/origin/test-prune", head, true, sig, "remote reference") + checkFatal(t, err) + + err = remoteRef.Delete() + checkFatal(t, err) + + err = config.SetBool("remote.origin.prune", true) + checkFatal(t, err) + + rr, err := repo.LookupRemote("origin") + checkFatal(t, err) + + err = rr.ConnectFetch() + checkFatal(t, err) + + err = rr.Prune() + checkFatal(t, err) + + _, err = repo.LookupReference("refs/remotes/origin/test-prune") + if err == nil { + t.Fatal("Expected error getting a pruned reference") + } +} -- 2.45.2 From 8adbc08d70642e2d281654a9122b6c5ecfe1d2a7 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Tue, 6 Jan 2015 14:02:44 -0800 Subject: [PATCH 27/41] Fix inconsistent function call in Submodule. --- submodule.go | 21 +++++++++++++++------ vendor/libgit2 | 2 +- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/submodule.go b/submodule.go index d5ab69f..bb86a0a 100644 --- a/submodule.go +++ b/submodule.go @@ -11,6 +11,15 @@ import ( "unsafe" ) +// SubmoduleUpdateOptions +type SubmoduleUpdateOptions struct { + *CheckoutOpts + *RemoteCallbacks + Version uint64 + CloneCheckoutStrategy CheckoutStrategy + Signature *Signature +} + // Submodule type Submodule struct { ptr *C.git_submodule @@ -20,10 +29,10 @@ type SubmoduleUpdate int const ( SubmoduleUpdateReset SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_RESET - SubmoduleUpdateCheckout SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_CHECKOUT - SubmoduleUpdateRebase SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_REBASE - SubmoduleUpdateMerge SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_MERGE - SubmoduleUpdateNone SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_NONE + SubmoduleUpdateCheckout SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_CHECKOUT + SubmoduleUpdateRebase SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_REBASE + SubmoduleUpdateMerge SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_MERGE + SubmoduleUpdateNone SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_NONE ) type SubmoduleIgnore int @@ -226,8 +235,8 @@ func (sub *Submodule) SetIgnore(ignore SubmoduleIgnore) SubmoduleIgnore { return SubmoduleIgnore(o) } -func (sub *Submodule) Update() SubmoduleUpdate { - o := C.git_submodule_update(sub.ptr) +func (sub *Submodule) UpdateStrategy() SubmoduleUpdate { + o := C.git_submodule_update_strategy(sub.ptr) return SubmoduleUpdate(o) } diff --git a/vendor/libgit2 b/vendor/libgit2 index 55d9c29..007f3ff 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit 55d9c29aa0c69cdd766c5100fc012d8e0b486e23 +Subproject commit 007f3ff6fa68a95feee4e70f825a49ea0ec9cb2d -- 2.45.2 From 4989fc5a15123debf82b2c1232cf511ec9ee4c15 Mon Sep 17 00:00:00 2001 From: Ben Burkert Date: Tue, 6 Jan 2015 10:21:33 -0800 Subject: [PATCH 28/41] Add git note support --- note.go | 99 +++++++++++++++++++++++++++++++++++++++++++ note_test.go | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++ repository.go | 100 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 312 insertions(+) create mode 100644 note.go create mode 100644 note_test.go diff --git a/note.go b/note.go new file mode 100644 index 0000000..c8d9248 --- /dev/null +++ b/note.go @@ -0,0 +1,99 @@ +package git + +/* +#include +*/ +import "C" + +import ( + "runtime" + "unsafe" +) + +// Note +type Note struct { + ptr *C.git_note +} + +// Free frees a git_note object +func (n *Note) Free() error { + if n.ptr == nil { + return ErrInvalid + } + runtime.SetFinalizer(n, nil) + C.git_note_free(n.ptr) + n.ptr = nil + return nil +} + +// Author returns the signature of the note author +func (n Note) Author() *Signature { + ptr := C.git_note_author(n.ptr) + return newSignatureFromC(ptr) +} + +// Id returns the note object's id +func (n Note) Id() *Oid { + ptr := C.git_note_id(n.ptr) + return newOidFromC(ptr) +} + +// Committer returns the signature of the note committer +func (n Note) Committer() *Signature { + ptr := C.git_note_committer(n.ptr) + return newSignatureFromC(ptr) +} + +// Message returns the note message +func (n Note) Message() string { + return C.GoString(C.git_note_message(n.ptr)) +} + +// NoteIterator +type NoteIterator struct { + ptr *C.git_note_iterator +} + +// NewNoteIterator creates a new iterator for notes +func (repo *Repository) NewNoteIterator(ref string) (*NoteIterator, error) { + var cref *C.char + if ref == "" { + cref = nil + } else { + cref = C.CString(ref) + defer C.free(unsafe.Pointer(cref)) + } + + var ptr *C.git_note_iterator + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if ret := C.git_note_iterator_new(&ptr, repo.ptr, cref); ret < 0 { + return nil, MakeGitError(ret) + } + + iter := &NoteIterator{ptr: ptr} + runtime.SetFinalizer(iter, (*NoteIterator).Free) + return iter, nil +} + +// Free frees the note interator +func (v *NoteIterator) Free() { + runtime.SetFinalizer(v, nil) + C.git_note_iterator_free(v.ptr) +} + +// Next returns the current item (note id & annotated id) and advances the +// iterator internally to the next item +func (it *NoteIterator) Next() (noteId, annotatedId *Oid, err error) { + noteId, annotatedId = new(Oid), new(Oid) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if ret := C.git_note_next(noteId.toC(), annotatedId.toC(), it.ptr); ret < 0 { + err = MakeGitError(ret) + } + return +} diff --git a/note_test.go b/note_test.go new file mode 100644 index 0000000..f5e9c01 --- /dev/null +++ b/note_test.go @@ -0,0 +1,113 @@ +package git + +import ( + "fmt" + "os" + "reflect" + "testing" + "time" +) + +func TestCreateNote(t *testing.T) { + repo := createTestRepo(t) + defer os.RemoveAll(repo.Workdir()) + + commitId, _ := seedTestRepo(t, repo) + + commit, err := repo.LookupCommit(commitId) + checkFatal(t, err) + + note, noteId := createTestNote(t, repo, commit) + + compareStrings(t, "I am a note\n", note.Message()) + compareStrings(t, noteId.String(), note.Id().String()) + compareStrings(t, "alice", note.Author().Name) + compareStrings(t, "alice@example.com", note.Author().Email) + compareStrings(t, "alice", note.Committer().Name) + compareStrings(t, "alice@example.com", note.Committer().Email) +} + +func TestNoteIterator(t *testing.T) { + repo := createTestRepo(t) + defer os.RemoveAll(repo.Workdir()) + seedTestRepo(t, repo) + + notes := make([]*Note, 5) + for i := range notes { + commitId, _ := updateReadme(t, repo, fmt.Sprintf("README v%d\n", i+1)) + commit, err := repo.LookupCommit(commitId) + checkFatal(t, err) + + note, _ := createTestNote(t, repo, commit) + notes[i] = note + } + + iter, err := repo.NewNoteIterator("") + checkFatal(t, err) + for { + noteId, commitId, err := iter.Next() + if err != nil { + if !IsErrorCode(err, ErrIterOver) { + checkFatal(t, err) + } + break + } + + note, err := repo.ReadNote("", commitId) + checkFatal(t, err) + + if !reflect.DeepEqual(note.Id(), noteId) { + t.Errorf("expected note oid '%v', actual '%v'", note.Id(), noteId) + } + } +} + +func TestRemoveNote(t *testing.T) { + repo := createTestRepo(t) + defer os.RemoveAll(repo.Workdir()) + + commitId, _ := seedTestRepo(t, repo) + + commit, err := repo.LookupCommit(commitId) + checkFatal(t, err) + + note, _ := createTestNote(t, repo, commit) + + _, err = repo.ReadNote("", commit.Id()) + checkFatal(t, err) + + err = repo.RemoveNote("", note.Author(), note.Committer(), commitId) + checkFatal(t, err) + + _, err = repo.ReadNote("", commit.Id()) + if err == nil { + t.Fatal("note remove failed") + } +} + +func TestDefaultNoteRef(t *testing.T) { + repo := createTestRepo(t) + defer os.RemoveAll(repo.Workdir()) + + ref, err := repo.DefaultNoteRef() + checkFatal(t, err) + + compareStrings(t, "refs/notes/commits", ref) +} + +func createTestNote(t *testing.T, repo *Repository, commit *Commit) (*Note, *Oid) { + loc, err := time.LoadLocation("Europe/Berlin") + sig := &Signature{ + Name: "alice", + Email: "alice@example.com", + When: time.Date(2015, 01, 05, 13, 0, 0, 0, loc), + } + + noteId, err := repo.CreateNote("", sig, sig, commit.Id(), "I am a note\n", false) + checkFatal(t, err) + + note, err := repo.ReadNote("", commit.Id()) + checkFatal(t, err) + + return note, noteId +} diff --git a/repository.go b/repository.go index 222ede3..7760c3a 100644 --- a/repository.go +++ b/repository.go @@ -530,3 +530,103 @@ func (v *Repository) DwimReference(name string) (*Reference, error) { return newReferenceFromC(ptr, v), nil } + +// CreateNote adds a note for an object +func (v *Repository) CreateNote( + ref string, author, committer *Signature, id *Oid, + note string, force bool) (*Oid, error) { + + oid := new(Oid) + + var cref *C.char + if ref == "" { + cref = nil + } else { + cref = C.CString(ref) + defer C.free(unsafe.Pointer(cref)) + } + + authorSig := author.toC() + defer C.git_signature_free(authorSig) + + committerSig := committer.toC() + defer C.git_signature_free(committerSig) + + cnote := C.CString(note) + defer C.free(unsafe.Pointer(cnote)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_note_create( + oid.toC(), v.ptr, cref, authorSig, + committerSig, id.toC(), cnote, cbool(force)) + + if ret < 0 { + return nil, MakeGitError(ret) + } + return oid, nil +} + +// ReadNote reads the note for an object +func (v *Repository) ReadNote(ref string, id *Oid) (*Note, error) { + var cref *C.char + if ref == "" { + cref = nil + } else { + cref = C.CString(ref) + defer C.free(unsafe.Pointer(cref)) + } + + note := new(Note) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if ret := C.git_note_read(¬e.ptr, v.ptr, cref, id.toC()); ret < 0 { + return nil, MakeGitError(ret) + } + + runtime.SetFinalizer(note, (*Note).Free) + return note, nil +} + +// RemoveNote removes the note for an object +func (v *Repository) RemoveNote(ref string, author, committer *Signature, id *Oid) error { + var cref *C.char + if ref == "" { + cref = nil + } else { + cref = C.CString(ref) + defer C.free(unsafe.Pointer(cref)) + } + + authorSig := author.toC() + defer C.git_signature_free(authorSig) + + committerSig := committer.toC() + defer C.git_signature_free(committerSig) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_note_remove(v.ptr, cref, authorSig, committerSig, id.toC()) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +// DefaultNoteRef returns the default notes reference for a repository +func (v *Repository) DefaultNoteRef() (string, error) { + var ptr *C.char + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if ret := C.git_note_default_ref(&ptr, v.ptr); ret < 0 { + return "", MakeGitError(ret) + } + + return C.GoString(ptr), nil +} -- 2.45.2 From 04e3c7f6cd965baca20309439abbbd5986c73e86 Mon Sep 17 00:00:00 2001 From: Ben Burkert Date: Thu, 8 Jan 2015 11:03:15 -0800 Subject: [PATCH 29/41] define Note methods on pointers --- note.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/note.go b/note.go index c8d9248..3cdd340 100644 --- a/note.go +++ b/note.go @@ -27,25 +27,25 @@ func (n *Note) Free() error { } // Author returns the signature of the note author -func (n Note) Author() *Signature { +func (n *Note) Author() *Signature { ptr := C.git_note_author(n.ptr) return newSignatureFromC(ptr) } // Id returns the note object's id -func (n Note) Id() *Oid { +func (n *Note) Id() *Oid { ptr := C.git_note_id(n.ptr) return newOidFromC(ptr) } // Committer returns the signature of the note committer -func (n Note) Committer() *Signature { +func (n *Note) Committer() *Signature { ptr := C.git_note_committer(n.ptr) return newSignatureFromC(ptr) } // Message returns the note message -func (n Note) Message() string { +func (n *Note) Message() string { return C.GoString(C.git_note_message(n.ptr)) } -- 2.45.2 From 9b914e07cc314daa42e61807b23350b2571f643f Mon Sep 17 00:00:00 2001 From: David Calavera Date: Thu, 8 Jan 2015 13:44:58 -0800 Subject: [PATCH 30/41] Add Submodule.Update method. Update libgit2 to a version that includes https://github.com/libgit2/libgit2/pull/2804. --- submodule.go | 30 ++++++++++++++++++++++++++++++ vendor/libgit2 | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/submodule.go b/submodule.go index bb86a0a..67e4fab 100644 --- a/submodule.go +++ b/submodule.go @@ -316,3 +316,33 @@ func (repo *Repository) ReloadAllSubmodules(force bool) error { } return nil } + +func (sub *Submodule) Update(init bool, opts *SubmoduleUpdateOptions) error { + var copts C.git_submodule_update_options + populateSubmoduleUpdateOptions(&copts, opts) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_submodule_update(sub.ptr, cbool(init), &copts) + if ret < 0 { + return MakeGitError(ret) + } + + return nil +} + +func populateSubmoduleUpdateOptions(ptr *C.git_submodule_update_options, opts *SubmoduleUpdateOptions) { + C.git_submodule_update_init_options(ptr, C.GIT_SUBMODULE_UPDATE_OPTIONS_VERSION) + + if opts == nil { + return + } + + populateCheckoutOpts(&ptr.checkout_opts, opts.CheckoutOpts) + populateRemoteCallbacks(&ptr.remote_callbacks, opts.RemoteCallbacks) + ptr.clone_checkout_strategy = C.uint(opts.CloneCheckoutStrategy) + if opts.Signature != nil { + ptr.signature = opts.Signature.toC() + } +} diff --git a/vendor/libgit2 b/vendor/libgit2 index 007f3ff..1646412 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit 007f3ff6fa68a95feee4e70f825a49ea0ec9cb2d +Subproject commit 1646412d8fc9e1532a194df2515e9a5fde4da988 -- 2.45.2 From 22f4a4edaa0c45979081993b92873a45d443e171 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Thu, 8 Jan 2015 14:21:29 -0800 Subject: [PATCH 31/41] Do not double check if the signature is nil. --- submodule.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/submodule.go b/submodule.go index 67e4fab..20fcbe9 100644 --- a/submodule.go +++ b/submodule.go @@ -342,7 +342,5 @@ func populateSubmoduleUpdateOptions(ptr *C.git_submodule_update_options, opts *S populateCheckoutOpts(&ptr.checkout_opts, opts.CheckoutOpts) populateRemoteCallbacks(&ptr.remote_callbacks, opts.RemoteCallbacks) ptr.clone_checkout_strategy = C.uint(opts.CloneCheckoutStrategy) - if opts.Signature != nil { - ptr.signature = opts.Signature.toC() - } + ptr.signature = opts.Signature.toC() } -- 2.45.2 From ea7f567756fbe5060ad830dc5fcf3cea2038c425 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Thu, 8 Jan 2015 15:06:35 -0800 Subject: [PATCH 32/41] Remove version from opts structure. It's not necessary. --- submodule.go | 1 - 1 file changed, 1 deletion(-) diff --git a/submodule.go b/submodule.go index 20fcbe9..f3e9e83 100644 --- a/submodule.go +++ b/submodule.go @@ -15,7 +15,6 @@ import ( type SubmoduleUpdateOptions struct { *CheckoutOpts *RemoteCallbacks - Version uint64 CloneCheckoutStrategy CheckoutStrategy Signature *Signature } -- 2.45.2 From 62272c41c6a85bbc1cef1e15751f5a3cf993d31b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 12 Jan 2015 18:01:43 +0100 Subject: [PATCH 33/41] Update vendored libgit2 --- vendor/libgit2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/libgit2 b/vendor/libgit2 index 1646412..04bdd97 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit 1646412d8fc9e1532a194df2515e9a5fde4da988 +Subproject commit 04bdd97f2b63793a8720fd19007911e946ba3c55 -- 2.45.2 From 57412d0293fa4d84bab60590aa9e4589f706ff0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 14 Jan 2015 14:11:19 +0100 Subject: [PATCH 34/41] Explain the difference between dynamic and static versions With the release of libgit2 v0.22 we can link against a version of library we've wrapped in more than a PoC sense. Explain the difference and say how to use each version. --- README.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9995707..faeb5f5 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,24 @@ git2go [![GoDoc](https://godoc.org/github.com/libgit2/git2go?status.svg)](http://godoc.org/github.com/libgit2/git2go) [![Build Status](https://travis-ci.org/libgit2/git2go.svg?branch=master)](https://travis-ci.org/libgit2/git2go) -Go bindings for [libgit2](http://libgit2.github.com/). The master branch follows the latest libgit2 release. +Go bindings for [libgit2](http://libgit2.github.com/). The master branch follows the latest libgit2 release. The versioned branches indicate which libgit2 version they work against. Installing ---------- -This project needs libgit2, which is written in C so we need to build that as well. In order to build libgit2, you need `cmake`, `pkg-config` and a C compiler. You will also need the development packages for OpenSSL and LibSSH2 if you want to use HTTPS and SSH respectively. +This project needs libgit2, which is written in C so we need to build that as well. In order to build libgit2, you need `cmake`, `pkg-config` and a C compiler. You will also need the development packages for OpenSSL and LibSSH2 installed if you want libgit2 to support HTTPS and SSH respectively. + +### Stable version + +git2go has versioned branches which indicate which version of libgit2 they work against. Install the development package it on your system via your favourite package manager or from source and you can use a service like gopkg.in to use the appropriate version. For the libgit2 v0.22 case, you can use + + import "gopkg.in/libgit2/git2go.v22" + +to use a version of git2go which will work against libgit2 v0.22 and dynamically link to the library. + +### From master + +The master branch follows libgit2's master branch, which means there is no stable API or ABI to link against. git2go can statically link against a vendored version of libgit2. Run `go get -d github.com/libgit2/git2go` to download the code and go to your `$GOPATH/src/github.com/libgit2/git2go` dir. From there, we need to build the C code and put it into the resulting go binary. @@ -25,7 +37,7 @@ libgit2 uses OpenSSL and LibSSH2 for performing encrypted network connections. F Running the tests ----------------- -Similarly to installing, running the tests requires linking against the local libgit2 library, so the Makefile provides a wrapper +For the stable version, `go test` will work as usual. For the master branch, similarly to installing, running the tests requires linking against the local libgit2 library, so the Makefile provides a wrapper make test -- 2.45.2 From 548bb9b5e9161fe876eeb3c23ceae061840cb532 Mon Sep 17 00:00:00 2001 From: Tarrant Rollins Date: Tue, 13 Jan 2015 19:48:45 -0800 Subject: [PATCH 35/41] Add Go functions for git_config_find_* functions ConfigFindGlobal -> git_config_find_global ConfigFindSystem -> git_config_find_system ConfigFindXDG -> git_config_find_xdg --- config.go | 52 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/config.go b/config.go index 2965a69..9d25e35 100644 --- a/config.go +++ b/config.go @@ -35,14 +35,14 @@ const ( ) type ConfigEntry struct { - Name string + Name string Value string Level ConfigLevel } func newConfigEntryFromC(centry *C.git_config_entry) *ConfigEntry { return &ConfigEntry{ - Name: C.GoString(centry.name), + Name: C.GoString(centry.name), Value: C.GoString(centry.value), Level: ConfigLevel(centry.level), } @@ -74,7 +74,6 @@ func (c *Config) AddFile(path string, level ConfigLevel, force bool) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_config_add_file_ondisk(c.ptr, cpath, C.git_config_level_t(level), cbool(force)) if ret < 0 { return MakeGitError(ret) @@ -130,7 +129,6 @@ func (c *Config) LookupString(name string) (string, error) { return C.GoString(ptr), nil } - func (c *Config) LookupBool(name string) (bool, error) { var out C.int cname := C.CString(name) @@ -234,7 +232,6 @@ func (c *Config) SetInt32(name string, value int32) (err error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) - runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -368,3 +365,48 @@ func (iter *ConfigIterator) Free() { runtime.SetFinalizer(iter, nil) C.free(unsafe.Pointer(iter.ptr)) } + +func ConfigFindGlobal() (string, error) { + var buf C.git_buf + defer C.git_buf_free(&buf) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_config_find_global(&buf) + if ret < 0 { + return "", MakeGitError(ret) + } + + return C.GoString(buf.ptr), nil +} + +func ConfigFindSystem() (string, error) { + var buf C.git_buf + defer C.git_buf_free(&buf) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_config_find_system(&buf) + if ret < 0 { + return "", MakeGitError(ret) + } + + return C.GoString(buf.ptr), nil +} + +func ConfigFindXDG() (string, error) { + var buf C.git_buf + defer C.git_buf_free(&buf) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_config_find_xdg(&buf) + if ret < 0 { + return "", MakeGitError(ret) + } + + return C.GoString(buf.ptr), nil +} -- 2.45.2 From 1107c6824f887423be59b1ae633553480304b70c Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Wed, 28 Jan 2015 15:03:58 +0100 Subject: [PATCH 36/41] Add test triggering ForeachSubmodule panic. --- submodule_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 submodule_test.go diff --git a/submodule_test.go b/submodule_test.go new file mode 100644 index 0000000..1c8f471 --- /dev/null +++ b/submodule_test.go @@ -0,0 +1,24 @@ +package git + +import ( + "testing" +) + +func TestSubmoduleForeach(t *testing.T) { + repo := createTestRepo(t) + seedTestRepo(t, repo) + + _, err := repo.AddSubmodule("http://example.org/submodule", "submodule", true) + checkFatal(t, err) + + i := 0 + err = repo.ForeachSubmodule(func(sub *Submodule, name string) int { + i++ + return 0 + }) + checkFatal(t, err) + + if i != 1 { + t.Fatalf("expected one submodule found but got %i", i) + } +} -- 2.45.2 From 2e481dbc7909beefd1afe199ba95b995819866cd Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Wed, 28 Jan 2015 14:38:42 +0100 Subject: [PATCH 37/41] Fix ForeachSubmodule panicing. As the SubmoduleVisitor function is called from inside libgit2 we cannot use Go types in its signature. Fix by using C types instead. --- submodule.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/submodule.go b/submodule.go index f3e9e83..6923c61 100644 --- a/submodule.go +++ b/submodule.go @@ -97,10 +97,10 @@ func (repo *Repository) LookupSubmodule(name string) (*Submodule, error) { type SubmoduleCbk func(sub *Submodule, name string) int //export SubmoduleVisitor -func SubmoduleVisitor(csub unsafe.Pointer, name string, cfct unsafe.Pointer) int { +func SubmoduleVisitor(csub unsafe.Pointer, name *C.char, cfct unsafe.Pointer) C.int { sub := &Submodule{(*C.git_submodule)(csub)} fct := *(*SubmoduleCbk)(cfct) - return fct(sub, name) + return (C.int)(fct(sub, C.GoString(name))) } func (repo *Repository) ForeachSubmodule(cbk SubmoduleCbk) error { -- 2.45.2 From 92a1f92d912cdb5f68da8c1e5e3a4d1ebfd282db Mon Sep 17 00:00:00 2001 From: joseferminj Date: Thu, 22 Jan 2015 00:44:51 -0500 Subject: [PATCH 38/41] Add TargetDirectory field to Checkout options. TargetDirectory field indicates a alternative checkout path to workdir. --- checkout.go | 38 +++++++++++++++++++++++++++++--------- clone.go | 1 + merge.go | 1 + 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/checkout.go b/checkout.go index 9c7188e..06d010c 100644 --- a/checkout.go +++ b/checkout.go @@ -7,6 +7,7 @@ import "C" import ( "os" "runtime" + "unsafe" ) type CheckoutStrategy uint @@ -31,11 +32,12 @@ 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 + 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 + TargetDirectory string // Alternative checkout path to workdir } func (opts *CheckoutOpts) toC() *C.git_checkout_options { @@ -60,17 +62,29 @@ func populateCheckoutOpts(ptr *C.git_checkout_options, opts *CheckoutOpts) *C.gi ptr.disable_filters = cbool(opts.DisableFilters) ptr.dir_mode = C.uint(opts.DirMode.Perm()) ptr.file_mode = C.uint(opts.FileMode.Perm()) - + if opts.TargetDirectory != "" { + ptr.target_directory = C.CString(opts.TargetDirectory) + } return ptr } +func freeCheckoutOpts(ptr *C.git_checkout_options) { + if ptr == nil { + return + } + C.free(unsafe.Pointer(ptr.target_directory)) +} + // Updates files in the index and the working tree to match the content of // the commit pointed at by HEAD. opts may be nil. func (v *Repository) CheckoutHead(opts *CheckoutOpts) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_checkout_head(v.ptr, opts.toC()) + cOpts := opts.toC() + defer freeCheckoutOpts(cOpts) + + ret := C.git_checkout_head(v.ptr, cOpts) if ret < 0 { return MakeGitError(ret) } @@ -90,7 +104,10 @@ func (v *Repository) CheckoutIndex(index *Index, opts *CheckoutOpts) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_checkout_index(v.ptr, iptr, opts.toC()) + cOpts := opts.toC() + defer freeCheckoutOpts(cOpts) + + ret := C.git_checkout_index(v.ptr, iptr, cOpts) if ret < 0 { return MakeGitError(ret) } @@ -102,7 +119,10 @@ func (v *Repository) CheckoutTree(tree *Tree, opts *CheckoutOpts) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_checkout_tree(v.ptr, tree.ptr, opts.toC()) + cOpts := opts.toC() + defer freeCheckoutOpts(cOpts) + + ret := C.git_checkout_tree(v.ptr, tree.ptr, cOpts) if ret < 0 { return MakeGitError(ret) } diff --git a/clone.go b/clone.go index 2e0fcce..b796b6e 100644 --- a/clone.go +++ b/clone.go @@ -30,6 +30,7 @@ func Clone(url string, path string, options *CloneOptions) (*Repository, error) var copts C.git_clone_options populateCloneOptions(&copts, options) + defer freeCheckoutOpts(&copts.checkout_opts) if len(options.CheckoutBranch) != 0 { copts.checkout_branch = C.CString(options.CheckoutBranch) diff --git a/merge.go b/merge.go index 3742f97..5b68a8b 100644 --- a/merge.go +++ b/merge.go @@ -145,6 +145,7 @@ func (r *Repository) Merge(theirHeads []*AnnotatedCommit, mergeOptions *MergeOpt 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++ { -- 2.45.2 From 3e05c1038589258b40efd4bfef4d5c387bcc73c9 Mon Sep 17 00:00:00 2001 From: motemen Date: Wed, 28 Jan 2015 20:49:05 +0900 Subject: [PATCH 39/41] Fix test to force diff prefixes. --- patch_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/patch_test.go b/patch_test.go index 569eac2..a061142 100644 --- a/patch_test.go +++ b/patch_test.go @@ -20,7 +20,11 @@ func TestPatch(t *testing.T) { newTree, err := repo.LookupTree(newTreeId) checkFatal(t, err) - diff, err := repo.DiffTreeToTree(originalTree, newTree, nil) + opts := &DiffOptions{ + OldPrefix: "a", + NewPrefix: "b", + } + diff, err := repo.DiffTreeToTree(originalTree, newTree, opts) checkFatal(t, err) patch, err := diff.Patch(0) -- 2.45.2 From c10445cd67d400b1a58ccd6ad07f63b4612f8b11 Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Wed, 11 Feb 2015 12:55:16 +0100 Subject: [PATCH 40/41] Add bindings for git_graph_* methods Add graph.go --- graph.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 graph.go diff --git a/graph.go b/graph.go new file mode 100644 index 0000000..f7cf8e9 --- /dev/null +++ b/graph.go @@ -0,0 +1,36 @@ +package git + +/* +#include +*/ +import "C" +import ( + "runtime" +) + +func (repo *Repository) GraphDescendantOf(commit, ancestor *Oid) (bool, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_graph_descendant_of(repo.ptr, commit.toC(), ancestor.toC()) + if ret < 0 { + return false, MakeGitError(ret) + } + + return (ret > 0), nil +} + +func (repo *Repository) GraphAheadBehind(local, upstream *Oid) (ahead, behind int, err error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + var aheadT C.size_t + var behindT C.size_t + + ret := C.git_graph_ahead_behind(&aheadT, &behindT, repo.ptr, local.toC(), upstream.toC()) + if ret < 0 { + return 0, 0, MakeGitError(ret) + } + + return int(aheadT), int(behindT), nil +} -- 2.45.2 From dddcbb71c45932feef3c2d0fe6fbdf375b0de644 Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Thu, 12 Feb 2015 18:49:54 +0100 Subject: [PATCH 41/41] Remove "Graph" prefix on method names --- graph.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graph.go b/graph.go index f7cf8e9..e5d7732 100644 --- a/graph.go +++ b/graph.go @@ -8,7 +8,7 @@ import ( "runtime" ) -func (repo *Repository) GraphDescendantOf(commit, ancestor *Oid) (bool, error) { +func (repo *Repository) DescendantOf(commit, ancestor *Oid) (bool, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -20,7 +20,7 @@ func (repo *Repository) GraphDescendantOf(commit, ancestor *Oid) (bool, error) { return (ret > 0), nil } -func (repo *Repository) GraphAheadBehind(local, upstream *Oid) (ahead, behind int, err error) { +func (repo *Repository) AheadBehind(local, upstream *Oid) (ahead, behind int, err error) { runtime.LockOSThread() defer runtime.UnlockOSThread() -- 2.45.2