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