2014-08-18 18:42:34 -05:00
|
|
|
package git
|
|
|
|
|
|
|
|
/*
|
|
|
|
#include <git2.h>
|
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
|
|
|
|
import (
|
2020-08-14 13:19:21 -05:00
|
|
|
"errors"
|
2014-08-18 18:42:34 -05:00
|
|
|
"runtime"
|
2014-08-25 17:20:54 -05:00
|
|
|
"unsafe"
|
2014-08-18 18:42:34 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
type Status int
|
|
|
|
|
|
|
|
const (
|
|
|
|
StatusCurrent Status = C.GIT_STATUS_CURRENT
|
2014-10-27 09:12:18 -05:00
|
|
|
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
|
2016-05-29 07:37:37 -05:00
|
|
|
StatusConflicted Status = C.GIT_STATUS_CONFLICTED
|
2014-08-18 18:42:34 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
type StatusEntry struct {
|
|
|
|
Status Status
|
|
|
|
HeadToIndex DiffDelta
|
|
|
|
IndexToWorkdir DiffDelta
|
|
|
|
}
|
|
|
|
|
2014-08-18 19:04:32 -05:00
|
|
|
func statusEntryFromC(statusEntry *C.git_status_entry) StatusEntry {
|
2014-08-25 22:18:00 -05:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2015-03-04 13:40:26 -06:00
|
|
|
return StatusEntry{
|
2014-08-18 19:04:32 -05:00
|
|
|
Status: Status(statusEntry.status),
|
2014-08-25 22:18:00 -05:00
|
|
|
HeadToIndex: headToIndex,
|
|
|
|
IndexToWorkdir: indexToWorkdir,
|
2014-08-18 19:04:32 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type StatusList struct {
|
|
|
|
ptr *C.git_status_list
|
2017-07-08 09:07:51 -05:00
|
|
|
r *Repository
|
2014-08-18 19:04:32 -05:00
|
|
|
}
|
|
|
|
|
2017-07-08 09:07:51 -05:00
|
|
|
func newStatusListFromC(ptr *C.git_status_list, r *Repository) *StatusList {
|
2014-08-18 18:42:34 -05:00
|
|
|
if ptr == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
statusList := &StatusList{
|
|
|
|
ptr: ptr,
|
2017-07-08 09:07:51 -05:00
|
|
|
r: r,
|
2014-08-18 18:42:34 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
runtime.SetFinalizer(statusList, (*StatusList).Free)
|
|
|
|
return statusList
|
|
|
|
}
|
|
|
|
|
2014-08-25 16:41:35 -05:00
|
|
|
func (statusList *StatusList) Free() {
|
2014-08-18 18:42:34 -05:00
|
|
|
if statusList.ptr == nil {
|
2014-08-25 16:41:35 -05:00
|
|
|
return
|
2014-08-18 18:42:34 -05:00
|
|
|
}
|
|
|
|
runtime.SetFinalizer(statusList, nil)
|
|
|
|
C.git_status_list_free(statusList.ptr)
|
|
|
|
statusList.ptr = nil
|
|
|
|
}
|
|
|
|
|
2014-08-18 19:04:32 -05:00
|
|
|
func (statusList *StatusList) ByIndex(index int) (StatusEntry, error) {
|
|
|
|
if statusList.ptr == nil {
|
|
|
|
return StatusEntry{}, ErrInvalid
|
2014-08-18 18:42:34 -05:00
|
|
|
}
|
2014-08-18 19:04:32 -05:00
|
|
|
ptr := C.git_status_byindex(statusList.ptr, C.size_t(index))
|
2020-08-14 13:19:21 -05:00
|
|
|
if ptr == nil {
|
|
|
|
return StatusEntry{}, errors.New("index out of Bounds")
|
|
|
|
}
|
2017-07-08 09:07:51 -05:00
|
|
|
entry := statusEntryFromC(ptr)
|
|
|
|
runtime.KeepAlive(statusList)
|
|
|
|
|
|
|
|
return entry, nil
|
2014-08-18 18:42:34 -05:00
|
|
|
}
|
2014-08-18 19:11:14 -05:00
|
|
|
|
|
|
|
func (statusList *StatusList) EntryCount() (int, error) {
|
|
|
|
if statusList.ptr == nil {
|
|
|
|
return -1, ErrInvalid
|
|
|
|
}
|
2017-07-08 09:07:51 -05:00
|
|
|
ret := int(C.git_status_list_entrycount(statusList.ptr))
|
|
|
|
runtime.KeepAlive(statusList)
|
|
|
|
|
|
|
|
return ret, nil
|
2014-08-18 19:11:14 -05:00
|
|
|
}
|
2014-08-18 21:58:53 -05:00
|
|
|
|
2014-09-04 09:15:13 -05:00
|
|
|
type StatusOpt int
|
|
|
|
|
2014-08-18 21:58:53 -05:00
|
|
|
const (
|
2015-03-04 13:40:26 -06:00
|
|
|
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
|
2014-08-18 21:58:53 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
type StatusShow int
|
|
|
|
|
|
|
|
const (
|
|
|
|
StatusShowIndexAndWorkdir StatusShow = C.GIT_STATUS_SHOW_INDEX_AND_WORKDIR
|
2014-10-27 09:12:18 -05:00
|
|
|
StatusShowIndexOnly StatusShow = C.GIT_STATUS_SHOW_INDEX_ONLY
|
|
|
|
StatusShowWorkdirOnly StatusShow = C.GIT_STATUS_SHOW_WORKDIR_ONLY
|
2014-08-18 21:58:53 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
type StatusOptions struct {
|
|
|
|
Show StatusShow
|
2014-09-04 09:15:13 -05:00
|
|
|
Flags StatusOpt
|
2014-08-18 21:58:53 -05:00
|
|
|
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 {
|
Fix crash when using Pathspec in StatusOptions
Using `StatusOptions.Pathspec` results in a fatal error panic with
the message 'unexpected signal during runtime execution'.
This is because the `&cpathspec` C.git_strarray gets freed in
`*StatusOptions.toC()` before being passed to
`C.git_status_init_options()` in `*Repository.StatusList()`
(see https://github.com/libgit2/git2go/blob/b3e7705c48f038ef335204a2a9e1ee829784c30e/status.go#L138)
The relevant panic trace is:
```
fatal error: unexpected signal during runtime execution
[signal 0xb code=0x1 addr=0xb01dfacedebac1e pc=0x4062609]
runtime stack:
runtime.throw(0x469a080, 0x2a)
/usr/local/Cellar/go/1.5.1/libexec/src/runtime/panic.go:527 +0x90
runtime.sigpanic()
/usr/local/Cellar/go/1.5.1/libexec/src/runtime/sigpanic_unix.go:12
+0x5a
goroutine 71 [syscall, locked to thread]:
runtime.cgocall(0x400a720, 0xc8204e9998, 0x0)
/usr/local/Cellar/go/1.5.1/libexec/src/runtime/cgocall.go:120 +0x11b
fp=0xc8204e9968 sp=0xc8204e9938
github.com/libgit2/git2go._Cfunc_git_status_list_new(0xc8204c39c8,
0x5e17780, 0xc820478c40, 0xc800000000)
??:0 +0x39 fp=0xc8204e9998 sp=0xc8204e9968
github.com/libgit2/git2go.(*Repository).StatusList(0xc820013290,
0xc8204e9b58, 0x0, 0x0, 0x0)
/Users/calin/go/src/github.com/libgit2/git2go/status.go:168 +0x11d
fp=0xc8204e99e8 sp=0xc8204e9998
```
2015-09-17 09:07:34 -05:00
|
|
|
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,
|
|
|
|
}
|
2014-08-18 21:58:53 -05:00
|
|
|
} else {
|
|
|
|
copts = &C.git_status_options{}
|
2020-10-22 08:18:11 -05:00
|
|
|
ret := C.git_status_options_init(copts, C.GIT_STATUS_OPTIONS_VERSION)
|
2014-08-18 21:58:53 -05:00
|
|
|
if ret < 0 {
|
|
|
|
return nil, MakeGitError(ret)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-25 17:20:54 -05:00
|
|
|
runtime.LockOSThread()
|
|
|
|
defer runtime.UnlockOSThread()
|
|
|
|
|
2014-08-18 21:58:53 -05:00
|
|
|
ret := C.git_status_list_new(&ptr, v.ptr, copts)
|
|
|
|
if ret < 0 {
|
|
|
|
return nil, MakeGitError(ret)
|
|
|
|
}
|
2014-08-25 22:18:00 -05:00
|
|
|
|
2017-07-08 09:07:51 -05:00
|
|
|
return newStatusListFromC(ptr, v), nil
|
2014-08-18 21:58:53 -05:00
|
|
|
}
|
2014-08-18 22:12:45 -05:00
|
|
|
|
|
|
|
func (v *Repository) StatusFile(path string) (Status, error) {
|
|
|
|
var statusFlags C.uint
|
|
|
|
cPath := C.CString(path)
|
2014-08-25 17:20:54 -05:00
|
|
|
defer C.free(unsafe.Pointer(cPath))
|
|
|
|
|
|
|
|
runtime.LockOSThread()
|
|
|
|
defer runtime.UnlockOSThread()
|
|
|
|
|
2014-08-18 22:12:45 -05:00
|
|
|
ret := C.git_status_file(&statusFlags, v.ptr, cPath)
|
2017-07-08 09:07:51 -05:00
|
|
|
runtime.KeepAlive(v)
|
2014-08-18 22:12:45 -05:00
|
|
|
if ret < 0 {
|
|
|
|
return 0, MakeGitError(ret)
|
|
|
|
}
|
|
|
|
return Status(statusFlags), nil
|
|
|
|
}
|