462 lines
10 KiB
Go
462 lines
10 KiB
Go
package git
|
|
|
|
/*
|
|
#include <git2.h>
|
|
|
|
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 (
|
|
"fmt"
|
|
"runtime"
|
|
"time"
|
|
"unsafe"
|
|
)
|
|
|
|
type IndexMatchedPathCallback func(string, string) int
|
|
|
|
type IndexAddOpts uint
|
|
|
|
const (
|
|
IndexAddDefault IndexAddOpts = C.GIT_INDEX_ADD_DEFAULT
|
|
IndexAddForce IndexAddOpts = C.GIT_INDEX_ADD_FORCE
|
|
IndexAddDisablePathspecMatch IndexAddOpts = C.GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH
|
|
IndexAddCheckPathspec IndexAddOpts = C.GIT_INDEX_ADD_CHECK_PATHSPEC
|
|
)
|
|
|
|
type Index struct {
|
|
ptr *C.git_index
|
|
}
|
|
|
|
type IndexEntry struct {
|
|
Ctime time.Time
|
|
Mtime time.Time
|
|
Mode Filemode
|
|
Uid uint
|
|
Gid uint
|
|
Size uint
|
|
Id *Oid
|
|
Path string
|
|
}
|
|
|
|
func newIndexEntryFromC(entry *C.git_index_entry) *IndexEntry {
|
|
if entry == nil {
|
|
return nil
|
|
}
|
|
return &IndexEntry{
|
|
time.Unix(int64(entry.ctime.seconds), int64(entry.ctime.nanoseconds)),
|
|
time.Unix(int64(entry.mtime.seconds), int64(entry.mtime.nanoseconds)),
|
|
Filemode(entry.mode),
|
|
uint(entry.uid),
|
|
uint(entry.gid),
|
|
uint(entry.file_size),
|
|
newOidFromC(&entry.id),
|
|
C.GoString(entry.path),
|
|
}
|
|
}
|
|
|
|
func populateCIndexEntry(source *IndexEntry, dest *C.git_index_entry) {
|
|
dest.ctime.seconds = C.git_time_t(source.Ctime.Unix())
|
|
dest.ctime.nanoseconds = C.uint(source.Ctime.UnixNano())
|
|
dest.mtime.seconds = C.git_time_t(source.Mtime.Unix())
|
|
dest.mtime.nanoseconds = C.uint(source.Mtime.UnixNano())
|
|
dest.mode = C.uint(source.Mode)
|
|
dest.uid = C.uint(source.Uid)
|
|
dest.gid = C.uint(source.Gid)
|
|
dest.file_size = C.git_off_t(source.Size)
|
|
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) *Index {
|
|
idx := &Index{ptr}
|
|
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 &Index{ptr: ptr}, 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 &Index{ptr: ptr}, nil
|
|
}
|
|
|
|
// Path returns the index' path on disk or an empty string if it
|
|
// exists only in memory.
|
|
func (v *Index) Path() string {
|
|
return C.GoString(C.git_index_path(v.ptr))
|
|
}
|
|
|
|
// 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()
|
|
|
|
if err := C.git_index_add(v.ptr, ¢ry); 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)
|
|
if ret < 0 {
|
|
return MakeGitError(ret)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (v *Index) AddAll(pathspecs []string, flags IndexAddOpts, callback IndexMatchedPathCallback) error {
|
|
cpathspecs := C.git_strarray{}
|
|
cpathspecs.count = C.size_t(len(pathspecs))
|
|
cpathspecs.strings = makeCStringsFromStrings(pathspecs)
|
|
defer freeStrarray(&cpathspecs)
|
|
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
|
|
var cb *IndexMatchedPathCallback
|
|
if callback != nil {
|
|
cb = &callback
|
|
}
|
|
|
|
ret := C._go_git_index_add_all(
|
|
v.ptr,
|
|
&cpathspecs,
|
|
C.uint(flags),
|
|
unsafe.Pointer(cb),
|
|
)
|
|
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)
|
|
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
|
|
var cb *IndexMatchedPathCallback
|
|
if callback != nil {
|
|
cb = &callback
|
|
}
|
|
|
|
ret := C._go_git_index_update_all(
|
|
v.ptr,
|
|
&cpathspecs,
|
|
unsafe.Pointer(cb),
|
|
)
|
|
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)
|
|
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
|
|
var cb *IndexMatchedPathCallback
|
|
if callback != nil {
|
|
cb = &callback
|
|
}
|
|
|
|
ret := C._go_git_index_remove_all(
|
|
v.ptr,
|
|
&cpathspecs,
|
|
unsafe.Pointer(cb),
|
|
)
|
|
if ret < 0 {
|
|
return MakeGitError(ret)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
//export indexMatchedPathCallback
|
|
func indexMatchedPathCallback(cPath, cMatchedPathspec *C.char, payload unsafe.Pointer) int {
|
|
callback := (*IndexMatchedPathCallback)(payload)
|
|
return (*callback)(C.GoString(cPath), C.GoString(cMatchedPathspec))
|
|
}
|
|
|
|
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)
|
|
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)
|
|
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);
|
|
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)
|
|
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)
|
|
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 {
|
|
return uint(C.git_index_entrycount(v.ptr))
|
|
}
|
|
|
|
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")
|
|
}
|
|
return newIndexEntryFromC(centry), nil
|
|
}
|
|
|
|
func (v *Index) HasConflicts() bool {
|
|
return C.git_index_has_conflicts(v.ptr) != 0
|
|
}
|
|
|
|
// FIXME: this might return an error
|
|
func (v *Index) CleanupConflicts() {
|
|
C.git_index_conflict_cleanup(v.ptr)
|
|
}
|
|
|
|
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)
|
|
if ecode < 0 {
|
|
return MakeGitError(ecode)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type IndexConflict struct {
|
|
Ancestor *IndexEntry
|
|
Our *IndexEntry
|
|
Their *IndexEntry
|
|
}
|
|
|
|
func (v *Index) GetConflict(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)
|
|
}
|
|
return IndexConflict{
|
|
Ancestor: newIndexEntryFromC(cancestor),
|
|
Our: newIndexEntryFromC(cour),
|
|
Their: newIndexEntryFromC(ctheir),
|
|
}, nil
|
|
}
|
|
|
|
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)
|
|
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)
|
|
}
|
|
return IndexConflict{
|
|
Ancestor: newIndexEntryFromC(cancestor),
|
|
Our: newIndexEntryFromC(cour),
|
|
Their: newIndexEntryFromC(ctheir),
|
|
}, nil
|
|
}
|