Skip to content

Commit 5d989f2

Browse files
committed
Merge pull request libgit2#236 from clns/object-peel
Add ability to peel any git object
2 parents fba081d + 17950c1 commit 5d989f2

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

object.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type Object interface {
2222
Id() *Oid
2323
Type() ObjectType
2424
Owner() *Repository
25+
Peel(t ObjectType) (Object, error)
2526
}
2627

2728
type gitObject struct {
@@ -69,6 +70,31 @@ func (o *gitObject) Free() {
6970
C.git_object_free(o.ptr)
7071
}
7172

73+
// Peel recursively peels an object until an object of the specified type is met.
74+
//
75+
// If the query cannot be satisfied due to the object model, ErrInvalidSpec
76+
// will be returned (e.g. trying to peel a blob to a tree).
77+
//
78+
// If you pass ObjectAny as the target type, then the object will be peeled
79+
// until the type changes. A tag will be peeled until the referenced object
80+
// is no longer a tag, and a commit will be peeled to a tree. Any other object
81+
// type will return ErrInvalidSpec.
82+
//
83+
// If peeling a tag we discover an object which cannot be peeled to the target
84+
// type due to the object model, an error will be returned.
85+
func (o *gitObject) Peel(t ObjectType) (Object, error) {
86+
var cobj *C.git_object
87+
88+
runtime.LockOSThread()
89+
defer runtime.UnlockOSThread()
90+
91+
if err := C.git_object_peel(&cobj, o.ptr, C.git_otype(t)); err < 0 {
92+
return nil, MakeGitError(err)
93+
}
94+
95+
return allocObject(cobj, o.repo), nil
96+
}
97+
7298
func allocObject(cobj *C.git_object, repo *Repository) Object {
7399
obj := gitObject{
74100
ptr: cobj,

object_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,63 @@ func TestObjectOwner(t *testing.T) {
102102
checkOwner(t, repo, commit)
103103
checkOwner(t, repo, tree)
104104
}
105+
106+
func TestObjectPeel(t *testing.T) {
107+
repo := createTestRepo(t)
108+
defer cleanupTestRepo(t, repo)
109+
110+
commitID, treeID := seedTestRepo(t, repo)
111+
112+
var obj Object
113+
114+
commit, err := repo.LookupCommit(commitID)
115+
checkFatal(t, err)
116+
117+
obj, err = commit.Peel(ObjectAny)
118+
checkFatal(t, err)
119+
120+
if obj.Type() != ObjectTree {
121+
t.Fatalf("Wrong object type when peeling a commit, expected tree, have %v", obj.Type())
122+
}
123+
124+
obj, err = commit.Peel(ObjectTag)
125+
126+
if !IsErrorCode(err, ErrInvalidSpec) {
127+
t.Fatalf("Wrong error when peeling a commit to a tag, expected ErrInvalidSpec, have %v", err)
128+
}
129+
130+
tree, err := repo.LookupTree(treeID)
131+
checkFatal(t, err)
132+
133+
obj, err = tree.Peel(ObjectAny)
134+
135+
if !IsErrorCode(err, ErrInvalidSpec) {
136+
t.Fatalf("Wrong error when peeling a tree, expected ErrInvalidSpec, have %v", err)
137+
}
138+
139+
entry := tree.EntryByName("README")
140+
141+
blob, err := repo.LookupBlob(entry.Id)
142+
checkFatal(t, err)
143+
144+
obj, err = blob.Peel(ObjectAny)
145+
146+
if !IsErrorCode(err, ErrInvalidSpec) {
147+
t.Fatalf("Wrong error when peeling a blob, expected ErrInvalidSpec, have %v", err)
148+
}
149+
150+
tagID := createTestTag(t, repo, commit)
151+
152+
tag, err := repo.LookupTag(tagID)
153+
checkFatal(t, err)
154+
155+
obj, err = tag.Peel(ObjectAny)
156+
checkFatal(t, err)
157+
158+
if obj.Type() != ObjectCommit {
159+
t.Fatalf("Wrong object type when peeling a tag, expected commit, have %v", obj.Type())
160+
}
161+
162+
// TODO: Should test a tag that annotates a different object than a commit
163+
// but it's impossible at the moment to tag such an object.
164+
}

0 commit comments

Comments
 (0)