pax_global_header00006660000000000000000000000064137632334300014516gustar00rootroot0000000000000052 comment=abf02bc7d79dfb7b0bbcd404ebecb202cff2a18e git2go-31.4.3/000077500000000000000000000000001376323343000130015ustar00rootroot00000000000000git2go-31.4.3/.github/000077500000000000000000000000001376323343000143415ustar00rootroot00000000000000git2go-31.4.3/.github/workflows/000077500000000000000000000000001376323343000163765ustar00rootroot00000000000000git2go-31.4.3/.github/workflows/backport.yml000066400000000000000000000032751376323343000207350ustar00rootroot00000000000000name: Backport to older releases on: push: branches: - master jobs: backport: name: Backport change to branch ${{ matrix.branch }} continue-on-error: true strategy: fail-fast: false matrix: branch: [ 'release-1.0', 'release-0.28', 'release-0.27' ] runs-on: ubuntu-20.04 steps: - name: Check out code uses: actions/checkout@v1 with: fetch-depth: 0 - name: Create a cherry-pick PR run: | if ! git diff --quiet HEAD^ HEAD -- vendor/libgit2; then echo '::warning::Skipping cherry-pick since it is a vendored libgit2 bump' exit 0 fi BRANCH_NAME="cherry-pick-${{ github.run_id }}-${{ matrix.branch }}" # Setup usernames and authentication git config --global user.name "${{ github.actor }}" git config --global user.email "${{ github.actor }}@users.noreply.github.com" cat <<- EOF > $HOME/.netrc machine github.com login ${{ github.actor }} password ${{ secrets.GITHUB_TOKEN }} machine api.github.com login ${{ github.actor }} password ${{ secrets.GITHUB_TOKEN }} EOF chmod 600 $HOME/.netrc # Create the cherry-pick commit and create the PR for it. git checkout "${{ matrix.branch }}" git switch -c "${BRANCH_NAME}" git cherry-pick -x "${{ github.sha }}" git push --set-upstream origin "${BRANCH_NAME}" GITHUB_TOKEN="${{ secrets.GITHUB_TOKEN }}" gh pr create \ --base "${{ matrix.branch }}" \ --title "$(git --no-pager show --format="%s" --no-patch HEAD)" \ --body "$(git --no-pager show --format="%b" --no-patch HEAD)" git2go-31.4.3/.github/workflows/ci.yml000066400000000000000000000067001376323343000175170ustar00rootroot00000000000000name: git2go CI on: pull_request: push: branches: - master - release-* - v* jobs: build-legacy: strategy: fail-fast: false matrix: go: [ '1.9', '1.10' ] name: Go ${{ matrix.go }} runs-on: ubuntu-18.04 steps: - name: Set up Go uses: actions/setup-go@v1 with: go-version: ${{ matrix.go }} id: go - name: Check out code into the GOPATH uses: actions/checkout@v1 with: fetch-depth: 1 path: src/github.com/${{ github.repository }} - name: Build env: GOPATH: /home/runner/work/git2go run: | git submodule update --init sudo apt-get install -y --no-install-recommends libssh2-1-dev make build-libgit2-static go get -tags static -t github.com/${{ github.repository }}/... go build -tags static github.com/${{ github.repository }}/... - name: Test env: GOPATH: /home/runner/work/git2go run: make TEST_ARGS=-test.v test-static build-static: strategy: fail-fast: false matrix: go: [ '1.11', '1.12', '1.13', '1.14', '1.15' ] name: Go ${{ matrix.go }} runs-on: ubuntu-20.04 steps: - name: Set up Go uses: actions/setup-go@v1 with: go-version: ${{ matrix.go }} id: go - name: Check out code into the Go module directory uses: actions/checkout@v1 - name: Build run: | git submodule update --init sudo apt-get install -y --no-install-recommends libssh2-1-dev make build-libgit2-static - name: Test run: make TEST_ARGS=-test.v test-static build-dynamic: strategy: fail-fast: false name: Go (dynamic) runs-on: ubuntu-20.04 steps: - name: Set up Go uses: actions/setup-go@v1 with: go-version: '1.15' id: go - name: Check out code into the Go module directory uses: actions/checkout@v1 - name: Build run: | git submodule update --init sudo apt-get install -y --no-install-recommends libssh2-1-dev make build-libgit2-dynamic - name: Test run: make TEST_ARGS=-test.v test-dynamic build-system-dynamic: strategy: fail-fast: false matrix: libgit2: [ '1.1.0' ] name: Go (system-wide, dynamic) runs-on: ubuntu-20.04 steps: - name: Set up Go uses: actions/setup-go@v1 with: go-version: '1.15' id: go - name: Check out code into the Go module directory uses: actions/checkout@v1 - name: Build libgit2 ${{ matrix.libgit2 }} run: | git submodule update --init sudo apt-get install -y --no-install-recommends libssh2-1-dev sudo env BUILD_LIBGIT_REF=v${{ matrix.libgit2 }} ./script/build-libgit2.sh --dynamic --system - name: Test run: make TEST_ARGS=-test.v test build-system-static: strategy: fail-fast: false name: Go (system-wide, static) runs-on: ubuntu-20.04 steps: - name: Set up Go uses: actions/setup-go@v1 with: go-version: '1.15' id: go - name: Check out code into the Go module directory uses: actions/checkout@v1 - name: Build libgit2 run: | git submodule update --init sudo apt-get install -y --no-install-recommends libssh2-1-dev sudo ./script/build-libgit2.sh --static --system - name: Test run: go test --count=1 --tags "static,system_libgit2" ./... git2go-31.4.3/.github/workflows/tag.yml000066400000000000000000000011101376323343000176650ustar00rootroot00000000000000name: Tag new releases on: push: branches: - master - release-* jobs: tag-release: name: Bump tag in ${{ github.ref }} runs-on: ubuntu-20.04 steps: - name: Check out code uses: actions/checkout@v1 with: fetch-depth: 0 - name: Bump version and push tag id: bump-version uses: anothrNick/github-tag-action@9aaabdb5e989894e95288328d8b17a6347217ae3 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} WITH_V: true DEFAULT_BUMP: patch TAG_CONTEXT: branch RELEASE_BRANCHES: .* git2go-31.4.3/.gitignore000066400000000000000000000000371376323343000147710ustar00rootroot00000000000000/static-build/ /dynamic-build/ git2go-31.4.3/.gitmodules000066400000000000000000000001361376323343000151560ustar00rootroot00000000000000[submodule "vendor/libgit2"] path = vendor/libgit2 url = https://github.com/libgit2/libgit2 git2go-31.4.3/.travis.yml000066400000000000000000000004641376323343000151160ustar00rootroot00000000000000language: go arch: - AMD64 - ppc64le go: - tip install: - sudo apt-get install -y --no-install-recommends libssh2-1-dev - make build-libgit2-static - go get --tags "static" ./... script: - make test-static git: submodules: true branches: only: - master - /v\d+/ - /release-.*/ git2go-31.4.3/LICENSE000066400000000000000000000020741376323343000140110ustar00rootroot00000000000000The MIT License Copyright (c) 2013 The git2go contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. git2go-31.4.3/Makefile000066400000000000000000000033041376323343000144410ustar00rootroot00000000000000TEST_ARGS ?= --count=1 default: test # System library # ============== # This uses whatever version of libgit2 can be found in the system. test: go run script/check-MakeGitError-thread-lock.go go test $(TEST_ARGS) ./... install: go install ./... # Bundled dynamic library # ======================= # In order to avoid having to manipulate `git_dynamic.go`, which would prevent # the system-wide libgit2.so from being used in a sort of ergonomic way, this # instead moves the complexity of overriding the paths so that the built # libraries can be found by the build and tests. .PHONY: build-libgit2-dynamic build-libgit2-dynamic: ./script/build-libgit2-dynamic.sh dynamic-build/install/lib/libgit2.so: ./script/build-libgit2-dynamic.sh test-dynamic: dynamic-build/install/lib/libgit2.so PKG_CONFIG_PATH=dynamic-build/install/lib/pkgconfig \ go run script/check-MakeGitError-thread-lock.go PKG_CONFIG_PATH=dynamic-build/install/lib/pkgconfig \ LD_LIBRARY_PATH=dynamic-build/install/lib \ go test $(TEST_ARGS) ./... install-dynamic: dynamic-build/install/lib/libgit2.so PKG_CONFIG_PATH=dynamic-build/install/lib/pkgconfig \ go install ./... # Bundled static library # ====================== # This is mostly used in tests, but can also be used to provide a # statically-linked library with the bundled version of libgit2. .PHONY: build-libgit2-static build-libgit2-static: ./script/build-libgit2-static.sh static-build/install/lib/libgit2.a: ./script/build-libgit2-static.sh test-static: static-build/install/lib/libgit2.a go run script/check-MakeGitError-thread-lock.go go test --tags "static" $(TEST_ARGS) ./... install-static: static-build/install/lib/libgit2.a go install --tags "static" ./... git2go-31.4.3/README.md000066400000000000000000000126761376323343000142740ustar00rootroot00000000000000git2go ====== [![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/). ### Which Go version to use Due to the fact that Go 1.11 module versions have semantic meaning and don't necessarily align with libgit2's release schedule, please consult the following table for a mapping between libgit2 and git2go module versions: | libgit2 | git2go | |---------|---------------| | master | (will be v32) | | 1.1 | v31 | | 1.0 | v30 | | 0.99 | v29 | | 0.28 | v28 | | 0.27 | v27 | You can import them in your project with the version's major number as a suffix. For example, if you have libgit2 v1.1 installed, you'd import git2go v31 with: ```sh go get github.com/libgit2/git2go/v31 ``` ```go import "github.com/libgit2/git2go/v31" ``` which will ensure there are no sudden changes to the API. The `master` branch follows the tip of libgit2 itself (with some lag) and as such has no guarantees on the stability of libgit2's API. Thus this only supports statically linking against libgit2. ### Which branch to send Pull requests to If there's something version-specific that you'd want to contribute to, you can send them to the `release-${MAJOR}.${MINOR}` branches, which follow libgit2's releases. Installing ---------- This project wraps the functionality provided by libgit2. It thus needs it in order to perform the work. 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. ### Versioned branch, dynamic linking 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 Go modules, e.g. to work against libgit2 v1.1 ```go import "github.com/libgit2/git2go/v31" ``` ### Versioned branch, static linking Follow the instructions for [Versioned branch, dynamic linking](#versioned-branch-dynamic-linking), but pass the `-tags static,system_libgit2` flag to all `go` commands that build any binaries. For instance: go build -tags static,system_libgit2 github.com/my/project/... go test -tags static,system_libgit2 github.com/my/project/... go install -tags static,system_libgit2 github.com/my/project/... ### Master branch, or vendored static linking If using `master` or building a branch with the vendored libgit2 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. 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. git submodule update --init # get libgit2 make install-static will compile libgit2, link it into git2go and install it. The `master` branch is set up to follow the specific libgit2 version that is vendored, so trying dynamic linking may or may not work depending on the exact versions involved. In order to let Go pass the correct flags to `pkg-config`, `-tags static` needs to be passed to all `go` commands that build any binaries. For instance: go build -tags static github.com/my/project/... go test -tags static github.com/my/project/... go install -tags static github.com/my/project/... One thing to take into account is that since Go expects the `pkg-config` file to be within the same directory where `make install-static` was called, so the `go.mod` file may need to have a [`replace` directive](https://github.com/golang/go/wiki/Modules#when-should-i-use-the-replace-directive) so that the correct setup is achieved. So if `git2go` is checked out at `$GOPATH/src/github.com/libgit2/git2go` and your project at `$GOPATH/src/github.com/my/project`, the `go.mod` file of `github.com/my/project` might need to have a line like replace github.com/libgit2/git2go/v31 ../../libgit2/git2go Parallelism and network operations ---------------------------------- 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. Running the tests ----------------- For the stable version, `go test` will work as usual. For the `master` 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 make test-static Alternatively, you can build the library manually first and then run the tests make install-static go test -v -tags static ./... License ------- M to the I to the T. See the LICENSE file if you've never seen an MIT license before. Authors ------- - Carlos Martín (@carlosmn) - Vicent Martí (@vmg) git2go-31.4.3/blame.go000066400000000000000000000105531376323343000144140ustar00rootroot00000000000000package git /* #include */ import "C" import ( "runtime" "unsafe" ) type BlameOptions struct { Flags BlameOptionsFlag MinMatchCharacters uint16 NewestCommit *Oid OldestCommit *Oid MinLine uint32 MaxLine uint32 } func DefaultBlameOptions() (BlameOptions, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() opts := C.git_blame_options{} ecode := C.git_blame_options_init(&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 BlameUseMailmap BlameOptionsFlag = C.GIT_BLAME_USE_MAILMAP BlameIgnoreWhitespace BlameOptionsFlag = C.GIT_BLAME_IGNORE_WHITESPACE ) 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.size_t(opts.MinLine), max_line: C.size_t(opts.MaxLine), } if opts.NewestCommit != nil { copts.newest_commit = *opts.NewestCommit.toC() } if opts.OldestCommit != nil { copts.oldest_commit = *opts.OldestCommit.toC() } } cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_blame_file(&blamePtr, v.ptr, cpath, copts) runtime.KeepAlive(v) if ecode < 0 { return nil, MakeGitError(ecode) } return newBlameFromC(blamePtr), nil } type Blame struct { ptr *C.git_blame } func (blame *Blame) HunkCount() int { ret := int(C.git_blame_get_hunk_count(blame.ptr)) runtime.KeepAlive(blame) return ret } func (blame *Blame) HunkByIndex(index int) (BlameHunk, error) { ptr := C.git_blame_get_hunk_byindex(blame.ptr, C.uint32_t(index)) runtime.KeepAlive(blame) if ptr == nil { return BlameHunk{}, ErrInvalid } return blameHunkFromC(ptr), nil } func (blame *Blame) HunkByLine(lineno int) (BlameHunk, error) { ptr := C.git_blame_get_hunk_byline(blame.ptr, C.size_t(lineno)) runtime.KeepAlive(blame) 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, } } git2go-31.4.3/blame_test.go000066400000000000000000000032661376323343000154560ustar00rootroot00000000000000package git import ( "reflect" "testing" ) func TestBlame(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) 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()) } wantHunk1 := BlameHunk{ LinesInHunk: 1, FinalCommitId: commitId1, FinalStartLineNumber: 1, OrigCommitId: commitId1, OrigPath: "README", OrigStartLineNumber: 1, Boundary: true, } wantHunk2 := BlameHunk{ LinesInHunk: 2, FinalCommitId: commitId2, FinalStartLineNumber: 2, OrigCommitId: commitId2, 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, label string, hunk, want BlameHunk) { hunk.FinalSignature = nil want.FinalSignature = nil hunk.OrigSignature = nil want.OrigSignature = nil if !reflect.DeepEqual(hunk, want) { t.Fatalf("%s: got hunk %+v, want %+v", label, hunk, want) } } git2go-31.4.3/blob.go000066400000000000000000000063561376323343000142600ustar00rootroot00000000000000package git /* #include #include int _go_git_writestream_write(git_writestream *stream, const char *buffer, size_t len); void _go_git_writestream_free(git_writestream *stream); */ import "C" import ( "reflect" "runtime" "unsafe" ) type Blob struct { Object cast_ptr *C.git_blob } func (b *Blob) AsObject() *Object { return &b.Object } func (v *Blob) Size() int64 { ret := int64(C.git_blob_rawsize(v.cast_ptr)) runtime.KeepAlive(v) return ret } func (v *Blob) Contents() []byte { size := C.int(C.git_blob_rawsize(v.cast_ptr)) buffer := unsafe.Pointer(C.git_blob_rawcontent(v.cast_ptr)) goBytes := C.GoBytes(buffer, size) runtime.KeepAlive(v) return goBytes } func (v *Blob) IsBinary() bool { ret := C.git_blob_is_binary(v.cast_ptr) == 1 runtime.KeepAlive(v) return ret } func (repo *Repository) CreateBlobFromBuffer(data []byte) (*Oid, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() var id C.git_oid var size C.size_t // Go 1.6 added some increased checking of passing pointer to // C, but its check depends on its expectations of what we // pass to the C function, so unless we take the address of // its contents at the call site itself, it can fail when // 'data' is a slice of a slice. // // When we're given an empty slice, create a dummy one where 0 // isn't out of bounds. if len(data) > 0 { size = C.size_t(len(data)) } else { data = []byte{0} size = C.size_t(0) } ecode := C.git_blob_create_from_buffer(&id, repo.ptr, unsafe.Pointer(&data[0]), size) runtime.KeepAlive(repo) if ecode < 0 { return nil, MakeGitError(ecode) } return newOidFromC(&id), nil } func (repo *Repository) CreateFromStream(hintPath string) (*BlobWriteStream, error) { var chintPath *C.char = nil var stream *C.git_writestream if len(hintPath) > 0 { chintPath = C.CString(hintPath) defer C.free(unsafe.Pointer(chintPath)) } runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_blob_create_from_stream(&stream, repo.ptr, chintPath) if ecode < 0 { return nil, MakeGitError(ecode) } return newBlobWriteStreamFromC(stream, repo), nil } type BlobWriteStream struct { ptr *C.git_writestream repo *Repository } func newBlobWriteStreamFromC(ptr *C.git_writestream, repo *Repository) *BlobWriteStream { stream := &BlobWriteStream{ ptr: ptr, repo: repo, } runtime.SetFinalizer(stream, (*BlobWriteStream).Free) return stream } // Implement io.Writer func (stream *BlobWriteStream) Write(p []byte) (int, error) { header := (*reflect.SliceHeader)(unsafe.Pointer(&p)) ptr := (*C.char)(unsafe.Pointer(header.Data)) size := C.size_t(header.Len) runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C._go_git_writestream_write(stream.ptr, ptr, size) runtime.KeepAlive(stream) if ecode < 0 { return 0, MakeGitError(ecode) } return len(p), nil } func (stream *BlobWriteStream) Free() { runtime.SetFinalizer(stream, nil) C._go_git_writestream_free(stream.ptr) } func (stream *BlobWriteStream) Commit() (*Oid, error) { oid := C.git_oid{} runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_blob_create_from_stream_commit(&oid, stream.ptr) runtime.KeepAlive(stream) if ecode < 0 { return nil, MakeGitError(ecode) } return newOidFromC(&oid), nil } git2go-31.4.3/blob_test.go000066400000000000000000000021771376323343000153140ustar00rootroot00000000000000package git import ( "bytes" "testing" ) type bufWrapper struct { buf [64]byte pointer []byte } func doublePointerBytes() []byte { o := &bufWrapper{} o.pointer = o.buf[0:10] return o.pointer[0:1] } func TestCreateBlobFromBuffer(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) id, err := repo.CreateBlobFromBuffer(make([]byte, 0)) checkFatal(t, err) if id.String() != "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391" { t.Fatal("Empty buffer did not deliver empty blob id") } tests := []struct { data []byte isBinary bool }{ { data: []byte("hello there"), isBinary: false, }, { data: doublePointerBytes(), isBinary: true, }, } for _, tt := range tests { data := tt.data id, err = repo.CreateBlobFromBuffer(data) checkFatal(t, err) blob, err := repo.LookupBlob(id) checkFatal(t, err) if !bytes.Equal(blob.Contents(), data) { t.Fatal("Loaded bytes don't match original bytes:", blob.Contents(), "!=", data) } want := tt.isBinary if got := blob.IsBinary(); got != want { t.Fatalf("IsBinary() = %v, want %v", got, want) } } } git2go-31.4.3/branch.go000066400000000000000000000131121376323343000145630ustar00rootroot00000000000000package git /* #include */ import "C" import ( "runtime" "unsafe" ) type BranchType uint const ( BranchAll BranchType = C.GIT_BRANCH_ALL BranchLocal BranchType = C.GIT_BRANCH_LOCAL BranchRemote BranchType = C.GIT_BRANCH_REMOTE ) type Branch struct { *Reference } func (r *Reference) Branch() *Branch { return &Branch{Reference: r} } type BranchIterator struct { ptr *C.git_branch_iterator repo *Repository } type BranchIteratorFunc func(*Branch, BranchType) error func newBranchIteratorFromC(repo *Repository, ptr *C.git_branch_iterator) *BranchIterator { i := &BranchIterator{repo: repo, ptr: ptr} runtime.SetFinalizer(i, (*BranchIterator).Free) return i } func (i *BranchIterator) Next() (*Branch, BranchType, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() var refPtr *C.git_reference var refType C.git_branch_t ecode := C.git_branch_next(&refPtr, &refType, i.ptr) if ecode < 0 { return nil, BranchLocal, MakeGitError(ecode) } branch := newReferenceFromC(refPtr, i.repo).Branch() return branch, BranchType(refType), nil } func (i *BranchIterator) Free() { runtime.SetFinalizer(i, nil) C.git_branch_iterator_free(i.ptr) } func (i *BranchIterator) ForEach(f BranchIteratorFunc) error { b, t, err := i.Next() for err == nil { err = f(b, t) if err == nil { b, t, err = i.Next() } } if err != nil && IsErrorCode(err, ErrorCodeIterOver) { return nil } return err } func (repo *Repository) NewBranchIterator(flags BranchType) (*BranchIterator, error) { refType := C.git_branch_t(flags) var ptr *C.git_branch_iterator runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_branch_iterator_new(&ptr, repo.ptr, refType) runtime.KeepAlive(repo) if ecode < 0 { return nil, MakeGitError(ecode) } return newBranchIteratorFromC(repo, ptr), nil } func (repo *Repository) CreateBranch(branchName string, target *Commit, force bool) (*Branch, error) { var ptr *C.git_reference cBranchName := C.CString(branchName) defer C.free(unsafe.Pointer(cBranchName)) cForce := cbool(force) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_branch_create(&ptr, repo.ptr, cBranchName, target.cast_ptr, cForce) runtime.KeepAlive(repo) runtime.KeepAlive(target) if ret < 0 { return nil, MakeGitError(ret) } return newReferenceFromC(ptr, repo).Branch(), nil } func (b *Branch) Delete() error { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_branch_delete(b.Reference.ptr) runtime.KeepAlive(b.Reference) if ret < 0 { return MakeGitError(ret) } return nil } func (b *Branch) Move(newBranchName string, force bool) (*Branch, error) { var ptr *C.git_reference cNewBranchName := C.CString(newBranchName) defer C.free(unsafe.Pointer(cNewBranchName)) cForce := cbool(force) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_branch_move(&ptr, b.Reference.ptr, cNewBranchName, cForce) runtime.KeepAlive(b.Reference) if ret < 0 { return nil, MakeGitError(ret) } return newReferenceFromC(ptr, b.repo).Branch(), nil } func (b *Branch) IsHead() (bool, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_branch_is_head(b.Reference.ptr) runtime.KeepAlive(b.Reference) switch ret { case 1: return true, nil case 0: return false, nil } return false, MakeGitError(ret) } func (repo *Repository) LookupBranch(branchName string, bt BranchType) (*Branch, error) { var ptr *C.git_reference cName := C.CString(branchName) defer C.free(unsafe.Pointer(cName)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_branch_lookup(&ptr, repo.ptr, cName, C.git_branch_t(bt)) runtime.KeepAlive(repo) if ret < 0 { return nil, MakeGitError(ret) } return newReferenceFromC(ptr, repo).Branch(), nil } func (b *Branch) Name() (string, error) { var cName *C.char defer C.free(unsafe.Pointer(cName)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_branch_name(&cName, b.Reference.ptr) runtime.KeepAlive(b.Reference) if ret < 0 { return "", MakeGitError(ret) } return C.GoString(cName), nil } func (repo *Repository) RemoteName(canonicalBranchName string) (string, error) { cName := C.CString(canonicalBranchName) defer C.free(unsafe.Pointer(cName)) nameBuf := C.git_buf{} runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_branch_remote_name(&nameBuf, repo.ptr, cName) runtime.KeepAlive(repo) if ret < 0 { return "", MakeGitError(ret) } defer C.git_buf_dispose(&nameBuf) return C.GoString(nameBuf.ptr), nil } func (b *Branch) SetUpstream(upstreamName string) error { cName := C.CString(upstreamName) defer C.free(unsafe.Pointer(cName)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_branch_set_upstream(b.Reference.ptr, cName) runtime.KeepAlive(b.Reference) if ret < 0 { return MakeGitError(ret) } return nil } func (b *Branch) Upstream() (*Reference, error) { var ptr *C.git_reference runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_branch_upstream(&ptr, b.Reference.ptr) runtime.KeepAlive(b.Reference) if ret < 0 { return nil, MakeGitError(ret) } return newReferenceFromC(ptr, b.repo), nil } func (repo *Repository) UpstreamName(canonicalBranchName string) (string, error) { cName := C.CString(canonicalBranchName) defer C.free(unsafe.Pointer(cName)) nameBuf := C.git_buf{} runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_branch_upstream_name(&nameBuf, repo.ptr, cName) runtime.KeepAlive(repo) if ret < 0 { return "", MakeGitError(ret) } defer C.git_buf_dispose(&nameBuf) return C.GoString(nameBuf.ptr), nil } git2go-31.4.3/branch_test.go000066400000000000000000000022601376323343000156240ustar00rootroot00000000000000package git import ( "testing" ) func TestBranchIterator(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) seedTestRepo(t, repo) i, err := repo.NewBranchIterator(BranchLocal) checkFatal(t, err) b, bt, err := i.Next() checkFatal(t, err) if name, _ := b.Name(); name != "master" { t.Fatalf("expected master") } else if bt != BranchLocal { t.Fatalf("expected BranchLocal, not %v", t) } b, bt, err = i.Next() if !IsErrorCode(err, ErrorCodeIterOver) { t.Fatal("expected iterover") } } func TestBranchIteratorEach(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) seedTestRepo(t, repo) i, err := repo.NewBranchIterator(BranchLocal) checkFatal(t, err) var names []string f := func(b *Branch, t BranchType) error { name, err := b.Name() if err != nil { return err } names = append(names, name) return nil } err = i.ForEach(f) if err != nil && !IsErrorCode(err, ErrorCodeIterOver) { t.Fatal(err) } if len(names) != 1 { t.Fatalf("expect 1 branch, but it was %d\n", len(names)) } if names[0] != "master" { t.Fatalf("expect branch master, but it was %s\n", names[0]) } } git2go-31.4.3/checkout.go000066400000000000000000000245131376323343000151420ustar00rootroot00000000000000package git /* #include extern void _go_git_populate_checkout_callbacks(git_checkout_options *opts); */ import "C" import ( "errors" "os" "runtime" "unsafe" ) type CheckoutNotifyType uint type CheckoutStrategy uint const ( CheckoutNotifyNone CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_NONE CheckoutNotifyConflict CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_CONFLICT CheckoutNotifyDirty CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_DIRTY CheckoutNotifyUpdated CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_UPDATED CheckoutNotifyUntracked CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_UNTRACKED CheckoutNotifyIgnored CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_IGNORED CheckoutNotifyAll CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_ALL CheckoutNone CheckoutStrategy = C.GIT_CHECKOUT_NONE // Dry run, no actual updates CheckoutSafe CheckoutStrategy = C.GIT_CHECKOUT_SAFE // Allow safe updates that cannot overwrite uncommitted data CheckoutForce CheckoutStrategy = C.GIT_CHECKOUT_FORCE // Allow all updates to force working directory to look like index CheckoutRecreateMissing CheckoutStrategy = C.GIT_CHECKOUT_RECREATE_MISSING // Allow checkout to recreate missing files CheckoutAllowConflicts CheckoutStrategy = C.GIT_CHECKOUT_ALLOW_CONFLICTS // Allow checkout to make safe updates even if conflicts are found CheckoutRemoveUntracked CheckoutStrategy = C.GIT_CHECKOUT_REMOVE_UNTRACKED // Remove untracked files not in index (that are not ignored) CheckoutRemoveIgnored CheckoutStrategy = C.GIT_CHECKOUT_REMOVE_IGNORED // Remove ignored files not in index CheckoutUpdateOnly CheckoutStrategy = C.GIT_CHECKOUT_UPDATE_ONLY // Only update existing files, don't create new ones CheckoutDontUpdateIndex CheckoutStrategy = C.GIT_CHECKOUT_DONT_UPDATE_INDEX // Normally checkout updates index entries as it goes; this stops that CheckoutNoRefresh CheckoutStrategy = C.GIT_CHECKOUT_NO_REFRESH // Don't refresh index/config/etc before doing checkout CheckoutSkipUnmerged CheckoutStrategy = C.GIT_CHECKOUT_SKIP_UNMERGED // Allow checkout to skip unmerged files CheckoutUseOurs CheckoutStrategy = C.GIT_CHECKOUT_USE_OURS // For unmerged files, checkout stage 2 from index CheckoutUseTheirs CheckoutStrategy = C.GIT_CHECKOUT_USE_THEIRS // For unmerged files, checkout stage 3 from index CheckoutDisablePathspecMatch CheckoutStrategy = C.GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH // Treat pathspec as simple list of exact match file paths CheckoutSkipLockedDirectories CheckoutStrategy = C.GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES // Ignore directories in use, they will be left empty CheckoutDontOverwriteIgnored CheckoutStrategy = C.GIT_CHECKOUT_DONT_OVERWRITE_IGNORED // Don't overwrite ignored files that exist in the checkout target CheckoutConflictStyleMerge CheckoutStrategy = C.GIT_CHECKOUT_CONFLICT_STYLE_MERGE // Write normal merge files for conflicts CheckoutConflictStyleDiff3 CheckoutStrategy = C.GIT_CHECKOUT_CONFLICT_STYLE_DIFF3 // Include common ancestor data in diff3 format files for conflicts CheckoutDontRemoveExisting CheckoutStrategy = C.GIT_CHECKOUT_DONT_REMOVE_EXISTING // Don't overwrite existing files or folders CheckoutDontWriteIndex CheckoutStrategy = C.GIT_CHECKOUT_DONT_WRITE_INDEX // Normally checkout writes the index upon completion; this prevents that CheckoutUpdateSubmodules CheckoutStrategy = C.GIT_CHECKOUT_UPDATE_SUBMODULES // Recursively checkout submodules with same options (NOT IMPLEMENTED) CheckoutUpdateSubmodulesIfChanged CheckoutStrategy = C.GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED // Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED) ) type CheckoutNotifyCallback func(why CheckoutNotifyType, path string, baseline, target, workdir DiffFile) ErrorCode type CheckoutProgressCallback func(path string, completed, total uint) ErrorCode type CheckoutOptions 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 NotifyFlags CheckoutNotifyType // Default will be none NotifyCallback CheckoutNotifyCallback ProgressCallback CheckoutProgressCallback TargetDirectory string // Alternative checkout path to workdir Paths []string Baseline *Tree } func checkoutOptionsFromC(c *C.git_checkout_options) CheckoutOptions { opts := CheckoutOptions{ Strategy: CheckoutStrategy(c.checkout_strategy), DisableFilters: c.disable_filters != 0, DirMode: os.FileMode(c.dir_mode), FileMode: os.FileMode(c.file_mode), FileOpenFlags: int(c.file_open_flags), NotifyFlags: CheckoutNotifyType(c.notify_flags), } if c.notify_payload != nil { opts.NotifyCallback = pointerHandles.Get(c.notify_payload).(*checkoutCallbackData).options.NotifyCallback } if c.progress_payload != nil { opts.ProgressCallback = pointerHandles.Get(c.progress_payload).(*checkoutCallbackData).options.ProgressCallback } if c.target_directory != nil { opts.TargetDirectory = C.GoString(c.target_directory) } return opts } func (opts *CheckoutOptions) toC(errorTarget *error) *C.git_checkout_options { if opts == nil { return nil } return populateCheckoutOptions(&C.git_checkout_options{}, opts, errorTarget) } type checkoutCallbackData struct { options *CheckoutOptions errorTarget *error } //export checkoutNotifyCallback func checkoutNotifyCallback( why C.git_checkout_notify_t, cpath *C.char, cbaseline, ctarget, cworkdir, handle unsafe.Pointer, ) C.int { if handle == nil { return C.int(ErrorCodeOK) } path := C.GoString(cpath) var baseline, target, workdir DiffFile if cbaseline != nil { baseline = diffFileFromC((*C.git_diff_file)(cbaseline)) } if ctarget != nil { target = diffFileFromC((*C.git_diff_file)(ctarget)) } if cworkdir != nil { workdir = diffFileFromC((*C.git_diff_file)(cworkdir)) } data := pointerHandles.Get(handle).(*checkoutCallbackData) if data.options.NotifyCallback == nil { return C.int(ErrorCodeOK) } ret := data.options.NotifyCallback(CheckoutNotifyType(why), path, baseline, target, workdir) if ret < 0 { *data.errorTarget = errors.New(ErrorCode(ret).String()) return C.int(ErrorCodeUser) } return C.int(ErrorCodeOK) } //export checkoutProgressCallback func checkoutProgressCallback( path *C.char, completed_steps, total_steps C.size_t, handle unsafe.Pointer, ) { data := pointerHandles.Get(handle).(*checkoutCallbackData) if data.options.ProgressCallback == nil { return } data.options.ProgressCallback(C.GoString(path), uint(completed_steps), uint(total_steps)) } // Convert the CheckoutOptions struct to the corresponding // C-struct. Returns a pointer to ptr, or nil if opts is nil, in order // to help with what to pass. func populateCheckoutOptions(ptr *C.git_checkout_options, opts *CheckoutOptions, errorTarget *error) *C.git_checkout_options { if opts == nil { return nil } C.git_checkout_options_init(ptr, 1) ptr.checkout_strategy = C.uint(opts.Strategy) ptr.disable_filters = cbool(opts.DisableFilters) ptr.dir_mode = C.uint(opts.DirMode.Perm()) ptr.file_mode = C.uint(opts.FileMode.Perm()) ptr.notify_flags = C.uint(opts.NotifyFlags) if opts.NotifyCallback != nil || opts.ProgressCallback != nil { C._go_git_populate_checkout_callbacks(ptr) data := &checkoutCallbackData{ options: opts, errorTarget: errorTarget, } payload := pointerHandles.Track(data) if opts.NotifyCallback != nil { ptr.notify_payload = payload } if opts.ProgressCallback != nil { ptr.progress_payload = payload } } if opts.TargetDirectory != "" { ptr.target_directory = C.CString(opts.TargetDirectory) } if len(opts.Paths) > 0 { ptr.paths.strings = makeCStringsFromStrings(opts.Paths) ptr.paths.count = C.size_t(len(opts.Paths)) } if opts.Baseline != nil { ptr.baseline = opts.Baseline.cast_ptr } return ptr } func freeCheckoutOptions(ptr *C.git_checkout_options) { if ptr == nil { return } C.free(unsafe.Pointer(ptr.target_directory)) if ptr.paths.count > 0 { freeStrarray(&ptr.paths) } if ptr.notify_payload != nil { pointerHandles.Untrack(ptr.notify_payload) } else if ptr.progress_payload != nil { pointerHandles.Untrack(ptr.progress_payload) } } // 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 *CheckoutOptions) error { runtime.LockOSThread() defer runtime.UnlockOSThread() var err error cOpts := opts.toC(&err) defer freeCheckoutOptions(cOpts) ret := C.git_checkout_head(v.ptr, cOpts) runtime.KeepAlive(v) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } // Updates files in the working tree to match the content of the given // index. If index is nil, the repository's index will be used. opts // may be nil. func (v *Repository) CheckoutIndex(index *Index, opts *CheckoutOptions) error { var iptr *C.git_index = nil if index != nil { iptr = index.ptr } runtime.LockOSThread() defer runtime.UnlockOSThread() var err error cOpts := opts.toC(&err) defer freeCheckoutOptions(cOpts) ret := C.git_checkout_index(v.ptr, iptr, cOpts) runtime.KeepAlive(v) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } func (v *Repository) CheckoutTree(tree *Tree, opts *CheckoutOptions) error { runtime.LockOSThread() defer runtime.UnlockOSThread() var err error cOpts := opts.toC(&err) defer freeCheckoutOptions(cOpts) ret := C.git_checkout_tree(v.ptr, tree.ptr, cOpts) runtime.KeepAlive(v) runtime.KeepAlive(tree) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } git2go-31.4.3/cherrypick.go000066400000000000000000000043511376323343000154760ustar00rootroot00000000000000package git /* #include */ import "C" import ( "runtime" ) type CherrypickOptions struct { Version uint Mainline uint MergeOpts MergeOptions CheckoutOpts CheckoutOptions } func cherrypickOptionsFromC(c *C.git_cherrypick_options) CherrypickOptions { opts := CherrypickOptions{ Version: uint(c.version), Mainline: uint(c.mainline), MergeOpts: mergeOptionsFromC(&c.merge_opts), CheckoutOpts: checkoutOptionsFromC(&c.checkout_opts), } return opts } func (opts *CherrypickOptions) toC(errorTarget *error) *C.git_cherrypick_options { if opts == nil { return nil } c := C.git_cherrypick_options{} c.version = C.uint(opts.Version) c.mainline = C.uint(opts.Mainline) c.merge_opts = *opts.MergeOpts.toC() c.checkout_opts = *opts.CheckoutOpts.toC(errorTarget) return &c } func freeCherrypickOpts(ptr *C.git_cherrypick_options) { if ptr == nil { return } freeMergeOptions(&ptr.merge_opts) freeCheckoutOptions(&ptr.checkout_opts) } func DefaultCherrypickOptions() (CherrypickOptions, error) { c := C.git_cherrypick_options{} runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_cherrypick_options_init(&c, C.GIT_CHERRYPICK_OPTIONS_VERSION) if ecode < 0 { return CherrypickOptions{}, MakeGitError(ecode) } defer freeCherrypickOpts(&c) return cherrypickOptionsFromC(&c), nil } func (v *Repository) Cherrypick(commit *Commit, opts CherrypickOptions) error { runtime.LockOSThread() defer runtime.UnlockOSThread() var err error cOpts := opts.toC(&err) defer freeCherrypickOpts(cOpts) ret := C.git_cherrypick(v.ptr, commit.cast_ptr, cOpts) runtime.KeepAlive(v) runtime.KeepAlive(commit) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } func (r *Repository) CherrypickCommit(pick, our *Commit, opts CherrypickOptions) (*Index, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() cOpts := opts.MergeOpts.toC() defer freeMergeOptions(cOpts) var ptr *C.git_index ret := C.git_cherrypick_commit(&ptr, r.ptr, pick.cast_ptr, our.cast_ptr, C.uint(opts.Mainline), cOpts) runtime.KeepAlive(pick) runtime.KeepAlive(our) if ret < 0 { return nil, MakeGitError(ret) } return newIndexFromC(ptr, r), nil } git2go-31.4.3/cherrypick_test.go000066400000000000000000000054051376323343000165360ustar00rootroot00000000000000package git import ( "io/ioutil" "testing" ) func checkout(t *testing.T, repo *Repository, commit *Commit) { tree, err := commit.Tree() if err != nil { t.Fatal(err) } err = repo.CheckoutTree(tree, &CheckoutOptions{Strategy: CheckoutSafe}) if err != nil { t.Fatal(err) } err = repo.SetHeadDetached(commit.Id()) if err != nil { t.Fatal(err) } } const content = "Herro, Worrd!" func readReadme(t *testing.T, repo *Repository) string { bytes, err := ioutil.ReadFile(pathInRepo(repo, "README")) if err != nil { t.Fatal(err) } return string(bytes) } func TestCherrypick(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) c1, _ := seedTestRepo(t, repo) c2, _ := updateReadme(t, repo, content) commit1, err := repo.LookupCommit(c1) if err != nil { t.Fatal(err) } commit2, err := repo.LookupCommit(c2) if err != nil { t.Fatal(err) } checkout(t, repo, commit1) if readReadme(t, repo) == content { t.Fatalf("README has wrong content after checking out initial commit") } opts, err := DefaultCherrypickOptions() if err != nil { t.Fatal(err) } err = repo.Cherrypick(commit2, opts) if err != nil { t.Fatal(err) } if readReadme(t, repo) != content { t.Fatalf("README has wrong contents after cherry-picking") } state := repo.State() if state != RepositoryStateCherrypick { t.Fatal("Incorrect repository state: ", state) } err = repo.StateCleanup() if err != nil { t.Fatal(err) } state = repo.State() if state != RepositoryStateNone { t.Fatal("Incorrect repository state: ", state) } } func TestCherrypickCommit(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) c1, _ := seedTestRepo(t, repo) c2, _ := updateReadme(t, repo, content) commit1, err := repo.LookupCommit(c1) if err != nil { t.Fatal(err) } commit2, err := repo.LookupCommit(c2) if err != nil { t.Fatal(err) } checkout(t, repo, commit1) if got := readReadme(t, repo); got == content { t.Fatalf("README = %q, want %q", got, content) } opts, err := DefaultCherrypickOptions() if err != nil { t.Fatal(err) } idx, err := repo.CherrypickCommit(commit2, commit1, opts) if err != nil { t.Fatal(err) } defer idx.Free() // The file is only updated in the index, not in the working directory. if got := readReadme(t, repo); got == content { t.Errorf("README = %q, want %q", got, content) } if got := repo.State(); got != RepositoryStateNone { t.Errorf("repo.State() = %v, want %v", got, RepositoryStateCherrypick) } if got := idx.EntryCount(); got != 1 { t.Fatalf("idx.EntryCount() = %v, want %v", got, 1) } entry, err := idx.EntryByIndex(0) if err != nil { t.Fatal(err) } if entry.Path != "README" { t.Errorf("entry.Path = %v, want %v", entry.Path, "README") } } git2go-31.4.3/clone.go000066400000000000000000000060111376323343000144260ustar00rootroot00000000000000package git /* #include extern void _go_git_populate_clone_callbacks(git_clone_options *opts); */ import "C" import ( "errors" "runtime" "unsafe" ) type RemoteCreateCallback func(repo *Repository, name, url string) (*Remote, ErrorCode) type CloneOptions struct { *CheckoutOpts *FetchOptions Bare bool CheckoutBranch string RemoteCreateCallback RemoteCreateCallback } func Clone(url string, path string, options *CloneOptions) (*Repository, error) { curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) var err error cOptions := populateCloneOptions(&C.git_clone_options{}, options, &err) defer freeCloneOptions(cOptions) if len(options.CheckoutBranch) != 0 { cOptions.checkout_branch = C.CString(options.CheckoutBranch) } runtime.LockOSThread() defer runtime.UnlockOSThread() var ptr *C.git_repository ret := C.git_clone(&ptr, curl, cpath, cOptions) if ret == C.int(ErrorCodeUser) && err != nil { return nil, err } if ret < 0 { return nil, MakeGitError(ret) } return newRepositoryFromC(ptr), nil } //export remoteCreateCallback func remoteCreateCallback( cremote unsafe.Pointer, crepo unsafe.Pointer, cname, curl *C.char, payload unsafe.Pointer, ) C.int { name := C.GoString(cname) url := C.GoString(curl) repo := newRepositoryFromC((*C.git_repository)(crepo)) // We don't own this repository, so make sure we don't try to free it runtime.SetFinalizer(repo, nil) data, ok := pointerHandles.Get(payload).(*cloneCallbackData) if !ok { panic("invalid remote create callback") } remote, ret := data.options.RemoteCreateCallback(repo, name, url) // clear finalizer as the calling C function will // free the remote itself runtime.SetFinalizer(remote, nil) if ret < 0 { *data.errorTarget = errors.New(ErrorCode(ret).String()) return C.int(ErrorCodeUser) } if remote == nil { panic("no remote created by callback") } cptr := (**C.git_remote)(cremote) *cptr = remote.ptr return C.int(ErrorCodeOK) } type cloneCallbackData struct { options *CloneOptions errorTarget *error } func populateCloneOptions(ptr *C.git_clone_options, opts *CloneOptions, errorTarget *error) *C.git_clone_options { C.git_clone_options_init(ptr, C.GIT_CLONE_OPTIONS_VERSION) if opts == nil { return nil } populateCheckoutOptions(&ptr.checkout_opts, opts.CheckoutOpts, errorTarget) populateFetchOptions(&ptr.fetch_opts, opts.FetchOptions) ptr.bare = cbool(opts.Bare) if opts.RemoteCreateCallback != nil { data := &cloneCallbackData{ options: opts, errorTarget: errorTarget, } // Go v1.1 does not allow to assign a C function pointer C._go_git_populate_clone_callbacks(ptr) ptr.remote_cb_payload = pointerHandles.Track(data) } return ptr } func freeCloneOptions(ptr *C.git_clone_options) { if ptr == nil { return } freeCheckoutOptions(&ptr.checkout_opts) if ptr.remote_cb_payload != nil { pointerHandles.Untrack(ptr.remote_cb_payload) } C.free(unsafe.Pointer(ptr.checkout_branch)) } git2go-31.4.3/clone_test.go000066400000000000000000000027071376323343000154750ustar00rootroot00000000000000package git import ( "io/ioutil" "testing" ) const ( REMOTENAME = "testremote" ) func TestClone(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) seedTestRepo(t, repo) path, err := ioutil.TempDir("", "git2go") checkFatal(t, err) ref, err := repo.References.Lookup("refs/heads/master") checkFatal(t, err) repo2, err := Clone(repo.Path(), path, &CloneOptions{Bare: true}) defer cleanupTestRepo(t, repo2) checkFatal(t, err) ref2, err := repo2.References.Lookup("refs/heads/master") checkFatal(t, err) if ref.Cmp(ref2) != 0 { t.Fatal("reference in clone does not match original ref") } } func TestCloneWithCallback(t *testing.T) { t.Parallel() testPayload := 0 repo := createTestRepo(t) defer cleanupTestRepo(t, repo) seedTestRepo(t, repo) path, err := ioutil.TempDir("", "git2go") checkFatal(t, err) opts := CloneOptions{ Bare: true, RemoteCreateCallback: func(r *Repository, name, url string) (*Remote, ErrorCode) { testPayload += 1 remote, err := r.Remotes.Create(REMOTENAME, url) if err != nil { return nil, ErrorCodeGeneric } return remote, ErrorCodeOK }, } repo2, err := Clone(repo.Path(), path, &opts) defer cleanupTestRepo(t, repo2) checkFatal(t, err) if testPayload != 1 { t.Fatal("Payload's value has not been changed") } remote, err := repo2.Remotes.Lookup(REMOTENAME) if err != nil || remote == nil { t.Fatal("Remote was not created properly") } } git2go-31.4.3/commit.go000066400000000000000000000120561376323343000146240ustar00rootroot00000000000000package git /* #include extern int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr); */ import "C" import ( "runtime" "unsafe" ) // Commit type Commit struct { Object cast_ptr *C.git_commit } func (c *Commit) AsObject() *Object { return &c.Object } func (c *Commit) Message() string { ret := C.GoString(C.git_commit_message(c.cast_ptr)) runtime.KeepAlive(c) return ret } func (c *Commit) MessageEncoding() string { ret := C.GoString(C.git_commit_message_encoding(c.cast_ptr)) runtime.KeepAlive(c) return ret } func (c *Commit) RawMessage() string { ret := C.GoString(C.git_commit_message_raw(c.cast_ptr)) runtime.KeepAlive(c) return ret } // RawHeader gets the full raw text of the commit header. func (c *Commit) RawHeader() string { ret := C.GoString(C.git_commit_raw_header(c.cast_ptr)) runtime.KeepAlive(c) return ret } // ContentToSign returns the content that will be passed to a signing function for this commit func (c *Commit) ContentToSign() string { return c.RawHeader() + "\n" + c.RawMessage() } // CommitSigningCallback defines a function type that takes some data to sign and returns (signature, signature_field, error) type CommitSigningCallback func(string) (signature, signatureField string, err error) // WithSignatureUsing creates a new signed commit from this one using the given signing callback func (c *Commit) WithSignatureUsing(f CommitSigningCallback) (*Oid, error) { signature, signatureField, err := f(c.ContentToSign()) if err != nil { return nil, err } return c.WithSignature(signature, signatureField) } // WithSignature creates a new signed commit from the given signature and signature field func (c *Commit) WithSignature(signature string, signatureField string) (*Oid, error) { totalCommit := c.ContentToSign() oid := new(Oid) var csf *C.char = nil if signatureField != "" { csf = C.CString(signatureField) defer C.free(unsafe.Pointer(csf)) } runtime.LockOSThread() defer runtime.UnlockOSThread() cTotalCommit := C.CString(totalCommit) cSignature := C.CString(signature) defer C.free(unsafe.Pointer(cTotalCommit)) defer C.free(unsafe.Pointer(cSignature)) ret := C.git_commit_create_with_signature( oid.toC(), c.Owner().ptr, cTotalCommit, cSignature, csf, ) runtime.KeepAlive(c) runtime.KeepAlive(oid) if ret < 0 { return nil, MakeGitError(ret) } return oid, nil } func (c *Commit) ExtractSignature() (string, string, error) { var c_signed C.git_buf defer C.git_buf_dispose(&c_signed) var c_signature C.git_buf defer C.git_buf_dispose(&c_signature) oid := c.Id() repo := C.git_commit_owner(c.cast_ptr) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_commit_extract_signature(&c_signature, &c_signed, repo, oid.toC(), nil) runtime.KeepAlive(oid) if ret < 0 { return "", "", MakeGitError(ret) } else { return C.GoString(c_signature.ptr), C.GoString(c_signed.ptr), nil } } func (c *Commit) Summary() string { ret := C.GoString(C.git_commit_summary(c.cast_ptr)) runtime.KeepAlive(c) return ret } func (c *Commit) Tree() (*Tree, error) { var ptr *C.git_tree runtime.LockOSThread() defer runtime.UnlockOSThread() err := C.git_commit_tree(&ptr, c.cast_ptr) runtime.KeepAlive(c) if err < 0 { return nil, MakeGitError(err) } return allocTree(ptr, c.repo), nil } func (c *Commit) TreeId() *Oid { ret := newOidFromC(C.git_commit_tree_id(c.cast_ptr)) runtime.KeepAlive(c) return ret } func (c *Commit) Author() *Signature { cast_ptr := C.git_commit_author(c.cast_ptr) ret := newSignatureFromC(cast_ptr) runtime.KeepAlive(c) return ret } func (c *Commit) Committer() *Signature { cast_ptr := C.git_commit_committer(c.cast_ptr) ret := newSignatureFromC(cast_ptr) runtime.KeepAlive(c) return ret } func (c *Commit) Parent(n uint) *Commit { var cobj *C.git_commit ret := C.git_commit_parent(&cobj, c.cast_ptr, C.uint(n)) if ret != 0 { return nil } parent := allocCommit(cobj, c.repo) runtime.KeepAlive(c) return parent } func (c *Commit) ParentId(n uint) *Oid { ret := newOidFromC(C.git_commit_parent_id(c.cast_ptr, C.uint(n))) runtime.KeepAlive(c) return ret } func (c *Commit) ParentCount() uint { ret := uint(C.git_commit_parentcount(c.cast_ptr)) runtime.KeepAlive(c) return ret } func (c *Commit) Amend(refname string, author, committer *Signature, message string, tree *Tree) (*Oid, error) { var cref *C.char if refname == "" { cref = nil } else { cref = C.CString(refname) defer C.free(unsafe.Pointer(cref)) } cmsg := C.CString(message) defer C.free(unsafe.Pointer(cmsg)) runtime.LockOSThread() defer runtime.UnlockOSThread() authorSig, err := author.toC() if err != nil { return nil, err } defer C.git_signature_free(authorSig) committerSig, err := committer.toC() if err != nil { return nil, err } defer C.git_signature_free(committerSig) oid := new(Oid) cerr := C.git_commit_amend(oid.toC(), c.cast_ptr, cref, authorSig, committerSig, nil, cmsg, tree.cast_ptr) runtime.KeepAlive(oid) runtime.KeepAlive(c) runtime.KeepAlive(tree) if cerr < 0 { return nil, MakeGitError(cerr) } return oid, nil } git2go-31.4.3/config.go000066400000000000000000000233501376323343000146000ustar00rootroot00000000000000package git /* #include */ import "C" import ( "runtime" "unsafe" ) type ConfigLevel int const ( // System-wide on Windows, for compatibility with portable git ConfigLevelProgramdata ConfigLevel = C.GIT_CONFIG_LEVEL_PROGRAMDATA // System-wide configuration file; /etc/gitconfig on Linux systems ConfigLevelSystem ConfigLevel = C.GIT_CONFIG_LEVEL_SYSTEM // XDG compatible configuration file; typically ~/.config/git/config ConfigLevelXDG ConfigLevel = C.GIT_CONFIG_LEVEL_XDG // User-specific configuration file (also called Global configuration // file); typically ~/.gitconfig ConfigLevelGlobal ConfigLevel = C.GIT_CONFIG_LEVEL_GLOBAL // Repository specific configuration file; $WORK_DIR/.git/config on // non-bare repos ConfigLevelLocal ConfigLevel = C.GIT_CONFIG_LEVEL_LOCAL // Application specific configuration file; freely defined by applications ConfigLevelApp ConfigLevel = C.GIT_CONFIG_LEVEL_APP // Represents the highest level available config file (i.e. the most // specific config file available that actually is loaded) ConfigLevelHighest ConfigLevel = C.GIT_CONFIG_HIGHEST_LEVEL ) type ConfigEntry struct { Name string Value string Level ConfigLevel } func newConfigEntryFromC(centry *C.git_config_entry) *ConfigEntry { return &ConfigEntry{ Name: C.GoString(centry.name), Value: C.GoString(centry.value), Level: ConfigLevel(centry.level), } } type Config struct { ptr *C.git_config } // NewConfig creates a new empty configuration object func NewConfig() (*Config, error) { config := new(Config) runtime.LockOSThread() defer runtime.UnlockOSThread() if ret := C.git_config_new(&config.ptr); ret < 0 { return nil, MakeGitError(ret) } return config, nil } // AddFile adds a file-backed backend to the config object at the specified level. func (c *Config) AddFile(path string, level ConfigLevel, force bool) error { cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_config_add_file_ondisk(c.ptr, cpath, C.git_config_level_t(level), nil, cbool(force)) runtime.KeepAlive(c) if ret < 0 { return MakeGitError(ret) } return nil } func (c *Config) LookupInt32(name string) (int32, error) { var out C.int32_t cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_config_get_int32(&out, c.ptr, cname) runtime.KeepAlive(c) if ret < 0 { return 0, MakeGitError(ret) } return int32(out), nil } func (c *Config) LookupInt64(name string) (int64, error) { var out C.int64_t cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_config_get_int64(&out, c.ptr, cname) runtime.KeepAlive(c) if ret < 0 { return 0, MakeGitError(ret) } return int64(out), nil } func (c *Config) LookupString(name string) (string, error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) valBuf := C.git_buf{} runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_config_get_string_buf(&valBuf, c.ptr, cname) runtime.KeepAlive(c) if ret < 0 { return "", MakeGitError(ret) } defer C.git_buf_dispose(&valBuf) return C.GoString(valBuf.ptr), nil } func (c *Config) LookupBool(name string) (bool, error) { var out C.int cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_config_get_bool(&out, c.ptr, cname) runtime.KeepAlive(c) if ret < 0 { return false, MakeGitError(ret) } return out != 0, nil } func (c *Config) NewMultivarIterator(name, regexp string) (*ConfigIterator, error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) var cregexp *C.char if regexp == "" { cregexp = nil } else { cregexp = C.CString(regexp) defer C.free(unsafe.Pointer(cregexp)) } iter := &ConfigIterator{cfg: c} runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_config_multivar_iterator_new(&iter.ptr, c.ptr, cname, cregexp) if ret < 0 { return nil, MakeGitError(ret) } runtime.SetFinalizer(iter, (*ConfigIterator).Free) return iter, nil } // NewIterator creates an iterator over each entry in the // configuration func (c *Config) NewIterator() (*ConfigIterator, error) { iter := &ConfigIterator{cfg: c} runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_config_iterator_new(&iter.ptr, c.ptr) if ret < 0 { return nil, MakeGitError(ret) } return iter, nil } // NewIteratorGlob creates an iterator over each entry in the // configuration whose name matches the given regular expression func (c *Config) NewIteratorGlob(regexp string) (*ConfigIterator, error) { iter := &ConfigIterator{cfg: c} cregexp := C.CString(regexp) defer C.free(unsafe.Pointer(cregexp)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_config_iterator_glob_new(&iter.ptr, c.ptr, cregexp) if ret < 0 { return nil, MakeGitError(ret) } return iter, nil } func (c *Config) SetString(name, value string) (err error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) cvalue := C.CString(value) defer C.free(unsafe.Pointer(cvalue)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_config_set_string(c.ptr, cname, cvalue) runtime.KeepAlive(c) if ret < 0 { return MakeGitError(ret) } return nil } func (c *Config) Free() { runtime.SetFinalizer(c, nil) C.git_config_free(c.ptr) } 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)) runtime.KeepAlive(c) if ret < 0 { return MakeGitError(ret) } return nil } func (c *Config) SetInt64(name string, value int64) (err error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_config_set_int64(c.ptr, cname, C.int64_t(value)) runtime.KeepAlive(c) if ret < 0 { return MakeGitError(ret) } return nil } func (c *Config) SetBool(name string, value bool) (err error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_config_set_bool(c.ptr, cname, cbool(value)) runtime.KeepAlive(c) if ret < 0 { return MakeGitError(ret) } return nil } func (c *Config) SetMultivar(name, regexp, value string) (err error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) cregexp := C.CString(regexp) defer C.free(unsafe.Pointer(cregexp)) cvalue := C.CString(value) defer C.free(unsafe.Pointer(cvalue)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_config_set_multivar(c.ptr, cname, cregexp, cvalue) runtime.KeepAlive(c) if ret < 0 { return MakeGitError(ret) } return nil } func (c *Config) Delete(name string) error { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_config_delete_entry(c.ptr, cname) runtime.KeepAlive(c) if ret < 0 { return MakeGitError(ret) } return nil } // OpenLevel creates a single-level focused config object from a multi-level one func (c *Config) OpenLevel(parent *Config, level ConfigLevel) (*Config, error) { config := new(Config) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_config_open_level(&config.ptr, parent.ptr, C.git_config_level_t(level)) runtime.KeepAlive(c) runtime.KeepAlive(parent) if ret < 0 { return nil, MakeGitError(ret) } return config, nil } // OpenOndisk creates a new config instance containing a single on-disk file func OpenOndisk(path string) (*Config, error) { cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) config := new(Config) runtime.LockOSThread() defer runtime.UnlockOSThread() if ret := C.git_config_open_ondisk(&config.ptr, cpath); ret < 0 { return nil, MakeGitError(ret) } return config, nil } type ConfigIterator struct { ptr *C.git_config_iterator cfg *Config } // Next returns the next entry for this iterator 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) } entry := newConfigEntryFromC(centry) runtime.KeepAlive(iter) return entry, nil } 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_dispose(&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_dispose(&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_dispose(&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 } // ConfigFindProgramdata locate the path to the configuration file in ProgramData. // // Look for the file in %PROGRAMDATA%\Git\config used by portable git. func ConfigFindProgramdata() (string, error) { var buf C.git_buf defer C.git_buf_dispose(&buf) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_config_find_programdata(&buf) if ret < 0 { return "", MakeGitError(ret) } return C.GoString(buf.ptr), nil } git2go-31.4.3/config_test.go000066400000000000000000000036511376323343000156410ustar00rootroot00000000000000package git import ( "os" "testing" ) var tempConfig = "./temp.gitconfig" func setupConfig() (*Config, error) { var ( c *Config err error ) c, err = OpenOndisk(tempConfig) if err != nil { return nil, err } err = c.SetString("foo.bar", "baz") if err != nil { return nil, err } err = c.SetBool("foo.bool", true) if err != nil { return nil, err } err = c.SetInt32("foo.int32", 32) if err != nil { return nil, err } err = c.SetInt64("foo.int64", 64) if err != nil { return nil, err } return c, err } func cleanupConfig() { os.Remove(tempConfig) } type TestRunner func(*Config, *testing.T) var tests = []TestRunner{ // LookupString func(c *Config, t *testing.T) { val, err := c.LookupString("foo.bar") if err != nil { t.Errorf("Got LookupString error: '%v', expected none\n", err) } if val != "baz" { t.Errorf("Got '%s' from LookupString, expected 'bar'\n", val) } }, // LookupBool func(c *Config, t *testing.T) { val, err := c.LookupBool("foo.bool") if err != nil { t.Errorf("Got LookupBool error: '%v', expected none\n", err) } if !val { t.Errorf("Got %t from LookupBool, expected 'false'\n", val) } }, // LookupInt32 func(c *Config, t *testing.T) { val, err := c.LookupInt32("foo.int32") if err != nil { t.Errorf("Got LookupInt32 error: '%v', expected none\n", err) } if val != 32 { t.Errorf("Got %v, expected 32\n", val) } }, // LookupInt64 func(c *Config, t *testing.T) { val, err := c.LookupInt64("foo.int64") if err != nil { t.Errorf("Got LookupInt64 error: '%v', expected none\n", err) } if val != 64 { t.Errorf("Got %v, expected 64\n", val) } }, } func TestConfigLookups(t *testing.T) { t.Parallel() var ( err error c *Config ) c, err = setupConfig() defer cleanupConfig() if err != nil { t.Errorf("Setup error: '%v'. Expected none\n", err) return } defer c.Free() for _, test := range tests { test(c, t) } } git2go-31.4.3/credentials.go000066400000000000000000000164311376323343000156320ustar00rootroot00000000000000package git /* #include #include #include git_credential_t _go_git_credential_credtype(git_credential *cred); void _go_git_populate_credential_ssh_custom(git_credential_ssh_custom *cred); */ import "C" import ( "crypto/rand" "fmt" "runtime" "strings" "unsafe" "golang.org/x/crypto/ssh" ) // CredentialType is a bitmask of supported credential types. // // This represents the various types of authentication methods supported by the // library. type CredentialType uint const ( CredentialTypeUserpassPlaintext CredentialType = C.GIT_CREDENTIAL_USERPASS_PLAINTEXT CredentialTypeSSHKey CredentialType = C.GIT_CREDENTIAL_SSH_KEY CredentialTypeSSHCustom CredentialType = C.GIT_CREDENTIAL_SSH_CUSTOM CredentialTypeDefault CredentialType = C.GIT_CREDENTIAL_DEFAULT CredentialTypeSSHInteractive CredentialType = C.GIT_CREDENTIAL_SSH_INTERACTIVE CredentialTypeUsername CredentialType = C.GIT_CREDENTIAL_USERNAME CredentialTypeSSHMemory CredentialType = C.GIT_CREDENTIAL_SSH_MEMORY ) func (t CredentialType) String() string { if t == 0 { return "CredentialType(0)" } var parts []string if (t & CredentialTypeUserpassPlaintext) != 0 { parts = append(parts, "UserpassPlaintext") t &= ^CredentialTypeUserpassPlaintext } if (t & CredentialTypeSSHKey) != 0 { parts = append(parts, "SSHKey") t &= ^CredentialTypeSSHKey } if (t & CredentialTypeSSHCustom) != 0 { parts = append(parts, "SSHCustom") t &= ^CredentialTypeSSHCustom } if (t & CredentialTypeDefault) != 0 { parts = append(parts, "Default") t &= ^CredentialTypeDefault } if (t & CredentialTypeSSHInteractive) != 0 { parts = append(parts, "SSHInteractive") t &= ^CredentialTypeSSHInteractive } if (t & CredentialTypeUsername) != 0 { parts = append(parts, "Username") t &= ^CredentialTypeUsername } if (t & CredentialTypeSSHMemory) != 0 { parts = append(parts, "SSHMemory") t &= ^CredentialTypeSSHMemory } if t != 0 { parts = append(parts, fmt.Sprintf("CredentialType(%#x)", t)) } return strings.Join(parts, "|") } type Credential struct { ptr *C.git_credential } func newCredential() *Credential { cred := &Credential{} runtime.SetFinalizer(cred, (*Credential).Free) return cred } func (o *Credential) HasUsername() bool { if C.git_credential_has_username(o.ptr) == 1 { return true } return false } func (o *Credential) Type() CredentialType { return (CredentialType)(C._go_git_credential_credtype(o.ptr)) } func (o *Credential) Free() { C.git_credential_free(o.ptr) runtime.SetFinalizer(o, nil) o.ptr = nil } func NewCredentialUsername(username string) (*Credential, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() cred := newCredential() cusername := C.CString(username) ret := C.git_credential_username_new(&cred.ptr, cusername) if ret != 0 { cred.Free() return nil, MakeGitError(ret) } return cred, nil } func NewCredentialUserpassPlaintext(username string, password string) (*Credential, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() cred := newCredential() cusername := C.CString(username) defer C.free(unsafe.Pointer(cusername)) cpassword := C.CString(password) defer C.free(unsafe.Pointer(cpassword)) ret := C.git_credential_userpass_plaintext_new(&cred.ptr, cusername, cpassword) if ret != 0 { cred.Free() return nil, MakeGitError(ret) } return cred, nil } // NewCredentialSSHKey creates new ssh credentials reading the public and private keys // from the file system. func NewCredentialSSHKey(username string, publicKeyPath string, privateKeyPath string, passphrase string) (*Credential, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() cred := newCredential() cusername := C.CString(username) defer C.free(unsafe.Pointer(cusername)) cpublickey := C.CString(publicKeyPath) defer C.free(unsafe.Pointer(cpublickey)) cprivatekey := C.CString(privateKeyPath) defer C.free(unsafe.Pointer(cprivatekey)) cpassphrase := C.CString(passphrase) defer C.free(unsafe.Pointer(cpassphrase)) ret := C.git_credential_ssh_key_new(&cred.ptr, cusername, cpublickey, cprivatekey, cpassphrase) if ret != 0 { cred.Free() return nil, MakeGitError(ret) } return cred, nil } // NewCredentialSSHKeyFromMemory creates new ssh credentials using the publicKey and privateKey // arguments as the values for the public and private keys. func NewCredentialSSHKeyFromMemory(username string, publicKey string, privateKey string, passphrase string) (*Credential, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() cred := newCredential() cusername := C.CString(username) defer C.free(unsafe.Pointer(cusername)) cpublickey := C.CString(publicKey) defer C.free(unsafe.Pointer(cpublickey)) cprivatekey := C.CString(privateKey) defer C.free(unsafe.Pointer(cprivatekey)) cpassphrase := C.CString(passphrase) defer C.free(unsafe.Pointer(cpassphrase)) ret := C.git_credential_ssh_key_memory_new(&cred.ptr, cusername, cpublickey, cprivatekey, cpassphrase) if ret != 0 { cred.Free() return nil, MakeGitError(ret) } return cred, nil } func NewCredentialSSHKeyFromAgent(username string) (*Credential, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() cred := newCredential() cusername := C.CString(username) defer C.free(unsafe.Pointer(cusername)) ret := C.git_credential_ssh_key_from_agent(&cred.ptr, cusername) if ret != 0 { cred.Free() return nil, MakeGitError(ret) } return cred, nil } type credentialSSHCustomData struct { signer ssh.Signer } //export credentialSSHCustomFree func credentialSSHCustomFree(cred *C.git_credential_ssh_custom) { if cred == nil { return } C.free(unsafe.Pointer(cred.username)) C.free(unsafe.Pointer(cred.publickey)) pointerHandles.Untrack(cred.payload) C.free(unsafe.Pointer(cred)) } //export credentialSSHSignCallback func credentialSSHSignCallback( errorMessage **C.char, sig **C.uchar, sig_len *C.size_t, data *C.uchar, data_len C.size_t, handle unsafe.Pointer, ) C.int { signer := pointerHandles.Get(handle).(*credentialSSHCustomData).signer signature, err := signer.Sign(rand.Reader, C.GoBytes(unsafe.Pointer(data), C.int(data_len))) if err != nil { return setCallbackError(errorMessage, err) } *sig = (*C.uchar)(C.CBytes(signature.Blob)) *sig_len = C.size_t(len(signature.Blob)) return C.int(ErrorCodeOK) } // NewCredentialSSHKeyFromSigner creates new SSH credentials using the provided signer. func NewCredentialSSHKeyFromSigner(username string, signer ssh.Signer) (*Credential, error) { publicKey := signer.PublicKey().Marshal() ccred := (*C.git_credential_ssh_custom)(C.calloc(1, C.size_t(unsafe.Sizeof(C.git_credential_ssh_custom{})))) ccred.parent.credtype = C.GIT_CREDENTIAL_SSH_CUSTOM ccred.username = C.CString(username) ccred.publickey = (*C.char)(C.CBytes(publicKey)) ccred.publickey_len = C.size_t(len(publicKey)) C._go_git_populate_credential_ssh_custom(ccred) data := credentialSSHCustomData{ signer: signer, } ccred.payload = pointerHandles.Track(&data) cred := newCredential() cred.ptr = &ccred.parent return cred, nil } func NewCredentialDefault() (*Credential, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() cred := newCredential() ret := C.git_credential_default_new(&cred.ptr) if ret != 0 { cred.Free() return nil, MakeGitError(ret) } return cred, nil } git2go-31.4.3/delta_string.go000066400000000000000000000016761376323343000160210ustar00rootroot00000000000000// Code generated by "stringer -type Delta -trimprefix Delta -tags static"; DO NOT EDIT. package git import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[DeltaUnmodified-0] _ = x[DeltaAdded-1] _ = x[DeltaDeleted-2] _ = x[DeltaModified-3] _ = x[DeltaRenamed-4] _ = x[DeltaCopied-5] _ = x[DeltaIgnored-6] _ = x[DeltaUntracked-7] _ = x[DeltaTypeChange-8] _ = x[DeltaUnreadable-9] _ = x[DeltaConflicted-10] } const _Delta_name = "UnmodifiedAddedDeletedModifiedRenamedCopiedIgnoredUntrackedTypeChangeUnreadableConflicted" var _Delta_index = [...]uint8{0, 10, 15, 22, 30, 37, 43, 50, 59, 69, 79, 89} func (i Delta) String() string { if i < 0 || i >= Delta(len(_Delta_index)-1) { return "Delta(" + strconv.FormatInt(int64(i), 10) + ")" } return _Delta_name[_Delta_index[i]:_Delta_index[i+1]] } git2go-31.4.3/deprecated.go000066400000000000000000000127201376323343000154320ustar00rootroot00000000000000package git /* #include */ import "C" import ( "unsafe" ) // The constants, functions, and types in this files are slated for deprecation // in the next major version. // blob.go // BlobChunkCallback is not used. type BlobChunkCallback func(maxLen int) ([]byte, error) // BlobCallbackData is not used. type BlobCallbackData struct { Callback BlobChunkCallback Error error } // checkout.go // CheckoutOpts is a deprecated alias of CheckoutOptions. type CheckoutOpts = CheckoutOptions // credentials.go // CredType is a deprecated alias of CredentialType type CredType = CredentialType const ( CredTypeUserpassPlaintext = CredentialTypeUserpassPlaintext CredTypeSshKey = CredentialTypeSSHKey CredTypeSshCustom = CredentialTypeSSHCustom CredTypeDefault = CredentialTypeDefault ) // Cred is a deprecated alias of Credential type Cred = Credential // NewCredUsername is a deprecated alias of NewCredentialUsername. func NewCredUsername(username string) (*Cred, error) { return NewCredentialUsername(username) } // NewCredUserpassPlaintext is a deprecated alias of NewCredentialUserpassPlaintext. func NewCredUserpassPlaintext(username string, password string) (*Cred, error) { return NewCredentialUserpassPlaintext(username, password) } // NewCredSshKey is a deprecated alias of NewCredentialSshKey. func NewCredSshKey(username string, publicKeyPath string, privateKeyPath string, passphrase string) (*Cred, error) { return NewCredentialSSHKey(username, publicKeyPath, privateKeyPath, passphrase) } // NewCredSshKeyFromMemory is a deprecated alias of NewCredentialSSHKeyFromMemory. func NewCredSshKeyFromMemory(username string, publicKey string, privateKey string, passphrase string) (*Cred, error) { return NewCredentialSSHKeyFromMemory(username, publicKey, privateKey, passphrase) } // NewCredSshKeyFromAgent is a deprecated alias of NewCredentialSSHFromAgent. func NewCredSshKeyFromAgent(username string) (*Cred, error) { return NewCredentialSSHKeyFromAgent(username) } // NewCredDefault is a deprecated alias fof NewCredentialDefault. func NewCredDefault() (*Cred, error) { return NewCredentialDefault() } // features.go const ( // FeatureHttps is a deprecated alias of FeatureHTTPS. FeatureHttps = FeatureHTTPS // FeatureSsh is a deprecated alias of FeatureSSH. FeatureSsh = FeatureSSH ) // git.go const ( ErrClassNone = ErrorClassNone ErrClassNoMemory = ErrorClassNoMemory ErrClassOs = ErrorClassOS ErrClassInvalid = ErrorClassInvalid ErrClassReference = ErrorClassReference ErrClassZlib = ErrorClassZlib ErrClassRepository = ErrorClassRepository ErrClassConfig = ErrorClassConfig ErrClassRegex = ErrorClassRegex ErrClassOdb = ErrorClassOdb ErrClassIndex = ErrorClassIndex ErrClassObject = ErrorClassObject ErrClassNet = ErrorClassNet ErrClassTag = ErrorClassTag ErrClassTree = ErrorClassTree ErrClassIndexer = ErrorClassIndexer ErrClassSSL = ErrorClassSSL ErrClassSubmodule = ErrorClassSubmodule ErrClassThread = ErrorClassThread ErrClassStash = ErrorClassStash ErrClassCheckout = ErrorClassCheckout ErrClassFetchHead = ErrorClassFetchHead ErrClassMerge = ErrorClassMerge ErrClassSsh = ErrorClassSSH ErrClassFilter = ErrorClassFilter ErrClassRevert = ErrorClassRevert ErrClassCallback = ErrorClassCallback ErrClassRebase = ErrorClassRebase ErrClassPatch = ErrorClassPatch ) const ( ErrOk = ErrorCodeOK ErrGeneric = ErrorCodeGeneric ErrNotFound = ErrorCodeNotFound ErrExists = ErrorCodeExists ErrAmbiguous = ErrorCodeAmbiguous ErrAmbigious = ErrorCodeAmbiguous ErrBuffs = ErrorCodeBuffs ErrUser = ErrorCodeUser ErrBareRepo = ErrorCodeBareRepo ErrUnbornBranch = ErrorCodeUnbornBranch ErrUnmerged = ErrorCodeUnmerged ErrNonFastForward = ErrorCodeNonFastForward ErrInvalidSpec = ErrorCodeInvalidSpec ErrConflict = ErrorCodeConflict ErrLocked = ErrorCodeLocked ErrModified = ErrorCodeModified ErrAuth = ErrorCodeAuth ErrCertificate = ErrorCodeCertificate ErrApplied = ErrorCodeApplied ErrPeel = ErrorCodePeel ErrEOF = ErrorCodeEOF ErrUncommitted = ErrorCodeUncommitted ErrDirectory = ErrorCodeDirectory ErrMergeConflict = ErrorCodeMergeConflict ErrPassthrough = ErrorCodePassthrough ErrIterOver = ErrorCodeIterOver ErrApplyFail = ErrorCodeApplyFail ) // index.go // IndexAddOpts is a deprecated alias of IndexAddOption. type IndexAddOpts = IndexAddOption // IndexStageOpts is a deprecated alias of IndexStageState. type IndexStageOpts = IndexStageState // submodule.go // SubmoduleCbk is a deprecated alias of SubmoduleCallback. type SubmoduleCbk = SubmoduleCallback // SubmoduleVisitor is not used. func SubmoduleVisitor(csub unsafe.Pointer, name *C.char, handle unsafe.Pointer) C.int { sub := &Submodule{(*C.git_submodule)(csub), nil} callback, ok := pointerHandles.Get(handle).(SubmoduleCallback) if !ok { panic("invalid submodule visitor callback") } return (C.int)(callback(sub, C.GoString(name))) } // tree.go // CallbackGitTreeWalk is not used. func CallbackGitTreeWalk(_root *C.char, entry *C.git_tree_entry, ptr unsafe.Pointer) C.int { root := C.GoString(_root) if callback, ok := pointerHandles.Get(ptr).(TreeWalkCallback); ok { return C.int(callback(root, newTreeEntry(entry))) } else { panic("invalid treewalk callback") } } git2go-31.4.3/describe.go000066400000000000000000000155611376323343000151200ustar00rootroot00000000000000package git /* #include */ import "C" import ( "runtime" "unsafe" ) // DescribeOptions represents the describe operation configuration. // // You can use DefaultDescribeOptions() to get default options. type DescribeOptions struct { // How many tags as candidates to consider to describe the input commit-ish. // Increasing it above 10 will take slightly longer but may produce a more // accurate result. 0 will cause only exact matches to be output. MaxCandidatesTags uint // default: 10 // By default describe only shows annotated tags. Change this in order // to show all refs from refs/tags or refs/. Strategy DescribeOptionsStrategy // default: DescribeDefault // Only consider tags matching the given glob(7) pattern, excluding // the "refs/tags/" prefix. Can be used to avoid leaking private // tags from the repo. Pattern string // When calculating the distance from the matching tag or // reference, only walk down the first-parent ancestry. OnlyFollowFirstParent bool // If no matching tag or reference is found, the describe // operation would normally fail. If this option is set, it // will instead fall back to showing the full id of the commit. ShowCommitOidAsFallback bool } // DefaultDescribeOptions returns default options for the describe operation. func DefaultDescribeOptions() (DescribeOptions, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() opts := C.git_describe_options{} ecode := C.git_describe_options_init(&opts, C.GIT_DESCRIBE_OPTIONS_VERSION) if ecode < 0 { return DescribeOptions{}, MakeGitError(ecode) } return DescribeOptions{ MaxCandidatesTags: uint(opts.max_candidates_tags), Strategy: DescribeOptionsStrategy(opts.describe_strategy), }, nil } // DescribeFormatOptions can be used for formatting the describe string. // // You can use DefaultDescribeFormatOptions() to get default options. type DescribeFormatOptions struct { // Size of the abbreviated commit id to use. This value is the // lower bound for the length of the abbreviated string. AbbreviatedSize uint // default: 7 // Set to use the long format even when a shorter name could be used. AlwaysUseLongFormat bool // If the workdir is dirty and this is set, this string will be // appended to the description string. DirtySuffix string } // DefaultDescribeFormatOptions returns default options for formatting // the output. func DefaultDescribeFormatOptions() (DescribeFormatOptions, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() opts := C.git_describe_format_options{} ecode := C.git_describe_format_options_init(&opts, C.GIT_DESCRIBE_FORMAT_OPTIONS_VERSION) if ecode < 0 { return DescribeFormatOptions{}, MakeGitError(ecode) } return DescribeFormatOptions{ AbbreviatedSize: uint(opts.abbreviated_size), AlwaysUseLongFormat: opts.always_use_long_format == 1, }, nil } // DescribeOptionsStrategy behaves like the --tags and --all options // to git-describe, namely they say to look for any reference in // either refs/tags/ or refs/ respectively. // // By default it only shows annotated tags. type DescribeOptionsStrategy uint // Describe strategy options. const ( DescribeDefault DescribeOptionsStrategy = C.GIT_DESCRIBE_DEFAULT DescribeTags DescribeOptionsStrategy = C.GIT_DESCRIBE_TAGS DescribeAll DescribeOptionsStrategy = C.GIT_DESCRIBE_ALL ) // Describe performs the describe operation on the commit. func (c *Commit) Describe(opts *DescribeOptions) (*DescribeResult, error) { var resultPtr *C.git_describe_result var cDescribeOpts *C.git_describe_options if opts != nil { var cpattern *C.char if len(opts.Pattern) > 0 { cpattern = C.CString(opts.Pattern) defer C.free(unsafe.Pointer(cpattern)) } cDescribeOpts = &C.git_describe_options{ version: C.GIT_DESCRIBE_OPTIONS_VERSION, max_candidates_tags: C.uint(opts.MaxCandidatesTags), describe_strategy: C.uint(opts.Strategy), pattern: cpattern, only_follow_first_parent: cbool(opts.OnlyFollowFirstParent), show_commit_oid_as_fallback: cbool(opts.ShowCommitOidAsFallback), } } runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_describe_commit(&resultPtr, c.ptr, cDescribeOpts) runtime.KeepAlive(c) if ecode < 0 { return nil, MakeGitError(ecode) } return newDescribeResultFromC(resultPtr), nil } // DescribeWorkdir describes the working tree. It means describe HEAD // and appends (-dirty by default) if the working tree is dirty. func (repo *Repository) DescribeWorkdir(opts *DescribeOptions) (*DescribeResult, error) { var resultPtr *C.git_describe_result var cDescribeOpts *C.git_describe_options if opts != nil { var cpattern *C.char if len(opts.Pattern) > 0 { cpattern = C.CString(opts.Pattern) defer C.free(unsafe.Pointer(cpattern)) } cDescribeOpts = &C.git_describe_options{ version: C.GIT_DESCRIBE_OPTIONS_VERSION, max_candidates_tags: C.uint(opts.MaxCandidatesTags), describe_strategy: C.uint(opts.Strategy), pattern: cpattern, only_follow_first_parent: cbool(opts.OnlyFollowFirstParent), show_commit_oid_as_fallback: cbool(opts.ShowCommitOidAsFallback), } } runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_describe_workdir(&resultPtr, repo.ptr, cDescribeOpts) runtime.KeepAlive(repo) if ecode < 0 { return nil, MakeGitError(ecode) } return newDescribeResultFromC(resultPtr), nil } // DescribeResult represents the output from the 'git_describe_commit' // and 'git_describe_workdir' functions in libgit2. // // Use Format() to get a string out of it. type DescribeResult struct { ptr *C.git_describe_result } func newDescribeResultFromC(ptr *C.git_describe_result) *DescribeResult { result := &DescribeResult{ ptr: ptr, } runtime.SetFinalizer(result, (*DescribeResult).Free) return result } // Format prints the DescribeResult as a string. func (result *DescribeResult) Format(opts *DescribeFormatOptions) (string, error) { resultBuf := C.git_buf{} var cFormatOpts *C.git_describe_format_options if opts != nil { cDirtySuffix := C.CString(opts.DirtySuffix) defer C.free(unsafe.Pointer(cDirtySuffix)) cFormatOpts = &C.git_describe_format_options{ version: C.GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, abbreviated_size: C.uint(opts.AbbreviatedSize), always_use_long_format: cbool(opts.AlwaysUseLongFormat), dirty_suffix: cDirtySuffix, } } runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_describe_format(&resultBuf, result.ptr, cFormatOpts) runtime.KeepAlive(result) if ecode < 0 { return "", MakeGitError(ecode) } defer C.git_buf_dispose(&resultBuf) return C.GoString(resultBuf.ptr), nil } // Free cleans up the C reference. func (result *DescribeResult) Free() { runtime.SetFinalizer(result, nil) C.git_describe_result_free(result.ptr) result.ptr = nil } git2go-31.4.3/describe_test.go000066400000000000000000000052261376323343000161540ustar00rootroot00000000000000package git import ( "path" "runtime" "strings" "testing" ) func TestDescribeCommit(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) describeOpts, err := DefaultDescribeOptions() checkFatal(t, err) formatOpts, err := DefaultDescribeFormatOptions() checkFatal(t, err) commitID, _ := seedTestRepo(t, repo) commit, err := repo.LookupCommit(commitID) checkFatal(t, err) // No annotated tags can be used to describe master _, err = commit.Describe(&describeOpts) checkDescribeNoRefsFound(t, err) // Fallback fallback := describeOpts fallback.ShowCommitOidAsFallback = true result, err := commit.Describe(&fallback) checkFatal(t, err) resultStr, err := result.Format(&formatOpts) checkFatal(t, err) compareStrings(t, "473bf77", resultStr) // Abbreviated abbreviated := formatOpts abbreviated.AbbreviatedSize = 2 result, err = commit.Describe(&fallback) checkFatal(t, err) resultStr, err = result.Format(&abbreviated) checkFatal(t, err) compareStrings(t, "473b", resultStr) createTestTag(t, repo, commit) // Exact tag patternOpts := describeOpts patternOpts.Pattern = "v[0-9]*" result, err = commit.Describe(&patternOpts) checkFatal(t, err) resultStr, err = result.Format(&formatOpts) checkFatal(t, err) compareStrings(t, "v0.0.0", resultStr) // Pattern no match patternOpts.Pattern = "v[1-9]*" result, err = commit.Describe(&patternOpts) checkDescribeNoRefsFound(t, err) commitID, _ = updateReadme(t, repo, "update1") commit, err = repo.LookupCommit(commitID) checkFatal(t, err) // Tag-1 result, err = commit.Describe(&describeOpts) checkFatal(t, err) resultStr, err = result.Format(&formatOpts) checkFatal(t, err) compareStrings(t, "v0.0.0-1-gd88ef8d", resultStr) // Strategy: All describeOpts.Strategy = DescribeAll result, err = commit.Describe(&describeOpts) checkFatal(t, err) resultStr, err = result.Format(&formatOpts) checkFatal(t, err) compareStrings(t, "heads/master", resultStr) repo.CreateBranch("hotfix", commit, false) // Workdir (branch) result, err = repo.DescribeWorkdir(&describeOpts) checkFatal(t, err) resultStr, err = result.Format(&formatOpts) checkFatal(t, err) compareStrings(t, "heads/hotfix", resultStr) } func checkDescribeNoRefsFound(t *testing.T, err error) { // The failure happens at wherever we were called, not here _, file, line, ok := runtime.Caller(1) expectedString := "no reference found, cannot describe anything" if !ok { t.Fatalf("Unable to get caller") } if err == nil || !strings.Contains(err.Error(), expectedString) { t.Fatalf( "%s:%v: was expecting error %v, got %v", path.Base(file), line, expectedString, err, ) } } git2go-31.4.3/diff.go000066400000000000000000000732001376323343000142420ustar00rootroot00000000000000package git /* #include extern void _go_git_populate_apply_callbacks(git_apply_options *options); extern int _go_git_diff_foreach(git_diff *diff, int eachFile, int eachHunk, int eachLine, void *payload); extern void _go_git_setup_diff_notify_callbacks(git_diff_options* opts); extern int _go_git_diff_blobs(git_blob *old, const char *old_path, git_blob *new, const char *new_path, git_diff_options *opts, int eachFile, int eachHunk, int eachLine, void *payload); */ import "C" import ( "errors" "runtime" "unsafe" ) type DiffFlag uint32 const ( DiffFlagBinary DiffFlag = C.GIT_DIFF_FLAG_BINARY DiffFlagNotBinary DiffFlag = C.GIT_DIFF_FLAG_NOT_BINARY DiffFlagValidOid DiffFlag = C.GIT_DIFF_FLAG_VALID_ID DiffFlagExists DiffFlag = C.GIT_DIFF_FLAG_EXISTS ) type Delta int const ( DeltaUnmodified Delta = C.GIT_DELTA_UNMODIFIED DeltaAdded Delta = C.GIT_DELTA_ADDED DeltaDeleted Delta = C.GIT_DELTA_DELETED DeltaModified Delta = C.GIT_DELTA_MODIFIED DeltaRenamed Delta = C.GIT_DELTA_RENAMED DeltaCopied Delta = C.GIT_DELTA_COPIED DeltaIgnored Delta = C.GIT_DELTA_IGNORED DeltaUntracked Delta = C.GIT_DELTA_UNTRACKED DeltaTypeChange Delta = C.GIT_DELTA_TYPECHANGE DeltaUnreadable Delta = C.GIT_DELTA_UNREADABLE DeltaConflicted Delta = C.GIT_DELTA_CONFLICTED ) //go:generate stringer -type Delta -trimprefix Delta -tags static type DiffLineType int const ( DiffLineContext DiffLineType = C.GIT_DIFF_LINE_CONTEXT DiffLineAddition DiffLineType = C.GIT_DIFF_LINE_ADDITION DiffLineDeletion DiffLineType = C.GIT_DIFF_LINE_DELETION DiffLineContextEOFNL DiffLineType = C.GIT_DIFF_LINE_CONTEXT_EOFNL DiffLineAddEOFNL DiffLineType = C.GIT_DIFF_LINE_ADD_EOFNL DiffLineDelEOFNL DiffLineType = C.GIT_DIFF_LINE_DEL_EOFNL DiffLineFileHdr DiffLineType = C.GIT_DIFF_LINE_FILE_HDR DiffLineHunkHdr DiffLineType = C.GIT_DIFF_LINE_HUNK_HDR DiffLineBinary DiffLineType = C.GIT_DIFF_LINE_BINARY ) //go:generate stringer -type DiffLineType -trimprefix DiffLine -tags static type DiffFile struct { Path string Oid *Oid Size int Flags DiffFlag Mode uint16 } func diffFileFromC(file *C.git_diff_file) DiffFile { return DiffFile{ Path: C.GoString(file.path), Oid: newOidFromC(&file.id), Size: int(file.size), Flags: DiffFlag(file.flags), Mode: uint16(file.mode), } } type DiffDelta struct { Status Delta Flags DiffFlag Similarity uint16 OldFile DiffFile NewFile DiffFile } func diffDeltaFromC(delta *C.git_diff_delta) DiffDelta { return DiffDelta{ Status: Delta(delta.status), Flags: DiffFlag(delta.flags), Similarity: uint16(delta.similarity), OldFile: diffFileFromC(&delta.old_file), NewFile: diffFileFromC(&delta.new_file), } } type DiffHunk struct { OldStart int OldLines int NewStart int NewLines int Header string } func diffHunkFromC(hunk *C.git_diff_hunk) DiffHunk { return DiffHunk{ OldStart: int(hunk.old_start), OldLines: int(hunk.old_lines), NewStart: int(hunk.new_start), NewLines: int(hunk.new_lines), Header: C.GoStringN(&hunk.header[0], C.int(hunk.header_len)), } } type DiffLine struct { Origin DiffLineType OldLineno int NewLineno int NumLines int Content string } func diffLineFromC(line *C.git_diff_line) DiffLine { return DiffLine{ Origin: DiffLineType(line.origin), OldLineno: int(line.old_lineno), NewLineno: int(line.new_lineno), NumLines: int(line.num_lines), Content: C.GoStringN(line.content, C.int(line.content_len)), } } type Diff struct { ptr *C.git_diff repo *Repository runFinalizer bool } func (diff *Diff) NumDeltas() (int, error) { if diff.ptr == nil { return -1, ErrInvalid } ret := int(C.git_diff_num_deltas(diff.ptr)) runtime.KeepAlive(diff) return ret, nil } func (diff *Diff) Delta(index int) (DiffDelta, error) { if diff.ptr == nil { return DiffDelta{}, ErrInvalid } ptr := C.git_diff_get_delta(diff.ptr, C.size_t(index)) ret := diffDeltaFromC(ptr) runtime.KeepAlive(diff) return ret, nil } // deprecated: You should use `Diff.Delta()` instead. func (diff *Diff) GetDelta(index int) (DiffDelta, error) { return diff.Delta(index) } func newDiffFromC(ptr *C.git_diff, repo *Repository) *Diff { if ptr == nil { return nil } diff := &Diff{ ptr: ptr, repo: repo, runFinalizer: true, } runtime.SetFinalizer(diff, (*Diff).Free) return diff } func (diff *Diff) Free() error { if diff.ptr == nil { return ErrInvalid } if !diff.runFinalizer { // This is the case with the Diff objects that are involved in the DiffNotifyCallback. diff.ptr = nil return nil } runtime.SetFinalizer(diff, nil) C.git_diff_free(diff.ptr) diff.ptr = nil 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), } } runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_diff_find_similar(diff.ptr, copts) runtime.KeepAlive(diff) if ecode < 0 { return MakeGitError(ecode) } 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 { ret := int(C.git_diff_stats_insertions(stats.ptr)) runtime.KeepAlive(stats) return ret } func (stats *DiffStats) Deletions() int { ret := int(C.git_diff_stats_deletions(stats.ptr)) runtime.KeepAlive(stats) return ret } func (stats *DiffStats) FilesChanged() int { ret := int(C.git_diff_stats_files_changed(stats.ptr)) runtime.KeepAlive(stats) return ret } type DiffStatsFormat int const ( DiffStatsNone DiffStatsFormat = C.GIT_DIFF_STATS_NONE DiffStatsFull DiffStatsFormat = C.GIT_DIFF_STATS_FULL DiffStatsShort DiffStatsFormat = C.GIT_DIFF_STATS_SHORT DiffStatsNumber DiffStatsFormat = C.GIT_DIFF_STATS_NUMBER DiffStatsIncludeSummary DiffStatsFormat = C.GIT_DIFF_STATS_INCLUDE_SUMMARY ) func (stats *DiffStats) String(format DiffStatsFormat, width uint) (string, error) { buf := C.git_buf{} defer C.git_buf_dispose(&buf) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_diff_stats_to_buf(&buf, stats.ptr, C.git_diff_stats_format_t(format), C.size_t(width)) runtime.KeepAlive(stats) if ret < 0 { return "", MakeGitError(ret) } return C.GoString(buf.ptr), nil } func (diff *Diff) Stats() (*DiffStats, error) { stats := new(DiffStats) runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_diff_get_stats(&stats.ptr, diff.ptr) runtime.KeepAlive(diff) if ecode < 0 { return nil, MakeGitError(ecode) } runtime.SetFinalizer(stats, (*DiffStats).Free) return stats, nil } type diffForEachCallbackData struct { fileCallback DiffForEachFileCallback hunkCallback DiffForEachHunkCallback lineCallback DiffForEachLineCallback errorTarget *error } type DiffForEachFileCallback func(delta DiffDelta, progress float64) (DiffForEachHunkCallback, error) type DiffDetail int const ( DiffDetailFiles DiffDetail = iota DiffDetailHunks DiffDetailLines ) func (diff *Diff) ForEach(cbFile DiffForEachFileCallback, detail DiffDetail) error { if diff.ptr == nil { return ErrInvalid } intHunks := C.int(0) if detail >= DiffDetailHunks { intHunks = C.int(1) } intLines := C.int(0) if detail >= DiffDetailLines { intLines = C.int(1) } var err error data := &diffForEachCallbackData{ fileCallback: cbFile, errorTarget: &err, } handle := pointerHandles.Track(data) defer pointerHandles.Untrack(handle) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C._go_git_diff_foreach(diff.ptr, 1, intHunks, intLines, handle) runtime.KeepAlive(diff) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } //export diffForEachFileCallback func diffForEachFileCallback(delta *C.git_diff_delta, progress C.float, handle unsafe.Pointer) C.int { payload := pointerHandles.Get(handle) data, ok := payload.(*diffForEachCallbackData) if !ok { panic("could not retrieve data for handle") } data.hunkCallback = nil if data.fileCallback != nil { cb, err := data.fileCallback(diffDeltaFromC(delta), float64(progress)) if err != nil { *data.errorTarget = err return C.int(ErrorCodeUser) } data.hunkCallback = cb } return C.int(ErrorCodeOK) } type DiffForEachHunkCallback func(DiffHunk) (DiffForEachLineCallback, error) //export diffForEachHunkCallback func diffForEachHunkCallback(delta *C.git_diff_delta, hunk *C.git_diff_hunk, handle unsafe.Pointer) C.int { payload := pointerHandles.Get(handle) data, ok := payload.(*diffForEachCallbackData) if !ok { panic("could not retrieve data for handle") } data.lineCallback = nil if data.hunkCallback != nil { cb, err := data.hunkCallback(diffHunkFromC(hunk)) if err != nil { *data.errorTarget = err return C.int(ErrorCodeUser) } data.lineCallback = cb } return C.int(ErrorCodeOK) } type DiffForEachLineCallback func(DiffLine) error //export diffForEachLineCallback func diffForEachLineCallback(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.git_diff_line, handle unsafe.Pointer) C.int { payload := pointerHandles.Get(handle) data, ok := payload.(*diffForEachCallbackData) if !ok { panic("could not retrieve data for handle") } err := data.lineCallback(diffLineFromC(line)) if err != nil { *data.errorTarget = err return C.int(ErrorCodeUser) } return C.int(ErrorCodeOK) } func (diff *Diff) Patch(deltaIndex int) (*Patch, error) { if diff.ptr == nil { return nil, ErrInvalid } var patchPtr *C.git_patch runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_patch_from_diff(&patchPtr, diff.ptr, C.size_t(deltaIndex)) runtime.KeepAlive(diff) if ecode < 0 { return nil, MakeGitError(ecode) } return newPatchFromC(patchPtr), nil } type DiffFormat int const ( DiffFormatPatch DiffFormat = C.GIT_DIFF_FORMAT_PATCH DiffFormatPatchHeader DiffFormat = C.GIT_DIFF_FORMAT_PATCH_HEADER DiffFormatRaw DiffFormat = C.GIT_DIFF_FORMAT_RAW DiffFormatNameOnly DiffFormat = C.GIT_DIFF_FORMAT_NAME_ONLY DiffFormatNameStatus DiffFormat = C.GIT_DIFF_FORMAT_NAME_STATUS ) func (diff *Diff) ToBuf(format DiffFormat) ([]byte, error) { if diff.ptr == nil { return nil, ErrInvalid } diffBuf := C.git_buf{} runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_diff_to_buf(&diffBuf, diff.ptr, C.git_diff_format_t(format)) runtime.KeepAlive(diff) if ecode < 0 { return nil, MakeGitError(ecode) } defer C.git_buf_dispose(&diffBuf) return C.GoBytes(unsafe.Pointer(diffBuf.ptr), C.int(diffBuf.size)), nil } type DiffOptionsFlag int const ( DiffNormal DiffOptionsFlag = C.GIT_DIFF_NORMAL DiffReverse DiffOptionsFlag = C.GIT_DIFF_REVERSE DiffIncludeIgnored DiffOptionsFlag = C.GIT_DIFF_INCLUDE_IGNORED DiffRecurseIgnoredDirs DiffOptionsFlag = C.GIT_DIFF_RECURSE_IGNORED_DIRS DiffIncludeUntracked DiffOptionsFlag = C.GIT_DIFF_INCLUDE_UNTRACKED DiffRecurseUntracked DiffOptionsFlag = C.GIT_DIFF_RECURSE_UNTRACKED_DIRS DiffIncludeUnmodified DiffOptionsFlag = C.GIT_DIFF_INCLUDE_UNMODIFIED DiffIncludeTypeChange DiffOptionsFlag = C.GIT_DIFF_INCLUDE_TYPECHANGE DiffIncludeTypeChangeTrees DiffOptionsFlag = C.GIT_DIFF_INCLUDE_TYPECHANGE_TREES DiffIgnoreFilemode DiffOptionsFlag = C.GIT_DIFF_IGNORE_FILEMODE DiffIgnoreSubmodules DiffOptionsFlag = C.GIT_DIFF_IGNORE_SUBMODULES DiffIgnoreCase DiffOptionsFlag = C.GIT_DIFF_IGNORE_CASE DiffIncludeCaseChange DiffOptionsFlag = C.GIT_DIFF_INCLUDE_CASECHANGE DiffDisablePathspecMatch DiffOptionsFlag = C.GIT_DIFF_DISABLE_PATHSPEC_MATCH DiffSkipBinaryCheck DiffOptionsFlag = C.GIT_DIFF_SKIP_BINARY_CHECK DiffEnableFastUntrackedDirs DiffOptionsFlag = C.GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS DiffForceText DiffOptionsFlag = C.GIT_DIFF_FORCE_TEXT DiffForceBinary DiffOptionsFlag = C.GIT_DIFF_FORCE_BINARY DiffIgnoreWhitespace DiffOptionsFlag = C.GIT_DIFF_IGNORE_WHITESPACE DiffIgnoreWhitespaceChange DiffOptionsFlag = C.GIT_DIFF_IGNORE_WHITESPACE_CHANGE DiffIgnoreWitespaceEol DiffOptionsFlag = C.GIT_DIFF_IGNORE_WHITESPACE_EOL DiffShowUntrackedContent DiffOptionsFlag = C.GIT_DIFF_SHOW_UNTRACKED_CONTENT DiffShowUnmodified DiffOptionsFlag = C.GIT_DIFF_SHOW_UNMODIFIED DiffPatience DiffOptionsFlag = C.GIT_DIFF_PATIENCE DiffMinimal DiffOptionsFlag = C.GIT_DIFF_MINIMAL DiffShowBinary DiffOptionsFlag = C.GIT_DIFF_SHOW_BINARY DiffIndentHeuristic DiffOptionsFlag = C.GIT_DIFF_INDENT_HEURISTIC ) type DiffNotifyCallback func(diffSoFar *Diff, deltaToAdd DiffDelta, matchedPathspec string) error type DiffOptions struct { Flags DiffOptionsFlag IgnoreSubmodules SubmoduleIgnore Pathspec []string NotifyCallback DiffNotifyCallback ContextLines uint32 InterhunkLines uint32 IdAbbrev uint16 MaxSize int OldPrefix string NewPrefix string } func DefaultDiffOptions() (DiffOptions, error) { opts := C.git_diff_options{} runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_diff_options_init(&opts, C.GIT_DIFF_OPTIONS_VERSION) if ecode < 0 { return DiffOptions{}, MakeGitError(ecode) } return DiffOptions{ Flags: DiffOptionsFlag(opts.flags), IgnoreSubmodules: SubmoduleIgnore(opts.ignore_submodules), Pathspec: makeStringsFromCStrings(opts.pathspec.strings, int(opts.pathspec.count)), ContextLines: uint32(opts.context_lines), InterhunkLines: uint32(opts.interhunk_lines), IdAbbrev: uint16(opts.id_abbrev), MaxSize: int(opts.max_size), OldPrefix: "a", NewPrefix: "b", }, 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{} runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_diff_find_options_init(&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") ) type diffNotifyCallbackData struct { callback DiffNotifyCallback repository *Repository errorTarget *error } //export diffNotifyCallback func diffNotifyCallback(_diff_so_far unsafe.Pointer, delta_to_add *C.git_diff_delta, matched_pathspec *C.char, handle unsafe.Pointer) C.int { diff_so_far := (*C.git_diff)(_diff_so_far) payload := pointerHandles.Get(handle) data, ok := payload.(*diffNotifyCallbackData) if !ok { panic("could not retrieve data for handle") } if data == nil { return C.int(ErrorCodeOK) } // We are not taking ownership of this diff pointer, so no finalizer is set. diff := &Diff{ ptr: diff_so_far, repo: data.repository, runFinalizer: false, } err := data.callback(diff, diffDeltaFromC(delta_to_add), C.GoString(matched_pathspec)) // Since the callback could theoretically keep a reference to the diff // (which could be freed by libgit2 if an error occurs later during the // diffing process), this converts a use-after-free (terrible!) into a nil // dereference ("just" pretty bad). diff.ptr = nil if err == ErrDeltaSkip { return 1 } if err != nil { *data.errorTarget = err return C.int(ErrorCodeUser) } return C.int(ErrorCodeOK) } func (opts *DiffOptions) toC(repo *Repository, errorTarget *error) *C.git_diff_options { if opts == nil { return nil } cpathspec := C.git_strarray{} if opts.Pathspec != nil { cpathspec.count = C.size_t(len(opts.Pathspec)) cpathspec.strings = makeCStringsFromStrings(opts.Pathspec) } 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), old_prefix: C.CString(opts.OldPrefix), new_prefix: C.CString(opts.NewPrefix), } if opts.NotifyCallback != nil { notifyData := &diffNotifyCallbackData{ callback: opts.NotifyCallback, repository: repo, errorTarget: errorTarget, } C._go_git_setup_diff_notify_callbacks(copts) copts.payload = pointerHandles.Track(notifyData) } return copts } func freeDiffOptions(copts *C.git_diff_options) { if copts == nil { return } cpathspec := copts.pathspec freeStrarray(&cpathspec) C.free(unsafe.Pointer(copts.old_prefix)) C.free(unsafe.Pointer(copts.new_prefix)) if copts.payload != nil { pointerHandles.Untrack(copts.payload) } } 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 } var err error copts := opts.toC(v, &err) defer freeDiffOptions(copts) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_diff_tree_to_tree(&diffPtr, v.ptr, oldPtr, newPtr, copts) runtime.KeepAlive(oldTree) runtime.KeepAlive(newTree) if ret == C.int(ErrorCodeUser) && err != nil { return nil, err } if ret < 0 { return nil, MakeGitError(ret) } return newDiffFromC(diffPtr, v), nil } func (v *Repository) DiffTreeToWorkdir(oldTree *Tree, opts *DiffOptions) (*Diff, error) { var diffPtr *C.git_diff var oldPtr *C.git_tree if oldTree != nil { oldPtr = oldTree.cast_ptr } var err error copts := opts.toC(v, &err) defer freeDiffOptions(copts) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_diff_tree_to_workdir(&diffPtr, v.ptr, oldPtr, copts) runtime.KeepAlive(oldTree) if ret == C.int(ErrorCodeUser) && err != nil { return nil, err } if ret < 0 { return nil, MakeGitError(ret) } return newDiffFromC(diffPtr, v), nil } func (v *Repository) DiffTreeToIndex(oldTree *Tree, index *Index, opts *DiffOptions) (*Diff, error) { var diffPtr *C.git_diff var oldPtr *C.git_tree var indexPtr *C.git_index if oldTree != nil { oldPtr = oldTree.cast_ptr } if index != nil { indexPtr = index.ptr } var err error copts := opts.toC(v, &err) defer freeDiffOptions(copts) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_diff_tree_to_index(&diffPtr, v.ptr, oldPtr, indexPtr, copts) runtime.KeepAlive(oldTree) runtime.KeepAlive(index) if ret == C.int(ErrorCodeUser) && err != nil { return nil, err } if ret < 0 { return nil, MakeGitError(ret) } return newDiffFromC(diffPtr, v), nil } func (v *Repository) DiffTreeToWorkdirWithIndex(oldTree *Tree, opts *DiffOptions) (*Diff, error) { var diffPtr *C.git_diff var oldPtr *C.git_tree if oldTree != nil { oldPtr = oldTree.cast_ptr } var err error copts := opts.toC(v, &err) defer freeDiffOptions(copts) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_diff_tree_to_workdir_with_index(&diffPtr, v.ptr, oldPtr, copts) runtime.KeepAlive(oldTree) if ret == C.int(ErrorCodeUser) && err != nil { return nil, err } if ret < 0 { return nil, MakeGitError(ret) } return newDiffFromC(diffPtr, v), nil } func (v *Repository) DiffIndexToWorkdir(index *Index, opts *DiffOptions) (*Diff, error) { var diffPtr *C.git_diff var indexPtr *C.git_index if index != nil { indexPtr = index.ptr } var err error copts := opts.toC(v, &err) defer freeDiffOptions(copts) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_diff_index_to_workdir(&diffPtr, v.ptr, indexPtr, copts) runtime.KeepAlive(index) if ret == C.int(ErrorCodeUser) && err != nil { return nil, err } if ret < 0 { return nil, MakeGitError(ret) } return newDiffFromC(diffPtr, v), nil } // DiffBlobs performs a diff between two arbitrary blobs. You can pass // whatever file names you'd like for them to appear as in the diff. func DiffBlobs(oldBlob *Blob, oldAsPath string, newBlob *Blob, newAsPath string, opts *DiffOptions, fileCallback DiffForEachFileCallback, detail DiffDetail) error { var err error data := &diffForEachCallbackData{ fileCallback: fileCallback, errorTarget: &err, } intHunks := C.int(0) if detail >= DiffDetailHunks { intHunks = C.int(1) } intLines := C.int(0) if detail >= DiffDetailLines { intLines = C.int(1) } handle := pointerHandles.Track(data) defer pointerHandles.Untrack(handle) var repo *Repository var oldBlobPtr, newBlobPtr *C.git_blob if oldBlob != nil { oldBlobPtr = oldBlob.cast_ptr repo = oldBlob.repo } if newBlob != nil { newBlobPtr = newBlob.cast_ptr repo = newBlob.repo } oldBlobPath := C.CString(oldAsPath) defer C.free(unsafe.Pointer(oldBlobPath)) newBlobPath := C.CString(newAsPath) defer C.free(unsafe.Pointer(newBlobPath)) copts := opts.toC(repo, &err) defer freeDiffOptions(copts) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C._go_git_diff_blobs(oldBlobPtr, oldBlobPath, newBlobPtr, newBlobPath, copts, 1, intHunks, intLines, handle) runtime.KeepAlive(oldBlob) runtime.KeepAlive(newBlob) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } // ApplyHunkCallback is a callback that will be made per delta (file) when applying a patch. type ApplyHunkCallback func(*DiffHunk) (apply bool, err error) // ApplyDeltaCallback is a callback that will be made per hunk when applying a patch. type ApplyDeltaCallback func(*DiffDelta) (apply bool, err error) // ApplyOptions has 2 callbacks that are called for hunks or deltas // If these functions return an error, abort the apply process immediately. // If the first return value is true, the delta/hunk will be applied. If it is false, the delta/hunk will not be applied. In either case, the rest of the apply process will continue. type ApplyOptions struct { ApplyHunkCallback ApplyHunkCallback ApplyDeltaCallback ApplyDeltaCallback Flags uint } type applyCallbackData struct { options *ApplyOptions errorTarget *error } //export hunkApplyCallback func hunkApplyCallback(_hunk *C.git_diff_hunk, _payload unsafe.Pointer) C.int { data, ok := pointerHandles.Get(_payload).(*applyCallbackData) if !ok { panic("invalid apply options payload") } if data.options.ApplyHunkCallback == nil { return C.int(ErrorCodeOK) } hunk := diffHunkFromC(_hunk) apply, err := data.options.ApplyHunkCallback(&hunk) if err != nil { *data.errorTarget = err return C.int(ErrorCodeUser) } if !apply { return 1 } return C.int(ErrorCodeOK) } //export deltaApplyCallback func deltaApplyCallback(_delta *C.git_diff_delta, _payload unsafe.Pointer) C.int { data, ok := pointerHandles.Get(_payload).(*applyCallbackData) if !ok { panic("invalid apply options payload") } if data.options.ApplyDeltaCallback == nil { return C.int(ErrorCodeOK) } delta := diffDeltaFromC(_delta) apply, err := data.options.ApplyDeltaCallback(&delta) if err != nil { *data.errorTarget = err return C.int(ErrorCodeUser) } if !apply { return 1 } return C.int(ErrorCodeOK) } // DefaultApplyOptions returns default options for applying diffs or patches. func DefaultApplyOptions() (*ApplyOptions, error) { opts := C.git_apply_options{} runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_apply_options_init(&opts, C.GIT_APPLY_OPTIONS_VERSION) if int(ecode) != 0 { return nil, MakeGitError(ecode) } return applyOptionsFromC(&opts), nil } func (a *ApplyOptions) toC(errorTarget *error) *C.git_apply_options { if a == nil { return nil } opts := &C.git_apply_options{ version: C.GIT_APPLY_OPTIONS_VERSION, flags: C.uint(a.Flags), } if a.ApplyDeltaCallback != nil || a.ApplyHunkCallback != nil { data := &applyCallbackData{ options: a, errorTarget: errorTarget, } C._go_git_populate_apply_callbacks(opts) opts.payload = pointerHandles.Track(data) } return opts } func freeApplyOptions(opts *C.git_apply_options) { if opts == nil { return } if opts.payload != nil { pointerHandles.Untrack(opts.payload) } } func applyOptionsFromC(opts *C.git_apply_options) *ApplyOptions { return &ApplyOptions{ Flags: uint(opts.flags), } } // ApplyLocation represents the possible application locations for applying // diffs. type ApplyLocation int const ( // ApplyLocationWorkdir applies the patch to the workdir, leaving the // index untouched. This is the equivalent of `git apply` with no location // argument. ApplyLocationWorkdir ApplyLocation = C.GIT_APPLY_LOCATION_WORKDIR // ApplyLocationIndex applies the patch to the index, leaving the working // directory untouched. This is the equivalent of `git apply --cached`. ApplyLocationIndex ApplyLocation = C.GIT_APPLY_LOCATION_INDEX // ApplyLocationBoth applies the patch to both the working directory and // the index. This is the equivalent of `git apply --index`. ApplyLocationBoth ApplyLocation = C.GIT_APPLY_LOCATION_BOTH ) // ApplyDiff appllies a Diff to the given repository, making changes directly // in the working directory, the index, or both. func (v *Repository) ApplyDiff(diff *Diff, location ApplyLocation, opts *ApplyOptions) error { runtime.LockOSThread() defer runtime.UnlockOSThread() var err error cOpts := opts.toC(&err) defer freeApplyOptions(cOpts) ret := C.git_apply(v.ptr, diff.ptr, C.git_apply_location_t(location), cOpts) runtime.KeepAlive(v) runtime.KeepAlive(diff) runtime.KeepAlive(cOpts) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } // ApplyToTree applies a Diff to a Tree and returns the resulting image as an Index. func (v *Repository) ApplyToTree(diff *Diff, tree *Tree, opts *ApplyOptions) (*Index, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() var err error cOpts := opts.toC(&err) defer freeApplyOptions(cOpts) var indexPtr *C.git_index ret := C.git_apply_to_tree(&indexPtr, v.ptr, tree.cast_ptr, diff.ptr, cOpts) runtime.KeepAlive(diff) runtime.KeepAlive(tree) runtime.KeepAlive(cOpts) if ret == C.int(ErrorCodeUser) && err != nil { return nil, err } if ret < 0 { return nil, MakeGitError(ret) } return newIndexFromC(indexPtr, v), nil } // DiffFromBuffer reads the contents of a git patch file into a Diff object. // // The diff object produced is similar to the one that would be produced if you // actually produced it computationally by comparing two trees, however there // may be subtle differences. For example, a patch file likely contains // abbreviated object IDs, so the object IDs in a git_diff_delta produced by // this function will also be abbreviated. // // This function will only read patch files created by a git implementation, it // will not read unified diffs produced by the diff program, nor any other // types of patch files. func DiffFromBuffer(buffer []byte, repo *Repository) (*Diff, error) { var diff *C.git_diff cBuffer := C.CBytes(buffer) defer C.free(unsafe.Pointer(cBuffer)) runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_diff_from_buffer(&diff, (*C.char)(cBuffer), C.size_t(len(buffer))) if ecode < 0 { return nil, MakeGitError(ecode) } runtime.KeepAlive(diff) return newDiffFromC(diff, repo), nil } git2go-31.4.3/diff_test.go000066400000000000000000000411201376323343000152750ustar00rootroot00000000000000package git import ( "errors" "fmt" "io/ioutil" "path" "reflect" "strings" "testing" ) func TestFindSimilar(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) 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) numDiffs := 0 numAdded := 0 numDeleted := 0 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) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) originalTree, newTree := createTestTrees(t, repo) callbackInvoked := false opts := DiffOptions{ NotifyCallback: func(diffSoFar *Diff, delta DiffDelta, matchedPathSpec string) error { callbackInvoked = true return nil }, OldPrefix: "x1/", NewPrefix: "y1/", } diff, err := repo.DiffTreeToTree(originalTree, newTree, &opts) checkFatal(t, err) if !callbackInvoked { t.Fatal("callback not invoked") } if diff == nil { t.Fatal("no diff returned") } 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) return func(line DiffLine) error { lines = append(lines, line) return nil }, nil }, nil }, DiffDetailLines) checkFatal(t, err) if len(files) != 1 { t.Fatal("Incorrect number of files in diff") } if files[0] != "README" { t.Fatal("File in diff was expected to be README") } if len(hunks) != 1 { t.Fatal("Incorrect number of hunks in diff") } if hunks[0].OldStart != 1 || hunks[0].NewStart != 1 { t.Fatal("Incorrect hunk") } if len(lines) != 2 { t.Fatal("Incorrect number of lines in diff") } if lines[0].Content != "foo\n" { t.Fatal("Incorrect lines in diff") } if lines[1].Content != "file changed\n" { 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]) } 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") err = diff.ForEach(func(file DiffDelta, progress float64) (DiffForEachHunkCallback, error) { return nil, errTest }, DiffDetailLines) if err != errTest { t.Fatalf("Expected custom error to be returned, got %v, want %v", err, errTest) } } 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 } func TestDiffBlobs(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) odb, err := repo.Odb() checkFatal(t, err) id1, err := odb.Write([]byte("hello\nhello\n"), ObjectBlob) checkFatal(t, err) id2, err := odb.Write([]byte("hallo\nhallo\n"), ObjectBlob) checkFatal(t, err) blob1, err := repo.LookupBlob(id1) checkFatal(t, err) blob2, err := repo.LookupBlob(id2) checkFatal(t, err) var files, hunks, lines int err = DiffBlobs(blob1, "hi", blob2, "hi", nil, func(delta DiffDelta, progress float64) (DiffForEachHunkCallback, error) { files++ return func(hunk DiffHunk) (DiffForEachLineCallback, error) { hunks++ return func(line DiffLine) error { lines++ return nil }, nil }, nil }, DiffDetailLines) if files != 1 { t.Fatal("Bad number of files iterated") } if hunks != 1 { t.Fatal("Bad number of hunks iterated") } // two removals, two additions if lines != 4 { t.Fatalf("Bad number of lines iterated") } } func TestApplyDiffAddfile(t *testing.T) { repo := createTestRepo(t) defer cleanupTestRepo(t, repo) seedTestRepo(t, repo) addFirstFileCommit, addFirstFileTree := addAndGetTree(t, repo, "file1", `hello`) defer addFirstFileCommit.Free() defer addFirstFileTree.Free() addSecondFileCommit, addSecondFileTree := addAndGetTree(t, repo, "file2", `hello2`) defer addSecondFileCommit.Free() defer addSecondFileTree.Free() diff, err := repo.DiffTreeToTree(addFirstFileTree, addSecondFileTree, nil) checkFatal(t, err) defer diff.Free() t.Run("check does not apply to current tree because file exists", func(t *testing.T) { err = repo.ResetToCommit(addSecondFileCommit, ResetHard, &CheckoutOptions{}) checkFatal(t, err) err = repo.ApplyDiff(diff, ApplyLocationBoth, nil) if err == nil { t.Error("expecting applying patch to current repo to fail") } }) t.Run("check apply to correct commit", func(t *testing.T) { err = repo.ResetToCommit(addFirstFileCommit, ResetHard, &CheckoutOptions{}) checkFatal(t, err) err = repo.ApplyDiff(diff, ApplyLocationBoth, nil) checkFatal(t, err) t.Run("Check that diff only changed one file", func(t *testing.T) { checkSecondFileStaged(t, repo) index, err := repo.Index() checkFatal(t, err) defer index.Free() newTreeOID, err := index.WriteTreeTo(repo) checkFatal(t, err) newTree, err := repo.LookupTree(newTreeOID) checkFatal(t, err) defer newTree.Free() _, err = repo.CreateCommit("HEAD", signature(), signature(), fmt.Sprintf("patch apply"), newTree, addFirstFileCommit) checkFatal(t, err) }) t.Run("test applying patch produced the same diff", func(t *testing.T) { head, err := repo.Head() checkFatal(t, err) commit, err := repo.LookupCommit(head.Target()) checkFatal(t, err) defer commit.Free() tree, err := commit.Tree() checkFatal(t, err) defer tree.Free() newDiff, err := repo.DiffTreeToTree(addFirstFileTree, tree, nil) checkFatal(t, err) defer newDiff.Free() raw1b, err := diff.ToBuf(DiffFormatPatch) checkFatal(t, err) raw2b, err := newDiff.ToBuf(DiffFormatPatch) checkFatal(t, err) raw1 := string(raw1b) raw2 := string(raw2b) if raw1 != raw2 { t.Error("diffs should be the same") } }) }) t.Run("check convert to raw buffer and apply", func(t *testing.T) { err = repo.ResetToCommit(addFirstFileCommit, ResetHard, &CheckoutOptions{}) checkFatal(t, err) raw, err := diff.ToBuf(DiffFormatPatch) checkFatal(t, err) if len(raw) == 0 { t.Error("empty diff created") } diff2, err := DiffFromBuffer(raw, repo) checkFatal(t, err) defer diff2.Free() err = repo.ApplyDiff(diff2, ApplyLocationBoth, nil) checkFatal(t, err) }) t.Run("check apply callbacks work", func(t *testing.T) { // reset the state and get new default options for test resetAndGetOpts := func(t *testing.T) *ApplyOptions { err = repo.ResetToCommit(addFirstFileCommit, ResetHard, &CheckoutOptions{}) checkFatal(t, err) opts, err := DefaultApplyOptions() checkFatal(t, err) return opts } t.Run("Check hunk callback working applies patch", func(t *testing.T) { opts := resetAndGetOpts(t) called := false opts.ApplyHunkCallback = func(hunk *DiffHunk) (apply bool, err error) { called = true return true, nil } err = repo.ApplyDiff(diff, ApplyLocationBoth, opts) checkFatal(t, err) if called == false { t.Error("apply hunk callback was not called") } checkSecondFileStaged(t, repo) }) t.Run("Check delta callback working applies patch", func(t *testing.T) { opts := resetAndGetOpts(t) called := false opts.ApplyDeltaCallback = func(hunk *DiffDelta) (apply bool, err error) { if hunk.NewFile.Path != "file2" { t.Error("Unexpected delta in diff application") } called = true return true, nil } err = repo.ApplyDiff(diff, ApplyLocationBoth, opts) checkFatal(t, err) if called == false { t.Error("apply hunk callback was not called") } checkSecondFileStaged(t, repo) }) t.Run("Check delta callback returning false does not apply patch", func(t *testing.T) { opts := resetAndGetOpts(t) called := false opts.ApplyDeltaCallback = func(hunk *DiffDelta) (apply bool, err error) { if hunk.NewFile.Path != "file2" { t.Error("Unexpected hunk in diff application") } called = true return false, nil } err = repo.ApplyDiff(diff, ApplyLocationBoth, opts) checkFatal(t, err) if called == false { t.Error("apply hunk callback was not called") } checkNoFilesStaged(t, repo) }) t.Run("Check hunk callback returning causes application to fail", func(t *testing.T) { opts := resetAndGetOpts(t) called := false opts.ApplyHunkCallback = func(hunk *DiffHunk) (apply bool, err error) { called = true return false, errors.New("something happened") } err = repo.ApplyDiff(diff, ApplyLocationBoth, opts) if err == nil { t.Error("expected an error after trying to apply") } if called == false { t.Error("apply hunk callback was not called") } checkNoFilesStaged(t, repo) }) t.Run("Check delta callback returning causes application to fail", func(t *testing.T) { opts := resetAndGetOpts(t) called := false opts.ApplyDeltaCallback = func(hunk *DiffDelta) (apply bool, err error) { if hunk.NewFile.Path != "file2" { t.Error("Unexpected delta in diff application") } called = true return false, errors.New("something happened") } err = repo.ApplyDiff(diff, ApplyLocationBoth, opts) if err == nil { t.Error("expected an error after trying to apply") } if called == false { t.Error("apply hunk callback was not called") } checkNoFilesStaged(t, repo) }) }) } func TestApplyToTree(t *testing.T) { repo := createTestRepo(t) defer cleanupTestRepo(t, repo) seedTestRepo(t, repo) commitA, treeA := addAndGetTree(t, repo, "file", "a") defer commitA.Free() defer treeA.Free() commitB, treeB := addAndGetTree(t, repo, "file", "b") defer commitB.Free() defer treeB.Free() commitC, treeC := addAndGetTree(t, repo, "file", "c") defer commitC.Free() defer treeC.Free() diffAB, err := repo.DiffTreeToTree(treeA, treeB, nil) checkFatal(t, err) diffAC, err := repo.DiffTreeToTree(treeA, treeC, nil) checkFatal(t, err) errMessageDropped := errors.New("message dropped") for _, tc := range []struct { name string tree *Tree diff *Diff applyHunkCallback ApplyHunkCallback applyDeltaCallback ApplyDeltaCallback err error expectedDiff *Diff }{ { name: "applying patch produces the same diff", tree: treeA, diff: diffAB, expectedDiff: diffAB, }, { name: "applying a conflicting patch errors", tree: treeB, diff: diffAC, err: &GitError{ Message: "hunk at line 1 did not apply", Code: ErrorCodeApplyFail, Class: ErrorClassPatch, }, }, { name: "callbacks succeeding apply the diff", tree: treeA, diff: diffAB, applyHunkCallback: func(*DiffHunk) (bool, error) { return true, nil }, applyDeltaCallback: func(*DiffDelta) (bool, error) { return true, nil }, expectedDiff: diffAB, }, { name: "hunk callback returning false does not apply", tree: treeA, diff: diffAB, applyHunkCallback: func(*DiffHunk) (bool, error) { return false, nil }, }, { name: "hunk callback erroring fails the call", tree: treeA, diff: diffAB, applyHunkCallback: func(*DiffHunk) (bool, error) { return true, errMessageDropped }, err: errMessageDropped, }, { name: "delta callback returning false does not apply", tree: treeA, diff: diffAB, applyDeltaCallback: func(*DiffDelta) (bool, error) { return false, nil }, }, { name: "delta callback erroring fails the call", tree: treeA, diff: diffAB, applyDeltaCallback: func(*DiffDelta) (bool, error) { return true, errMessageDropped }, err: errMessageDropped, }, } { t.Run(tc.name, func(t *testing.T) { opts, err := DefaultApplyOptions() checkFatal(t, err) opts.ApplyHunkCallback = tc.applyHunkCallback opts.ApplyDeltaCallback = tc.applyDeltaCallback index, err := repo.ApplyToTree(tc.diff, tc.tree, opts) if tc.err != nil { if !reflect.DeepEqual(tc.err, err) { t.Fatalf("expected error %q but got %q", tc.err, err) } return } checkFatal(t, err) patchedTreeOID, err := index.WriteTreeTo(repo) checkFatal(t, err) patchedTree, err := repo.LookupTree(patchedTreeOID) checkFatal(t, err) patchedDiff, err := repo.DiffTreeToTree(tc.tree, patchedTree, nil) checkFatal(t, err) appliedRaw, err := patchedDiff.ToBuf(DiffFormatPatch) checkFatal(t, err) if tc.expectedDiff == nil { if len(appliedRaw) > 0 { t.Fatalf("expected no diff but got: %s", appliedRaw) } return } expectedDiff, err := tc.expectedDiff.ToBuf(DiffFormatPatch) checkFatal(t, err) if string(expectedDiff) != string(appliedRaw) { t.Fatalf("diffs do not match:\nexpected: %s\n\nactual: %s", expectedDiff, appliedRaw) } }) } } // checkSecondFileStaged checks that there is a single file called "file2" uncommitted in the repo func checkSecondFileStaged(t *testing.T, repo *Repository) { opts := StatusOptions{ Show: StatusShowIndexAndWorkdir, Flags: StatusOptIncludeUntracked, } statuses, err := repo.StatusList(&opts) checkFatal(t, err) count, err := statuses.EntryCount() checkFatal(t, err) if count != 1 { t.Error("diff should affect exactly one file") } if count == 0 { t.Fatal("no statuses, cannot continue test") } entry, err := statuses.ByIndex(0) checkFatal(t, err) if entry.Status != StatusIndexNew { t.Error("status should be 'new' as file has been added between commits") } if entry.HeadToIndex.NewFile.Path != "file2" { t.Error("new file should be 'file2") } return } // checkNoFilesStaged checks that there is a single file called "file2" uncommitted in the repo func checkNoFilesStaged(t *testing.T, repo *Repository) { opts := StatusOptions{ Show: StatusShowIndexAndWorkdir, Flags: StatusOptIncludeUntracked, } statuses, err := repo.StatusList(&opts) checkFatal(t, err) count, err := statuses.EntryCount() checkFatal(t, err) if count != 0 { t.Error("files changed unexpectedly") } } // addAndGetTree creates a file and commits it, returning the commit and tree func addAndGetTree(t *testing.T, repo *Repository, filename string, content string) (*Commit, *Tree) { headCommit, err := headCommit(repo) checkFatal(t, err) defer headCommit.Free() p := repo.Path() p = strings.TrimSuffix(p, ".git") p = strings.TrimSuffix(p, ".git/") err = ioutil.WriteFile(path.Join(p, filename), []byte((content)), 0777) checkFatal(t, err) index, err := repo.Index() checkFatal(t, err) defer index.Free() err = index.AddByPath(filename) checkFatal(t, err) newTreeOID, err := index.WriteTreeTo(repo) checkFatal(t, err) newTree, err := repo.LookupTree(newTreeOID) checkFatal(t, err) defer newTree.Free() commitId, err := repo.CreateCommit("HEAD", signature(), signature(), fmt.Sprintf("add %s", filename), newTree, headCommit) checkFatal(t, err) commit, err := repo.LookupCommit(commitId) checkFatal(t, err) tree, err := commit.Tree() checkFatal(t, err) return commit, tree } git2go-31.4.3/difflinetype_string.go000066400000000000000000000026051376323343000174030ustar00rootroot00000000000000// Code generated by "stringer -type DiffLineType -trimprefix DiffLine -tags static"; DO NOT EDIT. package git import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[DiffLineContext-32] _ = x[DiffLineAddition-43] _ = x[DiffLineDeletion-45] _ = x[DiffLineContextEOFNL-61] _ = x[DiffLineAddEOFNL-62] _ = x[DiffLineDelEOFNL-60] _ = x[DiffLineFileHdr-70] _ = x[DiffLineHunkHdr-72] _ = x[DiffLineBinary-66] } const ( _DiffLineType_name_0 = "Context" _DiffLineType_name_1 = "Addition" _DiffLineType_name_2 = "Deletion" _DiffLineType_name_3 = "DelEOFNLContextEOFNLAddEOFNL" _DiffLineType_name_4 = "Binary" _DiffLineType_name_5 = "FileHdr" _DiffLineType_name_6 = "HunkHdr" ) var ( _DiffLineType_index_3 = [...]uint8{0, 8, 20, 28} ) func (i DiffLineType) String() string { switch { case i == 32: return _DiffLineType_name_0 case i == 43: return _DiffLineType_name_1 case i == 45: return _DiffLineType_name_2 case 60 <= i && i <= 62: i -= 60 return _DiffLineType_name_3[_DiffLineType_index_3[i]:_DiffLineType_index_3[i+1]] case i == 66: return _DiffLineType_name_4 case i == 70: return _DiffLineType_name_5 case i == 72: return _DiffLineType_name_6 default: return "DiffLineType(" + strconv.FormatInt(int64(i), 10) + ")" } } git2go-31.4.3/errorclass_string.go000066400000000000000000000034511376323343000171000ustar00rootroot00000000000000// Code generated by "stringer -type ErrorClass -trimprefix ErrorClass -tags static"; DO NOT EDIT. package git import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[ErrorClassNone-0] _ = x[ErrorClassNoMemory-1] _ = x[ErrorClassOS-2] _ = x[ErrorClassInvalid-3] _ = x[ErrorClassReference-4] _ = x[ErrorClassZlib-5] _ = x[ErrorClassRepository-6] _ = x[ErrorClassConfig-7] _ = x[ErrorClassRegex-8] _ = x[ErrorClassOdb-9] _ = x[ErrorClassIndex-10] _ = x[ErrorClassObject-11] _ = x[ErrorClassNet-12] _ = x[ErrorClassTag-13] _ = x[ErrorClassTree-14] _ = x[ErrorClassIndexer-15] _ = x[ErrorClassSSL-16] _ = x[ErrorClassSubmodule-17] _ = x[ErrorClassThread-18] _ = x[ErrorClassStash-19] _ = x[ErrorClassCheckout-20] _ = x[ErrorClassFetchHead-21] _ = x[ErrorClassMerge-22] _ = x[ErrorClassSSH-23] _ = x[ErrorClassFilter-24] _ = x[ErrorClassRevert-25] _ = x[ErrorClassCallback-26] _ = x[ErrorClassRebase-29] _ = x[ErrorClassPatch-31] } const ( _ErrorClass_name_0 = "NoneNoMemoryOSInvalidReferenceZlibRepositoryConfigRegexOdbIndexObjectNetTagTreeIndexerSSLSubmoduleThreadStashCheckoutFetchHeadMergeSSHFilterRevertCallback" _ErrorClass_name_1 = "Rebase" _ErrorClass_name_2 = "Patch" ) var ( _ErrorClass_index_0 = [...]uint8{0, 4, 12, 14, 21, 30, 34, 44, 50, 55, 58, 63, 69, 72, 75, 79, 86, 89, 98, 104, 109, 117, 126, 131, 134, 140, 146, 154} ) func (i ErrorClass) String() string { switch { case 0 <= i && i <= 26: return _ErrorClass_name_0[_ErrorClass_index_0[i]:_ErrorClass_index_0[i+1]] case i == 29: return _ErrorClass_name_1 case i == 31: return _ErrorClass_name_2 default: return "ErrorClass(" + strconv.FormatInt(int64(i), 10) + ")" } } git2go-31.4.3/errorcode_string.go000066400000000000000000000043441376323343000167070ustar00rootroot00000000000000// Code generated by "stringer -type ErrorCode -trimprefix ErrorCode -tags static"; DO NOT EDIT. package git import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[ErrorCodeOK-0] _ = x[ErrorCodeGeneric - -1] _ = x[ErrorCodeNotFound - -3] _ = x[ErrorCodeExists - -4] _ = x[ErrorCodeAmbiguous - -5] _ = x[ErrorCodeBuffs - -6] _ = x[ErrorCodeUser - -7] _ = x[ErrorCodeBareRepo - -8] _ = x[ErrorCodeUnbornBranch - -9] _ = x[ErrorCodeUnmerged - -10] _ = x[ErrorCodeNonFastForward - -11] _ = x[ErrorCodeInvalidSpec - -12] _ = x[ErrorCodeConflict - -13] _ = x[ErrorCodeLocked - -14] _ = x[ErrorCodeModified - -15] _ = x[ErrorCodeAuth - -16] _ = x[ErrorCodeCertificate - -17] _ = x[ErrorCodeApplied - -18] _ = x[ErrorCodePeel - -19] _ = x[ErrorCodeEOF - -20] _ = x[ErrorCodeInvalid - -21] _ = x[ErrorCodeUncommitted - -22] _ = x[ErrorCodeDirectory - -23] _ = x[ErrorCodeMergeConflict - -24] _ = x[ErrorCodePassthrough - -30] _ = x[ErrorCodeIterOver - -31] _ = x[ErrorCodeRetry - -32] _ = x[ErrorCodeMismatch - -33] _ = x[ErrorCodeIndexDirty - -34] _ = x[ErrorCodeApplyFail - -35] } const ( _ErrorCode_name_0 = "ApplyFailIndexDirtyMismatchRetryIterOverPassthrough" _ErrorCode_name_1 = "MergeConflictDirectoryUncommittedInvalidEOFPeelAppliedCertificateAuthModifiedLockedConflictInvalidSpecNonFastForwardUnmergedUnbornBranchBareRepoUserBuffsAmbiguousExistsNotFound" _ErrorCode_name_2 = "GenericOK" ) var ( _ErrorCode_index_0 = [...]uint8{0, 9, 19, 27, 32, 40, 51} _ErrorCode_index_1 = [...]uint8{0, 13, 22, 33, 40, 43, 47, 54, 65, 69, 77, 83, 91, 102, 116, 124, 136, 144, 148, 153, 162, 168, 176} _ErrorCode_index_2 = [...]uint8{0, 7, 9} ) func (i ErrorCode) String() string { switch { case -35 <= i && i <= -30: i -= -35 return _ErrorCode_name_0[_ErrorCode_index_0[i]:_ErrorCode_index_0[i+1]] case -24 <= i && i <= -3: i -= -24 return _ErrorCode_name_1[_ErrorCode_index_1[i]:_ErrorCode_index_1[i+1]] case -1 <= i && i <= 0: i -= -1 return _ErrorCode_name_2[_ErrorCode_index_2[i]:_ErrorCode_index_2[i+1]] default: return "ErrorCode(" + strconv.FormatInt(int64(i), 10) + ")" } } git2go-31.4.3/features.go000066400000000000000000000012211376323343000151420ustar00rootroot00000000000000package git /* #include */ import "C" type Feature int const ( // libgit2 was built with threading support FeatureThreads Feature = C.GIT_FEATURE_THREADS // libgit2 was built with HTTPS support built-in FeatureHTTPS Feature = C.GIT_FEATURE_HTTPS // libgit2 was build with SSH support built-in FeatureSSH Feature = C.GIT_FEATURE_SSH // libgit2 was built with nanosecond support for files FeatureNSec Feature = C.GIT_FEATURE_NSEC ) // Features returns a bit-flag of Feature values indicating which features the // loaded libgit2 library has. func Features() Feature { features := C.git_libgit2_features() return Feature(features) } git2go-31.4.3/git.go000066400000000000000000000251331376323343000141170ustar00rootroot00000000000000package git /* #include #include */ import "C" import ( "bytes" "encoding/hex" "errors" "runtime" "strings" "unsafe" ) type ErrorClass int const ( ErrorClassNone ErrorClass = C.GIT_ERROR_NONE ErrorClassNoMemory ErrorClass = C.GIT_ERROR_NOMEMORY ErrorClassOS ErrorClass = C.GIT_ERROR_OS ErrorClassInvalid ErrorClass = C.GIT_ERROR_INVALID ErrorClassReference ErrorClass = C.GIT_ERROR_REFERENCE ErrorClassZlib ErrorClass = C.GIT_ERROR_ZLIB ErrorClassRepository ErrorClass = C.GIT_ERROR_REPOSITORY ErrorClassConfig ErrorClass = C.GIT_ERROR_CONFIG ErrorClassRegex ErrorClass = C.GIT_ERROR_REGEX ErrorClassOdb ErrorClass = C.GIT_ERROR_ODB ErrorClassIndex ErrorClass = C.GIT_ERROR_INDEX ErrorClassObject ErrorClass = C.GIT_ERROR_OBJECT ErrorClassNet ErrorClass = C.GIT_ERROR_NET ErrorClassTag ErrorClass = C.GIT_ERROR_TAG ErrorClassTree ErrorClass = C.GIT_ERROR_TREE ErrorClassIndexer ErrorClass = C.GIT_ERROR_INDEXER ErrorClassSSL ErrorClass = C.GIT_ERROR_SSL ErrorClassSubmodule ErrorClass = C.GIT_ERROR_SUBMODULE ErrorClassThread ErrorClass = C.GIT_ERROR_THREAD ErrorClassStash ErrorClass = C.GIT_ERROR_STASH ErrorClassCheckout ErrorClass = C.GIT_ERROR_CHECKOUT ErrorClassFetchHead ErrorClass = C.GIT_ERROR_FETCHHEAD ErrorClassMerge ErrorClass = C.GIT_ERROR_MERGE ErrorClassSSH ErrorClass = C.GIT_ERROR_SSH ErrorClassFilter ErrorClass = C.GIT_ERROR_FILTER ErrorClassRevert ErrorClass = C.GIT_ERROR_REVERT ErrorClassCallback ErrorClass = C.GIT_ERROR_CALLBACK ErrorClassRebase ErrorClass = C.GIT_ERROR_REBASE ErrorClassPatch ErrorClass = C.GIT_ERROR_PATCH ) type ErrorCode int const ( // ErrorCodeOK indicates that the operation completed successfully. ErrorCodeOK ErrorCode = C.GIT_OK // ErrorCodeGeneric represents a generic error. ErrorCodeGeneric ErrorCode = C.GIT_ERROR // ErrorCodeNotFound represents that the requested object could not be found ErrorCodeNotFound ErrorCode = C.GIT_ENOTFOUND // ErrorCodeExists represents that the object exists preventing operation. ErrorCodeExists ErrorCode = C.GIT_EEXISTS // ErrorCodeAmbiguous represents that more than one object matches. ErrorCodeAmbiguous ErrorCode = C.GIT_EAMBIGUOUS // ErrorCodeBuffs represents that the output buffer is too short to hold data. ErrorCodeBuffs ErrorCode = C.GIT_EBUFS // ErrorCodeUser is a special error that is never generated by libgit2 // code. You can return it from a callback (e.g to stop an iteration) // to know that it was generated by the callback and not by libgit2. ErrorCodeUser ErrorCode = C.GIT_EUSER // ErrorCodeBareRepo represents that the operation not allowed on bare repository ErrorCodeBareRepo ErrorCode = C.GIT_EBAREREPO // ErrorCodeUnbornBranch represents that HEAD refers to branch with no commits. ErrorCodeUnbornBranch ErrorCode = C.GIT_EUNBORNBRANCH // ErrorCodeUnmerged represents that a merge in progress prevented operation. ErrorCodeUnmerged ErrorCode = C.GIT_EUNMERGED // ErrorCodeNonFastForward represents that the reference was not fast-forwardable. ErrorCodeNonFastForward ErrorCode = C.GIT_ENONFASTFORWARD // ErrorCodeInvalidSpec represents that the name/ref spec was not in a valid format. ErrorCodeInvalidSpec ErrorCode = C.GIT_EINVALIDSPEC // ErrorCodeConflict represents that checkout conflicts prevented operation. ErrorCodeConflict ErrorCode = C.GIT_ECONFLICT // ErrorCodeLocked represents that lock file prevented operation. ErrorCodeLocked ErrorCode = C.GIT_ELOCKED // ErrorCodeModified represents that the reference value does not match expected. ErrorCodeModified ErrorCode = C.GIT_EMODIFIED // ErrorCodeAuth represents that the authentication failed. ErrorCodeAuth ErrorCode = C.GIT_EAUTH // ErrorCodeCertificate represents that the server certificate is invalid. ErrorCodeCertificate ErrorCode = C.GIT_ECERTIFICATE // ErrorCodeApplied represents that the patch/merge has already been applied. ErrorCodeApplied ErrorCode = C.GIT_EAPPLIED // ErrorCodePeel represents that the requested peel operation is not possible. ErrorCodePeel ErrorCode = C.GIT_EPEEL // ErrorCodeEOF represents an unexpected EOF. ErrorCodeEOF ErrorCode = C.GIT_EEOF // ErrorCodeInvalid represents an invalid operation or input. ErrorCodeInvalid ErrorCode = C.GIT_EINVALID // ErrorCodeUIncommitted represents that uncommitted changes in index prevented operation. ErrorCodeUncommitted ErrorCode = C.GIT_EUNCOMMITTED // ErrorCodeDirectory represents that the operation is not valid for a directory. ErrorCodeDirectory ErrorCode = C.GIT_EDIRECTORY // ErrorCodeMergeConflict represents that a merge conflict exists and cannot continue. ErrorCodeMergeConflict ErrorCode = C.GIT_EMERGECONFLICT // ErrorCodePassthrough represents that a user-configured callback refused to act. ErrorCodePassthrough ErrorCode = C.GIT_PASSTHROUGH // ErrorCodeIterOver signals end of iteration with iterator. ErrorCodeIterOver ErrorCode = C.GIT_ITEROVER // ErrorCodeRetry is an internal-only error code. ErrorCodeRetry ErrorCode = C.GIT_RETRY // ErrorCodeMismatch represents a hashsum mismatch in object. ErrorCodeMismatch ErrorCode = C.GIT_EMISMATCH // ErrorCodeIndexDirty represents that unsaved changes in the index would be overwritten. ErrorCodeIndexDirty ErrorCode = C.GIT_EINDEXDIRTY // ErrorCodeApplyFail represents that a patch application failed. ErrorCodeApplyFail ErrorCode = C.GIT_EAPPLYFAIL ) var ( ErrInvalid = errors.New("Invalid state for operation") ) var pointerHandles *HandleList func init() { initLibGit2() } func initLibGit2() { pointerHandles = NewHandleList() C.git_libgit2_init() // Due to the multithreaded nature of Go and its interaction with // calling C functions, we cannot work with a library that was not built // with multi-threading support. The most likely outcome is a segfault // or panic at an incomprehensible time, so let's make it easy by // panicking right here. if Features()&FeatureThreads == 0 { panic("libgit2 was not built with threading support") } // 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() } // Shutdown frees all the resources acquired by libgit2. Make sure no // references to any git2go objects are live before calling this. // After this is called, invoking any function from this library will result in // undefined behavior, so make sure this is called carefully. func Shutdown() { pointerHandles.Clear() C.git_libgit2_shutdown() } // ReInit reinitializes the global state, this is useful if the effective user // id has changed and you want to update the stored search paths for gitconfig // files. This function frees any references to objects, so it should be called // before any other functions are called. func ReInit() { Shutdown() initLibGit2() } // Oid represents the id for a Git object. type Oid [20]byte func newOidFromC(coid *C.git_oid) *Oid { if coid == nil { return nil } oid := new(Oid) copy(oid[0:20], C.GoBytes(unsafe.Pointer(coid), 20)) return oid } func NewOidFromBytes(b []byte) *Oid { oid := new(Oid) copy(oid[0:20], b[0:20]) return oid } func (oid *Oid) toC() *C.git_oid { return (*C.git_oid)(unsafe.Pointer(oid)) } func NewOid(s string) (*Oid, error) { if len(s) > C.GIT_OID_HEXSZ { return nil, errors.New("string is too long for oid") } o := new(Oid) slice, error := hex.DecodeString(s) if error != nil { return nil, error } if len(slice) != 20 { return nil, &GitError{"Invalid Oid", ErrorClassNone, ErrGeneric} } copy(o[:], slice[:20]) return o, nil } func (oid *Oid) String() string { return hex.EncodeToString(oid[:]) } func (oid *Oid) Cmp(oid2 *Oid) int { return bytes.Compare(oid[:], oid2[:]) } func (oid *Oid) Copy() *Oid { ret := *oid return &ret } func (oid *Oid) Equal(oid2 *Oid) bool { return *oid == *oid2 } func (oid *Oid) IsZero() bool { return *oid == Oid{} } func (oid *Oid) NCmp(oid2 *Oid, n uint) int { return bytes.Compare(oid[:n], oid2[:n]) } func ShortenOids(ids []*Oid, minlen int) (int, error) { shorten := C.git_oid_shorten_new(C.size_t(minlen)) if shorten == nil { panic("Out of memory") } defer C.git_oid_shorten_free(shorten) var ret C.int runtime.LockOSThread() defer runtime.UnlockOSThread() for _, id := range ids { buf := make([]byte, 41) C.git_oid_fmt((*C.char)(unsafe.Pointer(&buf[0])), id.toC()) buf[40] = 0 ret = C.git_oid_shorten_add(shorten, (*C.char)(unsafe.Pointer(&buf[0]))) if ret < 0 { return int(ret), MakeGitError(ret) } } runtime.KeepAlive(ids) return int(ret), nil } type GitError struct { Message string Class ErrorClass Code ErrorCode } func (e GitError) Error() string { return e.Message } func IsErrorClass(err error, c ErrorClass) bool { if err == nil { return false } if gitError, ok := err.(*GitError); ok { return gitError.Class == c } return false } func IsErrorCode(err error, c ErrorCode) bool { if err == nil { return false } if gitError, ok := err.(*GitError); ok { return gitError.Code == c } return false } func MakeGitError(c C.int) error { var errMessage string var errClass ErrorClass errorCode := ErrorCode(c) if errorCode != ErrorCodeIterOver { err := C.git_error_last() if err != nil { errMessage = C.GoString(err.message) errClass = ErrorClass(err.klass) } else { errClass = ErrorClassInvalid } } if errMessage == "" { errMessage = errorCode.String() } return &GitError{errMessage, errClass, errorCode} } func MakeGitError2(err int) error { return MakeGitError(C.int(err)) } func cbool(b bool) C.int { if b { return C.int(1) } return C.int(0) } func ucbool(b bool) C.uint { if b { return C.uint(1) } return C.uint(0) } func setCallbackError(errorMessage **C.char, err error) C.int { if err != nil { *errorMessage = C.CString(err.Error()) if gitError, ok := err.(*GitError); ok { return C.int(gitError.Code) } return C.int(ErrorCodeUser) } return C.int(ErrorCodeOK) } func Discover(start string, across_fs bool, ceiling_dirs []string) (string, error) { ceildirs := C.CString(strings.Join(ceiling_dirs, string(C.GIT_PATH_LIST_SEPARATOR))) defer C.free(unsafe.Pointer(ceildirs)) cstart := C.CString(start) defer C.free(unsafe.Pointer(cstart)) var buf C.git_buf defer C.git_buf_dispose(&buf) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_repository_discover(&buf, cstart, cbool(across_fs), ceildirs) if ret < 0 { return "", MakeGitError(ret) } return C.GoString(buf.ptr), nil } git2go-31.4.3/git_bundled_static.go000066400000000000000000000010061376323343000171540ustar00rootroot00000000000000// +build static,!system_libgit2 package git /* #cgo windows CFLAGS: -I${SRCDIR}/static-build/install/include/ #cgo windows LDFLAGS: -L${SRCDIR}/static-build/install/lib/ -lgit2 -lwinhttp #cgo !windows pkg-config: --static ${SRCDIR}/static-build/install/lib/pkgconfig/libgit2.pc #cgo CFLAGS: -DLIBGIT2_STATIC #include #if LIBGIT2_VER_MAJOR != 1 || LIBGIT2_VER_MINOR < 1 || LIBGIT2_VER_MINOR > 1 # error "Invalid libgit2 version; this git2go supports libgit2 between v1.1.0 and v1.1.0" #endif */ import "C" git2go-31.4.3/git_system_dynamic.go000066400000000000000000000004521376323343000172240ustar00rootroot00000000000000// +build !static package git /* #cgo pkg-config: libgit2 #cgo CFLAGS: -DLIBGIT2_DYNAMIC #include #if LIBGIT2_VER_MAJOR != 1 || LIBGIT2_VER_MINOR < 1 || LIBGIT2_VER_MINOR > 1 # error "Invalid libgit2 version; this git2go supports libgit2 between v1.1.0 and v1.1.0" #endif */ import "C" git2go-31.4.3/git_system_static.go000066400000000000000000000005001376323343000170610ustar00rootroot00000000000000// +build static,system_libgit2 package git /* #cgo pkg-config: libgit2 --static #cgo CFLAGS: -DLIBGIT2_STATIC #include #if LIBGIT2_VER_MAJOR != 1 || LIBGIT2_VER_MINOR < 1 || LIBGIT2_VER_MINOR > 1 # error "Invalid libgit2 version; this git2go supports libgit2 between v1.1.0 and v1.1.0" #endif */ import "C" git2go-31.4.3/git_test.go000066400000000000000000000072051376323343000151560ustar00rootroot00000000000000package git import ( "io/ioutil" "os" "path" "testing" "time" ) func cleanupTestRepo(t *testing.T, r *Repository) { var err error if r.IsBare() { err = os.RemoveAll(r.Path()) } else { err = os.RemoveAll(r.Workdir()) } checkFatal(t, err) r.Free() } func createTestRepo(t *testing.T) *Repository { // figure out where we can create the test repo path, err := ioutil.TempDir("", "git2go") checkFatal(t, err) repo, err := InitRepository(path, false) checkFatal(t, err) tmpfile := "README" err = ioutil.WriteFile(path+"/"+tmpfile, []byte("foo\n"), 0644) checkFatal(t, err) return repo } func createBareTestRepo(t *testing.T) *Repository { // figure out where we can create the test repo path, err := ioutil.TempDir("", "git2go") checkFatal(t, err) repo, err := InitRepository(path, true) checkFatal(t, err) return repo } // commitOptions contains any extra options for creating commits in the seed repo type commitOptions struct { CommitSigningCallback } func seedTestRepo(t *testing.T, repo *Repository) (*Oid, *Oid) { return seedTestRepoOpt(t, repo, commitOptions{}) } func seedTestRepoOpt(t *testing.T, repo *Repository, opts commitOptions) (*Oid, *Oid) { loc, err := time.LoadLocation("Europe/Berlin") checkFatal(t, err) sig := &Signature{ Name: "Rand Om Hacker", Email: "random@hacker.com", When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc), } idx, err := repo.Index() checkFatal(t, err) err = idx.AddByPath("README") checkFatal(t, err) err = idx.Write() checkFatal(t, err) treeId, err := idx.WriteTree() checkFatal(t, err) message := "This is a commit\n" tree, err := repo.LookupTree(treeId) checkFatal(t, err) commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree) checkFatal(t, err) if opts.CommitSigningCallback != nil { commit, err := repo.LookupCommit(commitId) checkFatal(t, err) signature, signatureField, err := opts.CommitSigningCallback(commit.ContentToSign()) checkFatal(t, err) oid, err := commit.WithSignature(signature, signatureField) checkFatal(t, err) newCommit, err := repo.LookupCommit(oid) checkFatal(t, err) head, err := repo.Head() checkFatal(t, err) _, err = repo.References.Create( head.Name(), newCommit.Id(), true, "repoint to signed commit", ) checkFatal(t, err) } return commitId, treeId } func pathInRepo(repo *Repository, name string) string { return path.Join(path.Dir(path.Dir(repo.Path())), name) } func updateReadme(t *testing.T, repo *Repository, content string) (*Oid, *Oid) { loc, err := time.LoadLocation("Europe/Berlin") checkFatal(t, err) sig := &Signature{ Name: "Rand Om Hacker", Email: "random@hacker.com", When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc), } tmpfile := "README" err = ioutil.WriteFile(pathInRepo(repo, tmpfile), []byte(content), 0644) checkFatal(t, err) idx, err := repo.Index() checkFatal(t, err) err = idx.AddByPath("README") checkFatal(t, err) err = idx.Write() checkFatal(t, err) treeId, err := idx.WriteTree() checkFatal(t, err) currentBranch, err := repo.Head() checkFatal(t, err) currentTip, err := repo.LookupCommit(currentBranch.Target()) checkFatal(t, err) message := "This is a commit\n" tree, err := repo.LookupTree(treeId) checkFatal(t, err) commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree, currentTip) checkFatal(t, err) return commitId, treeId } func TestOidZero(t *testing.T) { t.Parallel() var zeroId Oid if !zeroId.IsZero() { t.Error("Zero Oid is not zero") } } func TestEmptyOid(t *testing.T) { t.Parallel() _, err := NewOid("") if err == nil || !IsErrorCode(err, ErrorCodeGeneric) { t.Fatal("Should have returned invalid error") } } git2go-31.4.3/go.mod000066400000000000000000000003601376323343000141060ustar00rootroot00000000000000module github.com/libgit2/git2go/v31 go 1.13 require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c golang.org/x/sys v0.0.0-20201204225414-ed752295db88 // indirect ) git2go-31.4.3/go.sum000066400000000000000000000025211376323343000141340ustar00rootroot00000000000000github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c h1:9HhBz5L/UjnK9XLtiZhYAdue5BVKep3PMmS2LuPDt8k= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88 h1:KmZPnMocC93w341XZp26yTJg8Za7lhb2KhkYmixoeso= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= git2go-31.4.3/graph.go000066400000000000000000000015741376323343000144400ustar00rootroot00000000000000package git /* #include */ import "C" import ( "runtime" ) func (repo *Repository) DescendantOf(commit, ancestor *Oid) (bool, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_graph_descendant_of(repo.ptr, commit.toC(), ancestor.toC()) runtime.KeepAlive(repo) runtime.KeepAlive(commit) runtime.KeepAlive(ancestor) if ret < 0 { return false, MakeGitError(ret) } return (ret > 0), nil } func (repo *Repository) AheadBehind(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()) runtime.KeepAlive(repo) runtime.KeepAlive(local) runtime.KeepAlive(upstream) if ret < 0 { return 0, 0, MakeGitError(ret) } return int(aheadT), int(behindT), nil } git2go-31.4.3/handles.go000066400000000000000000000023301376323343000147440ustar00rootroot00000000000000package git /* #include */ import "C" import ( "fmt" "sync" "unsafe" ) type HandleList struct { sync.RWMutex // stores the Go pointers handles map[unsafe.Pointer]interface{} } func NewHandleList() *HandleList { return &HandleList{ handles: make(map[unsafe.Pointer]interface{}), } } // Track adds the given pointer to the list of pointers to track and // returns a pointer value which can be passed to C as an opaque // pointer. func (v *HandleList) Track(pointer interface{}) unsafe.Pointer { handle := C.malloc(1) v.Lock() v.handles[handle] = pointer v.Unlock() return handle } // Untrack stops tracking the pointer given by the handle func (v *HandleList) Untrack(handle unsafe.Pointer) { v.Lock() delete(v.handles, handle) C.free(handle) v.Unlock() } // Clear stops tracking all the managed pointers. func (v *HandleList) Clear() { v.Lock() for handle := range v.handles { delete(v.handles, handle) C.free(handle) } v.Unlock() } // Get retrieves the pointer from the given handle func (v *HandleList) Get(handle unsafe.Pointer) interface{} { v.RLock() defer v.RUnlock() ptr, ok := v.handles[handle] if !ok { panic(fmt.Sprintf("invalid pointer handle: %p", handle)) } return ptr } git2go-31.4.3/ignore.go000066400000000000000000000017461376323343000146230ustar00rootroot00000000000000package git /* #include */ import "C" import ( "runtime" "unsafe" ) func (v *Repository) AddIgnoreRule(rules string) error { crules := C.CString(rules) defer C.free(unsafe.Pointer(crules)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_ignore_add_rule(v.ptr, crules) runtime.KeepAlive(v) if ret < 0 { return MakeGitError(ret) } return nil } func (v *Repository) ClearInternalIgnoreRules() error { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_ignore_clear_internal_rules(v.ptr) runtime.KeepAlive(v) if ret < 0 { return MakeGitError(ret) } return nil } func (v *Repository) IsPathIgnored(path string) (bool, error) { var ignored C.int cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_ignore_path_is_ignored(&ignored, v.ptr, cpath) runtime.KeepAlive(v) if ret < 0 { return false, MakeGitError(ret) } return ignored == 1, nil } git2go-31.4.3/index.go000066400000000000000000000363611376323343000144500ustar00rootroot00000000000000package git /* #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*); extern int _go_git_index_remove_all(git_index*, const git_strarray*, void*); */ import "C" import ( "errors" "fmt" "runtime" "unsafe" ) type IndexMatchedPathCallback func(string, string) int type indexMatchedPathCallbackData struct { callback IndexMatchedPathCallback errorTarget *error } // IndexAddOption is a set of flags for APIs that add files matching pathspec. type IndexAddOption uint const ( IndexAddDefault IndexAddOption = C.GIT_INDEX_ADD_DEFAULT IndexAddForce IndexAddOption = C.GIT_INDEX_ADD_FORCE IndexAddDisablePathspecMatch IndexAddOption = C.GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH IndexAddCheckPathspec IndexAddOption = C.GIT_INDEX_ADD_CHECK_PATHSPEC ) // IndexStageState indicates the state of the git index. type IndexStageState int const ( // IndexStageAny matches any index stage. // // Some index APIs take a stage to match; pass this value to match // any entry matching the path regardless of stage. IndexStageAny IndexStageState = C.GIT_INDEX_STAGE_ANY // IndexStageNormal is a normal staged file in the index. IndexStageNormal IndexStageState = C.GIT_INDEX_STAGE_NORMAL // IndexStageAncestor is the ancestor side of a conflict. IndexStageAncestor IndexStageState = C.GIT_INDEX_STAGE_ANCESTOR // IndexStageOurs is the "ours" side of a conflict. IndexStageOurs IndexStageState = C.GIT_INDEX_STAGE_OURS // IndexStageTheirs is the "theirs" side of a conflict. IndexStageTheirs IndexStageState = C.GIT_INDEX_STAGE_THEIRS ) type Index struct { ptr *C.git_index repo *Repository } type IndexTime struct { seconds int32 nanoseconds uint32 } type IndexEntry struct { Ctime IndexTime Mtime IndexTime Mode Filemode Uid uint32 Gid uint32 Size uint32 Id *Oid Path string } func newIndexEntryFromC(entry *C.git_index_entry) *IndexEntry { if entry == nil { return nil } return &IndexEntry{ IndexTime{int32(entry.ctime.seconds), uint32(entry.ctime.nanoseconds)}, IndexTime{int32(entry.mtime.seconds), uint32(entry.mtime.nanoseconds)}, Filemode(entry.mode), uint32(entry.uid), uint32(entry.gid), uint32(entry.file_size), newOidFromC(&entry.id), C.GoString(entry.path), } } func populateCIndexEntry(source *IndexEntry, dest *C.git_index_entry) { dest.ctime.seconds = C.int32_t(source.Ctime.seconds) dest.ctime.nanoseconds = C.uint32_t(source.Ctime.nanoseconds) dest.mtime.seconds = C.int32_t(source.Mtime.seconds) dest.mtime.nanoseconds = C.uint32_t(source.Mtime.nanoseconds) dest.mode = C.uint32_t(source.Mode) dest.uid = C.uint32_t(source.Uid) dest.gid = C.uint32_t(source.Gid) dest.file_size = C.uint32_t(source.Size) if source.Id != nil { dest.id = *source.Id.toC() } dest.path = C.CString(source.Path) } func freeCIndexEntry(entry *C.git_index_entry) { C.free(unsafe.Pointer(entry.path)) } func newIndexFromC(ptr *C.git_index, repo *Repository) *Index { idx := &Index{ptr, repo} runtime.SetFinalizer(idx, (*Index).Free) return idx } // NewIndex allocates a new index. It won't be associated with any // file on the filesystem or repository func NewIndex() (*Index, error) { var ptr *C.git_index runtime.LockOSThread() defer runtime.UnlockOSThread() if err := C.git_index_new(&ptr); err < 0 { return nil, MakeGitError(err) } return newIndexFromC(ptr, nil), nil } // OpenIndex creates a new index at the given path. If the file does // not exist it will be created when Write() is called. func OpenIndex(path string) (*Index, error) { var ptr *C.git_index var cpath = C.CString(path) defer C.free(unsafe.Pointer(cpath)) runtime.LockOSThread() defer runtime.UnlockOSThread() if err := C.git_index_open(&ptr, cpath); err < 0 { return nil, MakeGitError(err) } return newIndexFromC(ptr, nil), nil } // Path returns the index' path on disk or an empty string if it // exists only in memory. func (v *Index) Path() string { ret := C.GoString(C.git_index_path(v.ptr)) runtime.KeepAlive(v) return ret } // Clear clears the index object in memory; changes must be explicitly // written to disk for them to take effect persistently func (v *Index) Clear() error { runtime.LockOSThread() defer runtime.UnlockOSThread() err := C.git_index_clear(v.ptr) runtime.KeepAlive(v) if err < 0 { return MakeGitError(err) } return nil } // Add adds or replaces the given entry to the index, making a copy of // the data func (v *Index) Add(entry *IndexEntry) error { var centry C.git_index_entry populateCIndexEntry(entry, ¢ry) defer freeCIndexEntry(¢ry) runtime.LockOSThread() defer runtime.UnlockOSThread() err := C.git_index_add(v.ptr, ¢ry) runtime.KeepAlive(v) if err < 0 { return MakeGitError(err) } return nil } func (v *Index) AddByPath(path string) error { cstr := C.CString(path) defer C.free(unsafe.Pointer(cstr)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_index_add_bypath(v.ptr, cstr) runtime.KeepAlive(v) if ret < 0 { return MakeGitError(ret) } return nil } // AddFromBuffer adds or replaces an index entry from a buffer in memory func (v *Index) AddFromBuffer(entry *IndexEntry, buffer []byte) error { var centry C.git_index_entry populateCIndexEntry(entry, ¢ry) defer freeCIndexEntry(¢ry) var cbuffer unsafe.Pointer if len(buffer) > 0 { cbuffer = unsafe.Pointer(&buffer[0]) } runtime.LockOSThread() defer runtime.UnlockOSThread() if err := C.git_index_add_from_buffer(v.ptr, ¢ry, cbuffer, C.size_t(len(buffer))); err < 0 { return MakeGitError(err) } return nil } func (v *Index) AddAll(pathspecs []string, flags IndexAddOption, callback IndexMatchedPathCallback) error { cpathspecs := C.git_strarray{} cpathspecs.count = C.size_t(len(pathspecs)) cpathspecs.strings = makeCStringsFromStrings(pathspecs) defer freeStrarray(&cpathspecs) var err error data := indexMatchedPathCallbackData{ callback: callback, errorTarget: &err, } runtime.LockOSThread() defer runtime.UnlockOSThread() var handle unsafe.Pointer if callback != nil { handle = pointerHandles.Track(&data) defer pointerHandles.Untrack(handle) } ret := C._go_git_index_add_all( v.ptr, &cpathspecs, C.uint(flags), handle, ) runtime.KeepAlive(v) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } func (v *Index) UpdateAll(pathspecs []string, callback IndexMatchedPathCallback) error { cpathspecs := C.git_strarray{} cpathspecs.count = C.size_t(len(pathspecs)) cpathspecs.strings = makeCStringsFromStrings(pathspecs) defer freeStrarray(&cpathspecs) var err error data := indexMatchedPathCallbackData{ callback: callback, errorTarget: &err, } runtime.LockOSThread() defer runtime.UnlockOSThread() var handle unsafe.Pointer if callback != nil { handle = pointerHandles.Track(&data) defer pointerHandles.Untrack(handle) } ret := C._go_git_index_update_all( v.ptr, &cpathspecs, handle, ) runtime.KeepAlive(v) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } func (v *Index) RemoveAll(pathspecs []string, callback IndexMatchedPathCallback) error { cpathspecs := C.git_strarray{} cpathspecs.count = C.size_t(len(pathspecs)) cpathspecs.strings = makeCStringsFromStrings(pathspecs) defer freeStrarray(&cpathspecs) var err error data := indexMatchedPathCallbackData{ callback: callback, errorTarget: &err, } runtime.LockOSThread() defer runtime.UnlockOSThread() var handle unsafe.Pointer if callback != nil { handle = pointerHandles.Track(&data) defer pointerHandles.Untrack(handle) } ret := C._go_git_index_remove_all( v.ptr, &cpathspecs, handle, ) runtime.KeepAlive(v) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } //export indexMatchedPathCallback func indexMatchedPathCallback(cPath, cMatchedPathspec *C.char, payload unsafe.Pointer) C.int { data, ok := pointerHandles.Get(payload).(*indexMatchedPathCallbackData) if !ok { panic("invalid matched path callback") } ret := data.callback(C.GoString(cPath), C.GoString(cMatchedPathspec)) if ret < 0 { *data.errorTarget = errors.New(ErrorCode(ret).String()) return C.int(ErrorCodeUser) } return C.int(ErrorCodeOK) } func (v *Index) RemoveByPath(path string) error { cstr := C.CString(path) defer C.free(unsafe.Pointer(cstr)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_index_remove_bypath(v.ptr, cstr) runtime.KeepAlive(v) if ret < 0 { return MakeGitError(ret) } return nil } // RemoveDirectory removes all entries from the index under a given directory. func (v *Index) RemoveDirectory(dir string, stage int) error { cstr := C.CString(dir) defer C.free(unsafe.Pointer(cstr)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_index_remove_directory(v.ptr, cstr, C.int(stage)) runtime.KeepAlive(v) if ret < 0 { return MakeGitError(ret) } return nil } func (v *Index) WriteTreeTo(repo *Repository) (*Oid, error) { oid := new(Oid) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_index_write_tree_to(oid.toC(), v.ptr, repo.ptr) runtime.KeepAlive(v) runtime.KeepAlive(repo) if ret < 0 { return nil, MakeGitError(ret) } return oid, nil } // ReadTree replaces the contents of the index with those of the given // tree func (v *Index) ReadTree(tree *Tree) error { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_index_read_tree(v.ptr, tree.cast_ptr) runtime.KeepAlive(v) runtime.KeepAlive(tree) if ret < 0 { return MakeGitError(ret) } return nil } func (v *Index) WriteTree() (*Oid, error) { oid := new(Oid) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_index_write_tree(oid.toC(), v.ptr) runtime.KeepAlive(v) if ret < 0 { return nil, MakeGitError(ret) } return oid, nil } func (v *Index) Write() error { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_index_write(v.ptr) runtime.KeepAlive(v) if ret < 0 { return MakeGitError(ret) } return nil } func (v *Index) Free() { runtime.SetFinalizer(v, nil) C.git_index_free(v.ptr) } func (v *Index) EntryCount() uint { ret := uint(C.git_index_entrycount(v.ptr)) runtime.KeepAlive(v) return ret } func (v *Index) EntryByIndex(index uint) (*IndexEntry, error) { centry := C.git_index_get_byindex(v.ptr, C.size_t(index)) if centry == nil { return nil, fmt.Errorf("Index out of Bounds") } ret := newIndexEntryFromC(centry) runtime.KeepAlive(v) return ret, nil } func (v *Index) EntryByPath(path string, stage int) (*IndexEntry, error) { cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) runtime.LockOSThread() defer runtime.UnlockOSThread() centry := C.git_index_get_bypath(v.ptr, cpath, C.int(stage)) if centry == nil { return nil, MakeGitError(C.int(ErrorCodeNotFound)) } ret := newIndexEntryFromC(centry) runtime.KeepAlive(v) return ret, nil } func (v *Index) Find(path string) (uint, error) { cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) runtime.LockOSThread() defer runtime.UnlockOSThread() var pos C.size_t ret := C.git_index_find(&pos, v.ptr, cpath) runtime.KeepAlive(v) if ret < 0 { return uint(0), MakeGitError(ret) } return uint(pos), nil } func (v *Index) FindPrefix(prefix string) (uint, error) { cprefix := C.CString(prefix) defer C.free(unsafe.Pointer(cprefix)) runtime.LockOSThread() defer runtime.UnlockOSThread() var pos C.size_t ret := C.git_index_find_prefix(&pos, v.ptr, cprefix) runtime.KeepAlive(v) if ret < 0 { return uint(0), MakeGitError(ret) } return uint(pos), nil } func (v *Index) HasConflicts() bool { ret := C.git_index_has_conflicts(v.ptr) != 0 runtime.KeepAlive(v) return ret } // FIXME: this might return an error func (v *Index) CleanupConflicts() { C.git_index_conflict_cleanup(v.ptr) runtime.KeepAlive(v) } func (v *Index) AddConflict(ancestor *IndexEntry, our *IndexEntry, their *IndexEntry) error { var cancestor *C.git_index_entry var cour *C.git_index_entry var ctheir *C.git_index_entry if ancestor != nil { cancestor = &C.git_index_entry{} populateCIndexEntry(ancestor, cancestor) defer freeCIndexEntry(cancestor) } if our != nil { cour = &C.git_index_entry{} populateCIndexEntry(our, cour) defer freeCIndexEntry(cour) } if their != nil { ctheir = &C.git_index_entry{} populateCIndexEntry(their, ctheir) defer freeCIndexEntry(ctheir) } runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_index_conflict_add(v.ptr, cancestor, cour, ctheir) runtime.KeepAlive(v) runtime.KeepAlive(ancestor) runtime.KeepAlive(our) runtime.KeepAlive(their) if ecode < 0 { return MakeGitError(ecode) } return nil } type IndexConflict struct { Ancestor *IndexEntry Our *IndexEntry Their *IndexEntry } func (v *Index) Conflict(path string) (IndexConflict, error) { var cancestor *C.git_index_entry var cour *C.git_index_entry var ctheir *C.git_index_entry cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_index_conflict_get(&cancestor, &cour, &ctheir, v.ptr, cpath) if ecode < 0 { return IndexConflict{}, MakeGitError(ecode) } ret := IndexConflict{ Ancestor: newIndexEntryFromC(cancestor), Our: newIndexEntryFromC(cour), Their: newIndexEntryFromC(ctheir), } runtime.KeepAlive(v) return ret, nil } // deprecated: You should use `Index.Conflict()` instead. func (v *Index) GetConflict(path string) (IndexConflict, error) { return v.Conflict(path) } func (v *Index) RemoveConflict(path string) error { cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_index_conflict_remove(v.ptr, cpath) runtime.KeepAlive(v) if ecode < 0 { return MakeGitError(ecode) } return nil } type IndexConflictIterator struct { ptr *C.git_index_conflict_iterator index *Index } func newIndexConflictIteratorFromC(index *Index, ptr *C.git_index_conflict_iterator) *IndexConflictIterator { i := &IndexConflictIterator{ptr: ptr, index: index} runtime.SetFinalizer(i, (*IndexConflictIterator).Free) return i } func (v *IndexConflictIterator) Index() *Index { return v.index } func (v *IndexConflictIterator) Free() { runtime.SetFinalizer(v, nil) C.git_index_conflict_iterator_free(v.ptr) } func (v *Index) ConflictIterator() (*IndexConflictIterator, error) { var i *C.git_index_conflict_iterator runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_index_conflict_iterator_new(&i, v.ptr) if ecode < 0 { return nil, MakeGitError(ecode) } return newIndexConflictIteratorFromC(v, i), nil } func (v *IndexConflictIterator) Next() (IndexConflict, error) { var cancestor *C.git_index_entry var cour *C.git_index_entry var ctheir *C.git_index_entry runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_index_conflict_next(&cancestor, &cour, &ctheir, v.ptr) if ecode < 0 { return IndexConflict{}, MakeGitError(ecode) } ret := IndexConflict{ Ancestor: newIndexEntryFromC(cancestor), Our: newIndexEntryFromC(cour), Their: newIndexEntryFromC(ctheir), } runtime.KeepAlive(v) return ret, nil } git2go-31.4.3/index_test.go000066400000000000000000000125211376323343000154770ustar00rootroot00000000000000package git import ( "io/ioutil" "os" "path" "runtime" "testing" ) func TestCreateRepoAndStage(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) idx, err := repo.Index() checkFatal(t, err) err = idx.AddByPath("README") checkFatal(t, err) treeId, err := idx.WriteTree() checkFatal(t, err) if treeId.String() != "b7119b11e8ef7a1a5a34d3ac87f5b075228ac81e" { t.Fatalf("%v", treeId.String()) } } func TestIndexReadTree(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) _, _ = seedTestRepo(t, repo) ref, err := repo.Head() checkFatal(t, err) obj, err := ref.Peel(ObjectTree) checkFatal(t, err) tree, err := obj.AsTree() checkFatal(t, err) idx, err := NewIndex() checkFatal(t, err) err = idx.ReadTree(tree) checkFatal(t, err) id, err := idx.WriteTreeTo(repo) checkFatal(t, err) if tree.Id().Cmp(id) != 0 { t.Fatalf("Read and written trees are not the same") } } func TestIndexWriteTreeTo(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) idx, err := NewIndex() checkFatal(t, err) odb, err := repo.Odb() checkFatal(t, err) content, err := ioutil.ReadFile(path.Join(repo.Workdir(), "README")) checkFatal(t, err) id, err := odb.Write(content, ObjectBlob) checkFatal(t, err) err = idx.Add(&IndexEntry{ Mode: FilemodeBlob, Uid: 0, Gid: 0, Size: uint32(len(content)), Id: id, Path: "README", }) checkFatal(t, err) treeId, err := idx.WriteTreeTo(repo) checkFatal(t, err) if treeId.String() != "b7119b11e8ef7a1a5a34d3ac87f5b075228ac81e" { t.Fatalf("%v", treeId.String()) } } func TestIndexAddAndWriteTreeTo(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) odb, err := repo.Odb() checkFatal(t, err) blobID, err := odb.Write([]byte("foo\n"), ObjectBlob) checkFatal(t, err) idx, err := NewIndex() checkFatal(t, err) if idx.Path() != "" { t.Fatal("in-memory repo has a path") } entry := IndexEntry{ Path: "README", Id: blobID, Mode: FilemodeBlob, } err = idx.Add(&entry) checkFatal(t, err) treeId, err := idx.WriteTreeTo(repo) checkFatal(t, err) if treeId.String() != "b7119b11e8ef7a1a5a34d3ac87f5b075228ac81e" { t.Fatalf("%v", treeId.String()) } } func TestIndexRemoveDirectory(t *testing.T) { repo := createTestRepo(t) defer cleanupTestRepo(t, repo) odb, err := repo.Odb() checkFatal(t, err) blobID, err := odb.Write([]byte("fou\n"), ObjectBlob) checkFatal(t, err) idx, err := NewIndex() checkFatal(t, err) entryCount := idx.EntryCount() if entryCount != 0 { t.Fatal("Index should count 0 entry") } entry := IndexEntry{ Path: "path/to/LISEZ_MOI", Id: blobID, Mode: FilemodeBlob, } err = idx.Add(&entry) checkFatal(t, err) entryCount = idx.EntryCount() if entryCount != 1 { t.Fatal("Index should count 1 entry") } err = idx.RemoveDirectory("path", 0) entryCount = idx.EntryCount() if entryCount != 0 { t.Fatal("Index should count 0 entry") } } func TestIndexAddFromBuffer(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) idx, err := repo.Index() checkFatal(t, err) entry := IndexEntry{ Path: "README", Mode: FilemodeBlob, } err = idx.AddFromBuffer(&entry, []byte("foo\n")) checkFatal(t, err) treeId, err := idx.WriteTreeTo(repo) checkFatal(t, err) if treeId.String() != "b7119b11e8ef7a1a5a34d3ac87f5b075228ac81e" { t.Fatalf("%v", treeId.String()) } } func TestIndexAddAllNoCallback(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) err := ioutil.WriteFile(repo.Workdir()+"/README", []byte("foo\n"), 0644) checkFatal(t, err) idx, err := repo.Index() checkFatal(t, err) err = idx.AddAll([]string{}, IndexAddDefault, nil) checkFatal(t, err) treeId, err := idx.WriteTreeTo(repo) checkFatal(t, err) if treeId.String() != "b7119b11e8ef7a1a5a34d3ac87f5b075228ac81e" { t.Fatalf("%v", treeId.String()) } } func TestIndexAddAllCallback(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) err := ioutil.WriteFile(repo.Workdir()+"/README", []byte("foo\n"), 0644) checkFatal(t, err) idx, err := repo.Index() checkFatal(t, err) cbPath := "" err = idx.AddAll([]string{}, IndexAddDefault, func(p, mP string) int { cbPath = p return 0 }) checkFatal(t, err) if cbPath != "README" { t.Fatalf("%v", cbPath) } treeId, err := idx.WriteTreeTo(repo) checkFatal(t, err) if treeId.String() != "b7119b11e8ef7a1a5a34d3ac87f5b075228ac81e" { t.Fatalf("%v", treeId.String()) } } func TestIndexOpen(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) path := repo.Workdir() + "/heyindex" _, err := os.Stat(path) if !os.IsNotExist(err) { t.Fatal("new index file already exists") } idx, err := OpenIndex(path) checkFatal(t, err) if path != idx.Path() { t.Fatalf("mismatched index paths, expected %v, got %v", path, idx.Path()) } err = idx.Write() checkFatal(t, err) _, err = os.Stat(path) if os.IsNotExist(err) { t.Fatal("new index file did not get written") } } func checkFatal(t *testing.T, err error) { if err == nil { return } // The failure happens at wherever we were called, not here _, file, line, ok := runtime.Caller(1) if !ok { t.Fatalf("Unable to get caller") } t.Fatalf("Fail at %v:%v; %v", file, line, err) } git2go-31.4.3/indexer.go000066400000000000000000000052661376323343000147770ustar00rootroot00000000000000package git /* #include extern const git_oid * git_indexer_hash(const git_indexer *idx); extern int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_transfer_progress *stats); extern int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats); extern int _go_git_indexer_new(git_indexer **out, const char *path, unsigned int mode, git_odb *odb, void *progress_cb_payload); extern void git_indexer_free(git_indexer *idx); */ import "C" import ( "reflect" "runtime" "unsafe" ) // Indexer can post-process packfiles and create an .idx file for efficient // lookup. type Indexer struct { ptr *C.git_indexer stats C.git_transfer_progress callbacks RemoteCallbacks callbacksHandle unsafe.Pointer } // NewIndexer creates a new indexer instance. func NewIndexer(packfilePath string, odb *Odb, callback TransferProgressCallback) (indexer *Indexer, err error) { indexer = new(Indexer) runtime.LockOSThread() defer runtime.UnlockOSThread() var odbPtr *C.git_odb = nil if odb != nil { odbPtr = odb.ptr } indexer.callbacks.TransferProgressCallback = callback indexer.callbacksHandle = pointerHandles.Track(&indexer.callbacks) cstr := C.CString(packfilePath) defer C.free(unsafe.Pointer(cstr)) ret := C._go_git_indexer_new(&indexer.ptr, cstr, 0, odbPtr, indexer.callbacksHandle) runtime.KeepAlive(odb) if ret < 0 { pointerHandles.Untrack(indexer.callbacksHandle) return nil, MakeGitError(ret) } runtime.SetFinalizer(indexer, (*Indexer).Free) return indexer, nil } // Write adds data to the indexer. func (indexer *Indexer) Write(data []byte) (int, error) { header := (*reflect.SliceHeader)(unsafe.Pointer(&data)) ptr := unsafe.Pointer(header.Data) size := C.size_t(header.Len) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_indexer_append(indexer.ptr, ptr, size, &indexer.stats) runtime.KeepAlive(indexer) if ret < 0 { return 0, MakeGitError(ret) } return len(data), nil } // Commit finalizes the pack and index. It resolves any pending deltas and // writes out the index file. // // It also returns the packfile's hash. A packfile's name is derived from the // sorted hashing of all object names. func (indexer *Indexer) Commit() (*Oid, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_indexer_commit(indexer.ptr, &indexer.stats) if ret < 0 { return nil, MakeGitError(ret) } id := newOidFromC(C.git_indexer_hash(indexer.ptr)) runtime.KeepAlive(indexer) return id, nil } // Free frees the indexer and its resources. func (indexer *Indexer) Free() { pointerHandles.Untrack(indexer.callbacksHandle) runtime.SetFinalizer(indexer, nil) C.git_indexer_free(indexer.ptr) } git2go-31.4.3/indexer_test.go000066400000000000000000000056041376323343000160320ustar00rootroot00000000000000package git import ( "fmt" "io/ioutil" "os" "path" "testing" ) var ( // This is a packfile with three objects. The second is a delta which // depends on the third, which is also a delta. outOfOrderPack = []byte{ 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x32, 0x78, 0x9c, 0x63, 0x67, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x76, 0xe6, 0x8f, 0xe8, 0x12, 0x9b, 0x54, 0x6b, 0x10, 0x1a, 0xee, 0x95, 0x10, 0xc5, 0x32, 0x8e, 0x7f, 0x21, 0xca, 0x1d, 0x18, 0x78, 0x9c, 0x63, 0x62, 0x66, 0x4e, 0xcb, 0xcf, 0x07, 0x00, 0x02, 0xac, 0x01, 0x4d, 0x75, 0x01, 0xd7, 0x71, 0x36, 0x66, 0xf4, 0xde, 0x82, 0x27, 0x76, 0xc7, 0x62, 0x2c, 0x10, 0xf1, 0xb0, 0x7d, 0xe2, 0x80, 0xdc, 0x78, 0x9c, 0x63, 0x62, 0x62, 0x62, 0xb7, 0x03, 0x00, 0x00, 0x69, 0x00, 0x4c, 0xde, 0x7d, 0xaa, 0xe4, 0x19, 0x87, 0x58, 0x80, 0x61, 0x09, 0x9a, 0x33, 0xca, 0x7a, 0x31, 0x92, 0x6f, 0xae, 0x66, 0x75, } ) func TestIndexerOutOfOrder(t *testing.T) { t.Parallel() tmpPath, err := ioutil.TempDir("", "git2go") checkFatal(t, err) defer os.RemoveAll(tmpPath) var finalStats TransferProgress idx, err := NewIndexer(tmpPath, nil, func(stats TransferProgress) ErrorCode { finalStats = stats return ErrorCodeOK }) checkFatal(t, err) defer idx.Free() _, err = idx.Write(outOfOrderPack) checkFatal(t, err) oid, err := idx.Commit() checkFatal(t, err) // The packfile contains the hash as the last 20 bytes. expectedOid := NewOidFromBytes(outOfOrderPack[len(outOfOrderPack)-20:]) if !expectedOid.Equal(oid) { t.Errorf("mismatched packfile hash, expected %v, got %v", expectedOid, oid) } if finalStats.TotalObjects != 3 { t.Errorf("mismatched transferred objects, expected 3, got %v", finalStats.TotalObjects) } if finalStats.ReceivedObjects != 3 { t.Errorf("mismatched received objects, expected 3, got %v", finalStats.ReceivedObjects) } if finalStats.IndexedObjects != 3 { t.Errorf("mismatched indexed objects, expected 3, got %v", finalStats.IndexedObjects) } odb, err := NewOdb() checkFatal(t, err) defer odb.Free() backend, err := NewOdbBackendOnePack(path.Join(tmpPath, fmt.Sprintf("pack-%s.idx", oid.String()))) checkFatal(t, err) // Transfer the ownership of the backend to the odb, no freeing needed. err = odb.AddBackend(backend, 1) checkFatal(t, err) packfileObjects := 0 err = odb.ForEach(func(id *Oid) error { packfileObjects += 1 return nil }) checkFatal(t, err) if packfileObjects != 3 { t.Errorf("mismatched packfile objects, expected 3, got %v", packfileObjects) } // Inspect one of the well-known objects in the packfile. obj, err := odb.Read(NewOidFromBytes([]byte{ 0x19, 0x10, 0x28, 0x15, 0x66, 0x3d, 0x23, 0xf8, 0xb7, 0x5a, 0x47, 0xe7, 0xa0, 0x19, 0x65, 0xdc, 0xdc, 0x96, 0x46, 0x8c, })) checkFatal(t, err) defer obj.Free() if "foo" != string(obj.Data()) { t.Errorf("mismatched packfile object contents, expected foo, got %q", string(obj.Data())) } } git2go-31.4.3/mempack.go000066400000000000000000000047761376323343000147630ustar00rootroot00000000000000package git /* #include #include extern int git_mempack_new(git_odb_backend **out); extern int git_mempack_dump(git_buf *pack, git_repository *repo, git_odb_backend *backend); extern int git_mempack_reset(git_odb_backend *backend); extern void _go_git_odb_backend_free(git_odb_backend *backend); */ import "C" import ( "runtime" "unsafe" ) // Mempack is a custom ODB backend that permits packing object in-memory. type Mempack struct { ptr *C.git_odb_backend } // NewMempack creates a new mempack instance and registers it to the ODB. func NewMempack(odb *Odb) (mempack *Mempack, err error) { mempack = new(Mempack) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_mempack_new(&mempack.ptr) if ret < 0 { return nil, MakeGitError(ret) } ret = C.git_odb_add_backend(odb.ptr, mempack.ptr, C.int(999)) runtime.KeepAlive(odb) if ret < 0 { // Since git_odb_add_alternate() takes ownership of the ODB backend, the // only case in which we free the mempack's memory is if it fails to be // added to the ODB. C._go_git_odb_backend_free(mempack.ptr) return nil, MakeGitError(ret) } return mempack, nil } // Dump dumps all the queued in-memory writes to a packfile. // // It is the caller's responsibility to ensure that the generated packfile is // available to the repository (e.g. by writing it to disk, or doing something // crazy like distributing it across several copies of the repository over a // network). // // Once the generated packfile is available to the repository, call // Mempack.Reset to cleanup the memory store. // // Calling Mempack.Reset before the packfile has been written to disk will // result in an inconsistent repository (the objects in the memory store won't // be accessible). func (mempack *Mempack) Dump(repository *Repository) ([]byte, error) { buf := C.git_buf{} runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_mempack_dump(&buf, repository.ptr, mempack.ptr) runtime.KeepAlive(repository) if ret < 0 { return nil, MakeGitError(ret) } defer C.git_buf_dispose(&buf) return C.GoBytes(unsafe.Pointer(buf.ptr), C.int(buf.size)), nil } // Reset resets the memory packer by clearing all the queued objects. // // This assumes that Mempack.Dump has been called before to store all the // queued objects into a single packfile. func (mempack *Mempack) Reset() error { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_mempack_reset(mempack.ptr) if ret < 0 { return MakeGitError(ret) } return nil } git2go-31.4.3/mempack_test.go000066400000000000000000000025441376323343000160110ustar00rootroot00000000000000package git import ( "bytes" "testing" ) func TestMempack(t *testing.T) { t.Parallel() odb, err := NewOdb() checkFatal(t, err) repo, err := NewRepositoryWrapOdb(odb) checkFatal(t, err) mempack, err := NewMempack(odb) checkFatal(t, err) id, err := odb.Write([]byte("hello, world!"), ObjectBlob) checkFatal(t, err) expectedId, err := NewOid("30f51a3fba5274d53522d0f19748456974647b4f") checkFatal(t, err) if !expectedId.Equal(id) { t.Errorf("mismatched id. expected %v, got %v", expectedId.String(), id.String()) } // The object should be available from the odb. { obj, err := odb.Read(expectedId) checkFatal(t, err) defer obj.Free() } data, err := mempack.Dump(repo) checkFatal(t, err) expectedData := []byte{ 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x9d, 0x08, 0x82, 0x3b, 0xd8, 0xa8, 0xea, 0xb5, 0x10, 0xad, 0x6a, 0xc7, 0x5c, 0x82, 0x3c, 0xfd, 0x3e, 0xd3, 0x1e, } if !bytes.Equal(expectedData, data) { t.Errorf("mismatched mempack data. expected %v, got %v", expectedData, data) } mempack.Reset() // After the reset, the object should now be unavailable. { obj, err := odb.Read(expectedId) if err == nil { t.Errorf("object %s unexpectedly found", obj.Id().String()) obj.Free() } else if !IsErrorCode(err, ErrorCodeNotFound) { t.Errorf("unexpected error %v", err) } } } git2go-31.4.3/merge.go000066400000000000000000000364471376323343000144450ustar00rootroot00000000000000package git /* #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); extern git_annotated_commit* _go_git_annotated_commit_array_get(git_annotated_commit** array, size_t n); extern int _go_git_merge_file(git_merge_file_result*, char*, size_t, char*, unsigned int, char*, size_t, char*, unsigned int, char*, size_t, char*, unsigned int, git_merge_file_options*); */ import "C" import ( "reflect" "runtime" "unsafe" ) type AnnotatedCommit struct { ptr *C.git_annotated_commit r *Repository } func newAnnotatedCommitFromC(ptr *C.git_annotated_commit, r *Repository) *AnnotatedCommit { mh := &AnnotatedCommit{ptr: ptr, r: r} runtime.SetFinalizer(mh, (*AnnotatedCommit).Free) return mh } func (mh *AnnotatedCommit) Id() *Oid { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := newOidFromC(C.git_annotated_commit_id(mh.ptr)) runtime.KeepAlive(mh) return ret } func (mh *AnnotatedCommit) Free() { runtime.SetFinalizer(mh, nil) C.git_annotated_commit_free(mh.ptr) } func (r *Repository) AnnotatedCommitFromFetchHead(branchName string, remoteURL string, oid *Oid) (*AnnotatedCommit, error) { cbranchName := C.CString(branchName) defer C.free(unsafe.Pointer(cbranchName)) cremoteURL := C.CString(remoteURL) defer C.free(unsafe.Pointer(cremoteURL)) runtime.LockOSThread() defer runtime.UnlockOSThread() var ptr *C.git_annotated_commit ret := C.git_annotated_commit_from_fetchhead(&ptr, r.ptr, cbranchName, cremoteURL, oid.toC()) runtime.KeepAlive(oid) if ret < 0 { return nil, MakeGitError(ret) } annotatedCommit := newAnnotatedCommitFromC(ptr, r) runtime.KeepAlive(r) return annotatedCommit, nil } func (r *Repository) LookupAnnotatedCommit(oid *Oid) (*AnnotatedCommit, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() var ptr *C.git_annotated_commit ret := C.git_annotated_commit_lookup(&ptr, r.ptr, oid.toC()) runtime.KeepAlive(oid) if ret < 0 { return nil, MakeGitError(ret) } annotatedCommit := newAnnotatedCommitFromC(ptr, r) runtime.KeepAlive(r) return annotatedCommit, nil } func (r *Repository) AnnotatedCommitFromRef(ref *Reference) (*AnnotatedCommit, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() var ptr *C.git_annotated_commit ret := C.git_annotated_commit_from_ref(&ptr, r.ptr, ref.ptr) runtime.KeepAlive(r) runtime.KeepAlive(ref) if ret < 0 { return nil, MakeGitError(ret) } annotatedCommit := newAnnotatedCommitFromC(ptr, r) runtime.KeepAlive(r) return annotatedCommit, nil } func (r *Repository) AnnotatedCommitFromRevspec(spec string) (*AnnotatedCommit, error) { crevspec := C.CString(spec) defer C.free(unsafe.Pointer(crevspec)) runtime.LockOSThread() defer runtime.UnlockOSThread() var ptr *C.git_annotated_commit ret := C.git_annotated_commit_from_revspec(&ptr, r.ptr, crevspec) runtime.KeepAlive(r) if ret < 0 { return nil, MakeGitError(ret) } annotatedCommit := newAnnotatedCommitFromC(ptr, r) runtime.KeepAlive(r) return annotatedCommit, nil } type MergeTreeFlag int const ( // Detect renames that occur between the common ancestor and the "ours" // side or the common ancestor and the "theirs" side. This will enable // the ability to merge between a modified and renamed file. MergeTreeFindRenames MergeTreeFlag = C.GIT_MERGE_FIND_RENAMES // If a conflict occurs, exit immediately instead of attempting to // continue resolving conflicts. The merge operation will fail with // GIT_EMERGECONFLICT and no index will be returned. MergeTreeFailOnConflict MergeTreeFlag = C.GIT_MERGE_FAIL_ON_CONFLICT // MergeTreeSkipREUC specifies not to write the REUC extension on the // generated index. MergeTreeSkipREUC MergeTreeFlag = C.GIT_MERGE_SKIP_REUC // MergeTreeNoRecursive specifies not to build a recursive merge base (by // merging the multiple merge bases) if the commits being merged have // multiple merge bases. Instead, the first base is used. // This flag provides a similar merge base to `git-merge-resolve`. MergeTreeNoRecursive MergeTreeFlag = C.GIT_MERGE_NO_RECURSIVE ) type MergeOptions struct { Version uint TreeFlags MergeTreeFlag RenameThreshold uint TargetLimit uint RecursionLimit uint FileFavor MergeFileFavor //TODO: Diff similarity metric } func mergeOptionsFromC(opts *C.git_merge_options) MergeOptions { return MergeOptions{ Version: uint(opts.version), TreeFlags: MergeTreeFlag(opts.flags), RenameThreshold: uint(opts.rename_threshold), TargetLimit: uint(opts.target_limit), RecursionLimit: uint(opts.recursion_limit), FileFavor: MergeFileFavor(opts.file_favor), } } func DefaultMergeOptions() (MergeOptions, error) { opts := C.git_merge_options{} runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_merge_options_init(&opts, C.GIT_MERGE_OPTIONS_VERSION) if ecode < 0 { return MergeOptions{}, MakeGitError(ecode) } return mergeOptionsFromC(&opts), nil } func (mo *MergeOptions) toC() *C.git_merge_options { if mo == nil { return nil } return &C.git_merge_options{ version: C.uint(mo.Version), flags: C.uint32_t(mo.TreeFlags), rename_threshold: C.uint(mo.RenameThreshold), target_limit: C.uint(mo.TargetLimit), recursion_limit: C.uint(mo.RecursionLimit), file_favor: C.git_merge_file_favor_t(mo.FileFavor), } } func freeMergeOptions(opts *C.git_merge_options) { } type MergeFileFavor int const ( MergeFileFavorNormal MergeFileFavor = C.GIT_MERGE_FILE_FAVOR_NORMAL MergeFileFavorOurs MergeFileFavor = C.GIT_MERGE_FILE_FAVOR_OURS MergeFileFavorTheirs MergeFileFavor = C.GIT_MERGE_FILE_FAVOR_THEIRS MergeFileFavorUnion MergeFileFavor = C.GIT_MERGE_FILE_FAVOR_UNION ) func (r *Repository) Merge(theirHeads []*AnnotatedCommit, mergeOptions *MergeOptions, checkoutOptions *CheckoutOptions) error { runtime.LockOSThread() defer runtime.UnlockOSThread() var err error cMergeOpts := mergeOptions.toC() defer freeMergeOptions(cMergeOpts) cCheckoutOptions := checkoutOptions.toC(&err) defer freeCheckoutOptions(cCheckoutOptions) 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]) ret := C.git_merge(r.ptr, (**C.git_annotated_commit)(ptr), C.size_t(len(theirHeads)), cMergeOpts, cCheckoutOptions) runtime.KeepAlive(theirHeads) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } type MergeAnalysis int const ( MergeAnalysisNone MergeAnalysis = C.GIT_MERGE_ANALYSIS_NONE MergeAnalysisNormal MergeAnalysis = C.GIT_MERGE_ANALYSIS_NORMAL MergeAnalysisUpToDate MergeAnalysis = C.GIT_MERGE_ANALYSIS_UP_TO_DATE MergeAnalysisFastForward MergeAnalysis = C.GIT_MERGE_ANALYSIS_FASTFORWARD MergeAnalysisUnborn MergeAnalysis = C.GIT_MERGE_ANALYSIS_UNBORN ) type MergePreference int const ( MergePreferenceNone MergePreference = C.GIT_MERGE_PREFERENCE_NONE MergePreferenceNoFastForward MergePreference = C.GIT_MERGE_PREFERENCE_NO_FASTFORWARD MergePreferenceFastForwardOnly MergePreference = C.GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY ) // MergeAnalysis returns the possible actions which could be taken by // a 'git-merge' command. There may be multiple answers, so the first // return value is a bitmask of MergeAnalysis values. func (r *Repository) MergeAnalysis(theirHeads []*AnnotatedCommit) (MergeAnalysis, MergePreference, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() gmerge_head_array := make([]*C.git_annotated_commit, len(theirHeads)) for i := 0; i < len(theirHeads); i++ { gmerge_head_array[i] = theirHeads[i].ptr } ptr := unsafe.Pointer(&gmerge_head_array[0]) var analysis C.git_merge_analysis_t var preference C.git_merge_preference_t err := C.git_merge_analysis(&analysis, &preference, r.ptr, (**C.git_annotated_commit)(ptr), C.size_t(len(theirHeads))) runtime.KeepAlive(theirHeads) if err < 0 { return MergeAnalysisNone, MergePreferenceNone, MakeGitError(err) } return MergeAnalysis(analysis), MergePreference(preference), nil } func (r *Repository) MergeCommits(ours *Commit, theirs *Commit, options *MergeOptions) (*Index, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() copts := options.toC() defer freeMergeOptions(copts) var ptr *C.git_index ret := C.git_merge_commits(&ptr, r.ptr, ours.cast_ptr, theirs.cast_ptr, copts) runtime.KeepAlive(ours) runtime.KeepAlive(theirs) if ret < 0 { return nil, MakeGitError(ret) } return newIndexFromC(ptr, r), nil } func (r *Repository) MergeTrees(ancestor *Tree, ours *Tree, theirs *Tree, options *MergeOptions) (*Index, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() copts := options.toC() defer freeMergeOptions(copts) var ancestor_ptr *C.git_tree if ancestor != nil { ancestor_ptr = ancestor.cast_ptr } var ptr *C.git_index ret := C.git_merge_trees(&ptr, r.ptr, ancestor_ptr, ours.cast_ptr, theirs.cast_ptr, copts) runtime.KeepAlive(ancestor) runtime.KeepAlive(ours) runtime.KeepAlive(theirs) if ret < 0 { return nil, MakeGitError(ret) } return newIndexFromC(ptr, r), nil } func (r *Repository) MergeBase(one *Oid, two *Oid) (*Oid, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() var oid C.git_oid ret := C.git_merge_base(&oid, r.ptr, one.toC(), two.toC()) runtime.KeepAlive(one) runtime.KeepAlive(two) runtime.KeepAlive(r) if ret < 0 { return nil, MakeGitError(ret) } return newOidFromC(&oid), nil } // MergeBases retrieves the list of merge bases between two commits. // // If none are found, an empty slice is returned and the error is set // approprately func (r *Repository) MergeBases(one, two *Oid) ([]*Oid, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() var coids C.git_oidarray ret := C.git_merge_bases(&coids, r.ptr, one.toC(), two.toC()) runtime.KeepAlive(one) runtime.KeepAlive(two) if ret < 0 { return make([]*Oid, 0), MakeGitError(ret) } oids := make([]*Oid, coids.count) hdr := reflect.SliceHeader{ Data: uintptr(unsafe.Pointer(coids.ids)), Len: int(coids.count), Cap: int(coids.count), } goSlice := *(*[]C.git_oid)(unsafe.Pointer(&hdr)) for i, cid := range goSlice { oids[i] = newOidFromC(&cid) } return oids, nil } //TODO: int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[]); //TODO: GIT_EXTERN(int) git_merge_base_octopus(git_oid *out,git_repository *repo,size_t length,const git_oid input_array[]); type MergeFileResult struct { Automergeable bool Path string Mode uint Contents []byte ptr *C.git_merge_file_result } func newMergeFileResultFromC(c *C.git_merge_file_result) *MergeFileResult { var path string if c.path != nil { path = C.GoString(c.path) } originalBytes := C.GoBytes(unsafe.Pointer(c.ptr), C.int(c.len)) gobytes := make([]byte, len(originalBytes)) copy(gobytes, originalBytes) r := &MergeFileResult{ Automergeable: c.automergeable != 0, Path: path, Mode: uint(c.mode), Contents: gobytes, ptr: c, } runtime.SetFinalizer(r, (*MergeFileResult).Free) return r } func (r *MergeFileResult) Free() { runtime.SetFinalizer(r, nil) C.git_merge_file_result_free(r.ptr) } type MergeFileInput struct { Path string Mode uint Contents []byte } type MergeFileFlags int const ( MergeFileDefault MergeFileFlags = C.GIT_MERGE_FILE_DEFAULT // Create standard conflicted merge files MergeFileStyleMerge MergeFileFlags = C.GIT_MERGE_FILE_STYLE_MERGE // Create diff3-style files MergeFileStyleDiff MergeFileFlags = C.GIT_MERGE_FILE_STYLE_DIFF3 // Condense non-alphanumeric regions for simplified diff file MergeFileStyleSimplifyAlnum MergeFileFlags = C.GIT_MERGE_FILE_SIMPLIFY_ALNUM // Ignore all whitespace MergeFileIgnoreWhitespace MergeFileFlags = C.GIT_MERGE_FILE_IGNORE_WHITESPACE // Ignore changes in amount of whitespace MergeFileIgnoreWhitespaceChange MergeFileFlags = C.GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE // Ignore whitespace at end of line MergeFileIgnoreWhitespaceEOL MergeFileFlags = C.GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL // Use the "patience diff" algorithm MergeFileDiffPatience MergeFileFlags = C.GIT_MERGE_FILE_DIFF_PATIENCE // Take extra time to find minimal diff MergeFileDiffMinimal MergeFileFlags = C.GIT_MERGE_FILE_DIFF_MINIMAL ) type MergeFileOptions struct { AncestorLabel string OurLabel string TheirLabel string Favor MergeFileFavor Flags MergeFileFlags MarkerSize uint16 } func mergeFileOptionsFromC(c C.git_merge_file_options) MergeFileOptions { return MergeFileOptions{ AncestorLabel: C.GoString(c.ancestor_label), OurLabel: C.GoString(c.our_label), TheirLabel: C.GoString(c.their_label), Favor: MergeFileFavor(c.favor), Flags: MergeFileFlags(c.flags), MarkerSize: uint16(c.marker_size), } } func populateCMergeFileOptions(c *C.git_merge_file_options, options MergeFileOptions) { c.ancestor_label = C.CString(options.AncestorLabel) c.our_label = C.CString(options.OurLabel) c.their_label = C.CString(options.TheirLabel) c.favor = C.git_merge_file_favor_t(options.Favor) c.flags = C.uint32_t(options.Flags) c.marker_size = C.ushort(options.MarkerSize) } func freeCMergeFileOptions(c *C.git_merge_file_options) { if c == nil { return } C.free(unsafe.Pointer(c.ancestor_label)) C.free(unsafe.Pointer(c.our_label)) C.free(unsafe.Pointer(c.their_label)) } func MergeFile(ancestor MergeFileInput, ours MergeFileInput, theirs MergeFileInput, options *MergeFileOptions) (*MergeFileResult, error) { ancestorPath := C.CString(ancestor.Path) defer C.free(unsafe.Pointer(ancestorPath)) var ancestorContents *byte if len(ancestor.Contents) > 0 { ancestorContents = &ancestor.Contents[0] } oursPath := C.CString(ours.Path) defer C.free(unsafe.Pointer(oursPath)) var oursContents *byte if len(ours.Contents) > 0 { oursContents = &ours.Contents[0] } theirsPath := C.CString(theirs.Path) defer C.free(unsafe.Pointer(theirsPath)) var theirsContents *byte if len(theirs.Contents) > 0 { theirsContents = &theirs.Contents[0] } var copts *C.git_merge_file_options if options != nil { copts = &C.git_merge_file_options{} ecode := C.git_merge_file_options_init(copts, C.GIT_MERGE_FILE_OPTIONS_VERSION) if ecode < 0 { return nil, MakeGitError(ecode) } populateCMergeFileOptions(copts, *options) defer freeCMergeFileOptions(copts) } runtime.LockOSThread() defer runtime.UnlockOSThread() var result C.git_merge_file_result ecode := C._go_git_merge_file(&result, (*C.char)(unsafe.Pointer(ancestorContents)), C.size_t(len(ancestor.Contents)), ancestorPath, C.uint(ancestor.Mode), (*C.char)(unsafe.Pointer(oursContents)), C.size_t(len(ours.Contents)), oursPath, C.uint(ours.Mode), (*C.char)(unsafe.Pointer(theirsContents)), C.size_t(len(theirs.Contents)), theirsPath, C.uint(theirs.Mode), copts) runtime.KeepAlive(ancestor) runtime.KeepAlive(ours) runtime.KeepAlive(theirs) if ecode < 0 { return nil, MakeGitError(ecode) } return newMergeFileResultFromC(&result), nil } // TODO: GIT_EXTERN(int) git_merge_file_from_index(git_merge_file_result *out,git_repository *repo,const git_index_entry *ancestor, const git_index_entry *ours, const git_index_entry *theirs, const git_merge_file_options *opts); git2go-31.4.3/merge_test.go000066400000000000000000000102001376323343000154570ustar00rootroot00000000000000package git import ( "testing" "time" ) func TestAnnotatedCommitFromRevspec(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) seedTestRepo(t, repo) mergeHead, err := repo.AnnotatedCommitFromRevspec("refs/heads/master") checkFatal(t, err) expectedId := "473bf778b67b6d53e2ab289e0f1a2e8addef2fc2" if mergeHead.Id().String() != expectedId { t.Errorf("mergeHead.Id() = %v, want %v", mergeHead.Id(), expectedId) } } func TestMergeWithSelf(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) seedTestRepo(t, repo) master, err := repo.References.Lookup("refs/heads/master") checkFatal(t, err) mergeHead, err := repo.AnnotatedCommitFromRef(master) checkFatal(t, err) expectedId := "473bf778b67b6d53e2ab289e0f1a2e8addef2fc2" if mergeHead.Id().String() != expectedId { t.Errorf("mergeHead.Id() = %v, want %v", mergeHead.Id(), expectedId) } mergeHeads := make([]*AnnotatedCommit, 1) mergeHeads[0] = mergeHead err = repo.Merge(mergeHeads, nil, nil) checkFatal(t, err) } func TestMergeAnalysisWithSelf(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) seedTestRepo(t, repo) master, err := repo.References.Lookup("refs/heads/master") checkFatal(t, err) mergeHead, err := repo.AnnotatedCommitFromRef(master) checkFatal(t, err) mergeHeads := make([]*AnnotatedCommit, 1) mergeHeads[0] = mergeHead a, _, err := repo.MergeAnalysis(mergeHeads) checkFatal(t, err) if a != MergeAnalysisUpToDate { t.Fatalf("Expected up to date merge, not %v", a) } } func TestMergeSameFile(t *testing.T) { t.Parallel() file := MergeFileInput{ Path: "test", Mode: 33188, Contents: []byte("hello world"), } result, err := MergeFile(file, file, file, nil) checkFatal(t, err) if !result.Automergeable { t.Fatal("expected automergeable") } if result.Path != file.Path { t.Fatal("path was incorrect") } if result.Mode != file.Mode { t.Fatal("mode was incorrect") } compareBytes(t, file.Contents, result.Contents) } func TestMergeTreesWithoutAncestor(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) _, 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) index, err := repo.MergeTrees(nil, originalTree, newTree, nil) if !index.HasConflicts() { t.Fatal("expected conflicts in the index") } _, err = index.Conflict("README") checkFatal(t, err) } func appendCommit(t *testing.T, repo *Repository) (*Oid, *Oid) { loc, err := time.LoadLocation("Europe/Berlin") checkFatal(t, err) sig := &Signature{ Name: "Rand Om Hacker", Email: "random@hacker.com", When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc), } idx, err := repo.Index() checkFatal(t, err) err = idx.AddByPath("README") checkFatal(t, err) treeId, err := idx.WriteTree() checkFatal(t, err) message := "This is another commit\n" tree, err := repo.LookupTree(treeId) checkFatal(t, err) ref, err := repo.References.Lookup("HEAD") checkFatal(t, err) parent, err := ref.Peel(ObjectCommit) checkFatal(t, err) parentCommit, err := parent.AsCommit() checkFatal(t, err) commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree, parentCommit) checkFatal(t, err) return commitId, treeId } func TestMergeBase(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) commitAId, _ := seedTestRepo(t, repo) commitBId, _ := appendCommit(t, repo) mergeBase, err := repo.MergeBase(commitAId, commitBId) checkFatal(t, err) if mergeBase.Cmp(commitAId) != 0 { t.Fatalf("unexpected merge base") } mergeBases, err := repo.MergeBases(commitAId, commitBId) checkFatal(t, err) if len(mergeBases) != 1 { t.Fatalf("expected merge bases len to be 1, got %v", len(mergeBases)) } if mergeBases[0].Cmp(commitAId) != 0 { t.Fatalf("unexpected merge base") } } func compareBytes(t *testing.T, expected, actual []byte) { for i, v := range expected { if actual[i] != v { t.Fatalf("Bad bytes") } } } git2go-31.4.3/message.go000066400000000000000000000022531376323343000147560ustar00rootroot00000000000000package git /* #include */ import "C" import ( "runtime" "unsafe" ) // Trailer represents a single git message trailer. type Trailer struct { Key string Value string } // MessageTrailers parses trailers out of a message, returning a slice of // Trailer structs. Trailers are key/value pairs in the last paragraph of a // message, not including any patches or conflicts that may be present. func MessageTrailers(message string) ([]Trailer, error) { var trailersC C.git_message_trailer_array messageC := C.CString(message) defer C.free(unsafe.Pointer(messageC)) runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_message_trailers(&trailersC, messageC) if ecode < 0 { return nil, MakeGitError(ecode) } defer C.git_message_trailer_array_free(&trailersC) trailers := make([]Trailer, trailersC.count) var trailer *C.git_message_trailer for i, p := 0, uintptr(unsafe.Pointer(trailersC.trailers)); i < int(trailersC.count); i, p = i+1, p+unsafe.Sizeof(C.git_message_trailer{}) { trailer = (*C.git_message_trailer)(unsafe.Pointer(p)) trailers[i] = Trailer{Key: C.GoString(trailer.key), Value: C.GoString(trailer.value)} } return trailers, nil } git2go-31.4.3/message_test.go000066400000000000000000000017421376323343000160170ustar00rootroot00000000000000package git import ( "fmt" "reflect" "testing" ) func TestTrailers(t *testing.T) { t.Parallel() tests := []struct { input string expected []Trailer }{ { "commit with zero trailers\n", []Trailer{}, }, { "commit with one trailer\n\nCo-authored-by: Alice \n", []Trailer{ Trailer{Key: "Co-authored-by", Value: "Alice "}, }, }, { "commit with two trailers\n\nCo-authored-by: Alice \nSigned-off-by: Bob \n", []Trailer{ Trailer{Key: "Co-authored-by", Value: "Alice "}, Trailer{Key: "Signed-off-by", Value: "Bob "}}, }, } for _, test := range tests { fmt.Printf("%s", test.input) actual, err := MessageTrailers(test.input) if err != nil { t.Errorf("Trailers returned an unexpected error: %v", err) } if !reflect.DeepEqual(test.expected, actual) { t.Errorf("expecting %#v\ngot %#v", test.expected, actual) } } } git2go-31.4.3/note.go000066400000000000000000000117211376323343000142770ustar00rootroot00000000000000package git /* #include */ import "C" import ( "runtime" "unsafe" ) // This object represents the possible operations which can be // performed on the collection of notes for a repository. type NoteCollection struct { repo *Repository } // Create adds a note for an object func (c *NoteCollection) Create( 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, err := author.toC() if err != nil { return nil, err } defer C.git_signature_free(authorSig) committerSig, err := committer.toC() if err != nil { return nil, err } 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(), c.repo.ptr, cref, authorSig, committerSig, id.toC(), cnote, cbool(force)) runtime.KeepAlive(c) runtime.KeepAlive(id) if ret < 0 { return nil, MakeGitError(ret) } return oid, nil } // Read reads the note for an object func (c *NoteCollection) Read(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)) } runtime.LockOSThread() defer runtime.UnlockOSThread() var ptr *C.git_note ret := C.git_note_read(&ptr, c.repo.ptr, cref, id.toC()) runtime.KeepAlive(c) runtime.KeepAlive(id) if ret < 0 { return nil, MakeGitError(ret) } return newNoteFromC(ptr, c.repo), nil } // Remove removes the note for an object func (c *NoteCollection) Remove(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, err := author.toC() if err != nil { return err } defer C.git_signature_free(authorSig) committerSig, err := committer.toC() if err != nil { return err } defer C.git_signature_free(committerSig) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_note_remove(c.repo.ptr, cref, authorSig, committerSig, id.toC()) runtime.KeepAlive(c) runtime.KeepAlive(id) if ret < 0 { return MakeGitError(ret) } return nil } // DefaultRef returns the default notes reference for a repository func (c *NoteCollection) DefaultRef() (string, error) { buf := C.git_buf{} runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_note_default_ref(&buf, c.repo.ptr) runtime.KeepAlive(c) if ecode < 0 { return "", MakeGitError(ecode) } ret := C.GoString(buf.ptr) C.git_buf_dispose(&buf) return ret, nil } // Note type Note struct { ptr *C.git_note r *Repository } func newNoteFromC(ptr *C.git_note, r *Repository) *Note { note := &Note{ptr: ptr, r: r} runtime.SetFinalizer(note, (*Note).Free) return 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) runtime.KeepAlive(n) return newOidFromC(ptr) } // Committer returns the signature of the note committer func (n *Note) Committer() *Signature { ptr := C.git_note_committer(n.ptr) runtime.KeepAlive(n) return newSignatureFromC(ptr) } // Message returns the note message func (n *Note) Message() string { ret := C.GoString(C.git_note_message(n.ptr)) runtime.KeepAlive(n) return ret } // NoteIterator type NoteIterator struct { ptr *C.git_note_iterator r *Repository } // 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() ret := C.git_note_iterator_new(&ptr, repo.ptr, cref) runtime.KeepAlive(repo) if ret < 0 { return nil, MakeGitError(ret) } iter := &NoteIterator{ptr: ptr, r: repo} 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() ret := C.git_note_next(noteId.toC(), annotatedId.toC(), it.ptr) runtime.KeepAlive(noteId) runtime.KeepAlive(annotatedId) runtime.KeepAlive(it) if ret < 0 { err = MakeGitError(ret) } return } git2go-31.4.3/note_test.go000066400000000000000000000051251376323343000153370ustar00rootroot00000000000000package git import ( "fmt" "reflect" "testing" "time" ) func TestCreateNote(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) 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) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) 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, ErrorCodeIterOver) { checkFatal(t, err) } break } note, err := repo.Notes.Read("", 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) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) commitId, _ := seedTestRepo(t, repo) commit, err := repo.LookupCommit(commitId) checkFatal(t, err) note, _ := createTestNote(t, repo, commit) _, err = repo.Notes.Read("", commit.Id()) checkFatal(t, err) err = repo.Notes.Remove("", note.Author(), note.Committer(), commitId) checkFatal(t, err) _, err = repo.Notes.Read("", commit.Id()) if err == nil { t.Fatal("note remove failed") } } func TestDefaultNoteRef(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) ref, err := repo.Notes.DefaultRef() 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.Notes.Create("", sig, sig, commit.Id(), "I am a note\n", false) checkFatal(t, err) note, err := repo.Notes.Read("", commit.Id()) checkFatal(t, err) return note, noteId } git2go-31.4.3/object.go000066400000000000000000000114341376323343000146010ustar00rootroot00000000000000package git /* #include */ import "C" import ( "errors" "fmt" "runtime" ) type ObjectType int const ( ObjectAny ObjectType = C.GIT_OBJECT_ANY ObjectInvalid ObjectType = C.GIT_OBJECT_INVALID ObjectCommit ObjectType = C.GIT_OBJECT_COMMIT ObjectTree ObjectType = C.GIT_OBJECT_TREE ObjectBlob ObjectType = C.GIT_OBJECT_BLOB ObjectTag ObjectType = C.GIT_OBJECT_TAG ) type Object struct { ptr *C.git_object repo *Repository } // Objecter lets us accept any kind of Git object in functions. type Objecter interface { AsObject() *Object } func (t ObjectType) String() string { switch t { case ObjectAny: return "Any" case ObjectInvalid: return "Invalid" case ObjectCommit: return "Commit" case ObjectTree: return "Tree" case ObjectBlob: return "Blob" case ObjectTag: return "Tag" } // Never reached return "" } func (o *Object) Id() *Oid { ret := newOidFromC(C.git_object_id(o.ptr)) runtime.KeepAlive(o) return ret } func (o *Object) ShortId() (string, error) { resultBuf := C.git_buf{} runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_object_short_id(&resultBuf, o.ptr) runtime.KeepAlive(o) if ecode < 0 { return "", MakeGitError(ecode) } defer C.git_buf_dispose(&resultBuf) return C.GoString(resultBuf.ptr), nil } func (o *Object) Type() ObjectType { ret := ObjectType(C.git_object_type(o.ptr)) runtime.KeepAlive(o) return ret } // Owner returns a weak reference to the repository which owns this // object. This won't keep the underlying repository alive. func (o *Object) Owner() *Repository { ret := &Repository{ ptr: C.git_object_owner(o.ptr), } runtime.KeepAlive(o) return ret } func dupObject(obj *Object, kind ObjectType) (*C.git_object, error) { if obj.Type() != kind { return nil, errors.New(fmt.Sprintf("object is not a %v", kind)) } var cobj *C.git_object runtime.LockOSThread() defer runtime.UnlockOSThread() err := C.git_object_dup(&cobj, obj.ptr) runtime.KeepAlive(obj) if err < 0 { return nil, MakeGitError(err) } return cobj, nil } func allocTree(ptr *C.git_tree, repo *Repository) *Tree { tree := &Tree{ Object: Object{ ptr: (*C.git_object)(ptr), repo: repo, }, cast_ptr: ptr, } runtime.SetFinalizer(tree, (*Tree).Free) return tree } func (o *Object) AsTree() (*Tree, error) { cobj, err := dupObject(o, ObjectTree) if err != nil { return nil, err } return allocTree((*C.git_tree)(cobj), o.repo), nil } func allocCommit(ptr *C.git_commit, repo *Repository) *Commit { commit := &Commit{ Object: Object{ ptr: (*C.git_object)(ptr), repo: repo, }, cast_ptr: ptr, } runtime.SetFinalizer(commit, (*Commit).Free) return commit } func (o *Object) AsCommit() (*Commit, error) { cobj, err := dupObject(o, ObjectCommit) if err != nil { return nil, err } return allocCommit((*C.git_commit)(cobj), o.repo), nil } func allocBlob(ptr *C.git_blob, repo *Repository) *Blob { blob := &Blob{ Object: Object{ ptr: (*C.git_object)(ptr), repo: repo, }, cast_ptr: ptr, } runtime.SetFinalizer(blob, (*Blob).Free) return blob } func (o *Object) AsBlob() (*Blob, error) { cobj, err := dupObject(o, ObjectBlob) if err != nil { return nil, err } return allocBlob((*C.git_blob)(cobj), o.repo), nil } func allocTag(ptr *C.git_tag, repo *Repository) *Tag { tag := &Tag{ Object: Object{ ptr: (*C.git_object)(ptr), repo: repo, }, cast_ptr: ptr, } runtime.SetFinalizer(tag, (*Tag).Free) return tag } func (o *Object) AsTag() (*Tag, error) { cobj, err := dupObject(o, ObjectTag) if err != nil { return nil, err } return allocTag((*C.git_tag)(cobj), o.repo), nil } func (o *Object) Free() { runtime.SetFinalizer(o, nil) C.git_object_free(o.ptr) } // Peel recursively peels an object until an object of the specified type is met. // // If the query cannot be satisfied due to the object model, ErrorCodeInvalidSpec // will be returned (e.g. trying to peel a blob to a tree). // // If you pass ObjectAny as the target type, then the object will be peeled // until the type changes. A tag will be peeled until the referenced object // is no longer a tag, and a commit will be peeled to a tree. Any other object // type will return ErrorCodeInvalidSpec. // // If peeling a tag we discover an object which cannot be peeled to the target // type due to the object model, an error will be returned. func (o *Object) Peel(t ObjectType) (*Object, error) { var cobj *C.git_object runtime.LockOSThread() defer runtime.UnlockOSThread() err := C.git_object_peel(&cobj, o.ptr, C.git_object_t(t)) runtime.KeepAlive(o) if err < 0 { return nil, MakeGitError(err) } return allocObject(cobj, o.repo), nil } func allocObject(cobj *C.git_object, repo *Repository) *Object { obj := &Object{ ptr: cobj, repo: repo, } runtime.SetFinalizer(obj, (*Object).Free) return obj } git2go-31.4.3/object_test.go000066400000000000000000000104721376323343000156410ustar00rootroot00000000000000package git import ( "strings" "testing" ) func TestObjectPoymorphism(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) commitId, treeId := seedTestRepo(t, repo) var obj *Object commit, err := repo.LookupCommit(commitId) checkFatal(t, err) obj = &commit.Object if obj.Type() != ObjectCommit { t.Fatalf("Wrong object type, expected commit, have %v", obj.Type()) } commitTree, err := commit.Tree() checkFatal(t, err) commitTree.EntryCount() tree, err := repo.LookupTree(treeId) checkFatal(t, err) obj = &tree.Object if obj.Type() != ObjectTree { t.Fatalf("Wrong object type, expected tree, have %v", obj.Type()) } tree2, err := obj.AsTree() if err != nil { t.Fatalf("Converting back to *Tree is not ok") } entry := tree2.EntryByName("README") if entry == nil { t.Fatalf("Tree did not have expected \"README\" entry") } if entry.Filemode != FilemodeBlob { t.Fatal("Wrong filemode for \"README\"") } _, err = obj.AsCommit() if err == nil { t.Fatalf("*Tree is somehow the same as *Commit") } obj, err = repo.Lookup(tree.Id()) checkFatal(t, err) _, err = obj.AsTree() if err != nil { t.Fatalf("Lookup creates the wrong type") } if obj.Type() != ObjectTree { t.Fatalf("Type() doesn't agree with dynamic type") } obj, err = repo.RevparseSingle("HEAD") checkFatal(t, err) if obj.Type() != ObjectCommit || obj.Id().String() != commit.Id().String() { t.Fatalf("Failed to parse the right revision") } obj, err = repo.RevparseSingle("HEAD^{tree}") checkFatal(t, err) if obj.Type() != ObjectTree || obj.Id().String() != tree.Id().String() { t.Fatalf("Failed to parse the right revision") } } func checkOwner(t *testing.T, repo *Repository, obj Object) { owner := obj.Owner() if owner == nil { t.Fatal("bad owner") } if owner.ptr != repo.ptr { t.Fatalf("bad owner, got %v expected %v\n", owner.ptr, repo.ptr) } } func TestObjectOwner(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) commitId, treeId := seedTestRepo(t, repo) commit, err := repo.LookupCommit(commitId) checkFatal(t, err) tree, err := repo.LookupTree(treeId) checkFatal(t, err) checkOwner(t, repo, commit.Object) checkOwner(t, repo, tree.Object) } func checkShortId(t *testing.T, Id, shortId string) { if len(shortId) < 7 || len(shortId) >= len(Id) { t.Fatalf("bad shortId length %d", len(shortId)) } if !strings.HasPrefix(Id, shortId) { t.Fatalf("bad shortId, should be prefix of %s, but is %s\n", Id, shortId) } } func TestObjectShortId(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) commitId, _ := seedTestRepo(t, repo) commit, err := repo.LookupCommit(commitId) checkFatal(t, err) shortId, err := commit.ShortId() checkFatal(t, err) checkShortId(t, commitId.String(), shortId) } func TestObjectPeel(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) commitID, treeID := seedTestRepo(t, repo) var obj *Object commit, err := repo.LookupCommit(commitID) checkFatal(t, err) obj, err = commit.Peel(ObjectAny) checkFatal(t, err) if obj.Type() != ObjectTree { t.Fatalf("Wrong object type when peeling a commit, expected tree, have %v", obj.Type()) } obj, err = commit.Peel(ObjectTag) if !IsErrorCode(err, ErrorCodeInvalidSpec) { t.Fatalf("Wrong error when peeling a commit to a tag, expected ErrorCodeInvalidSpec, have %v", err) } tree, err := repo.LookupTree(treeID) checkFatal(t, err) obj, err = tree.Peel(ObjectAny) if !IsErrorCode(err, ErrorCodeInvalidSpec) { t.Fatalf("Wrong error when peeling a tree, expected ErrorCodeInvalidSpec, have %v", err) } entry := tree.EntryByName("README") blob, err := repo.LookupBlob(entry.Id) checkFatal(t, err) obj, err = blob.Peel(ObjectAny) if !IsErrorCode(err, ErrorCodeInvalidSpec) { t.Fatalf("Wrong error when peeling a blob, expected ErrorCodeInvalidSpec, have %v", err) } tagID := createTestTag(t, repo, commit) tag, err := repo.LookupTag(tagID) checkFatal(t, err) obj, err = tag.Peel(ObjectAny) checkFatal(t, err) if obj.Type() != ObjectCommit { t.Fatalf("Wrong object type when peeling a tag, expected commit, have %v", obj.Type()) } // TODO: Should test a tag that annotates a different object than a commit // but it's impossible at the moment to tag such an object. } git2go-31.4.3/odb.go000066400000000000000000000274221376323343000141030ustar00rootroot00000000000000package git /* #include extern int git_odb_backend_one_pack(git_odb_backend **out, const char *index_file); extern int git_odb_backend_loose(git_odb_backend **out, const char *objects_dir, int compression_level, int do_fsync, unsigned int dir_mode, unsigned int file_mode); extern int _go_git_odb_foreach(git_odb *db, void *payload); extern void _go_git_odb_backend_free(git_odb_backend *backend); extern int _go_git_odb_write_pack(git_odb_writepack **out, git_odb *db, void *progress_payload); extern int _go_git_odb_writepack_append(git_odb_writepack *writepack, const void *, size_t, git_transfer_progress *); extern int _go_git_odb_writepack_commit(git_odb_writepack *writepack, git_transfer_progress *); extern void _go_git_odb_writepack_free(git_odb_writepack *writepack); */ import "C" import ( "io" "os" "reflect" "runtime" "unsafe" ) type Odb struct { ptr *C.git_odb } type OdbBackend struct { ptr *C.git_odb_backend } 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) } runtime.SetFinalizer(odb, (*Odb).Free) return odb, nil } func NewOdbBackendFromC(ptr unsafe.Pointer) (backend *OdbBackend) { backend = &OdbBackend{(*C.git_odb_backend)(ptr)} return backend } func (v *Odb) AddAlternate(backend *OdbBackend, priority int) (err error) { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_odb_add_alternate(v.ptr, backend.ptr, C.int(priority)) runtime.KeepAlive(v) if ret < 0 { backend.Free() return MakeGitError(ret) } return nil } 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)) runtime.KeepAlive(v) if ret < 0 { backend.Free() return MakeGitError(ret) } return nil } func NewOdbBackendOnePack(packfileIndexPath string) (backend *OdbBackend, err error) { runtime.LockOSThread() defer runtime.UnlockOSThread() cstr := C.CString(packfileIndexPath) defer C.free(unsafe.Pointer(cstr)) var odbOnePack *C.git_odb_backend = nil ret := C.git_odb_backend_one_pack(&odbOnePack, cstr) if ret < 0 { return nil, MakeGitError(ret) } return NewOdbBackendFromC(unsafe.Pointer(odbOnePack)), nil } // NewOdbBackendLoose creates a backend for loose objects. func NewOdbBackendLoose(objectsDir string, compressionLevel int, doFsync bool, dirMode os.FileMode, fileMode os.FileMode) (backend *OdbBackend, err error) { runtime.LockOSThread() defer runtime.UnlockOSThread() var odbLoose *C.git_odb_backend = nil var doFsyncInt C.int if doFsync { doFsyncInt = C.int(1) } cstr := C.CString(objectsDir) defer C.free(unsafe.Pointer(cstr)) ret := C.git_odb_backend_loose(&odbLoose, cstr, C.int(compressionLevel), doFsyncInt, C.uint(dirMode), C.uint(fileMode)) if ret < 0 { return nil, MakeGitError(ret) } return NewOdbBackendFromC(unsafe.Pointer(odbLoose)), nil } func (v *Odb) ReadHeader(oid *Oid) (uint64, ObjectType, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() var sz C.size_t var cotype C.git_object_t ret := C.git_odb_read_header(&sz, &cotype, v.ptr, oid.toC()) runtime.KeepAlive(v) if ret < 0 { return 0, ObjectInvalid, MakeGitError(ret) } return uint64(sz), ObjectType(cotype), nil } func (v *Odb) Exists(oid *Oid) bool { ret := C.git_odb_exists(v.ptr, oid.toC()) runtime.KeepAlive(v) runtime.KeepAlive(oid) return ret != 0 } func (v *Odb) Write(data []byte, otype ObjectType) (oid *Oid, err error) { oid = new(Oid) runtime.LockOSThread() defer runtime.UnlockOSThread() var size C.size_t if len(data) > 0 { size = C.size_t(len(data)) } else { data = []byte{0} size = C.size_t(0) } ret := C.git_odb_write(oid.toC(), v.ptr, unsafe.Pointer(&data[0]), size, C.git_object_t(otype)) runtime.KeepAlive(v) if ret < 0 { return nil, MakeGitError(ret) } return oid, nil } func (v *Odb) Read(oid *Oid) (obj *OdbObject, err error) { obj = new(OdbObject) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_odb_read(&obj.ptr, v.ptr, oid.toC()) runtime.KeepAlive(v) runtime.KeepAlive(oid) if ret < 0 { return nil, MakeGitError(ret) } runtime.SetFinalizer(obj, (*OdbObject).Free) return obj, nil } type OdbForEachCallback func(id *Oid) error type odbForEachCallbackData struct { callback OdbForEachCallback errorTarget *error } //export odbForEachCallback func odbForEachCallback(id *C.git_oid, handle unsafe.Pointer) C.int { data, ok := pointerHandles.Get(handle).(*odbForEachCallbackData) if !ok { panic("could not retrieve handle") } err := data.callback(newOidFromC(id)) if err != nil { *data.errorTarget = err return C.int(ErrorCodeUser) } return C.int(ErrorCodeOK) } func (v *Odb) ForEach(callback OdbForEachCallback) error { var err error data := odbForEachCallbackData{ callback: callback, errorTarget: &err, } runtime.LockOSThread() defer runtime.UnlockOSThread() handle := pointerHandles.Track(&data) defer pointerHandles.Untrack(handle) ret := C._go_git_odb_foreach(v.ptr, handle) runtime.KeepAlive(v) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } // Hash determines the object-ID (sha1) of a data buffer. func (v *Odb) Hash(data []byte, otype ObjectType) (oid *Oid, err error) { oid = new(Oid) runtime.LockOSThread() defer runtime.UnlockOSThread() var size C.size_t if len(data) > 0 { size = C.size_t(len(data)) } else { data = []byte{0} size = C.size_t(0) } ret := C.git_odb_hash(oid.toC(), unsafe.Pointer(&data[0]), size, C.git_object_t(otype)) runtime.KeepAlive(data) if ret < 0 { return nil, MakeGitError(ret) } return oid, nil } // NewReadStream opens a read stream from the ODB. Reading from it will give you the // contents of the object. func (v *Odb) NewReadStream(id *Oid) (*OdbReadStream, error) { stream := new(OdbReadStream) var ctype C.git_object_t var csize C.size_t runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_odb_open_rstream(&stream.ptr, &csize, &ctype, v.ptr, id.toC()) runtime.KeepAlive(v) runtime.KeepAlive(id) if ret < 0 { return nil, MakeGitError(ret) } stream.Size = uint64(csize) stream.Type = ObjectType(ctype) runtime.SetFinalizer(stream, (*OdbReadStream).Free) return stream, nil } // NewWriteStream opens a write stream to the ODB, which allows you to // create a new object in the database. The size and type must be // known in advance func (v *Odb) NewWriteStream(size int64, otype ObjectType) (*OdbWriteStream, error) { stream := new(OdbWriteStream) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_odb_open_wstream(&stream.ptr, v.ptr, C.git_object_size_t(size), C.git_object_t(otype)) runtime.KeepAlive(v) if ret < 0 { return nil, MakeGitError(ret) } runtime.SetFinalizer(stream, (*OdbWriteStream).Free) return stream, nil } // NewWritePack opens a stream for writing a pack file to the ODB. If the ODB // layer understands pack files, then the given packfile will likely be // streamed directly to disk (and a corresponding index created). If the ODB // layer does not understand pack files, the objects will be stored in whatever // format the ODB layer uses. func (v *Odb) NewWritePack(callback TransferProgressCallback) (*OdbWritepack, error) { writepack := new(OdbWritepack) runtime.LockOSThread() defer runtime.UnlockOSThread() writepack.callbacks.TransferProgressCallback = callback writepack.callbacksHandle = pointerHandles.Track(&writepack.callbacks) ret := C._go_git_odb_write_pack(&writepack.ptr, v.ptr, writepack.callbacksHandle) runtime.KeepAlive(v) if ret < 0 { pointerHandles.Untrack(writepack.callbacksHandle) return nil, MakeGitError(ret) } runtime.SetFinalizer(writepack, (*OdbWritepack).Free) return writepack, nil } func (v *OdbBackend) Free() { C._go_git_odb_backend_free(v.ptr) } type OdbObject struct { ptr *C.git_odb_object } func (v *OdbObject) Free() { runtime.SetFinalizer(v, nil) C.git_odb_object_free(v.ptr) } func (object *OdbObject) Id() (oid *Oid) { ret := newOidFromC(C.git_odb_object_id(object.ptr)) runtime.KeepAlive(object) return ret } func (object *OdbObject) Len() (len uint64) { ret := uint64(C.git_odb_object_size(object.ptr)) runtime.KeepAlive(object) return ret } func (object *OdbObject) Type() ObjectType { ret := ObjectType(C.git_odb_object_type(object.ptr)) runtime.KeepAlive(object) return ret } // Data returns a slice pointing to the unmanaged object memory. You must make // sure the object is referenced for at least as long as the slice is used. func (object *OdbObject) Data() (data []byte) { var c_blob unsafe.Pointer = C.git_odb_object_data(object.ptr) var blob []byte len := int(C.git_odb_object_size(object.ptr)) sliceHeader := (*reflect.SliceHeader)((unsafe.Pointer(&blob))) sliceHeader.Cap = len sliceHeader.Len = len sliceHeader.Data = uintptr(c_blob) return blob } type OdbReadStream struct { ptr *C.git_odb_stream Size uint64 Type ObjectType } // Read reads from the stream 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) runtime.KeepAlive(stream) if ret < 0 { return 0, MakeGitError(ret) } if ret == 0 { return 0, io.EOF } header.Len = int(ret) return len(data), nil } // Close is a dummy function in order to implement the Closer and // ReadCloser interfaces func (stream *OdbReadStream) Close() error { return nil } func (stream *OdbReadStream) Free() { runtime.SetFinalizer(stream, nil) C.git_odb_stream_free(stream.ptr) } type OdbWriteStream struct { ptr *C.git_odb_stream Id Oid } // Write writes to the stream func (stream *OdbWriteStream) Write(data []byte) (int, error) { header := (*reflect.SliceHeader)(unsafe.Pointer(&data)) 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) runtime.KeepAlive(stream) if ret < 0 { return 0, MakeGitError(ret) } return len(data), nil } // 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) runtime.KeepAlive(stream) if ret < 0 { return MakeGitError(ret) } return nil } func (stream *OdbWriteStream) Free() { runtime.SetFinalizer(stream, nil) C.git_odb_stream_free(stream.ptr) } // OdbWritepack is a stream to write a packfile to the ODB. type OdbWritepack struct { ptr *C.git_odb_writepack stats C.git_transfer_progress callbacks RemoteCallbacks callbacksHandle unsafe.Pointer } func (writepack *OdbWritepack) Write(data []byte) (int, error) { header := (*reflect.SliceHeader)(unsafe.Pointer(&data)) ptr := unsafe.Pointer(header.Data) size := C.size_t(header.Len) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C._go_git_odb_writepack_append(writepack.ptr, ptr, size, &writepack.stats) runtime.KeepAlive(writepack) if ret < 0 { return 0, MakeGitError(ret) } return len(data), nil } func (writepack *OdbWritepack) Commit() error { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C._go_git_odb_writepack_commit(writepack.ptr, &writepack.stats) runtime.KeepAlive(writepack) if ret < 0 { return MakeGitError(ret) } return nil } func (writepack *OdbWritepack) Free() { pointerHandles.Untrack(writepack.callbacksHandle) runtime.SetFinalizer(writepack, nil) C._go_git_odb_writepack_free(writepack.ptr) } git2go-31.4.3/odb_test.go000066400000000000000000000123321376323343000151340ustar00rootroot00000000000000package git import ( "bytes" "errors" "fmt" "io" "io/ioutil" "os" "path" "testing" ) func TestOdbRead(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) _, _ = seedTestRepo(t, repo) odb, err := repo.Odb() if err != nil { t.Fatalf("Odb: %v", err) } data := []byte("hello") id, err := odb.Write(data, ObjectBlob) if err != nil { t.Fatalf("odb.Write: %v", err) } sz, typ, err := odb.ReadHeader(id) if err != nil { t.Fatalf("ReadHeader: %v", err) } if sz != uint64(len(data)) { t.Errorf("ReadHeader got size %d, want %d", sz, len(data)) } if typ != ObjectBlob { t.Errorf("ReadHeader got object type %s", typ) } obj, err := odb.Read(id) if err != nil { t.Fatalf("Read: %v", err) } if !bytes.Equal(obj.Data(), data) { t.Errorf("Read got wrong data") } if sz := obj.Len(); sz != uint64(len(data)) { t.Errorf("Read got size %d, want %d", sz, len(data)) } if typ := obj.Type(); typ != ObjectBlob { t.Errorf("Read got object type %s", typ) } } func TestOdbStream(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) _, _ = seedTestRepo(t, repo) odb, error := repo.Odb() checkFatal(t, error) str := "hello, world!" writeStream, error := odb.NewWriteStream(int64(len(str)), ObjectBlob) checkFatal(t, error) n, error := io.WriteString(writeStream, str) checkFatal(t, error) if n != len(str) { t.Fatalf("Bad write length %v != %v", n, len(str)) } error = writeStream.Close() checkFatal(t, error) expectedId, error := NewOid("30f51a3fba5274d53522d0f19748456974647b4f") checkFatal(t, error) if writeStream.Id.Cmp(expectedId) != 0 { t.Fatal("Wrong data written") } readStream, error := odb.NewReadStream(&writeStream.Id) checkFatal(t, error) data, error := ioutil.ReadAll(readStream) if str != string(data) { t.Fatalf("Wrong data read %v != %v", str, string(data)) } } func TestOdbHash(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) _, _ = seedTestRepo(t, repo) odb, error := repo.Odb() checkFatal(t, error) str := `tree 115fcae49287c82eb55bb275cbbd4556fbed72b7 parent 66e1c476199ebcd3e304659992233132c5a52c6c author John Doe 1390682018 +0000 committer John Doe 1390682018 +0000 Initial commit.` for _, data := range [][]byte{[]byte(str), doublePointerBytes()} { oid, error := odb.Hash(data, ObjectCommit) checkFatal(t, error) coid, error := odb.Write(data, ObjectCommit) checkFatal(t, error) if oid.Cmp(coid) != 0 { t.Fatal("Hash and write Oids are different") } } } func TestOdbForeach(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) _, _ = seedTestRepo(t, repo) odb, err := repo.Odb() checkFatal(t, err) expect := 3 count := 0 err = odb.ForEach(func(id *Oid) error { count++ return nil }) checkFatal(t, err) if count != expect { t.Fatalf("Expected %v objects, got %v", expect, count) } expect = 1 count = 0 to_return := errors.New("not really an error") err = odb.ForEach(func(id *Oid) error { count++ return to_return }) if err != to_return { t.Fatalf("Odb.ForEach() did not return the expected error, got %v", err) } } func TestOdbWritepack(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) _, _ = seedTestRepo(t, repo) odb, err := repo.Odb() checkFatal(t, err) var finalStats TransferProgress writepack, err := odb.NewWritePack(func(stats TransferProgress) ErrorCode { finalStats = stats return ErrorCodeOK }) checkFatal(t, err) defer writepack.Free() _, err = writepack.Write(outOfOrderPack) checkFatal(t, err) err = writepack.Commit() checkFatal(t, err) if finalStats.TotalObjects != 3 { t.Errorf("mismatched transferred objects, expected 3, got %v", finalStats.TotalObjects) } if finalStats.ReceivedObjects != 3 { t.Errorf("mismatched received objects, expected 3, got %v", finalStats.ReceivedObjects) } if finalStats.IndexedObjects != 3 { t.Errorf("mismatched indexed objects, expected 3, got %v", finalStats.IndexedObjects) } } func TestOdbBackendLoose(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) _, _ = seedTestRepo(t, repo) odb, err := repo.Odb() checkFatal(t, err) looseObjectsDir, err := ioutil.TempDir("", fmt.Sprintf("loose_objects_%s", path.Base(repo.Path()))) checkFatal(t, err) defer os.RemoveAll(looseObjectsDir) looseObjectsBackend, err := NewOdbBackendLoose(looseObjectsDir, -1, false, 0, 0) checkFatal(t, err) if err := odb.AddBackend(looseObjectsBackend, 999); err != nil { looseObjectsBackend.Free() checkFatal(t, err) } str := "hello, world!" writeStream, err := odb.NewWriteStream(int64(len(str)), ObjectBlob) checkFatal(t, err) n, err := io.WriteString(writeStream, str) checkFatal(t, err) if n != len(str) { t.Fatalf("Bad write length %v != %v", n, len(str)) } err = writeStream.Close() checkFatal(t, err) expectedId, err := NewOid("30f51a3fba5274d53522d0f19748456974647b4f") checkFatal(t, err) if !writeStream.Id.Equal(expectedId) { t.Fatalf("writeStream.id = %v; want %v", writeStream.Id, expectedId) } _, err = os.Stat(path.Join(looseObjectsDir, expectedId.String()[:2], expectedId.String()[2:])) checkFatal(t, err) } git2go-31.4.3/packbuilder.go000066400000000000000000000076751376323343000156340ustar00rootroot00000000000000package git /* #include #include #include extern int _go_git_packbuilder_foreach(git_packbuilder *pb, void *payload); */ import "C" import ( "io" "os" "runtime" "unsafe" ) type Packbuilder struct { ptr *C.git_packbuilder r *Repository } func (repo *Repository) NewPackbuilder() (*Packbuilder, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() var ptr *C.git_packbuilder ret := C.git_packbuilder_new(&ptr, repo.ptr) if ret != 0 { return nil, MakeGitError(ret) } return newPackbuilderFromC(ptr, repo), nil } func newPackbuilderFromC(ptr *C.git_packbuilder, r *Repository) *Packbuilder { pb := &Packbuilder{ptr: ptr, r: r} runtime.SetFinalizer(pb, (*Packbuilder).Free) return pb } func (pb *Packbuilder) Free() { runtime.SetFinalizer(pb, nil) C.git_packbuilder_free(pb.ptr) } func (pb *Packbuilder) Insert(id *Oid, name string) error { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_packbuilder_insert(pb.ptr, id.toC(), cname) runtime.KeepAlive(pb) runtime.KeepAlive(id) if ret != 0 { return MakeGitError(ret) } return nil } func (pb *Packbuilder) InsertCommit(id *Oid) error { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_packbuilder_insert_commit(pb.ptr, id.toC()) runtime.KeepAlive(pb) runtime.KeepAlive(id) if ret != 0 { return MakeGitError(ret) } return nil } func (pb *Packbuilder) InsertTree(id *Oid) error { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_packbuilder_insert_tree(pb.ptr, id.toC()) runtime.KeepAlive(pb) runtime.KeepAlive(id) if ret != 0 { return MakeGitError(ret) } return nil } func (pb *Packbuilder) InsertWalk(walk *RevWalk) error { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_packbuilder_insert_walk(pb.ptr, walk.ptr) runtime.KeepAlive(pb) runtime.KeepAlive(walk) if ret != 0 { return MakeGitError(ret) } return nil } func (pb *Packbuilder) ObjectCount() uint32 { ret := uint32(C.git_packbuilder_object_count(pb.ptr)) runtime.KeepAlive(pb) return ret } func (pb *Packbuilder) WriteToFile(name string, mode os.FileMode) error { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_packbuilder_write(pb.ptr, cname, C.uint(mode.Perm()), nil, nil) runtime.KeepAlive(pb) if ret != 0 { return MakeGitError(ret) } return nil } func (pb *Packbuilder) Write(w io.Writer) error { return pb.ForEach(func(slice []byte) error { _, err := w.Write(slice) return err }) } func (pb *Packbuilder) Written() uint32 { ret := uint32(C.git_packbuilder_written(pb.ptr)) runtime.KeepAlive(pb) return ret } type PackbuilderForeachCallback func([]byte) error type packbuilderCallbackData struct { callback PackbuilderForeachCallback errorTarget *error } //export packbuilderForEachCallback func packbuilderForEachCallback(buf unsafe.Pointer, size C.size_t, handle unsafe.Pointer) C.int { payload := pointerHandles.Get(handle) data, ok := payload.(*packbuilderCallbackData) if !ok { panic("could not get packbuilder CB data") } slice := C.GoBytes(buf, C.int(size)) err := data.callback(slice) if err != nil { *data.errorTarget = err return C.int(ErrorCodeUser) } return C.int(ErrorCodeOK) } // ForEach repeatedly calls the callback with new packfile data until // there is no more data or the callback returns an error func (pb *Packbuilder) ForEach(callback PackbuilderForeachCallback) error { var err error data := packbuilderCallbackData{ callback: callback, errorTarget: &err, } handle := pointerHandles.Track(&data) defer pointerHandles.Untrack(handle) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C._go_git_packbuilder_foreach(pb.ptr, handle) runtime.KeepAlive(pb) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } git2go-31.4.3/patch.go000066400000000000000000000034441376323343000144340ustar00rootroot00000000000000package git /* #include */ import "C" import ( "runtime" "unsafe" ) type Patch struct { ptr *C.git_patch } func newPatchFromC(ptr *C.git_patch) *Patch { if ptr == nil { return nil } patch := &Patch{ ptr: ptr, } runtime.SetFinalizer(patch, (*Patch).Free) return patch } func (patch *Patch) Free() error { if patch.ptr == nil { return ErrInvalid } runtime.SetFinalizer(patch, nil) C.git_patch_free(patch.ptr) patch.ptr = nil return nil } func (patch *Patch) String() (string, error) { if patch.ptr == nil { return "", ErrInvalid } runtime.LockOSThread() defer runtime.UnlockOSThread() var buf C.git_buf ecode := C.git_patch_to_buf(&buf, patch.ptr) runtime.KeepAlive(patch) if ecode < 0 { return "", MakeGitError(ecode) } defer C.git_buf_dispose(&buf) 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 := toPointer(newBuf) cOldPath := C.CString(oldPath) defer C.free(unsafe.Pointer(cOldPath)) cNewPath := C.CString(newPath) defer C.free(unsafe.Pointer(cNewPath)) var err error copts := opts.toC(v, &err) defer freeDiffOptions(copts) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_patch_from_buffers(&patchPtr, oldPtr, C.size_t(len(oldBuf)), cOldPath, newPtr, C.size_t(len(newBuf)), cNewPath, copts) runtime.KeepAlive(oldBuf) runtime.KeepAlive(newBuf) if ret == C.int(ErrorCodeUser) && err != nil { return nil, err } if ret < 0 { return nil, MakeGitError(ret) } return newPatchFromC(patchPtr), nil } git2go-31.4.3/patch_test.go000066400000000000000000000015061376323343000154700ustar00rootroot00000000000000package git import ( "strings" "testing" ) func TestPatch(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) _, 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) opts := &DiffOptions{ OldPrefix: "a", NewPrefix: "b", } diff, err := repo.DiffTreeToTree(originalTree, newTree, opts) checkFatal(t, err) patch, err := diff.Patch(0) checkFatal(t, err) patchStr, err := patch.String() checkFatal(t, err) if strings.Index(patchStr, "diff --git a/README b/README\nindex 257cc56..820734a 100644\n--- a/README\n+++ b/README\n@@ -1 +1 @@\n-foo\n+file changed") == -1 { t.Fatalf("patch was bad") } } git2go-31.4.3/push_test.go000066400000000000000000000011051376323343000153430ustar00rootroot00000000000000package git import ( "testing" ) func TestRemotePush(t *testing.T) { t.Parallel() repo := createBareTestRepo(t) defer cleanupTestRepo(t, repo) localRepo := createTestRepo(t) defer cleanupTestRepo(t, localRepo) remote, err := localRepo.Remotes.Create("test_push", repo.Path()) checkFatal(t, err) seedTestRepo(t, localRepo) err = remote.Push([]string{"refs/heads/master"}, nil) checkFatal(t, err) _, err = localRepo.References.Lookup("refs/remotes/test_push/master") checkFatal(t, err) _, err = repo.References.Lookup("refs/heads/master") checkFatal(t, err) } git2go-31.4.3/rebase.go000066400000000000000000000256661376323343000146100ustar00rootroot00000000000000package git /* #include extern void _go_git_populate_rebase_callbacks(git_rebase_options *opts); */ import "C" import ( "errors" "fmt" "runtime" "unsafe" ) // RebaseOperationType is the type of rebase operation type RebaseOperationType uint const ( // RebaseOperationPick The given commit is to be cherry-picked. The client should commit the changes and continue if there are no conflicts. RebaseOperationPick RebaseOperationType = C.GIT_REBASE_OPERATION_PICK // RebaseOperationReword The given commit is to be cherry-picked, but the client should prompt the user to provide an updated commit message. RebaseOperationReword RebaseOperationType = C.GIT_REBASE_OPERATION_REWORD // RebaseOperationEdit The given commit is to be cherry-picked, but the client should stop to allow the user to edit the changes before committing them. RebaseOperationEdit RebaseOperationType = C.GIT_REBASE_OPERATION_EDIT // RebaseOperationSquash The given commit is to be squashed into the previous commit. The commit message will be merged with the previous message. RebaseOperationSquash RebaseOperationType = C.GIT_REBASE_OPERATION_SQUASH // RebaseOperationFixup No commit will be cherry-picked. The client should run the given command and (if successful) continue. RebaseOperationFixup RebaseOperationType = C.GIT_REBASE_OPERATION_FIXUP // RebaseOperationExec No commit will be cherry-picked. The client should run the given command and (if successful) continue. RebaseOperationExec RebaseOperationType = C.GIT_REBASE_OPERATION_EXEC ) func (t RebaseOperationType) String() string { switch t { case RebaseOperationPick: return "pick" case RebaseOperationReword: return "reword" case RebaseOperationEdit: return "edit" case RebaseOperationSquash: return "squash" case RebaseOperationFixup: return "fixup" case RebaseOperationExec: return "exec" } return fmt.Sprintf("RebaseOperationType(%d)", t) } // Special value indicating that there is no currently active operation var RebaseNoOperation uint = ^uint(0) // Error returned if there is no current rebase operation var ErrRebaseNoOperation = errors.New("no current rebase operation") // RebaseOperation describes a single instruction/operation to be performed during the rebase. type RebaseOperation struct { Type RebaseOperationType Id *Oid Exec string } func newRebaseOperationFromC(c *C.git_rebase_operation) *RebaseOperation { operation := &RebaseOperation{} operation.Type = RebaseOperationType(c._type) operation.Id = newOidFromC(&c.id) operation.Exec = C.GoString(c.exec) return operation } //export commitSigningCallback func commitSigningCallback(errorMessage **C.char, _signature *C.git_buf, _signature_field *C.git_buf, _commit_content *C.char, _payload unsafe.Pointer) C.int { opts, ok := pointerHandles.Get(_payload).(*RebaseOptions) if !ok { panic("invalid sign payload") } if opts.CommitSigningCallback == nil { return C.int(ErrorCodePassthrough) } commitContent := C.GoString(_commit_content) signature, signatureField, err := opts.CommitSigningCallback(commitContent) if err != nil { return setCallbackError(errorMessage, err) } fillBuf := func(bufData string, buf *C.git_buf) error { clen := C.size_t(len(bufData)) cstr := unsafe.Pointer(C.CString(bufData)) defer C.free(cstr) // libgit2 requires the contents of the buffer to be NULL-terminated. // C.CString() guarantees that the returned buffer will be // NULL-terminated, so we can safely copy the terminator. if int(C.git_buf_set(buf, cstr, clen+1)) != 0 { return errors.New("could not set buffer") } return nil } if signatureField != "" { err := fillBuf(signatureField, _signature_field) if err != nil { return setCallbackError(errorMessage, err) } } err = fillBuf(signature, _signature) if err != nil { return setCallbackError(errorMessage, err) } return C.int(ErrorCodeOK) } // RebaseOptions are used to tell the rebase machinery how to operate type RebaseOptions struct { Version uint Quiet int InMemory int RewriteNotesRef string MergeOptions MergeOptions CheckoutOptions CheckoutOptions CommitSigningCallback CommitSigningCallback } // DefaultRebaseOptions returns a RebaseOptions with default values. func DefaultRebaseOptions() (RebaseOptions, error) { opts := C.git_rebase_options{} runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_rebase_options_init(&opts, C.GIT_REBASE_OPTIONS_VERSION) if ecode < 0 { return RebaseOptions{}, MakeGitError(ecode) } return rebaseOptionsFromC(&opts), nil } func rebaseOptionsFromC(opts *C.git_rebase_options) RebaseOptions { return RebaseOptions{ Version: uint(opts.version), Quiet: int(opts.quiet), InMemory: int(opts.inmemory), RewriteNotesRef: C.GoString(opts.rewrite_notes_ref), MergeOptions: mergeOptionsFromC(&opts.merge_options), CheckoutOptions: checkoutOptionsFromC(&opts.checkout_options), } } func (ro *RebaseOptions) toC(errorTarget *error) *C.git_rebase_options { if ro == nil { return nil } cOptions := &C.git_rebase_options{ version: C.uint(ro.Version), quiet: C.int(ro.Quiet), inmemory: C.int(ro.InMemory), rewrite_notes_ref: mapEmptyStringToNull(ro.RewriteNotesRef), merge_options: *ro.MergeOptions.toC(), checkout_options: *ro.CheckoutOptions.toC(errorTarget), } if ro.CommitSigningCallback != nil { C._go_git_populate_rebase_callbacks(cOptions) cOptions.payload = pointerHandles.Track(ro) } return cOptions } func freeRebaseOptions(opts *C.git_rebase_options) { if opts == nil { return } C.free(unsafe.Pointer(opts.rewrite_notes_ref)) freeMergeOptions(&opts.merge_options) freeCheckoutOptions(&opts.checkout_options) if opts.payload != nil { pointerHandles.Untrack(opts.payload) } } func mapEmptyStringToNull(ref string) *C.char { if ref == "" { return nil } return C.CString(ref) } // Rebase is the struct representing a Rebase object. type Rebase struct { ptr *C.git_rebase r *Repository options *C.git_rebase_options } // InitRebase initializes a rebase operation to rebase the changes in branch relative to upstream onto another branch. func (r *Repository) InitRebase(branch *AnnotatedCommit, upstream *AnnotatedCommit, onto *AnnotatedCommit, opts *RebaseOptions) (*Rebase, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() if branch == nil { branch = &AnnotatedCommit{ptr: nil} } if upstream == nil { upstream = &AnnotatedCommit{ptr: nil} } if onto == nil { onto = &AnnotatedCommit{ptr: nil} } var ptr *C.git_rebase var err error cOpts := opts.toC(&err) ret := C.git_rebase_init(&ptr, r.ptr, branch.ptr, upstream.ptr, onto.ptr, cOpts) runtime.KeepAlive(branch) runtime.KeepAlive(upstream) runtime.KeepAlive(onto) if ret == C.int(ErrorCodeUser) && err != nil { freeRebaseOptions(cOpts) return nil, err } if ret < 0 { freeRebaseOptions(cOpts) return nil, MakeGitError(ret) } return newRebaseFromC(ptr, cOpts), nil } // OpenRebase opens an existing rebase that was previously started by either an invocation of InitRebase or by another client. func (r *Repository) OpenRebase(opts *RebaseOptions) (*Rebase, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() var ptr *C.git_rebase var err error cOpts := opts.toC(&err) ret := C.git_rebase_open(&ptr, r.ptr, cOpts) runtime.KeepAlive(r) if ret == C.int(ErrorCodeUser) && err != nil { freeRebaseOptions(cOpts) return nil, err } if ret < 0 { freeRebaseOptions(cOpts) return nil, MakeGitError(ret) } return newRebaseFromC(ptr, cOpts), nil } // OperationAt gets the rebase operation specified by the given index. func (rebase *Rebase) OperationAt(index uint) *RebaseOperation { operation := C.git_rebase_operation_byindex(rebase.ptr, C.size_t(index)) return newRebaseOperationFromC(operation) } // CurrentOperationIndex gets the index of the rebase operation that is // currently being applied. There is also an error returned for API // compatibility. func (rebase *Rebase) CurrentOperationIndex() (uint, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() var err error operationIndex := uint(C.git_rebase_operation_current(rebase.ptr)) runtime.KeepAlive(rebase) if operationIndex == RebaseNoOperation { err = ErrRebaseNoOperation } return uint(operationIndex), err } // OperationCount gets the count of rebase operations that are to be applied. func (rebase *Rebase) OperationCount() uint { ret := uint(C.git_rebase_operation_entrycount(rebase.ptr)) runtime.KeepAlive(rebase) return ret } // Next performs the next rebase operation and returns the information about it. // If the operation is one that applies a patch (which is any operation except RebaseOperationExec) // then the patch will be applied and the index and working directory will be updated with the changes. // If there are conflicts, you will need to address those before committing the changes. func (rebase *Rebase) Next() (*RebaseOperation, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() var ptr *C.git_rebase_operation err := C.git_rebase_next(&ptr, rebase.ptr) runtime.KeepAlive(rebase) if err < 0 { return nil, MakeGitError(err) } return newRebaseOperationFromC(ptr), nil } // Commit commits the current patch. // You must have resolved any conflicts that were introduced during the patch application from the Next() invocation. func (rebase *Rebase) Commit(ID *Oid, author, committer *Signature, message string) error { runtime.LockOSThread() defer runtime.UnlockOSThread() authorSig, err := author.toC() if err != nil { return err } defer C.git_signature_free(authorSig) committerSig, err := committer.toC() if err != nil { return err } defer C.git_signature_free(committerSig) cmsg := C.CString(message) defer C.free(unsafe.Pointer(cmsg)) cerr := C.git_rebase_commit(ID.toC(), rebase.ptr, authorSig, committerSig, nil, cmsg) runtime.KeepAlive(ID) runtime.KeepAlive(rebase) if cerr < 0 { return MakeGitError(cerr) } return nil } // Finish finishes a rebase that is currently in progress once all patches have been applied. func (rebase *Rebase) Finish() error { runtime.LockOSThread() defer runtime.UnlockOSThread() err := C.git_rebase_finish(rebase.ptr, nil) runtime.KeepAlive(rebase) if err < 0 { return MakeGitError(err) } return nil } // Abort aborts a rebase that is currently in progress, resetting the repository and working directory to their state before rebase began. func (rebase *Rebase) Abort() error { runtime.LockOSThread() defer runtime.UnlockOSThread() err := C.git_rebase_abort(rebase.ptr) runtime.KeepAlive(rebase) if err < 0 { return MakeGitError(err) } return nil } // Free frees the Rebase object. func (r *Rebase) Free() { runtime.SetFinalizer(r, nil) C.git_rebase_free(r.ptr) freeRebaseOptions(r.options) } func newRebaseFromC(ptr *C.git_rebase, opts *C.git_rebase_options) *Rebase { rebase := &Rebase{ptr: ptr, options: opts} runtime.SetFinalizer(rebase, (*Rebase).Free) return rebase } git2go-31.4.3/rebase_test.go000066400000000000000000000271541376323343000156410ustar00rootroot00000000000000package git import ( "bytes" "errors" "strconv" "strings" "testing" "time" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/packet" ) // Tests func TestRebaseAbort(t *testing.T) { // TEST DATA // Inputs branchName := "emile" masterCommit := "something" emileCommits := []string{ "fou", "barre", } // Outputs expectedHistory := []string{ "Test rebase, Baby! " + emileCommits[1], "Test rebase, Baby! " + emileCommits[0], "This is a commit\n", } // TEST repo := createTestRepo(t) defer cleanupTestRepo(t, repo) seedTestRepo(t, repo) // Setup a repo with 2 branches and a different tree err := setupRepoForRebase(repo, masterCommit, branchName, commitOptions{}) checkFatal(t, err) // Create several commits in emile for _, commit := range emileCommits { _, err = commitSomething(repo, commit, commit, commitOptions{}) checkFatal(t, err) } // Check history actualHistory, err := commitMsgsList(repo) checkFatal(t, err) assertStringList(t, expectedHistory, actualHistory) // Rebase onto master rebase, err := performRebaseOnto(repo, "master", nil) checkFatal(t, err) defer rebase.Free() // Abort rebase rebase.Abort() // Check history is still the same actualHistory, err = commitMsgsList(repo) checkFatal(t, err) assertStringList(t, expectedHistory, actualHistory) } func TestRebaseNoConflicts(t *testing.T) { // TEST DATA // Inputs branchName := "emile" masterCommit := "something" emileCommits := []string{ "fou", "barre", "ouich", } // Outputs expectedHistory := []string{ "Test rebase, Baby! " + emileCommits[2], "Test rebase, Baby! " + emileCommits[1], "Test rebase, Baby! " + emileCommits[0], "Test rebase, Baby! " + masterCommit, "This is a commit\n", } // TEST repo := createTestRepo(t) defer cleanupTestRepo(t, repo) seedTestRepo(t, repo) // Try to open existing rebase oRebase, err := repo.OpenRebase(nil) if err == nil { t.Fatal("Did not expect to find a rebase in progress") } // Setup a repo with 2 branches and a different tree err = setupRepoForRebase(repo, masterCommit, branchName, commitOptions{}) checkFatal(t, err) // Create several commits in emile for _, commit := range emileCommits { _, err = commitSomething(repo, commit, commit, commitOptions{}) checkFatal(t, err) } // Rebase onto master rebase, err := performRebaseOnto(repo, "master", nil) checkFatal(t, err) defer rebase.Free() // Open existing rebase oRebase, err = repo.OpenRebase(nil) checkFatal(t, err) defer oRebase.Free() if oRebase == nil { t.Fatal("Expected to find an existing rebase in progress") } // Finish the rebase properly err = rebase.Finish() checkFatal(t, err) // Check no more rebase is in progress oRebase, err = repo.OpenRebase(nil) if err == nil { t.Fatal("Did not expect to find a rebase in progress") } // Check history is in correct order actualHistory, err := commitMsgsList(repo) checkFatal(t, err) assertStringList(t, expectedHistory, actualHistory) } func TestRebaseGpgSigned(t *testing.T) { // TEST DATA entity, err := openpgp.NewEntity("Namey mcnameface", "test comment", "test@example.com", nil) checkFatal(t, err) rebaseOpts, err := DefaultRebaseOptions() checkFatal(t, err) signCommitContent := func(commitContent string) (string, string, error) { cipherText := new(bytes.Buffer) err := openpgp.ArmoredDetachSignText(cipherText, entity, strings.NewReader(commitContent), &packet.Config{}) if err != nil { return "", "", errors.New("error signing payload") } return cipherText.String(), "", nil } rebaseOpts.CommitSigningCallback = signCommitContent commitOpts := commitOptions{ CommitSigningCallback: signCommitContent, } // Inputs branchName := "emile" masterCommit := "something" emileCommits := []string{ "fou", "barre", "ouich", } // Outputs expectedHistory := []string{ "Test rebase, Baby! " + emileCommits[2], "Test rebase, Baby! " + emileCommits[1], "Test rebase, Baby! " + emileCommits[0], "Test rebase, Baby! " + masterCommit, "This is a commit\n", } // TEST repo := createTestRepo(t) defer cleanupTestRepo(t, repo) seedTestRepoOpt(t, repo, commitOpts) // Try to open existing rebase _, err = repo.OpenRebase(nil) if err == nil { t.Fatal("Did not expect to find a rebase in progress") } // Setup a repo with 2 branches and a different tree err = setupRepoForRebase(repo, masterCommit, branchName, commitOpts) checkFatal(t, err) // Create several commits in emile for _, commit := range emileCommits { _, err = commitSomething(repo, commit, commit, commitOpts) checkFatal(t, err) } // Rebase onto master rebase, err := performRebaseOnto(repo, "master", &rebaseOpts) checkFatal(t, err) defer rebase.Free() // Finish the rebase properly err = rebase.Finish() checkFatal(t, err) // Check history is in correct order actualHistory, err := commitMsgsList(repo) checkFatal(t, err) assertStringList(t, expectedHistory, actualHistory) checkAllCommitsSigned(t, entity, repo) } func checkAllCommitsSigned(t *testing.T, entity *openpgp.Entity, repo *Repository) { head, err := headCommit(repo) checkFatal(t, err) defer head.Free() parent := head err = checkCommitSigned(t, entity, parent) checkFatal(t, err) for parent.ParentCount() != 0 { parent = parent.Parent(0) defer parent.Free() err = checkCommitSigned(t, entity, parent) checkFatal(t, err) } } func checkCommitSigned(t *testing.T, entity *openpgp.Entity, commit *Commit) error { t.Helper() signature, signedData, err := commit.ExtractSignature() if err != nil { t.Logf("No signature on commit\n%s", commit.ContentToSign()) return err } _, err = openpgp.CheckArmoredDetachedSignature(openpgp.EntityList{entity}, strings.NewReader(signedData), bytes.NewBufferString(signature)) if err != nil { t.Logf("Commit is not signed correctly\n%s", commit.ContentToSign()) return err } return nil } // Utils func setupRepoForRebase(repo *Repository, masterCommit, branchName string, commitOpts commitOptions) error { // Create a new branch from master err := createBranch(repo, branchName) if err != nil { return err } // Create a commit in master _, err = commitSomething(repo, masterCommit, masterCommit, commitOpts) if err != nil { return err } // Switch to emile err = repo.SetHead("refs/heads/" + branchName) if err != nil { return err } // Check master commit is not in emile branch if entryExists(repo, masterCommit) { return errors.New(masterCommit + " entry should not exist in " + branchName + " branch.") } return nil } func performRebaseOnto(repo *Repository, branch string, rebaseOpts *RebaseOptions) (*Rebase, error) { master, err := repo.LookupBranch(branch, BranchLocal) if err != nil { return nil, err } defer master.Free() onto, err := repo.AnnotatedCommitFromRef(master.Reference) if err != nil { return nil, err } defer onto.Free() // Init rebase rebase, err := repo.InitRebase(nil, nil, onto, rebaseOpts) if err != nil { return nil, err } // Check no operation has been started yet rebaseOperationIndex, err := rebase.CurrentOperationIndex() if rebaseOperationIndex != RebaseNoOperation && err != ErrRebaseNoOperation { return nil, errors.New("No operation should have been started yet") } // Iterate in rebase operations regarding operation count opCount := int(rebase.OperationCount()) for op := 0; op < opCount; op++ { operation, err := rebase.Next() if err != nil { return nil, err } // Check operation index is correct rebaseOperationIndex, err = rebase.CurrentOperationIndex() if int(rebaseOperationIndex) != op { return nil, errors.New("Bad operation index") } if !operationsAreEqual(rebase.OperationAt(uint(op)), operation) { return nil, errors.New("Rebase operations should be equal") } // Get current rebase operation created commit commit, err := repo.LookupCommit(operation.Id) if err != nil { return nil, err } defer commit.Free() // Apply commit err = rebase.Commit(operation.Id, signature(), signature(), commit.Message()) if err != nil { return nil, err } } return rebase, nil } func operationsAreEqual(l, r *RebaseOperation) bool { return l.Exec == r.Exec && l.Type == r.Type && l.Id.String() == r.Id.String() } func createBranch(repo *Repository, branch string) error { commit, err := headCommit(repo) if err != nil { return err } defer commit.Free() _, err = repo.CreateBranch(branch, commit, false) if err != nil { return err } return nil } func signature() *Signature { return &Signature{ Name: "Emile", Email: "emile@emile.com", When: time.Now(), } } func headCommit(repo *Repository) (*Commit, error) { head, err := repo.Head() if err != nil { return nil, err } defer head.Free() commit, err := repo.LookupCommit(head.Target()) if err != nil { return nil, err } return commit, nil } func headTree(repo *Repository) (*Tree, error) { headCommit, err := headCommit(repo) if err != nil { return nil, err } defer headCommit.Free() tree, err := headCommit.Tree() if err != nil { return nil, err } return tree, nil } func commitSomething(repo *Repository, something, content string, commitOpts commitOptions) (*Oid, error) { headCommit, err := headCommit(repo) if err != nil { return nil, err } defer headCommit.Free() index, err := NewIndex() if err != nil { return nil, err } defer index.Free() blobOID, err := repo.CreateBlobFromBuffer([]byte(content)) if err != nil { return nil, err } entry := &IndexEntry{ Mode: FilemodeBlob, Id: blobOID, Path: something, } if err := index.Add(entry); err != nil { return nil, err } newTreeOID, err := index.WriteTreeTo(repo) if err != nil { return nil, err } newTree, err := repo.LookupTree(newTreeOID) if err != nil { return nil, err } defer newTree.Free() commit, err := repo.CreateCommit("HEAD", signature(), signature(), "Test rebase, Baby! "+something, newTree, headCommit) if err != nil { return nil, err } if commitOpts.CommitSigningCallback != nil { commit, err := repo.LookupCommit(commit) if err != nil { return nil, err } oid, err := commit.WithSignatureUsing(commitOpts.CommitSigningCallback) if err != nil { return nil, err } newCommit, err := repo.LookupCommit(oid) if err != nil { return nil, err } head, err := repo.Head() if err != nil { return nil, err } _, err = repo.References.Create( head.Name(), newCommit.Id(), true, "repoint to signed commit", ) if err != nil { return nil, err } } checkoutOpts := &CheckoutOptions{ Strategy: CheckoutRemoveUntracked | CheckoutForce, } err = repo.CheckoutIndex(index, checkoutOpts) if err != nil { return nil, err } return commit, nil } func entryExists(repo *Repository, file string) bool { headTree, err := headTree(repo) if err != nil { return false } defer headTree.Free() _, err = headTree.EntryByPath(file) return err == nil } func commitMsgsList(repo *Repository) ([]string, error) { head, err := headCommit(repo) if err != nil { return nil, err } defer head.Free() var commits []string parent := head.Parent(0) defer parent.Free() commits = append(commits, head.Message(), parent.Message()) for parent.ParentCount() != 0 { parent = parent.Parent(0) defer parent.Free() commits = append(commits, parent.Message()) } return commits, nil } func assertStringList(t *testing.T, expected, actual []string) { if len(expected) != len(actual) { t.Fatal("Lists are not the same size, expected " + strconv.Itoa(len(expected)) + ", got " + strconv.Itoa(len(actual))) } for index, element := range expected { if element != actual[index] { t.Error("Expected element " + strconv.Itoa(index) + " to be " + element + ", got " + actual[index]) } } } git2go-31.4.3/refdb.go000066400000000000000000000022221376323343000144100ustar00rootroot00000000000000package git /* #include #include extern void _go_git_refdb_backend_free(git_refdb_backend *backend); */ import "C" import ( "runtime" "unsafe" ) type Refdb struct { ptr *C.git_refdb r *Repository } type RefdbBackend struct { ptr *C.git_refdb_backend } func (v *Repository) NewRefdb() (refdb *Refdb, err error) { var ptr *C.git_refdb runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_refdb_new(&ptr, v.ptr) if ret < 0 { return nil, MakeGitError(ret) } refdb = &Refdb{ptr: ptr, r: v} runtime.SetFinalizer(refdb, (*Refdb).Free) return refdb, nil } func NewRefdbBackendFromC(ptr unsafe.Pointer) (backend *RefdbBackend) { backend = &RefdbBackend{(*C.git_refdb_backend)(ptr)} return backend } func (v *Refdb) SetBackend(backend *RefdbBackend) (err error) { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_refdb_set_backend(v.ptr, backend.ptr) runtime.KeepAlive(v) runtime.KeepAlive(backend) if ret < 0 { backend.Free() return MakeGitError(ret) } return nil } func (v *RefdbBackend) Free() { runtime.SetFinalizer(v, nil) C._go_git_refdb_backend_free(v.ptr) } git2go-31.4.3/reference.go000066400000000000000000000302211376323343000152640ustar00rootroot00000000000000package git /* #include */ import "C" import ( "runtime" "unsafe" ) type ReferenceType int const ( ReferenceSymbolic ReferenceType = C.GIT_REF_SYMBOLIC ReferenceOid ReferenceType = C.GIT_REF_OID ) type Reference struct { ptr *C.git_reference repo *Repository } type ReferenceCollection struct { repo *Repository } func (c *ReferenceCollection) Lookup(name string) (*Reference, error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) var ptr *C.git_reference runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_reference_lookup(&ptr, c.repo.ptr, cname) runtime.KeepAlive(c) if ecode < 0 { return nil, MakeGitError(ecode) } return newReferenceFromC(ptr, c.repo), nil } func (c *ReferenceCollection) Create(name string, id *Oid, force bool, msg string) (*Reference, error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) var cmsg *C.char if msg == "" { cmsg = nil } else { cmsg = C.CString(msg) defer C.free(unsafe.Pointer(cmsg)) } var ptr *C.git_reference runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_reference_create(&ptr, c.repo.ptr, cname, id.toC(), cbool(force), cmsg) runtime.KeepAlive(c) runtime.KeepAlive(id) if ecode < 0 { return nil, MakeGitError(ecode) } return newReferenceFromC(ptr, c.repo), nil } func (c *ReferenceCollection) CreateSymbolic(name, target string, force bool, msg string) (*Reference, error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) ctarget := C.CString(target) defer C.free(unsafe.Pointer(ctarget)) var cmsg *C.char if msg == "" { cmsg = nil } else { cmsg = C.CString(msg) defer C.free(unsafe.Pointer(cmsg)) } var ptr *C.git_reference runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_reference_symbolic_create(&ptr, c.repo.ptr, cname, ctarget, cbool(force), cmsg) runtime.KeepAlive(c) if ecode < 0 { return nil, MakeGitError(ecode) } return newReferenceFromC(ptr, c.repo), nil } // EnsureLog ensures that there is a reflog for the given reference // name and creates an empty one if necessary. func (c *ReferenceCollection) EnsureLog(name string) error { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_reference_ensure_log(c.repo.ptr, cname) runtime.KeepAlive(c) if ret < 0 { return MakeGitError(ret) } return nil } // HasLog returns whether there is a reflog for the given reference // name func (c *ReferenceCollection) HasLog(name string) (bool, error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_reference_has_log(c.repo.ptr, cname) runtime.KeepAlive(c) if ret < 0 { return false, MakeGitError(ret) } return ret == 1, nil } // Dwim looks up a reference by DWIMing its short name func (c *ReferenceCollection) Dwim(name string) (*Reference, error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) runtime.LockOSThread() defer runtime.UnlockOSThread() var ptr *C.git_reference ret := C.git_reference_dwim(&ptr, c.repo.ptr, cname) runtime.KeepAlive(c) if ret < 0 { return nil, MakeGitError(ret) } return newReferenceFromC(ptr, c.repo), nil } func newReferenceFromC(ptr *C.git_reference, repo *Repository) *Reference { ref := &Reference{ptr: ptr, repo: repo} runtime.SetFinalizer(ref, (*Reference).Free) return ref } func (v *Reference) SetSymbolicTarget(target string, msg string) (*Reference, error) { var ptr *C.git_reference ctarget := C.CString(target) defer C.free(unsafe.Pointer(ctarget)) runtime.LockOSThread() defer runtime.UnlockOSThread() var cmsg *C.char if msg == "" { cmsg = nil } else { cmsg = C.CString(msg) defer C.free(unsafe.Pointer(cmsg)) } ret := C.git_reference_symbolic_set_target(&ptr, v.ptr, ctarget, cmsg) runtime.KeepAlive(v) if ret < 0 { return nil, MakeGitError(ret) } return newReferenceFromC(ptr, v.repo), nil } func (v *Reference) SetTarget(target *Oid, msg string) (*Reference, error) { var ptr *C.git_reference runtime.LockOSThread() defer runtime.UnlockOSThread() var cmsg *C.char if msg == "" { cmsg = nil } else { cmsg = C.CString(msg) defer C.free(unsafe.Pointer(cmsg)) } ret := C.git_reference_set_target(&ptr, v.ptr, target.toC(), cmsg) runtime.KeepAlive(v) if ret < 0 { return nil, MakeGitError(ret) } return newReferenceFromC(ptr, v.repo), nil } func (v *Reference) Resolve() (*Reference, error) { var ptr *C.git_reference runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_reference_resolve(&ptr, v.ptr) runtime.KeepAlive(v) if ret < 0 { return nil, MakeGitError(ret) } return newReferenceFromC(ptr, v.repo), nil } func (v *Reference) Rename(name string, force bool, msg string) (*Reference, error) { var ptr *C.git_reference cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) 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_reference_rename(&ptr, v.ptr, cname, cbool(force), cmsg) runtime.KeepAlive(v) if ret < 0 { return nil, MakeGitError(ret) } return newReferenceFromC(ptr, v.repo), nil } func (v *Reference) Target() *Oid { ret := newOidFromC(C.git_reference_target(v.ptr)) runtime.KeepAlive(v) return ret } func (v *Reference) SymbolicTarget() string { var ret string cstr := C.git_reference_symbolic_target(v.ptr) if cstr != nil { return C.GoString(cstr) } runtime.KeepAlive(v) return ret } func (v *Reference) Delete() error { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_reference_delete(v.ptr) runtime.KeepAlive(v) if ret < 0 { return MakeGitError(ret) } return nil } func (v *Reference) Peel(t ObjectType) (*Object, error) { var cobj *C.git_object runtime.LockOSThread() defer runtime.UnlockOSThread() err := C.git_reference_peel(&cobj, v.ptr, C.git_object_t(t)) runtime.KeepAlive(v) if err < 0 { return nil, MakeGitError(err) } return allocObject(cobj, v.repo), nil } // Owner returns a weak reference to the repository which owns this // reference. func (v *Reference) Owner() *Repository { return &Repository{ ptr: C.git_reference_owner(v.ptr), } } // Cmp compares v to ref2. It returns 0 on equality, otherwise a // stable sorting. func (v *Reference) Cmp(ref2 *Reference) int { ret := int(C.git_reference_cmp(v.ptr, ref2.ptr)) runtime.KeepAlive(v) runtime.KeepAlive(ref2) return ret } // Shorthand returns a "human-readable" short reference name. func (v *Reference) Shorthand() string { ret := C.GoString(C.git_reference_shorthand(v.ptr)) runtime.KeepAlive(v) return ret } // Name returns the full name of v. func (v *Reference) Name() string { ret := C.GoString(C.git_reference_name(v.ptr)) runtime.KeepAlive(v) return ret } func (v *Reference) Type() ReferenceType { ret := ReferenceType(C.git_reference_type(v.ptr)) runtime.KeepAlive(v) return ret } func (v *Reference) IsBranch() bool { ret := C.git_reference_is_branch(v.ptr) == 1 runtime.KeepAlive(v) return ret } func (v *Reference) IsRemote() bool { ret := C.git_reference_is_remote(v.ptr) == 1 runtime.KeepAlive(v) return ret } func (v *Reference) IsTag() bool { ret := C.git_reference_is_tag(v.ptr) == 1 runtime.KeepAlive(v) return ret } // IsNote checks if the reference is a note. func (v *Reference) IsNote() bool { ret := C.git_reference_is_note(v.ptr) == 1 runtime.KeepAlive(v) return ret } func (v *Reference) Free() { runtime.SetFinalizer(v, nil) C.git_reference_free(v.ptr) } type ReferenceIterator struct { ptr *C.git_reference_iterator repo *Repository } type ReferenceNameIterator struct { *ReferenceIterator } // NewReferenceIterator creates a new iterator over reference names func (repo *Repository) NewReferenceIterator() (*ReferenceIterator, error) { var ptr *C.git_reference_iterator runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_reference_iterator_new(&ptr, repo.ptr) if ret < 0 { return nil, MakeGitError(ret) } return newReferenceIteratorFromC(ptr, repo), nil } // NewReferenceIterator creates a new branch iterator over reference names func (repo *Repository) NewReferenceNameIterator() (*ReferenceNameIterator, error) { var ptr *C.git_reference_iterator runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_reference_iterator_new(&ptr, repo.ptr) if ret < 0 { return nil, MakeGitError(ret) } iter := newReferenceIteratorFromC(ptr, repo) return iter.Names(), nil } // NewReferenceIteratorGlob creates an iterator over reference names // that match the speicified glob. The glob is of the usual fnmatch // type. func (repo *Repository) NewReferenceIteratorGlob(glob string) (*ReferenceIterator, error) { cstr := C.CString(glob) defer C.free(unsafe.Pointer(cstr)) var ptr *C.git_reference_iterator runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_reference_iterator_glob_new(&ptr, repo.ptr, cstr) if ret < 0 { return nil, MakeGitError(ret) } return newReferenceIteratorFromC(ptr, repo), nil } func (i *ReferenceIterator) Names() *ReferenceNameIterator { return &ReferenceNameIterator{i} } // NextName retrieves the next reference name. If the iteration is over, // the returned error code is git.ErrorCodeIterOver func (v *ReferenceNameIterator) Next() (string, error) { var ptr *C.char runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_reference_next_name(&ptr, v.ptr) if ret < 0 { return "", MakeGitError(ret) } return C.GoString(ptr), nil } // Next retrieves the next reference. If the iterationis over, the // returned error code is git.ErrorCodeIterOver 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) } return newReferenceFromC(ptr, v.repo), nil } func newReferenceIteratorFromC(ptr *C.git_reference_iterator, r *Repository) *ReferenceIterator { iter := &ReferenceIterator{ ptr: ptr, repo: r, } runtime.SetFinalizer(iter, (*ReferenceIterator).Free) return iter } // Free the reference iterator func (v *ReferenceIterator) Free() { runtime.SetFinalizer(v, nil) C.git_reference_iterator_free(v.ptr) } // ReferenceIsValidName returns whether the reference name is well-formed. // // Valid reference names must follow one of two patterns: // // 1. Top-level names must contain only capital letters and underscores, // and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD"). // // 2. Names prefixed with "refs/" can be almost anything. You must avoid // the characters '~', '^', ':', ' \ ', '?', '[', and '*', and the sequences // ".." and " @ {" which have special meaning to revparse. func ReferenceIsValidName(name string) bool { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) return C.git_reference_is_valid_name(cname) == 1 } const ( // This should match GIT_REFNAME_MAX in src/refs.h _refnameMaxLength = C.size_t(1024) ) type ReferenceFormat uint const ( ReferenceFormatNormal ReferenceFormat = C.GIT_REFERENCE_FORMAT_NORMAL ReferenceFormatAllowOnelevel ReferenceFormat = C.GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL ReferenceFormatRefspecPattern ReferenceFormat = C.GIT_REFERENCE_FORMAT_REFSPEC_PATTERN ReferenceFormatRefspecShorthand ReferenceFormat = C.GIT_REFERENCE_FORMAT_REFSPEC_SHORTHAND ) // ReferenceNormalizeName normalizes the reference name and checks validity. // // This will normalize the reference name by removing any leading slash '/' // characters and collapsing runs of adjacent slashes between name components // into a single slash. // // See git_reference_symbolic_create() for rules about valid names. func ReferenceNormalizeName(name string, flags ReferenceFormat) (string, error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) buf := (*C.char)(C.malloc(_refnameMaxLength)) defer C.free(unsafe.Pointer(buf)) runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_reference_normalize_name(buf, _refnameMaxLength, cname, C.uint(flags)) if ecode < 0 { return "", MakeGitError(ecode) } return C.GoString(buf), nil } git2go-31.4.3/reference_test.go000066400000000000000000000140631376323343000163310ustar00rootroot00000000000000package git import ( "runtime" "sort" "testing" "time" ) func TestRefModification(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) commitId, treeId := seedTestRepo(t, repo) _, err := repo.References.Create("refs/tags/tree", treeId, true, "testTreeTag") checkFatal(t, err) tag, err := repo.References.Lookup("refs/tags/tree") checkFatal(t, err) checkRefType(t, tag, ReferenceOid) ref, err := repo.References.Lookup("HEAD") checkFatal(t, err) checkRefType(t, ref, ReferenceSymbolic) if target := ref.Target(); target != nil { t.Fatalf("Expected nil *Oid, got %v", target) } ref, err = ref.Resolve() checkFatal(t, err) checkRefType(t, ref, ReferenceOid) if target := ref.Target(); target == nil { t.Fatalf("Expected valid target got nil") } if target := ref.SymbolicTarget(); target != "" { t.Fatalf("Expected empty string, got %v", target) } if commitId.String() != ref.Target().String() { t.Fatalf("Wrong ref target") } _, err = tag.Rename("refs/tags/renamed", false, "") checkFatal(t, err) tag, err = repo.References.Lookup("refs/tags/renamed") checkFatal(t, err) checkRefType(t, ref, ReferenceOid) } func TestReferenceIterator(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) loc, err := time.LoadLocation("Europe/Berlin") checkFatal(t, err) sig := &Signature{ Name: "Rand Om Hacker", Email: "random@hacker.com", When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc), } idx, err := repo.Index() checkFatal(t, err) err = idx.AddByPath("README") checkFatal(t, err) treeId, err := idx.WriteTree() checkFatal(t, err) message := "This is a commit\n" tree, err := repo.LookupTree(treeId) checkFatal(t, err) commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree) checkFatal(t, err) _, err = repo.References.Create("refs/heads/one", commitId, true, "headOne") checkFatal(t, err) _, err = repo.References.Create("refs/heads/two", commitId, true, "headTwo") checkFatal(t, err) _, err = repo.References.Create("refs/heads/three", commitId, true, "headThree") checkFatal(t, err) iter, err := repo.NewReferenceIterator() checkFatal(t, err) var list []string expected := []string{ "refs/heads/master", "refs/heads/one", "refs/heads/three", "refs/heads/two", } // test some manual iteration nameIter := iter.Names() name, err := nameIter.Next() for err == nil { list = append(list, name) name, err = nameIter.Next() } if !IsErrorCode(err, ErrorCodeIterOver) { t.Fatal("Iteration not over") } sort.Strings(list) compareStringList(t, expected, list) // test the iterator for full refs, rather than just names iter, err = repo.NewReferenceIterator() checkFatal(t, err) count := 0 _, err = iter.Next() for err == nil { count++ _, err = iter.Next() } if !IsErrorCode(err, ErrorCodeIterOver) { t.Fatal("Iteration not over") } if count != 4 { t.Fatalf("Wrong number of references returned %v", count) } } func TestReferenceOwner(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) commitId, _ := seedTestRepo(t, repo) ref, err := repo.References.Create("refs/heads/foo", commitId, true, "") checkFatal(t, err) owner := ref.Owner() if owner == nil { t.Fatal("nil owner") } if owner.ptr != repo.ptr { t.Fatalf("bad ptr, expected %v have %v\n", repo.ptr, owner.ptr) } } func TestUtil(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) commitId, _ := seedTestRepo(t, repo) ref, err := repo.References.Create("refs/heads/foo", commitId, true, "") checkFatal(t, err) ref2, err := repo.References.Dwim("foo") checkFatal(t, err) if ref.Cmp(ref2) != 0 { t.Fatalf("foo didn't dwim to the right thing") } if ref.Shorthand() != "foo" { t.Fatalf("refs/heads/foo has no foo shorthand") } hasLog, err := repo.References.HasLog("refs/heads/foo") checkFatal(t, err) if !hasLog { t.Fatalf("branches have logs by default") } } func TestIsNote(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) commitID, _ := seedTestRepo(t, repo) sig := &Signature{ Name: "Rand Om Hacker", Email: "random@hacker.com", When: time.Now(), } refname, err := repo.Notes.DefaultRef() checkFatal(t, err) _, err = repo.Notes.Create(refname, sig, sig, commitID, "This is a note", false) checkFatal(t, err) ref, err := repo.References.Lookup(refname) checkFatal(t, err) if !ref.IsNote() { t.Fatalf("%s should be a note", ref.Name()) } ref, err = repo.References.Create("refs/heads/foo", commitID, true, "") checkFatal(t, err) if ref.IsNote() { t.Fatalf("%s should not be a note", ref.Name()) } } func TestReferenceIsValidName(t *testing.T) { t.Parallel() if !ReferenceIsValidName("HEAD") { t.Errorf("HEAD should be a valid reference name") } if ReferenceIsValidName("HEAD1") { t.Errorf("HEAD1 should not be a valid reference name") } } func TestReferenceNormalizeName(t *testing.T) { t.Parallel() ref, err := ReferenceNormalizeName("refs/heads//master", ReferenceFormatNormal) checkFatal(t, err) if ref != "refs/heads/master" { t.Errorf("ReferenceNormalizeName(%q) = %q; want %q", "refs/heads//master", ref, "refs/heads/master") } ref, err = ReferenceNormalizeName("master", ReferenceFormatAllowOnelevel|ReferenceFormatRefspecShorthand) checkFatal(t, err) if ref != "master" { t.Errorf("ReferenceNormalizeName(%q) = %q; want %q", "master", ref, "master") } ref, err = ReferenceNormalizeName("foo^", ReferenceFormatNormal) if !IsErrorCode(err, ErrorCodeInvalidSpec) { t.Errorf("foo^ should be invalid") } } func compareStringList(t *testing.T, expected, actual []string) { for i, v := range expected { if actual[i] != v { t.Fatalf("Bad list") } } } func checkRefType(t *testing.T, ref *Reference, kind ReferenceType) { if ref.Type() == kind { return } // The failure happens at wherever we were called, not here _, file, line, ok := runtime.Caller(1) if !ok { t.Fatalf("Unable to get caller") } t.Fatalf("Wrong ref type at %v:%v; have %v, expected %v", file, line, ref.Type(), kind) } git2go-31.4.3/remote.go000066400000000000000000000623031376323343000146270ustar00rootroot00000000000000package git /* #include #include #include extern void _go_git_populate_remote_callbacks(git_remote_callbacks *callbacks); */ import "C" import ( "crypto/x509" "errors" "reflect" "runtime" "strings" "unsafe" ) type TransferProgress struct { TotalObjects uint IndexedObjects uint ReceivedObjects uint LocalObjects uint TotalDeltas uint ReceivedBytes uint } func newTransferProgressFromC(c *C.git_transfer_progress) TransferProgress { return TransferProgress{ TotalObjects: uint(c.total_objects), IndexedObjects: uint(c.indexed_objects), ReceivedObjects: uint(c.received_objects), LocalObjects: uint(c.local_objects), TotalDeltas: uint(c.total_deltas), ReceivedBytes: uint(c.received_bytes)} } type RemoteCompletion uint type ConnectDirection uint const ( RemoteCompletionDownload RemoteCompletion = C.GIT_REMOTE_COMPLETION_DOWNLOAD RemoteCompletionIndexing RemoteCompletion = C.GIT_REMOTE_COMPLETION_INDEXING RemoteCompletionError RemoteCompletion = C.GIT_REMOTE_COMPLETION_ERROR ConnectDirectionFetch ConnectDirection = C.GIT_DIRECTION_FETCH ConnectDirectionPush ConnectDirection = C.GIT_DIRECTION_PUSH ) type TransportMessageCallback func(str string) ErrorCode type CompletionCallback func(RemoteCompletion) ErrorCode type CredentialsCallback func(url string, username_from_url string, allowed_types CredentialType) (*Credential, error) 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 CompletionCallback CredentialsCallback TransferProgressCallback UpdateTipsCallback CertificateCheckCallback PackProgressCallback PackbuilderProgressCallback PushTransferProgressCallback PushUpdateReferenceCallback } type FetchPrune uint const ( // Use the setting from the configuration FetchPruneUnspecified FetchPrune = C.GIT_FETCH_PRUNE_UNSPECIFIED // Force pruning on FetchPruneOn FetchPrune = C.GIT_FETCH_PRUNE // Force pruning off FetchNoPrune FetchPrune = C.GIT_FETCH_NO_PRUNE ) type DownloadTags uint const ( // Use the setting from the configuration. DownloadTagsUnspecified DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED // Ask the server for tags pointing to objects we're already // downloading. DownloadTagsAuto DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_AUTO // Don't ask for any tags beyond the refspecs. DownloadTagsNone DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_NONE // Ask for the all the tags. DownloadTagsAll DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_ALL ) type FetchOptions struct { // Callbacks to use for this fetch operation RemoteCallbacks RemoteCallbacks // Whether to perform a prune after the fetch Prune FetchPrune // Whether to write the results to FETCH_HEAD. Defaults to // on. Leave this default in order to behave like git. UpdateFetchhead bool // Determines how to behave regarding tags on the remote, such // as auto-downloading tags for objects we're downloading or // downloading all of them. // // The default is to auto-follow tags. DownloadTags DownloadTags // Headers are extra headers for the fetch operation. Headers []string // Proxy options to use for this fetch operation ProxyOptions ProxyOptions } type ProxyType uint const ( // Do not attempt to connect through a proxy // // If built against lbicurl, it itself may attempt to connect // to a proxy if the environment variables specify it. ProxyTypeNone ProxyType = C.GIT_PROXY_NONE // Try to auto-detect the proxy from the git configuration. ProxyTypeAuto ProxyType = C.GIT_PROXY_AUTO // Connect via the URL given in the options ProxyTypeSpecified ProxyType = C.GIT_PROXY_SPECIFIED ) type ProxyOptions struct { // The type of proxy to use (or none) Type ProxyType // The proxy's URL Url string } type Remote struct { ptr *C.git_remote callbacks RemoteCallbacks repo *Repository } type CertificateKind uint const ( CertificateX509 CertificateKind = C.GIT_CERT_X509 CertificateHostkey CertificateKind = C.GIT_CERT_HOSTKEY_LIBSSH2 ) // Certificate represents the two possible certificates which libgit2 // knows it might find. If Kind is CertficateX509 then the X509 field // will be filled. If Kind is CertificateHostkey then the Hostkey // field will be fille.d type Certificate struct { Kind CertificateKind X509 *x509.Certificate Hostkey HostkeyCertificate } type HostkeyKind uint const ( HostkeyMD5 HostkeyKind = C.GIT_CERT_SSH_MD5 HostkeySHA1 HostkeyKind = C.GIT_CERT_SSH_SHA1 HostkeySHA256 HostkeyKind = C.GIT_CERT_SSH_SHA256 ) // Server host key information. A bitmask containing the available fields. // Check for combinations of: HostkeyMD5, HostkeySHA1, HostkeySHA256. type HostkeyCertificate struct { Kind HostkeyKind HashMD5 [16]byte HashSHA1 [20]byte HashSHA256 [32]byte } type PushOptions struct { // Callbacks to use for this push operation RemoteCallbacks RemoteCallbacks PbParallelism uint // Headers are extra headers for the push operation. Headers []string } type RemoteHead struct { Id *Oid Name string } func newRemoteHeadFromC(ptr *C.git_remote_head) RemoteHead { return RemoteHead{ Id: newOidFromC(&ptr.oid), Name: C.GoString(ptr.name), } } func untrackCalbacksPayload(callbacks *C.git_remote_callbacks) { if callbacks != nil && callbacks.payload != nil { pointerHandles.Untrack(callbacks.payload) } } func populateRemoteCallbacks(ptr *C.git_remote_callbacks, callbacks *RemoteCallbacks) { C.git_remote_init_callbacks(ptr, C.GIT_REMOTE_CALLBACKS_VERSION) if callbacks == nil { return } C._go_git_populate_remote_callbacks(ptr) ptr.payload = pointerHandles.Track(callbacks) } //export sidebandProgressCallback func sidebandProgressCallback(errorMessage **C.char, _str *C.char, _len C.int, data unsafe.Pointer) C.int { callbacks := pointerHandles.Get(data).(*RemoteCallbacks) if callbacks.SidebandProgressCallback == nil { return C.int(ErrorCodeOK) } str := C.GoStringN(_str, _len) ret := callbacks.SidebandProgressCallback(str) if ret < 0 { return setCallbackError(errorMessage, errors.New(ErrorCode(ret).String())) } return C.int(ErrorCodeOK) } //export completionCallback func completionCallback(errorMessage **C.char, completion_type C.git_remote_completion_type, data unsafe.Pointer) C.int { callbacks := pointerHandles.Get(data).(*RemoteCallbacks) if callbacks.CompletionCallback == nil { return C.int(ErrorCodeOK) } ret := callbacks.CompletionCallback(RemoteCompletion(completion_type)) if ret < 0 { return setCallbackError(errorMessage, errors.New(ErrorCode(ret).String())) } return C.int(ErrorCodeOK) } //export credentialsCallback func credentialsCallback( errorMessage **C.char, _cred **C.git_credential, _url *C.char, _username_from_url *C.char, allowed_types uint, data unsafe.Pointer, ) C.int { callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) if callbacks.CredentialsCallback == nil { return C.int(ErrorCodePassthrough) } url := C.GoString(_url) username_from_url := C.GoString(_username_from_url) cred, err := callbacks.CredentialsCallback(url, username_from_url, (CredentialType)(allowed_types)) if err != nil { return setCallbackError(errorMessage, err) } if cred != nil { *_cred = cred.ptr // have transferred ownership to libgit, 'forget' the native pointer cred.ptr = nil runtime.SetFinalizer(cred, nil) } return C.int(ErrorCodeOK) } //export transferProgressCallback func transferProgressCallback(errorMessage **C.char, stats *C.git_transfer_progress, data unsafe.Pointer) C.int { callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) if callbacks.TransferProgressCallback == nil { return C.int(ErrorCodeOK) } ret := callbacks.TransferProgressCallback(newTransferProgressFromC(stats)) if ret < 0 { return setCallbackError(errorMessage, errors.New(ErrorCode(ret).String())) } return C.int(ErrorCodeOK) } //export updateTipsCallback func updateTipsCallback( errorMessage **C.char, _refname *C.char, _a *C.git_oid, _b *C.git_oid, data unsafe.Pointer, ) C.int { callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) if callbacks.UpdateTipsCallback == nil { return C.int(ErrorCodeOK) } refname := C.GoString(_refname) a := newOidFromC(_a) b := newOidFromC(_b) ret := callbacks.UpdateTipsCallback(refname, a, b) if ret < 0 { return setCallbackError(errorMessage, errors.New(ErrorCode(ret).String())) } return C.int(ErrorCodeOK) } //export certificateCheckCallback func certificateCheckCallback( errorMessage **C.char, _cert *C.git_cert, _valid C.int, _host *C.char, data unsafe.Pointer, ) C.int { callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) // if there's no callback set, we need to make sure we fail if the library didn't consider this cert valid if callbacks.CertificateCheckCallback == nil { if _valid == 0 { return C.int(ErrorCodeCertificate) } return C.int(ErrorCodeOK) } host := C.GoString(_host) valid := _valid != 0 var cert Certificate if _cert.cert_type == C.GIT_CERT_X509 { cert.Kind = CertificateX509 ccert := (*C.git_cert_x509)(unsafe.Pointer(_cert)) x509_certs, err := x509.ParseCertificates(C.GoBytes(ccert.data, C.int(ccert.len))) if err != nil { return setCallbackError(errorMessage, err) } if len(x509_certs) < 1 { return setCallbackError(errorMessage, errors.New("empty certificate list")) } // we assume there's only one, which should hold true for any web server we want to talk to cert.X509 = x509_certs[0] } else if _cert.cert_type == C.GIT_CERT_HOSTKEY_LIBSSH2 { cert.Kind = CertificateHostkey ccert := (*C.git_cert_hostkey)(unsafe.Pointer(_cert)) cert.Hostkey.Kind = HostkeyKind(ccert._type) C.memcpy(unsafe.Pointer(&cert.Hostkey.HashMD5[0]), unsafe.Pointer(&ccert.hash_md5[0]), C.size_t(len(cert.Hostkey.HashMD5))) C.memcpy(unsafe.Pointer(&cert.Hostkey.HashSHA1[0]), unsafe.Pointer(&ccert.hash_sha1[0]), C.size_t(len(cert.Hostkey.HashSHA1))) C.memcpy(unsafe.Pointer(&cert.Hostkey.HashSHA256[0]), unsafe.Pointer(&ccert.hash_sha256[0]), C.size_t(len(cert.Hostkey.HashSHA256))) } else { return setCallbackError(errorMessage, errors.New("unsupported certificate type")) } ret := callbacks.CertificateCheckCallback(&cert, valid, host) if ret < 0 { return setCallbackError(errorMessage, errors.New(ErrorCode(ret).String())) } return C.int(ErrorCodeOK) } //export packProgressCallback func packProgressCallback(errorMessage **C.char, stage C.int, current, total C.uint, data unsafe.Pointer) C.int { callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) if callbacks.PackProgressCallback == nil { return C.int(ErrorCodeOK) } ret := callbacks.PackProgressCallback(int32(stage), uint32(current), uint32(total)) if ret < 0 { return setCallbackError(errorMessage, errors.New(ErrorCode(ret).String())) } return C.int(ErrorCodeOK) } //export pushTransferProgressCallback func pushTransferProgressCallback(errorMessage **C.char, current, total C.uint, bytes C.size_t, data unsafe.Pointer) C.int { callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) if callbacks.PushTransferProgressCallback == nil { return C.int(ErrorCodeOK) } ret := callbacks.PushTransferProgressCallback(uint32(current), uint32(total), uint(bytes)) if ret < 0 { return setCallbackError(errorMessage, errors.New(ErrorCode(ret).String())) } return C.int(ErrorCodeOK) } //export pushUpdateReferenceCallback func pushUpdateReferenceCallback(errorMessage **C.char, refname, status *C.char, data unsafe.Pointer) C.int { callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) if callbacks.PushUpdateReferenceCallback == nil { return C.int(ErrorCodeOK) } ret := callbacks.PushUpdateReferenceCallback(C.GoString(refname), C.GoString(status)) if ret < 0 { return setCallbackError(errorMessage, errors.New(ErrorCode(ret).String())) } return C.int(ErrorCodeOK) } func populateProxyOptions(ptr *C.git_proxy_options, opts *ProxyOptions) { C.git_proxy_options_init(ptr, C.GIT_PROXY_OPTIONS_VERSION) if opts == nil { return } ptr._type = C.git_proxy_t(opts.Type) ptr.url = C.CString(opts.Url) } func freeProxyOptions(ptr *C.git_proxy_options) { if ptr == nil { return } C.free(unsafe.Pointer(ptr.url)) } // RemoteIsValidName returns whether the remote name is well-formed. func RemoteIsValidName(name string) bool { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) return C.git_remote_is_valid_name(cname) == 1 } func (r *Remote) Free() { runtime.SetFinalizer(r, nil) C.git_remote_free(r.ptr) r.ptr = nil } type RemoteCollection struct { repo *Repository } func (c *RemoteCollection) List() ([]string, error) { var r C.git_strarray runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_remote_list(&r, c.repo.ptr) runtime.KeepAlive(c.repo) if ecode < 0 { return nil, MakeGitError(ecode) } defer C.git_strarray_dispose(&r) remotes := makeStringsFromCStrings(r.strings, int(r.count)) return remotes, nil } func (c *RemoteCollection) Create(name string, url string) (*Remote, error) { remote := &Remote{repo: c.repo} cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_remote_create(&remote.ptr, c.repo.ptr, cname, curl) if ret < 0 { return nil, MakeGitError(ret) } runtime.SetFinalizer(remote, (*Remote).Free) return remote, nil } func (c *RemoteCollection) Delete(name string) error { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_remote_delete(c.repo.ptr, cname) runtime.KeepAlive(c.repo) if ret < 0 { return MakeGitError(ret) } return nil } func (c *RemoteCollection) CreateWithFetchspec(name string, url string, fetch string) (*Remote, error) { remote := &Remote{repo: c.repo} cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) cfetch := C.CString(fetch) defer C.free(unsafe.Pointer(cfetch)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_remote_create_with_fetchspec(&remote.ptr, c.repo.ptr, cname, curl, cfetch) if ret < 0 { return nil, MakeGitError(ret) } runtime.SetFinalizer(remote, (*Remote).Free) return remote, nil } func (c *RemoteCollection) CreateAnonymous(url string) (*Remote, error) { remote := &Remote{repo: c.repo} curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_remote_create_anonymous(&remote.ptr, c.repo.ptr, curl) if ret < 0 { return nil, MakeGitError(ret) } runtime.SetFinalizer(remote, (*Remote).Free) return remote, nil } func (c *RemoteCollection) Lookup(name string) (*Remote, error) { remote := &Remote{repo: c.repo} cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_remote_lookup(&remote.ptr, c.repo.ptr, cname) if ret < 0 { return nil, MakeGitError(ret) } runtime.SetFinalizer(remote, (*Remote).Free) return remote, nil } func (o *Remote) Name() string { s := C.git_remote_name(o.ptr) runtime.KeepAlive(o) return C.GoString(s) } func (o *Remote) Url() string { s := C.git_remote_url(o.ptr) runtime.KeepAlive(o) return C.GoString(s) } func (o *Remote) PushUrl() string { s := C.git_remote_pushurl(o.ptr) runtime.KeepAlive(o) return C.GoString(s) } func (c *RemoteCollection) Rename(remote, newname string) ([]string, error) { cproblems := C.git_strarray{} defer freeStrarray(&cproblems) cnewname := C.CString(newname) defer C.free(unsafe.Pointer(cnewname)) cremote := C.CString(remote) defer C.free(unsafe.Pointer(cremote)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_remote_rename(&cproblems, c.repo.ptr, cremote, cnewname) runtime.KeepAlive(c.repo) if ret < 0 { return []string{}, MakeGitError(ret) } problems := makeStringsFromCStrings(cproblems.strings, int(cproblems.count)) return problems, nil } func (c *RemoteCollection) SetUrl(remote, url string) error { curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) cremote := C.CString(remote) defer C.free(unsafe.Pointer(cremote)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_remote_set_url(c.repo.ptr, cremote, curl) runtime.KeepAlive(c.repo) if ret < 0 { return MakeGitError(ret) } return nil } func (c *RemoteCollection) SetPushUrl(remote, url string) error { curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) cremote := C.CString(remote) defer C.free(unsafe.Pointer(cremote)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_remote_set_pushurl(c.repo.ptr, cremote, curl) runtime.KeepAlive(c.repo) if ret < 0 { return MakeGitError(ret) } return nil } func (c *RemoteCollection) AddFetch(remote, refspec string) error { crefspec := C.CString(refspec) defer C.free(unsafe.Pointer(crefspec)) cremote := C.CString(remote) defer C.free(unsafe.Pointer(cremote)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_remote_add_fetch(c.repo.ptr, cremote, crefspec) runtime.KeepAlive(c.repo) if ret < 0 { return MakeGitError(ret) } return nil } func sptr(p uintptr) *C.char { return *(**C.char)(unsafe.Pointer(p)) } func makeStringsFromCStrings(x **C.char, l int) []string { s := make([]string, l) i := 0 for p := uintptr(unsafe.Pointer(x)); i < l; p += unsafe.Sizeof(uintptr(0)) { s[i] = C.GoString(sptr(p)) i++ } return s } func makeCStringsFromStrings(s []string) **C.char { l := len(s) x := (**C.char)(C.malloc(C.size_t(unsafe.Sizeof(unsafe.Pointer(nil)) * uintptr(l)))) i := 0 for p := uintptr(unsafe.Pointer(x)); i < l; p += unsafe.Sizeof(uintptr(0)) { *(**C.char)(unsafe.Pointer(p)) = C.CString(s[i]) i++ } return x } func freeStrarray(arr *C.git_strarray) { count := int(arr.count) size := unsafe.Sizeof(unsafe.Pointer(nil)) i := 0 for p := uintptr(unsafe.Pointer(arr.strings)); i < count; p += size { C.free(unsafe.Pointer(sptr(p))) i++ } C.free(unsafe.Pointer(arr.strings)) } func (o *Remote) FetchRefspecs() ([]string, error) { crefspecs := C.git_strarray{} runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_remote_get_fetch_refspecs(&crefspecs, o.ptr) runtime.KeepAlive(o) if ret < 0 { return nil, MakeGitError(ret) } defer C.git_strarray_dispose(&crefspecs) refspecs := makeStringsFromCStrings(crefspecs.strings, int(crefspecs.count)) return refspecs, nil } func (c *RemoteCollection) AddPush(remote, refspec string) error { crefspec := C.CString(refspec) defer C.free(unsafe.Pointer(crefspec)) cremote := C.CString(remote) defer C.free(unsafe.Pointer(cremote)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_remote_add_push(c.repo.ptr, cremote, crefspec) runtime.KeepAlive(c.repo) if ret < 0 { return MakeGitError(ret) } return nil } func (o *Remote) PushRefspecs() ([]string, error) { crefspecs := C.git_strarray{} runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_remote_get_push_refspecs(&crefspecs, o.ptr) if ret < 0 { return nil, MakeGitError(ret) } defer C.git_strarray_dispose(&crefspecs) runtime.KeepAlive(o) refspecs := makeStringsFromCStrings(crefspecs.strings, int(crefspecs.count)) return refspecs, nil } func (o *Remote) RefspecCount() uint { count := C.git_remote_refspec_count(o.ptr) runtime.KeepAlive(o) return uint(count) } func populateFetchOptions(options *C.git_fetch_options, opts *FetchOptions) { C.git_fetch_options_init(options, C.GIT_FETCH_OPTIONS_VERSION) if opts == nil { return } populateRemoteCallbacks(&options.callbacks, &opts.RemoteCallbacks) options.prune = C.git_fetch_prune_t(opts.Prune) options.update_fetchhead = cbool(opts.UpdateFetchhead) options.download_tags = C.git_remote_autotag_option_t(opts.DownloadTags) options.custom_headers = C.git_strarray{} options.custom_headers.count = C.size_t(len(opts.Headers)) options.custom_headers.strings = makeCStringsFromStrings(opts.Headers) populateProxyOptions(&options.proxy_opts, &opts.ProxyOptions) } func populatePushOptions(options *C.git_push_options, opts *PushOptions) { C.git_push_options_init(options, C.GIT_PUSH_OPTIONS_VERSION) if opts == nil { return } options.pb_parallelism = C.uint(opts.PbParallelism) options.custom_headers = C.git_strarray{} options.custom_headers.count = C.size_t(len(opts.Headers)) options.custom_headers.strings = makeCStringsFromStrings(opts.Headers) populateRemoteCallbacks(&options.callbacks, &opts.RemoteCallbacks) } // Fetch performs a fetch operation. refspecs specifies which refspecs // to use for this fetch, use an empty list to use the refspecs from // the configuration; msg specifies what to use for the reflog // entries. Leave "" to use defaults. func (o *Remote) Fetch(refspecs []string, opts *FetchOptions, msg string) error { var cmsg *C.char = nil if msg != "" { cmsg = C.CString(msg) defer C.free(unsafe.Pointer(cmsg)) } crefspecs := C.git_strarray{} crefspecs.count = C.size_t(len(refspecs)) crefspecs.strings = makeCStringsFromStrings(refspecs) defer freeStrarray(&crefspecs) coptions := (*C.git_fetch_options)(C.calloc(1, C.size_t(unsafe.Sizeof(C.git_fetch_options{})))) defer C.free(unsafe.Pointer(coptions)) populateFetchOptions(coptions, opts) defer untrackCalbacksPayload(&coptions.callbacks) defer freeStrarray(&coptions.custom_headers) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_remote_fetch(o.ptr, &crefspecs, coptions, cmsg) runtime.KeepAlive(o) if ret < 0 { return MakeGitError(ret) } return nil } func (o *Remote) ConnectFetch(callbacks *RemoteCallbacks, proxyOpts *ProxyOptions, headers []string) error { return o.Connect(ConnectDirectionFetch, callbacks, proxyOpts, headers) } func (o *Remote) ConnectPush(callbacks *RemoteCallbacks, proxyOpts *ProxyOptions, headers []string) error { return o.Connect(ConnectDirectionPush, callbacks, proxyOpts, headers) } // Connect opens a connection to a remote. // // The transport is selected based on the URL. The direction argument // is due to a limitation of the git protocol (over TCP or SSH) which // starts up a specific binary which can only do the one or the other. // // 'headers' are extra HTTP headers to use in this connection. func (o *Remote) Connect(direction ConnectDirection, callbacks *RemoteCallbacks, proxyOpts *ProxyOptions, headers []string) error { var ccallbacks C.git_remote_callbacks populateRemoteCallbacks(&ccallbacks, callbacks) var cproxy C.git_proxy_options populateProxyOptions(&cproxy, proxyOpts) defer freeProxyOptions(&cproxy) cheaders := C.git_strarray{} cheaders.count = C.size_t(len(headers)) cheaders.strings = makeCStringsFromStrings(headers) defer freeStrarray(&cheaders) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_remote_connect(o.ptr, C.git_direction(direction), &ccallbacks, &cproxy, &cheaders) runtime.KeepAlive(o) if ret != 0 { return MakeGitError(ret) } return nil } func (o *Remote) Disconnect() { runtime.LockOSThread() defer runtime.UnlockOSThread() C.git_remote_disconnect(o.ptr) runtime.KeepAlive(o) } func (o *Remote) Ls(filterRefs ...string) ([]RemoteHead, error) { var refs **C.git_remote_head var length C.size_t runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_remote_ls(&refs, &length, o.ptr) runtime.KeepAlive(o) if ret != 0 { return nil, MakeGitError(ret) } size := int(length) if size == 0 { return make([]RemoteHead, 0), nil } hdr := reflect.SliceHeader{ Data: uintptr(unsafe.Pointer(refs)), Len: size, Cap: size, } goSlice := *(*[]*C.git_remote_head)(unsafe.Pointer(&hdr)) var heads []RemoteHead for _, s := range goSlice { head := newRemoteHeadFromC(s) if len(filterRefs) > 0 { for _, r := range filterRefs { if strings.Contains(head.Name, r) { heads = append(heads, head) break } } } else { heads = append(heads, head) } } return heads, nil } func (o *Remote) Push(refspecs []string, opts *PushOptions) error { crefspecs := C.git_strarray{} crefspecs.count = C.size_t(len(refspecs)) crefspecs.strings = makeCStringsFromStrings(refspecs) defer freeStrarray(&crefspecs) coptions := (*C.git_push_options)(C.calloc(1, C.size_t(unsafe.Sizeof(C.git_push_options{})))) defer C.free(unsafe.Pointer(coptions)) populatePushOptions(coptions, opts) defer untrackCalbacksPayload(&coptions.callbacks) defer freeStrarray(&coptions.custom_headers) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_remote_push(o.ptr, &crefspecs, coptions) runtime.KeepAlive(o) if ret < 0 { return MakeGitError(ret) } return nil } func (o *Remote) PruneRefs() bool { return C.git_remote_prune_refs(o.ptr) > 0 } func (o *Remote) Prune(callbacks *RemoteCallbacks) error { var ccallbacks C.git_remote_callbacks populateRemoteCallbacks(&ccallbacks, callbacks) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_remote_prune(o.ptr, &ccallbacks) runtime.KeepAlive(o) if ret < 0 { return MakeGitError(ret) } return nil } git2go-31.4.3/remote_test.go000066400000000000000000000266311376323343000156720ustar00rootroot00000000000000package git import ( "bytes" "crypto/rand" "crypto/rsa" "fmt" "io" "net" "os" "os/exec" "strings" "sync" "testing" "time" "github.com/google/shlex" "golang.org/x/crypto/ssh" ) func TestListRemotes(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) _, err := repo.Remotes.Create("test", "git://foo/bar") checkFatal(t, err) expected := []string{ "test", } actual, err := repo.Remotes.List() checkFatal(t, err) compareStringList(t, expected, actual) } func assertHostname(cert *Certificate, valid bool, hostname string, t *testing.T) ErrorCode { if hostname != "github.com" { t.Fatal("Hostname does not match") return ErrorCodeUser } return ErrorCodeOK } func TestCertificateCheck(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) options := FetchOptions{ RemoteCallbacks: RemoteCallbacks{ CertificateCheckCallback: func(cert *Certificate, valid bool, hostname string) ErrorCode { return assertHostname(cert, valid, hostname, t) }, }, } err = remote.Fetch([]string{}, &options, "") checkFatal(t, err) } func TestRemoteConnect(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) err = remote.ConnectFetch(nil, nil, nil) checkFatal(t, err) } func TestRemoteLs(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) err = remote.ConnectFetch(nil, nil, nil) checkFatal(t, err) heads, err := remote.Ls() checkFatal(t, err) if len(heads) == 0 { t.Error("Expected remote heads") } } func TestRemoteLsFiltering(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) err = remote.ConnectFetch(nil, nil, nil) checkFatal(t, err) heads, err := remote.Ls("master") checkFatal(t, err) if len(heads) != 1 { t.Fatalf("Expected one head for master but I got %d", len(heads)) } if heads[0].Id == nil { t.Fatalf("Expected head to have an Id, but it's nil") } if heads[0].Name == "" { t.Fatalf("Expected head to have a name, but it's empty") } } func TestRemotePruneRefs(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) config, err := repo.Config() checkFatal(t, err) defer config.Free() err = config.SetBool("remote.origin.prune", true) checkFatal(t, err) _, err = repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) remote, err := repo.Remotes.Lookup("origin") checkFatal(t, err) if !remote.PruneRefs() { t.Fatal("Expected remote to be configured to prune references") } } func TestRemotePrune(t *testing.T) { t.Parallel() remoteRepo := createTestRepo(t) defer cleanupTestRepo(t, remoteRepo) head, _ := seedTestRepo(t, remoteRepo) commit, err := remoteRepo.LookupCommit(head) checkFatal(t, err) defer commit.Free() remoteRef, err := remoteRepo.CreateBranch("test-prune", commit, true) checkFatal(t, err) repo := createTestRepo(t) defer cleanupTestRepo(t, repo) config, err := repo.Config() checkFatal(t, err) defer config.Free() remoteUrl := fmt.Sprintf("file://%s", remoteRepo.Workdir()) remote, err := repo.Remotes.Create("origin", remoteUrl) checkFatal(t, err) err = remote.Fetch([]string{"test-prune"}, nil, "") checkFatal(t, err) _, err = repo.References.Create("refs/remotes/origin/test-prune", head, true, "remote reference") checkFatal(t, err) err = remoteRef.Delete() checkFatal(t, err) err = config.SetBool("remote.origin.prune", true) checkFatal(t, err) rr, err := repo.Remotes.Lookup("origin") checkFatal(t, err) err = rr.ConnectFetch(nil, nil, nil) checkFatal(t, err) err = rr.Prune(nil) checkFatal(t, err) _, err = repo.References.Lookup("refs/remotes/origin/test-prune") if err == nil { t.Fatal("Expected error getting a pruned reference") } } func newChannelPipe(t *testing.T, w io.Writer, wg *sync.WaitGroup) (*os.File, error) { pr, pw, err := os.Pipe() if err != nil { return nil, err } wg.Add(1) go func() { _, err := io.Copy(w, pr) if err != nil && err != io.EOF { t.Logf("Failed to copy: %v", err) } wg.Done() }() return pw, nil } func startSSHServer(t *testing.T, hostKey ssh.Signer, authorizedKeys []ssh.PublicKey) net.Listener { t.Helper() marshaledAuthorizedKeys := make([][]byte, len(authorizedKeys)) for i, authorizedKey := range authorizedKeys { marshaledAuthorizedKeys[i] = authorizedKey.Marshal() } config := &ssh.ServerConfig{ PublicKeyCallback: func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) { marshaledPubKey := pubKey.Marshal() for _, marshaledAuthorizedKey := range marshaledAuthorizedKeys { if bytes.Equal(marshaledPubKey, marshaledAuthorizedKey) { return &ssh.Permissions{ // Record the public key used for authentication. Extensions: map[string]string{ "pubkey-fp": ssh.FingerprintSHA256(pubKey), }, }, nil } } t.Logf("unknown public key for %q:\n\t%+v\n\t%+v\n", c.User(), pubKey.Marshal(), authorizedKeys) return nil, fmt.Errorf("unknown public key for %q", c.User()) }, } config.AddHostKey(hostKey) listener, err := net.Listen("tcp", "localhost:0") if err != nil { t.Fatalf("Failed to listen for connection: %v", err) } go func() { nConn, err := listener.Accept() if err != nil { if strings.Contains(err.Error(), "use of closed network connection") { return } t.Logf("Failed to accept incoming connection: %v", err) return } defer nConn.Close() conn, chans, reqs, err := ssh.NewServerConn(nConn, config) if err != nil { t.Logf("failed to handshake: %+v, %+v", conn, err) return } // The incoming Request channel must be serviced. go func() { for newRequest := range reqs { t.Logf("new request %v", newRequest) } }() // Service only the first channel request newChannel := <-chans defer func() { for newChannel := range chans { t.Logf("new channel %v", newChannel) newChannel.Reject(ssh.UnknownChannelType, "server closing") } }() // Channels have a type, depending on the application level // protocol intended. In the case of a shell, the type is // "session" and ServerShell may be used to present a simple // terminal interface. if newChannel.ChannelType() != "session" { newChannel.Reject(ssh.UnknownChannelType, "unknown channel type") return } channel, requests, err := newChannel.Accept() if err != nil { t.Logf("Could not accept channel: %v", err) return } defer channel.Close() // Sessions have out-of-band requests such as "shell", // "pty-req" and "env". Here we handle only the // "exec" request. req := <-requests if req.Type != "exec" { req.Reply(false, nil) return } // RFC 4254 Section 6.5. var payload struct { Command string } if err := ssh.Unmarshal(req.Payload, &payload); err != nil { t.Logf("invalid payload on channel %v: %v", channel, err) req.Reply(false, nil) return } args, err := shlex.Split(payload.Command) if err != nil { t.Logf("invalid command on channel %v: %v", channel, err) req.Reply(false, nil) return } if len(args) < 2 || (args[0] != "git-upload-pack" && args[0] != "git-receive-pack") { t.Logf("invalid command (%v) on channel %v: %v", args, channel, err) req.Reply(false, nil) return } req.Reply(true, nil) go func(in <-chan *ssh.Request) { for req := range in { t.Logf("draining request %v", req) } }(requests) // The first parameter is the (absolute) path of the repository. args[1] = "./testdata" + args[1] cmd := exec.Command(args[0], args[1:]...) cmd.Stdin = channel var wg sync.WaitGroup stdoutPipe, err := newChannelPipe(t, channel, &wg) if err != nil { t.Logf("Failed to create stdout pipe: %v", err) return } cmd.Stdout = stdoutPipe stderrPipe, err := newChannelPipe(t, channel.Stderr(), &wg) if err != nil { t.Logf("Failed to create stderr pipe: %v", err) return } cmd.Stderr = stderrPipe go func() { wg.Wait() channel.CloseWrite() }() err = cmd.Start() if err != nil { t.Logf("Failed to start %v: %v", args, err) return } // Once the process has started, we need to close the write end of the // pipes from this process so that we can know when the child has done // writing to it. stdoutPipe.Close() stderrPipe.Close() timer := time.AfterFunc(5*time.Second, func() { t.Log("process timed out, terminating") cmd.Process.Kill() }) defer timer.Stop() err = cmd.Wait() if err != nil { t.Logf("Failed to run %v: %v", args, err) return } }() return listener } func TestRemoteSSH(t *testing.T) { t.Parallel() pubKeyUsername := "testuser" hostPrivKey, err := rsa.GenerateKey(rand.Reader, 512) if err != nil { t.Fatalf("Failed to generate the host RSA private key: %v", err) } hostSigner, err := ssh.NewSignerFromKey(hostPrivKey) if err != nil { t.Fatalf("Failed to generate SSH hostSigner: %v", err) } privKey, err := rsa.GenerateKey(rand.Reader, 512) if err != nil { t.Fatalf("Failed to generate the user RSA private key: %v", err) } signer, err := ssh.NewSignerFromKey(privKey) if err != nil { t.Fatalf("Failed to generate SSH signer: %v", err) } // This is in the format "xx:xx:xx:...", so we remove the colons so that it // matches the fmt.Sprintf() below. // Note that not all libssh2 implementations support the SHA256 fingerprint, // so we use MD5 here for testing. publicKeyFingerprint := strings.Replace(ssh.FingerprintLegacyMD5(hostSigner.PublicKey()), ":", "", -1) listener := startSSHServer(t, hostSigner, []ssh.PublicKey{signer.PublicKey()}) defer listener.Close() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) certificateCheckCallbackCalled := false fetchOpts := FetchOptions{ RemoteCallbacks: RemoteCallbacks{ CertificateCheckCallback: func(cert *Certificate, valid bool, hostname string) ErrorCode { hostkeyFingerprint := fmt.Sprintf("%x", cert.Hostkey.HashMD5[:]) if hostkeyFingerprint != publicKeyFingerprint { t.Logf("server hostkey %q, want %q", hostkeyFingerprint, publicKeyFingerprint) return ErrorCodeAuth } certificateCheckCallbackCalled = true return ErrorCodeOK }, CredentialsCallback: func(url, username string, allowedTypes CredentialType) (*Credential, error) { if allowedTypes&(CredentialTypeSSHKey|CredentialTypeSSHCustom|CredentialTypeSSHMemory) != 0 { return NewCredentialSSHKeyFromSigner(pubKeyUsername, signer) } if (allowedTypes & CredentialTypeUsername) != 0 { return NewCredentialUsername(pubKeyUsername) } return nil, fmt.Errorf("unknown credential type %+v", allowedTypes) }, }, } remote, err := repo.Remotes.Create( "origin", fmt.Sprintf("ssh://%s/TestGitRepository", listener.Addr().String()), ) checkFatal(t, err) defer remote.Free() err = remote.Fetch(nil, &fetchOpts, "") checkFatal(t, err) if !certificateCheckCallbackCalled { t.Fatalf("CertificateCheckCallback was not called") } heads, err := remote.Ls() checkFatal(t, err) if len(heads) == 0 { t.Error("Expected remote heads") } } git2go-31.4.3/repository.go000066400000000000000000000373611376323343000155610ustar00rootroot00000000000000package git /* #include #include #include #include */ import "C" import ( "runtime" "unsafe" ) // Repository type Repository struct { ptr *C.git_repository // Remotes represents the collection of remotes and can be // used to add, remove and configure remotes for this // repository. Remotes RemoteCollection // Submodules represents the collection of submodules and can // be used to add, remove and configure submodules in this // repository. Submodules SubmoduleCollection // References represents the collection of references and can // be used to create, remove or update references for this repository. References ReferenceCollection // Notes represents the collection of notes and can be used to // read, write and delete notes from this repository. Notes NoteCollection // Tags represents the collection of tags and can be used to create, // list, iterate and remove tags in this repository. Tags TagsCollection // Stashes represents the collection of stashes and can be used to // save, apply and iterate over stash states in this repository. Stashes StashCollection } func newRepositoryFromC(ptr *C.git_repository) *Repository { repo := &Repository{ptr: ptr} repo.Remotes.repo = repo repo.Submodules.repo = repo repo.References.repo = repo repo.Notes.repo = repo repo.Tags.repo = repo repo.Stashes.repo = repo runtime.SetFinalizer(repo, (*Repository).Free) return repo } func OpenRepository(path string) (*Repository, error) { cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) runtime.LockOSThread() defer runtime.UnlockOSThread() var ptr *C.git_repository ret := C.git_repository_open(&ptr, cpath) if ret < 0 { return nil, MakeGitError(ret) } return newRepositoryFromC(ptr), nil } type RepositoryOpenFlag int const ( RepositoryOpenNoSearch RepositoryOpenFlag = C.GIT_REPOSITORY_OPEN_NO_SEARCH RepositoryOpenCrossFs RepositoryOpenFlag = C.GIT_REPOSITORY_OPEN_CROSS_FS RepositoryOpenBare RepositoryOpenFlag = C.GIT_REPOSITORY_OPEN_BARE RepositoryOpenFromEnv RepositoryOpenFlag = C.GIT_REPOSITORY_OPEN_FROM_ENV RepositoryOpenNoDotGit RepositoryOpenFlag = C.GIT_REPOSITORY_OPEN_NO_DOTGIT ) func OpenRepositoryExtended(path string, flags RepositoryOpenFlag, ceiling string) (*Repository, error) { cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) var cceiling *C.char = nil if len(ceiling) > 0 { cceiling = C.CString(ceiling) defer C.free(unsafe.Pointer(cceiling)) } runtime.LockOSThread() defer runtime.UnlockOSThread() var ptr *C.git_repository ret := C.git_repository_open_ext(&ptr, cpath, C.uint(flags), cceiling) if ret < 0 { return nil, MakeGitError(ret) } return newRepositoryFromC(ptr), nil } func InitRepository(path string, isbare bool) (*Repository, error) { cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) runtime.LockOSThread() defer runtime.UnlockOSThread() var ptr *C.git_repository ret := C.git_repository_init(&ptr, cpath, ucbool(isbare)) if ret < 0 { return nil, MakeGitError(ret) } return newRepositoryFromC(ptr), nil } func NewRepositoryWrapOdb(odb *Odb) (repo *Repository, err error) { runtime.LockOSThread() defer runtime.UnlockOSThread() var ptr *C.git_repository ret := C.git_repository_wrap_odb(&ptr, odb.ptr) runtime.KeepAlive(odb) if ret < 0 { return nil, MakeGitError(ret) } return newRepositoryFromC(ptr), nil } func (v *Repository) SetRefdb(refdb *Refdb) { C.git_repository_set_refdb(v.ptr, refdb.ptr) runtime.KeepAlive(v) } func (v *Repository) Free() { runtime.SetFinalizer(v, nil) C.git_repository_free(v.ptr) } func (v *Repository) Config() (*Config, error) { config := new(Config) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_repository_config(&config.ptr, v.ptr) runtime.KeepAlive(v) if ret < 0 { return nil, MakeGitError(ret) } runtime.SetFinalizer(config, (*Config).Free) return config, nil } func (v *Repository) Index() (*Index, error) { var ptr *C.git_index runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_repository_index(&ptr, v.ptr) if ret < 0 { return nil, MakeGitError(ret) } return newIndexFromC(ptr, v), nil } func (v *Repository) lookupType(id *Oid, t ObjectType) (*Object, error) { var ptr *C.git_object runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_object_lookup(&ptr, v.ptr, id.toC(), C.git_object_t(t)) runtime.KeepAlive(id) if ret < 0 { return nil, MakeGitError(ret) } return allocObject(ptr, v), nil } func (v *Repository) lookupPrefixType(id *Oid, prefix uint, t ObjectType) (*Object, error) { var ptr *C.git_object runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_object_lookup_prefix(&ptr, v.ptr, id.toC(), C.size_t(prefix), C.git_object_t(t)) runtime.KeepAlive(id) if ret < 0 { return nil, MakeGitError(ret) } return allocObject(ptr, v), nil } func (v *Repository) Lookup(id *Oid) (*Object, error) { return v.lookupType(id, ObjectAny) } // LookupPrefix looks up an object by its OID given a prefix of its identifier. func (v *Repository) LookupPrefix(id *Oid, prefix uint) (*Object, error) { return v.lookupPrefixType(id, prefix, ObjectAny) } func (v *Repository) LookupTree(id *Oid) (*Tree, error) { obj, err := v.lookupType(id, ObjectTree) if err != nil { return nil, err } defer obj.Free() return obj.AsTree() } // LookupPrefixTree looks up a tree by its OID given a prefix of its identifier. func (v *Repository) LookupPrefixTree(id *Oid, prefix uint) (*Tree, error) { obj, err := v.lookupPrefixType(id, prefix, ObjectTree) if err != nil { return nil, err } defer obj.Free() return obj.AsTree() } func (v *Repository) LookupCommit(id *Oid) (*Commit, error) { obj, err := v.lookupType(id, ObjectCommit) if err != nil { return nil, err } defer obj.Free() return obj.AsCommit() } // LookupPrefixCommit looks up a commit by its OID given a prefix of its identifier. func (v *Repository) LookupPrefixCommit(id *Oid, prefix uint) (*Commit, error) { obj, err := v.lookupPrefixType(id, prefix, ObjectCommit) if err != nil { return nil, err } defer obj.Free() return obj.AsCommit() } func (v *Repository) LookupBlob(id *Oid) (*Blob, error) { obj, err := v.lookupType(id, ObjectBlob) if err != nil { return nil, err } defer obj.Free() return obj.AsBlob() } // LookupPrefixBlob looks up a blob by its OID given a prefix of its identifier. func (v *Repository) LookupPrefixBlob(id *Oid, prefix uint) (*Blob, error) { obj, err := v.lookupPrefixType(id, prefix, ObjectBlob) if err != nil { return nil, err } defer obj.Free() return obj.AsBlob() } func (v *Repository) LookupTag(id *Oid) (*Tag, error) { obj, err := v.lookupType(id, ObjectTag) if err != nil { return nil, err } defer obj.Free() return obj.AsTag() } // LookupPrefixTag looks up a tag by its OID given a prefix of its identifier. func (v *Repository) LookupPrefixTag(id *Oid, prefix uint) (*Tag, error) { obj, err := v.lookupPrefixType(id, prefix, ObjectTag) if err != nil { return nil, err } defer obj.Free() return obj.AsTag() } func (v *Repository) Head() (*Reference, error) { var ptr *C.git_reference runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_repository_head(&ptr, v.ptr) if ecode < 0 { return nil, MakeGitError(ecode) } return newReferenceFromC(ptr, v), nil } func (v *Repository) SetHead(refname string) error { cname := C.CString(refname) defer C.free(unsafe.Pointer(cname)) runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_repository_set_head(v.ptr, cname) runtime.KeepAlive(v) if ecode != 0 { return MakeGitError(ecode) } return nil } func (v *Repository) SetHeadDetached(id *Oid) error { runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_repository_set_head_detached(v.ptr, id.toC()) runtime.KeepAlive(v) runtime.KeepAlive(id) if ecode != 0 { return MakeGitError(ecode) } return nil } func (v *Repository) IsHeadDetached() (bool, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_repository_head_detached(v.ptr) runtime.KeepAlive(v) if ret < 0 { return false, MakeGitError(ret) } return ret != 0, nil } func (v *Repository) IsHeadUnborn() (bool, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_repository_head_unborn(v.ptr) runtime.KeepAlive(v) if ret < 0 { return false, MakeGitError(ret) } return ret != 0, nil } func (v *Repository) IsEmpty() (bool, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_repository_is_empty(v.ptr) runtime.KeepAlive(v) if ret < 0 { return false, MakeGitError(ret) } return ret != 0, nil } func (v *Repository) IsShallow() (bool, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_repository_is_shallow(v.ptr) runtime.KeepAlive(v) if ret < 0 { return false, MakeGitError(ret) } return ret != 0, nil } func (v *Repository) Walk() (*RevWalk, error) { var walkPtr *C.git_revwalk runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_revwalk_new(&walkPtr, v.ptr) if ecode < 0 { return nil, MakeGitError(ecode) } return revWalkFromC(v, walkPtr), nil } func (v *Repository) CreateCommit( refname string, author, committer *Signature, message string, tree *Tree, parents ...*Commit) (*Oid, error) { oid := new(Oid) var cref *C.char if refname == "" { cref = nil } else { cref = C.CString(refname) defer C.free(unsafe.Pointer(cref)) } cmsg := C.CString(message) defer C.free(unsafe.Pointer(cmsg)) var cparents []*C.git_commit = nil var parentsarg **C.git_commit = nil nparents := len(parents) if nparents > 0 { cparents = make([]*C.git_commit, nparents) for i, v := range parents { cparents[i] = v.cast_ptr } parentsarg = &cparents[0] } authorSig, err := author.toC() if err != nil { return nil, err } defer C.git_signature_free(authorSig) committerSig, err := committer.toC() if err != nil { return nil, err } defer C.git_signature_free(committerSig) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_commit_create( oid.toC(), v.ptr, cref, authorSig, committerSig, nil, cmsg, tree.cast_ptr, C.size_t(nparents), parentsarg) runtime.KeepAlive(v) runtime.KeepAlive(oid) runtime.KeepAlive(parents) if ret < 0 { return nil, MakeGitError(ret) } return oid, nil } func (v *Repository) CreateCommitFromIds( refname string, author, committer *Signature, message string, tree *Oid, parents ...*Oid) (*Oid, error) { oid := new(Oid) var cref *C.char if refname == "" { cref = nil } else { cref = C.CString(refname) defer C.free(unsafe.Pointer(cref)) } cmsg := C.CString(message) defer C.free(unsafe.Pointer(cmsg)) var parentsarg **C.git_oid = nil nparents := len(parents) if nparents > 0 { // All this awful pointer arithmetic is needed to avoid passing a Go // pointer to Go pointer into C. Other methods (like CreateCommits) are // fine without this workaround because they are just passing Go pointers // to C pointers, but arrays-of-pointers-to-git_oid are a bit special since // both the array and the objects are allocated from Go. var emptyOidPtr *C.git_oid sizeofOidPtr := unsafe.Sizeof(emptyOidPtr) parentsarg = (**C.git_oid)(C.calloc(C.size_t(uintptr(nparents)), C.size_t(sizeofOidPtr))) defer C.free(unsafe.Pointer(parentsarg)) parentsptr := uintptr(unsafe.Pointer(parentsarg)) for _, v := range parents { *(**C.git_oid)(unsafe.Pointer(parentsptr)) = v.toC() parentsptr += sizeofOidPtr } } authorSig, err := author.toC() if err != nil { return nil, err } defer C.git_signature_free(authorSig) committerSig, err := committer.toC() if err != nil { return nil, err } defer C.git_signature_free(committerSig) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_commit_create_from_ids( oid.toC(), v.ptr, cref, authorSig, committerSig, nil, cmsg, tree.toC(), C.size_t(nparents), parentsarg) runtime.KeepAlive(v) runtime.KeepAlive(oid) runtime.KeepAlive(tree) runtime.KeepAlive(parents) if ret < 0 { return nil, MakeGitError(ret) } return oid, nil } func (v *Odb) Free() { runtime.SetFinalizer(v, nil) C.git_odb_free(v.ptr) } func (v *Refdb) Free() { runtime.SetFinalizer(v, nil) C.git_refdb_free(v.ptr) } func (v *Repository) Odb() (odb *Odb, err error) { odb = new(Odb) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_repository_odb(&odb.ptr, v.ptr) runtime.KeepAlive(v) if ret < 0 { return nil, MakeGitError(ret) } runtime.SetFinalizer(odb, (*Odb).Free) return odb, nil } func (repo *Repository) Path() string { s := C.GoString(C.git_repository_path(repo.ptr)) runtime.KeepAlive(repo) return s } func (repo *Repository) IsBare() bool { ret := C.git_repository_is_bare(repo.ptr) != 0 runtime.KeepAlive(repo) return ret } func (repo *Repository) Workdir() string { s := C.GoString(C.git_repository_workdir(repo.ptr)) runtime.KeepAlive(repo) return s } func (repo *Repository) SetWorkdir(workdir string, updateGitlink bool) error { cstr := C.CString(workdir) defer C.free(unsafe.Pointer(cstr)) runtime.LockOSThread() defer runtime.UnlockOSThread() if ret := C.git_repository_set_workdir(repo.ptr, cstr, cbool(updateGitlink)); ret < 0 { return MakeGitError(ret) } runtime.KeepAlive(repo) return nil } func (v *Repository) TreeBuilder() (*TreeBuilder, error) { bld := new(TreeBuilder) runtime.LockOSThread() defer runtime.UnlockOSThread() if ret := C.git_treebuilder_new(&bld.ptr, v.ptr, nil); ret < 0 { return nil, MakeGitError(ret) } runtime.SetFinalizer(bld, (*TreeBuilder).Free) bld.repo = v return bld, nil } func (v *Repository) TreeBuilderFromTree(tree *Tree) (*TreeBuilder, error) { bld := new(TreeBuilder) runtime.LockOSThread() defer runtime.UnlockOSThread() if ret := C.git_treebuilder_new(&bld.ptr, v.ptr, tree.cast_ptr); ret < 0 { return nil, MakeGitError(ret) } runtime.SetFinalizer(bld, (*TreeBuilder).Free) bld.repo = v return bld, nil } type RepositoryState int const ( RepositoryStateNone RepositoryState = C.GIT_REPOSITORY_STATE_NONE RepositoryStateMerge RepositoryState = C.GIT_REPOSITORY_STATE_MERGE RepositoryStateRevert RepositoryState = C.GIT_REPOSITORY_STATE_REVERT RepositoryStateCherrypick RepositoryState = C.GIT_REPOSITORY_STATE_CHERRYPICK RepositoryStateBisect RepositoryState = C.GIT_REPOSITORY_STATE_BISECT RepositoryStateRebase RepositoryState = C.GIT_REPOSITORY_STATE_REBASE RepositoryStateRebaseInteractive RepositoryState = C.GIT_REPOSITORY_STATE_REBASE_INTERACTIVE RepositoryStateRebaseMerge RepositoryState = C.GIT_REPOSITORY_STATE_REBASE_MERGE RepositoryStateApplyMailbox RepositoryState = C.GIT_REPOSITORY_STATE_APPLY_MAILBOX RepositoryStateApplyMailboxOrRebase RepositoryState = C.GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE ) func (r *Repository) State() RepositoryState { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := RepositoryState(C.git_repository_state(r.ptr)) runtime.KeepAlive(r) return ret } func (r *Repository) StateCleanup() error { runtime.LockOSThread() defer runtime.UnlockOSThread() cErr := C.git_repository_state_cleanup(r.ptr) runtime.KeepAlive(r) if cErr < 0 { return MakeGitError(cErr) } return nil } func (r *Repository) AddGitIgnoreRules(rules string) error { runtime.LockOSThread() defer runtime.UnlockOSThread() crules := C.CString(rules) defer C.free(unsafe.Pointer(crules)) ret := C.git_ignore_add_rule(r.ptr, crules) runtime.KeepAlive(r) if ret < 0 { return MakeGitError(ret) } return nil } func (r *Repository) ClearGitIgnoreRules() error { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_ignore_clear_internal_rules(r.ptr) runtime.KeepAlive(r) if ret < 0 { return MakeGitError(ret) } return nil } git2go-31.4.3/repository_test.go000066400000000000000000000017321376323343000166110ustar00rootroot00000000000000package git import ( "testing" "time" ) func TestCreateCommitFromIds(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) loc, err := time.LoadLocation("Europe/Berlin") checkFatal(t, err) sig := &Signature{ Name: "Rand Om Hacker", Email: "random@hacker.com", When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc), } idx, err := repo.Index() checkFatal(t, err) err = idx.AddByPath("README") checkFatal(t, err) err = idx.Write() checkFatal(t, err) treeId, err := idx.WriteTree() checkFatal(t, err) message := "This is a commit\n" tree, err := repo.LookupTree(treeId) checkFatal(t, err) expectedCommitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree) checkFatal(t, err) commitId, err := repo.CreateCommitFromIds("", sig, sig, message, treeId) checkFatal(t, err) if !expectedCommitId.Equal(commitId) { t.Errorf("mismatched commit ids, expected %v, got %v", expectedCommitId.String(), commitId.String()) } } git2go-31.4.3/reset.go000066400000000000000000000020701376323343000144510ustar00rootroot00000000000000package git /* #include */ import "C" import "runtime" type ResetType int const ( ResetSoft ResetType = C.GIT_RESET_SOFT ResetMixed ResetType = C.GIT_RESET_MIXED ResetHard ResetType = C.GIT_RESET_HARD ) func (r *Repository) ResetToCommit(commit *Commit, resetType ResetType, opts *CheckoutOptions) error { runtime.LockOSThread() defer runtime.UnlockOSThread() var err error cOpts := opts.toC(&err) defer freeCheckoutOptions(cOpts) ret := C.git_reset(r.ptr, commit.ptr, C.git_reset_t(resetType), cOpts) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } func (r *Repository) ResetDefaultToCommit(commit *Commit, pathspecs []string) error { cpathspecs := C.git_strarray{} cpathspecs.count = C.size_t(len(pathspecs)) cpathspecs.strings = makeCStringsFromStrings(pathspecs) defer freeStrarray(&cpathspecs) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_reset_default(r.ptr, commit.ptr, &cpathspecs) if ret < 0 { return MakeGitError(ret) } return nil } git2go-31.4.3/reset_test.go000066400000000000000000000024261376323343000155150ustar00rootroot00000000000000package git import ( "io/ioutil" "testing" ) func TestResetToCommit(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) seedTestRepo(t, repo) // create commit to reset to commitId, _ := updateReadme(t, repo, "testing reset") // create commit to reset from nextCommitId, _ := updateReadme(t, repo, "will be reset") // confirm that we wrote "will be reset" to the readme newBytes, err := ioutil.ReadFile(pathInRepo(repo, "README")) checkFatal(t, err) if string(newBytes) != "will be reset" { t.Fatalf("expected %s to equal 'will be reset'", string(newBytes)) } // confirm that the head of the repo is the next commit id head, err := repo.Head() checkFatal(t, err) if head.Target().String() != nextCommitId.String() { t.Fatalf( "expected to be at latest commit %s, but was %s", nextCommitId.String(), head.Target().String(), ) } commitToResetTo, err := repo.LookupCommit(commitId) checkFatal(t, err) repo.ResetToCommit(commitToResetTo, ResetHard, &CheckoutOptions{}) // check that the file now reads "testing reset" like it did before bytes, err := ioutil.ReadFile(pathInRepo(repo, "README")) checkFatal(t, err) if string(bytes) != "testing reset" { t.Fatalf("expected %s to equal 'testing reset'", string(bytes)) } } git2go-31.4.3/revert.go000066400000000000000000000051071376323343000146420ustar00rootroot00000000000000package git /* #include */ import "C" import ( "runtime" ) // RevertOptions contains options for performing a revert type RevertOptions struct { Mainline uint MergeOpts MergeOptions CheckoutOpts CheckoutOptions } func (opts *RevertOptions) toC(errorTarget *error) *C.git_revert_options { if opts == nil { return nil } return &C.git_revert_options{ version: C.GIT_REVERT_OPTIONS_VERSION, mainline: C.uint(opts.Mainline), merge_opts: *opts.MergeOpts.toC(), checkout_opts: *opts.CheckoutOpts.toC(errorTarget), } } func revertOptionsFromC(opts *C.git_revert_options) RevertOptions { return RevertOptions{ Mainline: uint(opts.mainline), MergeOpts: mergeOptionsFromC(&opts.merge_opts), CheckoutOpts: checkoutOptionsFromC(&opts.checkout_opts), } } func freeRevertOptions(opts *C.git_revert_options) { if opts != nil { return } freeMergeOptions(&opts.merge_opts) freeCheckoutOptions(&opts.checkout_opts) } // DefaultRevertOptions initialises a RevertOptions struct with default values func DefaultRevertOptions() (RevertOptions, error) { opts := C.git_revert_options{} runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_revert_options_init(&opts, C.GIT_REVERT_OPTIONS_VERSION) if ecode < 0 { return RevertOptions{}, MakeGitError(ecode) } defer freeRevertOptions(&opts) return revertOptionsFromC(&opts), nil } // Revert the provided commit leaving the index updated with the results of the revert func (r *Repository) Revert(commit *Commit, revertOptions *RevertOptions) error { runtime.LockOSThread() defer runtime.UnlockOSThread() var err error cOpts := revertOptions.toC(&err) defer freeRevertOptions(cOpts) ret := C.git_revert(r.ptr, commit.cast_ptr, cOpts) runtime.KeepAlive(r) runtime.KeepAlive(commit) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } // RevertCommit reverts the provided commit against "ourCommit" // The returned index contains the result of the revert and should be freed func (r *Repository) RevertCommit(revertCommit *Commit, ourCommit *Commit, mainline uint, mergeOptions *MergeOptions) (*Index, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() cOpts := mergeOptions.toC() defer freeMergeOptions(cOpts) var index *C.git_index ecode := C.git_revert_commit(&index, r.ptr, revertCommit.cast_ptr, ourCommit.cast_ptr, C.uint(mainline), cOpts) runtime.KeepAlive(revertCommit) runtime.KeepAlive(ourCommit) if ecode < 0 { return nil, MakeGitError(ecode) } return newIndexFromC(index, r), nil } git2go-31.4.3/revert_test.go000066400000000000000000000035011376323343000156750ustar00rootroot00000000000000package git import ( "testing" ) const ( expectedRevertedReadmeContents = "foo\n" ) func TestRevert(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) seedTestRepo(t, repo) commitID, _ := updateReadme(t, repo, content) commit, err := repo.LookupCommit(commitID) checkFatal(t, err) revertOptions, err := DefaultRevertOptions() checkFatal(t, err) err = repo.Revert(commit, &revertOptions) checkFatal(t, err) actualReadmeContents := readReadme(t, repo) if actualReadmeContents != expectedRevertedReadmeContents { t.Fatalf(`README has incorrect contents after revert. Expected: "%v", Actual: "%v"`, expectedRevertedReadmeContents, actualReadmeContents) } state := repo.State() if state != RepositoryStateRevert { t.Fatalf("Incorrect repository state. Expected: %v, Actual: %v", RepositoryStateRevert, state) } err = repo.StateCleanup() checkFatal(t, err) state = repo.State() if state != RepositoryStateNone { t.Fatalf("Incorrect repository state. Expected: %v, Actual: %v", RepositoryStateNone, state) } } func TestRevertCommit(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) seedTestRepo(t, repo) commitID, _ := updateReadme(t, repo, content) commit, err := repo.LookupCommit(commitID) checkFatal(t, err) revertOptions, err := DefaultRevertOptions() checkFatal(t, err) index, err := repo.RevertCommit(commit, commit, 0, &revertOptions.MergeOpts) checkFatal(t, err) defer index.Free() err = repo.CheckoutIndex(index, &revertOptions.CheckoutOpts) checkFatal(t, err) actualReadmeContents := readReadme(t, repo) if actualReadmeContents != expectedRevertedReadmeContents { t.Fatalf(`README has incorrect contents after revert. Expected: "%v", Actual: "%v"`, expectedRevertedReadmeContents, actualReadmeContents) } } git2go-31.4.3/revparse.go000066400000000000000000000040651376323343000151640ustar00rootroot00000000000000package git /* #include extern void _go_git_revspec_free(git_revspec *revspec); */ import "C" import ( "runtime" "unsafe" ) type RevparseFlag int const ( RevparseSingle RevparseFlag = C.GIT_REVPARSE_SINGLE RevparseRange RevparseFlag = C.GIT_REVPARSE_RANGE RevparseMergeBase RevparseFlag = C.GIT_REVPARSE_MERGE_BASE ) type Revspec struct { to *Object from *Object flags RevparseFlag } func (rs *Revspec) To() *Object { return rs.to } func (rs *Revspec) From() *Object { return rs.from } func (rs *Revspec) Flags() RevparseFlag { return rs.flags } func newRevspecFromC(ptr *C.git_revspec, repo *Repository) *Revspec { var to *Object var from *Object if ptr.to != nil { to = allocObject(ptr.to, repo) } if ptr.from != nil { from = allocObject(ptr.from, repo) } return &Revspec{ to: to, from: from, flags: RevparseFlag(ptr.flags), } } func (r *Repository) Revparse(spec string) (*Revspec, error) { cspec := C.CString(spec) defer C.free(unsafe.Pointer(cspec)) var crevspec C.git_revspec runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_revparse(&crevspec, r.ptr, cspec) if ecode != 0 { return nil, MakeGitError(ecode) } return newRevspecFromC(&crevspec, r), nil } func (v *Repository) RevparseSingle(spec string) (*Object, error) { cspec := C.CString(spec) defer C.free(unsafe.Pointer(cspec)) var ptr *C.git_object runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_revparse_single(&ptr, v.ptr, cspec) if ecode < 0 { return nil, MakeGitError(ecode) } return allocObject(ptr, v), nil } func (r *Repository) RevparseExt(spec string) (*Object, *Reference, error) { cspec := C.CString(spec) defer C.free(unsafe.Pointer(cspec)) var obj *C.git_object var ref *C.git_reference runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_revparse_ext(&obj, &ref, r.ptr, cspec) if ecode != 0 { return nil, nil, MakeGitError(ecode) } if ref == nil { return allocObject(obj, r), nil, nil } return allocObject(obj, r), newReferenceFromC(ref, r), nil } git2go-31.4.3/revparse_test.go000066400000000000000000000021611376323343000162160ustar00rootroot00000000000000package git import ( "testing" ) func TestRevparse(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) commitId, _ := seedTestRepo(t, repo) revSpec, err := repo.Revparse("HEAD") checkFatal(t, err) checkObject(t, revSpec.From(), commitId) } func TestRevparseSingle(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) commitId, _ := seedTestRepo(t, repo) obj, err := repo.RevparseSingle("HEAD") checkFatal(t, err) checkObject(t, obj, commitId) } func TestRevparseExt(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) _, treeId := seedTestRepo(t, repo) ref, err := repo.References.Create("refs/heads/master", treeId, true, "") checkFatal(t, err) obj, ref, err := repo.RevparseExt("master") checkFatal(t, err) checkObject(t, obj, treeId) if ref == nil { t.Fatalf("bad reference") } } func checkObject(t *testing.T, obj *Object, id *Oid) { if obj == nil { t.Fatalf("bad object") } if !obj.Id().Equal(id) { t.Fatalf("bad object, expected %s, got %s", id.String(), obj.Id().String()) } } git2go-31.4.3/script/000077500000000000000000000000001376323343000143055ustar00rootroot00000000000000git2go-31.4.3/script/build-libgit2-dynamic.sh000077500000000000000000000001051376323343000207130ustar00rootroot00000000000000#!/bin/sh set -e exec "$(dirname "$0")/build-libgit2.sh" --dynamic git2go-31.4.3/script/build-libgit2-static.sh000077500000000000000000000001041376323343000205550ustar00rootroot00000000000000#!/bin/sh set -e exec "$(dirname "$0")/build-libgit2.sh" --static git2go-31.4.3/script/build-libgit2.sh000077500000000000000000000033561376323343000173040ustar00rootroot00000000000000#!/bin/sh # Since CMake cannot build the static and dynamic libraries in the same # directory, this script helps build both static and dynamic versions of it and # have the common flags in one place instead of split between two places. set -e usage() { echo "Usage: $0 <--dynamic|--static> [--system]">&2 exit 1 } if [ "$#" -eq "0" ]; then usage fi ROOT=${ROOT-"$(cd "$(dirname "$0")/.." && echo "${PWD}")"} VENDORED_PATH=${VENDORED_PATH-"${ROOT}/vendor/libgit2"} BUILD_SYSTEM=OFF while [ $# -gt 0 ]; do case "$1" in --static) BUILD_PATH="${ROOT}/static-build" BUILD_SHARED_LIBS=OFF ;; --dynamic) BUILD_PATH="${ROOT}/dynamic-build" BUILD_SHARED_LIBS=ON ;; --system) BUILD_SYSTEM=ON ;; *) usage ;; esac shift done if [ -z "${BUILD_SHARED_LIBS}" ]; then usage fi if [ -n "${BUILD_LIBGIT_REF}" ]; then git -C "${VENDORED_PATH}" checkout "${BUILD_LIBGIT_REF}" trap "git submodule update --init" EXIT fi if [ "${BUILD_SYSTEM}" = "ON" ]; then BUILD_INSTALL_PREFIX=${SYSTEM_INSTALL_PREFIX-"/usr"} else BUILD_INSTALL_PREFIX="${BUILD_PATH}/install" mkdir -p "${BUILD_PATH}/install/lib" fi mkdir -p "${BUILD_PATH}/build" && cd "${BUILD_PATH}/build" && cmake -DTHREADSAFE=ON \ -DBUILD_CLAR=OFF \ -DBUILD_SHARED_LIBS"=${BUILD_SHARED_LIBS}" \ -DREGEX_BACKEND=builtin \ -DCMAKE_C_FLAGS=-fPIC \ -DCMAKE_BUILD_TYPE="RelWithDebInfo" \ -DCMAKE_INSTALL_PREFIX="${BUILD_INSTALL_PREFIX}" \ -DCMAKE_INSTALL_LIBDIR="lib" \ -DDEPRECATE_HARD=ON \ "${VENDORED_PATH}" if which make nproc >/dev/null && [ -f Makefile ]; then # Make the build parallel if make is available and cmake used Makefiles. exec make "-j$(nproc --all)" install else exec cmake --build . --target install fi git2go-31.4.3/script/check-MakeGitError-thread-lock.go000066400000000000000000000033011376323343000224320ustar00rootroot00000000000000// +build ignore package main import ( "bytes" "fmt" "go/ast" "go/build" "go/parser" "go/printer" "go/token" "log" "os" "path/filepath" "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, func(fi os.FileInfo) bool { return filepath.Ext(fi.Name()) == ".go" }, 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 } git2go-31.4.3/script/install-libgit2.sh000077500000000000000000000011301376323343000176370ustar00rootroot00000000000000#!/bin/sh # # Install libgit2 to git2go in dynamic mode on Travis # set -ex # We don't want to build libgit2 on the next branch, as we carry a # submodule with the exact version we support if [ "x$TRAVIS_BRANCH" = "xnext" ]; then exit 0 fi cd "${HOME}" LG2VER="0.24.0" wget -O libgit2-${LG2VER}.tar.gz https://github.com/libgit2/libgit2/archive/v${LG2VER}.tar.gz tar -xzvf libgit2-${LG2VER}.tar.gz cd libgit2-${LG2VER} && mkdir build && cd build cmake -DTHREADSAFE=ON -DBUILD_CLAR=OFF -DCMAKE_BUILD_TYPE="RelWithDebInfo" .. && make && sudo make install sudo ldconfig cd "${TRAVIS_BUILD_DIR}" git2go-31.4.3/settings.go000066400000000000000000000067221376323343000151770ustar00rootroot00000000000000package git /* #include int _go_git_opts_get_search_path(int level, git_buf *buf) { return git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, level, buf); } int _go_git_opts_set_search_path(int level, const char *path) { return git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, level, path); } int _go_git_opts_set_size_t(int opt, size_t val) { return git_libgit2_opts(opt, val); } int _go_git_opts_set_cache_object_limit(git_object_t type, size_t size) { return git_libgit2_opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, type, size); } int _go_git_opts_get_size_t(int opt, size_t *val) { return git_libgit2_opts(opt, val); } int _go_git_opts_get_size_t_size_t(int opt, size_t *val1, size_t *val2) { return git_libgit2_opts(opt, val1, val2); } */ import "C" import ( "runtime" "unsafe" ) func SearchPath(level ConfigLevel) (string, error) { var buf C.git_buf defer C.git_buf_dispose(&buf) runtime.LockOSThread() defer runtime.UnlockOSThread() err := C._go_git_opts_get_search_path(C.int(level), &buf) if err < 0 { return "", MakeGitError(err) } return C.GoString(buf.ptr), nil } func SetSearchPath(level ConfigLevel, path string) error { cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) runtime.LockOSThread() defer runtime.UnlockOSThread() err := C._go_git_opts_set_search_path(C.int(level), cpath) if err < 0 { return MakeGitError(err) } return nil } func MwindowSize() (int, error) { return getSizet(C.GIT_OPT_GET_MWINDOW_SIZE) } func SetMwindowSize(size int) error { return setSizet(C.GIT_OPT_SET_MWINDOW_SIZE, size) } func MwindowMappedLimit() (int, error) { return getSizet(C.GIT_OPT_GET_MWINDOW_MAPPED_LIMIT) } func SetMwindowMappedLimit(size int) error { return setSizet(C.GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, size) } func EnableCaching(enabled bool) error { if enabled { return setSizet(C.GIT_OPT_ENABLE_CACHING, 1) } else { return setSizet(C.GIT_OPT_ENABLE_CACHING, 0) } } func EnableStrictHashVerification(enabled bool) error { if enabled { return setSizet(C.GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 1) } else { return setSizet(C.GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0) } } func CachedMemory() (current int, allowed int, err error) { return getSizetSizet(C.GIT_OPT_GET_CACHED_MEMORY) } // deprecated: You should use `CachedMemory()` instead. func GetCachedMemory() (current int, allowed int, err error) { return CachedMemory() } func SetCacheMaxSize(maxSize int) error { return setSizet(C.GIT_OPT_SET_CACHE_MAX_SIZE, maxSize) } func SetCacheObjectLimit(objectType ObjectType, size int) error { runtime.LockOSThread() defer runtime.UnlockOSThread() err := C._go_git_opts_set_cache_object_limit(C.git_object_t(objectType), C.size_t(size)) if err < 0 { return MakeGitError(err) } return nil } func getSizet(opt C.int) (int, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() var val C.size_t err := C._go_git_opts_get_size_t(opt, &val) if err < 0 { return 0, MakeGitError(err) } return int(val), nil } func getSizetSizet(opt C.int) (int, int, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() var val1, val2 C.size_t err := C._go_git_opts_get_size_t_size_t(opt, &val1, &val2) if err < 0 { return 0, 0, MakeGitError(err) } return int(val1), int(val2), nil } func setSizet(opt C.int, val int) error { runtime.LockOSThread() defer runtime.UnlockOSThread() cval := C.size_t(val) err := C._go_git_opts_set_size_t(opt, cval) if err < 0 { return MakeGitError(err) } return nil } git2go-31.4.3/settings_test.go000066400000000000000000000031271376323343000162320ustar00rootroot00000000000000package git import ( "testing" ) type pathPair struct { Level ConfigLevel Path string } func TestSearchPath(t *testing.T) { paths := []pathPair{ pathPair{ConfigLevelSystem, "/tmp/system"}, pathPair{ConfigLevelGlobal, "/tmp/global"}, pathPair{ConfigLevelXDG, "/tmp/xdg"}, } for _, pair := range paths { err := SetSearchPath(pair.Level, pair.Path) checkFatal(t, err) actual, err := SearchPath(pair.Level) checkFatal(t, err) if pair.Path != actual { t.Fatal("Search paths don't match") } } } func TestMmapSizes(t *testing.T) { size := 42 * 1024 err := SetMwindowSize(size) checkFatal(t, err) actual, err := MwindowSize() if size != actual { t.Fatal("Sizes don't match") } err = SetMwindowMappedLimit(size) checkFatal(t, err) actual, err = MwindowMappedLimit() if size != actual { t.Fatal("Sizes don't match") } } func TestEnableCaching(t *testing.T) { err := EnableCaching(false) checkFatal(t, err) err = EnableCaching(true) checkFatal(t, err) } func TestEnableStrictHashVerification(t *testing.T) { err := EnableStrictHashVerification(false) checkFatal(t, err) err = EnableStrictHashVerification(true) checkFatal(t, err) } func TestCachedMemory(t *testing.T) { current, allowed, err := CachedMemory() checkFatal(t, err) if current < 0 { t.Fatal("current < 0") } if allowed < 0 { t.Fatal("allowed < 0") } } func TestSetCacheMaxSize(t *testing.T) { err := SetCacheMaxSize(0) checkFatal(t, err) err = SetCacheMaxSize(1024 * 1024) checkFatal(t, err) // revert to default 256MB err = SetCacheMaxSize(256 * 1024 * 1024) checkFatal(t, err) } git2go-31.4.3/signature.go000066400000000000000000000026751376323343000153430ustar00rootroot00000000000000package git /* #include */ import "C" import ( "runtime" "time" "unsafe" ) type Signature struct { Name string Email string When time.Time } func newSignatureFromC(sig *C.git_signature) *Signature { if sig == nil { return nil } // git stores minutes, go wants seconds loc := time.FixedZone("", int(sig.when.offset)*60) return &Signature{ C.GoString(sig.name), C.GoString(sig.email), time.Unix(int64(sig.when.time), 0).In(loc), } } // Offset returns the time zone offset of v.When in minutes, which is what git wants. func (v *Signature) Offset() int { _, offset := v.When.Zone() return offset / 60 } func (sig *Signature) toC() (*C.git_signature, error) { if sig == nil { return nil, nil } var out *C.git_signature runtime.LockOSThread() defer runtime.UnlockOSThread() name := C.CString(sig.Name) defer C.free(unsafe.Pointer(name)) email := C.CString(sig.Email) defer C.free(unsafe.Pointer(email)) ret := C.git_signature_new(&out, name, email, C.git_time_t(sig.When.Unix()), C.int(sig.Offset())) if ret < 0 { return nil, MakeGitError(ret) } return out, nil } func (repo *Repository) DefaultSignature() (*Signature, error) { var out *C.git_signature runtime.LockOSThread() defer runtime.UnlockOSThread() cErr := C.git_signature_default(&out, repo.ptr) runtime.KeepAlive(repo) if cErr < 0 { return nil, MakeGitError(cErr) } defer C.git_signature_free(out) return newSignatureFromC(out), nil } git2go-31.4.3/stash.go000066400000000000000000000251051376323343000144550ustar00rootroot00000000000000package git /* #include extern void _go_git_populate_stash_apply_callbacks(git_stash_apply_options *opts); extern int _go_git_stash_foreach(git_repository *repo, void *payload); */ import "C" import ( "runtime" "unsafe" ) // StashFlag are flags that affect the stash save operation. type StashFlag int const ( // StashDefault represents no option, default. StashDefault StashFlag = C.GIT_STASH_DEFAULT // StashKeepIndex leaves all changes already added to the // index intact in the working directory. StashKeepIndex StashFlag = C.GIT_STASH_KEEP_INDEX // StashIncludeUntracked means all untracked files are also // stashed and then cleaned up from the working directory. StashIncludeUntracked StashFlag = C.GIT_STASH_INCLUDE_UNTRACKED // StashIncludeIgnored means all ignored files are also // stashed and then cleaned up from the working directory. StashIncludeIgnored StashFlag = C.GIT_STASH_INCLUDE_IGNORED ) // StashCollection represents the possible operations that can be // performed on the collection of stashes for a repository. type StashCollection struct { repo *Repository } // Save saves the local modifications to a new stash. // // Stasher is the identity of the person performing the stashing. // Message is the optional description along with the stashed state. // Flags control the stashing process and are given as bitwise OR. func (c *StashCollection) Save( stasher *Signature, message string, flags StashFlag) (*Oid, error) { oid := new(Oid) stasherC, err := stasher.toC() if err != nil { return nil, err } defer C.git_signature_free(stasherC) messageC := C.CString(message) defer C.free(unsafe.Pointer(messageC)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_stash_save( oid.toC(), c.repo.ptr, stasherC, messageC, C.uint32_t(flags)) runtime.KeepAlive(c) if ret < 0 { return nil, MakeGitError(ret) } return oid, nil } // StashApplyFlag are flags that affect the stash apply operation. type StashApplyFlag int const ( // StashApplyDefault is the default. StashApplyDefault StashApplyFlag = C.GIT_STASH_APPLY_DEFAULT // StashApplyReinstateIndex will try to reinstate not only the // working tree's changes, but also the index's changes. StashApplyReinstateIndex StashApplyFlag = C.GIT_STASH_APPLY_REINSTATE_INDEX ) // StashApplyProgress are flags describing the progress of the apply operation. type StashApplyProgress int const ( // StashApplyProgressNone means loading the stashed data from the object store. StashApplyProgressNone StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_NONE // StashApplyProgressLoadingStash means the stored index is being analyzed. StashApplyProgressLoadingStash StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_LOADING_STASH // StashApplyProgressAnalyzeIndex means the stored index is being analyzed. StashApplyProgressAnalyzeIndex StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX // StashApplyProgressAnalyzeModified means the modified files are being analyzed. StashApplyProgressAnalyzeModified StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED // StashApplyProgressAnalyzeUntracked means the untracked and ignored files are being analyzed. StashApplyProgressAnalyzeUntracked StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED // StashApplyProgressCheckoutUntracked means the untracked files are being written to disk. StashApplyProgressCheckoutUntracked StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED // StashApplyProgressCheckoutModified means the modified files are being written to disk. StashApplyProgressCheckoutModified StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED // StashApplyProgressDone means the stash was applied successfully. StashApplyProgressDone StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_DONE ) // StashApplyProgressCallback is the apply operation notification callback. type StashApplyProgressCallback func(progress StashApplyProgress) error type stashApplyProgressCallbackData struct { callback StashApplyProgressCallback errorTarget *error } //export stashApplyProgressCallback func stashApplyProgressCallback(progress C.git_stash_apply_progress_t, handle unsafe.Pointer) C.int { payload := pointerHandles.Get(handle) data, ok := payload.(*stashApplyProgressCallbackData) if !ok { panic("could not retrieve data for handle") } if data == nil || data.callback == nil { return C.int(ErrorCodeOK) } err := data.callback(StashApplyProgress(progress)) if err != nil { *data.errorTarget = err return C.int(ErrorCodeUser) } return C.int(ErrorCodeOK) } // StashApplyOptions represents options to control the apply operation. type StashApplyOptions struct { Flags StashApplyFlag CheckoutOptions CheckoutOptions // options to use when writing files to the working directory ProgressCallback StashApplyProgressCallback // optional callback to notify the consumer of application progress } // DefaultStashApplyOptions initializes the structure with default values. func DefaultStashApplyOptions() (StashApplyOptions, error) { optsC := C.git_stash_apply_options{} runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_stash_apply_options_init(&optsC, C.GIT_STASH_APPLY_OPTIONS_VERSION) if ecode < 0 { return StashApplyOptions{}, MakeGitError(ecode) } return StashApplyOptions{ Flags: StashApplyFlag(optsC.flags), CheckoutOptions: checkoutOptionsFromC(&optsC.checkout_options), }, nil } func (opts *StashApplyOptions) toC(errorTarget *error) *C.git_stash_apply_options { if opts == nil { return nil } optsC := &C.git_stash_apply_options{ version: C.GIT_STASH_APPLY_OPTIONS_VERSION, flags: C.uint32_t(opts.Flags), } populateCheckoutOptions(&optsC.checkout_options, &opts.CheckoutOptions, errorTarget) if opts.ProgressCallback != nil { progressData := &stashApplyProgressCallbackData{ callback: opts.ProgressCallback, errorTarget: errorTarget, } C._go_git_populate_stash_apply_callbacks(optsC) optsC.progress_payload = pointerHandles.Track(progressData) } return optsC } func freeStashApplyOptions(optsC *C.git_stash_apply_options) { if optsC == nil { return } if optsC.progress_payload != nil { pointerHandles.Untrack(optsC.progress_payload) } freeCheckoutOptions(&optsC.checkout_options) } // Apply applies a single stashed state from the stash list. // // If local changes in the working directory conflict with changes in the // stash then ErrorCodeConflict will be returned. In this case, the index // will always remain unmodified and all files in the working directory will // remain unmodified. However, if you are restoring untracked files or // ignored files and there is a conflict when applying the modified files, // then those files will remain in the working directory. // // If passing the StashApplyReinstateIndex flag and there would be conflicts // when reinstating the index, the function will return ErrorCodeConflict // and both the working directory and index will be left unmodified. // // Note that a minimum checkout strategy of 'CheckoutSafe' is implied. // // 'index' is the position within the stash list. 0 points to the most // recent stashed state. // // Returns error code ErrorCodeNotFound if there's no stashed state for the given // index, error code ErrorCodeConflict if local changes in the working directory // conflict with changes in the stash, the user returned error from the // StashApplyProgressCallback, if any, or other error code. // // Error codes can be interogated with IsErrorCode(err, ErrorCodeNotFound). func (c *StashCollection) Apply(index int, opts StashApplyOptions) error { var err error optsC := opts.toC(&err) defer freeStashApplyOptions(optsC) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_stash_apply(c.repo.ptr, C.size_t(index), optsC) runtime.KeepAlive(c) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } // StashCallback is called per entry when interating over all // the stashed states. // // 'index' is the position of the current stash in the stash list, // 'message' is the message used when creating the stash and 'id' // is the commit id of the stash. type StashCallback func(index int, message string, id *Oid) error type stashCallbackData struct { callback StashCallback errorTarget *error } //export stashForeachCallback func stashForeachCallback(index C.size_t, message *C.char, id *C.git_oid, handle unsafe.Pointer) C.int { payload := pointerHandles.Get(handle) data, ok := payload.(*stashCallbackData) if !ok { panic("could not retrieve data for handle") } err := data.callback(int(index), C.GoString(message), newOidFromC(id)) if err != nil { *data.errorTarget = err return C.int(ErrorCodeUser) } return C.int(ErrorCodeOK) } // Foreach loops over all the stashed states and calls the callback // for each one. // // If callback returns an error, this will stop looping. func (c *StashCollection) Foreach(callback StashCallback) error { var err error data := stashCallbackData{ callback: callback, errorTarget: &err, } handle := pointerHandles.Track(&data) defer pointerHandles.Untrack(handle) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C._go_git_stash_foreach(c.repo.ptr, handle) runtime.KeepAlive(c) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } // Drop removes a single stashed state from the stash list. // // 'index' is the position within the stash list. 0 points // to the most recent stashed state. // // Returns error code ErrorCodeNotFound if there's no stashed // state for the given index. func (c *StashCollection) Drop(index int) error { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_stash_drop(c.repo.ptr, C.size_t(index)) runtime.KeepAlive(c) if ret < 0 { return MakeGitError(ret) } return nil } // Pop applies a single stashed state from the stash list // and removes it from the list if successful. // // 'index' is the position within the stash list. 0 points // to the most recent stashed state. // // 'opts' controls how stashes are applied. // // Returns error code ErrorCodeNotFound if there's no stashed // state for the given index. func (c *StashCollection) Pop(index int, opts StashApplyOptions) error { var err error optsC := opts.toC(&err) defer freeStashApplyOptions(optsC) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_stash_pop(c.repo.ptr, C.size_t(index), optsC) runtime.KeepAlive(c) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } git2go-31.4.3/stash_test.go000066400000000000000000000111021376323343000155040ustar00rootroot00000000000000package git import ( "fmt" "io/ioutil" "os" "path" "reflect" "runtime" "testing" "time" ) func TestStash(t *testing.T) { repo := createTestRepo(t) defer cleanupTestRepo(t, repo) prepareStashRepo(t, repo) sig := &Signature{ Name: "Rand Om Hacker", Email: "random@hacker.com", When: time.Now(), } stash1, err := repo.Stashes.Save(sig, "First stash", StashDefault) checkFatal(t, err) _, err = repo.LookupCommit(stash1) checkFatal(t, err) b, err := ioutil.ReadFile(pathInRepo(repo, "README")) checkFatal(t, err) if string(b) == "Update README goes to stash\n" { t.Errorf("README still contains the uncommitted changes") } if !fileExistsInRepo(repo, "untracked.txt") { t.Errorf("untracked.txt doesn't exist in the repo; should be untracked") } // Apply: default opts, err := DefaultStashApplyOptions() checkFatal(t, err) err = repo.Stashes.Apply(0, opts) checkFatal(t, err) b, err = ioutil.ReadFile(pathInRepo(repo, "README")) checkFatal(t, err) if string(b) != "Update README goes to stash\n" { t.Errorf("README changes aren't here") } // Apply: no stash for the given index err = repo.Stashes.Apply(1, opts) if !IsErrorCode(err, ErrorCodeNotFound) { t.Errorf("expecting GIT_ENOTFOUND error code %d, got %v", ErrorCodeNotFound, err) } // Apply: callback stopped opts.ProgressCallback = func(progress StashApplyProgress) error { if progress == StashApplyProgressCheckoutModified { return fmt.Errorf("Stop") } return nil } err = repo.Stashes.Apply(0, opts) if err.Error() != "Stop" { t.Errorf("expecting error 'Stop', got %v", err) } // Create second stash with ignored files os.MkdirAll(pathInRepo(repo, "tmp"), os.ModeDir|os.ModePerm) err = ioutil.WriteFile(pathInRepo(repo, "tmp/ignored.txt"), []byte("Ignore me\n"), 0644) checkFatal(t, err) stash2, err := repo.Stashes.Save(sig, "Second stash", StashIncludeIgnored) checkFatal(t, err) if fileExistsInRepo(repo, "tmp/ignored.txt") { t.Errorf("tmp/ignored.txt should not exist anymore in the work dir") } // Stash foreach expected := []stash{ {0, "On master: Second stash", stash2.String()}, {1, "On master: First stash", stash1.String()}, } checkStashes(t, repo, expected) // Stash pop opts, _ = DefaultStashApplyOptions() err = repo.Stashes.Pop(1, opts) checkFatal(t, err) b, err = ioutil.ReadFile(pathInRepo(repo, "README")) checkFatal(t, err) if string(b) != "Update README goes to stash\n" { t.Errorf("README changes aren't here") } expected = []stash{ {0, "On master: Second stash", stash2.String()}, } checkStashes(t, repo, expected) // Stash drop err = repo.Stashes.Drop(0) checkFatal(t, err) expected = []stash{} checkStashes(t, repo, expected) } type stash struct { index int msg string id string } func checkStashes(t *testing.T, repo *Repository, expected []stash) { var actual []stash repo.Stashes.Foreach(func(index int, msg string, id *Oid) error { stash := stash{index, msg, id.String()} if len(expected) > len(actual) { if s := expected[len(actual)]; s.id == "" { stash.id = "" // don't check id } } actual = append(actual, stash) return nil }) if len(expected) > 0 && !reflect.DeepEqual(expected, actual) { // The failure happens at wherever we were called, not here _, file, line, ok := runtime.Caller(1) if !ok { t.Fatalf("Unable to get caller") } t.Errorf("%v:%v: expecting %#v\ngot %#v", path.Base(file), line, expected, actual) } } func prepareStashRepo(t *testing.T, repo *Repository) { seedTestRepo(t, repo) err := ioutil.WriteFile(pathInRepo(repo, ".gitignore"), []byte("tmp\n"), 0644) checkFatal(t, err) sig := &Signature{ Name: "Rand Om Hacker", Email: "random@hacker.com", When: time.Now(), } idx, err := repo.Index() checkFatal(t, err) err = idx.AddByPath(".gitignore") checkFatal(t, err) treeID, err := idx.WriteTree() checkFatal(t, err) err = idx.Write() checkFatal(t, err) currentBranch, err := repo.Head() checkFatal(t, err) currentTip, err := repo.LookupCommit(currentBranch.Target()) checkFatal(t, err) message := "Add .gitignore\n" tree, err := repo.LookupTree(treeID) checkFatal(t, err) _, err = repo.CreateCommit("HEAD", sig, sig, message, tree, currentTip) checkFatal(t, err) err = ioutil.WriteFile(pathInRepo(repo, "README"), []byte("Update README goes to stash\n"), 0644) checkFatal(t, err) err = ioutil.WriteFile(pathInRepo(repo, "untracked.txt"), []byte("Hello, World\n"), 0644) checkFatal(t, err) } func fileExistsInRepo(repo *Repository, name string) bool { if _, err := os.Stat(pathInRepo(repo, name)); err != nil { return false } return true } git2go-31.4.3/status.go000066400000000000000000000124461376323343000146620ustar00rootroot00000000000000package git /* #include */ import "C" import ( "errors" "runtime" "unsafe" ) type Status int const ( StatusCurrent Status = C.GIT_STATUS_CURRENT StatusIndexNew Status = C.GIT_STATUS_INDEX_NEW StatusIndexModified Status = C.GIT_STATUS_INDEX_MODIFIED StatusIndexDeleted Status = C.GIT_STATUS_INDEX_DELETED StatusIndexRenamed Status = C.GIT_STATUS_INDEX_RENAMED StatusIndexTypeChange Status = C.GIT_STATUS_INDEX_TYPECHANGE StatusWtNew Status = C.GIT_STATUS_WT_NEW StatusWtModified Status = C.GIT_STATUS_WT_MODIFIED StatusWtDeleted Status = C.GIT_STATUS_WT_DELETED StatusWtTypeChange Status = C.GIT_STATUS_WT_TYPECHANGE StatusWtRenamed Status = C.GIT_STATUS_WT_RENAMED StatusIgnored Status = C.GIT_STATUS_IGNORED StatusConflicted Status = C.GIT_STATUS_CONFLICTED ) type StatusEntry struct { Status Status HeadToIndex DiffDelta IndexToWorkdir DiffDelta } func statusEntryFromC(statusEntry *C.git_status_entry) StatusEntry { var headToIndex DiffDelta = DiffDelta{} var indexToWorkdir DiffDelta = DiffDelta{} // Based on the libgit2 status example, head_to_index can be null in some cases if statusEntry.head_to_index != nil { headToIndex = diffDeltaFromC(statusEntry.head_to_index) } if statusEntry.index_to_workdir != nil { indexToWorkdir = diffDeltaFromC(statusEntry.index_to_workdir) } return StatusEntry{ Status: Status(statusEntry.status), HeadToIndex: headToIndex, IndexToWorkdir: indexToWorkdir, } } type StatusList struct { ptr *C.git_status_list r *Repository } func newStatusListFromC(ptr *C.git_status_list, r *Repository) *StatusList { if ptr == nil { return nil } statusList := &StatusList{ ptr: ptr, r: r, } runtime.SetFinalizer(statusList, (*StatusList).Free) return statusList } func (statusList *StatusList) Free() { if statusList.ptr == nil { return } runtime.SetFinalizer(statusList, nil) C.git_status_list_free(statusList.ptr) statusList.ptr = nil } func (statusList *StatusList) ByIndex(index int) (StatusEntry, error) { if statusList.ptr == nil { return StatusEntry{}, ErrInvalid } ptr := C.git_status_byindex(statusList.ptr, C.size_t(index)) if ptr == nil { return StatusEntry{}, errors.New("index out of Bounds") } entry := statusEntryFromC(ptr) runtime.KeepAlive(statusList) return entry, nil } func (statusList *StatusList) EntryCount() (int, error) { if statusList.ptr == nil { return -1, ErrInvalid } ret := int(C.git_status_list_entrycount(statusList.ptr)) runtime.KeepAlive(statusList) return ret, nil } type StatusOpt int const ( StatusOptIncludeUntracked StatusOpt = C.GIT_STATUS_OPT_INCLUDE_UNTRACKED StatusOptIncludeIgnored StatusOpt = C.GIT_STATUS_OPT_INCLUDE_IGNORED StatusOptIncludeUnmodified StatusOpt = C.GIT_STATUS_OPT_INCLUDE_UNMODIFIED StatusOptExcludeSubmodules StatusOpt = C.GIT_STATUS_OPT_EXCLUDE_SUBMODULES StatusOptRecurseUntrackedDirs StatusOpt = C.GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS StatusOptDisablePathspecMatch StatusOpt = C.GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH StatusOptRecurseIgnoredDirs StatusOpt = C.GIT_STATUS_OPT_RECURSE_IGNORED_DIRS StatusOptRenamesHeadToIndex StatusOpt = C.GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX StatusOptRenamesIndexToWorkdir StatusOpt = C.GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR StatusOptSortCaseSensitively StatusOpt = C.GIT_STATUS_OPT_SORT_CASE_SENSITIVELY StatusOptSortCaseInsensitively StatusOpt = C.GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY StatusOptRenamesFromRewrites StatusOpt = C.GIT_STATUS_OPT_RENAMES_FROM_REWRITES StatusOptNoRefresh StatusOpt = C.GIT_STATUS_OPT_NO_REFRESH StatusOptUpdateIndex StatusOpt = C.GIT_STATUS_OPT_UPDATE_INDEX ) type StatusShow int const ( StatusShowIndexAndWorkdir StatusShow = C.GIT_STATUS_SHOW_INDEX_AND_WORKDIR StatusShowIndexOnly StatusShow = C.GIT_STATUS_SHOW_INDEX_ONLY StatusShowWorkdirOnly StatusShow = C.GIT_STATUS_SHOW_WORKDIR_ONLY ) type StatusOptions struct { Show StatusShow Flags StatusOpt Pathspec []string } func (v *Repository) StatusList(opts *StatusOptions) (*StatusList, error) { var ptr *C.git_status_list var copts *C.git_status_options if opts != nil { cpathspec := C.git_strarray{} if opts.Pathspec != nil { cpathspec.count = C.size_t(len(opts.Pathspec)) cpathspec.strings = makeCStringsFromStrings(opts.Pathspec) defer freeStrarray(&cpathspec) } copts = &C.git_status_options{ version: C.GIT_STATUS_OPTIONS_VERSION, show: C.git_status_show_t(opts.Show), flags: C.uint(opts.Flags), pathspec: cpathspec, } } else { copts = &C.git_status_options{} ret := C.git_status_options_init(copts, C.GIT_STATUS_OPTIONS_VERSION) if ret < 0 { return nil, MakeGitError(ret) } } runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_status_list_new(&ptr, v.ptr, copts) if ret < 0 { return nil, MakeGitError(ret) } return newStatusListFromC(ptr, v), nil } func (v *Repository) StatusFile(path string) (Status, error) { var statusFlags C.uint cPath := C.CString(path) defer C.free(unsafe.Pointer(cPath)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_status_file(&statusFlags, v.ptr, cPath) runtime.KeepAlive(v) if ret < 0 { return 0, MakeGitError(ret) } return Status(statusFlags), nil } git2go-31.4.3/status_test.go000066400000000000000000000042171376323343000157160ustar00rootroot00000000000000package git import ( "io/ioutil" "path" "testing" ) func TestStatusFile(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) state := repo.State() if state != RepositoryStateNone { t.Fatal("Incorrect repository state: ", state) } err := ioutil.WriteFile(path.Join(path.Dir(repo.Workdir()), "hello.txt"), []byte("Hello, World"), 0644) checkFatal(t, err) status, err := repo.StatusFile("hello.txt") checkFatal(t, err) if status != StatusWtNew { t.Fatal("Incorrect status flags: ", status) } } func TestStatusList(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) // This commits the test repo README, so it doesn't show up in the status list and there's a head to compare to seedTestRepo(t, repo) err := ioutil.WriteFile(path.Join(path.Dir(repo.Workdir()), "hello.txt"), []byte("Hello, World"), 0644) checkFatal(t, err) opts := &StatusOptions{} opts.Show = StatusShowIndexAndWorkdir opts.Flags = StatusOptIncludeUntracked | StatusOptRenamesHeadToIndex | StatusOptSortCaseSensitively statusList, err := repo.StatusList(opts) checkFatal(t, err) entryCount, err := statusList.EntryCount() checkFatal(t, err) if entryCount != 1 { t.Fatal("Incorrect number of status entries: ", entryCount) } entry, err := statusList.ByIndex(0) checkFatal(t, err) if entry.Status != StatusWtNew { t.Fatal("Incorrect status flags: ", entry.Status) } if entry.IndexToWorkdir.NewFile.Path != "hello.txt" { t.Fatal("Incorrect entry path: ", entry.IndexToWorkdir.NewFile.Path) } } func TestStatusNothing(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) seedTestRepo(t, repo) opts := &StatusOptions{ Show: StatusShowIndexAndWorkdir, Flags: StatusOptIncludeUntracked | StatusOptRenamesHeadToIndex | StatusOptSortCaseSensitively, } statusList, err := repo.StatusList(opts) checkFatal(t, err) entryCount, err := statusList.EntryCount() checkFatal(t, err) if entryCount != 0 { t.Fatal("expected no statuses in empty repo") } _, err = statusList.ByIndex(0) if err == nil { t.Error("expected error getting status by index") } } git2go-31.4.3/submodule.go000066400000000000000000000242171376323343000153350ustar00rootroot00000000000000package git /* #include extern int _go_git_visit_submodule(git_repository *repo, void *fct); */ import "C" import ( "errors" "runtime" "unsafe" ) // SubmoduleUpdateOptions type SubmoduleUpdateOptions struct { *CheckoutOpts *FetchOptions } // Submodule type Submodule struct { ptr *C.git_submodule r *Repository } func newSubmoduleFromC(ptr *C.git_submodule, r *Repository) *Submodule { s := &Submodule{ptr: ptr, r: r} runtime.SetFinalizer(s, (*Submodule).Free) return s } func (sub *Submodule) Free() { runtime.SetFinalizer(sub, nil) C.git_submodule_free(sub.ptr) } type SubmoduleUpdate int const ( 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 const ( SubmoduleIgnoreNone SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_NONE SubmoduleIgnoreUntracked SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_UNTRACKED SubmoduleIgnoreDirty SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_DIRTY SubmoduleIgnoreAll SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_ALL ) type SubmoduleStatus int const ( SubmoduleStatusInHead SubmoduleStatus = C.GIT_SUBMODULE_STATUS_IN_HEAD SubmoduleStatusInIndex SubmoduleStatus = C.GIT_SUBMODULE_STATUS_IN_INDEX SubmoduleStatusInConfig SubmoduleStatus = C.GIT_SUBMODULE_STATUS_IN_CONFIG SubmoduleStatusInWd SubmoduleStatus = C.GIT_SUBMODULE_STATUS_IN_WD SubmoduleStatusIndexAdded SubmoduleStatus = C.GIT_SUBMODULE_STATUS_INDEX_ADDED SubmoduleStatusIndexDeleted SubmoduleStatus = C.GIT_SUBMODULE_STATUS_INDEX_DELETED SubmoduleStatusIndexModified SubmoduleStatus = C.GIT_SUBMODULE_STATUS_INDEX_MODIFIED SubmoduleStatusWdUninitialized SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_UNINITIALIZED SubmoduleStatusWdAdded SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_ADDED SubmoduleStatusWdDeleted SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_DELETED SubmoduleStatusWdModified SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_MODIFIED SubmoduleStatusWdIndexModified SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED SubmoduleStatusWdWdModified SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_WD_MODIFIED SubmoduleStatusWdUntracked SubmoduleStatus = C.GIT_SUBMODULE_STATUS_WD_UNTRACKED ) type SubmoduleRecurse int const ( SubmoduleRecurseNo SubmoduleRecurse = C.GIT_SUBMODULE_RECURSE_NO SubmoduleRecurseYes SubmoduleRecurse = C.GIT_SUBMODULE_RECURSE_YES SubmoduleRecurseOndemand SubmoduleRecurse = C.GIT_SUBMODULE_RECURSE_ONDEMAND ) type SubmoduleCollection struct { repo *Repository } func SubmoduleStatusIsUnmodified(status int) bool { o := SubmoduleStatus(status) & ^(SubmoduleStatusInHead | SubmoduleStatusInIndex | SubmoduleStatusInConfig | SubmoduleStatusInWd) return o == 0 } func (c *SubmoduleCollection) Lookup(name string) (*Submodule, error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) var ptr *C.git_submodule runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_submodule_lookup(&ptr, c.repo.ptr, cname) if ret < 0 { return nil, MakeGitError(ret) } return newSubmoduleFromC(ptr, c.repo), nil } // SubmoduleCallback is a function that is called for every submodule found in SubmoduleCollection.Foreach. type SubmoduleCallback func(sub *Submodule, name string) int type submoduleCallbackData struct { callback SubmoduleCallback errorTarget *error } //export submoduleCallback func submoduleCallback(csub unsafe.Pointer, name *C.char, handle unsafe.Pointer) C.int { sub := &Submodule{(*C.git_submodule)(csub), nil} data, ok := pointerHandles.Get(handle).(submoduleCallbackData) if !ok { panic("invalid submodule visitor callback") } ret := data.callback(sub, C.GoString(name)) if ret < 0 { *data.errorTarget = errors.New(ErrorCode(ret).String()) return C.int(ErrorCodeUser) } return C.int(ErrorCodeOK) } func (c *SubmoduleCollection) Foreach(callback SubmoduleCallback) error { var err error data := submoduleCallbackData{ callback: callback, errorTarget: &err, } runtime.LockOSThread() defer runtime.UnlockOSThread() handle := pointerHandles.Track(data) defer pointerHandles.Untrack(handle) ret := C._go_git_visit_submodule(c.repo.ptr, handle) runtime.KeepAlive(c) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } func (c *SubmoduleCollection) Add(url, path string, use_git_link bool) (*Submodule, error) { curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) runtime.LockOSThread() defer runtime.UnlockOSThread() var ptr *C.git_submodule ret := C.git_submodule_add_setup(&ptr, c.repo.ptr, curl, cpath, cbool(use_git_link)) if ret < 0 { return nil, MakeGitError(ret) } return newSubmoduleFromC(ptr, c.repo), nil } func (sub *Submodule) FinalizeAdd() error { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_submodule_add_finalize(sub.ptr) runtime.KeepAlive(sub) if ret < 0 { return MakeGitError(ret) } return nil } func (sub *Submodule) AddToIndex(write_index bool) error { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_submodule_add_to_index(sub.ptr, cbool(write_index)) runtime.KeepAlive(sub) if ret < 0 { return MakeGitError(ret) } return nil } func (sub *Submodule) Name() string { n := C.GoString(C.git_submodule_name(sub.ptr)) runtime.KeepAlive(sub) return n } func (sub *Submodule) Path() string { n := C.GoString(C.git_submodule_path(sub.ptr)) runtime.KeepAlive(sub) return n } func (sub *Submodule) Url() string { n := C.GoString(C.git_submodule_url(sub.ptr)) runtime.KeepAlive(sub) return n } func (c *SubmoduleCollection) SetUrl(submodule, url string) error { csubmodule := C.CString(submodule) defer C.free(unsafe.Pointer(csubmodule)) curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_submodule_set_url(c.repo.ptr, csubmodule, curl) runtime.KeepAlive(c) if ret < 0 { return MakeGitError(ret) } return nil } func (sub *Submodule) IndexId() *Oid { var id *Oid idx := C.git_submodule_index_id(sub.ptr) if idx != nil { id = newOidFromC(idx) } runtime.KeepAlive(sub) return id } func (sub *Submodule) HeadId() *Oid { var id *Oid idx := C.git_submodule_head_id(sub.ptr) if idx != nil { id = newOidFromC(idx) } runtime.KeepAlive(sub) return id } func (sub *Submodule) WdId() *Oid { var id *Oid idx := C.git_submodule_wd_id(sub.ptr) if idx != nil { id = newOidFromC(idx) } runtime.KeepAlive(sub) return id } func (sub *Submodule) Ignore() SubmoduleIgnore { o := C.git_submodule_ignore(sub.ptr) runtime.KeepAlive(sub) return SubmoduleIgnore(o) } func (c *SubmoduleCollection) SetIgnore(submodule string, ignore SubmoduleIgnore) error { csubmodule := C.CString(submodule) defer C.free(unsafe.Pointer(csubmodule)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_submodule_set_ignore(c.repo.ptr, csubmodule, C.git_submodule_ignore_t(ignore)) runtime.KeepAlive(c) if ret < 0 { return MakeGitError(ret) } return nil } func (sub *Submodule) UpdateStrategy() SubmoduleUpdate { o := C.git_submodule_update_strategy(sub.ptr) runtime.KeepAlive(sub) return SubmoduleUpdate(o) } func (c *SubmoduleCollection) SetUpdate(submodule string, update SubmoduleUpdate) error { csubmodule := C.CString(submodule) defer C.free(unsafe.Pointer(csubmodule)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_submodule_set_update(c.repo.ptr, csubmodule, C.git_submodule_update_t(update)) runtime.KeepAlive(c) if ret < 0 { return MakeGitError(ret) } return nil } func (sub *Submodule) FetchRecurseSubmodules() SubmoduleRecurse { return SubmoduleRecurse(C.git_submodule_fetch_recurse_submodules(sub.ptr)) } func (c *SubmoduleCollection) SetFetchRecurseSubmodules(submodule string, recurse SubmoduleRecurse) error { csubmodule := C.CString(submodule) defer C.free(unsafe.Pointer(csubmodule)) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_submodule_set_fetch_recurse_submodules(c.repo.ptr, csubmodule, C.git_submodule_recurse_t(recurse)) runtime.KeepAlive(c) if ret < 0 { return MakeGitError(C.int(ret)) } return nil } func (sub *Submodule) Init(overwrite bool) error { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_submodule_init(sub.ptr, cbool(overwrite)) runtime.KeepAlive(sub) if ret < 0 { return MakeGitError(ret) } return nil } func (sub *Submodule) Sync() error { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_submodule_sync(sub.ptr) runtime.KeepAlive(sub) if ret < 0 { return MakeGitError(ret) } return nil } func (sub *Submodule) Open() (*Repository, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() var ptr *C.git_repository ret := C.git_submodule_open(&ptr, sub.ptr) runtime.KeepAlive(sub) if ret < 0 { return nil, MakeGitError(ret) } return newRepositoryFromC(ptr), nil } func (sub *Submodule) Update(init bool, opts *SubmoduleUpdateOptions) error { var err error cOpts := populateSubmoduleUpdateOptions(&C.git_submodule_update_options{}, opts, &err) defer freeSubmoduleUpdateOptions(cOpts) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_submodule_update(sub.ptr, cbool(init), cOpts) runtime.KeepAlive(sub) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } func populateSubmoduleUpdateOptions(ptr *C.git_submodule_update_options, opts *SubmoduleUpdateOptions, errorTarget *error) *C.git_submodule_update_options { C.git_submodule_update_options_init(ptr, C.GIT_SUBMODULE_UPDATE_OPTIONS_VERSION) if opts == nil { return nil } populateCheckoutOptions(&ptr.checkout_opts, opts.CheckoutOpts, errorTarget) populateFetchOptions(&ptr.fetch_opts, opts.FetchOptions) return ptr } func freeSubmoduleUpdateOptions(ptr *C.git_submodule_update_options) { if ptr == nil { return } freeCheckoutOptions(&ptr.checkout_opts) } git2go-31.4.3/submodule_test.go000066400000000000000000000007331376323343000163710ustar00rootroot00000000000000package git import ( "testing" ) func TestSubmoduleForeach(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) seedTestRepo(t, repo) _, err := repo.Submodules.Add("http://example.org/submodule", "submodule", true) checkFatal(t, err) i := 0 err = repo.Submodules.Foreach(func(sub *Submodule, name string) int { i++ return 0 }) checkFatal(t, err) if i != 1 { t.Fatalf("expected one submodule found but got %d", i) } } git2go-31.4.3/tag.go000066400000000000000000000132111376323343000141010ustar00rootroot00000000000000package git /* #include extern int _go_git_tag_foreach(git_repository *repo, void *payload); */ import "C" import ( "runtime" "unsafe" ) // Tag type Tag struct { Object cast_ptr *C.git_tag } func (t *Tag) AsObject() *Object { return &t.Object } func (t *Tag) Message() string { ret := C.GoString(C.git_tag_message(t.cast_ptr)) runtime.KeepAlive(t) return ret } func (t *Tag) Name() string { ret := C.GoString(C.git_tag_name(t.cast_ptr)) runtime.KeepAlive(t) return ret } func (t *Tag) Tagger() *Signature { cast_ptr := C.git_tag_tagger(t.cast_ptr) ret := newSignatureFromC(cast_ptr) runtime.KeepAlive(t) return ret } func (t *Tag) Target() *Object { var ptr *C.git_object ret := C.git_tag_target(&ptr, t.cast_ptr) runtime.KeepAlive(t) if ret != 0 { return nil } return allocObject(ptr, t.repo) } func (t *Tag) TargetId() *Oid { ret := newOidFromC(C.git_tag_target_id(t.cast_ptr)) runtime.KeepAlive(t) return ret } func (t *Tag) TargetType() ObjectType { ret := ObjectType(C.git_tag_target_type(t.cast_ptr)) runtime.KeepAlive(t) return ret } type TagsCollection struct { repo *Repository } func (c *TagsCollection) Create(name string, obj Objecter, tagger *Signature, message string) (*Oid, error) { oid := new(Oid) cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) cmessage := C.CString(message) defer C.free(unsafe.Pointer(cmessage)) taggerSig, err := tagger.toC() if err != nil { return nil, err } defer C.git_signature_free(taggerSig) runtime.LockOSThread() defer runtime.UnlockOSThread() o := obj.AsObject() ret := C.git_tag_create(oid.toC(), c.repo.ptr, cname, o.ptr, taggerSig, cmessage, 0) runtime.KeepAlive(c) runtime.KeepAlive(obj) if ret < 0 { return nil, MakeGitError(ret) } return oid, nil } func (c *TagsCollection) Remove(name string) error { runtime.LockOSThread() defer runtime.UnlockOSThread() cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) ret := C.git_tag_delete(c.repo.ptr, cname) runtime.KeepAlive(c) if ret < 0 { return MakeGitError(ret) } return nil } // CreateLightweight creates a new lightweight tag pointing to an object // and returns the id of the target object. // // The name of the tag is validated for consistency (see git_tag_create() for the rules // https://libgit2.github.com/libgit2/#HEAD/group/tag/git_tag_create) and should // not conflict with an already existing tag name. // // If force is true and a reference already exists with the given name, it'll be replaced. // // The created tag is a simple reference and can be queried using // repo.References.Lookup("refs/tags/"). The name of the tag (eg "v1.0.0") // is queried with ref.Shorthand(). func (c *TagsCollection) CreateLightweight(name string, obj Objecter, force bool) (*Oid, error) { oid := new(Oid) cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) runtime.LockOSThread() defer runtime.UnlockOSThread() o := obj.AsObject() err := C.git_tag_create_lightweight(oid.toC(), c.repo.ptr, cname, o.ptr, cbool(force)) runtime.KeepAlive(c) runtime.KeepAlive(obj) if err < 0 { return nil, MakeGitError(err) } return oid, nil } // List returns the names of all the tags in the repository, // eg: ["v1.0.1", "v2.0.0"]. func (c *TagsCollection) List() ([]string, error) { var strC C.git_strarray runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_tag_list(&strC, c.repo.ptr) runtime.KeepAlive(c) if ecode < 0 { return nil, MakeGitError(ecode) } defer C.git_strarray_dispose(&strC) tags := makeStringsFromCStrings(strC.strings, int(strC.count)) return tags, nil } // ListWithMatch returns the names of all the tags in the repository // that match a given pattern. // // The pattern is a standard fnmatch(3) pattern http://man7.org/linux/man-pages/man3/fnmatch.3.html func (c *TagsCollection) ListWithMatch(pattern string) ([]string, error) { var strC C.git_strarray patternC := C.CString(pattern) defer C.free(unsafe.Pointer(patternC)) runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_tag_list_match(&strC, patternC, c.repo.ptr) runtime.KeepAlive(c) if ecode < 0 { return nil, MakeGitError(ecode) } defer C.git_strarray_dispose(&strC) tags := makeStringsFromCStrings(strC.strings, int(strC.count)) return tags, nil } // TagForeachCallback is called for each tag in the repository. // // The name is the full ref name eg: "refs/tags/v1.0.0". // // Note that the callback is called for lightweight tags as well, // so repo.LookupTag() will return an error for these tags. Use // repo.References.Lookup() instead. type TagForeachCallback func(name string, id *Oid) error type tagForeachCallbackData struct { callback TagForeachCallback errorTarget *error } //export tagForeachCallback func tagForeachCallback(name *C.char, id *C.git_oid, handle unsafe.Pointer) C.int { payload := pointerHandles.Get(handle) data, ok := payload.(*tagForeachCallbackData) if !ok { panic("could not retrieve tag foreach CB handle") } err := data.callback(C.GoString(name), newOidFromC(id)) if err != nil { *data.errorTarget = err return C.int(ErrorCodeUser) } return C.int(ErrorCodeOK) } // Foreach calls the callback for each tag in the repository. func (c *TagsCollection) Foreach(callback TagForeachCallback) error { var err error data := tagForeachCallbackData{ callback: callback, errorTarget: &err, } handle := pointerHandles.Track(&data) defer pointerHandles.Untrack(handle) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C._go_git_tag_foreach(c.repo.ptr, handle) runtime.KeepAlive(c) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } git2go-31.4.3/tag_test.go000066400000000000000000000110611376323343000151410ustar00rootroot00000000000000package git import ( "errors" "testing" "time" ) func TestCreateTag(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) commitId, _ := seedTestRepo(t, repo) commit, err := repo.LookupCommit(commitId) checkFatal(t, err) tagId := createTestTag(t, repo, commit) tag, err := repo.LookupTag(tagId) checkFatal(t, err) compareStrings(t, "v0.0.0", tag.Name()) compareStrings(t, "This is a tag", tag.Message()) compareStrings(t, commitId.String(), tag.TargetId().String()) } func TestCreateTagLightweight(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) commitID, _ := seedTestRepo(t, repo) commit, err := repo.LookupCommit(commitID) checkFatal(t, err) tagID, err := repo.Tags.CreateLightweight("v0.1.0", commit, false) checkFatal(t, err) _, err = repo.Tags.CreateLightweight("v0.1.0", commit, true) checkFatal(t, err) ref, err := repo.References.Lookup("refs/tags/v0.1.0") checkFatal(t, err) compareStrings(t, "refs/tags/v0.1.0", ref.Name()) compareStrings(t, "v0.1.0", ref.Shorthand()) compareStrings(t, tagID.String(), commitID.String()) compareStrings(t, commitID.String(), ref.Target().String()) } func TestListTags(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) commitID, _ := seedTestRepo(t, repo) commit, err := repo.LookupCommit(commitID) checkFatal(t, err) createTag(t, repo, commit, "v1.0.1", "Release v1.0.1") commitID, _ = updateReadme(t, repo, "Release version 2") commit, err = repo.LookupCommit(commitID) checkFatal(t, err) createTag(t, repo, commit, "v2.0.0", "Release v2.0.0") expected := []string{ "v1.0.1", "v2.0.0", } actual, err := repo.Tags.List() checkFatal(t, err) compareStringList(t, expected, actual) } func TestListTagsWithMatch(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) commitID, _ := seedTestRepo(t, repo) commit, err := repo.LookupCommit(commitID) checkFatal(t, err) createTag(t, repo, commit, "v1.0.1", "Release v1.0.1") commitID, _ = updateReadme(t, repo, "Release version 2") commit, err = repo.LookupCommit(commitID) checkFatal(t, err) createTag(t, repo, commit, "v2.0.0", "Release v2.0.0") expected := []string{ "v2.0.0", } actual, err := repo.Tags.ListWithMatch("v2*") checkFatal(t, err) compareStringList(t, expected, actual) expected = []string{ "v1.0.1", } actual, err = repo.Tags.ListWithMatch("v1*") checkFatal(t, err) compareStringList(t, expected, actual) } func TestTagForeach(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) commitID, _ := seedTestRepo(t, repo) commit, err := repo.LookupCommit(commitID) checkFatal(t, err) tag1 := createTag(t, repo, commit, "v1.0.1", "Release v1.0.1") commitID, _ = updateReadme(t, repo, "Release version 2") commit, err = repo.LookupCommit(commitID) checkFatal(t, err) tag2 := createTag(t, repo, commit, "v2.0.0", "Release v2.0.0") expectedNames := []string{ "refs/tags/v1.0.1", "refs/tags/v2.0.0", } actualNames := []string{} expectedOids := []string{ tag1.String(), tag2.String(), } actualOids := []string{} err = repo.Tags.Foreach(func(name string, id *Oid) error { actualNames = append(actualNames, name) actualOids = append(actualOids, id.String()) return nil }) checkFatal(t, err) compareStringList(t, expectedNames, actualNames) compareStringList(t, expectedOids, actualOids) fakeErr := errors.New("fake error") err = repo.Tags.Foreach(func(name string, id *Oid) error { return fakeErr }) if err != fakeErr { t.Fatalf("Tags.Foreach() did not return the expected error, got %v", err) } } func compareStrings(t *testing.T, expected, value string) { if value != expected { t.Fatalf("expected '%v', actual '%v'", expected, value) } } func createTestTag(t *testing.T, repo *Repository, commit *Commit) *Oid { loc, err := time.LoadLocation("Europe/Berlin") checkFatal(t, err) sig := &Signature{ Name: "Rand Om Hacker", Email: "random@hacker.com", When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc), } tagId, err := repo.Tags.Create("v0.0.0", commit, sig, "This is a tag") checkFatal(t, err) return tagId } func createTag(t *testing.T, repo *Repository, commit *Commit, name, message string) *Oid { loc, err := time.LoadLocation("Europe/Bucharest") checkFatal(t, err) sig := &Signature{ Name: "Rand Om Hacker", Email: "random@hacker.com", When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc), } tagId, err := repo.Tags.Create(name, commit, sig, message) checkFatal(t, err) return tagId } git2go-31.4.3/testdata/000077500000000000000000000000001376323343000146125ustar00rootroot00000000000000git2go-31.4.3/testdata/TestGitRepository.git/000077500000000000000000000000001376323343000210575ustar00rootroot00000000000000git2go-31.4.3/testdata/TestGitRepository.git/HEAD000066400000000000000000000000271376323343000215020ustar00rootroot00000000000000ref: refs/heads/master git2go-31.4.3/testdata/TestGitRepository.git/config000066400000000000000000000002101376323343000222400ustar00rootroot00000000000000[core] repositoryformatversion = 0 filemode = true bare = true [remote "origin"] url = https://github.com/libgit2/TestGitRepository git2go-31.4.3/testdata/TestGitRepository.git/description000066400000000000000000000001111376323343000233160ustar00rootroot00000000000000Unnamed repository; edit this file 'description' to name the repository. git2go-31.4.3/testdata/TestGitRepository.git/info/000077500000000000000000000000001376323343000220125ustar00rootroot00000000000000git2go-31.4.3/testdata/TestGitRepository.git/info/exclude000066400000000000000000000003601376323343000233650ustar00rootroot00000000000000# git ls-files --others --exclude-from=.git/info/exclude # Lines that start with '#' are comments. # For a project mostly in C, the following would be a good set of # exclude patterns (uncomment them if you want to use them): # *.[oa] # *~ git2go-31.4.3/testdata/TestGitRepository.git/info/refs000066400000000000000000000007701376323343000227000ustar00rootroot000000000000000966a434eb1a025db6b71485ab63a3bfbea520b6 refs/heads/first-merge 49322bb17d3acc9146f98c97d078513228bbf3c0 refs/heads/master 42e4e7c5e507e113ebbb7801b16b52cf867b7ce1 refs/heads/no-parent d96c4e80345534eccee5ac7b07fc7603b56124cb refs/tags/annotated_tag c070ad8c08840c8116da865b2d65593a6bb9cd2a refs/tags/annotated_tag^{} 55a1a760df4b86a02094a904dfa511deb5655905 refs/tags/blob 8f50ba15d49353813cc6e20298002c0d17b0a9ee refs/tags/commit_tree 6e0c7bdb9b4ed93212491ee778ca1c65047cab4e refs/tags/nearly-dangling git2go-31.4.3/testdata/TestGitRepository.git/objects/000077500000000000000000000000001376323343000225105ustar00rootroot00000000000000git2go-31.4.3/testdata/TestGitRepository.git/objects/info/000077500000000000000000000000001376323343000234435ustar00rootroot00000000000000git2go-31.4.3/testdata/TestGitRepository.git/objects/info/commit-graph000066400000000000000000000043701376323343000257610ustar00rootroot00000000000000CGPHOIDFDOIDLDCDATEDGE  f4]c =̻gw?(eK ,45,) BxkRφ{|I2+}:̑FxQ2(XFYWEbK5&!oRnYpj⹉'8 ~dbCMvQzl{e'1Y|fA{mUs6s inu nW# Clr"(9`˻?Jz`VgdC,(;O~~D^G*Jx2}fﺶkH6 NfCoz p چ[-eY:k*J2k0W9uxšZ`1V%8 -j*@Nd;g2fk*;ͻ DlР"1N9BUiE3;f;sٳmp$BUj/*o2 /ّoI}p BUib\S#[سppBU ` 6I̽ SvF1% ,BU0rAfL"GpBU: |:*ׇp BU>/H.p{o9 pBUI{Pö O]ppBU_%s研iy'3FtpBU2dQQkpwu (BU9"P[B){'p$BUц&\3+{X#t= BUº =/k_p BU v9If{]4pBU-˓hme:4Ե  p BUPԓS<,  BUYK},k `R pBU.xsut 7z pBUYnl%O+z$$pBUK},k `R pBUuTP5'#@9M “%git2go-31.4.3/testdata/TestGitRepository.git/objects/info/packs000066400000000000000000000000661376323343000244710ustar00rootroot00000000000000P pack-ccace4e169a0858c13d9ae781a91d76fc33769b8.pack git2go-31.4.3/testdata/TestGitRepository.git/objects/pack/000077500000000000000000000000001376323343000234265ustar00rootroot00000000000000pack-ccace4e169a0858c13d9ae781a91d76fc33769b8.bitmap000066400000000000000000000024661376323343000332330ustar00rootroot00000000000000git2go-31.4.3/testdata/TestGitRepository.git/objects/packBITM̬iٮxo7i9@F? o?<g0Eg0go?gC#>5g>$g< 'L1H07@0=9p %)` g0?c0a0 Ax`01A`@ !  aabFEG<F<cbcb@@cEaFcaF<FEbMaF֧v|zc`J{pack-ccace4e169a0858c13d9ae781a91d76fc33769b8.idx000066400000000000000000000057301376323343000325400ustar00rootroot00000000000000git2go-31.4.3/testdata/TestGitRepository.git/objects/packtOc  #&&')))))))**++,,,,,............/011111111334444444444456666778::::::::;;;;<<<===>????@ABBBBBBBBBBCCDDDDDDDDDDDDEEEEEFFFFFFFFF2dQQkpwu f4]c 嚃({ڟ ܑ' =wrHko('׬&0P4ɸFWXh iPSKpRR;=̻gw?(eK Q=?ZzAUh5s:2Ԝyj-,x4CL v9If{]4&uixռMri!;X,45,) ,Z\?]U0rAfL"GBxkRφ{|I2+}:̑FxQ2(IQ@nowU`K ߥ޵eYXFYWEbK5&!oRnYpj⹉'8 ~_%s研iy'3Ft` 6I̽ SvF1%b\S#[سdbCMvQzlР"1N9l{e'1Y|fA{mUs6s in {ۛN2Ixe|Nnu nW# Clr"(9`˻?b|t}[¼00] Ţwݓ$ЄfP_fxtO4q .xsut 7z]LqAdͪS=s僃Jz`VgdC,(;Onl%O+z$$&\3+{X#t=Ƶ} fyeW/j/*o2 /ّoI}ُPԓS<, ]CL,=xibnSa./|'u0WÊKlUI_u^>/H.p{o9 : |:*ׇ in/.SշӬ~~D^G*Jx2}f"P[B){'WjnA#+3oº =/k_񅔺kH6 NfCoz E3;f;sٳmp چ[-eY:k*{Pö O]GjrXp,ƯxGI >,MKaK},k `RJ2k0W9uxšZ`1V%8 -Թj\awjj*@NdlN4U4{va$/{;&&]\, r & F+\PR:1˓hme:4Ե ;g2fk*;ͻ DI.U4&*l K Aʘ? hK&eφ9hmZI0'~a/a;AN3*Ηr{R,o+2AmNIzS#cѫJn&ƒ "R:.x˾8_rO]{BYWPY6vP4CGt Z\ I[$OduV(ϰ/I#Q8V np)ma`H]9 z7Om^ A  NZ#. O :6!H&P@  uyV "54[ ̬iٮxo7i5G4ln?]_6Spack-ccace4e169a0858c13d9ae781a91d76fc33769b8.pack000066400000000000000000000136701376323343000326740ustar00rootroot00000000000000git2go-31.4.3/testdata/TestGitRepository.git/objects/packPACKFxAN0 }O]6Pq$Bpvpq$,쾾f((NHSS4BjAyf A @j#ICBRVKdGAi D@H"AC")}^X02[ɥjfQtܫpuU/f=/bDJ._nNo}W=ُڝ7?`nxKj1D:E-~BrZ-ې1B/Uz5*0:g]"'Ԣ V9+w]TGs:c䨢%]+QCc]s.̢DDC.< NxAj0 E9vjeC)k`)rgIE/PI+\<D$b(TPV2jm@ dE^,dDee;S<+?_Z&\*5/qNG YlMz|<>?Ы  O(Atz堽nvຍlnQ x 0 3E8uB008>TSVtWc"*l!Aͬȉ[dz=82w9E >_pfk~b5i>rhFv|jfiOԺ/-@ x[ 0Fb6`$4]{[*!z %K쁘:ba5 V*3z XX'I/Hkҕt [4gֺh]3]Wjn)S:&k?՚xKn0 D>wY%(t$@mu6=@737ANb^(1jkjB|s^tnۀ(C*8kJ XRػRL0q/q;|UD#溯oT=8vn:zq>_ VpܶC~Qk x[ 0Fb6`LN " tERSp-v(!Z >Fhu})7s\[VR`9qZC4iЙntO'jo%QmvC3"Z^ķm(eR&-xKKUS(HUHI,>4 xQ 0 p&ig:OH ΍ځ]Z|A8qS9"t9pl"^է1>w2&J,j 1w(ltLӚ5 NPJ ._X]j$/Cʔ^ !QP V8\B}_SٔZ2J0A)(l9 t-]?gmu:>2Ft|&&< G8 @. x[ 0@"f&ɤ]d}PRp-w9p۪jHZTrTS &0(S.qAaB <{D92ګ}'n~x\ڕy<[ ۃKΙܴS|d0_AxKj1D:E !.9>1 9~E.]WʉTL(ECDBlVT=uPa'>%b"IqsFbɡj?euK>Z+s@]f/<51.{3|3<ћ~ӥ+XkIKf8bD41y{k3ncd~.SƝ x[ 0s\l<@D<`ł%m0002HAdKȃdO)Y/.nK]a=>Kq\X9W}}_weZ >`46϶xDP?$AP xM 0@}N X3I@D7@0I&(X[j _{w^_D5 \)DͱwD5" 2 D%&hUaVlY~A[o0t1`wx31D."lmG]S;, r .y"yGC^^440031QM,.I-+(ahx%e@|[Y&y@.{x340031QH4dwS_Zl̒:0Y_/>+k*QU`**' v̿v\}R]%7x340031QH2+(ah;wlkm% y*S>oUaV!p+bj0Cf[AAWx340031QH6+(a~wStѩ0qI"tm dTHV~kx31D."lmG]S;D!7$Hiㅖmi+Jg.iZx31Dc%}tl+^$hR X.A5j,29ˍ=\2D.@b;%(ih``fbX\ZWRQдBKˀ%L 4/7gx4 Tx31Dc%}tl+^$hR X.ATFf=E݊묿E(&T04mВ2 -m{E,<V$Mx340031QH6+(a~wStѩ0qIByGLﲒnte΋ݮx31Dc%}tl+^$hR X.aZwΪe 3xWB/C3"Z^ķm(eR&Ӥx340031QH6+(aX7%<+lei9} mUaV! üЌ#_wY~72a4&bx31di]9C5xw^ qJ x31d؅^.pL9*ؖS; x340031QH6+(aX7%<+lei9} x31Dc%}tl+^$hR X.A5j,29ˍ=\򲡁BnbqIj^IE C -i/ҶW2)\{6$x31Dc%}tl+^$hR X.AѨ[1'ba =qFD!7$Hiㅖmi+Jg.i%+x340031QH2+(ah;wlkm% y*S>oUaVlY~A[o0t1d{x31$+;7ʟ`!ӖDY{ LLrKRJ*J6^hI{ߖIrFrWx31$'IkJjۛKo}=f&& %Ez%% M/ oK^Q?ˤp9O#x340031QH2+(a8yl}}wOvM:>e ڢx31Dc%}tl+^$hRBnbqIj^IE C -i/ҶW2)\#x31D%>\_ǧ/(&T04mВ2 -m{E,< x340031QH4+(aY 탿vUx`S=CCwEt,ٺM ݢx31Dǟj w&25(&T04mВ2 -m{E,<k.x340031QH4+(awS_Zl̒:PF`buU4-`m:x31Dǟj w&25(&T04|i6x;NV`{x31D'NsN}-5n2s;(&T04|i6x;NVh5x340031QH4+(awS_Zl̒:G x340031QM,.I-+(ahx%e@|[Y&y v@x340031QM,.I-+(ah2l5vE%ݝ6xK4J496xK4J4;3xK23xK2xSM,.I-IT3xK46xK2J2;6xK2J2=6xK6J6 =3xK63xK63xK4:xSM,.I-kt̬iٮxo7igit2go-31.4.3/testdata/TestGitRepository.git/packed-refs000066400000000000000000000010141376323343000231620ustar00rootroot00000000000000# pack-refs with: peeled fully-peeled sorted 0966a434eb1a025db6b71485ab63a3bfbea520b6 refs/heads/first-merge 49322bb17d3acc9146f98c97d078513228bbf3c0 refs/heads/master 42e4e7c5e507e113ebbb7801b16b52cf867b7ce1 refs/heads/no-parent d96c4e80345534eccee5ac7b07fc7603b56124cb refs/tags/annotated_tag ^c070ad8c08840c8116da865b2d65593a6bb9cd2a 55a1a760df4b86a02094a904dfa511deb5655905 refs/tags/blob 8f50ba15d49353813cc6e20298002c0d17b0a9ee refs/tags/commit_tree 6e0c7bdb9b4ed93212491ee778ca1c65047cab4e refs/tags/nearly-dangling git2go-31.4.3/testdata/TestGitRepository.git/refs/000077500000000000000000000000001376323343000220165ustar00rootroot00000000000000git2go-31.4.3/testdata/TestGitRepository.git/refs/heads/000077500000000000000000000000001376323343000231025ustar00rootroot00000000000000git2go-31.4.3/testdata/TestGitRepository.git/refs/heads/master000066400000000000000000000000511376323343000243140ustar00rootroot0000000000000049322bb17d3acc9146f98c97d078513228bbf3c0 git2go-31.4.3/tree.go000066400000000000000000000115261376323343000142740ustar00rootroot00000000000000package git /* #include extern int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr); */ import "C" import ( "errors" "runtime" "unsafe" ) type Filemode int const ( FilemodeTree Filemode = C.GIT_FILEMODE_TREE FilemodeBlob Filemode = C.GIT_FILEMODE_BLOB FilemodeBlobExecutable Filemode = C.GIT_FILEMODE_BLOB_EXECUTABLE FilemodeLink Filemode = C.GIT_FILEMODE_LINK FilemodeCommit Filemode = C.GIT_FILEMODE_COMMIT ) type Tree struct { Object cast_ptr *C.git_tree } func (t *Tree) AsObject() *Object { return &t.Object } type TreeEntry struct { Name string Id *Oid Type ObjectType Filemode Filemode } func newTreeEntry(entry *C.git_tree_entry) *TreeEntry { return &TreeEntry{ C.GoString(C.git_tree_entry_name(entry)), newOidFromC(C.git_tree_entry_id(entry)), ObjectType(C.git_tree_entry_type(entry)), Filemode(C.git_tree_entry_filemode(entry)), } } func (t *Tree) EntryByName(filename string) *TreeEntry { cname := C.CString(filename) defer C.free(unsafe.Pointer(cname)) entry := C.git_tree_entry_byname(t.cast_ptr, cname) if entry == nil { return nil } goEntry := newTreeEntry(entry) runtime.KeepAlive(t) return goEntry } // EntryById performs a lookup for a tree entry with the given SHA value. // // It returns a *TreeEntry that is owned by the Tree. You don't have to // free it, but you must not use it after the Tree is freed. // // Warning: this must examine every entry in the tree, so it is not fast. func (t *Tree) EntryById(id *Oid) *TreeEntry { runtime.LockOSThread() defer runtime.UnlockOSThread() entry := C.git_tree_entry_byid(t.cast_ptr, id.toC()) runtime.KeepAlive(id) if entry == nil { return nil } goEntry := newTreeEntry(entry) runtime.KeepAlive(t) return goEntry } // EntryByPath looks up an entry by its full path, recursing into // deeper trees if necessary (i.e. if there are slashes in the path) func (t *Tree) EntryByPath(path string) (*TreeEntry, error) { cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) var entry *C.git_tree_entry runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_tree_entry_bypath(&entry, t.cast_ptr, cpath) runtime.KeepAlive(t) if ret < 0 { return nil, MakeGitError(ret) } defer C.git_tree_entry_free(entry) return newTreeEntry(entry), nil } func (t *Tree) EntryByIndex(index uint64) *TreeEntry { entry := C.git_tree_entry_byindex(t.cast_ptr, C.size_t(index)) if entry == nil { return nil } goEntry := newTreeEntry(entry) runtime.KeepAlive(t) return goEntry } func (t *Tree) EntryCount() uint64 { num := C.git_tree_entrycount(t.cast_ptr) runtime.KeepAlive(t) return uint64(num) } type TreeWalkCallback func(string, *TreeEntry) int type treeWalkCallbackData struct { callback TreeWalkCallback errorTarget *error } //export treeWalkCallback func treeWalkCallback(_root *C.char, entry *C.git_tree_entry, ptr unsafe.Pointer) C.int { data, ok := pointerHandles.Get(ptr).(*treeWalkCallbackData) if !ok { panic("invalid treewalk callback") } ret := data.callback(C.GoString(_root), newTreeEntry(entry)) if ret < 0 { *data.errorTarget = errors.New(ErrorCode(ret).String()) return C.int(ErrorCodeUser) } return C.int(ErrorCodeOK) } func (t *Tree) Walk(callback TreeWalkCallback) error { var err error data := treeWalkCallbackData{ callback: callback, errorTarget: &err, } runtime.LockOSThread() defer runtime.UnlockOSThread() handle := pointerHandles.Track(&data) defer pointerHandles.Untrack(handle) ret := C._go_git_treewalk(t.cast_ptr, C.GIT_TREEWALK_PRE, handle) runtime.KeepAlive(t) if ret == C.int(ErrorCodeUser) && err != nil { return err } if ret < 0 { return MakeGitError(ret) } return nil } type TreeBuilder struct { ptr *C.git_treebuilder repo *Repository } func (v *TreeBuilder) Free() { runtime.SetFinalizer(v, nil) C.git_treebuilder_free(v.ptr) } func (v *TreeBuilder) Insert(filename string, id *Oid, filemode Filemode) error { cfilename := C.CString(filename) defer C.free(unsafe.Pointer(cfilename)) runtime.LockOSThread() defer runtime.UnlockOSThread() err := C.git_treebuilder_insert(nil, v.ptr, cfilename, id.toC(), C.git_filemode_t(filemode)) runtime.KeepAlive(v) runtime.KeepAlive(id) if err < 0 { return MakeGitError(err) } return nil } func (v *TreeBuilder) Remove(filename string) error { cfilename := C.CString(filename) defer C.free(unsafe.Pointer(cfilename)) runtime.LockOSThread() defer runtime.UnlockOSThread() err := C.git_treebuilder_remove(v.ptr, cfilename) runtime.KeepAlive(v) if err < 0 { return MakeGitError(err) } return nil } func (v *TreeBuilder) Write() (*Oid, error) { oid := new(Oid) runtime.LockOSThread() defer runtime.UnlockOSThread() err := C.git_treebuilder_write(oid.toC(), v.ptr) runtime.KeepAlive(v) if err < 0 { return nil, MakeGitError(err) } return oid, nil } git2go-31.4.3/tree_test.go000066400000000000000000000025161376323343000153320ustar00rootroot00000000000000package git import "testing" func TestTreeEntryById(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) _, treeID := seedTestRepo(t, repo) tree, err := repo.LookupTree(treeID) checkFatal(t, err) id, err := NewOid("257cc5642cb1a054f08cc83f2d943e56fd3ebe99") checkFatal(t, err) entry := tree.EntryById(id) if entry == nil { t.Fatalf("entry id %v was not found", id) } } func TestTreeBuilderInsert(t *testing.T) { t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) subTree, err := repo.TreeBuilder() if err != nil { t.Fatalf("TreeBuilder: %v", err) } defer subTree.Free() odb, err := repo.Odb() if err != nil { t.Fatalf("repo.Odb: %v", err) } blobId, err := odb.Write([]byte("hello"), ObjectBlob) if err != nil { t.Fatalf("odb.Write: %v", err) } if err = subTree.Insert("subfile", blobId, FilemodeBlobExecutable); err != nil { t.Fatalf("TreeBuilder.Insert: %v", err) } treeID, err := subTree.Write() if err != nil { t.Fatalf("TreeBuilder.Write: %v", err) } tree, err := repo.LookupTree(treeID) if err != nil { t.Fatalf("LookupTree: %v", err) } entry, err := tree.EntryByPath("subfile") if err != nil { t.Fatalf("tree.EntryByPath(%q): %v", "subfile", err) } if !entry.Id.Equal(blobId) { t.Fatalf("got oid %v, want %v", entry.Id, blobId) } } git2go-31.4.3/vendor/000077500000000000000000000000001376323343000142765ustar00rootroot00000000000000git2go-31.4.3/vendor/libgit2/000077500000000000000000000000001376323343000156325ustar00rootroot00000000000000git2go-31.4.3/walk.go000066400000000000000000000076071376323343000143000ustar00rootroot00000000000000package git /* #include */ import "C" import ( "runtime" "unsafe" ) // RevWalk type SortType uint const ( SortNone SortType = C.GIT_SORT_NONE SortTopological SortType = C.GIT_SORT_TOPOLOGICAL SortTime SortType = C.GIT_SORT_TIME SortReverse SortType = C.GIT_SORT_REVERSE ) type RevWalk struct { ptr *C.git_revwalk repo *Repository } func revWalkFromC(repo *Repository, c *C.git_revwalk) *RevWalk { v := &RevWalk{ptr: c, repo: repo} runtime.SetFinalizer(v, (*RevWalk).Free) return v } func (v *RevWalk) Reset() { C.git_revwalk_reset(v.ptr) runtime.KeepAlive(v) } func (v *RevWalk) Push(id *Oid) error { runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_revwalk_push(v.ptr, id.toC()) runtime.KeepAlive(v) runtime.KeepAlive(id) if ecode < 0 { return MakeGitError(ecode) } return nil } func (v *RevWalk) PushGlob(glob string) error { runtime.LockOSThread() defer runtime.UnlockOSThread() cstr := C.CString(glob) defer C.free(unsafe.Pointer(cstr)) ecode := C.git_revwalk_push_glob(v.ptr, cstr) runtime.KeepAlive(v) if ecode < 0 { return MakeGitError(ecode) } return nil } func (v *RevWalk) PushRange(r string) error { runtime.LockOSThread() defer runtime.UnlockOSThread() cstr := C.CString(r) defer C.free(unsafe.Pointer(cstr)) ecode := C.git_revwalk_push_range(v.ptr, cstr) runtime.KeepAlive(v) if ecode < 0 { return MakeGitError(ecode) } return nil } func (v *RevWalk) PushRef(r string) error { runtime.LockOSThread() defer runtime.UnlockOSThread() cstr := C.CString(r) defer C.free(unsafe.Pointer(cstr)) ecode := C.git_revwalk_push_ref(v.ptr, cstr) runtime.KeepAlive(v) if ecode < 0 { return MakeGitError(ecode) } return nil } func (v *RevWalk) PushHead() (err error) { runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_revwalk_push_head(v.ptr) runtime.KeepAlive(v) if ecode < 0 { err = MakeGitError(ecode) } return nil } func (v *RevWalk) Hide(id *Oid) error { runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_revwalk_hide(v.ptr, id.toC()) runtime.KeepAlive(v) runtime.KeepAlive(id) if ecode < 0 { return MakeGitError(ecode) } return nil } func (v *RevWalk) HideGlob(glob string) error { runtime.LockOSThread() defer runtime.UnlockOSThread() cstr := C.CString(glob) defer C.free(unsafe.Pointer(cstr)) ecode := C.git_revwalk_hide_glob(v.ptr, cstr) runtime.KeepAlive(v) if ecode < 0 { return MakeGitError(ecode) } return nil } func (v *RevWalk) HideRef(r string) error { runtime.LockOSThread() defer runtime.UnlockOSThread() cstr := C.CString(r) defer C.free(unsafe.Pointer(cstr)) ecode := C.git_revwalk_hide_ref(v.ptr, cstr) runtime.KeepAlive(v) if ecode < 0 { return MakeGitError(ecode) } return nil } func (v *RevWalk) HideHead() (err error) { runtime.LockOSThread() defer runtime.UnlockOSThread() ecode := C.git_revwalk_hide_head(v.ptr) runtime.KeepAlive(v) if ecode < 0 { err = MakeGitError(ecode) } return nil } func (v *RevWalk) Next(id *Oid) (err error) { runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_revwalk_next(id.toC(), v.ptr) runtime.KeepAlive(v) switch { case ret < 0: err = MakeGitError(ret) } return } type RevWalkIterator func(commit *Commit) bool func (v *RevWalk) Iterate(fun RevWalkIterator) (err error) { oid := new(Oid) for { err = v.Next(oid) if IsErrorCode(err, ErrorCodeIterOver) { return nil } if err != nil { return err } commit, err := v.repo.LookupCommit(oid) if err != nil { return err } cont := fun(commit) if !cont { break } } return nil } func (v *RevWalk) SimplifyFirstParent() { C.git_revwalk_simplify_first_parent(v.ptr) runtime.KeepAlive(v) } func (v *RevWalk) Sorting(sm SortType) { C.git_revwalk_sorting(v.ptr, C.uint(sm)) runtime.KeepAlive(v) } func (v *RevWalk) Free() { runtime.SetFinalizer(v, nil) C.git_revwalk_free(v.ptr) } git2go-31.4.3/wrapper.c000066400000000000000000000323001376323343000146230ustar00rootroot00000000000000#include "_cgo_export.h" #include #include #include #include // There are two ways in which to declare a callback: // // * If there is a guarantee that the callback will always be called within the // same stack (e.g. by passing the callback directly into a function / into a // struct that goes into a function), the following pattern is preferred, // which preserves the error object as-is: // // // myfile.go // type FooCallback func(...) (..., error) // type FooCallbackData struct { // callback FooCallback // errorTarget *error // } // // //export fooCallback // func fooCallback(..., handle unsafe.Pointer) C.int { // payload := pointerHandles.Get(handle) // data := payload.(*fooCallbackData) // ... // err := data.callback(...) // if err != nil { // *data.errorTarget = err // return C.int(ErrorCodeUser) // } // return C.int(ErrorCodeOK) // } // // func MyFunction(... callback FooCallback) error { // var err error // data := fooCallbackData{ // callback: callback, // errorTarget: &err, // } // handle := pointerHandles.Track(&data) // defer pointerHandles.Untrack(handle) // // runtime.LockOSThread() // defer runtime.UnlockOSThread() // // ret := C._go_git_my_function(..., handle) // if ret == C.int(ErrorCodeUser) && err != nil { // return err // } // if ret < 0 { // return MakeGitError(ret) // } // return nil // } // // // wrapper.c // int _go_git_my_function(..., void *payload) // { // return git_my_function(..., (git_foo_cb)&fooCallback, payload); // } // // * Otherwise, if the same callback can be invoked from multiple functions or // from different stacks (e.g. when passing the callback to an object), a // different pattern should be used instead, which has the downside of losing // the original error object and converting it to a GitError: // // // myfile.go // type FooCallback func(...) (..., error) // // //export fooCallback // func fooCallback(errorMessage **C.char, ..., handle unsafe.Pointer) C.int { // callback := pointerHandles.Get(data).(*FooCallback) // ... // err := callback(...) // if err != nil { // return setCallbackError(errorMessage, err) // } // return C.int(ErrorCodeOK) // } // // // wrapper.c // static int foo_callback(...) // { // char *error_message = NULL; // const int ret = fooCallback(&error_message, ...); // return set_callback_error(error_message, ret); // } /** * Sets the thread-local error to the provided string. This needs to happen in * C because Go might change Goroutines _just_ before returning, which would * lose the contents of the error message. */ static int set_callback_error(char *error_message, int ret) { if (error_message != NULL) { if (ret < 0) git_error_set_str(GIT_ERROR_CALLBACK, error_message); free(error_message); } return ret; } void _go_git_populate_apply_callbacks(git_apply_options *options) { options->delta_cb = (git_apply_delta_cb)&deltaApplyCallback; options->hunk_cb = (git_apply_hunk_cb)&hunkApplyCallback; } static int commit_signing_callback( git_buf *signature, git_buf *signature_field, const char *commit_contents, void *payload) { char *error_message = NULL; const int ret = commitSigningCallback( &error_message, signature, signature_field, (char *)commit_contents, payload ); return set_callback_error(error_message, ret); } void _go_git_populate_rebase_callbacks(git_rebase_options *opts) { opts->signing_cb = commit_signing_callback; } void _go_git_populate_clone_callbacks(git_clone_options *opts) { opts->remote_cb = (git_remote_create_cb)&remoteCreateCallback; } void _go_git_populate_checkout_callbacks(git_checkout_options *opts) { opts->notify_cb = (git_checkout_notify_cb)&checkoutNotifyCallback; opts->progress_cb = (git_checkout_progress_cb)&checkoutProgressCallback; } int _go_git_visit_submodule(git_repository *repo, void *fct) { return git_submodule_foreach(repo, (git_submodule_cb)&submoduleCallback, fct); } int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr) { return git_tree_walk(tree, mode, (git_treewalk_cb)&treeWalkCallback, ptr); } int _go_git_packbuilder_foreach(git_packbuilder *pb, void *payload) { return git_packbuilder_foreach(pb, (git_packbuilder_foreach_cb)&packbuilderForEachCallback, payload); } int _go_git_odb_foreach(git_odb *db, void *payload) { return git_odb_foreach(db, (git_odb_foreach_cb)&odbForEachCallback, payload); } void _go_git_odb_backend_free(git_odb_backend *backend) { if (!backend->free) return; backend->free(backend); } void _go_git_refdb_backend_free(git_refdb_backend *backend) { if (!backend->free) return; backend->free(backend); } int _go_git_diff_foreach(git_diff *diff, int eachFile, int eachHunk, int eachLine, void *payload) { git_diff_file_cb fcb = NULL; git_diff_hunk_cb hcb = NULL; git_diff_line_cb lcb = NULL; if (eachFile) fcb = (git_diff_file_cb)&diffForEachFileCallback; if (eachHunk) hcb = (git_diff_hunk_cb)&diffForEachHunkCallback; if (eachLine) lcb = (git_diff_line_cb)&diffForEachLineCallback; return git_diff_foreach(diff, fcb, NULL, hcb, lcb, payload); } int _go_git_diff_blobs( git_blob *old, const char *old_path, git_blob *new, const char *new_path, git_diff_options *opts, int eachFile, int eachHunk, int eachLine, void *payload) { git_diff_file_cb fcb = NULL; git_diff_hunk_cb hcb = NULL; git_diff_line_cb lcb = NULL; if (eachFile) fcb = (git_diff_file_cb)&diffForEachFileCallback; if (eachHunk) hcb = (git_diff_hunk_cb)&diffForEachHunkCallback; if (eachLine) lcb = (git_diff_line_cb)&diffForEachLineCallback; return git_diff_blobs(old, old_path, new, new_path, opts, fcb, NULL, hcb, lcb, payload); } void _go_git_setup_diff_notify_callbacks(git_diff_options *opts) { opts->notify_cb = (git_diff_notify_cb)&diffNotifyCallback; } static int sideband_progress_callback(const char *str, int len, void *payload) { char *error_message = NULL; const int ret = sidebandProgressCallback(&error_message, (char *)str, len, payload); return set_callback_error(error_message, ret); } static int completion_callback(git_remote_completion_type completion_type, void *data) { char *error_message = NULL; const int ret = completionCallback(&error_message, completion_type, data); return set_callback_error(error_message, ret); } static int credentials_callback( git_credential **cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *data) { char *error_message = NULL; const int ret = credentialsCallback( &error_message, cred, (char *)url, (char *)username_from_url, allowed_types, data ); return set_callback_error(error_message, ret); } static int transfer_progress_callback(const git_transfer_progress *stats, void *data) { char *error_message = NULL; const int ret = transferProgressCallback( &error_message, (git_transfer_progress *)stats, data ); return set_callback_error(error_message, ret); } static int update_tips_callback(const char *refname, const git_oid *a, const git_oid *b, void *data) { char *error_message = NULL; const int ret = updateTipsCallback( &error_message, (char *)refname, (git_oid *)a, (git_oid *)b, data ); return set_callback_error(error_message, ret); } static int certificate_check_callback(git_cert *cert, int valid, const char *host, void *data) { char *error_message = NULL; const int ret = certificateCheckCallback( &error_message, cert, valid, (char *)host, data ); return set_callback_error(error_message, ret); } static int pack_progress_callback(int stage, unsigned int current, unsigned int total, void *data) { char *error_message = NULL; const int ret = packProgressCallback( &error_message, stage, current, total, data ); return set_callback_error(error_message, ret); } static int push_transfer_progress_callback( unsigned int current, unsigned int total, size_t bytes, void *data) { char *error_message = NULL; const int ret = pushTransferProgressCallback( &error_message, current, total, bytes, data ); return set_callback_error(error_message, ret); } static int push_update_reference_callback(const char *refname, const char *status, void *data) { char *error_message = NULL; const int ret = pushUpdateReferenceCallback( &error_message, (char *)refname, (char *)status, data ); return set_callback_error(error_message, ret); } void _go_git_populate_remote_callbacks(git_remote_callbacks *callbacks) { callbacks->sideband_progress = sideband_progress_callback; callbacks->completion = completion_callback; callbacks->credentials = credentials_callback; callbacks->transfer_progress = transfer_progress_callback; callbacks->update_tips = update_tips_callback; callbacks->certificate_check = certificate_check_callback; callbacks->pack_progress = pack_progress_callback; callbacks->push_transfer_progress = push_transfer_progress_callback; callbacks->push_update_reference = push_update_reference_callback; } int _go_git_index_add_all(git_index *index, const git_strarray *pathspec, unsigned int flags, void *callback) { git_index_matched_path_cb cb = callback ? (git_index_matched_path_cb)&indexMatchedPathCallback : NULL; return git_index_add_all(index, pathspec, flags, cb, callback); } int _go_git_index_update_all(git_index *index, const git_strarray *pathspec, void *callback) { git_index_matched_path_cb cb = callback ? (git_index_matched_path_cb)&indexMatchedPathCallback : NULL; return git_index_update_all(index, pathspec, cb, callback); } int _go_git_index_remove_all(git_index *index, const git_strarray *pathspec, void *callback) { git_index_matched_path_cb cb = callback ? (git_index_matched_path_cb)&indexMatchedPathCallback : NULL; return git_index_remove_all(index, pathspec, cb, callback); } int _go_git_tag_foreach(git_repository *repo, void *payload) { return git_tag_foreach(repo, (git_tag_foreach_cb)&tagForeachCallback, payload); } int _go_git_merge_file( git_merge_file_result* out, char* ancestorContents, size_t ancestorLen, char* ancestorPath, unsigned int ancestorMode, char* oursContents, size_t oursLen, char* oursPath, unsigned int oursMode, char* theirsContents, size_t theirsLen, char* theirsPath, unsigned int theirsMode, git_merge_file_options* copts) { git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT; git_merge_file_input ours = GIT_MERGE_FILE_INPUT_INIT; git_merge_file_input theirs = GIT_MERGE_FILE_INPUT_INIT; ancestor.ptr = ancestorContents; ancestor.size = ancestorLen; ancestor.path = ancestorPath; ancestor.mode = ancestorMode; ours.ptr = oursContents; ours.size = oursLen; ours.path = oursPath; ours.mode = oursMode; theirs.ptr = theirsContents; theirs.size = theirsLen; theirs.path = theirsPath; theirs.mode = theirsMode; return git_merge_file(out, &ancestor, &ours, &theirs, copts); } void _go_git_populate_stash_apply_callbacks(git_stash_apply_options *opts) { opts->progress_cb = (git_stash_apply_progress_cb)&stashApplyProgressCallback; } int _go_git_stash_foreach(git_repository *repo, void *payload) { return git_stash_foreach(repo, (git_stash_cb)&stashForeachCallback, payload); } int _go_git_writestream_write(git_writestream *stream, const char *buffer, size_t len) { return stream->write(stream, buffer, len); } int _go_git_writestream_close(git_writestream *stream) { return stream->close(stream); } void _go_git_writestream_free(git_writestream *stream) { stream->free(stream); } git_credential_t _go_git_credential_credtype(git_credential *cred) { return cred->credtype; } static int credential_ssh_sign_callback( LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len, const unsigned char *data, size_t data_len, void **abstract) { char *error_message = NULL; const int ret = credentialSSHSignCallback( &error_message, sig, sig_len, (unsigned char *)data, data_len, (void *)*(uintptr_t *)abstract); return set_callback_error(error_message, ret); } void _go_git_populate_credential_ssh_custom(git_credential_ssh_custom *cred) { cred->parent.free = (void (*)(git_credential *))credentialSSHCustomFree; cred->sign_callback = credential_ssh_sign_callback; } int _go_git_odb_write_pack(git_odb_writepack **out, git_odb *db, void *progress_payload) { return git_odb_write_pack(out, db, transfer_progress_callback, progress_payload); } int _go_git_odb_writepack_append( git_odb_writepack *writepack, const void *data, size_t size, git_transfer_progress *stats) { return writepack->append(writepack, data, size, stats); } int _go_git_odb_writepack_commit(git_odb_writepack *writepack, git_transfer_progress *stats) { return writepack->commit(writepack, stats); } void _go_git_odb_writepack_free(git_odb_writepack *writepack) { writepack->free(writepack); } int _go_git_indexer_new( git_indexer **out, const char *path, unsigned int mode, git_odb *odb, void *progress_cb_payload) { git_indexer_options indexer_options = GIT_INDEXER_OPTIONS_INIT; indexer_options.progress_cb = transfer_progress_callback; indexer_options.progress_cb_payload = progress_cb_payload; return git_indexer_new(out, path, mode, odb, &indexer_options); }