2015-10-02 11:55:31 +00:00
|
|
|
#include "cache.h"
|
2017-06-22 18:43:37 +00:00
|
|
|
#include "repository.h"
|
2015-10-02 11:55:31 +00:00
|
|
|
#include "refs.h"
|
|
|
|
#include "strbuf.h"
|
|
|
|
#include "worktree.h"
|
2016-04-22 13:01:28 +00:00
|
|
|
#include "dir.h"
|
2016-04-22 13:01:33 +00:00
|
|
|
#include "wt-status.h"
|
2015-10-02 11:55:31 +00:00
|
|
|
|
2015-10-08 17:01:03 +00:00
|
|
|
void free_worktrees(struct worktree **worktrees)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
for (i = 0; worktrees[i]; i++) {
|
|
|
|
free(worktrees[i]->path);
|
2016-04-22 13:01:26 +00:00
|
|
|
free(worktrees[i]->id);
|
2015-10-08 17:01:04 +00:00
|
|
|
free(worktrees[i]->head_ref);
|
2016-06-13 12:18:23 +00:00
|
|
|
free(worktrees[i]->lock_reason);
|
2015-10-08 17:01:03 +00:00
|
|
|
free(worktrees[i]);
|
|
|
|
}
|
|
|
|
free (worktrees);
|
|
|
|
}
|
|
|
|
|
2015-10-08 17:01:04 +00:00
|
|
|
/**
|
2020-09-27 13:15:45 +00:00
|
|
|
* Update head_oid, head_ref and is_detached of the given worktree
|
2015-10-08 17:01:04 +00:00
|
|
|
*/
|
2017-04-24 10:01:23 +00:00
|
|
|
static void add_head_info(struct worktree *wt)
|
2015-10-08 17:01:04 +00:00
|
|
|
{
|
2017-04-24 10:01:23 +00:00
|
|
|
int flags;
|
|
|
|
const char *target;
|
|
|
|
|
|
|
|
target = refs_resolve_ref_unsafe(get_worktree_ref_store(wt),
|
|
|
|
"HEAD",
|
branch: fix branch renaming not updating HEADs correctly
There are two bugs that sort of work together and cause
problems. Let's start with one in replace_each_worktree_head_symref.
Before fa099d2322 (worktree.c: kill parse_ref() in favor of
refs_resolve_ref_unsafe() - 2017-04-24), this code looks like this:
if (strcmp(oldref, worktrees[i]->head_ref))
continue;
set_worktree_head_symref(...);
After fa099d2322, it is possible that head_ref can be NULL. However,
the updated code takes the wrong exit. In the error case (NULL
head_ref), we should "continue;" to the next worktree. The updated
code makes us _skip_ "continue;" and update HEAD anyway.
The NULL head_ref is triggered by the second bug in add_head_info (in
the same commit). With the flag RESOLVE_REF_READING, resolve_ref_unsafe()
will abort if it cannot resolve the target ref. For orphan checkouts,
HEAD always points to an unborned branch, resolving target ref will
always fail. Now we have NULL head_ref. Now we always update HEAD.
Correct the logic in replace_ function so that we don't accidentally
update HEAD on error. As it turns out, correcting the logic bug above
breaks branch renaming completely, thanks to the second bug.
"git branch -[Mm]" does two steps (on a normal checkout, no orphan!):
- rename the branch on disk (e.g. refs/heads/abc to refs/heads/def)
- update HEAD if it points to the branch being renamed.
At the second step, since the branch pointed to by HEAD (e.g. "abc") no
longer exists on disk, we run into a temporary orphan checkout situation
that has been just corrected to _not_ update HEAD. But we need to update
HEAD since it's not actually an orphan checkout. We need to update HEAD
to move out of that orphan state.
Correct add_head_info(), remove RESOLVE_REF_READING flag. With the flag
gone, we should always return good "head_ref" in orphan checkouts (either
temporary or permanent). With good head_ref, things start to work again.
Noticed-by: Nish Aravamudan <nish.aravamudan@canonical.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-08-24 10:41:24 +00:00
|
|
|
0,
|
refs: convert resolve_ref_unsafe to struct object_id
Convert resolve_ref_unsafe to take a pointer to struct object_id by
converting one remaining caller to use struct object_id, removing the
temporary NULL pointer check in expand_ref, converting the declaration
and definition, and applying the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3.hash, E4)
+ resolve_ref_unsafe(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_ref_unsafe(E1, E2, E3->hash, E4)
+ resolve_ref_unsafe(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-15 22:07:09 +00:00
|
|
|
&wt->head_oid, &flags);
|
2017-04-24 10:01:23 +00:00
|
|
|
if (!target)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (flags & REF_ISSYMREF)
|
|
|
|
wt->head_ref = xstrdup(target);
|
|
|
|
else
|
|
|
|
wt->is_detached = 1;
|
2015-10-08 17:01:04 +00:00
|
|
|
}
|
|
|
|
|
2015-10-08 17:01:03 +00:00
|
|
|
/**
|
|
|
|
* get the main worktree
|
|
|
|
*/
|
|
|
|
static struct worktree *get_main_worktree(void)
|
2015-10-08 17:01:02 +00:00
|
|
|
{
|
2015-10-08 17:01:03 +00:00
|
|
|
struct worktree *worktree = NULL;
|
|
|
|
struct strbuf worktree_path = STRBUF_INIT;
|
2015-10-08 17:01:02 +00:00
|
|
|
|
worktree: retire special-case normalization of main worktree path
In order for "git-worktree list" to present consistent results,
get_main_worktree() performs manual normalization on the repository
path (returned by get_common_dir()) after passing it through
strbuf_add_absolute_path(). In particular, it cleans up the path for
three distinct cases when the current working directory is (1) the main
worktree, (2) the .git/ subdirectory, or (3) a bare repository.
The need for such special-cases is a direct consequence of employing
strbuf_add_absolute_path() which, for the sake of efficiency, doesn't
bother normalizing the path (such as folding out redundant path
components) after making it absolute. Lack of normalization is not
typically a problem since redundant path elements make no difference
when working with paths at the filesystem level. However, when preparing
paths for presentation, possible redundant path components make it
difficult to ensure consistency.
Eliminate the need for these special cases by instead making the path
absolute via strbuf_add_real_path() which normalizes the path for us.
Once normalized, the only case we need to handle manually is converting
it to the path of the main worktree by stripping the "/.git" suffix.
This stripping of the "/.git" suffix is a regular idiom in
worktree-related code; for instance, it is employed by
get_linked_worktree(), as well.
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-31 23:32:14 +00:00
|
|
|
strbuf_add_real_path(&worktree_path, get_git_common_dir());
|
|
|
|
strbuf_strip_suffix(&worktree_path, "/.git");
|
2015-10-08 17:01:03 +00:00
|
|
|
|
2016-11-22 10:00:44 +00:00
|
|
|
worktree = xcalloc(1, sizeof(*worktree));
|
2015-10-08 17:01:04 +00:00
|
|
|
worktree->path = strbuf_detach(&worktree_path, NULL);
|
worktree: update is_bare heuristics
When "git branch -D <name>" is run, Git usually first checks if that
branch is currently checked out. But this check is not performed if the
Git directory of that repository is not at "<repo>/.git", which is the
case if that repository is a submodule that has its Git directory stored
as "super/.git/modules/<repo>", for example. This results in the branch
being deleted even though it is checked out.
This is because get_main_worktree() in worktree.c sets is_bare on a
worktree only using the heuristic that a repo is bare if the worktree's
path does not end in "/.git", and not bare otherwise. This is_bare code
was introduced in 92718b7438 ("worktree: add details to the worktree
struct", 2015-10-08), following a pre-core.bare heuristic. This patch
does 2 things:
- Teach get_main_worktree() to use is_bare_repository() instead,
introduced in 7d1864ce67 ("Introduce is_bare_repository() and
core.bare configuration variable", 2007-01-07) and updated in
e90fdc39b6 ("Clean up work-tree handling", 2007-08-01). This solves
the "git branch -D <name>" problem described above. However...
- If a repository has core.bare=1 but the "git" command is being run
from one of its secondary worktrees, is_bare_repository() returns
false (which is fine, since there is a worktree available). However,
treating the main worktree as non-bare when it is bare causes issues:
for example, failure to delete a branch from a secondary worktree
that is referred to by a main worktree's HEAD, even if that main
worktree is bare.
In order to avoid that, also check core.bare when setting is_bare. If
core.bare=1, trust it, and otherwise, use is_bare_repository().
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-04-19 17:21:28 +00:00
|
|
|
/*
|
|
|
|
* NEEDSWORK: If this function is called from a secondary worktree and
|
|
|
|
* config.worktree is present, is_bare_repository_cfg will reflect the
|
|
|
|
* contents of config.worktree, not the contents of the main worktree.
|
|
|
|
* This means that worktree->is_bare may be set to 0 even if the main
|
|
|
|
* worktree is configured to be bare.
|
|
|
|
*/
|
|
|
|
worktree->is_bare = (is_bare_repository_cfg == 1) ||
|
|
|
|
is_bare_repository();
|
2017-04-24 10:01:23 +00:00
|
|
|
add_head_info(worktree);
|
2015-10-08 17:01:03 +00:00
|
|
|
return worktree;
|
2015-10-08 17:01:02 +00:00
|
|
|
}
|
|
|
|
|
2015-10-08 17:01:03 +00:00
|
|
|
static struct worktree *get_linked_worktree(const char *id)
|
2015-10-02 11:55:31 +00:00
|
|
|
{
|
2015-10-08 17:01:03 +00:00
|
|
|
struct worktree *worktree = NULL;
|
2015-10-02 11:55:31 +00:00
|
|
|
struct strbuf path = STRBUF_INIT;
|
2015-10-08 17:01:03 +00:00
|
|
|
struct strbuf worktree_path = STRBUF_INIT;
|
2015-10-02 11:55:31 +00:00
|
|
|
|
2015-10-08 17:01:02 +00:00
|
|
|
if (!id)
|
|
|
|
die("Missing linked worktree name");
|
2015-10-02 11:55:31 +00:00
|
|
|
|
2017-06-22 18:43:37 +00:00
|
|
|
strbuf_git_common_path(&path, the_repository, "worktrees/%s/gitdir", id);
|
2015-10-08 17:01:03 +00:00
|
|
|
if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
|
|
|
|
/* invalid gitdir file */
|
2015-10-02 11:55:31 +00:00
|
|
|
goto done;
|
2015-10-08 17:01:03 +00:00
|
|
|
strbuf_rtrim(&worktree_path);
|
worktree: drop bogus and unnecessary path munging
The content of .git/worktrees/<id>/gitdir must be a path of the form
"/path/to/worktree/.git". Any other content would be indicative of a
corrupt "gitdir" file. To determine the path of the worktree itself one
merely strips the "/.git" suffix, and this is indeed how the worktree
path was determined from inception.
However, 5193490442 (worktree: add a function to get worktree details,
2015-10-08) extended the path manipulation in a mysterious way. If it is
unable to strip "/.git" from the path, then it instead reports the
current working directory as the linked worktree's path:
if (!strbuf_strip_suffix(&worktree_path, "/.git")) {
strbuf_reset(&worktree_path);
strbuf_add_absolute_path(&worktree_path, ".");
strbuf_strip_suffix(&worktree_path, "/.");
}
This logic is clearly bogus; it can never be generally correct behavior.
It materialized out of thin air in 5193490442 with neither explanation
nor tests to illustrate a case in which it would be desirable.
It's possible that this logic was introduced to somehow deal with a
corrupt "gitdir" file, so that it returns _some_ sort of meaningful
value, but returning the current working directory is not helpful. In
fact, it is quite misleading (except in the one specific case when the
current directory is the worktree whose "gitdir" entry is corrupt).
Moreover, reporting the corrupt value to the user, rather than fibbing
about it and hiding it outright, is more helpful since it may aid in
diagnosing the problem.
Therefore, drop this bogus path munging and restore the logic to the
original behavior of merely stripping "/.git".
Signed-off-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-31 23:32:13 +00:00
|
|
|
strbuf_strip_suffix(&worktree_path, "/.git");
|
2015-10-08 17:01:03 +00:00
|
|
|
|
2016-11-22 10:00:44 +00:00
|
|
|
worktree = xcalloc(1, sizeof(*worktree));
|
2015-10-08 17:01:04 +00:00
|
|
|
worktree->path = strbuf_detach(&worktree_path, NULL);
|
2016-04-22 13:01:26 +00:00
|
|
|
worktree->id = xstrdup(id);
|
2017-04-24 10:01:23 +00:00
|
|
|
add_head_info(worktree);
|
2015-10-02 11:55:31 +00:00
|
|
|
|
|
|
|
done:
|
|
|
|
strbuf_release(&path);
|
2015-10-08 17:01:03 +00:00
|
|
|
strbuf_release(&worktree_path);
|
|
|
|
return worktree;
|
2015-10-02 11:55:31 +00:00
|
|
|
}
|
|
|
|
|
2016-04-22 13:01:28 +00:00
|
|
|
static void mark_current_worktree(struct worktree **worktrees)
|
|
|
|
{
|
2017-01-26 17:54:23 +00:00
|
|
|
char *git_dir = absolute_pathdup(get_git_dir());
|
2016-04-22 13:01:28 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; worktrees[i]; i++) {
|
|
|
|
struct worktree *wt = worktrees[i];
|
2016-05-22 09:33:52 +00:00
|
|
|
const char *wt_git_dir = get_worktree_git_dir(wt);
|
|
|
|
|
|
|
|
if (!fspathcmp(git_dir, absolute_path(wt_git_dir))) {
|
|
|
|
wt->is_current = 1;
|
2016-04-22 13:01:28 +00:00
|
|
|
break;
|
2016-05-22 09:33:52 +00:00
|
|
|
}
|
2016-04-22 13:01:28 +00:00
|
|
|
}
|
2016-05-22 09:33:52 +00:00
|
|
|
free(git_dir);
|
2016-04-22 13:01:28 +00:00
|
|
|
}
|
|
|
|
|
2020-06-19 23:35:44 +00:00
|
|
|
struct worktree **get_worktrees(void)
|
2015-10-02 11:55:31 +00:00
|
|
|
{
|
2015-10-08 17:01:03 +00:00
|
|
|
struct worktree **list = NULL;
|
2015-10-02 11:55:31 +00:00
|
|
|
struct strbuf path = STRBUF_INIT;
|
|
|
|
DIR *dir;
|
|
|
|
struct dirent *d;
|
2015-10-08 17:01:03 +00:00
|
|
|
int counter = 0, alloc = 2;
|
|
|
|
|
2017-02-25 10:30:03 +00:00
|
|
|
ALLOC_ARRAY(list, alloc);
|
2015-10-02 11:55:31 +00:00
|
|
|
|
get_worktrees() must return main worktree as first item even on error
This is required by git-worktree.txt, stating that the main worktree is
the first line (especially in --porcelain mode when we can't just change
behavior at will).
There's only one case when get_worktrees() may skip main worktree, when
parse_ref() fails. Update the code so that we keep first item as main
worktree and return something sensible in this case:
- In user-friendly mode, since we're not constraint by anything,
returning "(error)" should do the job (we already show "(detached
HEAD)" which is not machine-friendly). Actually errors should be
printed on stderr by parse_ref() (*)
- In plumbing mode, we do not show neither 'bare', 'detached' or
'branch ...', which is possible by the format description if I read
it right.
Careful readers may realize that when the local variable "head_ref" in
get_main_worktree() is emptied, add_head_info() will do nothing to
wt->head_sha1. But that's ok because head_sha1 is zero-ized in the
previous patch.
(*) Well, it does not. But it's supposed to be a stop gap implementation
until we can reuse refs code to parse "ref: " stuff in HEAD, from
resolve_refs_unsafe(). Now may be the time since refs refactoring is
mostly done.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-11-28 09:36:54 +00:00
|
|
|
list[counter++] = get_main_worktree();
|
2015-10-02 11:55:31 +00:00
|
|
|
|
|
|
|
strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
|
|
|
|
dir = opendir(path.buf);
|
|
|
|
strbuf_release(&path);
|
2015-10-08 17:01:03 +00:00
|
|
|
if (dir) {
|
|
|
|
while ((d = readdir(dir)) != NULL) {
|
|
|
|
struct worktree *linked = NULL;
|
2016-05-22 09:33:54 +00:00
|
|
|
if (is_dot_or_dotdot(d->d_name))
|
2015-10-08 17:01:03 +00:00
|
|
|
continue;
|
2015-10-02 11:55:31 +00:00
|
|
|
|
2016-01-18 11:21:29 +00:00
|
|
|
if ((linked = get_linked_worktree(d->d_name))) {
|
|
|
|
ALLOC_GROW(list, counter + 1, alloc);
|
|
|
|
list[counter++] = linked;
|
|
|
|
}
|
2015-10-08 17:01:03 +00:00
|
|
|
}
|
|
|
|
closedir(dir);
|
|
|
|
}
|
|
|
|
ALLOC_GROW(list, counter + 1, alloc);
|
|
|
|
list[counter] = NULL;
|
2016-04-22 13:01:28 +00:00
|
|
|
|
|
|
|
mark_current_worktree(list);
|
2015-10-08 17:01:03 +00:00
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
2016-04-22 13:01:26 +00:00
|
|
|
const char *get_worktree_git_dir(const struct worktree *wt)
|
|
|
|
{
|
|
|
|
if (!wt)
|
|
|
|
return get_git_dir();
|
|
|
|
else if (!wt->id)
|
|
|
|
return get_git_common_dir();
|
|
|
|
else
|
|
|
|
return git_common_path("worktrees/%s", wt->id);
|
|
|
|
}
|
|
|
|
|
2016-06-13 12:18:26 +00:00
|
|
|
static struct worktree *find_worktree_by_suffix(struct worktree **list,
|
|
|
|
const char *suffix)
|
|
|
|
{
|
|
|
|
struct worktree *found = NULL;
|
|
|
|
int nr_found = 0, suffixlen;
|
|
|
|
|
|
|
|
suffixlen = strlen(suffix);
|
|
|
|
if (!suffixlen)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (; *list && nr_found < 2; list++) {
|
|
|
|
const char *path = (*list)->path;
|
|
|
|
int pathlen = strlen(path);
|
|
|
|
int start = pathlen - suffixlen;
|
|
|
|
|
|
|
|
/* suffix must start at directory boundary */
|
|
|
|
if ((!start || (start > 0 && is_dir_sep(path[start - 1]))) &&
|
|
|
|
!fspathcmp(suffix, path + start)) {
|
|
|
|
found = *list;
|
|
|
|
nr_found++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nr_found == 1 ? found : NULL;
|
|
|
|
}
|
|
|
|
|
2016-06-03 12:19:39 +00:00
|
|
|
struct worktree *find_worktree(struct worktree **list,
|
|
|
|
const char *prefix,
|
|
|
|
const char *arg)
|
|
|
|
{
|
2016-06-13 12:18:26 +00:00
|
|
|
struct worktree *wt;
|
2017-03-21 01:28:49 +00:00
|
|
|
char *to_free = NULL;
|
2016-06-03 12:19:39 +00:00
|
|
|
|
2016-06-13 12:18:26 +00:00
|
|
|
if ((wt = find_worktree_by_suffix(list, arg)))
|
|
|
|
return wt;
|
|
|
|
|
2017-03-21 01:28:49 +00:00
|
|
|
if (prefix)
|
|
|
|
arg = to_free = prefix_filename(prefix, arg);
|
2020-02-24 09:08:47 +00:00
|
|
|
wt = find_worktree_by_path(list, arg);
|
|
|
|
free(to_free);
|
|
|
|
return wt;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct worktree *find_worktree_by_path(struct worktree **list, const char *p)
|
|
|
|
{
|
2020-03-10 13:11:23 +00:00
|
|
|
struct strbuf wt_path = STRBUF_INIT;
|
2020-02-24 09:08:47 +00:00
|
|
|
char *path = real_pathdup(p, 0);
|
|
|
|
|
|
|
|
if (!path)
|
2018-08-28 21:20:18 +00:00
|
|
|
return NULL;
|
2019-05-13 10:49:44 +00:00
|
|
|
for (; *list; list++) {
|
2020-03-10 13:11:23 +00:00
|
|
|
if (!strbuf_realpath(&wt_path, (*list)->path, 0))
|
|
|
|
continue;
|
2019-05-13 10:49:44 +00:00
|
|
|
|
2020-03-10 13:11:23 +00:00
|
|
|
if (!fspathcmp(path, wt_path.buf))
|
2016-06-03 12:19:39 +00:00
|
|
|
break;
|
2019-05-13 10:49:44 +00:00
|
|
|
}
|
2016-06-03 12:19:39 +00:00
|
|
|
free(path);
|
2020-03-10 13:11:23 +00:00
|
|
|
strbuf_release(&wt_path);
|
2016-06-03 12:19:39 +00:00
|
|
|
return *list;
|
|
|
|
}
|
|
|
|
|
2016-06-03 12:19:40 +00:00
|
|
|
int is_main_worktree(const struct worktree *wt)
|
|
|
|
{
|
|
|
|
return !wt->id;
|
|
|
|
}
|
|
|
|
|
2018-10-30 06:24:09 +00:00
|
|
|
const char *worktree_lock_reason(struct worktree *wt)
|
2016-06-13 12:18:23 +00:00
|
|
|
{
|
|
|
|
assert(!is_main_worktree(wt));
|
|
|
|
|
|
|
|
if (!wt->lock_reason_valid) {
|
|
|
|
struct strbuf path = STRBUF_INIT;
|
|
|
|
|
|
|
|
strbuf_addstr(&path, worktree_git_path(wt, "locked"));
|
|
|
|
if (file_exists(path.buf)) {
|
|
|
|
struct strbuf lock_reason = STRBUF_INIT;
|
|
|
|
if (strbuf_read_file(&lock_reason, path.buf, 0) < 0)
|
|
|
|
die_errno(_("failed to read '%s'"), path.buf);
|
|
|
|
strbuf_trim(&lock_reason);
|
|
|
|
wt->lock_reason = strbuf_detach(&lock_reason, NULL);
|
|
|
|
} else
|
|
|
|
wt->lock_reason = NULL;
|
|
|
|
wt->lock_reason_valid = 1;
|
|
|
|
strbuf_release(&path);
|
|
|
|
}
|
|
|
|
|
|
|
|
return wt->lock_reason;
|
|
|
|
}
|
|
|
|
|
2018-01-24 09:53:51 +00:00
|
|
|
/* convenient wrapper to deal with NULL strbuf */
|
|
|
|
static void strbuf_addf_gently(struct strbuf *buf, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list params;
|
|
|
|
|
|
|
|
if (!buf)
|
|
|
|
return;
|
|
|
|
|
|
|
|
va_start(params, fmt);
|
|
|
|
strbuf_vaddf(buf, fmt, params);
|
|
|
|
va_end(params);
|
|
|
|
}
|
|
|
|
|
2018-02-12 09:49:40 +00:00
|
|
|
int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
|
|
|
|
unsigned flags)
|
2018-01-24 09:53:51 +00:00
|
|
|
{
|
|
|
|
struct strbuf wt_path = STRBUF_INIT;
|
2020-03-10 13:11:22 +00:00
|
|
|
struct strbuf realpath = STRBUF_INIT;
|
2018-01-24 09:53:51 +00:00
|
|
|
char *path = NULL;
|
|
|
|
int err, ret = -1;
|
|
|
|
|
|
|
|
strbuf_addf(&wt_path, "%s/.git", wt->path);
|
|
|
|
|
|
|
|
if (is_main_worktree(wt)) {
|
|
|
|
if (is_directory(wt_path.buf)) {
|
|
|
|
ret = 0;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Main worktree using .git file to point to the
|
|
|
|
* repository would make it impossible to know where
|
|
|
|
* the actual worktree is if this function is executed
|
|
|
|
* from another worktree. No .git file support for now.
|
|
|
|
*/
|
|
|
|
strbuf_addf_gently(errmsg,
|
|
|
|
_("'%s' at main working tree is not the repository directory"),
|
|
|
|
wt_path.buf);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure "gitdir" file points to a real .git file and that
|
|
|
|
* file points back here.
|
|
|
|
*/
|
|
|
|
if (!is_absolute_path(wt->path)) {
|
|
|
|
strbuf_addf_gently(errmsg,
|
|
|
|
_("'%s' file does not contain absolute path to the working tree location"),
|
|
|
|
git_common_path("worktrees/%s/gitdir", wt->id));
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2018-02-12 09:49:40 +00:00
|
|
|
if (flags & WT_VALIDATE_WORKTREE_MISSING_OK &&
|
|
|
|
!file_exists(wt->path)) {
|
|
|
|
ret = 0;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2018-01-24 09:53:51 +00:00
|
|
|
if (!file_exists(wt_path.buf)) {
|
|
|
|
strbuf_addf_gently(errmsg, _("'%s' does not exist"), wt_path.buf);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
path = xstrdup_or_null(read_gitfile_gently(wt_path.buf, &err));
|
|
|
|
if (!path) {
|
|
|
|
strbuf_addf_gently(errmsg, _("'%s' is not a .git file, error code %d"),
|
|
|
|
wt_path.buf, err);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2020-03-10 13:11:22 +00:00
|
|
|
strbuf_realpath(&realpath, git_common_path("worktrees/%s", wt->id), 1);
|
|
|
|
ret = fspathcmp(path, realpath.buf);
|
2018-01-24 09:53:51 +00:00
|
|
|
|
|
|
|
if (ret)
|
|
|
|
strbuf_addf_gently(errmsg, _("'%s' does not point back to '%s'"),
|
|
|
|
wt->path, git_common_path("worktrees/%s", wt->id));
|
|
|
|
done:
|
|
|
|
free(path);
|
|
|
|
strbuf_release(&wt_path);
|
2020-03-10 13:11:22 +00:00
|
|
|
strbuf_release(&realpath);
|
2018-01-24 09:53:51 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-02-12 09:49:35 +00:00
|
|
|
void update_worktree_location(struct worktree *wt, const char *path_)
|
|
|
|
{
|
|
|
|
struct strbuf path = STRBUF_INIT;
|
|
|
|
|
|
|
|
if (is_main_worktree(wt))
|
2018-05-02 09:38:39 +00:00
|
|
|
BUG("can't relocate main worktree");
|
2018-02-12 09:49:35 +00:00
|
|
|
|
|
|
|
strbuf_realpath(&path, path_, 1);
|
|
|
|
if (fspathcmp(wt->path, path.buf)) {
|
|
|
|
write_file(git_common_path("worktrees/%s/gitdir", wt->id),
|
|
|
|
"%s/.git", path.buf);
|
|
|
|
free(wt->path);
|
|
|
|
wt->path = strbuf_detach(&path, NULL);
|
|
|
|
}
|
|
|
|
strbuf_release(&path);
|
|
|
|
}
|
|
|
|
|
2016-04-22 13:01:36 +00:00
|
|
|
int is_worktree_being_rebased(const struct worktree *wt,
|
|
|
|
const char *target)
|
2016-04-22 13:01:33 +00:00
|
|
|
{
|
|
|
|
struct wt_status_state state;
|
|
|
|
int found_rebase;
|
|
|
|
|
|
|
|
memset(&state, 0, sizeof(state));
|
|
|
|
found_rebase = wt_status_check_rebase(wt, &state) &&
|
2020-09-27 13:15:47 +00:00
|
|
|
(state.rebase_in_progress ||
|
|
|
|
state.rebase_interactive_in_progress) &&
|
|
|
|
state.branch &&
|
|
|
|
skip_prefix(target, "refs/heads/", &target) &&
|
|
|
|
!strcmp(state.branch, target);
|
wt-status: introduce wt_status_state_free_buffers()
When we have a `struct wt_status_state`, we manually free its `branch`,
`onto` and `detached_from`, or sometimes just one or two of them.
Provide a function `wt_status_state_free_buffers()` which does the
freeing.
The callers are still aware of these fields, e.g., they check whether
`branch` was populated or not. But this way, they don't need to know
about *all* of them, and if `struct wt_status_state` gets more fields,
they will not need to learn to free them.
Users of `struct wt_status` (which contains a `wt_status_state`) already
have `wt_status_collect_free_buffers()` (corresponding to
`wt_status_collect()`) which we can also teach to use this new helper.
Finally, note that we're currently leaving dangling pointers behind.
Some callers work on a stack-allocated struct, where this is obviously
ok. But for the users of `run_status()` in builtin/commit.c, there are
ample opportunities for someone to mistakenly use those dangling
pointers. We seem to be ok for now, but it's a use-after-free waiting to
happen. Let's leave NULL-pointers behind instead.
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-27 13:15:43 +00:00
|
|
|
wt_status_state_free_buffers(&state);
|
2016-04-22 13:01:33 +00:00
|
|
|
return found_rebase;
|
|
|
|
}
|
|
|
|
|
2016-04-22 13:01:36 +00:00
|
|
|
int is_worktree_being_bisected(const struct worktree *wt,
|
|
|
|
const char *target)
|
2015-10-08 17:01:03 +00:00
|
|
|
{
|
2016-04-22 13:01:35 +00:00
|
|
|
struct wt_status_state state;
|
2020-09-27 13:15:46 +00:00
|
|
|
int found_bisect;
|
2016-04-22 13:01:35 +00:00
|
|
|
|
|
|
|
memset(&state, 0, sizeof(state));
|
2020-09-27 13:15:46 +00:00
|
|
|
found_bisect = wt_status_check_bisect(wt, &state) &&
|
|
|
|
state.branch &&
|
2020-09-27 13:15:47 +00:00
|
|
|
skip_prefix(target, "refs/heads/", &target) &&
|
|
|
|
!strcmp(state.branch, target);
|
wt-status: introduce wt_status_state_free_buffers()
When we have a `struct wt_status_state`, we manually free its `branch`,
`onto` and `detached_from`, or sometimes just one or two of them.
Provide a function `wt_status_state_free_buffers()` which does the
freeing.
The callers are still aware of these fields, e.g., they check whether
`branch` was populated or not. But this way, they don't need to know
about *all* of them, and if `struct wt_status_state` gets more fields,
they will not need to learn to free them.
Users of `struct wt_status` (which contains a `wt_status_state`) already
have `wt_status_collect_free_buffers()` (corresponding to
`wt_status_collect()`) which we can also teach to use this new helper.
Finally, note that we're currently leaving dangling pointers behind.
Some callers work on a stack-allocated struct, where this is obviously
ok. But for the users of `run_status()` in builtin/commit.c, there are
ample opportunities for someone to mistakenly use those dangling
pointers. We seem to be ok for now, but it's a use-after-free waiting to
happen. Let's leave NULL-pointers behind instead.
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-09-27 13:15:43 +00:00
|
|
|
wt_status_state_free_buffers(&state);
|
2020-09-27 13:15:46 +00:00
|
|
|
return found_bisect;
|
2016-04-22 13:01:35 +00:00
|
|
|
}
|
|
|
|
|
2016-04-22 13:01:33 +00:00
|
|
|
/*
|
|
|
|
* note: this function should be able to detect shared symref even if
|
|
|
|
* HEAD is temporarily detached (e.g. in the middle of rebase or
|
|
|
|
* bisect). New commands that do similar things should update this
|
|
|
|
* function as well.
|
|
|
|
*/
|
2016-04-22 13:01:27 +00:00
|
|
|
const struct worktree *find_shared_symref(const char *symref,
|
|
|
|
const char *target)
|
2015-10-08 17:01:03 +00:00
|
|
|
{
|
2016-04-22 13:01:27 +00:00
|
|
|
const struct worktree *existing = NULL;
|
|
|
|
static struct worktree **worktrees;
|
2015-10-08 17:01:03 +00:00
|
|
|
int i = 0;
|
|
|
|
|
2016-04-22 13:01:27 +00:00
|
|
|
if (worktrees)
|
|
|
|
free_worktrees(worktrees);
|
2020-06-19 23:35:44 +00:00
|
|
|
worktrees = get_worktrees();
|
2016-04-22 13:01:27 +00:00
|
|
|
|
2015-10-08 17:01:03 +00:00
|
|
|
for (i = 0; worktrees[i]; i++) {
|
2016-04-22 13:01:32 +00:00
|
|
|
struct worktree *wt = worktrees[i];
|
2017-04-24 10:01:23 +00:00
|
|
|
const char *symref_target;
|
|
|
|
struct ref_store *refs;
|
|
|
|
int flags;
|
|
|
|
|
2016-10-12 16:41:07 +00:00
|
|
|
if (wt->is_bare)
|
|
|
|
continue;
|
2016-04-22 13:01:32 +00:00
|
|
|
|
2016-04-22 13:01:33 +00:00
|
|
|
if (wt->is_detached && !strcmp(symref, "HEAD")) {
|
|
|
|
if (is_worktree_being_rebased(wt, target)) {
|
|
|
|
existing = wt;
|
|
|
|
break;
|
|
|
|
}
|
2016-04-22 13:01:35 +00:00
|
|
|
if (is_worktree_being_bisected(wt, target)) {
|
|
|
|
existing = wt;
|
|
|
|
break;
|
|
|
|
}
|
2016-04-22 13:01:33 +00:00
|
|
|
}
|
|
|
|
|
2017-04-24 10:01:23 +00:00
|
|
|
refs = get_worktree_ref_store(wt);
|
|
|
|
symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
|
2017-09-23 09:44:57 +00:00
|
|
|
NULL, &flags);
|
2017-10-19 17:49:36 +00:00
|
|
|
if ((flags & REF_ISSYMREF) &&
|
|
|
|
symref_target && !strcmp(symref_target, target)) {
|
2016-04-22 13:01:32 +00:00
|
|
|
existing = wt;
|
2015-10-08 17:01:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2015-10-02 11:55:31 +00:00
|
|
|
}
|
2015-10-08 17:01:03 +00:00
|
|
|
|
2015-10-02 11:55:31 +00:00
|
|
|
return existing;
|
|
|
|
}
|
2016-12-12 19:04:33 +00:00
|
|
|
|
|
|
|
int submodule_uses_worktrees(const char *path)
|
|
|
|
{
|
|
|
|
char *submodule_gitdir;
|
2020-02-22 20:17:41 +00:00
|
|
|
struct strbuf sb = STRBUF_INIT, err = STRBUF_INIT;
|
2016-12-12 19:04:33 +00:00
|
|
|
DIR *dir;
|
|
|
|
struct dirent *d;
|
2016-12-27 17:50:13 +00:00
|
|
|
int ret = 0;
|
setup: fix memory leaks with `struct repository_format`
After we set up a `struct repository_format`, it owns various pieces of
allocated memory. We then either use those members, because we decide we
want to use the "candidate" repository format, or we discard the
candidate / scratch space. In the first case, we transfer ownership of
the memory to a few global variables. In the latter case, we just
silently drop the struct and end up leaking memory.
Introduce an initialization macro `REPOSITORY_FORMAT_INIT` and a
function `clear_repository_format()`, to be used on each side of
`read_repository_format()`. To have a clear and simple memory ownership,
let all users of `struct repository_format` duplicate the strings that
they take from it, rather than stealing the pointers.
Call `clear_...()` at the start of `read_...()` instead of just zeroing
the struct, since we sometimes enter the function multiple times. Thus,
it is important to initialize the struct before calling `read_...()`, so
document that. It's also important because we might not even call
`read_...()` before we call `clear_...()`, see, e.g., builtin/init-db.c.
Teach `read_...()` to clear the struct on error, so that it is reset to
a safe state, and document this. (In `setup_git_directory_gently()`, we
look at `repo_fmt.hash_algo` even if `repo_fmt.version` is -1, which we
weren't actually supposed to do per the API. After this commit, that's
ok.)
We inherit the existing code's combining "error" and "no version found".
Both are signalled through `version == -1` and now both cause us to
clear any partial configuration we have picked up. For "extensions.*",
that's fine, since they require a positive version number. For
"core.bare" and "core.worktree", we're already verifying that we have a
non-negative version number before using them.
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-02-28 20:36:28 +00:00
|
|
|
struct repository_format format = REPOSITORY_FORMAT_INIT;
|
2016-12-12 19:04:33 +00:00
|
|
|
|
|
|
|
submodule_gitdir = git_pathdup_submodule(path, "%s", "");
|
|
|
|
if (!submodule_gitdir)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* The env would be set for the superproject. */
|
|
|
|
get_common_dir_noenv(&sb, submodule_gitdir);
|
2017-05-04 13:59:19 +00:00
|
|
|
free(submodule_gitdir);
|
2016-12-12 19:04:33 +00:00
|
|
|
|
|
|
|
strbuf_addstr(&sb, "/config");
|
|
|
|
read_repository_format(&format, sb.buf);
|
2020-02-22 20:17:41 +00:00
|
|
|
if (verify_repository_format(&format, &err)) {
|
|
|
|
strbuf_release(&err);
|
2016-12-12 19:04:33 +00:00
|
|
|
strbuf_release(&sb);
|
setup: fix memory leaks with `struct repository_format`
After we set up a `struct repository_format`, it owns various pieces of
allocated memory. We then either use those members, because we decide we
want to use the "candidate" repository format, or we discard the
candidate / scratch space. In the first case, we transfer ownership of
the memory to a few global variables. In the latter case, we just
silently drop the struct and end up leaking memory.
Introduce an initialization macro `REPOSITORY_FORMAT_INIT` and a
function `clear_repository_format()`, to be used on each side of
`read_repository_format()`. To have a clear and simple memory ownership,
let all users of `struct repository_format` duplicate the strings that
they take from it, rather than stealing the pointers.
Call `clear_...()` at the start of `read_...()` instead of just zeroing
the struct, since we sometimes enter the function multiple times. Thus,
it is important to initialize the struct before calling `read_...()`, so
document that. It's also important because we might not even call
`read_...()` before we call `clear_...()`, see, e.g., builtin/init-db.c.
Teach `read_...()` to clear the struct on error, so that it is reset to
a safe state, and document this. (In `setup_git_directory_gently()`, we
look at `repo_fmt.hash_algo` even if `repo_fmt.version` is -1, which we
weren't actually supposed to do per the API. After this commit, that's
ok.)
We inherit the existing code's combining "error" and "no version found".
Both are signalled through `version == -1` and now both cause us to
clear any partial configuration we have picked up. For "extensions.*",
that's fine, since they require a positive version number. For
"core.bare" and "core.worktree", we're already verifying that we have a
non-negative version number before using them.
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-02-28 20:36:28 +00:00
|
|
|
clear_repository_format(&format);
|
2016-12-12 19:04:33 +00:00
|
|
|
return 1;
|
|
|
|
}
|
setup: fix memory leaks with `struct repository_format`
After we set up a `struct repository_format`, it owns various pieces of
allocated memory. We then either use those members, because we decide we
want to use the "candidate" repository format, or we discard the
candidate / scratch space. In the first case, we transfer ownership of
the memory to a few global variables. In the latter case, we just
silently drop the struct and end up leaking memory.
Introduce an initialization macro `REPOSITORY_FORMAT_INIT` and a
function `clear_repository_format()`, to be used on each side of
`read_repository_format()`. To have a clear and simple memory ownership,
let all users of `struct repository_format` duplicate the strings that
they take from it, rather than stealing the pointers.
Call `clear_...()` at the start of `read_...()` instead of just zeroing
the struct, since we sometimes enter the function multiple times. Thus,
it is important to initialize the struct before calling `read_...()`, so
document that. It's also important because we might not even call
`read_...()` before we call `clear_...()`, see, e.g., builtin/init-db.c.
Teach `read_...()` to clear the struct on error, so that it is reset to
a safe state, and document this. (In `setup_git_directory_gently()`, we
look at `repo_fmt.hash_algo` even if `repo_fmt.version` is -1, which we
weren't actually supposed to do per the API. After this commit, that's
ok.)
We inherit the existing code's combining "error" and "no version found".
Both are signalled through `version == -1` and now both cause us to
clear any partial configuration we have picked up. For "extensions.*",
that's fine, since they require a positive version number. For
"core.bare" and "core.worktree", we're already verifying that we have a
non-negative version number before using them.
Signed-off-by: Martin Ågren <martin.agren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-02-28 20:36:28 +00:00
|
|
|
clear_repository_format(&format);
|
2020-02-22 20:17:41 +00:00
|
|
|
strbuf_release(&err);
|
2016-12-12 19:04:33 +00:00
|
|
|
|
|
|
|
/* Replace config by worktrees. */
|
|
|
|
strbuf_setlen(&sb, sb.len - strlen("config"));
|
|
|
|
strbuf_addstr(&sb, "worktrees");
|
|
|
|
|
|
|
|
/* See if there is any file inside the worktrees directory. */
|
|
|
|
dir = opendir(sb.buf);
|
|
|
|
strbuf_release(&sb);
|
|
|
|
|
|
|
|
if (!dir)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while ((d = readdir(dir)) != NULL) {
|
|
|
|
if (is_dot_or_dotdot(d->d_name))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
closedir(dir);
|
|
|
|
return ret;
|
|
|
|
}
|
2017-08-23 12:36:59 +00:00
|
|
|
|
2018-10-21 08:08:54 +00:00
|
|
|
int parse_worktree_ref(const char *worktree_ref, const char **name,
|
|
|
|
int *name_length, const char **ref)
|
|
|
|
{
|
|
|
|
if (skip_prefix(worktree_ref, "main-worktree/", &worktree_ref)) {
|
|
|
|
if (!*worktree_ref)
|
|
|
|
return -1;
|
|
|
|
if (name)
|
|
|
|
*name = NULL;
|
|
|
|
if (name_length)
|
|
|
|
*name_length = 0;
|
|
|
|
if (ref)
|
|
|
|
*ref = worktree_ref;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (skip_prefix(worktree_ref, "worktrees/", &worktree_ref)) {
|
|
|
|
const char *slash = strchr(worktree_ref, '/');
|
|
|
|
|
|
|
|
if (!slash || slash == worktree_ref || !slash[1])
|
|
|
|
return -1;
|
|
|
|
if (name)
|
|
|
|
*name = worktree_ref;
|
|
|
|
if (name_length)
|
|
|
|
*name_length = slash - worktree_ref;
|
|
|
|
if (ref)
|
|
|
|
*ref = slash + 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-10-21 08:08:56 +00:00
|
|
|
void strbuf_worktree_ref(const struct worktree *wt,
|
|
|
|
struct strbuf *sb,
|
|
|
|
const char *refname)
|
|
|
|
{
|
|
|
|
switch (ref_type(refname)) {
|
|
|
|
case REF_TYPE_PSEUDOREF:
|
|
|
|
case REF_TYPE_PER_WORKTREE:
|
|
|
|
if (wt && !wt->is_current) {
|
|
|
|
if (is_main_worktree(wt))
|
|
|
|
strbuf_addstr(sb, "main-worktree/");
|
|
|
|
else
|
|
|
|
strbuf_addf(sb, "worktrees/%s/", wt->id);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case REF_TYPE_MAIN_PSEUDOREF:
|
|
|
|
case REF_TYPE_OTHER_PSEUDOREF:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case REF_TYPE_NORMAL:
|
|
|
|
/*
|
|
|
|
* For shared refs, don't prefix worktrees/ or
|
|
|
|
* main-worktree/. It's not necessary and
|
|
|
|
* files-backend.c can't handle it anyway.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
strbuf_addstr(sb, refname);
|
|
|
|
}
|
|
|
|
|
2017-08-23 12:36:59 +00:00
|
|
|
int other_head_refs(each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
struct worktree **worktrees, **p;
|
2020-09-27 13:15:44 +00:00
|
|
|
struct strbuf refname = STRBUF_INIT;
|
2017-08-23 12:36:59 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
2020-06-19 23:35:44 +00:00
|
|
|
worktrees = get_worktrees();
|
2017-08-23 12:36:59 +00:00
|
|
|
for (p = worktrees; *p; p++) {
|
|
|
|
struct worktree *wt = *p;
|
2018-10-21 08:08:56 +00:00
|
|
|
struct object_id oid;
|
|
|
|
int flag;
|
2017-08-23 12:36:59 +00:00
|
|
|
|
|
|
|
if (wt->is_current)
|
|
|
|
continue;
|
|
|
|
|
2020-09-27 13:15:44 +00:00
|
|
|
strbuf_reset(&refname);
|
|
|
|
strbuf_worktree_ref(wt, &refname, "HEAD");
|
2018-10-21 08:08:56 +00:00
|
|
|
if (!refs_read_ref_full(get_main_ref_store(the_repository),
|
2020-09-27 13:15:44 +00:00
|
|
|
refname.buf,
|
2018-10-21 08:08:56 +00:00
|
|
|
RESOLVE_REF_READING,
|
|
|
|
&oid, &flag))
|
2020-09-27 13:15:44 +00:00
|
|
|
ret = fn(refname.buf, &oid, flag, cb_data);
|
2017-08-23 12:36:59 +00:00
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
free_worktrees(worktrees);
|
2020-09-27 13:15:44 +00:00
|
|
|
strbuf_release(&refname);
|
2017-08-23 12:36:59 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2020-08-31 06:57:57 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Repair worktree's /path/to/worktree/.git file if missing, corrupt, or not
|
|
|
|
* pointing at <repo>/worktrees/<id>.
|
|
|
|
*/
|
|
|
|
static void repair_gitfile(struct worktree *wt,
|
|
|
|
worktree_repair_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
struct strbuf dotgit = STRBUF_INIT;
|
|
|
|
struct strbuf repo = STRBUF_INIT;
|
|
|
|
char *backlink;
|
|
|
|
const char *repair = NULL;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/* missing worktree can't be repaired */
|
|
|
|
if (!file_exists(wt->path))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!is_directory(wt->path)) {
|
|
|
|
fn(1, wt->path, _("not a directory"), cb_data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
strbuf_realpath(&repo, git_common_path("worktrees/%s", wt->id), 1);
|
|
|
|
strbuf_addf(&dotgit, "%s/.git", wt->path);
|
|
|
|
backlink = xstrdup_or_null(read_gitfile_gently(dotgit.buf, &err));
|
|
|
|
|
|
|
|
if (err == READ_GITFILE_ERR_NOT_A_FILE)
|
|
|
|
fn(1, wt->path, _(".git is not a file"), cb_data);
|
|
|
|
else if (err)
|
|
|
|
repair = _(".git file broken");
|
|
|
|
else if (fspathcmp(backlink, repo.buf))
|
|
|
|
repair = _(".git file incorrect");
|
|
|
|
|
|
|
|
if (repair) {
|
|
|
|
fn(0, wt->path, repair, cb_data);
|
|
|
|
write_file(dotgit.buf, "gitdir: %s", repo.buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(backlink);
|
|
|
|
strbuf_release(&repo);
|
|
|
|
strbuf_release(&dotgit);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void repair_noop(int iserr, const char *path, const char *msg,
|
|
|
|
void *cb_data)
|
|
|
|
{
|
|
|
|
/* nothing */
|
|
|
|
}
|
|
|
|
|
|
|
|
void repair_worktrees(worktree_repair_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
struct worktree **worktrees = get_worktrees();
|
|
|
|
struct worktree **wt = worktrees + 1; /* +1 skips main worktree */
|
|
|
|
|
|
|
|
if (!fn)
|
|
|
|
fn = repair_noop;
|
|
|
|
for (; *wt; wt++)
|
|
|
|
repair_gitfile(*wt, fn, cb_data);
|
|
|
|
free_worktrees(worktrees);
|
|
|
|
}
|
2020-08-31 06:57:58 +00:00
|
|
|
|
|
|
|
static int is_main_worktree_path(const char *path)
|
|
|
|
{
|
|
|
|
struct strbuf target = STRBUF_INIT;
|
|
|
|
struct strbuf maindir = STRBUF_INIT;
|
|
|
|
int cmp;
|
|
|
|
|
|
|
|
strbuf_add_real_path(&target, path);
|
|
|
|
strbuf_strip_suffix(&target, "/.git");
|
|
|
|
strbuf_add_real_path(&maindir, get_git_common_dir());
|
|
|
|
strbuf_strip_suffix(&maindir, "/.git");
|
|
|
|
cmp = fspathcmp(maindir.buf, target.buf);
|
|
|
|
|
|
|
|
strbuf_release(&maindir);
|
|
|
|
strbuf_release(&target);
|
|
|
|
return !cmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Repair <repo>/worktrees/<id>/gitdir if missing, corrupt, or not pointing at
|
|
|
|
* the worktree's path.
|
|
|
|
*/
|
|
|
|
void repair_worktree_at_path(const char *path,
|
|
|
|
worktree_repair_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
struct strbuf dotgit = STRBUF_INIT;
|
|
|
|
struct strbuf realdotgit = STRBUF_INIT;
|
|
|
|
struct strbuf gitdir = STRBUF_INIT;
|
|
|
|
struct strbuf olddotgit = STRBUF_INIT;
|
|
|
|
char *backlink = NULL;
|
|
|
|
const char *repair = NULL;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!fn)
|
|
|
|
fn = repair_noop;
|
|
|
|
|
|
|
|
if (is_main_worktree_path(path))
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
strbuf_addf(&dotgit, "%s/.git", path);
|
|
|
|
if (!strbuf_realpath(&realdotgit, dotgit.buf, 0)) {
|
|
|
|
fn(1, path, _("not a valid path"), cb_data);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
backlink = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));
|
|
|
|
if (err == READ_GITFILE_ERR_NOT_A_FILE) {
|
|
|
|
fn(1, realdotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
|
|
|
|
goto done;
|
|
|
|
} else if (err) {
|
|
|
|
fn(1, realdotgit.buf, _("unable to locate repository; .git file broken"), cb_data);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
strbuf_addf(&gitdir, "%s/gitdir", backlink);
|
|
|
|
if (strbuf_read_file(&olddotgit, gitdir.buf, 0) < 0)
|
|
|
|
repair = _("gitdir unreadable");
|
|
|
|
else {
|
|
|
|
strbuf_rtrim(&olddotgit);
|
|
|
|
if (fspathcmp(olddotgit.buf, realdotgit.buf))
|
|
|
|
repair = _("gitdir incorrect");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (repair) {
|
|
|
|
fn(0, gitdir.buf, repair, cb_data);
|
|
|
|
write_file(gitdir.buf, "%s", realdotgit.buf);
|
|
|
|
}
|
|
|
|
done:
|
|
|
|
free(backlink);
|
|
|
|
strbuf_release(&olddotgit);
|
|
|
|
strbuf_release(&gitdir);
|
|
|
|
strbuf_release(&realdotgit);
|
|
|
|
strbuf_release(&dotgit);
|
|
|
|
}
|