Skip to content

Commit 9232cba

Browse files
authored
Merge pull request #1 from coveord/feature/DT-5224-worktree-api
Added the bindings for the Worktree API for @wtrep
2 parents 4b14d29 + bdf5a6f commit 9232cba

File tree

3 files changed

+500
-0
lines changed

3 files changed

+500
-0
lines changed

repository.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ type Repository struct {
3636
// Stashes represents the collection of stashes and can be used to
3737
// save, apply and iterate over stash states in this repository.
3838
Stashes StashCollection
39+
// Worktrees represents the collection of worktrees and can be used to
40+
// add, list and remove worktrees for this repository
41+
Worktrees WorktreeCollection
3942

4043
// weak indicates that a repository is a weak pointer and should not be
4144
// freed.
@@ -52,6 +55,7 @@ func newRepositoryFromC(ptr *C.git_repository) *Repository {
5255
repo.Notes.repo = repo
5356
repo.Tags.repo = repo
5457
repo.Stashes.repo = repo
58+
repo.Worktrees.repo = repo
5559

5660
runtime.SetFinalizer(repo, (*Repository).Free)
5761

worktree.go

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
package git
2+
3+
/*
4+
#include <git2.h>
5+
*/
6+
import "C"
7+
import (
8+
"runtime"
9+
"unsafe"
10+
)
11+
12+
type WorktreeCollection struct {
13+
doNotCompare
14+
repo *Repository
15+
}
16+
17+
type Worktree struct {
18+
doNotCompare
19+
ptr *C.git_worktree
20+
}
21+
22+
type AddWorktreeOptions struct {
23+
// Lock the newly created worktree
24+
Lock bool
25+
// Reference to use for the new worktree
26+
Reference *Reference
27+
// CheckoutOptions is used for configuring the checkout for the newly created worktree
28+
CheckoutOptions CheckoutOptions
29+
}
30+
31+
// Add adds a new working tree for the given repository
32+
func (c *WorktreeCollection) Add(name string, path string, options *AddWorktreeOptions) (*Worktree, error) {
33+
cName := C.CString(name)
34+
defer C.free(unsafe.Pointer(cName))
35+
36+
cPath := C.CString(path)
37+
defer C.free(unsafe.Pointer(cPath))
38+
39+
var err error
40+
cOptions := populateAddWorktreeOptions(&C.git_worktree_add_options{}, options, &err)
41+
defer freeAddWorktreeOptions(cOptions)
42+
43+
runtime.LockOSThread()
44+
defer runtime.UnlockOSThread()
45+
46+
var ptr *C.git_worktree
47+
ret := C.git_worktree_add(&ptr, c.repo.ptr, cName, cPath, cOptions)
48+
runtime.KeepAlive(c)
49+
if options != nil && options.Reference != nil {
50+
runtime.KeepAlive(options.Reference)
51+
}
52+
53+
if ret == C.int(ErrorCodeUser) && err != nil {
54+
return nil, err
55+
} else if ret < 0 {
56+
return nil, MakeGitError(ret)
57+
}
58+
return newWorktreeFromC(ptr), nil
59+
}
60+
61+
// List lists names of linked working trees for the given repository
62+
func (c *WorktreeCollection) List() ([]string, error) {
63+
var strC C.git_strarray
64+
defer C.git_strarray_dispose(&strC)
65+
66+
runtime.LockOSThread()
67+
defer runtime.UnlockOSThread()
68+
69+
ret := C.git_worktree_list(&strC, c.repo.ptr)
70+
runtime.KeepAlive(c)
71+
if ret < 0 {
72+
return nil, MakeGitError(ret)
73+
}
74+
75+
w := makeStringsFromCStrings(strC.strings, int(strC.count))
76+
return w, nil
77+
}
78+
79+
// Lookup gets a working tree by its name for the given repository
80+
func (c *WorktreeCollection) Lookup(name string) (*Worktree, error) {
81+
cname := C.CString(name)
82+
defer C.free(unsafe.Pointer(cname))
83+
84+
runtime.LockOSThread()
85+
defer runtime.UnlockOSThread()
86+
87+
var ptr *C.git_worktree
88+
ret := C.git_worktree_lookup(&ptr, c.repo.ptr, cname)
89+
runtime.KeepAlive(c)
90+
91+
if ret < 0 {
92+
return nil, MakeGitError(ret)
93+
} else if ptr == nil {
94+
return nil, nil
95+
}
96+
return newWorktreeFromC(ptr), nil
97+
}
98+
99+
// OpenFromRepository retrieves a worktree for the given repository
100+
func (c *WorktreeCollection) OpenFromRepository() (*Worktree, error) {
101+
runtime.LockOSThread()
102+
defer runtime.UnlockOSThread()
103+
104+
var ptr *C.git_worktree
105+
ret := C.git_worktree_open_from_repository(&ptr, c.repo.ptr)
106+
runtime.KeepAlive(c)
107+
108+
if ret < 0 {
109+
return nil, MakeGitError(ret)
110+
}
111+
return newWorktreeFromC(ptr), nil
112+
}
113+
114+
func newWorktreeFromC(ptr *C.git_worktree) *Worktree {
115+
worktree := &Worktree{ptr: ptr}
116+
runtime.SetFinalizer(worktree, (*Worktree).Free)
117+
return worktree
118+
}
119+
120+
func freeAddWorktreeOptions(cOptions *C.git_worktree_add_options) {
121+
if cOptions == nil {
122+
return
123+
}
124+
freeCheckoutOptions(&cOptions.checkout_options)
125+
}
126+
127+
func populateAddWorktreeOptions(cOptions *C.git_worktree_add_options, options *AddWorktreeOptions, errorTarget *error) *C.git_worktree_add_options {
128+
C.git_worktree_add_options_init(cOptions, C.GIT_WORKTREE_ADD_OPTIONS_VERSION)
129+
if options == nil {
130+
return nil
131+
}
132+
133+
populateCheckoutOptions(&cOptions.checkout_options, &options.CheckoutOptions, errorTarget)
134+
cOptions.lock = cbool(options.Lock)
135+
if options.Reference != nil {
136+
cOptions.ref = options.Reference.ptr
137+
}
138+
return cOptions
139+
}
140+
141+
// Free a previously allocated worktree
142+
func (w *Worktree) Free() {
143+
runtime.SetFinalizer(w, nil)
144+
C.git_worktree_free(w.ptr)
145+
}
146+
147+
// IsLocked checks if the given worktree is locked
148+
func (w *Worktree) IsLocked() (locked bool, reason string, err error) {
149+
buf := C.git_buf{}
150+
defer C.git_buf_dispose(&buf)
151+
152+
runtime.LockOSThread()
153+
defer runtime.UnlockOSThread()
154+
155+
ret := C.git_worktree_is_locked(&buf, w.ptr)
156+
runtime.KeepAlive(w)
157+
158+
if ret < 0 {
159+
return false, "", MakeGitError(ret)
160+
}
161+
return ret != 0, C.GoString(buf.ptr), nil
162+
}
163+
164+
type WorktreePruneFlag uint32
165+
166+
const (
167+
// WorktreePruneValid means prune working tree even if working tree is valid
168+
WorktreePruneValid WorktreePruneFlag = C.GIT_WORKTREE_PRUNE_VALID
169+
// WorktreePruneLocked means prune working tree even if it is locked
170+
WorktreePruneLocked WorktreePruneFlag = C.GIT_WORKTREE_PRUNE_LOCKED
171+
// WorktreePruneWorkingTree means prune checked out working tree
172+
WorktreePruneWorkingTree WorktreePruneFlag = C.GIT_WORKTREE_PRUNE_WORKING_TREE
173+
)
174+
175+
// IsPrunable checks that the worktree is prunable with the given flags
176+
func (w *Worktree) IsPrunable(flags WorktreePruneFlag) (bool, error) {
177+
cOptions := C.git_worktree_prune_options{}
178+
C.git_worktree_prune_options_init(&cOptions, C.GIT_WORKTREE_PRUNE_OPTIONS_VERSION)
179+
cOptions.flags = C.uint32_t(flags)
180+
181+
runtime.LockOSThread()
182+
defer runtime.UnlockOSThread()
183+
184+
ret := C.git_worktree_is_prunable(w.ptr, &cOptions)
185+
runtime.KeepAlive(w)
186+
187+
if ret < 0 {
188+
return false, MakeGitError(ret)
189+
}
190+
return ret != 0, nil
191+
}
192+
193+
// Lock locks the worktree if not already locked
194+
func (w *Worktree) Lock(reason string) error {
195+
var cReason *C.char
196+
if reason != "" {
197+
cReason = C.CString(reason)
198+
defer C.free(unsafe.Pointer(cReason))
199+
}
200+
201+
runtime.LockOSThread()
202+
defer runtime.UnlockOSThread()
203+
204+
ret := C.git_worktree_lock(w.ptr, cReason)
205+
runtime.KeepAlive(w)
206+
207+
if ret < 0 {
208+
return MakeGitError(ret)
209+
}
210+
return nil
211+
}
212+
213+
// Name retrieves the name of the worktree
214+
func (w *Worktree) Name() string {
215+
s := C.GoString(C.git_worktree_name(w.ptr))
216+
runtime.KeepAlive(w)
217+
return s
218+
}
219+
220+
// Path retrieves the path of the worktree
221+
func (w *Worktree) Path() string {
222+
s := C.GoString(C.git_worktree_path(w.ptr))
223+
runtime.KeepAlive(w)
224+
return s
225+
}
226+
227+
// Prune the worktree with the provided flags
228+
func (w *Worktree) Prune(flags WorktreePruneFlag) error {
229+
cOptions := C.git_worktree_prune_options{}
230+
C.git_worktree_prune_options_init(&cOptions, C.GIT_WORKTREE_PRUNE_OPTIONS_VERSION)
231+
cOptions.flags = C.uint32_t(flags)
232+
233+
runtime.LockOSThread()
234+
defer runtime.UnlockOSThread()
235+
236+
ret := C.git_worktree_prune(w.ptr, &cOptions)
237+
runtime.KeepAlive(w)
238+
239+
if ret < 0 {
240+
return MakeGitError(ret)
241+
}
242+
return nil
243+
}
244+
245+
// Unlock a locked worktree
246+
func (w *Worktree) Unlock() (notLocked bool, err error) {
247+
runtime.LockOSThread()
248+
defer runtime.UnlockOSThread()
249+
250+
ret := C.git_worktree_unlock(w.ptr)
251+
runtime.KeepAlive(w)
252+
253+
if ret < 0 {
254+
return false, MakeGitError(ret)
255+
}
256+
return ret != 0, nil
257+
}
258+
259+
// Validate checks if the given worktree is valid
260+
func (w *Worktree) Validate() error {
261+
runtime.LockOSThread()
262+
defer runtime.UnlockOSThread()
263+
264+
ret := C.git_worktree_validate(w.ptr)
265+
runtime.KeepAlive(w)
266+
267+
if ret < 0 {
268+
return MakeGitError(ret)
269+
}
270+
return nil
271+
}

0 commit comments

Comments
 (0)