diff --git a/Makefile b/Makefile index 8d5df7ea1e..f4ee2d223d 100644 --- a/Makefile +++ b/Makefile @@ -807,6 +807,7 @@ LIB_OBJS += version.o LIB_OBJS += versioncmp.o LIB_OBJS += walker.o LIB_OBJS += wildmatch.o +LIB_OBJS += worktree.o LIB_OBJS += wrapper.o LIB_OBJS += write_or_die.o LIB_OBJS += ws.o diff --git a/branch.c b/branch.c index d013374e5a..77d7f2a63e 100644 --- a/branch.c +++ b/branch.c @@ -4,6 +4,7 @@ #include "refs.h" #include "remote.h" #include "commit.h" +#include "worktree.h" struct tracking { struct refspec spec; @@ -311,84 +312,6 @@ void remove_branch_state(void) unlink(git_path_squash_msg()); } -static char *find_linked_symref(const char *symref, const char *branch, - const char *id) -{ - struct strbuf sb = STRBUF_INIT; - struct strbuf path = STRBUF_INIT; - struct strbuf gitdir = STRBUF_INIT; - char *existing = NULL; - - /* - * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside - * $GIT_DIR so resolve_ref_unsafe() won't work (it uses - * git_path). Parse the ref ourselves. - */ - if (id) - strbuf_addf(&path, "%s/worktrees/%s/%s", get_git_common_dir(), id, symref); - else - strbuf_addf(&path, "%s/%s", get_git_common_dir(), symref); - - if (!strbuf_readlink(&sb, path.buf, 0)) { - if (!starts_with(sb.buf, "refs/") || - check_refname_format(sb.buf, 0)) - goto done; - } else if (strbuf_read_file(&sb, path.buf, 0) >= 0 && - starts_with(sb.buf, "ref:")) { - strbuf_remove(&sb, 0, strlen("ref:")); - strbuf_trim(&sb); - } else - goto done; - if (strcmp(sb.buf, branch)) - goto done; - if (id) { - strbuf_reset(&path); - strbuf_addf(&path, "%s/worktrees/%s/gitdir", get_git_common_dir(), id); - if (strbuf_read_file(&gitdir, path.buf, 0) <= 0) - goto done; - strbuf_rtrim(&gitdir); - } else - strbuf_addstr(&gitdir, get_git_common_dir()); - strbuf_strip_suffix(&gitdir, ".git"); - - existing = strbuf_detach(&gitdir, NULL); -done: - strbuf_release(&path); - strbuf_release(&sb); - strbuf_release(&gitdir); - - return existing; -} - -char *find_shared_symref(const char *symref, const char *target) -{ - struct strbuf path = STRBUF_INIT; - DIR *dir; - struct dirent *d; - char *existing; - - if ((existing = find_linked_symref(symref, target, NULL))) - return existing; - - strbuf_addf(&path, "%s/worktrees", get_git_common_dir()); - dir = opendir(path.buf); - strbuf_release(&path); - if (!dir) - return NULL; - - while ((d = readdir(dir)) != NULL) { - if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) - continue; - existing = find_linked_symref(symref, target, d->d_name); - if (existing) - goto done; - } -done: - closedir(dir); - - return existing; -} - void die_if_checked_out(const char *branch) { char *existing; diff --git a/branch.h b/branch.h index d3446ed73c..58aa45fe72 100644 --- a/branch.h +++ b/branch.h @@ -59,12 +59,4 @@ extern int read_branch_desc(struct strbuf *, const char *branch_name); */ extern void die_if_checked_out(const char *branch); -/* - * Check if a per-worktree symref points to a ref in the main worktree - * or any linked worktree, and return the path to the exising worktree - * if it is. Returns NULL if there is no existing ref. The caller is - * responsible for freeing the returned path. - */ -extern char *find_shared_symref(const char *symref, const char *target); - #endif diff --git a/builtin/notes.c b/builtin/notes.c index 3608c64785..13c0af9155 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -19,7 +19,7 @@ #include "string-list.h" #include "notes-merge.h" #include "notes-utils.h" -#include "branch.h" +#include "worktree.h" static const char * const git_notes_usage[] = { N_("git notes [--ref ] [list []]"), diff --git a/worktree.c b/worktree.c new file mode 100644 index 0000000000..10e14960b2 --- /dev/null +++ b/worktree.c @@ -0,0 +1,82 @@ +#include "cache.h" +#include "refs.h" +#include "strbuf.h" +#include "worktree.h" + +static char *find_linked_symref(const char *symref, const char *branch, + const char *id) +{ + struct strbuf sb = STRBUF_INIT; + struct strbuf path = STRBUF_INIT; + struct strbuf gitdir = STRBUF_INIT; + char *existing = NULL; + + /* + * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside + * $GIT_DIR so resolve_ref_unsafe() won't work (it uses + * git_path). Parse the ref ourselves. + */ + if (id) + strbuf_addf(&path, "%s/worktrees/%s/%s", get_git_common_dir(), id, symref); + else + strbuf_addf(&path, "%s/%s", get_git_common_dir(), symref); + + if (!strbuf_readlink(&sb, path.buf, 0)) { + if (!starts_with(sb.buf, "refs/") || + check_refname_format(sb.buf, 0)) + goto done; + } else if (strbuf_read_file(&sb, path.buf, 0) >= 0 && + starts_with(sb.buf, "ref:")) { + strbuf_remove(&sb, 0, strlen("ref:")); + strbuf_trim(&sb); + } else + goto done; + if (strcmp(sb.buf, branch)) + goto done; + if (id) { + strbuf_reset(&path); + strbuf_addf(&path, "%s/worktrees/%s/gitdir", get_git_common_dir(), id); + if (strbuf_read_file(&gitdir, path.buf, 0) <= 0) + goto done; + strbuf_rtrim(&gitdir); + } else + strbuf_addstr(&gitdir, get_git_common_dir()); + strbuf_strip_suffix(&gitdir, ".git"); + + existing = strbuf_detach(&gitdir, NULL); +done: + strbuf_release(&path); + strbuf_release(&sb); + strbuf_release(&gitdir); + + return existing; +} + +char *find_shared_symref(const char *symref, const char *target) +{ + struct strbuf path = STRBUF_INIT; + DIR *dir; + struct dirent *d; + char *existing; + + if ((existing = find_linked_symref(symref, target, NULL))) + return existing; + + strbuf_addf(&path, "%s/worktrees", get_git_common_dir()); + dir = opendir(path.buf); + strbuf_release(&path); + if (!dir) + return NULL; + + while ((d = readdir(dir)) != NULL) { + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + existing = find_linked_symref(symref, target, d->d_name); + if (existing) + goto done; + } +done: + closedir(dir); + + return existing; +} diff --git a/worktree.h b/worktree.h new file mode 100644 index 0000000000..71b14092c4 --- /dev/null +++ b/worktree.h @@ -0,0 +1,12 @@ +#ifndef WORKTREE_H +#define WORKTREE_H + +/* + * Check if a per-worktree symref points to a ref in the main worktree + * or any linked worktree, and return the path to the exising worktree + * if it is. Returns NULL if there is no existing ref. The caller is + * responsible for freeing the returned path. + */ +extern char *find_shared_symref(const char *symref, const char *target); + +#endif