summaryrefslogtreecommitdiff
path: root/object.go
blob: 6ecebf8ce1604bf98f2e7e917785cdc79966d461 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package git

/*
#include <git2.h>
*/
import "C"
import "runtime"

type ObjectType int

const (
	ObjectAny    ObjectType = C.GIT_OBJ_ANY
	ObjectBad    ObjectType = C.GIT_OBJ_BAD
	ObjectCommit ObjectType = C.GIT_OBJ_COMMIT
	ObjectTree   ObjectType = C.GIT_OBJ_TREE
	ObjectBlob   ObjectType = C.GIT_OBJ_BLOB
	ObjectTag    ObjectType = C.GIT_OBJ_TAG
)

type Object interface {
	Free()
	Id() *Oid
	Type() ObjectType
	Owner() *Repository
	Peel(t ObjectType) (Object, error)
}

type gitObject struct {
	ptr  *C.git_object
	repo *Repository
}

func (t ObjectType) String() string {
	switch t {
	case ObjectAny:
		return "Any"
	case ObjectBad:
		return "Bad"
	case ObjectCommit:
		return "Commit"
	case ObjectTree:
		return "Tree"
	case ObjectBlob:
		return "Blob"
	case ObjectTag:
		return "Tag"
	}
	// Never reached
	return ""
}

func (o gitObject) Id() *Oid {
	return newOidFromC(C.git_object_id(o.ptr))
}

func (o gitObject) Type() ObjectType {
	return ObjectType(C.git_object_type(o.ptr))
}

// Owner returns a weak reference to the repository which owns this
// object
func (o gitObject) Owner() *Repository {
	return &Repository{
		ptr: C.git_object_owner(o.ptr),
	}
}

func (o *gitObject) Free() {
	runtime.SetFinalizer(o, nil)
	C.git_object_free(o.ptr)
}

// Peel recursively peels an object until an object of the specified type is met.
//
// If the query cannot be satisfied due to the object model, ErrInvalidSpec
// will be returned (e.g. trying to peel a blob to a tree).
//
// If you pass ObjectAny as the target type, then the object will be peeled
// until the type changes. A tag will be peeled until the referenced object
// is no longer a tag, and a commit will be peeled to a tree. Any other object
// type will return ErrInvalidSpec.
//
// If peeling a tag we discover an object which cannot be peeled to the target
// type due to the object model, an error will be returned.
func (o *gitObject) Peel(t ObjectType) (Object, error) {
	var cobj *C.git_object

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	if err := C.git_object_peel(&cobj, o.ptr, C.git_otype(t)); err < 0 {
		return nil, MakeGitError(err)
	}

	return allocObject(cobj, o.repo), nil
}

func allocObject(cobj *C.git_object, repo *Repository) Object {
	obj := gitObject{
		ptr:  cobj,
		repo: repo,
	}

	switch ObjectType(C.git_object_type(cobj)) {
	case ObjectCommit:
		commit := &Commit{
			gitObject: obj,
			cast_ptr:  (*C.git_commit)(cobj),
		}
		runtime.SetFinalizer(commit, (*Commit).Free)
		return commit

	case ObjectTree:
		tree := &Tree{
			gitObject: obj,
			cast_ptr:  (*C.git_tree)(cobj),
		}
		runtime.SetFinalizer(tree, (*Tree).Free)
		return tree

	case ObjectBlob:
		blob := &Blob{
			gitObject: obj,
			cast_ptr:  (*C.git_blob)(cobj),
		}
		runtime.SetFinalizer(blob, (*Blob).Free)
		return blob
	case ObjectTag:
		tag := &Tag{
			gitObject: obj,
			cast_ptr:  (*C.git_tag)(cobj),
		}
		runtime.SetFinalizer(tag, (*Tag).Free)
		return tag
	}

	return nil
}