]> git.ipfire.org Git - thirdparty/git.git/blame - worktree.c
path.h: move function declarations for path.c functions from cache.h
[thirdparty/git.git] / worktree.c
CommitLineData
a6dc3d36 1#include "cache.h"
0b027f6c 2#include "abspath.h"
36bf1958 3#include "alloc.h"
f394e093 4#include "gettext.h"
b337172c 5#include "repository.h"
ac6c561b
MR
6#include "refs.h"
7#include "strbuf.h"
8#include "worktree.h"
750e8a60 9#include "dir.h"
8d9fdd70 10#include "wt-status.h"
615a84ad 11#include "config.h"
ac6c561b 12
51934904
MR
13void free_worktrees(struct worktree **worktrees)
14{
15 int i = 0;
16
17 for (i = 0; worktrees[i]; i++) {
18 free(worktrees[i]->path);
69dfe3b9 19 free(worktrees[i]->id);
92718b74 20 free(worktrees[i]->head_ref);
346ef530 21 free(worktrees[i]->lock_reason);
fc0c7d5e 22 free(worktrees[i]->prune_reason);
51934904
MR
23 free(worktrees[i]);
24 }
25 free (worktrees);
26}
27
92718b74 28/**
cfaf9f05 29 * Update head_oid, head_ref and is_detached of the given worktree
92718b74 30 */
fa099d23 31static void add_head_info(struct worktree *wt)
92718b74 32{
fa099d23
NTND
33 int flags;
34 const char *target;
35
f1da24ca 36 target = refs_resolve_ref_unsafe(get_worktree_ref_store(wt),
fa099d23 37 "HEAD",
31824d18 38 0,
ce14de03 39 &wt->head_oid, &flags);
fa099d23
NTND
40 if (!target)
41 return;
42
43 if (flags & REF_ISSYMREF)
44 wt->head_ref = xstrdup(target);
45 else
46 wt->is_detached = 1;
92718b74
MR
47}
48
51934904
MR
49/**
50 * get the main worktree
51 */
52static struct worktree *get_main_worktree(void)
1ceb7f90 53{
51934904 54 struct worktree *worktree = NULL;
51934904 55 struct strbuf worktree_path = STRBUF_INIT;
1ceb7f90 56
918d8ff7
ES
57 strbuf_add_real_path(&worktree_path, get_git_common_dir());
58 strbuf_strip_suffix(&worktree_path, "/.git");
51934904 59
ca56dadb 60 CALLOC_ARRAY(worktree, 1);
92718b74 61 worktree->path = strbuf_detach(&worktree_path, NULL);
f3534c98
JT
62 /*
63 * NEEDSWORK: If this function is called from a secondary worktree and
64 * config.worktree is present, is_bare_repository_cfg will reflect the
65 * contents of config.worktree, not the contents of the main worktree.
66 * This means that worktree->is_bare may be set to 0 even if the main
67 * worktree is configured to be bare.
68 */
69 worktree->is_bare = (is_bare_repository_cfg == 1) ||
70 is_bare_repository();
fa099d23 71 add_head_info(worktree);
51934904 72 return worktree;
1ceb7f90
MR
73}
74
51934904 75static struct worktree *get_linked_worktree(const char *id)
ac6c561b 76{
51934904 77 struct worktree *worktree = NULL;
ac6c561b 78 struct strbuf path = STRBUF_INIT;
51934904 79 struct strbuf worktree_path = STRBUF_INIT;
ac6c561b 80
1ceb7f90
MR
81 if (!id)
82 die("Missing linked worktree name");
ac6c561b 83
b337172c 84 strbuf_git_common_path(&path, the_repository, "worktrees/%s/gitdir", id);
51934904
MR
85 if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
86 /* invalid gitdir file */
ac6c561b 87 goto done;
51934904 88 strbuf_rtrim(&worktree_path);
1c4854ec 89 strbuf_strip_suffix(&worktree_path, "/.git");
51934904 90
ca56dadb 91 CALLOC_ARRAY(worktree, 1);
92718b74 92 worktree->path = strbuf_detach(&worktree_path, NULL);
69dfe3b9 93 worktree->id = xstrdup(id);
fa099d23 94 add_head_info(worktree);
ac6c561b 95
ac6c561b
MR
96done:
97 strbuf_release(&path);
51934904 98 strbuf_release(&worktree_path);
51934904 99 return worktree;
ac6c561b
MR
100}
101
750e8a60
NTND
102static void mark_current_worktree(struct worktree **worktrees)
103{
0aaad415 104 char *git_dir = absolute_pathdup(get_git_dir());
750e8a60
NTND
105 int i;
106
750e8a60
NTND
107 for (i = 0; worktrees[i]; i++) {
108 struct worktree *wt = worktrees[i];
360af2da
NTND
109 const char *wt_git_dir = get_worktree_git_dir(wt);
110
111 if (!fspathcmp(git_dir, absolute_path(wt_git_dir))) {
112 wt->is_current = 1;
750e8a60 113 break;
360af2da 114 }
750e8a60 115 }
360af2da 116 free(git_dir);
750e8a60
NTND
117}
118
03f2465b 119struct worktree **get_worktrees(void)
ac6c561b 120{
51934904 121 struct worktree **list = NULL;
ac6c561b
MR
122 struct strbuf path = STRBUF_INIT;
123 DIR *dir;
124 struct dirent *d;
51934904
MR
125 int counter = 0, alloc = 2;
126
3f64699f 127 ALLOC_ARRAY(list, alloc);
ac6c561b 128
a234563a 129 list[counter++] = get_main_worktree();
ac6c561b
MR
130
131 strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
132 dir = opendir(path.buf);
133 strbuf_release(&path);
51934904 134 if (dir) {
b548f0f1 135 while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
51934904 136 struct worktree *linked = NULL;
ac6c561b 137
d4cddd66
NTND
138 if ((linked = get_linked_worktree(d->d_name))) {
139 ALLOC_GROW(list, counter + 1, alloc);
140 list[counter++] = linked;
141 }
51934904
MR
142 }
143 closedir(dir);
144 }
145 ALLOC_GROW(list, counter + 1, alloc);
146 list[counter] = NULL;
750e8a60
NTND
147
148 mark_current_worktree(list);
51934904
MR
149 return list;
150}
151
69dfe3b9
NTND
152const char *get_worktree_git_dir(const struct worktree *wt)
153{
154 if (!wt)
155 return get_git_dir();
156 else if (!wt->id)
157 return get_git_common_dir();
158 else
159 return git_common_path("worktrees/%s", wt->id);
160}
161
080739ba
NTND
162static struct worktree *find_worktree_by_suffix(struct worktree **list,
163 const char *suffix)
164{
165 struct worktree *found = NULL;
166 int nr_found = 0, suffixlen;
167
168 suffixlen = strlen(suffix);
169 if (!suffixlen)
170 return NULL;
171
172 for (; *list && nr_found < 2; list++) {
173 const char *path = (*list)->path;
174 int pathlen = strlen(path);
175 int start = pathlen - suffixlen;
176
177 /* suffix must start at directory boundary */
178 if ((!start || (start > 0 && is_dir_sep(path[start - 1]))) &&
179 !fspathcmp(suffix, path + start)) {
180 found = *list;
181 nr_found++;
182 }
183 }
184 return nr_found == 1 ? found : NULL;
185}
186
68353144
NTND
187struct worktree *find_worktree(struct worktree **list,
188 const char *prefix,
189 const char *arg)
190{
080739ba 191 struct worktree *wt;
e4da43b1 192 char *to_free = NULL;
68353144 193
080739ba
NTND
194 if ((wt = find_worktree_by_suffix(list, arg)))
195 return wt;
196
e4da43b1
JK
197 if (prefix)
198 arg = to_free = prefix_filename(prefix, arg);
bb4995fc
ES
199 wt = find_worktree_by_path(list, arg);
200 free(to_free);
201 return wt;
202}
203
204struct worktree *find_worktree_by_path(struct worktree **list, const char *p)
205{
4530a85b 206 struct strbuf wt_path = STRBUF_INIT;
bb4995fc
ES
207 char *path = real_pathdup(p, 0);
208
209 if (!path)
4c5fa9e6 210 return NULL;
105df73e 211 for (; *list; list++) {
4530a85b
AM
212 if (!strbuf_realpath(&wt_path, (*list)->path, 0))
213 continue;
105df73e 214
4530a85b 215 if (!fspathcmp(path, wt_path.buf))
68353144 216 break;
105df73e 217 }
68353144 218 free(path);
4530a85b 219 strbuf_release(&wt_path);
68353144
NTND
220 return *list;
221}
222
984ad9e5
NTND
223int is_main_worktree(const struct worktree *wt)
224{
225 return !wt->id;
226}
227
d236f12b 228const char *worktree_lock_reason(struct worktree *wt)
346ef530 229{
eb36135a
RS
230 if (is_main_worktree(wt))
231 return NULL;
346ef530
NTND
232
233 if (!wt->lock_reason_valid) {
234 struct strbuf path = STRBUF_INIT;
235
236 strbuf_addstr(&path, worktree_git_path(wt, "locked"));
237 if (file_exists(path.buf)) {
238 struct strbuf lock_reason = STRBUF_INIT;
239 if (strbuf_read_file(&lock_reason, path.buf, 0) < 0)
240 die_errno(_("failed to read '%s'"), path.buf);
241 strbuf_trim(&lock_reason);
242 wt->lock_reason = strbuf_detach(&lock_reason, NULL);
243 } else
244 wt->lock_reason = NULL;
245 wt->lock_reason_valid = 1;
246 strbuf_release(&path);
247 }
248
249 return wt->lock_reason;
250}
251
fc0c7d5e
RS
252const char *worktree_prune_reason(struct worktree *wt, timestamp_t expire)
253{
254 struct strbuf reason = STRBUF_INIT;
255 char *path = NULL;
256
257 if (is_main_worktree(wt))
258 return NULL;
259 if (wt->prune_reason_valid)
260 return wt->prune_reason;
261
262 if (should_prune_worktree(wt->id, &reason, &path, expire))
263 wt->prune_reason = strbuf_detach(&reason, NULL);
264 wt->prune_reason_valid = 1;
265
266 strbuf_release(&reason);
267 free(path);
268 return wt->prune_reason;
269}
270
4ddddc1f 271/* convenient wrapper to deal with NULL strbuf */
48ca53ca 272__attribute__((format (printf, 2, 3)))
4ddddc1f
NTND
273static void strbuf_addf_gently(struct strbuf *buf, const char *fmt, ...)
274{
275 va_list params;
276
277 if (!buf)
278 return;
279
280 va_start(params, fmt);
281 strbuf_vaddf(buf, fmt, params);
282 va_end(params);
283}
284
ee6763af
NTND
285int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
286 unsigned flags)
4ddddc1f
NTND
287{
288 struct strbuf wt_path = STRBUF_INIT;
3d7747e3 289 struct strbuf realpath = STRBUF_INIT;
4ddddc1f
NTND
290 char *path = NULL;
291 int err, ret = -1;
292
293 strbuf_addf(&wt_path, "%s/.git", wt->path);
294
295 if (is_main_worktree(wt)) {
296 if (is_directory(wt_path.buf)) {
297 ret = 0;
298 goto done;
299 }
300 /*
301 * Main worktree using .git file to point to the
302 * repository would make it impossible to know where
303 * the actual worktree is if this function is executed
304 * from another worktree. No .git file support for now.
305 */
306 strbuf_addf_gently(errmsg,
307 _("'%s' at main working tree is not the repository directory"),
308 wt_path.buf);
309 goto done;
310 }
311
312 /*
313 * Make sure "gitdir" file points to a real .git file and that
314 * file points back here.
315 */
316 if (!is_absolute_path(wt->path)) {
317 strbuf_addf_gently(errmsg,
318 _("'%s' file does not contain absolute path to the working tree location"),
319 git_common_path("worktrees/%s/gitdir", wt->id));
320 goto done;
321 }
322
ee6763af
NTND
323 if (flags & WT_VALIDATE_WORKTREE_MISSING_OK &&
324 !file_exists(wt->path)) {
325 ret = 0;
326 goto done;
327 }
328
4ddddc1f
NTND
329 if (!file_exists(wt_path.buf)) {
330 strbuf_addf_gently(errmsg, _("'%s' does not exist"), wt_path.buf);
331 goto done;
332 }
333
334 path = xstrdup_or_null(read_gitfile_gently(wt_path.buf, &err));
335 if (!path) {
336 strbuf_addf_gently(errmsg, _("'%s' is not a .git file, error code %d"),
337 wt_path.buf, err);
338 goto done;
339 }
340
3d7747e3
AM
341 strbuf_realpath(&realpath, git_common_path("worktrees/%s", wt->id), 1);
342 ret = fspathcmp(path, realpath.buf);
4ddddc1f
NTND
343
344 if (ret)
345 strbuf_addf_gently(errmsg, _("'%s' does not point back to '%s'"),
346 wt->path, git_common_path("worktrees/%s", wt->id));
347done:
348 free(path);
349 strbuf_release(&wt_path);
3d7747e3 350 strbuf_release(&realpath);
4ddddc1f
NTND
351 return ret;
352}
353
9c620fc7
NTND
354void update_worktree_location(struct worktree *wt, const char *path_)
355{
356 struct strbuf path = STRBUF_INIT;
357
358 if (is_main_worktree(wt))
033abf97 359 BUG("can't relocate main worktree");
9c620fc7
NTND
360
361 strbuf_realpath(&path, path_, 1);
362 if (fspathcmp(wt->path, path.buf)) {
363 write_file(git_common_path("worktrees/%s/gitdir", wt->id),
364 "%s/.git", path.buf);
365 free(wt->path);
366 wt->path = strbuf_detach(&path, NULL);
367 }
368 strbuf_release(&path);
369}
370
14ace5b7
NTND
371int is_worktree_being_rebased(const struct worktree *wt,
372 const char *target)
8d9fdd70
NTND
373{
374 struct wt_status_state state;
375 int found_rebase;
376
377 memset(&state, 0, sizeof(state));
378 found_rebase = wt_status_check_rebase(wt, &state) &&
a46d1f73
379 (state.rebase_in_progress ||
380 state.rebase_interactive_in_progress) &&
381 state.branch &&
382 skip_prefix(target, "refs/heads/", &target) &&
383 !strcmp(state.branch, target);
962dd7eb 384 wt_status_state_free_buffers(&state);
8d9fdd70
NTND
385 return found_rebase;
386}
387
14ace5b7
NTND
388int is_worktree_being_bisected(const struct worktree *wt,
389 const char *target)
51934904 390{
04a3dfb8 391 struct wt_status_state state;
fb07bd42 392 int found_bisect;
04a3dfb8
NTND
393
394 memset(&state, 0, sizeof(state));
fb07bd42
395 found_bisect = wt_status_check_bisect(wt, &state) &&
396 state.branch &&
a46d1f73
397 skip_prefix(target, "refs/heads/", &target) &&
398 !strcmp(state.branch, target);
962dd7eb 399 wt_status_state_free_buffers(&state);
fb07bd42 400 return found_bisect;
04a3dfb8
NTND
401}
402
8d9fdd70
NTND
403/*
404 * note: this function should be able to detect shared symref even if
405 * HEAD is temporarily detached (e.g. in the middle of rebase or
406 * bisect). New commands that do similar things should update this
407 * function as well.
408 */
662078ca
RJ
409int is_shared_symref(const struct worktree *wt, const char *symref,
410 const char *target)
51934904 411{
662078ca
RJ
412 const char *symref_target;
413 struct ref_store *refs;
414 int flags;
51934904 415
662078ca
RJ
416 if (wt->is_bare)
417 return 0;
fa099d23 418
662078ca
RJ
419 if (wt->is_detached && !strcmp(symref, "HEAD")) {
420 if (is_worktree_being_rebased(wt, target))
421 return 1;
422 if (is_worktree_being_bisected(wt, target))
423 return 1;
424 }
c8717148 425
662078ca
RJ
426 refs = get_worktree_ref_store(wt);
427 symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
428 NULL, &flags);
429 if ((flags & REF_ISSYMREF) &&
430 symref_target && !strcmp(symref_target, target))
431 return 1;
8d9fdd70 432
662078ca
RJ
433 return 0;
434}
435
436const struct worktree *find_shared_symref(struct worktree **worktrees,
437 const char *symref,
438 const char *target)
439{
440
faa4d598 441 for (int i = 0; worktrees[i]; i++)
662078ca
RJ
442 if (is_shared_symref(worktrees[i], symref, target))
443 return worktrees[i];
51934904 444
662078ca 445 return NULL;
ac6c561b 446}
1a248cf2
SB
447
448int submodule_uses_worktrees(const char *path)
449{
450 char *submodule_gitdir;
e02a7141 451 struct strbuf sb = STRBUF_INIT, err = STRBUF_INIT;
1a248cf2
SB
452 DIR *dir;
453 struct dirent *d;
7c4be458 454 int ret = 0;
e8805af1 455 struct repository_format format = REPOSITORY_FORMAT_INIT;
1a248cf2
SB
456
457 submodule_gitdir = git_pathdup_submodule(path, "%s", "");
458 if (!submodule_gitdir)
459 return 0;
460
461 /* The env would be set for the superproject. */
462 get_common_dir_noenv(&sb, submodule_gitdir);
d32de66a 463 free(submodule_gitdir);
1a248cf2 464
1a248cf2
SB
465 strbuf_addstr(&sb, "/config");
466 read_repository_format(&format, sb.buf);
e02a7141 467 if (verify_repository_format(&format, &err)) {
468 strbuf_release(&err);
1a248cf2 469 strbuf_release(&sb);
e8805af1 470 clear_repository_format(&format);
1a248cf2
SB
471 return 1;
472 }
e8805af1 473 clear_repository_format(&format);
e02a7141 474 strbuf_release(&err);
1a248cf2
SB
475
476 /* Replace config by worktrees. */
477 strbuf_setlen(&sb, sb.len - strlen("config"));
478 strbuf_addstr(&sb, "worktrees");
479
480 /* See if there is any file inside the worktrees directory. */
481 dir = opendir(sb.buf);
482 strbuf_release(&sb);
1a248cf2
SB
483
484 if (!dir)
485 return 0;
486
b548f0f1 487 d = readdir_skip_dot_and_dotdot(dir);
afe8a907 488 if (d)
1a248cf2 489 ret = 1;
1a248cf2
SB
490 closedir(dir);
491 return ret;
492}
d0c39a49 493
ab3e1f78
NTND
494void strbuf_worktree_ref(const struct worktree *wt,
495 struct strbuf *sb,
496 const char *refname)
497{
71e54734
HWN
498 if (parse_worktree_ref(refname, NULL, NULL, NULL) ==
499 REF_WORKTREE_CURRENT &&
500 wt && !wt->is_current) {
501 if (is_main_worktree(wt))
502 strbuf_addstr(sb, "main-worktree/");
503 else
504 strbuf_addf(sb, "worktrees/%s/", wt->id);
ab3e1f78
NTND
505 }
506 strbuf_addstr(sb, refname);
507}
508
d0c39a49
NTND
509int other_head_refs(each_ref_fn fn, void *cb_data)
510{
511 struct worktree **worktrees, **p;
ef2d5547 512 struct strbuf refname = STRBUF_INIT;
d0c39a49
NTND
513 int ret = 0;
514
03f2465b 515 worktrees = get_worktrees();
d0c39a49
NTND
516 for (p = worktrees; *p; p++) {
517 struct worktree *wt = *p;
ab3e1f78
NTND
518 struct object_id oid;
519 int flag;
d0c39a49
NTND
520
521 if (wt->is_current)
522 continue;
523
ef2d5547
524 strbuf_reset(&refname);
525 strbuf_worktree_ref(wt, &refname, "HEAD");
f1da24ca 526 if (refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
76887df0
ÆAB
527 refname.buf,
528 RESOLVE_REF_READING,
ce14de03 529 &oid, &flag))
ef2d5547 530 ret = fn(refname.buf, &oid, flag, cb_data);
d0c39a49
NTND
531 if (ret)
532 break;
533 }
534 free_worktrees(worktrees);
ef2d5547 535 strbuf_release(&refname);
d0c39a49
NTND
536 return ret;
537}
bdd1f3e4
ES
538
539/*
540 * Repair worktree's /path/to/worktree/.git file if missing, corrupt, or not
541 * pointing at <repo>/worktrees/<id>.
542 */
543static void repair_gitfile(struct worktree *wt,
544 worktree_repair_fn fn, void *cb_data)
545{
546 struct strbuf dotgit = STRBUF_INIT;
547 struct strbuf repo = STRBUF_INIT;
548 char *backlink;
549 const char *repair = NULL;
550 int err;
551
552 /* missing worktree can't be repaired */
553 if (!file_exists(wt->path))
554 return;
555
556 if (!is_directory(wt->path)) {
557 fn(1, wt->path, _("not a directory"), cb_data);
558 return;
559 }
560
561 strbuf_realpath(&repo, git_common_path("worktrees/%s", wt->id), 1);
562 strbuf_addf(&dotgit, "%s/.git", wt->path);
563 backlink = xstrdup_or_null(read_gitfile_gently(dotgit.buf, &err));
564
565 if (err == READ_GITFILE_ERR_NOT_A_FILE)
566 fn(1, wt->path, _(".git is not a file"), cb_data);
567 else if (err)
568 repair = _(".git file broken");
569 else if (fspathcmp(backlink, repo.buf))
570 repair = _(".git file incorrect");
571
572 if (repair) {
573 fn(0, wt->path, repair, cb_data);
574 write_file(dotgit.buf, "gitdir: %s", repo.buf);
575 }
576
577 free(backlink);
578 strbuf_release(&repo);
579 strbuf_release(&dotgit);
580}
581
582static void repair_noop(int iserr, const char *path, const char *msg,
583 void *cb_data)
584{
585 /* nothing */
586}
587
588void repair_worktrees(worktree_repair_fn fn, void *cb_data)
589{
590 struct worktree **worktrees = get_worktrees();
591 struct worktree **wt = worktrees + 1; /* +1 skips main worktree */
592
593 if (!fn)
594 fn = repair_noop;
595 for (; *wt; wt++)
596 repair_gitfile(*wt, fn, cb_data);
597 free_worktrees(worktrees);
598}
b214ab5a
ES
599
600static int is_main_worktree_path(const char *path)
601{
602 struct strbuf target = STRBUF_INIT;
603 struct strbuf maindir = STRBUF_INIT;
604 int cmp;
605
606 strbuf_add_real_path(&target, path);
607 strbuf_strip_suffix(&target, "/.git");
608 strbuf_add_real_path(&maindir, get_git_common_dir());
609 strbuf_strip_suffix(&maindir, "/.git");
610 cmp = fspathcmp(maindir.buf, target.buf);
611
612 strbuf_release(&maindir);
613 strbuf_release(&target);
614 return !cmp;
615}
616
cf76baea
ES
617/*
618 * If both the main worktree and linked worktree have been moved, then the
619 * gitfile /path/to/worktree/.git won't point into the repository, thus we
620 * won't know which <repo>/worktrees/<id>/gitdir to repair. However, we may
621 * be able to infer the gitdir by manually reading /path/to/worktree/.git,
622 * extracting the <id>, and checking if <repo>/worktrees/<id> exists.
623 */
624static char *infer_backlink(const char *gitfile)
625{
626 struct strbuf actual = STRBUF_INIT;
627 struct strbuf inferred = STRBUF_INIT;
628 const char *id;
629
630 if (strbuf_read_file(&actual, gitfile, 0) < 0)
631 goto error;
632 if (!starts_with(actual.buf, "gitdir:"))
633 goto error;
634 if (!(id = find_last_dir_sep(actual.buf)))
635 goto error;
636 strbuf_trim(&actual);
637 id++; /* advance past '/' to point at <id> */
638 if (!*id)
639 goto error;
640 strbuf_git_common_path(&inferred, the_repository, "worktrees/%s", id);
641 if (!is_directory(inferred.buf))
642 goto error;
643
644 strbuf_release(&actual);
645 return strbuf_detach(&inferred, NULL);
646
647error:
648 strbuf_release(&actual);
649 strbuf_release(&inferred);
650 return NULL;
651}
652
b214ab5a
ES
653/*
654 * Repair <repo>/worktrees/<id>/gitdir if missing, corrupt, or not pointing at
655 * the worktree's path.
656 */
657void repair_worktree_at_path(const char *path,
658 worktree_repair_fn fn, void *cb_data)
659{
660 struct strbuf dotgit = STRBUF_INIT;
661 struct strbuf realdotgit = STRBUF_INIT;
662 struct strbuf gitdir = STRBUF_INIT;
663 struct strbuf olddotgit = STRBUF_INIT;
664 char *backlink = NULL;
665 const char *repair = NULL;
666 int err;
667
668 if (!fn)
669 fn = repair_noop;
670
671 if (is_main_worktree_path(path))
672 goto done;
673
674 strbuf_addf(&dotgit, "%s/.git", path);
675 if (!strbuf_realpath(&realdotgit, dotgit.buf, 0)) {
676 fn(1, path, _("not a valid path"), cb_data);
677 goto done;
678 }
679
680 backlink = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));
681 if (err == READ_GITFILE_ERR_NOT_A_FILE) {
682 fn(1, realdotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
683 goto done;
cf76baea
ES
684 } else if (err == READ_GITFILE_ERR_NOT_A_REPO) {
685 if (!(backlink = infer_backlink(realdotgit.buf))) {
686 fn(1, realdotgit.buf, _("unable to locate repository; .git file does not reference a repository"), cb_data);
687 goto done;
688 }
b214ab5a
ES
689 } else if (err) {
690 fn(1, realdotgit.buf, _("unable to locate repository; .git file broken"), cb_data);
691 goto done;
692 }
693
694 strbuf_addf(&gitdir, "%s/gitdir", backlink);
695 if (strbuf_read_file(&olddotgit, gitdir.buf, 0) < 0)
696 repair = _("gitdir unreadable");
697 else {
698 strbuf_rtrim(&olddotgit);
699 if (fspathcmp(olddotgit.buf, realdotgit.buf))
700 repair = _("gitdir incorrect");
701 }
702
703 if (repair) {
704 fn(0, gitdir.buf, repair, cb_data);
705 write_file(gitdir.buf, "%s", realdotgit.buf);
706 }
707done:
708 free(backlink);
709 strbuf_release(&olddotgit);
710 strbuf_release(&gitdir);
711 strbuf_release(&realdotgit);
712 strbuf_release(&dotgit);
713}
a29a8b75
RS
714
715int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath, timestamp_t expire)
716{
717 struct stat st;
718 char *path;
719 int fd;
720 size_t len;
721 ssize_t read_result;
722
723 *wtpath = NULL;
724 if (!is_directory(git_path("worktrees/%s", id))) {
725 strbuf_addstr(reason, _("not a valid directory"));
726 return 1;
727 }
728 if (file_exists(git_path("worktrees/%s/locked", id)))
729 return 0;
730 if (stat(git_path("worktrees/%s/gitdir", id), &st)) {
731 strbuf_addstr(reason, _("gitdir file does not exist"));
732 return 1;
733 }
734 fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
735 if (fd < 0) {
736 strbuf_addf(reason, _("unable to read gitdir file (%s)"),
737 strerror(errno));
738 return 1;
739 }
740 len = xsize_t(st.st_size);
741 path = xmallocz(len);
742
743 read_result = read_in_full(fd, path, len);
744 if (read_result < 0) {
745 strbuf_addf(reason, _("unable to read gitdir file (%s)"),
746 strerror(errno));
747 close(fd);
748 free(path);
749 return 1;
750 }
751 close(fd);
752
753 if (read_result != len) {
754 strbuf_addf(reason,
755 _("short read (expected %"PRIuMAX" bytes, read %"PRIuMAX")"),
756 (uintmax_t)len, (uintmax_t)read_result);
757 free(path);
758 return 1;
759 }
760 while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
761 len--;
762 if (!len) {
763 strbuf_addstr(reason, _("invalid gitdir file"));
764 free(path);
765 return 1;
766 }
767 path[len] = '\0';
768 if (!file_exists(path)) {
769 if (stat(git_path("worktrees/%s/index", id), &st) ||
770 st.st_mtime <= expire) {
771 strbuf_addstr(reason, _("gitdir file points to non-existent location"));
772 free(path);
773 return 1;
774 } else {
775 *wtpath = path;
776 return 0;
777 }
778 }
779 *wtpath = path;
780 return 0;
781}
615a84ad
DS
782
783static int move_config_setting(const char *key, const char *value,
784 const char *from_file, const char *to_file)
785{
786 if (git_config_set_in_file_gently(to_file, key, value))
787 return error(_("unable to set %s in '%s'"), key, to_file);
788 if (git_config_set_in_file_gently(from_file, key, NULL))
789 return error(_("unable to unset %s in '%s'"), key, from_file);
790 return 0;
791}
792
793int init_worktree_config(struct repository *r)
794{
795 int res = 0;
796 int bare = 0;
797 struct config_set cs = { { 0 } };
798 const char *core_worktree;
799 char *common_config_file;
800 char *main_worktree_file;
801
802 /*
803 * If the extension is already enabled, then we can skip the
804 * upgrade process.
805 */
806 if (repository_format_worktree_config)
807 return 0;
808 if ((res = git_config_set_gently("extensions.worktreeConfig", "true")))
809 return error(_("failed to set extensions.worktreeConfig setting"));
810
811 common_config_file = xstrfmt("%s/config", r->commondir);
812 main_worktree_file = xstrfmt("%s/config.worktree", r->commondir);
813
814 git_configset_init(&cs);
815 git_configset_add_file(&cs, common_config_file);
816
817 /*
818 * If core.bare is true in the common config file, then we need to
819 * move it to the main worktree's config file or it will break all
820 * worktrees. If it is false, then leave it in place because it
821 * _could_ be negating a global core.bare=true.
822 */
823 if (!git_configset_get_bool(&cs, "core.bare", &bare) && bare) {
824 if ((res = move_config_setting("core.bare", "true",
825 common_config_file,
826 main_worktree_file)))
827 goto cleanup;
828 }
829 /*
830 * If core.worktree is set, then the main worktree is located
831 * somewhere different than the parent of the common Git dir.
832 * Relocate that value to avoid breaking all worktrees with this
833 * upgrade to worktree config.
834 */
835 if (!git_configset_get_value(&cs, "core.worktree", &core_worktree)) {
836 if ((res = move_config_setting("core.worktree", core_worktree,
837 common_config_file,
838 main_worktree_file)))
839 goto cleanup;
840 }
841
842 /*
843 * Ensure that we use worktree config for the remaining lifetime
844 * of the current process.
845 */
846 repository_format_worktree_config = 1;
847
848cleanup:
849 git_configset_clear(&cs);
850 free(common_config_file);
851 free(main_worktree_file);
852 return res;
853}