| 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" |
| "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 IndexStageOpts 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 IndexStageOpts = C.GIT_INDEX_STAGE_ANY |
| // IndexStageNormal is a normal staged file in the index. |
| IndexStageNormal IndexStageOpts = C.GIT_INDEX_STAGE_NORMAL |
| // IndexStageAncestor is the ancestor side of a conflict. |
| IndexStageAncestor IndexStageOpts = C.GIT_INDEX_STAGE_ANCESTOR |
| // IndexStageOurs is the "ours" side of a conflict. |
| IndexStageOurs IndexStageOpts = C.GIT_INDEX_STAGE_OURS |
| // IndexStageTheirs is the "theirs" side of a conflict. |
| IndexStageTheirs IndexStageOpts = 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) |
| 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 |
| } |
| |
| // 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 |
| } |
| |
| 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 handle unsafe.Pointer |
| if callback != nil { |
| handle = pointerHandles.Track(callback) |
| defer pointerHandles.Untrack(handle) |
| } |
| |
| ret := C._go_git_index_add_all( |
| v.ptr, |
| &cpathspecs, |
| C.uint(flags), |
| handle, |
| ) |
| runtime.KeepAlive(v) |
| 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 handle unsafe.Pointer |
| if callback != nil { |
| handle = pointerHandles.Track(callback) |
| defer pointerHandles.Untrack(handle) |
| } |
| |
| ret := C._go_git_index_update_all( |
| v.ptr, |
| &cpathspecs, |
| handle, |
| ) |
| runtime.KeepAlive(v) |
| 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 handle unsafe.Pointer |
| if callback != nil { |
| handle = pointerHandles.Track(callback) |
| defer pointerHandles.Untrack(handle) |
| } |
| |
| ret := C._go_git_index_remove_all( |
| v.ptr, |
| &cpathspecs, |
| handle, |
| ) |
| runtime.KeepAlive(v) |
| if ret < 0 { |
| return MakeGitError(ret) |
| } |
| return nil |
| } |
| |
| //export indexMatchedPathCallback |
| func indexMatchedPathCallback(cPath, cMatchedPathspec *C.char, payload unsafe.Pointer) int { |
| if callback, ok := pointerHandles.Get(payload).(IndexMatchedPathCallback); ok { |
| return callback(C.GoString(cPath), C.GoString(cMatchedPathspec)) |
| } else { |
| panic("invalid matched path callback") |
| } |
| } |
| |
| 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.GIT_ENOTFOUND) |
| } |
| 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) 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) |
| } |
| ret := IndexConflict{ |
| Ancestor: newIndexEntryFromC(cancestor), |
| Our: newIndexEntryFromC(cour), |
| Their: newIndexEntryFromC(ctheir), |
| } |
| runtime.KeepAlive(v) |
| return ret, 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) |
| 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 |
| } |