2015-10-02 14:55:31 +03:00
|
|
|
#include "cache.h"
|
2017-06-22 21:43:37 +03:00
|
|
|
#include "repository.h"
|
2015-10-02 14:55:31 +03:00
|
|
|
#include "refs.h"
|
|
|
|
#include "strbuf.h"
|
|
|
|
#include "worktree.h"
|
2016-04-22 16:01:28 +03:00
|
|
|
#include "dir.h"
|
2016-04-22 16:01:33 +03:00
|
|
|
#include "wt-status.h"
|
2015-10-02 14:55:31 +03:00
|
|
|
|
2015-10-08 20:01:03 +03:00
|
|
|
void free_worktrees(struct worktree **worktrees)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
for (i = 0; worktrees[i]; i++) {
|
|
|
|
free(worktrees[i]->path);
|
2016-04-22 16:01:26 +03:00
|
|
|
free(worktrees[i]->id);
|
2015-10-08 20:01:04 +03:00
|
|
|
free(worktrees[i]->head_ref);
|
2016-06-13 15:18:23 +03:00
|
|
|
free(worktrees[i]->lock_reason);
|
2015-10-08 20:01:03 +03:00
|
|
|
free(worktrees[i]);
|
|
|
|
}
|
|
|
|
free (worktrees);
|
|
|
|
}
|
|
|
|
|
2015-10-08 20:01:04 +03:00
|
|
|
/**
|
2017-04-24 13:01:23 +03:00
|
|
|
* Update head_sha1, head_ref and is_detached of the given worktree
|
2015-10-08 20:01:04 +03:00
|
|
|
*/
|
2017-04-24 13:01:23 +03:00
|
|
|
static void add_head_info(struct worktree *wt)
|
2015-10-08 20:01:04 +03:00
|
|
|
{
|
2017-04-24 13:01:23 +03: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 13:41:24 +03: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-16 01:07:09 +03:00
|
|
|
&wt->head_oid, &flags);
|
2017-04-24 13:01:23 +03:00
|
|
|
if (!target)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (flags & REF_ISSYMREF)
|
|
|
|
wt->head_ref = xstrdup(target);
|
|
|
|
else
|
|
|
|
wt->is_detached = 1;
|
2015-10-08 20:01:04 +03:00
|
|
|
}
|
|
|
|
|
2015-10-08 20:01:03 +03:00
|
|
|
/**
|
|
|
|
* get the main worktree
|
|
|
|
*/
|
|
|
|
static struct worktree *get_main_worktree(void)
|
2015-10-08 20:01:02 +03:00
|
|
|
{
|
2015-10-08 20:01:03 +03:00
|
|
|
struct worktree *worktree = NULL;
|
2015-10-08 20:01:02 +03:00
|
|
|
struct strbuf path = STRBUF_INIT;
|
2015-10-08 20:01:03 +03:00
|
|
|
struct strbuf worktree_path = STRBUF_INIT;
|
2015-10-08 20:01:04 +03:00
|
|
|
int is_bare = 0;
|
2015-10-08 20:01:02 +03:00
|
|
|
|
2016-07-09 18:43:59 +03:00
|
|
|
strbuf_add_absolute_path(&worktree_path, get_git_common_dir());
|
2015-10-08 20:01:04 +03:00
|
|
|
is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
|
|
|
|
if (is_bare)
|
2015-10-08 20:01:03 +03:00
|
|
|
strbuf_strip_suffix(&worktree_path, "/.");
|
|
|
|
|
|
|
|
strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
|
|
|
|
|
2016-11-22 13:00:44 +03:00
|
|
|
worktree = xcalloc(1, sizeof(*worktree));
|
2015-10-08 20:01:04 +03:00
|
|
|
worktree->path = strbuf_detach(&worktree_path, NULL);
|
|
|
|
worktree->is_bare = is_bare;
|
2017-04-24 13:01:23 +03:00
|
|
|
add_head_info(worktree);
|
2015-10-08 20:01:04 +03:00
|
|
|
|
2015-10-08 20:01:02 +03:00
|
|
|
strbuf_release(&path);
|
2015-10-08 20:01:03 +03:00
|
|
|
strbuf_release(&worktree_path);
|
|
|
|
return worktree;
|
2015-10-08 20:01:02 +03:00
|
|
|
}
|
|
|
|
|
2015-10-08 20:01:03 +03:00
|
|
|
static struct worktree *get_linked_worktree(const char *id)
|
2015-10-02 14:55:31 +03:00
|
|
|
{
|
2015-10-08 20:01:03 +03:00
|
|
|
struct worktree *worktree = NULL;
|
2015-10-02 14:55:31 +03:00
|
|
|
struct strbuf path = STRBUF_INIT;
|
2015-10-08 20:01:03 +03:00
|
|
|
struct strbuf worktree_path = STRBUF_INIT;
|
2015-10-02 14:55:31 +03:00
|
|
|
|
2015-10-08 20:01:02 +03:00
|
|
|
if (!id)
|
|
|
|
die("Missing linked worktree name");
|
2015-10-02 14:55:31 +03:00
|
|
|
|
2017-06-22 21:43:37 +03:00
|
|
|
strbuf_git_common_path(&path, the_repository, "worktrees/%s/gitdir", id);
|
2015-10-08 20:01:03 +03:00
|
|
|
if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
|
|
|
|
/* invalid gitdir file */
|
2015-10-02 14:55:31 +03:00
|
|
|
goto done;
|
2015-10-08 20:01:03 +03:00
|
|
|
|
|
|
|
strbuf_rtrim(&worktree_path);
|
|
|
|
if (!strbuf_strip_suffix(&worktree_path, "/.git")) {
|
|
|
|
strbuf_reset(&worktree_path);
|
2016-07-09 18:43:59 +03:00
|
|
|
strbuf_add_absolute_path(&worktree_path, ".");
|
2015-10-08 20:01:03 +03:00
|
|
|
strbuf_strip_suffix(&worktree_path, "/.");
|
|
|
|
}
|
|
|
|
|
2015-10-08 20:01:02 +03:00
|
|
|
strbuf_reset(&path);
|
2015-10-08 20:01:03 +03:00
|
|
|
strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
|
|
|
|
|
2016-11-22 13:00:44 +03:00
|
|
|
worktree = xcalloc(1, sizeof(*worktree));
|
2015-10-08 20:01:04 +03:00
|
|
|
worktree->path = strbuf_detach(&worktree_path, NULL);
|
2016-04-22 16:01:26 +03:00
|
|
|
worktree->id = xstrdup(id);
|
2017-04-24 13:01:23 +03:00
|
|
|
add_head_info(worktree);
|
2015-10-02 14:55:31 +03:00
|
|
|
|
|
|
|
done:
|
|
|
|
strbuf_release(&path);
|
2015-10-08 20:01:03 +03:00
|
|
|
strbuf_release(&worktree_path);
|
|
|
|
return worktree;
|
2015-10-02 14:55:31 +03:00
|
|
|
}
|
|
|
|
|
2016-04-22 16:01:28 +03:00
|
|
|
static void mark_current_worktree(struct worktree **worktrees)
|
|
|
|
{
|
2017-01-26 20:54:23 +03:00
|
|
|
char *git_dir = absolute_pathdup(get_git_dir());
|
2016-04-22 16:01:28 +03:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; worktrees[i]; i++) {
|
|
|
|
struct worktree *wt = worktrees[i];
|
2016-05-22 12:33:52 +03: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 16:01:28 +03:00
|
|
|
break;
|
2016-05-22 12:33:52 +03:00
|
|
|
}
|
2016-04-22 16:01:28 +03:00
|
|
|
}
|
2016-05-22 12:33:52 +03:00
|
|
|
free(git_dir);
|
2016-04-22 16:01:28 +03:00
|
|
|
}
|
|
|
|
|
2016-11-28 12:36:56 +03:00
|
|
|
static int compare_worktree(const void *a_, const void *b_)
|
|
|
|
{
|
|
|
|
const struct worktree *const *a = a_;
|
|
|
|
const struct worktree *const *b = b_;
|
|
|
|
return fspathcmp((*a)->path, (*b)->path);
|
|
|
|
}
|
|
|
|
|
2016-11-28 12:36:55 +03:00
|
|
|
struct worktree **get_worktrees(unsigned flags)
|
2015-10-02 14:55:31 +03:00
|
|
|
{
|
2015-10-08 20:01:03 +03:00
|
|
|
struct worktree **list = NULL;
|
2015-10-02 14:55:31 +03:00
|
|
|
struct strbuf path = STRBUF_INIT;
|
|
|
|
DIR *dir;
|
|
|
|
struct dirent *d;
|
2015-10-08 20:01:03 +03:00
|
|
|
int counter = 0, alloc = 2;
|
|
|
|
|
2017-02-25 13:30:03 +03:00
|
|
|
ALLOC_ARRAY(list, alloc);
|
2015-10-02 14:55:31 +03: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 12:36:54 +03:00
|
|
|
list[counter++] = get_main_worktree();
|
2015-10-02 14:55:31 +03:00
|
|
|
|
|
|
|
strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
|
|
|
|
dir = opendir(path.buf);
|
|
|
|
strbuf_release(&path);
|
2015-10-08 20:01:03 +03:00
|
|
|
if (dir) {
|
|
|
|
while ((d = readdir(dir)) != NULL) {
|
|
|
|
struct worktree *linked = NULL;
|
2016-05-22 12:33:54 +03:00
|
|
|
if (is_dot_or_dotdot(d->d_name))
|
2015-10-08 20:01:03 +03:00
|
|
|
continue;
|
2015-10-02 14:55:31 +03:00
|
|
|
|
2016-01-18 14:21:29 +03:00
|
|
|
if ((linked = get_linked_worktree(d->d_name))) {
|
|
|
|
ALLOC_GROW(list, counter + 1, alloc);
|
|
|
|
list[counter++] = linked;
|
|
|
|
}
|
2015-10-08 20:01:03 +03:00
|
|
|
}
|
|
|
|
closedir(dir);
|
|
|
|
}
|
|
|
|
ALLOC_GROW(list, counter + 1, alloc);
|
|
|
|
list[counter] = NULL;
|
2016-04-22 16:01:28 +03:00
|
|
|
|
2016-11-28 12:36:56 +03:00
|
|
|
if (flags & GWT_SORT_LINKED)
|
|
|
|
/*
|
|
|
|
* don't sort the first item (main worktree), which will
|
|
|
|
* always be the first
|
|
|
|
*/
|
|
|
|
QSORT(list + 1, counter - 1, compare_worktree);
|
|
|
|
|
2016-04-22 16:01:28 +03:00
|
|
|
mark_current_worktree(list);
|
2015-10-08 20:01:03 +03:00
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
2016-04-22 16:01:26 +03: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 15:18:26 +03: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 15:19:39 +03:00
|
|
|
struct worktree *find_worktree(struct worktree **list,
|
|
|
|
const char *prefix,
|
|
|
|
const char *arg)
|
|
|
|
{
|
2016-06-13 15:18:26 +03:00
|
|
|
struct worktree *wt;
|
2016-06-03 15:19:39 +03:00
|
|
|
char *path;
|
2017-03-21 04:28:49 +03:00
|
|
|
char *to_free = NULL;
|
2016-06-03 15:19:39 +03:00
|
|
|
|
2016-06-13 15:18:26 +03:00
|
|
|
if ((wt = find_worktree_by_suffix(list, arg)))
|
|
|
|
return wt;
|
|
|
|
|
2017-03-21 04:28:49 +03:00
|
|
|
if (prefix)
|
|
|
|
arg = to_free = prefix_filename(prefix, arg);
|
2018-08-29 00:20:18 +03:00
|
|
|
path = real_pathdup(arg, 0);
|
|
|
|
if (!path) {
|
|
|
|
free(to_free);
|
|
|
|
return NULL;
|
|
|
|
}
|
2019-05-13 13:49:44 +03:00
|
|
|
for (; *list; list++) {
|
|
|
|
const char *wt_path = real_path_if_valid((*list)->path);
|
|
|
|
|
|
|
|
if (wt_path && !fspathcmp(path, wt_path))
|
2016-06-03 15:19:39 +03:00
|
|
|
break;
|
2019-05-13 13:49:44 +03:00
|
|
|
}
|
2016-06-03 15:19:39 +03:00
|
|
|
free(path);
|
2017-03-21 04:28:49 +03:00
|
|
|
free(to_free);
|
2016-06-03 15:19:39 +03:00
|
|
|
return *list;
|
|
|
|
}
|
|
|
|
|
2016-06-03 15:19:40 +03:00
|
|
|
int is_main_worktree(const struct worktree *wt)
|
|
|
|
{
|
|
|
|
return !wt->id;
|
|
|
|
}
|
|
|
|
|
2018-10-30 09:24:09 +03:00
|
|
|
const char *worktree_lock_reason(struct worktree *wt)
|
2016-06-13 15:18:23 +03: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 12:53:51 +03: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 12:49:40 +03:00
|
|
|
int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
|
|
|
|
unsigned flags)
|
2018-01-24 12:53:51 +03:00
|
|
|
{
|
|
|
|
struct strbuf wt_path = STRBUF_INIT;
|
|
|
|
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 12:49:40 +03:00
|
|
|
if (flags & WT_VALIDATE_WORKTREE_MISSING_OK &&
|
|
|
|
!file_exists(wt->path)) {
|
|
|
|
ret = 0;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2018-01-24 12:53:51 +03: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;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = fspathcmp(path, real_path(git_common_path("worktrees/%s", wt->id)));
|
|
|
|
|
|
|
|
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);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-02-12 12:49:35 +03:00
|
|
|
void update_worktree_location(struct worktree *wt, const char *path_)
|
|
|
|
{
|
|
|
|
struct strbuf path = STRBUF_INIT;
|
|
|
|
|
|
|
|
if (is_main_worktree(wt))
|
2018-05-02 12:38:39 +03:00
|
|
|
BUG("can't relocate main worktree");
|
2018-02-12 12:49:35 +03: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 16:01:36 +03:00
|
|
|
int is_worktree_being_rebased(const struct worktree *wt,
|
|
|
|
const char *target)
|
2016-04-22 16:01:33 +03:00
|
|
|
{
|
|
|
|
struct wt_status_state state;
|
|
|
|
int found_rebase;
|
|
|
|
|
|
|
|
memset(&state, 0, sizeof(state));
|
|
|
|
found_rebase = wt_status_check_rebase(wt, &state) &&
|
|
|
|
((state.rebase_in_progress ||
|
|
|
|
state.rebase_interactive_in_progress) &&
|
|
|
|
state.branch &&
|
|
|
|
starts_with(target, "refs/heads/") &&
|
|
|
|
!strcmp(state.branch, target + strlen("refs/heads/")));
|
|
|
|
free(state.branch);
|
|
|
|
free(state.onto);
|
|
|
|
return found_rebase;
|
|
|
|
}
|
|
|
|
|
2016-04-22 16:01:36 +03:00
|
|
|
int is_worktree_being_bisected(const struct worktree *wt,
|
|
|
|
const char *target)
|
2015-10-08 20:01:03 +03:00
|
|
|
{
|
2016-04-22 16:01:35 +03:00
|
|
|
struct wt_status_state state;
|
|
|
|
int found_rebase;
|
|
|
|
|
|
|
|
memset(&state, 0, sizeof(state));
|
|
|
|
found_rebase = wt_status_check_bisect(wt, &state) &&
|
|
|
|
state.branch &&
|
|
|
|
starts_with(target, "refs/heads/") &&
|
|
|
|
!strcmp(state.branch, target + strlen("refs/heads/"));
|
|
|
|
free(state.branch);
|
|
|
|
return found_rebase;
|
|
|
|
}
|
|
|
|
|
2016-04-22 16:01:33 +03: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 16:01:27 +03:00
|
|
|
const struct worktree *find_shared_symref(const char *symref,
|
|
|
|
const char *target)
|
2015-10-08 20:01:03 +03:00
|
|
|
{
|
2016-04-22 16:01:27 +03:00
|
|
|
const struct worktree *existing = NULL;
|
|
|
|
static struct worktree **worktrees;
|
2015-10-08 20:01:03 +03:00
|
|
|
int i = 0;
|
|
|
|
|
2016-04-22 16:01:27 +03:00
|
|
|
if (worktrees)
|
|
|
|
free_worktrees(worktrees);
|
2016-11-28 12:36:55 +03:00
|
|
|
worktrees = get_worktrees(0);
|
2016-04-22 16:01:27 +03:00
|
|
|
|
2015-10-08 20:01:03 +03:00
|
|
|
for (i = 0; worktrees[i]; i++) {
|
2016-04-22 16:01:32 +03:00
|
|
|
struct worktree *wt = worktrees[i];
|
2017-04-24 13:01:23 +03:00
|
|
|
const char *symref_target;
|
|
|
|
struct ref_store *refs;
|
|
|
|
int flags;
|
|
|
|
|
2016-10-12 19:41:07 +03:00
|
|
|
if (wt->is_bare)
|
|
|
|
continue;
|
2016-04-22 16:01:32 +03:00
|
|
|
|
2016-04-22 16:01:33 +03:00
|
|
|
if (wt->is_detached && !strcmp(symref, "HEAD")) {
|
|
|
|
if (is_worktree_being_rebased(wt, target)) {
|
|
|
|
existing = wt;
|
|
|
|
break;
|
|
|
|
}
|
2016-04-22 16:01:35 +03:00
|
|
|
if (is_worktree_being_bisected(wt, target)) {
|
|
|
|
existing = wt;
|
|
|
|
break;
|
|
|
|
}
|
2016-04-22 16:01:33 +03:00
|
|
|
}
|
|
|
|
|
2017-04-24 13:01:23 +03:00
|
|
|
refs = get_worktree_ref_store(wt);
|
|
|
|
symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
|
2017-09-23 12:44:57 +03:00
|
|
|
NULL, &flags);
|
2017-10-19 20:49:36 +03:00
|
|
|
if ((flags & REF_ISSYMREF) &&
|
|
|
|
symref_target && !strcmp(symref_target, target)) {
|
2016-04-22 16:01:32 +03:00
|
|
|
existing = wt;
|
2015-10-08 20:01:03 +03:00
|
|
|
break;
|
|
|
|
}
|
2015-10-02 14:55:31 +03:00
|
|
|
}
|
2015-10-08 20:01:03 +03:00
|
|
|
|
2015-10-02 14:55:31 +03:00
|
|
|
return existing;
|
|
|
|
}
|
2016-12-12 22:04:33 +03:00
|
|
|
|
|
|
|
int submodule_uses_worktrees(const char *path)
|
|
|
|
{
|
|
|
|
char *submodule_gitdir;
|
|
|
|
struct strbuf sb = STRBUF_INIT;
|
|
|
|
DIR *dir;
|
|
|
|
struct dirent *d;
|
2016-12-27 20:50:13 +03:00
|
|
|
int ret = 0;
|
2016-12-12 22:04:33 +03:00
|
|
|
struct repository_format format;
|
|
|
|
|
|
|
|
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 16:59:19 +03:00
|
|
|
free(submodule_gitdir);
|
2016-12-12 22:04:33 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The check below is only known to be good for repository format
|
|
|
|
* version 0 at the time of writing this code.
|
|
|
|
*/
|
|
|
|
strbuf_addstr(&sb, "/config");
|
|
|
|
read_repository_format(&format, sb.buf);
|
|
|
|
if (format.version != 0) {
|
|
|
|
strbuf_release(&sb);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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 15:36:59 +03:00
|
|
|
|
2018-10-21 11:08:54 +03: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 11:08:56 +03: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);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *worktree_ref(const struct worktree *wt, const char *refname)
|
|
|
|
{
|
|
|
|
static struct strbuf sb = STRBUF_INIT;
|
|
|
|
|
|
|
|
strbuf_reset(&sb);
|
|
|
|
strbuf_worktree_ref(wt, &sb, refname);
|
|
|
|
return sb.buf;
|
|
|
|
}
|
|
|
|
|
2017-08-23 15:36:59 +03:00
|
|
|
int other_head_refs(each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
struct worktree **worktrees, **p;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
worktrees = get_worktrees(0);
|
|
|
|
for (p = worktrees; *p; p++) {
|
|
|
|
struct worktree *wt = *p;
|
2018-10-21 11:08:56 +03:00
|
|
|
struct object_id oid;
|
|
|
|
int flag;
|
2017-08-23 15:36:59 +03:00
|
|
|
|
|
|
|
if (wt->is_current)
|
|
|
|
continue;
|
|
|
|
|
2018-10-21 11:08:56 +03:00
|
|
|
if (!refs_read_ref_full(get_main_ref_store(the_repository),
|
|
|
|
worktree_ref(wt, "HEAD"),
|
|
|
|
RESOLVE_REF_READING,
|
|
|
|
&oid, &flag))
|
|
|
|
ret = fn(worktree_ref(wt, "HEAD"), &oid, flag, cb_data);
|
2017-08-23 15:36:59 +03:00
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
free_worktrees(worktrees);
|
|
|
|
return ret;
|
|
|
|
}
|