summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--checkout.go32
-rw-r--r--object.go32
-rw-r--r--object_test.go21
-rw-r--r--odb.go94
-rw-r--r--odb_test.go35
-rw-r--r--reference.go11
-rw-r--r--reference_test.go10
-rw-r--r--repository.go8
-rw-r--r--tree.go27
-rw-r--r--walk.go11
10 files changed, 223 insertions, 58 deletions
diff --git a/checkout.go b/checkout.go
index bbeb240..92ced9e 100644
--- a/checkout.go
+++ b/checkout.go
@@ -16,22 +16,22 @@ import (
type CheckoutStrategy uint
const (
- CHECKOUT_NONE CheckoutStrategy = C.GIT_CHECKOUT_NONE // Dry run, no actual updates
- CHECKOUT_SAFE = C.GIT_CHECKOUT_SAFE // Allow safe updates that cannot overwrite uncommitted data
- CHECKOUT_SAFE_CREATE = C.GIT_CHECKOUT_SAFE_CREATE // Allow safe updates plus creation of missing files
- CHECKOUT_FORCE = C.GIT_CHECKOUT_FORCE // Allow all updates to force working directory to look like index
- CHECKOUT_ALLOW_CONFLICTS = C.GIT_CHECKOUT_ALLOW_CONFLICTS // Allow checkout to make safe updates even if conflicts are found
- CHECKOUT_REMOVE_UNTRACKED = C.GIT_CHECKOUT_REMOVE_UNTRACKED // Remove untracked files not in index (that are not ignored)
- CHECKOUT_REMOVE_IGNORED = C.GIT_CHECKOUT_REMOVE_IGNORED // Remove ignored files not in index
- CHECKOUT_UPDATE_ONLY = C.GIT_CHECKOUT_UPDATE_ONLY // Only update existing files, don't create new ones
- CHECKOUT_DONT_UPDATE_INDEX = C.GIT_CHECKOUT_DONT_UPDATE_INDEX // Normally checkout updates index entries as it goes; this stops that
- CHECKOUT_NO_REFRESH = C.GIT_CHECKOUT_NO_REFRESH // Don't refresh index/config/etc before doing checkout
- CHECKOUT_DISABLE_PATHSPEC_MATCH = C.GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH // Treat pathspec as simple list of exact match file paths
- CHECKOUT_SKIP_UNMERGED = C.GIT_CHECKOUT_SKIP_UNMERGED // Allow checkout to skip unmerged files (NOT IMPLEMENTED)
- CHECKOUT_USE_OURS = C.GIT_CHECKOUT_USE_OURS // For unmerged files, checkout stage 2 from index (NOT IMPLEMENTED)
- CHECKOUT_USE_THEIRS = C.GIT_CHECKOUT_USE_THEIRS // For unmerged files, checkout stage 3 from index (NOT IMPLEMENTED)
- CHECKOUT_UPDATE_SUBMODULES = C.GIT_CHECKOUT_UPDATE_SUBMODULES // Recursively checkout submodules with same options (NOT IMPLEMENTED)
- CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED = C.GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED // Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED)
+ CheckoutNone CheckoutStrategy = C.GIT_CHECKOUT_NONE // Dry run, no actual updates
+ CheckoutSafe = C.GIT_CHECKOUT_SAFE // Allow safe updates that cannot overwrite uncommitted data
+ CheckoutSafeCreate = C.GIT_CHECKOUT_SAFE_CREATE // Allow safe updates plus creation of missing files
+ CheckoutForce = C.GIT_CHECKOUT_FORCE // Allow all updates to force working directory to look like index
+ CheckoutAllowConflicts = C.GIT_CHECKOUT_ALLOW_CONFLICTS // Allow checkout to make safe updates even if conflicts are found
+ CheckoutRemoveUntracked = C.GIT_CHECKOUT_REMOVE_UNTRACKED // Remove untracked files not in index (that are not ignored)
+ CheckoutRemoveIgnored = C.GIT_CHECKOUT_REMOVE_IGNORED // Remove ignored files not in index
+ CheckotUpdateOnly = C.GIT_CHECKOUT_UPDATE_ONLY // Only update existing files, don't create new ones
+ CheckoutDontUpdateIndex = C.GIT_CHECKOUT_DONT_UPDATE_INDEX // Normally checkout updates index entries as it goes; this stops that
+ CheckoutNoRefresh = C.GIT_CHECKOUT_NO_REFRESH // Don't refresh index/config/etc before doing checkout
+ CheckooutDisablePathspecMatch = C.GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH // Treat pathspec as simple list of exact match file paths
+ CheckoutSkipUnmerged = C.GIT_CHECKOUT_SKIP_UNMERGED // Allow checkout to skip unmerged files (NOT IMPLEMENTED)
+ CheckoutUserOurs = C.GIT_CHECKOUT_USE_OURS // For unmerged files, checkout stage 2 from index (NOT IMPLEMENTED)
+ CheckoutUseTheirs = C.GIT_CHECKOUT_USE_THEIRS // For unmerged files, checkout stage 3 from index (NOT IMPLEMENTED)
+ CheckoutUpdateSubmodules = C.GIT_CHECKOUT_UPDATE_SUBMODULES // Recursively checkout submodules with same options (NOT IMPLEMENTED)
+ CheckoutUpdateSubmodulesIfChanged = C.GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED // Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED)
)
type CheckoutOpts struct {
diff --git a/object.go b/object.go
index c6cd8a8..7b141aa 100644
--- a/object.go
+++ b/object.go
@@ -10,13 +10,13 @@ import "runtime"
type ObjectType int
-var (
- OBJ_ANY ObjectType = C.GIT_OBJ_ANY
- OBJ_BAD ObjectType = C.GIT_OBJ_BAD
- OBJ_COMMIT ObjectType = C.GIT_OBJ_COMMIT
- OBJ_TREE ObjectType = C.GIT_OBJ_TREE
- OBJ_BLOB ObjectType = C.GIT_OBJ_BLOB
- OBJ_TAG ObjectType = C.GIT_OBJ_TAG
+const (
+ ObjectAny ObjectType = C.GIT_OBJ_ANY
+ ObjectBad = C.GIT_OBJ_BAD
+ ObjectCommit = C.GIT_OBJ_COMMIT
+ ObjectTree = C.GIT_OBJ_TREE
+ ObjectBlob = C.GIT_OBJ_BLOB
+ ObjectTag = C.GIT_OBJ_TAG
)
type Object interface {
@@ -31,17 +31,17 @@ type gitObject struct {
func (t ObjectType) String() (string) {
switch (t) {
- case OBJ_ANY:
+ case ObjectAny:
return "Any"
- case OBJ_BAD:
+ case ObjectBad:
return "Bad"
- case OBJ_COMMIT:
+ case ObjectCommit:
return "Commit"
- case OBJ_TREE:
+ case ObjectTree:
return "Tree"
- case OBJ_BLOB:
+ case ObjectBlob:
return "Blob"
- case OBJ_TAG:
+ case ObjectTag:
return "tag"
}
// Never reached
@@ -64,17 +64,17 @@ func (o *gitObject) Free() {
func allocObject(cobj *C.git_object) Object {
switch ObjectType(C.git_object_type(cobj)) {
- case OBJ_COMMIT:
+ case ObjectCommit:
commit := &Commit{gitObject{cobj}}
runtime.SetFinalizer(commit, (*Commit).Free)
return commit
- case OBJ_TREE:
+ case ObjectTree:
tree := &Tree{gitObject{cobj}}
runtime.SetFinalizer(tree, (*Tree).Free)
return tree
- case OBJ_BLOB:
+ case ObjectBlob:
blob := &Blob{gitObject{cobj}}
runtime.SetFinalizer(blob, (*Blob).Free)
return blob
diff --git a/object_test.go b/object_test.go
index 82dce98..85daf78 100644
--- a/object_test.go
+++ b/object_test.go
@@ -16,15 +16,19 @@ func TestObjectPoymorphism(t *testing.T) {
checkFatal(t, err)
obj = commit
- if obj.Type() != OBJ_COMMIT {
+ if obj.Type() != ObjectCommit {
t.Fatalf("Wrong object type, expected commit, have %v", obj.Type())
}
+ commitTree, err := commit.Tree()
+ checkFatal(t, err)
+ commitTree.EntryCount()
+
tree, err := repo.LookupTree(treeId)
checkFatal(t, err)
obj = tree
- if obj.Type() != OBJ_TREE {
+ if obj.Type() != ObjectTree {
t.Fatalf("Wrong object type, expected tree, have %v", obj.Type())
}
@@ -33,10 +37,15 @@ func TestObjectPoymorphism(t *testing.T) {
t.Fatalf("Converting back to *Tree is not ok")
}
- if tree2.EntryByName("README") == nil {
+ entry := tree2.EntryByName("README")
+ if entry == nil {
t.Fatalf("Tree did not have expected \"README\" entry")
}
+ if entry.Filemode != FilemodeBlob {
+ t.Fatal("Wrong filemode for \"README\"")
+ }
+
_, ok = obj.(*Commit)
if ok {
t.Fatalf("*Tree is somehow the same as *Commit")
@@ -50,19 +59,19 @@ func TestObjectPoymorphism(t *testing.T) {
t.Fatalf("Lookup creates the wrong type")
}
- if obj.Type() != OBJ_TREE {
+ if obj.Type() != ObjectTree {
t.Fatalf("Type() doesn't agree with dynamic type")
}
obj, err = repo.RevparseSingle("HEAD")
checkFatal(t, err)
- if obj.Type() != OBJ_COMMIT || obj.Id().String() != commit.Id().String() {
+ if obj.Type() != ObjectCommit || obj.Id().String() != commit.Id().String() {
t.Fatalf("Failed to parse the right revision")
}
obj, err = repo.RevparseSingle("HEAD^{tree}")
checkFatal(t, err)
- if obj.Type() != OBJ_TREE || obj.Id().String() != tree.Id().String() {
+ if obj.Type() != ObjectTree || obj.Id().String() != tree.Id().String() {
t.Fatalf("Failed to parse the right revision")
}
}
diff --git a/odb.go b/odb.go
index 9d7d02e..3212bac 100644
--- a/odb.go
+++ b/odb.go
@@ -23,7 +23,7 @@ func (v *Odb) Exists(oid *Oid) bool {
return ret != 0
}
-func (v *Odb) Write(data []byte, otype int) (oid *Oid, err error) {
+func (v *Odb) Write(data []byte, otype ObjectType) (oid *Oid, err error) {
oid = new(Oid)
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&data))
ret := C.git_odb_write(oid.toC(), v.ptr, unsafe.Pointer(hdr.Data), C.size_t(hdr.Len), C.git_otype(otype))
@@ -72,6 +72,33 @@ func (v *Odb) ForEach() chan *Oid {
return ch
}
+// NewReadStream opens a read stream from the ODB. Reading from it will give you the
+// contents of the object.
+func (v *Odb) NewReadStream(id *Oid) (*OdbReadStream, error) {
+ stream := new(OdbReadStream)
+ ret := C.git_odb_open_rstream(&stream.ptr, v.ptr, id.toC())
+ if ret < 0 {
+ return nil, LastError()
+ }
+
+ runtime.SetFinalizer(stream, (*OdbReadStream).Free)
+ return stream, nil
+}
+
+// NewWriteStream opens a write stream to the ODB, which allows you to
+// create a new object in the database. The size and type must be
+// known in advance
+func (v *Odb) NewWriteStream(size int, otype ObjectType) (*OdbWriteStream, error) {
+ stream := new(OdbWriteStream)
+ ret := C.git_odb_open_wstream(&stream.ptr, v.ptr, C.size_t(size), C.git_otype(otype))
+ if ret < 0 {
+ return nil, LastError()
+ }
+
+ runtime.SetFinalizer(stream, (*OdbWriteStream).Free)
+ return stream, nil
+}
+
type OdbObject struct {
ptr *C.git_odb_object
}
@@ -102,3 +129,68 @@ func (object *OdbObject) Data() (data []byte) {
return blob
}
+
+type OdbReadStream struct {
+ ptr *C.git_odb_stream
+}
+
+// Read reads from the stream
+func (stream *OdbReadStream) Read(data []byte) (int, error) {
+ header := (*reflect.SliceHeader)(unsafe.Pointer(&data))
+ ptr := (*C.char)(unsafe.Pointer(header.Data))
+ size := C.size_t(header.Cap)
+ ret := C.git_odb_stream_read(stream.ptr, ptr, size)
+ if ret < 0 {
+ return 0, LastError()
+ }
+
+ header.Len = int(ret)
+
+ return len(data), nil
+}
+
+// Close is a dummy function in order to implement the Closer and
+// ReadCloser interfaces
+func (stream *OdbReadStream) Close() error {
+ return nil
+}
+
+func (stream *OdbReadStream) Free() {
+ runtime.SetFinalizer(stream, nil)
+ C.git_odb_stream_free(stream.ptr)
+}
+
+type OdbWriteStream struct {
+ ptr *C.git_odb_stream
+ Id Oid
+}
+
+// Write writes to the stream
+func (stream *OdbWriteStream) Write(data []byte) (int, error) {
+ header := (*reflect.SliceHeader)(unsafe.Pointer(&data))
+ ptr := (*C.char)(unsafe.Pointer(header.Data))
+ size := C.size_t(header.Len)
+
+ ret := C.git_odb_stream_write(stream.ptr, ptr, size)
+ if ret < 0 {
+ return 0, LastError()
+ }
+
+ return len(data), nil
+}
+
+// Close signals that all the data has been written and stores the
+// resulting object id in the stream's Id field.
+func (stream *OdbWriteStream) Close() error {
+ ret := C.git_odb_stream_finalize_write(stream.Id.toC(), stream.ptr)
+ if ret < 0 {
+ return LastError()
+ }
+
+ return nil
+}
+
+func (stream *OdbWriteStream) Free() {
+ runtime.SetFinalizer(stream, nil)
+ C.git_odb_stream_free(stream.ptr)
+}
diff --git a/odb_test.go b/odb_test.go
new file mode 100644
index 0000000..bf1f847
--- /dev/null
+++ b/odb_test.go
@@ -0,0 +1,35 @@
+package git
+
+import (
+ "io"
+ "os"
+ "testing"
+)
+
+func TestOdbStream(t *testing.T) {
+ repo := createTestRepo(t)
+ defer os.RemoveAll(repo.Workdir())
+ _, _ = seedTestRepo(t, repo)
+
+ odb, error := repo.Odb()
+ checkFatal(t, error)
+
+ str := "hello, world!"
+
+ stream, error := odb.NewWriteStream(len(str), OBJ_BLOB)
+ checkFatal(t, error)
+ n, error := io.WriteString(stream, str)
+ checkFatal(t, error)
+ if n != len(str) {
+ t.Fatalf("Bad write length %v != %v", n, len(str))
+ }
+
+ error = stream.Close()
+ checkFatal(t, error)
+
+ expectedId, error := NewOidFromString("30f51a3fba5274d53522d0f19748456974647b4f")
+ checkFatal(t, error)
+ if stream.Id.Cmp(expectedId) != 0 {
+ t.Fatal("Wrong data written")
+ }
+} \ No newline at end of file
diff --git a/reference.go b/reference.go
index cd8c42a..6ebe00f 100644
--- a/reference.go
+++ b/reference.go
@@ -11,9 +11,10 @@ import (
"unsafe"
)
-var (
- SYMBOLIC = C.GIT_REF_SYMBOLIC
- OID = C.GIT_REF_OID
+type ReferenceType int
+const (
+ ReferenceSymbolic ReferenceType = C.GIT_REF_SYMBOLIC
+ ReferenceOid = C.GIT_REF_OID
)
type Reference struct {
@@ -103,8 +104,8 @@ func (v *Reference) Name() string {
return C.GoString(C.git_reference_name(v.ptr))
}
-func (v *Reference) Type() int {
- return int(C.git_reference_type(v.ptr))
+func (v *Reference) Type() ReferenceType {
+ return ReferenceType(C.git_reference_type(v.ptr))
}
func (v *Reference) Free() {
diff --git a/reference_test.go b/reference_test.go
index eb9bb3f..f955a2c 100644
--- a/reference_test.go
+++ b/reference_test.go
@@ -19,11 +19,11 @@ func TestRefModification(t *testing.T) {
tag, err := repo.LookupReference("refs/tags/tree")
checkFatal(t, err)
- checkRefType(t, tag, OID)
+ checkRefType(t, tag, ReferenceOid)
ref, err := repo.LookupReference("HEAD")
checkFatal(t, err)
- checkRefType(t, ref, SYMBOLIC)
+ checkRefType(t, ref, ReferenceSymbolic)
if target := ref.Target(); target != nil {
t.Fatalf("Expected nil *Oid, got %v", target)
@@ -31,7 +31,7 @@ func TestRefModification(t *testing.T) {
ref, err = ref.Resolve()
checkFatal(t, err)
- checkRefType(t, ref, OID)
+ checkRefType(t, ref, ReferenceOid)
if target := ref.Target(); target == nil {
t.Fatalf("Expected valid target got nil")
@@ -49,7 +49,7 @@ func TestRefModification(t *testing.T) {
checkFatal(t, err)
tag, err = repo.LookupReference("refs/tags/renamed")
checkFatal(t, err)
- checkRefType(t, ref, OID)
+ checkRefType(t, ref, ReferenceOid)
}
@@ -162,7 +162,7 @@ func compareStringList(t *testing.T, expected, actual []string) {
}
}
-func checkRefType(t *testing.T, ref *Reference, kind int) {
+func checkRefType(t *testing.T, ref *Reference, kind ReferenceType) {
if ref.Type() == kind {
return
}
diff --git a/repository.go b/repository.go
index 3a44000..eec95dc 100644
--- a/repository.go
+++ b/repository.go
@@ -83,11 +83,11 @@ func (v *Repository) lookupType(oid *Oid, t ObjectType) (Object, error) {
}
func (v *Repository) Lookup(oid *Oid) (Object, error) {
- return v.lookupType(oid, OBJ_ANY)
+ return v.lookupType(oid, ObjectAny)
}
func (v *Repository) LookupTree(oid *Oid) (*Tree, error) {
- obj, err := v.lookupType(oid, OBJ_TREE)
+ obj, err := v.lookupType(oid, ObjectTree)
if err != nil {
return nil, err
}
@@ -96,7 +96,7 @@ func (v *Repository) LookupTree(oid *Oid) (*Tree, error) {
}
func (v *Repository) LookupCommit(oid *Oid) (*Commit, error) {
- obj, err := v.lookupType(oid, OBJ_COMMIT)
+ obj, err := v.lookupType(oid, ObjectCommit)
if err != nil {
return nil, err
}
@@ -105,7 +105,7 @@ func (v *Repository) LookupCommit(oid *Oid) (*Commit, error) {
}
func (v *Repository) LookupBlob(oid *Oid) (*Blob, error) {
- obj, err := v.lookupType(oid, OBJ_BLOB)
+ obj, err := v.lookupType(oid, ObjectBlob)
if err != nil {
return nil, err
}
diff --git a/tree.go b/tree.go
index 9a9fd63..3abd31c 100644
--- a/tree.go
+++ b/tree.go
@@ -13,6 +13,16 @@ import (
"unsafe"
)
+type Filemode int
+const (
+ FilemodeNew Filemode = C.GIT_FILEMODE_NEW
+ FilemodeTree = C.GIT_FILEMODE_TREE
+ FilemodeBlob = C.GIT_FILEMODE_BLOB
+ FilemodeBlobExecutable = C.GIT_FILEMODE_BLOB_EXECUTABLE
+ FilemodeLink = C.GIT_FILEMODE_LINK
+ FilemodeCommit = C.GIT_FILEMODE_COMMIT
+)
+
type Tree struct {
gitObject
}
@@ -21,6 +31,7 @@ type TreeEntry struct {
Name string
Id *Oid
Type ObjectType
+ Filemode int
}
func newTreeEntry(entry *C.git_tree_entry) *TreeEntry {
@@ -28,6 +39,7 @@ func newTreeEntry(entry *C.git_tree_entry) *TreeEntry {
C.GoString(C.git_tree_entry_name(entry)),
newOidFromC(C.git_tree_entry_id(entry)),
ObjectType(C.git_tree_entry_type(entry)),
+ int(C.git_tree_entry_filemode(entry)),
}
}
@@ -43,6 +55,21 @@ func (t Tree) EntryByName(filename string) *TreeEntry {
return newTreeEntry(entry)
}
+// EntryByPath looks up an entry by its full path, recursing into
+// deeper trees if necessary (i.e. if there are slashes in the path)
+func (t Tree) EntryByPath(path string) (*TreeEntry, error) {
+ cpath := C.CString(path)
+ defer C.free(unsafe.Pointer(cpath))
+ var entry *C.git_tree_entry
+
+ ret := C.git_tree_entry_bypath(&entry, t.ptr, cpath)
+ if ret < 0 {
+ return nil, LastError()
+ }
+
+ return newTreeEntry(entry), nil
+}
+
func (t Tree) EntryByIndex(index uint64) *TreeEntry {
entry := C.git_tree_entry_byindex(t.ptr, C.size_t(index))
if entry == nil {
diff --git a/walk.go b/walk.go
index 216eb65..6525db1 100644
--- a/walk.go
+++ b/walk.go
@@ -13,11 +13,12 @@ import (
// RevWalk
+type SortType uint
const (
- SORT_NONE = C.GIT_SORT_NONE
- SORT_TOPOLOGICAL = C.GIT_SORT_TOPOLOGICAL
- SORT_TIME = C.GIT_SORT_TIME
- SORT_REVERSE = C.GIT_SORT_REVERSE
+ SortNone SortType = C.GIT_SORT_NONE
+ SortTopological = C.GIT_SORT_TOPOLOGICAL
+ SortTime = C.GIT_SORT_TIME
+ SortReverse = C.GIT_SORT_REVERSE
)
type RevWalk struct {
@@ -81,7 +82,7 @@ func (v *RevWalk) Iterate(fun RevWalkIterator) (err error) {
return nil
}
-func (v *RevWalk) Sorting(sm uint) {
+func (v *RevWalk) Sorting(sm SortType) {
C.git_revwalk_sorting(v.ptr, C.uint(sm))
}