package 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 ( "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 }