2 * Copyright (C) the libgit2 contributors. All rights reserved.
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
10 #include "git2/branch.h"
11 #include "git2/commit.h"
12 #include "git2/worktree.h"
14 #include "repository.h"
17 static bool is_worktree_dir(const char *dir
)
19 git_buf buf
= GIT_BUF_INIT
;
22 if (git_buf_sets(&buf
, dir
) < 0)
25 error
= git_path_contains_file(&buf
, "commondir")
26 && git_path_contains_file(&buf
, "gitdir")
27 && git_path_contains_file(&buf
, "HEAD");
33 int git_worktree_list(git_strarray
*wts
, git_repository
*repo
)
35 git_vector worktrees
= GIT_VECTOR_INIT
;
36 git_buf path
= GIT_BUF_INIT
;
46 if ((error
= git_buf_printf(&path
, "%s/worktrees/", repo
->commondir
)) < 0)
48 if (!git_path_exists(path
.ptr
) || git_path_is_empty_dir(path
.ptr
))
50 if ((error
= git_path_dirload(&worktrees
, path
.ptr
, path
.size
, 0x0)) < 0)
55 git_vector_foreach(&worktrees
, i
, worktree
) {
56 git_buf_truncate(&path
, len
);
57 git_buf_puts(&path
, worktree
);
59 if (!is_worktree_dir(path
.ptr
)) {
60 git_vector_remove(&worktrees
, i
);
65 wts
->strings
= (char **)git_vector_detach(&wts
->count
, NULL
, &worktrees
);
73 char *git_worktree__read_link(const char *base
, const char *file
)
75 git_buf path
= GIT_BUF_INIT
, buf
= GIT_BUF_INIT
;
79 if (git_buf_joinpath(&path
, base
, file
) < 0)
81 if (git_futils_readbuffer(&buf
, path
.ptr
) < 0)
87 if (!git_path_is_relative(buf
.ptr
))
88 return git_buf_detach(&buf
);
90 if (git_buf_sets(&path
, base
) < 0)
92 if (git_path_apply_relative(&path
, buf
.ptr
) < 0)
96 return git_buf_detach(&path
);
105 static int write_wtfile(const char *base
, const char *file
, const git_buf
*buf
)
107 git_buf path
= GIT_BUF_INIT
;
110 assert(base
&& file
&& buf
);
112 if ((err
= git_buf_joinpath(&path
, base
, file
)) < 0)
115 if ((err
= git_futils_writebuffer(buf
, path
.ptr
, O_CREAT
|O_EXCL
|O_WRONLY
, 0644)) < 0)
124 static int open_worktree_dir(git_worktree
**out
, const char *parent
, const char *dir
, const char *name
)
126 git_buf gitdir
= GIT_BUF_INIT
;
127 git_worktree
*wt
= NULL
;
130 if (!is_worktree_dir(dir
)) {
135 if ((wt
= git__calloc(1, sizeof(struct git_repository
))) == NULL
) {
140 if ((wt
->name
= git__strdup(name
)) == NULL
141 || (wt
->commondir_path
= git_worktree__read_link(dir
, "commondir")) == NULL
142 || (wt
->gitlink_path
= git_worktree__read_link(dir
, "gitdir")) == NULL
143 || (wt
->parent_path
= git__strdup(parent
)) == NULL
) {
148 if ((error
= git_path_prettify_dir(&gitdir
, dir
, NULL
)) < 0)
150 wt
->gitdir_path
= git_buf_detach(&gitdir
);
152 wt
->locked
= !!git_worktree_is_locked(NULL
, wt
);
158 git_worktree_free(wt
);
159 git_buf_free(&gitdir
);
164 int git_worktree_lookup(git_worktree
**out
, git_repository
*repo
, const char *name
)
166 git_buf path
= GIT_BUF_INIT
;
167 git_worktree
*wt
= NULL
;
170 assert(repo
&& name
);
174 if ((error
= git_buf_printf(&path
, "%s/worktrees/%s", repo
->commondir
, name
)) < 0)
177 if ((error
= (open_worktree_dir(out
, git_repository_workdir(repo
), path
.ptr
, name
))) < 0)
184 git_worktree_free(wt
);
189 int git_worktree_open_from_repository(git_worktree
**out
, git_repository
*repo
)
191 git_buf parent
= GIT_BUF_INIT
;
192 const char *gitdir
, *commondir
;
196 if (!git_repository_is_worktree(repo
)) {
197 giterr_set(GITERR_WORKTREE
, "cannot open worktree of a non-worktree repo");
202 gitdir
= git_repository_path(repo
);
203 commondir
= git_repository_commondir(repo
);
205 if ((error
= git_path_prettify_dir(&parent
, "..", commondir
)) < 0)
208 /* The name is defined by the last component in '.git/worktree/%s' */
209 name
= git_path_basename(gitdir
);
211 if ((error
= open_worktree_dir(out
, parent
.ptr
, gitdir
, name
)) < 0)
216 git_buf_free(&parent
);
221 void git_worktree_free(git_worktree
*wt
)
226 git__free(wt
->commondir_path
);
227 git__free(wt
->gitlink_path
);
228 git__free(wt
->gitdir_path
);
229 git__free(wt
->parent_path
);
234 int git_worktree_validate(const git_worktree
*wt
)
236 git_buf buf
= GIT_BUF_INIT
;
241 git_buf_puts(&buf
, wt
->gitdir_path
);
242 if (!is_worktree_dir(buf
.ptr
)) {
243 giterr_set(GITERR_WORKTREE
,
244 "Worktree gitdir ('%s') is not valid",
250 if (!git_path_exists(wt
->parent_path
)) {
251 giterr_set(GITERR_WORKTREE
,
252 "Worktree parent directory ('%s') does not exist ",
258 if (!git_path_exists(wt
->commondir_path
)) {
259 giterr_set(GITERR_WORKTREE
,
260 "Worktree common directory ('%s') does not exist ",
272 int git_worktree_add(git_worktree
**out
, git_repository
*repo
, const char *name
, const char *worktree
)
274 git_buf gitdir
= GIT_BUF_INIT
, wddir
= GIT_BUF_INIT
, buf
= GIT_BUF_INIT
;
275 git_reference
*ref
= NULL
, *head
= NULL
;
276 git_commit
*commit
= NULL
;
277 git_repository
*wt
= NULL
;
278 git_checkout_options coopts
= GIT_CHECKOUT_OPTIONS_INIT
;
281 assert(out
&& repo
&& name
&& worktree
);
285 /* Create gitdir directory ".git/worktrees/<name>" */
286 if ((err
= git_buf_joinpath(&gitdir
, repo
->commondir
, "worktrees")) < 0)
288 if (!git_path_exists(gitdir
.ptr
))
289 if ((err
= git_futils_mkdir(gitdir
.ptr
, 0755, GIT_MKDIR_EXCL
)) < 0)
291 if ((err
= git_buf_joinpath(&gitdir
, gitdir
.ptr
, name
)) < 0)
293 if ((err
= git_futils_mkdir(gitdir
.ptr
, 0755, GIT_MKDIR_EXCL
)) < 0)
295 if ((err
= git_path_prettify_dir(&gitdir
, gitdir
.ptr
, NULL
)) < 0)
298 /* Create worktree work dir */
299 if ((err
= git_futils_mkdir(worktree
, 0755, GIT_MKDIR_EXCL
)) < 0)
301 if ((err
= git_path_prettify_dir(&wddir
, worktree
, NULL
)) < 0)
304 /* Create worktree .git file */
305 if ((err
= git_buf_printf(&buf
, "gitdir: %s\n", gitdir
.ptr
)) < 0)
307 if ((err
= write_wtfile(wddir
.ptr
, ".git", &buf
)) < 0)
310 /* Create gitdir files */
311 if ((err
= git_path_prettify_dir(&buf
, repo
->commondir
, NULL
) < 0)
312 || (err
= git_buf_putc(&buf
, '\n')) < 0
313 || (err
= write_wtfile(gitdir
.ptr
, "commondir", &buf
)) < 0)
315 if ((err
= git_buf_joinpath(&buf
, wddir
.ptr
, ".git")) < 0
316 || (err
= git_buf_putc(&buf
, '\n')) < 0
317 || (err
= write_wtfile(gitdir
.ptr
, "gitdir", &buf
)) < 0)
320 /* Create new branch */
321 if ((err
= git_repository_head(&head
, repo
)) < 0)
323 if ((err
= git_commit_lookup(&commit
, repo
, &head
->target
.oid
)) < 0)
325 if ((err
= git_branch_create(&ref
, repo
, name
, commit
, false)) < 0)
328 /* Set worktree's HEAD */
329 if ((err
= git_repository_create_head(gitdir
.ptr
, git_reference_name(ref
))) < 0)
331 if ((err
= git_repository_open(&wt
, wddir
.ptr
)) < 0)
334 /* Checkout worktree's HEAD */
335 coopts
.checkout_strategy
= GIT_CHECKOUT_FORCE
;
336 if ((err
= git_checkout_head(wt
, &coopts
)) < 0)
340 if ((err
= git_worktree_lookup(out
, repo
, name
)) < 0)
344 git_buf_free(&gitdir
);
345 git_buf_free(&wddir
);
347 git_reference_free(ref
);
348 git_reference_free(head
);
349 git_commit_free(commit
);
350 git_repository_free(wt
);
355 int git_worktree_lock(git_worktree
*wt
, char *creason
)
357 git_buf buf
= GIT_BUF_INIT
, path
= GIT_BUF_INIT
;
362 if ((err
= git_worktree_is_locked(NULL
, wt
)) < 0)
365 if ((err
= git_buf_joinpath(&path
, wt
->gitdir_path
, "locked")) < 0)
369 git_buf_attach_notowned(&buf
, creason
, strlen(creason
));
371 if ((err
= git_futils_writebuffer(&buf
, path
.ptr
, O_CREAT
|O_EXCL
|O_WRONLY
, 0644)) < 0)
382 int git_worktree_unlock(git_worktree
*wt
)
384 git_buf path
= GIT_BUF_INIT
;
388 if (!git_worktree_is_locked(NULL
, wt
))
391 if (git_buf_joinpath(&path
, wt
->gitdir_path
, "locked") < 0)
394 if (p_unlink(path
.ptr
) != 0) {
406 int git_worktree_is_locked(git_buf
*reason
, const git_worktree
*wt
)
408 git_buf path
= GIT_BUF_INIT
;
414 git_buf_clear(reason
);
416 if ((ret
= git_buf_joinpath(&path
, wt
->gitdir_path
, "locked")) < 0)
418 if ((ret
= git_path_exists(path
.ptr
)) && reason
)
419 git_futils_readbuffer(reason
, path
.ptr
);
427 int git_worktree_is_prunable(git_worktree
*wt
, unsigned flags
)
429 git_buf reason
= GIT_BUF_INIT
;
431 if ((flags
& GIT_WORKTREE_PRUNE_LOCKED
) == 0 &&
432 git_worktree_is_locked(&reason
, wt
))
435 git_buf_attach_notowned(&reason
, "no reason given", 15);
436 giterr_set(GITERR_WORKTREE
, "Not pruning locked working tree: '%s'", reason
.ptr
);
437 git_buf_free(&reason
);
442 if ((flags
& GIT_WORKTREE_PRUNE_VALID
) == 0 &&
443 git_worktree_validate(wt
) == 0)
445 giterr_set(GITERR_WORKTREE
, "Not pruning valid working tree");
452 int git_worktree_prune(git_worktree
*wt
, unsigned flags
)
454 git_buf path
= GIT_BUF_INIT
;
458 if (!git_worktree_is_prunable(wt
, flags
)) {
463 /* Delete gitdir in parent repository */
464 if ((err
= git_buf_printf(&path
, "%s/worktrees/%s", wt
->commondir_path
, wt
->name
)) < 0)
466 if (!git_path_exists(path
.ptr
))
468 giterr_set(GITERR_WORKTREE
, "Worktree gitdir '%s' does not exist", path
.ptr
);
472 if ((err
= git_futils_rmdir_r(path
.ptr
, NULL
, GIT_RMDIR_REMOVE_FILES
)) < 0)
475 /* Skip deletion of the actual working tree if it does
476 * not exist or deletion was not requested */
477 if ((flags
& GIT_WORKTREE_PRUNE_WORKING_TREE
) == 0 ||
478 !git_path_exists(wt
->gitlink_path
))
483 if ((wtpath
= git_path_dirname(wt
->gitlink_path
)) == NULL
)
485 git_buf_attach(&path
, wtpath
, 0);
486 if (!git_path_exists(path
.ptr
))
488 giterr_set(GITERR_WORKTREE
, "Working tree '%s' does not exist", path
.ptr
);
492 if ((err
= git_futils_rmdir_r(path
.ptr
, NULL
, GIT_RMDIR_REMOVE_FILES
)) < 0)