Skip to content

Commit 2a50348

Browse files
committed
worktree: implement locking mechanisms
Working trees support locking by creating a file `locked` inside the tree's gitdir with an optional reason inside. Support this feature by adding functions to get and set the locking status.
1 parent dea7488 commit 2a50348

File tree

4 files changed

+170
-0
lines changed

4 files changed

+170
-0
lines changed

include/git2/worktree.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#define INCLUDE_git_worktree_h__
99

1010
#include "common.h"
11+
#include "buffer.h"
1112
#include "types.h"
1213
#include "strarray.h"
1314

@@ -76,6 +77,41 @@ GIT_EXTERN(int) git_worktree_validate(const git_worktree *wt);
7677
*/
7778
GIT_EXTERN(int) git_worktree_add(git_worktree **out, git_repository *repo, const char *name, const char *path);
7879

80+
/*
81+
* Lock worktree if not already locked
82+
*
83+
* Lock a worktree, optionally specifying a reason why the linked
84+
* working tree is being locked.
85+
*
86+
* @param wt Worktree to lock
87+
* @param reason Reason why the working tree is being locked
88+
* @return 0 on success, non-zero otherwise
89+
*/
90+
GIT_EXTERN(int) git_worktree_lock(git_worktree *wt, char *reason);
91+
92+
/**
93+
* Unlock a locked worktree
94+
*
95+
* @param wt Worktree to unlock
96+
* @return 0 on success, 1 if worktree was not locked, error-code
97+
* otherwise
98+
*/
99+
GIT_EXTERN(int) git_worktree_unlock(git_worktree *wt);
100+
101+
/**
102+
* Check if worktree is locked
103+
*
104+
* A worktree may be locked if the linked working tree is stored
105+
* on a portable device which is not available.
106+
*
107+
* @param reason Buffer to store reason in. If NULL no reason is stored.
108+
* @param wt Worktree to check
109+
* @return 0 when the working tree not locked, a value greater
110+
* than zero if it is locked, less than zero if there was an
111+
* error
112+
*/
113+
GIT_EXTERN(int) git_worktree_is_locked(git_buf *reason, const git_worktree *wt);
114+
79115
/** @} */
80116
GIT_END_DECL
81117
#endif

src/worktree.c

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ int git_worktree_lookup(git_worktree **out, git_repository *repo, const char *na
143143
goto out;
144144
}
145145
wt->gitdir_path = git_buf_detach(&path);
146+
wt->locked = !!git_worktree_is_locked(NULL, wt);
146147

147148
(*out) = wt;
148149

@@ -283,3 +284,75 @@ int git_worktree_add(git_worktree **out, git_repository *repo, const char *name,
283284

284285
return err;
285286
}
287+
288+
int git_worktree_lock(git_worktree *wt, char *creason)
289+
{
290+
git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
291+
int err;
292+
293+
assert(wt);
294+
295+
if ((err = git_worktree_is_locked(NULL, wt)) < 0)
296+
goto out;
297+
298+
if ((err = git_buf_joinpath(&path, wt->gitdir_path, "locked")) < 0)
299+
goto out;
300+
301+
if (creason)
302+
git_buf_attach_notowned(&buf, creason, strlen(creason));
303+
304+
if ((err = git_futils_writebuffer(&buf, path.ptr, O_CREAT|O_EXCL|O_WRONLY, 0644)) < 0)
305+
goto out;
306+
307+
wt->locked = 1;
308+
309+
out:
310+
git_buf_free(&path);
311+
312+
return err;
313+
}
314+
315+
int git_worktree_unlock(git_worktree *wt)
316+
{
317+
git_buf path = GIT_BUF_INIT;
318+
319+
assert(wt);
320+
321+
if (!git_worktree_is_locked(NULL, wt))
322+
return 0;
323+
324+
if (git_buf_joinpath(&path, wt->gitdir_path, "locked") < 0)
325+
return -1;
326+
327+
if (p_unlink(path.ptr) != 0) {
328+
git_buf_free(&path);
329+
return -1;
330+
}
331+
332+
wt->locked = 0;
333+
334+
git_buf_free(&path);
335+
336+
return 0;
337+
}
338+
339+
int git_worktree_is_locked(git_buf *reason, const git_worktree *wt)
340+
{
341+
git_buf path = GIT_BUF_INIT;
342+
int ret;
343+
344+
assert(wt);
345+
346+
if (reason)
347+
git_buf_clear(reason);
348+
349+
if ((ret = git_buf_joinpath(&path, wt->gitdir_path, "locked")) < 0)
350+
goto out;
351+
if ((ret = git_path_exists(path.ptr)) && reason)
352+
git_futils_readbuffer(reason, path.ptr);
353+
354+
out:
355+
git_buf_free(&path);
356+
357+
return ret;
358+
}

src/worktree.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ struct git_worktree {
2626
char *commondir_path;
2727
/* Path to the parent's .git directory */
2828
char *parent_path;
29+
30+
int locked:1;
2931
};
3032

3133
#endif

tests/worktree/worktree.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,3 +336,62 @@ void test_worktree_worktree__validate_invalid_parent(void)
336336
wt->parent_path = NULL;
337337
git_worktree_free(wt);
338338
}
339+
340+
void test_worktree_worktree__lock_with_reason(void)
341+
{
342+
git_worktree *wt;
343+
git_buf reason = GIT_BUF_INIT;
344+
345+
cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
346+
347+
cl_assert(!git_worktree_is_locked(NULL, wt));
348+
cl_git_pass(git_worktree_lock(wt, "because"));
349+
cl_assert(git_worktree_is_locked(&reason, wt) > 0);
350+
cl_assert_equal_s(reason.ptr, "because");
351+
cl_assert(wt->locked);
352+
353+
git_buf_free(&reason);
354+
git_worktree_free(wt);
355+
}
356+
357+
void test_worktree_worktree__lock_without_reason(void)
358+
{
359+
git_worktree *wt;
360+
git_buf reason = GIT_BUF_INIT;
361+
362+
cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
363+
364+
cl_assert(!git_worktree_is_locked(NULL, wt));
365+
cl_git_pass(git_worktree_lock(wt, NULL));
366+
cl_assert(git_worktree_is_locked(&reason, wt) > 0);
367+
cl_assert_equal_i(reason.size, 0);
368+
cl_assert(wt->locked);
369+
370+
git_buf_free(&reason);
371+
git_worktree_free(wt);
372+
}
373+
374+
void test_worktree_worktree__unlock_unlocked_worktree(void)
375+
{
376+
git_worktree *wt;
377+
378+
cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
379+
cl_assert(!git_worktree_is_locked(NULL, wt));
380+
cl_assert(git_worktree_unlock(wt) == 0);
381+
cl_assert(!wt->locked);
382+
383+
git_worktree_free(wt);
384+
}
385+
386+
void test_worktree_worktree__unlock_locked_worktree(void)
387+
{
388+
git_worktree *wt;
389+
390+
cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
391+
cl_git_pass(git_worktree_lock(wt, NULL));
392+
cl_assert(git_worktree_is_locked(NULL, wt));
393+
cl_git_pass(git_worktree_unlock(wt));
394+
cl_assert(!wt->locked);
395+
396+
git_worktree_free(wt);
397+
}

0 commit comments

Comments
 (0)