Skip to content

Commit 4a14260

Browse files
authored
Merge pull request libgit2#362 from libgit2/cmn/master-tip-static
Update master to latest libgit2 and build statically
2 parents b8a9efd + b020c11 commit 4a14260

20 files changed

+803
-83
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "vendor/libgit2"]
2+
path = vendor/libgit2
3+
url = https://github.com/libgit2/libgit2

.travis.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@ sudo: required
55
install: ./script/install-libgit2.sh
66

77
go:
8-
- 1.1
9-
- 1.2
10-
- 1.3
11-
- 1.4
128
- 1.5
139
- 1.6
1410
- 1.7

Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
default: test
22

3-
test:
3+
build-libgit2:
4+
./script/build-libgit2-static.sh
5+
6+
test: build-libgit2
47
go run script/check-MakeGitError-thread-lock.go
58
go test ./...
69

7-
install:
10+
install: build-libgit2
811
go install ./...

README.md

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,57 +2,59 @@ git2go
22
======
33
[![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)
44

5+
Go bindings for [libgit2](http://libgit2.github.com/).
56

6-
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.
7+
### Which branch to use
78

8-
Installing
9-
----------
9+
The numbered branches work against the version of libgit2 as specified by their number. You can import them in your project via gopkg.in, e.g. if you have libgit2 v0.25 installed you'd import with
1010

11-
This project wraps the functionality provided by libgit2. If you're using a stable version, install it to your system via your system's package manager and then install git2go as usual.
11+
import "gopkg.in/libgit2/git2go.v25"
1212

13-
Otherwise (`next` which tracks an unstable version), we need to build libgit2 as well. In order to build it, 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.
13+
which will ensure there are no sudden changes to the API.
1414

15-
### Stable version
15+
The `master` branch follows the tip of libgit2 itself (with some lag) and as such has no guarantees on its own API nor does it have expectations the stability of libgit2's. Thus this only supports statically linking against libgit2.
1616

17-
git2go has `master` which tracks the latest release of libgit2, and versioned branches which indicate which version of libgit2 they work against. Install the development package 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
17+
Installing
18+
----------
1819

19-
import "gopkg.in/libgit2/git2go.v22"
20+
This project wraps the functionality provided by libgit2. It thus needs it in order to perform the work.
2021

21-
to use a version of git2go which will work against libgit2 v0.22 and dynamically link to the library. You can use
22+
This project wraps the functionality provided by libgit2. If you're using a versioned branch, install it to your system via your system's package manager and then install git2go.
2223

23-
import "github.com/libgit2/git2go"
2424

25-
to use the version which works against the latest release.
25+
### Versioned branch, dynamic linking
2626

27-
### From `next`
27+
When linking dynamically against a released version of libgit2, install it via your system's package manager. CGo will take care of finding its pkg-config file and set up the linking. Import via gopkg.in, e.g. to work against libgit2 v0.25
2828

29-
The `next` 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.
29+
import "gopkg.in/libgit2/git2go.v25"
30+
31+
### Master branch, or static linking
32+
33+
If using `master` or building a branch statically, we need to build libgit2 first. In order to build it, you need `cmake`, `pkg-config` and a C compiler. You will also need the development packages for OpenSSL (outside of Windows or macOS) and LibSSH2 installed if you want libgit2 to support HTTPS and SSH respectively. Note that even if libgit2 is included in the resulting binary, its dependencies will not be.
3034

3135
Run `go get -d github.com/libgit2/git2go` to download the code and go to your `$GOPATH/src/github.com/libgit2/git2go` directory. From there, we need to build the C code and put it into the resulting go binary.
3236

33-
git checkout next
3437
git submodule update --init # get libgit2
3538
make install
3639

37-
will compile libgit2. Run `go install` so that it's statically linked to the git2go package.
40+
will compile libgit2, link it into git2go and install it.
3841

3942
Parallelism and network operations
4043
----------------------------------
4144

42-
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.
45+
libgit2 may use 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.
4346

4447
Running the tests
4548
-----------------
4649

47-
For the stable version, `go test` will work as usual. For the `next` branch, similarly to installing, running the tests requires linking against the local libgit2 library, so the Makefile provides a wrapper
50+
For the stable version, `go test` will work as usual. For the `next` branch, similarly to installing, running the tests requires building a local libgit2 library, so the Makefile provides a wrapper that makes sure it's built
4851

4952
make test
5053

51-
Alternatively, if you want to pass arguments to `go test`, you can use the script that sets it all up
52-
53-
./script/with-static.sh go test -v
54+
Alternatively, you can build the library manually first and then run the tests
5455

55-
which will run the specified arguments with the correct environment variables.
56+
./script/build-libgit2-static.sh
57+
go test -v
5658

5759
License
5860
-------

blob.go

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@ package git
44
#include <git2.h>
55
#include <string.h>
66
7-
extern int _go_git_blob_create_fromchunks(git_oid *id,
8-
git_repository *repo,
9-
const char *hintpath,
10-
void *payload);
11-
7+
int _go_git_writestream_write(git_writestream *stream, const char *buffer, size_t len);
8+
void _go_git_writestream_free(git_writestream *stream);
129
*/
1310
import "C"
1411
import (
1512
"io"
13+
"reflect"
1614
"runtime"
1715
"unsafe"
1816
)
@@ -87,27 +85,71 @@ func blobChunkCb(buffer *C.char, maxLen C.size_t, handle unsafe.Pointer) int {
8785
return len(goBuf)
8886
}
8987

90-
func (repo *Repository) CreateBlobFromChunks(hintPath string, callback BlobChunkCallback) (*Oid, error) {
91-
runtime.LockOSThread()
92-
defer runtime.UnlockOSThread()
93-
88+
func (repo *Repository) CreateFromStream(hintPath string) (*BlobWriteStream, error) {
9489
var chintPath *C.char = nil
90+
var stream *C.git_writestream
91+
9592
if len(hintPath) > 0 {
9693
chintPath = C.CString(hintPath)
9794
defer C.free(unsafe.Pointer(chintPath))
9895
}
99-
oid := C.git_oid{}
10096

101-
payload := &BlobCallbackData{Callback: callback}
102-
handle := pointerHandles.Track(payload)
103-
defer pointerHandles.Untrack(handle)
97+
runtime.LockOSThread()
98+
defer runtime.UnlockOSThread()
99+
100+
ecode := C.git_blob_create_fromstream(&stream, repo.ptr, chintPath)
101+
if ecode < 0 {
102+
return nil, MakeGitError(ecode)
103+
}
104+
105+
return newBlobWriteStreamFromC(stream), nil
106+
}
107+
108+
type BlobWriteStream struct {
109+
ptr *C.git_writestream
110+
}
111+
112+
func newBlobWriteStreamFromC(ptr *C.git_writestream) *BlobWriteStream {
113+
stream := &BlobWriteStream{
114+
ptr: ptr,
115+
}
116+
117+
runtime.SetFinalizer(stream, (*BlobWriteStream).Free)
118+
return stream
119+
}
120+
121+
// Implement io.Writer
122+
func (stream *BlobWriteStream) Write(p []byte) (int, error) {
123+
header := (*reflect.SliceHeader)(unsafe.Pointer(&p))
124+
ptr := (*C.char)(unsafe.Pointer(header.Data))
125+
size := C.size_t(header.Len)
104126

105-
ecode := C._go_git_blob_create_fromchunks(&oid, repo.ptr, chintPath, handle)
106-
if payload.Error != nil {
107-
return nil, payload.Error
127+
runtime.LockOSThread()
128+
defer runtime.UnlockOSThread()
129+
130+
ecode := C._go_git_writestream_write(stream.ptr, ptr, size)
131+
if ecode < 0 {
132+
return 0, MakeGitError(ecode)
108133
}
134+
135+
return len(p), nil
136+
}
137+
138+
func (stream *BlobWriteStream) Free() {
139+
runtime.SetFinalizer(stream, nil)
140+
C._go_git_writestream_free(stream.ptr)
141+
}
142+
143+
func (stream *BlobWriteStream) Commit() (*Oid, error) {
144+
oid := C.git_oid{}
145+
146+
runtime.LockOSThread()
147+
defer runtime.UnlockOSThread()
148+
149+
ecode := C.git_blob_create_fromstream_commit(&oid, stream.ptr)
109150
if ecode < 0 {
110151
return nil, MakeGitError(ecode)
111152
}
153+
112154
return newOidFromC(&oid), nil
113155
}

commit.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ func (c Commit) Message() string {
2222
return C.GoString(C.git_commit_message(c.cast_ptr))
2323
}
2424

25+
func (c Commit) RawMessage() string {
26+
return C.GoString(C.git_commit_message_raw(c.cast_ptr))
27+
}
28+
2529
func (c Commit) Summary() string {
2630
return C.GoString(C.git_commit_summary(c.cast_ptr))
2731
}

describe_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,16 @@ func TestDescribeCommit(t *testing.T) {
9393
func checkDescribeNoRefsFound(t *testing.T, err error) {
9494
// The failure happens at wherever we were called, not here
9595
_, file, line, ok := runtime.Caller(1)
96+
expectedString := "no reference found, cannot describe anything"
9697
if !ok {
9798
t.Fatalf("Unable to get caller")
9899
}
99-
if err == nil || !strings.Contains(err.Error(), "No reference found, cannot describe anything") {
100+
if err == nil || !strings.Contains(err.Error(), expectedString) {
100101
t.Fatalf(
101-
"%s:%v: was expecting error 'No reference found, cannot describe anything', got %v",
102+
"%s:%v: was expecting error %v, got %v",
102103
path.Base(file),
103104
line,
105+
expectedString,
104106
err,
105107
)
106108
}

diff.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const (
2020
DiffFlagBinary DiffFlag = C.GIT_DIFF_FLAG_BINARY
2121
DiffFlagNotBinary DiffFlag = C.GIT_DIFF_FLAG_NOT_BINARY
2222
DiffFlagValidOid DiffFlag = C.GIT_DIFF_FLAG_VALID_ID
23+
DiffFlagExists DiffFlag = C.GIT_DIFF_FLAG_EXISTS
2324
)
2425

2526
type Delta int
@@ -34,6 +35,8 @@ const (
3435
DeltaIgnored Delta = C.GIT_DELTA_IGNORED
3536
DeltaUntracked Delta = C.GIT_DELTA_UNTRACKED
3637
DeltaTypeChange Delta = C.GIT_DELTA_TYPECHANGE
38+
DeltaUnreadable Delta = C.GIT_DELTA_UNREADABLE
39+
DeltaConflicted Delta = C.GIT_DELTA_CONFLICTED
3740
)
3841

3942
type DiffLineType int
@@ -399,6 +402,7 @@ const (
399402
DiffIgnoreFilemode DiffOptionsFlag = C.GIT_DIFF_IGNORE_FILEMODE
400403
DiffIgnoreSubmodules DiffOptionsFlag = C.GIT_DIFF_IGNORE_SUBMODULES
401404
DiffIgnoreCase DiffOptionsFlag = C.GIT_DIFF_IGNORE_CASE
405+
DiffIncludeCaseChange DiffOptionsFlag = C.GIT_DIFF_INCLUDE_CASECHANGE
402406

403407
DiffDisablePathspecMatch DiffOptionsFlag = C.GIT_DIFF_DISABLE_PATHSPEC_MATCH
404408
DiffSkipBinaryCheck DiffOptionsFlag = C.GIT_DIFF_SKIP_BINARY_CHECK

features.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package git
2+
3+
/*
4+
#include <git2.h>
5+
*/
6+
import "C"
7+
8+
type Feature int
9+
10+
const (
11+
// libgit2 was built with threading support
12+
FeatureThreads Feature = C.GIT_FEATURE_THREADS
13+
14+
// libgit2 was built with HTTPS support built-in
15+
FeatureHttps Feature = C.GIT_FEATURE_HTTPS
16+
17+
// libgit2 was build with SSH support built-in
18+
FeatureSsh Feature = C.GIT_FEATURE_SSH
19+
20+
// libgit2 was built with nanosecond support for files
21+
FeatureNSec Feature = C.GIT_FEATURE_NSEC
22+
)
23+
24+
// Features returns a bit-flag of Feature values indicating which features the
25+
// loaded libgit2 library has.
26+
func Features() Feature {
27+
features := C.git_libgit2_features()
28+
29+
return Feature(features)
30+
}

git.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package git
22

33
/*
4+
#cgo CFLAGS: -I${SRCDIR}/vendor/libgit2/include
5+
#cgo LDFLAGS: -L${SRCDIR}/vendor/libgit2/build/ -lgit2
6+
#cgo windows LDFLAGS: -lwinhttp
7+
#cgo !windows pkg-config: --static ${SRCDIR}/vendor/libgit2/build/libgit2.pc
48
#include <git2.h>
59
#include <git2/sys/openssl.h>
6-
#cgo pkg-config: libgit2
710
8-
#if LIBGIT2_VER_MAJOR != 0 || LIBGIT2_VER_MINOR != 24
9-
# error "Invalid libgit2 version; this git2go supports libgit2 v0.24"
11+
#if LIBGIT2_VER_MAJOR != 0 || LIBGIT2_VER_MINOR != 25
12+
# error "Invalid libgit2 version; this git2go supports libgit2 v0.25"
1013
#endif
1114
1215
*/
@@ -125,6 +128,15 @@ func init() {
125128

126129
C.git_libgit2_init()
127130

131+
// Due to the multithreaded nature of Go and its interaction with
132+
// calling C functions, we cannot work with a library that was not built
133+
// with multi-threading support. The most likely outcome is a segfault
134+
// or panic at an incomprehensible time, so let's make it easy by
135+
// panicking right here.
136+
if Features()&FeatureThreads == 0 {
137+
panic("libgit2 was not built with threading support")
138+
}
139+
128140
// This is not something we should be doing, as we may be
129141
// stomping all over someone else's setup. The user should do
130142
// this themselves or use some binding/wrapper which does it

git_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ func updateReadme(t *testing.T, repo *Repository, content string) (*Oid, *Oid) {
9393
checkFatal(t, err)
9494
err = idx.AddByPath("README")
9595
checkFatal(t, err)
96+
err = idx.Write()
97+
checkFatal(t, err)
9698
treeId, err := idx.WriteTree()
9799
checkFatal(t, err)
98100

odb.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,10 @@ func (object *OdbObject) Len() (len uint64) {
226226
return uint64(C.git_odb_object_size(object.ptr))
227227
}
228228

229+
func (object *OdbObject) Type() ObjectType {
230+
return ObjectType(C.git_odb_object_type(object.ptr))
231+
}
232+
229233
func (object *OdbObject) Data() (data []byte) {
230234
var c_blob unsafe.Pointer = C.git_odb_object_data(object.ptr)
231235
var blob []byte

0 commit comments

Comments
 (0)