summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--status.go191
-rw-r--r--status_test.go58
-rw-r--r--wrapper.c2
3 files changed, 250 insertions, 1 deletions
diff --git a/status.go b/status.go
new file mode 100644
index 0000000..a95c302
--- /dev/null
+++ b/status.go
@@ -0,0 +1,191 @@
+package git
+
+/*
+#include <git2.h>
+#include <git2/errors.h>
+*/
+import "C"
+
+import (
+ "runtime"
+ "unsafe"
+)
+
+type Status int
+
+const (
+ StatusCurrent Status = C.GIT_STATUS_CURRENT
+ StatusIndexNew = C.GIT_STATUS_INDEX_NEW
+ StatusIndexModified = C.GIT_STATUS_INDEX_MODIFIED
+ StatusIndexDeleted = C.GIT_STATUS_INDEX_DELETED
+ StatusIndexRenamed = C.GIT_STATUS_INDEX_RENAMED
+ StatusIndexTypeChange = C.GIT_STATUS_INDEX_TYPECHANGE
+ StatusWtNew = C.GIT_STATUS_WT_NEW
+ StatusWtModified = C.GIT_STATUS_WT_MODIFIED
+ StatusWtDeleted = C.GIT_STATUS_WT_DELETED
+ StatusWtTypeChange = C.GIT_STATUS_WT_TYPECHANGE
+ StatusWtRenamed = C.GIT_STATUS_WT_RENAMED
+ StatusIgnored = C.GIT_STATUS_IGNORED
+)
+
+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
+}
+
+func newStatusListFromC(ptr *C.git_status_list) *StatusList {
+ if ptr == nil {
+ return nil
+ }
+
+ statusList := &StatusList{
+ ptr: ptr,
+ }
+
+ 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))
+ return statusEntryFromC(ptr), nil
+}
+
+func (statusList *StatusList) EntryCount() (int, error) {
+ if statusList.ptr == nil {
+ return -1, ErrInvalid
+ }
+ return int(C.git_status_list_entrycount(statusList.ptr)), nil
+}
+
+type StatusOpt int
+
+const (
+ StatusOptIncludeUntracked StatusOpt = C.GIT_STATUS_OPT_INCLUDE_UNTRACKED
+ StatusOptIncludeIgnored = C.GIT_STATUS_OPT_INCLUDE_IGNORED
+ StatusOptIncludeUnmodified = C.GIT_STATUS_OPT_INCLUDE_UNMODIFIED
+ StatusOptExcludeSubmodules = C.GIT_STATUS_OPT_EXCLUDE_SUBMODULES
+ StatusOptRecurseUntrackedDirs = C.GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS
+ StatusOptDisablePathspecMatch = C.GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH
+ StatusOptRecurseIgnoredDirs = C.GIT_STATUS_OPT_RECURSE_IGNORED_DIRS
+ StatusOptRenamesHeadToIndex = C.GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX
+ StatusOptRenamesIndexToWorkdir = C.GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR
+ StatusOptSortCaseSensitively = C.GIT_STATUS_OPT_SORT_CASE_SENSITIVELY
+ StatusOptSortCaseInsensitively = C.GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY
+ StatusOptRenamesFromRewrites = C.GIT_STATUS_OPT_RENAMES_FROM_REWRITES
+ StatusOptNoRefresh = C.GIT_STATUS_OPT_NO_REFRESH
+ StatusOptUpdateIndex = C.GIT_STATUS_OPT_UPDATE_INDEX
+)
+
+type StatusShow int
+
+const (
+ StatusShowIndexAndWorkdir StatusShow = C.GIT_STATUS_SHOW_INDEX_AND_WORKDIR
+ StatusShowIndexOnly = C.GIT_STATUS_SHOW_INDEX_ONLY
+ StatusShowWorkdirOnly = C.GIT_STATUS_SHOW_WORKDIR_ONLY
+)
+
+type StatusOptions struct {
+ Show StatusShow
+ Flags StatusOpt
+ Pathspec []string
+}
+
+func (opts *StatusOptions) toC() *C.git_status_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)
+ 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,
+ }
+
+ return copts
+}
+
+func (v *Repository) StatusList(opts *StatusOptions) (*StatusList, error) {
+ var ptr *C.git_status_list
+ var copts *C.git_status_options
+
+ if opts != nil {
+ copts = opts.toC()
+ } else {
+ copts = &C.git_status_options{}
+ ret := C.git_status_init_options(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), 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)
+ if ret < 0 {
+ return 0, MakeGitError(ret)
+ }
+ return Status(statusFlags), nil
+}
diff --git a/status_test.go b/status_test.go
new file mode 100644
index 0000000..4be4824
--- /dev/null
+++ b/status_test.go
@@ -0,0 +1,58 @@
+package git
+
+import (
+ "io/ioutil"
+ "os"
+ "path"
+ "testing"
+)
+
+func TestStatusFile(t *testing.T) {
+ repo := createTestRepo(t)
+ defer repo.Free()
+ defer os.RemoveAll(repo.Workdir())
+
+ 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) {
+ repo := createTestRepo(t)
+ // 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)
+ defer repo.Free()
+ defer os.RemoveAll(repo.Workdir())
+
+ 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)
+ }
+}
diff --git a/wrapper.c b/wrapper.c
index 2fd8fb7..45c4358 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -45,7 +45,7 @@ void _go_git_refdb_backend_free(git_refdb_backend *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_file_cb fcb = NULL;
git_diff_hunk_cb hcb = NULL;
git_diff_line_cb lcb = NULL;