summaryrefslogtreecommitdiff
path: root/builtin/worktree.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/worktree.c')
-rw-r--r--builtin/worktree.c110
1 files changed, 74 insertions, 36 deletions
diff --git a/builtin/worktree.c b/builtin/worktree.c
index fc31d072a6..87ccd47794 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -1,4 +1,6 @@
#define USE_THE_REPOSITORY_VARIABLE
+#define DISABLE_SIGN_COMPARE_WARNINGS
+
#include "builtin.h"
#include "abspath.h"
#include "advice.h"
@@ -120,12 +122,14 @@ struct add_opts {
int quiet;
int checkout;
int orphan;
+ int relative_paths;
const char *keep_locked;
};
static int show_only;
static int verbose;
static int guess_remote;
+static int use_relative_paths;
static timestamp_t expire;
static int git_worktree_config(const char *var, const char *value,
@@ -134,6 +138,9 @@ static int git_worktree_config(const char *var, const char *value,
if (!strcmp(var, "worktree.guessremote")) {
guess_remote = git_config_bool(var, value);
return 0;
+ } else if (!strcmp(var, "worktree.userelativepaths")) {
+ use_relative_paths = git_config_bool(var, value);
+ return 0;
}
return git_default_config(var, value, ctx, cb);
@@ -144,7 +151,7 @@ static int delete_git_dir(const char *id)
struct strbuf sb = STRBUF_INIT;
int ret;
- strbuf_addstr(&sb, git_common_path("worktrees/%s", id));
+ repo_common_path_append(the_repository, &sb, "worktrees/%s", id);
ret = remove_dir_recursively(&sb, 0);
if (ret < 0 && errno == ENOTDIR)
ret = unlink(sb.buf);
@@ -156,7 +163,9 @@ static int delete_git_dir(const char *id)
static void delete_worktrees_dir_if_empty(void)
{
- rmdir(git_path("worktrees")); /* ignore failed removal */
+ char *path = repo_git_path(the_repository, "worktrees");
+ rmdir(path); /* ignore failed removal */
+ free(path);
}
static void prune_worktree(const char *id, const char *reason)
@@ -205,8 +214,13 @@ static void prune_worktrees(void)
struct strbuf reason = STRBUF_INIT;
struct strbuf main_path = STRBUF_INIT;
struct string_list kept = STRING_LIST_INIT_DUP;
- DIR *dir = opendir(git_path("worktrees"));
+ char *path;
+ DIR *dir;
struct dirent *d;
+
+ path = repo_git_path(the_repository, "worktrees");
+ dir = opendir(path);
+ free(path);
if (!dir)
return;
while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
@@ -231,7 +245,8 @@ static void prune_worktrees(void)
strbuf_release(&reason);
}
-static int prune(int ac, const char **av, const char *prefix)
+static int prune(int ac, const char **av, const char *prefix,
+ struct repository *repo UNUSED)
{
struct option options[] = {
OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
@@ -329,7 +344,7 @@ static void check_candidate_path(const char *path,
static void copy_sparse_checkout(const char *worktree_git_dir)
{
- char *from_file = git_pathdup("info/sparse-checkout");
+ char *from_file = repo_git_path(the_repository, "info/sparse-checkout");
char *to_file = xstrfmt("%s/info/sparse-checkout", worktree_git_dir);
if (file_exists(from_file)) {
@@ -345,7 +360,7 @@ static void copy_sparse_checkout(const char *worktree_git_dir)
static void copy_filtered_worktree_config(const char *worktree_git_dir)
{
- char *from_file = git_pathdup("config.worktree");
+ char *from_file = repo_git_path(the_repository, "config.worktree");
char *to_file = xstrfmt("%s/config.worktree", worktree_git_dir);
if (file_exists(from_file)) {
@@ -414,7 +429,7 @@ static int add_worktree(const char *path, const char *refname,
const struct add_opts *opts)
{
struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
- struct strbuf sb = STRBUF_INIT, realpath = STRBUF_INIT;
+ struct strbuf sb = STRBUF_INIT;
const char *name;
struct strvec child_env = STRVEC_INIT;
unsigned int counter = 0;
@@ -432,7 +447,7 @@ static int add_worktree(const char *path, const char *refname,
worktrees = NULL;
/* is 'refname' a branch or commit? */
- if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
+ if (!opts->detach && !check_branch_ref(&symref, refname) &&
refs_ref_exists(get_main_ref_store(the_repository), symref.buf)) {
is_branch = 1;
if (!opts->force)
@@ -449,7 +464,7 @@ static int add_worktree(const char *path, const char *refname,
BUG("How come '%s' becomes empty after sanitization?", sb.buf);
strbuf_reset(&sb);
name = sb_name.buf;
- git_path_buf(&sb_repo, "worktrees/%s", name);
+ repo_git_path_replace(the_repository, &sb_repo, "worktrees/%s", name);
len = sb_repo.len;
if (safe_create_leading_directories_const(sb_repo.buf))
die_errno(_("could not create leading directories of '%s'"),
@@ -490,11 +505,7 @@ static int add_worktree(const char *path, const char *refname,
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
- strbuf_realpath(&realpath, sb_git.buf, 1);
- write_file(sb.buf, "%s", realpath.buf);
- strbuf_realpath(&realpath, repo_get_common_dir(the_repository), 1);
- write_file(sb_git.buf, "gitdir: %s/worktrees/%s",
- realpath.buf, name);
+ write_worktree_linking_files(sb_git, sb, opts->relative_paths);
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
write_file(sb.buf, "../..");
@@ -567,7 +578,7 @@ done:
strvec_pushl(&opt.env, "GIT_DIR", "GIT_WORK_TREE", NULL);
strvec_pushl(&opt.args,
- oid_to_hex(null_oid()),
+ oid_to_hex(null_oid(the_hash_algo)),
oid_to_hex(&commit->object.oid),
"1",
NULL);
@@ -582,7 +593,6 @@ done:
strbuf_release(&sb_repo);
strbuf_release(&sb_git);
strbuf_release(&sb_name);
- strbuf_release(&realpath);
free_worktree(wt);
return ret;
}
@@ -604,7 +614,7 @@ static void print_preparing_worktree_line(int detach,
fprintf_ln(stderr, _("Preparing worktree (new branch '%s')"), new_branch);
} else {
struct strbuf s = STRBUF_INIT;
- if (!detach && !strbuf_check_branch_ref(&s, branch) &&
+ if (!detach && !check_branch_ref(&s, branch) &&
refs_ref_exists(get_main_ref_store(the_repository), s.buf))
fprintf_ln(stderr, _("Preparing worktree (checking out '%s')"),
branch);
@@ -654,8 +664,9 @@ static int can_use_local_refs(const struct add_opts *opts)
if (!opts->quiet) {
struct strbuf path = STRBUF_INIT;
struct strbuf contents = STRBUF_INIT;
+ char *wt_gitdir = get_worktree_git_dir(NULL);
- strbuf_add_real_path(&path, get_worktree_git_dir(NULL));
+ strbuf_add_real_path(&path, wt_gitdir);
strbuf_addstr(&path, "/HEAD");
strbuf_read_file(&contents, path.buf, 64);
strbuf_stripspace(&contents, NULL);
@@ -667,6 +678,7 @@ static int can_use_local_refs(const struct add_opts *opts)
path.buf, contents.buf);
strbuf_release(&path);
strbuf_release(&contents);
+ free(wt_gitdir);
}
return 1;
}
@@ -745,7 +757,7 @@ static char *dwim_branch(const char *path, char **new_branch)
char *branchname = xstrndup(s, n);
struct strbuf ref = STRBUF_INIT;
- branch_exists = !strbuf_check_branch_ref(&ref, branchname) &&
+ branch_exists = !check_branch_ref(&ref, branchname) &&
refs_ref_exists(get_main_ref_store(the_repository),
ref.buf);
strbuf_release(&ref);
@@ -761,7 +773,8 @@ static char *dwim_branch(const char *path, char **new_branch)
return NULL;
}
-static int add(int ac, const char **av, const char *prefix)
+static int add(int ac, const char **av, const char *prefix,
+ struct repository *repo UNUSED)
{
struct add_opts opts;
const char *new_branch_force = NULL;
@@ -794,12 +807,15 @@ static int add(int ac, const char **av, const char *prefix)
PARSE_OPT_NOARG | PARSE_OPT_OPTARG),
OPT_BOOL(0, "guess-remote", &guess_remote,
N_("try to match the new branch name with a remote-tracking branch")),
+ OPT_BOOL(0, "relative-paths", &opts.relative_paths,
+ N_("use relative paths for worktrees")),
OPT_END()
};
int ret;
memset(&opts, 0, sizeof(opts));
opts.checkout = 1;
+ opts.relative_paths = use_relative_paths;
ac = parse_options(ac, av, prefix, options, git_worktree_add_usage, 0);
if (!!opts.detach + !!new_branch + !!new_branch_force > 1)
die(_("options '%s', '%s', and '%s' cannot be used together"), "-b", "-B", "--detach");
@@ -838,7 +854,7 @@ static int add(int ac, const char **av, const char *prefix)
new_branch = new_branch_force;
if (!opts.force &&
- !strbuf_check_branch_ref(&symref, new_branch) &&
+ !check_branch_ref(&symref, new_branch) &&
refs_ref_exists(get_main_ref_store(the_repository), symref.buf))
die_if_checked_out(symref.buf, 0);
strbuf_release(&symref);
@@ -1037,7 +1053,8 @@ static void pathsort(struct worktree **wt)
QSORT(wt, n, pathcmp);
}
-static int list(int ac, const char **av, const char *prefix)
+static int list(int ac, const char **av, const char *prefix,
+ struct repository *repo UNUSED)
{
int porcelain = 0;
int line_terminator = '\n';
@@ -1082,7 +1099,8 @@ static int list(int ac, const char **av, const char *prefix)
return 0;
}
-static int lock_worktree(int ac, const char **av, const char *prefix)
+static int lock_worktree(int ac, const char **av, const char *prefix,
+ struct repository *repo UNUSED)
{
const char *reason = "", *old_reason;
struct option options[] = {
@@ -1091,6 +1109,7 @@ static int lock_worktree(int ac, const char **av, const char *prefix)
OPT_END()
};
struct worktree **worktrees, *wt;
+ char *path;
ac = parse_options(ac, av, prefix, options, git_worktree_lock_usage, 0);
if (ac != 1)
@@ -1111,18 +1130,22 @@ static int lock_worktree(int ac, const char **av, const char *prefix)
die(_("'%s' is already locked"), av[0]);
}
- write_file(git_common_path("worktrees/%s/locked", wt->id),
- "%s", reason);
+ path = repo_common_path(the_repository, "worktrees/%s/locked", wt->id);
+ write_file(path, "%s", reason);
+
free_worktrees(worktrees);
+ free(path);
return 0;
}
-static int unlock_worktree(int ac, const char **av, const char *prefix)
+static int unlock_worktree(int ac, const char **av, const char *prefix,
+ struct repository *repo UNUSED)
{
struct option options[] = {
OPT_END()
};
struct worktree **worktrees, *wt;
+ char *path;
int ret;
ac = parse_options(ac, av, prefix, options, git_worktree_unlock_usage, 0);
@@ -1137,8 +1160,12 @@ static int unlock_worktree(int ac, const char **av, const char *prefix)
die(_("The main working tree cannot be locked or unlocked"));
if (!worktree_lock_reason(wt))
die(_("'%s' is not locked"), av[0]);
- ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
+
+ path = repo_common_path(the_repository, "worktrees/%s/locked", wt->id);
+ ret = unlink_or_warn(path);
+
free_worktrees(worktrees);
+ free(path);
return ret;
}
@@ -1147,6 +1174,9 @@ static void validate_no_submodules(const struct worktree *wt)
struct index_state istate = INDEX_STATE_INIT(the_repository);
struct strbuf path = STRBUF_INIT;
int i, found_submodules = 0;
+ char *wt_gitdir;
+
+ wt_gitdir = get_worktree_git_dir(wt);
if (is_directory(worktree_git_path(the_repository, wt, "modules"))) {
/*
@@ -1156,7 +1186,7 @@ static void validate_no_submodules(const struct worktree *wt)
*/
found_submodules = 1;
} else if (read_index_from(&istate, worktree_git_path(the_repository, wt, "index"),
- get_worktree_git_dir(wt)) > 0) {
+ wt_gitdir) > 0) {
for (i = 0; i < istate.cache_nr; i++) {
struct cache_entry *ce = istate.cache[i];
int err;
@@ -1175,18 +1205,22 @@ static void validate_no_submodules(const struct worktree *wt)
}
discard_index(&istate);
strbuf_release(&path);
+ free(wt_gitdir);
if (found_submodules)
die(_("working trees containing submodules cannot be moved or removed"));
}
-static int move_worktree(int ac, const char **av, const char *prefix)
+static int move_worktree(int ac, const char **av, const char *prefix,
+ struct repository *repo UNUSED)
{
int force = 0;
struct option options[] = {
OPT__FORCE(&force,
N_("force move even if worktree is dirty or locked"),
PARSE_OPT_NOCOMPLETE),
+ OPT_BOOL(0, "relative-paths", &use_relative_paths,
+ N_("use relative paths for worktrees")),
OPT_END()
};
struct worktree **worktrees, *wt;
@@ -1239,7 +1273,7 @@ static int move_worktree(int ac, const char **av, const char *prefix)
if (rename(wt->path, dst.buf) == -1)
die_errno(_("failed to move '%s' to '%s'"), wt->path, dst.buf);
- update_worktree_location(wt, dst.buf);
+ update_worktree_location(wt, dst.buf, use_relative_paths);
strbuf_release(&dst);
free_worktrees(worktrees);
@@ -1310,7 +1344,8 @@ static int delete_git_work_tree(struct worktree *wt)
return ret;
}
-static int remove_worktree(int ac, const char **av, const char *prefix)
+static int remove_worktree(int ac, const char **av, const char *prefix,
+ struct repository *repo UNUSED)
{
int force = 0;
struct option options[] = {
@@ -1375,11 +1410,14 @@ static void report_repair(int iserr, const char *path, const char *msg, void *cb
}
}
-static int repair(int ac, const char **av, const char *prefix)
+static int repair(int ac, const char **av, const char *prefix,
+ struct repository *repo UNUSED)
{
const char **p;
const char *self[] = { ".", NULL };
struct option options[] = {
+ OPT_BOOL(0, "relative-paths", &use_relative_paths,
+ N_("use relative paths for worktrees")),
OPT_END()
};
int rc = 0;
@@ -1387,15 +1425,15 @@ static int repair(int ac, const char **av, const char *prefix)
ac = parse_options(ac, av, prefix, options, git_worktree_repair_usage, 0);
p = ac > 0 ? av : self;
for (; *p; p++)
- repair_worktree_at_path(*p, report_repair, &rc);
- repair_worktrees(report_repair, &rc);
+ repair_worktree_at_path(*p, report_repair, &rc, use_relative_paths);
+ repair_worktrees(report_repair, &rc, use_relative_paths);
return rc;
}
int cmd_worktree(int ac,
const char **av,
const char *prefix,
- struct repository *repo UNUSED)
+ struct repository *repo)
{
parse_opt_subcommand_fn *fn = NULL;
struct option options[] = {
@@ -1420,5 +1458,5 @@ int cmd_worktree(int ac,
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
- return fn(ac, av, prefix);
+ return fn(ac, av, prefix, repo);
}