1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-05-10 20:06:10 +02:00
git/builtin/worktree.c

1198 lines
32 KiB
C
Raw Normal View History

#include "cache.h"
#include "checkout.h"
#include "config.h"
#include "builtin.h"
#include "dir.h"
#include "hex.h"
#include "parse-options.h"
#include "strvec.h"
#include "branch.h"
#include "refs.h"
#include "run-command.h"
#include "hook.h"
#include "sigchain.h"
#include "submodule.h"
#include "utf8.h"
#include "worktree.h"
worktree: teach `list --porcelain` to annotate locked worktree Commit c57b3367be (worktree: teach `list` to annotate locked worktree, 2020-10-11) taught "git worktree list" to annotate locked worktrees by appending "locked" text to its output, however, this is not listed in the --porcelain format. Teach "list --porcelain" to do the same and add a "locked" attribute followed by its reason, thus making both default and porcelain format consistent. If the locked reason is not available then only "locked" is shown. The output of the "git worktree list --porcelain" becomes like so: $ git worktree list --porcelain ... worktree /path/to/locked HEAD 123abcdea123abcd123acbd123acbda123abcd12 detached locked worktree /path/to/locked-with-reason HEAD abc123abc123abc123abc123abc123abc123abc1 detached locked reason why it is locked ... In porcelain mode, if the lock reason contains special characters such as newlines, they are escaped with backslashes and the entire reason is enclosed in double quotes. For example: $ git worktree list --porcelain ... locked "worktree's path mounted in\nremovable device" ... Furthermore, let's update the documentation to state that some attributes in the porcelain format might be listed alone or together with its value depending whether the value is available or not. Thus documenting the case of the new "locked" attribute. Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk> Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Rafael Silva <rafaeloliveira.cs@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-01-27 09:03:08 +01:00
#include "quote.h"
#define BUILTIN_WORKTREE_ADD_USAGE \
N_("git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n" \
" [-b <new-branch>] <path> [<commit-ish>]")
#define BUILTIN_WORKTREE_LIST_USAGE \
N_("git worktree list [-v | --porcelain [-z]]")
#define BUILTIN_WORKTREE_LOCK_USAGE \
N_("git worktree lock [--reason <string>] <worktree>")
#define BUILTIN_WORKTREE_MOVE_USAGE \
N_("git worktree move <worktree> <new-path>")
#define BUILTIN_WORKTREE_PRUNE_USAGE \
N_("git worktree prune [-n] [-v] [--expire <expire>]")
#define BUILTIN_WORKTREE_REMOVE_USAGE \
N_("git worktree remove [-f] <worktree>")
#define BUILTIN_WORKTREE_REPAIR_USAGE \
N_("git worktree repair [<path>...]")
#define BUILTIN_WORKTREE_UNLOCK_USAGE \
N_("git worktree unlock <worktree>")
static const char * const git_worktree_usage[] = {
BUILTIN_WORKTREE_ADD_USAGE,
BUILTIN_WORKTREE_LIST_USAGE,
BUILTIN_WORKTREE_LOCK_USAGE,
BUILTIN_WORKTREE_MOVE_USAGE,
BUILTIN_WORKTREE_PRUNE_USAGE,
BUILTIN_WORKTREE_REMOVE_USAGE,
BUILTIN_WORKTREE_REPAIR_USAGE,
BUILTIN_WORKTREE_UNLOCK_USAGE,
NULL
};
static const char * const git_worktree_add_usage[] = {
BUILTIN_WORKTREE_ADD_USAGE,
NULL,
};
static const char * const git_worktree_list_usage[] = {
BUILTIN_WORKTREE_LIST_USAGE,
NULL
};
static const char * const git_worktree_lock_usage[] = {
BUILTIN_WORKTREE_LOCK_USAGE,
NULL
};
static const char * const git_worktree_move_usage[] = {
BUILTIN_WORKTREE_MOVE_USAGE,
NULL
};
static const char * const git_worktree_prune_usage[] = {
BUILTIN_WORKTREE_PRUNE_USAGE,
NULL
};
static const char * const git_worktree_remove_usage[] = {
BUILTIN_WORKTREE_REMOVE_USAGE,
NULL
};
static const char * const git_worktree_repair_usage[] = {
BUILTIN_WORKTREE_REPAIR_USAGE,
NULL
};
static const char * const git_worktree_unlock_usage[] = {
BUILTIN_WORKTREE_UNLOCK_USAGE,
NULL
};
struct add_opts {
int force;
int detach;
int quiet;
int checkout;
const char *keep_locked;
};
static int show_only;
static int verbose;
static int guess_remote;
static timestamp_t expire;
static int git_worktree_config(const char *var, const char *value, void *cb)
{
if (!strcmp(var, "worktree.guessremote")) {
guess_remote = git_config_bool(var, value);
return 0;
}
return git_default_config(var, value, cb);
}
static int delete_git_dir(const char *id)
{
struct strbuf sb = STRBUF_INIT;
int ret;
strbuf_addstr(&sb, git_common_path("worktrees/%s", id));
ret = remove_dir_recursively(&sb, 0);
if (ret < 0 && errno == ENOTDIR)
ret = unlink(sb.buf);
if (ret)
error_errno(_("failed to delete '%s'"), sb.buf);
strbuf_release(&sb);
return ret;
}
static void delete_worktrees_dir_if_empty(void)
{
rmdir(git_path("worktrees")); /* ignore failed removal */
}
static void prune_worktree(const char *id, const char *reason)
{
if (show_only || verbose)
worktree: send "chatty" messages to stderr The order in which the stdout and stderr streams are flushed is not guaranteed to be the same across platforms or `libc` implementations. This lack of determinism can lead to anomalous and potentially confusing output if normal (stdout) output is flushed after error (stderr) output. For instance, the following output which clearly indicates a failure due to a fatal error: % git worktree add ../foo bar Preparing worktree (checking out 'bar') fatal: 'bar' is already checked out at '.../wherever' has been reported[1] on Microsoft Windows to appear as: % git worktree add ../foo bar fatal: 'bar' is already checked out at '.../wherever' Preparing worktree (checking out 'bar') which may confuse the reader into thinking that the command somehow recovered and ran to completion despite the error. This problem crops up because the "chatty" status message "Preparing worktree" is sent to stdout, whereas the "fatal" error message is sent to stderr. One way to fix this would be to flush stdout manually before git-worktree reports any errors to stderr. However, common practice in Git is for "chatty" messages to be sent to stderr. Therefore, a more appropriate fix is to adjust git-worktree to conform to that practice by sending its "chatty" messages to stderr rather than stdout as is currently the case. There may be concern that relocating messages from stdout to stderr could break existing tooling, however, these messages are already internationalized, thus are unstable. And, indeed, the "Preparing worktree" message has already been the subject of somewhat significant changes in 2c27002a0a (worktree: improve message when creating a new worktree, 2018-04-24). Moreover, there is existing precedent, such as 68b939b2f0 (clone: send diagnostic messages to stderr, 2013-09-18) which likewise relocated "chatty" messages from stdout to stderr for git-clone. [1]: https://lore.kernel.org/git/CA+34VNLj6VB1kCkA=MfM7TZR+6HgqNi5-UaziAoCXacSVkch4A@mail.gmail.com/T/ Reported-by: Baruch Burstein <bmburstein@gmail.com> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-03 04:44:19 +01:00
fprintf_ln(stderr, _("Removing %s/%s: %s"), "worktrees", id, reason);
if (!show_only)
delete_git_dir(id);
}
static int prune_cmp(const void *a, const void *b)
{
const struct string_list_item *x = a;
const struct string_list_item *y = b;
int c;
if ((c = fspathcmp(x->string, y->string)))
return c;
/*
* paths same; prune_dupes() removes all but the first worktree entry
* having the same path, so sort main worktree ('util' is NULL) above
* linked worktrees ('util' not NULL) since main worktree can't be
* removed
*/
if (!x->util)
return -1;
if (!y->util)
return 1;
/* paths same; sort by .git/worktrees/<id> */
return strcmp(x->util, y->util);
}
static void prune_dups(struct string_list *l)
{
int i;
QSORT(l->items, l->nr, prune_cmp);
for (i = 1; i < l->nr; i++) {
if (!fspathcmp(l->items[i].string, l->items[i - 1].string))
prune_worktree(l->items[i].util, "duplicate entry");
}
}
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"));
struct dirent *d;
if (!dir)
return;
while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
char *path;
strbuf_reset(&reason);
if (should_prune_worktree(d->d_name, &reason, &path, expire))
prune_worktree(d->d_name, reason.buf);
else if (path)
string_list_append_nodup(&kept, path)->util = xstrdup(d->d_name);
}
closedir(dir);
strbuf_add_absolute_path(&main_path, get_git_common_dir());
/* massage main worktree absolute path to match 'gitdir' content */
strbuf_strip_suffix(&main_path, "/.");
string_list_append_nodup(&kept, strbuf_detach(&main_path, NULL));
prune_dups(&kept);
string_list_clear(&kept, 1);
if (!show_only)
delete_worktrees_dir_if_empty();
strbuf_release(&reason);
}
static int prune(int ac, const char **av, const char *prefix)
{
struct option options[] = {
OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
OPT__VERBOSE(&verbose, N_("report pruned working trees")),
OPT_EXPIRY_DATE(0, "expire", &expire,
N_("expire working trees older than <time>")),
OPT_END()
};
expire = TIME_MAX;
ac = parse_options(ac, av, prefix, options, git_worktree_prune_usage,
0);
if (ac)
usage_with_options(git_worktree_prune_usage, options);
prune_worktrees();
return 0;
}
static char *junk_work_tree;
static char *junk_git_dir;
static int is_junk;
static pid_t junk_pid;
static void remove_junk(void)
{
struct strbuf sb = STRBUF_INIT;
if (!is_junk || getpid() != junk_pid)
return;
if (junk_git_dir) {
strbuf_addstr(&sb, junk_git_dir);
remove_dir_recursively(&sb, 0);
strbuf_reset(&sb);
}
if (junk_work_tree) {
strbuf_addstr(&sb, junk_work_tree);
remove_dir_recursively(&sb, 0);
}
strbuf_release(&sb);
}
static void remove_junk_on_signal(int signo)
{
remove_junk();
sigchain_pop(signo);
raise(signo);
}
static const char *worktree_basename(const char *path, int *olen)
{
const char *name;
int len;
len = strlen(path);
while (len && is_dir_sep(path[len - 1]))
len--;
for (name = path + len - 1; name > path; name--)
if (is_dir_sep(*name)) {
name++;
break;
}
*olen = len;
return name;
}
/* check that path is viable location for worktree */
static void check_candidate_path(const char *path,
int force,
struct worktree **worktrees,
const char *cmd)
{
worktree: disallow adding same path multiple times A given path should only ever be associated with a single registered worktree. This invariant is enforced by refusing to create a new worktree at a given path if that path already exists. For example: $ git worktree add -q --detach foo $ git worktree add -q --detach foo fatal: 'foo' already exists However, the check can be fooled, and the invariant broken, if the path is missing. Continuing the example: $ rm -fr foo $ git worktree add -q --detach foo $ git worktree list ... eadebfe [master] .../foo eadebfe (detached HEAD) .../foo eadebfe (detached HEAD) This "corruption" leads to the unfortunate situation in which the worktree can not be removed: $ git worktree remove foo fatal: validation failed, cannot remove working tree: '.../foo' does not point back to '.git/worktrees/foo' Nor can the bogus entry be pruned: $ git worktree prune -v $ git worktree list ... eadebfe [master] .../foo eadebfe (detached HEAD) .../foo eadebfe (detached HEAD) without first deleting the worktree directory manually: $ rm -fr foo $ git worktree prune -v Removing .../foo: gitdir file points to non-existent location Removing .../foo1: gitdir file points to non-existent location $ git worktree list ... eadebfe [master] or by manually deleting the worktree entry in .git/worktrees. To address this problem, upgrade "git worktree add" validation to allow worktree creation only if the given path is not already associated with an existing worktree (even if the path itself is non-existent), thus preventing such bogus worktree entries from being created in the first place. Reported-by: Jeff King <peff@peff.net> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-28 23:20:22 +02:00
struct worktree *wt;
int locked;
if (file_exists(path) && !is_empty_dir(path))
die(_("'%s' already exists"), path);
worktree: disallow adding same path multiple times A given path should only ever be associated with a single registered worktree. This invariant is enforced by refusing to create a new worktree at a given path if that path already exists. For example: $ git worktree add -q --detach foo $ git worktree add -q --detach foo fatal: 'foo' already exists However, the check can be fooled, and the invariant broken, if the path is missing. Continuing the example: $ rm -fr foo $ git worktree add -q --detach foo $ git worktree list ... eadebfe [master] .../foo eadebfe (detached HEAD) .../foo eadebfe (detached HEAD) This "corruption" leads to the unfortunate situation in which the worktree can not be removed: $ git worktree remove foo fatal: validation failed, cannot remove working tree: '.../foo' does not point back to '.git/worktrees/foo' Nor can the bogus entry be pruned: $ git worktree prune -v $ git worktree list ... eadebfe [master] .../foo eadebfe (detached HEAD) .../foo eadebfe (detached HEAD) without first deleting the worktree directory manually: $ rm -fr foo $ git worktree prune -v Removing .../foo: gitdir file points to non-existent location Removing .../foo1: gitdir file points to non-existent location $ git worktree list ... eadebfe [master] or by manually deleting the worktree entry in .git/worktrees. To address this problem, upgrade "git worktree add" validation to allow worktree creation only if the given path is not already associated with an existing worktree (even if the path itself is non-existent), thus preventing such bogus worktree entries from being created in the first place. Reported-by: Jeff King <peff@peff.net> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-28 23:20:22 +02:00
worktree: don't allow "add" validation to be fooled by suffix matching "git worktree add <path>" performs various checks before approving <path> as a valid location for the new worktree. Aside from ensuring that <path> does not already exist, one of the questions it asks is whether <path> is already a registered worktree. To perform this check, it queries find_worktree() and disallows the "add" operation if find_worktree() finds a match for <path>. As a convenience, however, find_worktree() casts an overly wide net to allow users to identify worktrees by shorthand in order to keep typing to a minimum. For instance, it performs suffix matching which, given subtrees "foo/bar" and "foo/baz", can correctly select the latter when asked only for "baz". "add" validation knows the exact path it is interrogating, so this sort of heuristic-based matching is, at best, questionable for this use-case and, at worst, may may accidentally interpret <path> as matching an existing worktree and incorrectly report it as already registered even when it isn't. (In fact, validate_worktree_add() already contains a special case to avoid accidentally matching against the main worktree, precisely due to this problem.) Avoid the problem of potential accidental matching against an existing worktree by instead taking advantage of find_worktree_by_path() which matches paths deterministically, without applying any sort of magic shorthand matching performed by find_worktree(). Reported-by: Cameron Gunnin <cameron.gunnin@synopsys.com> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-24 10:08:48 +01:00
wt = find_worktree_by_path(worktrees, path);
worktree: disallow adding same path multiple times A given path should only ever be associated with a single registered worktree. This invariant is enforced by refusing to create a new worktree at a given path if that path already exists. For example: $ git worktree add -q --detach foo $ git worktree add -q --detach foo fatal: 'foo' already exists However, the check can be fooled, and the invariant broken, if the path is missing. Continuing the example: $ rm -fr foo $ git worktree add -q --detach foo $ git worktree list ... eadebfe [master] .../foo eadebfe (detached HEAD) .../foo eadebfe (detached HEAD) This "corruption" leads to the unfortunate situation in which the worktree can not be removed: $ git worktree remove foo fatal: validation failed, cannot remove working tree: '.../foo' does not point back to '.git/worktrees/foo' Nor can the bogus entry be pruned: $ git worktree prune -v $ git worktree list ... eadebfe [master] .../foo eadebfe (detached HEAD) .../foo eadebfe (detached HEAD) without first deleting the worktree directory manually: $ rm -fr foo $ git worktree prune -v Removing .../foo: gitdir file points to non-existent location Removing .../foo1: gitdir file points to non-existent location $ git worktree list ... eadebfe [master] or by manually deleting the worktree entry in .git/worktrees. To address this problem, upgrade "git worktree add" validation to allow worktree creation only if the given path is not already associated with an existing worktree (even if the path itself is non-existent), thus preventing such bogus worktree entries from being created in the first place. Reported-by: Jeff King <peff@peff.net> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-28 23:20:22 +02:00
if (!wt)
return;
worktree: disallow adding same path multiple times A given path should only ever be associated with a single registered worktree. This invariant is enforced by refusing to create a new worktree at a given path if that path already exists. For example: $ git worktree add -q --detach foo $ git worktree add -q --detach foo fatal: 'foo' already exists However, the check can be fooled, and the invariant broken, if the path is missing. Continuing the example: $ rm -fr foo $ git worktree add -q --detach foo $ git worktree list ... eadebfe [master] .../foo eadebfe (detached HEAD) .../foo eadebfe (detached HEAD) This "corruption" leads to the unfortunate situation in which the worktree can not be removed: $ git worktree remove foo fatal: validation failed, cannot remove working tree: '.../foo' does not point back to '.git/worktrees/foo' Nor can the bogus entry be pruned: $ git worktree prune -v $ git worktree list ... eadebfe [master] .../foo eadebfe (detached HEAD) .../foo eadebfe (detached HEAD) without first deleting the worktree directory manually: $ rm -fr foo $ git worktree prune -v Removing .../foo: gitdir file points to non-existent location Removing .../foo1: gitdir file points to non-existent location $ git worktree list ... eadebfe [master] or by manually deleting the worktree entry in .git/worktrees. To address this problem, upgrade "git worktree add" validation to allow worktree creation only if the given path is not already associated with an existing worktree (even if the path itself is non-existent), thus preventing such bogus worktree entries from being created in the first place. Reported-by: Jeff King <peff@peff.net> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-28 23:20:22 +02:00
locked = !!worktree_lock_reason(wt);
if ((!locked && force) || (locked && force > 1)) {
if (delete_git_dir(wt->id))
die(_("unusable worktree destination '%s'"), path);
return;
}
worktree: disallow adding same path multiple times A given path should only ever be associated with a single registered worktree. This invariant is enforced by refusing to create a new worktree at a given path if that path already exists. For example: $ git worktree add -q --detach foo $ git worktree add -q --detach foo fatal: 'foo' already exists However, the check can be fooled, and the invariant broken, if the path is missing. Continuing the example: $ rm -fr foo $ git worktree add -q --detach foo $ git worktree list ... eadebfe [master] .../foo eadebfe (detached HEAD) .../foo eadebfe (detached HEAD) This "corruption" leads to the unfortunate situation in which the worktree can not be removed: $ git worktree remove foo fatal: validation failed, cannot remove working tree: '.../foo' does not point back to '.git/worktrees/foo' Nor can the bogus entry be pruned: $ git worktree prune -v $ git worktree list ... eadebfe [master] .../foo eadebfe (detached HEAD) .../foo eadebfe (detached HEAD) without first deleting the worktree directory manually: $ rm -fr foo $ git worktree prune -v Removing .../foo: gitdir file points to non-existent location Removing .../foo1: gitdir file points to non-existent location $ git worktree list ... eadebfe [master] or by manually deleting the worktree entry in .git/worktrees. To address this problem, upgrade "git worktree add" validation to allow worktree creation only if the given path is not already associated with an existing worktree (even if the path itself is non-existent), thus preventing such bogus worktree entries from being created in the first place. Reported-by: Jeff King <peff@peff.net> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-28 23:20:22 +02:00
if (locked)
die(_("'%s' is a missing but locked worktree;\nuse '%s -f -f' to override, or 'unlock' and 'prune' or 'remove' to clear"), path, cmd);
worktree: disallow adding same path multiple times A given path should only ever be associated with a single registered worktree. This invariant is enforced by refusing to create a new worktree at a given path if that path already exists. For example: $ git worktree add -q --detach foo $ git worktree add -q --detach foo fatal: 'foo' already exists However, the check can be fooled, and the invariant broken, if the path is missing. Continuing the example: $ rm -fr foo $ git worktree add -q --detach foo $ git worktree list ... eadebfe [master] .../foo eadebfe (detached HEAD) .../foo eadebfe (detached HEAD) This "corruption" leads to the unfortunate situation in which the worktree can not be removed: $ git worktree remove foo fatal: validation failed, cannot remove working tree: '.../foo' does not point back to '.git/worktrees/foo' Nor can the bogus entry be pruned: $ git worktree prune -v $ git worktree list ... eadebfe [master] .../foo eadebfe (detached HEAD) .../foo eadebfe (detached HEAD) without first deleting the worktree directory manually: $ rm -fr foo $ git worktree prune -v Removing .../foo: gitdir file points to non-existent location Removing .../foo1: gitdir file points to non-existent location $ git worktree list ... eadebfe [master] or by manually deleting the worktree entry in .git/worktrees. To address this problem, upgrade "git worktree add" validation to allow worktree creation only if the given path is not already associated with an existing worktree (even if the path itself is non-existent), thus preventing such bogus worktree entries from being created in the first place. Reported-by: Jeff King <peff@peff.net> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-28 23:20:22 +02:00
else
die(_("'%s' is a missing but already registered worktree;\nuse '%s -f' to override, or 'prune' or 'remove' to clear"), path, cmd);
}
static void copy_sparse_checkout(const char *worktree_git_dir)
{
char *from_file = git_pathdup("info/sparse-checkout");
char *to_file = xstrfmt("%s/info/sparse-checkout", worktree_git_dir);
if (file_exists(from_file)) {
if (safe_create_leading_directories(to_file) ||
copy_file(to_file, from_file, 0666))
error(_("failed to copy '%s' to '%s'; sparse-checkout may not work correctly"),
from_file, to_file);
}
free(from_file);
free(to_file);
}
static void copy_filtered_worktree_config(const char *worktree_git_dir)
{
char *from_file = git_pathdup("config.worktree");
char *to_file = xstrfmt("%s/config.worktree", worktree_git_dir);
if (file_exists(from_file)) {
struct config_set cs = { { 0 } };
const char *core_worktree;
int bare;
if (safe_create_leading_directories(to_file) ||
copy_file(to_file, from_file, 0666)) {
error(_("failed to copy worktree config from '%s' to '%s'"),
from_file, to_file);
goto worktree_copy_cleanup;
}
git_configset_init(&cs);
git_configset_add_file(&cs, from_file);
if (!git_configset_get_bool(&cs, "core.bare", &bare) &&
bare &&
git_config_set_multivar_in_file_gently(
to_file, "core.bare", NULL, "true", 0))
error(_("failed to unset '%s' in '%s'"),
"core.bare", to_file);
if (!git_configset_get_value(&cs, "core.worktree", &core_worktree) &&
git_config_set_in_file_gently(to_file,
"core.worktree", NULL))
error(_("failed to unset '%s' in '%s'"),
"core.worktree", to_file);
git_configset_clear(&cs);
}
worktree_copy_cleanup:
free(from_file);
free(to_file);
}
static int checkout_worktree(const struct add_opts *opts,
struct strvec *child_env)
{
struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
strvec_pushl(&cp.args, "reset", "--hard", "--no-recurse-submodules", NULL);
if (opts->quiet)
strvec_push(&cp.args, "--quiet");
strvec_pushv(&cp.env, child_env->v);
return run_command(&cp);
}
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;
const char *name;
struct child_process cp = CHILD_PROCESS_INIT;
struct strvec child_env = STRVEC_INIT;
unsigned int counter = 0;
int len, ret;
struct strbuf symref = STRBUF_INIT;
struct commit *commit = NULL;
int is_branch = 0;
struct strbuf sb_name = STRBUF_INIT;
struct worktree **worktrees;
worktrees = get_worktrees();
check_candidate_path(path, opts->force, worktrees, "add");
free_worktrees(worktrees);
worktrees = NULL;
/* is 'refname' a branch or commit? */
if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
ref_exists(symref.buf)) {
is_branch = 1;
if (!opts->force)
2016-04-22 15:01:33 +02:00
die_if_checked_out(symref.buf, 0);
}
commit = lookup_commit_reference_by_name(refname);
if (!commit)
die(_("invalid reference: %s"), refname);
name = worktree_basename(path, &len);
strbuf_add(&sb, name, path + len - name);
sanitize_refname_component(sb.buf, &sb_name);
if (!sb_name.len)
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);
len = sb_repo.len;
if (safe_create_leading_directories_const(sb_repo.buf))
die_errno(_("could not create leading directories of '%s'"),
sb_repo.buf);
while (mkdir(sb_repo.buf, 0777)) {
counter++;
if ((errno != EEXIST) || !counter /* overflow */)
die_errno(_("could not create directory of '%s'"),
sb_repo.buf);
strbuf_setlen(&sb_repo, len);
strbuf_addf(&sb_repo, "%d", counter);
}
name = strrchr(sb_repo.buf, '/') + 1;
junk_pid = getpid();
atexit(remove_junk);
sigchain_push_common(remove_junk_on_signal);
junk_git_dir = xstrdup(sb_repo.buf);
is_junk = 1;
/*
* lock the incomplete repo so prune won't delete it, unlock
* after the preparation is over.
*/
strbuf_addf(&sb, "%s/locked", sb_repo.buf);
if (opts->keep_locked)
write_file(sb.buf, "%s", opts->keep_locked);
else
write_file(sb.buf, _("initializing"));
strbuf_addf(&sb_git, "%s/.git", path);
if (safe_create_leading_directories_const(sb_git.buf))
die_errno(_("could not create leading directories of '%s'"),
sb_git.buf);
junk_work_tree = xstrdup(path);
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, get_git_common_dir(), 1);
write_file(sb_git.buf, "gitdir: %s/worktrees/%s",
realpath.buf, name);
/*
* This is to keep resolve_ref() happy. We need a valid HEAD
* or is_git_directory() will reject the directory. Any value which
* looks like an object ID will do since it will be immediately
* replaced by the symbolic-ref or update-ref invocation in the new
* worktree.
*/
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
write_file(sb.buf, "%s", oid_to_hex(null_oid()));
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
write_file(sb.buf, "../..");
2022-02-07 22:33:02 +01:00
/*
* If the current worktree has sparse-checkout enabled, then copy
* the sparse-checkout patterns from the current worktree.
*/
if (core_apply_sparse_checkout)
copy_sparse_checkout(sb_repo.buf);
2022-02-07 22:33:02 +01:00
/*
* If we are using worktree config, then copy all current config
* values from the current worktree into the new one, that way the
* new worktree behaves the same as this one.
*/
if (repository_format_worktree_config)
copy_filtered_worktree_config(sb_repo.buf);
2022-02-07 22:33:02 +01:00
strvec_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
strvec_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
cp.git_cmd = 1;
if (!is_branch)
strvec_pushl(&cp.args, "update-ref", "HEAD",
oid_to_hex(&commit->object.oid), NULL);
else {
strvec_pushl(&cp.args, "symbolic-ref", "HEAD",
symref.buf, NULL);
if (opts->quiet)
strvec_push(&cp.args, "--quiet");
}
strvec_pushv(&cp.env, child_env.v);
ret = run_command(&cp);
if (ret)
goto done;
if (opts->checkout &&
(ret = checkout_worktree(opts, &child_env)))
goto done;
is_junk = 0;
FREE_AND_NULL(junk_work_tree);
FREE_AND_NULL(junk_git_dir);
done:
if (ret || !opts->keep_locked) {
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/locked", sb_repo.buf);
unlink_or_warn(sb.buf);
}
/*
* Hook failure does not warrant worktree deletion, so run hook after
* is_junk is cleared, but do return appropriate code when hook fails.
*/
worktree: add: fix 'post-checkout' not knowing new worktree location Although "git worktree add" learned to run the 'post-checkout' hook in ade546be47 (worktree: invoke post-checkout hook, 2017-12-07), it neglected to change to the directory of the newly-created worktree before running the hook. Instead, the hook runs within the directory from which the "git worktree add" command itself was invoked, which effectively neuters the hook since it knows nothing about the new worktree directory. Further, ade546be47 failed to sanitize the environment before running the hook, which means that user-assigned values of GIT_DIR and GIT_WORK_TREE could mislead the hook about the location of the new worktree. In the case of "git worktree add" being run from a bare repository, the GIT_DIR="." assigned by Git itself leaks into the hook's environment and breaks Git commands; this is so even when the working directory is correctly changed to the new worktree before the hook runs since ".", relative to the new worktree directory, does not point at the bare repository. Fix these problems by (1) changing to the new worktree's directory before running the hook, and (2) sanitizing the environment of GIT_DIR and GIT_WORK_TREE so hooks can't be confused by misleading values. Enhance the t2025 'post-checkout' tests to verify that the hook is indeed run within the correct directory and that Git commands invoked by the hook compute Git-dir and top-level worktree locations correctly. While at it, also add two new tests: (1) verify that the hook is run within the correct directory even when the new worktree is created from a sibling worktree (as opposed to the main worktree); (2) verify that the hook is provided with correct context when the new worktree is created from a bare repository (test provided by Lars Schneider). Implementation Notes: Rather than sanitizing the environment of GIT_DIR and GIT_WORK_TREE, an alternative would be to set them explicitly, as is already done for other Git commands run internally by "git worktree add". This patch opts instead to sanitize the environment in order to clearly document that the worktree is fully functional by the time the hook is run, thus does not require special environmental overrides. The hook is run manually, rather than via run_hook_le(), since it needs to change the working directory to that of the worktree, and run_hook_le() does not provide such functionality. As this is a one-off case, adding 'run_hook' overloads which allow the directory to be set does not seem warranted at this time. Reported-by: Lars Schneider <larsxschneider@gmail.com> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-02-15 20:18:41 +01:00
if (!ret && opts->checkout) {
struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
strvec_pushl(&opt.env, "GIT_DIR", "GIT_WORK_TREE", NULL);
strvec_pushl(&opt.args,
oid_to_hex(null_oid()),
oid_to_hex(&commit->object.oid),
"1",
NULL);
opt.dir = path;
ret = run_hooks_opt("post-checkout", &opt);
worktree: add: fix 'post-checkout' not knowing new worktree location Although "git worktree add" learned to run the 'post-checkout' hook in ade546be47 (worktree: invoke post-checkout hook, 2017-12-07), it neglected to change to the directory of the newly-created worktree before running the hook. Instead, the hook runs within the directory from which the "git worktree add" command itself was invoked, which effectively neuters the hook since it knows nothing about the new worktree directory. Further, ade546be47 failed to sanitize the environment before running the hook, which means that user-assigned values of GIT_DIR and GIT_WORK_TREE could mislead the hook about the location of the new worktree. In the case of "git worktree add" being run from a bare repository, the GIT_DIR="." assigned by Git itself leaks into the hook's environment and breaks Git commands; this is so even when the working directory is correctly changed to the new worktree before the hook runs since ".", relative to the new worktree directory, does not point at the bare repository. Fix these problems by (1) changing to the new worktree's directory before running the hook, and (2) sanitizing the environment of GIT_DIR and GIT_WORK_TREE so hooks can't be confused by misleading values. Enhance the t2025 'post-checkout' tests to verify that the hook is indeed run within the correct directory and that Git commands invoked by the hook compute Git-dir and top-level worktree locations correctly. While at it, also add two new tests: (1) verify that the hook is run within the correct directory even when the new worktree is created from a sibling worktree (as opposed to the main worktree); (2) verify that the hook is provided with correct context when the new worktree is created from a bare repository (test provided by Lars Schneider). Implementation Notes: Rather than sanitizing the environment of GIT_DIR and GIT_WORK_TREE, an alternative would be to set them explicitly, as is already done for other Git commands run internally by "git worktree add". This patch opts instead to sanitize the environment in order to clearly document that the worktree is fully functional by the time the hook is run, thus does not require special environmental overrides. The hook is run manually, rather than via run_hook_le(), since it needs to change the working directory to that of the worktree, and run_hook_le() does not provide such functionality. As this is a one-off case, adding 'run_hook' overloads which allow the directory to be set does not seem warranted at this time. Reported-by: Lars Schneider <larsxschneider@gmail.com> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-02-15 20:18:41 +01:00
}
strvec_clear(&child_env);
strbuf_release(&sb);
strbuf_release(&symref);
strbuf_release(&sb_repo);
strbuf_release(&sb_git);
strbuf_release(&sb_name);
strbuf_release(&realpath);
return ret;
}
worktree: improve message when creating a new worktree Currently 'git worktree add' produces output like the following: Preparing ../foo (identifier foo) HEAD is now at 26da330922 <title> The '../foo' is the path where the worktree is created, which the user has just given on the command line. The identifier is an internal implementation detail, which is not particularly relevant for the user and indeed isn't mentioned explicitly anywhere in the man page. Instead of this message, print a message that gives the user a bit more detail of what exactly 'git worktree' is doing. There are various dwim modes which perform some magic under the hood, which should be helpful to users. Just from the output of the command it is not always visible to users what exactly has happened. Help the users a bit more by modifying the "Preparing ..." message and adding some additional information of what 'git worktree add' did under the hood, while not displaying the identifier anymore. Currently there are several different cases: - 'git worktree add -b ...' or 'git worktree add <path>', both of which create a new branch, either through the user explicitly requesting it, or through 'git worktree add' implicitly creating it. This will end up with the following output: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title> - 'git worktree add -B ...', which may either create a new branch if the branch with the given name does not exist yet, or resets an existing branch to the current HEAD, or the commit-ish given. Depending on which action is taken, we'll end up with the following output: Preparing worktree (resetting branch '<branch>'; was at caa68db14) HEAD is now at 26da330922 <title> or: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title> - 'git worktree add --detach' or 'git worktree add <path> <commit-ish>', both of which create a new worktree with a detached HEAD, for which we will print the following output: Preparing worktree (detached HEAD 26da330922) HEAD is now at 26da330922 <title> - 'git worktree add <path> <local-branch>', which checks out the branch and prints the following output: Preparing worktree (checking out '<local-branch>') HEAD is now at 47007d5 <title> Additionally currently the "Preparing ..." line is printed to stderr, while the "HEAD is now at ..." line is printed to stdout by 'git reset --hard', which is used internally by 'git worktree add'. Fix this inconsistency by printing the "Preparing ..." message to stdout as well. As "Preparing ..." is not an error, stdout also seems like the more appropriate output stream. Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-04-24 23:56:33 +02:00
static void print_preparing_worktree_line(int detach,
const char *branch,
const char *new_branch,
int force_new_branch)
{
if (force_new_branch) {
struct commit *commit = lookup_commit_reference_by_name(new_branch);
if (!commit)
worktree: send "chatty" messages to stderr The order in which the stdout and stderr streams are flushed is not guaranteed to be the same across platforms or `libc` implementations. This lack of determinism can lead to anomalous and potentially confusing output if normal (stdout) output is flushed after error (stderr) output. For instance, the following output which clearly indicates a failure due to a fatal error: % git worktree add ../foo bar Preparing worktree (checking out 'bar') fatal: 'bar' is already checked out at '.../wherever' has been reported[1] on Microsoft Windows to appear as: % git worktree add ../foo bar fatal: 'bar' is already checked out at '.../wherever' Preparing worktree (checking out 'bar') which may confuse the reader into thinking that the command somehow recovered and ran to completion despite the error. This problem crops up because the "chatty" status message "Preparing worktree" is sent to stdout, whereas the "fatal" error message is sent to stderr. One way to fix this would be to flush stdout manually before git-worktree reports any errors to stderr. However, common practice in Git is for "chatty" messages to be sent to stderr. Therefore, a more appropriate fix is to adjust git-worktree to conform to that practice by sending its "chatty" messages to stderr rather than stdout as is currently the case. There may be concern that relocating messages from stdout to stderr could break existing tooling, however, these messages are already internationalized, thus are unstable. And, indeed, the "Preparing worktree" message has already been the subject of somewhat significant changes in 2c27002a0a (worktree: improve message when creating a new worktree, 2018-04-24). Moreover, there is existing precedent, such as 68b939b2f0 (clone: send diagnostic messages to stderr, 2013-09-18) which likewise relocated "chatty" messages from stdout to stderr for git-clone. [1]: https://lore.kernel.org/git/CA+34VNLj6VB1kCkA=MfM7TZR+6HgqNi5-UaziAoCXacSVkch4A@mail.gmail.com/T/ Reported-by: Baruch Burstein <bmburstein@gmail.com> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-03 04:44:19 +01:00
fprintf_ln(stderr, _("Preparing worktree (new branch '%s')"), new_branch);
worktree: improve message when creating a new worktree Currently 'git worktree add' produces output like the following: Preparing ../foo (identifier foo) HEAD is now at 26da330922 <title> The '../foo' is the path where the worktree is created, which the user has just given on the command line. The identifier is an internal implementation detail, which is not particularly relevant for the user and indeed isn't mentioned explicitly anywhere in the man page. Instead of this message, print a message that gives the user a bit more detail of what exactly 'git worktree' is doing. There are various dwim modes which perform some magic under the hood, which should be helpful to users. Just from the output of the command it is not always visible to users what exactly has happened. Help the users a bit more by modifying the "Preparing ..." message and adding some additional information of what 'git worktree add' did under the hood, while not displaying the identifier anymore. Currently there are several different cases: - 'git worktree add -b ...' or 'git worktree add <path>', both of which create a new branch, either through the user explicitly requesting it, or through 'git worktree add' implicitly creating it. This will end up with the following output: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title> - 'git worktree add -B ...', which may either create a new branch if the branch with the given name does not exist yet, or resets an existing branch to the current HEAD, or the commit-ish given. Depending on which action is taken, we'll end up with the following output: Preparing worktree (resetting branch '<branch>'; was at caa68db14) HEAD is now at 26da330922 <title> or: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title> - 'git worktree add --detach' or 'git worktree add <path> <commit-ish>', both of which create a new worktree with a detached HEAD, for which we will print the following output: Preparing worktree (detached HEAD 26da330922) HEAD is now at 26da330922 <title> - 'git worktree add <path> <local-branch>', which checks out the branch and prints the following output: Preparing worktree (checking out '<local-branch>') HEAD is now at 47007d5 <title> Additionally currently the "Preparing ..." line is printed to stderr, while the "HEAD is now at ..." line is printed to stdout by 'git reset --hard', which is used internally by 'git worktree add'. Fix this inconsistency by printing the "Preparing ..." message to stdout as well. As "Preparing ..." is not an error, stdout also seems like the more appropriate output stream. Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-04-24 23:56:33 +02:00
else
worktree: send "chatty" messages to stderr The order in which the stdout and stderr streams are flushed is not guaranteed to be the same across platforms or `libc` implementations. This lack of determinism can lead to anomalous and potentially confusing output if normal (stdout) output is flushed after error (stderr) output. For instance, the following output which clearly indicates a failure due to a fatal error: % git worktree add ../foo bar Preparing worktree (checking out 'bar') fatal: 'bar' is already checked out at '.../wherever' has been reported[1] on Microsoft Windows to appear as: % git worktree add ../foo bar fatal: 'bar' is already checked out at '.../wherever' Preparing worktree (checking out 'bar') which may confuse the reader into thinking that the command somehow recovered and ran to completion despite the error. This problem crops up because the "chatty" status message "Preparing worktree" is sent to stdout, whereas the "fatal" error message is sent to stderr. One way to fix this would be to flush stdout manually before git-worktree reports any errors to stderr. However, common practice in Git is for "chatty" messages to be sent to stderr. Therefore, a more appropriate fix is to adjust git-worktree to conform to that practice by sending its "chatty" messages to stderr rather than stdout as is currently the case. There may be concern that relocating messages from stdout to stderr could break existing tooling, however, these messages are already internationalized, thus are unstable. And, indeed, the "Preparing worktree" message has already been the subject of somewhat significant changes in 2c27002a0a (worktree: improve message when creating a new worktree, 2018-04-24). Moreover, there is existing precedent, such as 68b939b2f0 (clone: send diagnostic messages to stderr, 2013-09-18) which likewise relocated "chatty" messages from stdout to stderr for git-clone. [1]: https://lore.kernel.org/git/CA+34VNLj6VB1kCkA=MfM7TZR+6HgqNi5-UaziAoCXacSVkch4A@mail.gmail.com/T/ Reported-by: Baruch Burstein <bmburstein@gmail.com> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-03 04:44:19 +01:00
fprintf_ln(stderr, _("Preparing worktree (resetting branch '%s'; was at %s)"),
worktree: improve message when creating a new worktree Currently 'git worktree add' produces output like the following: Preparing ../foo (identifier foo) HEAD is now at 26da330922 <title> The '../foo' is the path where the worktree is created, which the user has just given on the command line. The identifier is an internal implementation detail, which is not particularly relevant for the user and indeed isn't mentioned explicitly anywhere in the man page. Instead of this message, print a message that gives the user a bit more detail of what exactly 'git worktree' is doing. There are various dwim modes which perform some magic under the hood, which should be helpful to users. Just from the output of the command it is not always visible to users what exactly has happened. Help the users a bit more by modifying the "Preparing ..." message and adding some additional information of what 'git worktree add' did under the hood, while not displaying the identifier anymore. Currently there are several different cases: - 'git worktree add -b ...' or 'git worktree add <path>', both of which create a new branch, either through the user explicitly requesting it, or through 'git worktree add' implicitly creating it. This will end up with the following output: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title> - 'git worktree add -B ...', which may either create a new branch if the branch with the given name does not exist yet, or resets an existing branch to the current HEAD, or the commit-ish given. Depending on which action is taken, we'll end up with the following output: Preparing worktree (resetting branch '<branch>'; was at caa68db14) HEAD is now at 26da330922 <title> or: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title> - 'git worktree add --detach' or 'git worktree add <path> <commit-ish>', both of which create a new worktree with a detached HEAD, for which we will print the following output: Preparing worktree (detached HEAD 26da330922) HEAD is now at 26da330922 <title> - 'git worktree add <path> <local-branch>', which checks out the branch and prints the following output: Preparing worktree (checking out '<local-branch>') HEAD is now at 47007d5 <title> Additionally currently the "Preparing ..." line is printed to stderr, while the "HEAD is now at ..." line is printed to stdout by 'git reset --hard', which is used internally by 'git worktree add'. Fix this inconsistency by printing the "Preparing ..." message to stdout as well. As "Preparing ..." is not an error, stdout also seems like the more appropriate output stream. Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-04-24 23:56:33 +02:00
new_branch,
find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
worktree: improve message when creating a new worktree Currently 'git worktree add' produces output like the following: Preparing ../foo (identifier foo) HEAD is now at 26da330922 <title> The '../foo' is the path where the worktree is created, which the user has just given on the command line. The identifier is an internal implementation detail, which is not particularly relevant for the user and indeed isn't mentioned explicitly anywhere in the man page. Instead of this message, print a message that gives the user a bit more detail of what exactly 'git worktree' is doing. There are various dwim modes which perform some magic under the hood, which should be helpful to users. Just from the output of the command it is not always visible to users what exactly has happened. Help the users a bit more by modifying the "Preparing ..." message and adding some additional information of what 'git worktree add' did under the hood, while not displaying the identifier anymore. Currently there are several different cases: - 'git worktree add -b ...' or 'git worktree add <path>', both of which create a new branch, either through the user explicitly requesting it, or through 'git worktree add' implicitly creating it. This will end up with the following output: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title> - 'git worktree add -B ...', which may either create a new branch if the branch with the given name does not exist yet, or resets an existing branch to the current HEAD, or the commit-ish given. Depending on which action is taken, we'll end up with the following output: Preparing worktree (resetting branch '<branch>'; was at caa68db14) HEAD is now at 26da330922 <title> or: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title> - 'git worktree add --detach' or 'git worktree add <path> <commit-ish>', both of which create a new worktree with a detached HEAD, for which we will print the following output: Preparing worktree (detached HEAD 26da330922) HEAD is now at 26da330922 <title> - 'git worktree add <path> <local-branch>', which checks out the branch and prints the following output: Preparing worktree (checking out '<local-branch>') HEAD is now at 47007d5 <title> Additionally currently the "Preparing ..." line is printed to stderr, while the "HEAD is now at ..." line is printed to stdout by 'git reset --hard', which is used internally by 'git worktree add'. Fix this inconsistency by printing the "Preparing ..." message to stdout as well. As "Preparing ..." is not an error, stdout also seems like the more appropriate output stream. Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-04-24 23:56:33 +02:00
} else if (new_branch) {
worktree: send "chatty" messages to stderr The order in which the stdout and stderr streams are flushed is not guaranteed to be the same across platforms or `libc` implementations. This lack of determinism can lead to anomalous and potentially confusing output if normal (stdout) output is flushed after error (stderr) output. For instance, the following output which clearly indicates a failure due to a fatal error: % git worktree add ../foo bar Preparing worktree (checking out 'bar') fatal: 'bar' is already checked out at '.../wherever' has been reported[1] on Microsoft Windows to appear as: % git worktree add ../foo bar fatal: 'bar' is already checked out at '.../wherever' Preparing worktree (checking out 'bar') which may confuse the reader into thinking that the command somehow recovered and ran to completion despite the error. This problem crops up because the "chatty" status message "Preparing worktree" is sent to stdout, whereas the "fatal" error message is sent to stderr. One way to fix this would be to flush stdout manually before git-worktree reports any errors to stderr. However, common practice in Git is for "chatty" messages to be sent to stderr. Therefore, a more appropriate fix is to adjust git-worktree to conform to that practice by sending its "chatty" messages to stderr rather than stdout as is currently the case. There may be concern that relocating messages from stdout to stderr could break existing tooling, however, these messages are already internationalized, thus are unstable. And, indeed, the "Preparing worktree" message has already been the subject of somewhat significant changes in 2c27002a0a (worktree: improve message when creating a new worktree, 2018-04-24). Moreover, there is existing precedent, such as 68b939b2f0 (clone: send diagnostic messages to stderr, 2013-09-18) which likewise relocated "chatty" messages from stdout to stderr for git-clone. [1]: https://lore.kernel.org/git/CA+34VNLj6VB1kCkA=MfM7TZR+6HgqNi5-UaziAoCXacSVkch4A@mail.gmail.com/T/ Reported-by: Baruch Burstein <bmburstein@gmail.com> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-03 04:44:19 +01:00
fprintf_ln(stderr, _("Preparing worktree (new branch '%s')"), new_branch);
worktree: improve message when creating a new worktree Currently 'git worktree add' produces output like the following: Preparing ../foo (identifier foo) HEAD is now at 26da330922 <title> The '../foo' is the path where the worktree is created, which the user has just given on the command line. The identifier is an internal implementation detail, which is not particularly relevant for the user and indeed isn't mentioned explicitly anywhere in the man page. Instead of this message, print a message that gives the user a bit more detail of what exactly 'git worktree' is doing. There are various dwim modes which perform some magic under the hood, which should be helpful to users. Just from the output of the command it is not always visible to users what exactly has happened. Help the users a bit more by modifying the "Preparing ..." message and adding some additional information of what 'git worktree add' did under the hood, while not displaying the identifier anymore. Currently there are several different cases: - 'git worktree add -b ...' or 'git worktree add <path>', both of which create a new branch, either through the user explicitly requesting it, or through 'git worktree add' implicitly creating it. This will end up with the following output: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title> - 'git worktree add -B ...', which may either create a new branch if the branch with the given name does not exist yet, or resets an existing branch to the current HEAD, or the commit-ish given. Depending on which action is taken, we'll end up with the following output: Preparing worktree (resetting branch '<branch>'; was at caa68db14) HEAD is now at 26da330922 <title> or: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title> - 'git worktree add --detach' or 'git worktree add <path> <commit-ish>', both of which create a new worktree with a detached HEAD, for which we will print the following output: Preparing worktree (detached HEAD 26da330922) HEAD is now at 26da330922 <title> - 'git worktree add <path> <local-branch>', which checks out the branch and prints the following output: Preparing worktree (checking out '<local-branch>') HEAD is now at 47007d5 <title> Additionally currently the "Preparing ..." line is printed to stderr, while the "HEAD is now at ..." line is printed to stdout by 'git reset --hard', which is used internally by 'git worktree add'. Fix this inconsistency by printing the "Preparing ..." message to stdout as well. As "Preparing ..." is not an error, stdout also seems like the more appropriate output stream. Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-04-24 23:56:33 +02:00
} else {
struct strbuf s = STRBUF_INIT;
if (!detach && !strbuf_check_branch_ref(&s, branch) &&
ref_exists(s.buf))
worktree: send "chatty" messages to stderr The order in which the stdout and stderr streams are flushed is not guaranteed to be the same across platforms or `libc` implementations. This lack of determinism can lead to anomalous and potentially confusing output if normal (stdout) output is flushed after error (stderr) output. For instance, the following output which clearly indicates a failure due to a fatal error: % git worktree add ../foo bar Preparing worktree (checking out 'bar') fatal: 'bar' is already checked out at '.../wherever' has been reported[1] on Microsoft Windows to appear as: % git worktree add ../foo bar fatal: 'bar' is already checked out at '.../wherever' Preparing worktree (checking out 'bar') which may confuse the reader into thinking that the command somehow recovered and ran to completion despite the error. This problem crops up because the "chatty" status message "Preparing worktree" is sent to stdout, whereas the "fatal" error message is sent to stderr. One way to fix this would be to flush stdout manually before git-worktree reports any errors to stderr. However, common practice in Git is for "chatty" messages to be sent to stderr. Therefore, a more appropriate fix is to adjust git-worktree to conform to that practice by sending its "chatty" messages to stderr rather than stdout as is currently the case. There may be concern that relocating messages from stdout to stderr could break existing tooling, however, these messages are already internationalized, thus are unstable. And, indeed, the "Preparing worktree" message has already been the subject of somewhat significant changes in 2c27002a0a (worktree: improve message when creating a new worktree, 2018-04-24). Moreover, there is existing precedent, such as 68b939b2f0 (clone: send diagnostic messages to stderr, 2013-09-18) which likewise relocated "chatty" messages from stdout to stderr for git-clone. [1]: https://lore.kernel.org/git/CA+34VNLj6VB1kCkA=MfM7TZR+6HgqNi5-UaziAoCXacSVkch4A@mail.gmail.com/T/ Reported-by: Baruch Burstein <bmburstein@gmail.com> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-03 04:44:19 +01:00
fprintf_ln(stderr, _("Preparing worktree (checking out '%s')"),
worktree: improve message when creating a new worktree Currently 'git worktree add' produces output like the following: Preparing ../foo (identifier foo) HEAD is now at 26da330922 <title> The '../foo' is the path where the worktree is created, which the user has just given on the command line. The identifier is an internal implementation detail, which is not particularly relevant for the user and indeed isn't mentioned explicitly anywhere in the man page. Instead of this message, print a message that gives the user a bit more detail of what exactly 'git worktree' is doing. There are various dwim modes which perform some magic under the hood, which should be helpful to users. Just from the output of the command it is not always visible to users what exactly has happened. Help the users a bit more by modifying the "Preparing ..." message and adding some additional information of what 'git worktree add' did under the hood, while not displaying the identifier anymore. Currently there are several different cases: - 'git worktree add -b ...' or 'git worktree add <path>', both of which create a new branch, either through the user explicitly requesting it, or through 'git worktree add' implicitly creating it. This will end up with the following output: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title> - 'git worktree add -B ...', which may either create a new branch if the branch with the given name does not exist yet, or resets an existing branch to the current HEAD, or the commit-ish given. Depending on which action is taken, we'll end up with the following output: Preparing worktree (resetting branch '<branch>'; was at caa68db14) HEAD is now at 26da330922 <title> or: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title> - 'git worktree add --detach' or 'git worktree add <path> <commit-ish>', both of which create a new worktree with a detached HEAD, for which we will print the following output: Preparing worktree (detached HEAD 26da330922) HEAD is now at 26da330922 <title> - 'git worktree add <path> <local-branch>', which checks out the branch and prints the following output: Preparing worktree (checking out '<local-branch>') HEAD is now at 47007d5 <title> Additionally currently the "Preparing ..." line is printed to stderr, while the "HEAD is now at ..." line is printed to stdout by 'git reset --hard', which is used internally by 'git worktree add'. Fix this inconsistency by printing the "Preparing ..." message to stdout as well. As "Preparing ..." is not an error, stdout also seems like the more appropriate output stream. Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-04-24 23:56:33 +02:00
branch);
else {
struct commit *commit = lookup_commit_reference_by_name(branch);
if (!commit)
die(_("invalid reference: %s"), branch);
worktree: send "chatty" messages to stderr The order in which the stdout and stderr streams are flushed is not guaranteed to be the same across platforms or `libc` implementations. This lack of determinism can lead to anomalous and potentially confusing output if normal (stdout) output is flushed after error (stderr) output. For instance, the following output which clearly indicates a failure due to a fatal error: % git worktree add ../foo bar Preparing worktree (checking out 'bar') fatal: 'bar' is already checked out at '.../wherever' has been reported[1] on Microsoft Windows to appear as: % git worktree add ../foo bar fatal: 'bar' is already checked out at '.../wherever' Preparing worktree (checking out 'bar') which may confuse the reader into thinking that the command somehow recovered and ran to completion despite the error. This problem crops up because the "chatty" status message "Preparing worktree" is sent to stdout, whereas the "fatal" error message is sent to stderr. One way to fix this would be to flush stdout manually before git-worktree reports any errors to stderr. However, common practice in Git is for "chatty" messages to be sent to stderr. Therefore, a more appropriate fix is to adjust git-worktree to conform to that practice by sending its "chatty" messages to stderr rather than stdout as is currently the case. There may be concern that relocating messages from stdout to stderr could break existing tooling, however, these messages are already internationalized, thus are unstable. And, indeed, the "Preparing worktree" message has already been the subject of somewhat significant changes in 2c27002a0a (worktree: improve message when creating a new worktree, 2018-04-24). Moreover, there is existing precedent, such as 68b939b2f0 (clone: send diagnostic messages to stderr, 2013-09-18) which likewise relocated "chatty" messages from stdout to stderr for git-clone. [1]: https://lore.kernel.org/git/CA+34VNLj6VB1kCkA=MfM7TZR+6HgqNi5-UaziAoCXacSVkch4A@mail.gmail.com/T/ Reported-by: Baruch Burstein <bmburstein@gmail.com> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-03 04:44:19 +01:00
fprintf_ln(stderr, _("Preparing worktree (detached HEAD %s)"),
find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
worktree: improve message when creating a new worktree Currently 'git worktree add' produces output like the following: Preparing ../foo (identifier foo) HEAD is now at 26da330922 <title> The '../foo' is the path where the worktree is created, which the user has just given on the command line. The identifier is an internal implementation detail, which is not particularly relevant for the user and indeed isn't mentioned explicitly anywhere in the man page. Instead of this message, print a message that gives the user a bit more detail of what exactly 'git worktree' is doing. There are various dwim modes which perform some magic under the hood, which should be helpful to users. Just from the output of the command it is not always visible to users what exactly has happened. Help the users a bit more by modifying the "Preparing ..." message and adding some additional information of what 'git worktree add' did under the hood, while not displaying the identifier anymore. Currently there are several different cases: - 'git worktree add -b ...' or 'git worktree add <path>', both of which create a new branch, either through the user explicitly requesting it, or through 'git worktree add' implicitly creating it. This will end up with the following output: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title> - 'git worktree add -B ...', which may either create a new branch if the branch with the given name does not exist yet, or resets an existing branch to the current HEAD, or the commit-ish given. Depending on which action is taken, we'll end up with the following output: Preparing worktree (resetting branch '<branch>'; was at caa68db14) HEAD is now at 26da330922 <title> or: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title> - 'git worktree add --detach' or 'git worktree add <path> <commit-ish>', both of which create a new worktree with a detached HEAD, for which we will print the following output: Preparing worktree (detached HEAD 26da330922) HEAD is now at 26da330922 <title> - 'git worktree add <path> <local-branch>', which checks out the branch and prints the following output: Preparing worktree (checking out '<local-branch>') HEAD is now at 47007d5 <title> Additionally currently the "Preparing ..." line is printed to stderr, while the "HEAD is now at ..." line is printed to stdout by 'git reset --hard', which is used internally by 'git worktree add'. Fix this inconsistency by printing the "Preparing ..." message to stdout as well. As "Preparing ..." is not an error, stdout also seems like the more appropriate output stream. Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-04-24 23:56:33 +02:00
}
strbuf_release(&s);
}
}
static const char *dwim_branch(const char *path, const char **new_branch)
{
int n;
worktree: fix leak in dwim_branch() Make sure that we release the temporary strbuf during dwim_branch() for all codepaths (and not just for the early return). This leak appears to have been introduced in: f60a7b763f (worktree: teach "add" to check out existing branches, 2018-04-24) Note that UNLEAK(branchname) is still needed: the returned result is used in add(), and is stored in a pointer which is used to point at one of: - a string literal ("HEAD") - member of argv (whatever the user specified in their invocation) - or our newly allocated string returned from dwim_branch() Fixing the branchname leak isn't impossible, but does not seem worthwhile given that add() is called directly from cmd_main(), and cmd_main() returns immediately thereafter - UNLEAK is good enough. This leak was found when running t0001 with LSAN, see also LSAN output below: Direct leak of 60 byte(s) in 1 object(s) allocated from: #0 0x49a859 in realloc /home/abuild/rpmbuild/BUILD/llvm-11.0.0.src/build/../projects/compiler-rt/lib/asan/asan_malloc_linux.cpp:164:3 #1 0x9ab076 in xrealloc /home/ahunt/oss-fuzz/git/wrapper.c:126:8 #2 0x939fcd in strbuf_grow /home/ahunt/oss-fuzz/git/strbuf.c:98:2 #3 0x93af53 in strbuf_splice /home/ahunt/oss-fuzz/git/strbuf.c:239:3 #4 0x83559a in strbuf_check_branch_ref /home/ahunt/oss-fuzz/git/object-name.c:1593:2 #5 0x6988b9 in dwim_branch /home/ahunt/oss-fuzz/git/builtin/worktree.c:454:20 #6 0x695f8f in add /home/ahunt/oss-fuzz/git/builtin/worktree.c:525:19 #7 0x694a04 in cmd_worktree /home/ahunt/oss-fuzz/git/builtin/worktree.c:1036:10 #8 0x4cd60d in run_builtin /home/ahunt/oss-fuzz/git/git.c:453:11 #9 0x4cb2da in handle_builtin /home/ahunt/oss-fuzz/git/git.c:704:3 #10 0x4ccc37 in run_argv /home/ahunt/oss-fuzz/git/git.c:771:4 #11 0x4cac29 in cmd_main /home/ahunt/oss-fuzz/git/git.c:902:19 #12 0x69caee in main /home/ahunt/oss-fuzz/git/common-main.c:52:11 #13 0x7f7b7dd10349 in __libc_start_main (/lib64/libc.so.6+0x24349) Signed-off-by: Andrzej Hunt <ajrhunt@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-03-14 19:47:37 +01:00
int branch_exists;
const char *s = worktree_basename(path, &n);
const char *branchname = xstrndup(s, n);
struct strbuf ref = STRBUF_INIT;
UNLEAK(branchname);
worktree: fix leak in dwim_branch() Make sure that we release the temporary strbuf during dwim_branch() for all codepaths (and not just for the early return). This leak appears to have been introduced in: f60a7b763f (worktree: teach "add" to check out existing branches, 2018-04-24) Note that UNLEAK(branchname) is still needed: the returned result is used in add(), and is stored in a pointer which is used to point at one of: - a string literal ("HEAD") - member of argv (whatever the user specified in their invocation) - or our newly allocated string returned from dwim_branch() Fixing the branchname leak isn't impossible, but does not seem worthwhile given that add() is called directly from cmd_main(), and cmd_main() returns immediately thereafter - UNLEAK is good enough. This leak was found when running t0001 with LSAN, see also LSAN output below: Direct leak of 60 byte(s) in 1 object(s) allocated from: #0 0x49a859 in realloc /home/abuild/rpmbuild/BUILD/llvm-11.0.0.src/build/../projects/compiler-rt/lib/asan/asan_malloc_linux.cpp:164:3 #1 0x9ab076 in xrealloc /home/ahunt/oss-fuzz/git/wrapper.c:126:8 #2 0x939fcd in strbuf_grow /home/ahunt/oss-fuzz/git/strbuf.c:98:2 #3 0x93af53 in strbuf_splice /home/ahunt/oss-fuzz/git/strbuf.c:239:3 #4 0x83559a in strbuf_check_branch_ref /home/ahunt/oss-fuzz/git/object-name.c:1593:2 #5 0x6988b9 in dwim_branch /home/ahunt/oss-fuzz/git/builtin/worktree.c:454:20 #6 0x695f8f in add /home/ahunt/oss-fuzz/git/builtin/worktree.c:525:19 #7 0x694a04 in cmd_worktree /home/ahunt/oss-fuzz/git/builtin/worktree.c:1036:10 #8 0x4cd60d in run_builtin /home/ahunt/oss-fuzz/git/git.c:453:11 #9 0x4cb2da in handle_builtin /home/ahunt/oss-fuzz/git/git.c:704:3 #10 0x4ccc37 in run_argv /home/ahunt/oss-fuzz/git/git.c:771:4 #11 0x4cac29 in cmd_main /home/ahunt/oss-fuzz/git/git.c:902:19 #12 0x69caee in main /home/ahunt/oss-fuzz/git/common-main.c:52:11 #13 0x7f7b7dd10349 in __libc_start_main (/lib64/libc.so.6+0x24349) Signed-off-by: Andrzej Hunt <ajrhunt@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-03-14 19:47:37 +01:00
branch_exists = !strbuf_check_branch_ref(&ref, branchname) &&
ref_exists(ref.buf);
strbuf_release(&ref);
if (branch_exists)
return branchname;
*new_branch = branchname;
if (guess_remote) {
struct object_id oid;
const char *remote =
unique_tracking_name(*new_branch, &oid, NULL);
return remote;
}
return NULL;
}
static int add(int ac, const char **av, const char *prefix)
{
struct add_opts opts;
const char *new_branch_force = NULL;
char *path;
const char *branch;
const char *new_branch = NULL;
const char *opt_track = NULL;
const char *lock_reason = NULL;
int keep_locked = 0;
struct option options[] = {
OPT__FORCE(&opts.force,
N_("checkout <branch> even if already checked out in other worktree"),
PARSE_OPT_NOCOMPLETE),
OPT_STRING('b', NULL, &new_branch, N_("branch"),
N_("create a new branch")),
OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
N_("create or reset a branch")),
OPT_BOOL('d', "detach", &opts.detach, N_("detach HEAD at named commit")),
OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
OPT_BOOL(0, "lock", &keep_locked, N_("keep the new working tree locked")),
OPT_STRING(0, "reason", &lock_reason, N_("string"),
N_("reason for locking")),
OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
OPT_PASSTHRU(0, "track", &opt_track, NULL,
N_("set up tracking mode (see git-branch(1))"),
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_END()
};
built-ins: use free() not UNLEAK() if trivial, rm dead code For a lot of uses of UNLEAK() it would be quite tricky to release the memory involved, or we're missing the relevant *_(release|clear)() functions. But in these cases we have them already, and can just invoke them on the variable(s) involved, instead of UNLEAK(). For "builtin/worktree.c" the UNLEAK() was also added in [1], but the struct member it's unleaking was removed in [2]. The only non-"int" member of that structure is "const char *keep_locked", which comes to us via "argv" or a string literal[3]. We have good visibility via the compiler and tooling (e.g. SANITIZE=address) on bad free()-ing, but none on UNLEAK() we don't need anymore. So let's prefer releasing the memory when it's easy. For "bugreport", "worktree" and "config" we need to start using a "ret = ..." return pattern. For "builtin/bugreport.c" these UNLEAK() were added in [4], and for "builtin/config.c" in [1]. For "config" the code seen here was the only user of the "value" variable. For "ACTION_{RENAME,REMOVE}_SECTION" we need to be sure to return the right exit code in the cases where we were relying on falling through to the top-level. I think there's still a use-case for UNLEAK(), but hat it's changed since then. Using it so that "we can see the real leaks" is counter-productive in these cases. It's more useful to have UNLEAK() be a marker of the remaining odd cases where it's hard to free() the memory for whatever reason. With this change less than 20 of them remain in-tree. 1. 0e5bba53af7 (add UNLEAK annotation for reducing leak false positives, 2017-09-08) 2. d861d34a6ed (worktree: remove extra members from struct add_opts, 2018-04-24) 3. 0db4961c49b (worktree: teach `add` to accept --reason <string> with --lock, 2021-07-15) 4. 0e5bba53af7 and 00d8c311050 (commit: fix "author_ident" leak, 2022-05-12). Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Taylor Blau <me@ttaylorr.com>
2022-11-08 19:17:51 +01:00
int ret;
memset(&opts, 0, sizeof(opts));
opts.checkout = 1;
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");
if (lock_reason && !keep_locked)
die(_("the option '%s' requires '%s'"), "--reason", "--lock");
if (lock_reason)
opts.keep_locked = lock_reason;
else if (keep_locked)
opts.keep_locked = _("added with --lock");
if (ac < 1 || ac > 2)
usage_with_options(git_worktree_add_usage, options);
path = prefix_filename(prefix, av[0]);
branch = ac < 2 ? "HEAD" : av[1];
if (!strcmp(branch, "-"))
branch = "@{-1}";
if (new_branch_force) {
struct strbuf symref = STRBUF_INIT;
new_branch = new_branch_force;
if (!opts.force &&
!strbuf_check_branch_ref(&symref, new_branch) &&
ref_exists(symref.buf))
2016-04-22 15:01:33 +02:00
die_if_checked_out(symref.buf, 0);
strbuf_release(&symref);
}
if (ac < 2 && !new_branch && !opts.detach) {
const char *s = dwim_branch(path, &new_branch);
if (s)
branch = s;
}
if (ac == 2 && !new_branch && !opts.detach) {
struct object_id oid;
struct commit *commit;
const char *remote;
commit = lookup_commit_reference_by_name(branch);
if (!commit) {
remote = unique_tracking_name(branch, &oid, NULL);
if (remote) {
new_branch = branch;
branch = remote;
}
}
}
if (!opts.quiet)
print_preparing_worktree_line(opts.detach, branch, new_branch, !!new_branch_force);
worktree: improve message when creating a new worktree Currently 'git worktree add' produces output like the following: Preparing ../foo (identifier foo) HEAD is now at 26da330922 <title> The '../foo' is the path where the worktree is created, which the user has just given on the command line. The identifier is an internal implementation detail, which is not particularly relevant for the user and indeed isn't mentioned explicitly anywhere in the man page. Instead of this message, print a message that gives the user a bit more detail of what exactly 'git worktree' is doing. There are various dwim modes which perform some magic under the hood, which should be helpful to users. Just from the output of the command it is not always visible to users what exactly has happened. Help the users a bit more by modifying the "Preparing ..." message and adding some additional information of what 'git worktree add' did under the hood, while not displaying the identifier anymore. Currently there are several different cases: - 'git worktree add -b ...' or 'git worktree add <path>', both of which create a new branch, either through the user explicitly requesting it, or through 'git worktree add' implicitly creating it. This will end up with the following output: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title> - 'git worktree add -B ...', which may either create a new branch if the branch with the given name does not exist yet, or resets an existing branch to the current HEAD, or the commit-ish given. Depending on which action is taken, we'll end up with the following output: Preparing worktree (resetting branch '<branch>'; was at caa68db14) HEAD is now at 26da330922 <title> or: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title> - 'git worktree add --detach' or 'git worktree add <path> <commit-ish>', both of which create a new worktree with a detached HEAD, for which we will print the following output: Preparing worktree (detached HEAD 26da330922) HEAD is now at 26da330922 <title> - 'git worktree add <path> <local-branch>', which checks out the branch and prints the following output: Preparing worktree (checking out '<local-branch>') HEAD is now at 47007d5 <title> Additionally currently the "Preparing ..." line is printed to stderr, while the "HEAD is now at ..." line is printed to stdout by 'git reset --hard', which is used internally by 'git worktree add'. Fix this inconsistency by printing the "Preparing ..." message to stdout as well. As "Preparing ..." is not an error, stdout also seems like the more appropriate output stream. Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-04-24 23:56:33 +02:00
if (new_branch) {
struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
strvec_push(&cp.args, "branch");
if (new_branch_force)
strvec_push(&cp.args, "--force");
if (opts.quiet)
strvec_push(&cp.args, "--quiet");
strvec_push(&cp.args, new_branch);
strvec_push(&cp.args, branch);
if (opt_track)
strvec_push(&cp.args, opt_track);
if (run_command(&cp))
return -1;
branch = new_branch;
} else if (opt_track) {
die(_("--[no-]track can only be used if a new branch is created"));
}
built-ins: use free() not UNLEAK() if trivial, rm dead code For a lot of uses of UNLEAK() it would be quite tricky to release the memory involved, or we're missing the relevant *_(release|clear)() functions. But in these cases we have them already, and can just invoke them on the variable(s) involved, instead of UNLEAK(). For "builtin/worktree.c" the UNLEAK() was also added in [1], but the struct member it's unleaking was removed in [2]. The only non-"int" member of that structure is "const char *keep_locked", which comes to us via "argv" or a string literal[3]. We have good visibility via the compiler and tooling (e.g. SANITIZE=address) on bad free()-ing, but none on UNLEAK() we don't need anymore. So let's prefer releasing the memory when it's easy. For "bugreport", "worktree" and "config" we need to start using a "ret = ..." return pattern. For "builtin/bugreport.c" these UNLEAK() were added in [4], and for "builtin/config.c" in [1]. For "config" the code seen here was the only user of the "value" variable. For "ACTION_{RENAME,REMOVE}_SECTION" we need to be sure to return the right exit code in the cases where we were relying on falling through to the top-level. I think there's still a use-case for UNLEAK(), but hat it's changed since then. Using it so that "we can see the real leaks" is counter-productive in these cases. It's more useful to have UNLEAK() be a marker of the remaining odd cases where it's hard to free() the memory for whatever reason. With this change less than 20 of them remain in-tree. 1. 0e5bba53af7 (add UNLEAK annotation for reducing leak false positives, 2017-09-08) 2. d861d34a6ed (worktree: remove extra members from struct add_opts, 2018-04-24) 3. 0db4961c49b (worktree: teach `add` to accept --reason <string> with --lock, 2021-07-15) 4. 0e5bba53af7 and 00d8c311050 (commit: fix "author_ident" leak, 2022-05-12). Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Taylor Blau <me@ttaylorr.com>
2022-11-08 19:17:51 +01:00
ret = add_worktree(path, branch, &opts);
free(path);
return ret;
}
static void show_worktree_porcelain(struct worktree *wt, int line_terminator)
{
worktree: teach `list --porcelain` to annotate locked worktree Commit c57b3367be (worktree: teach `list` to annotate locked worktree, 2020-10-11) taught "git worktree list" to annotate locked worktrees by appending "locked" text to its output, however, this is not listed in the --porcelain format. Teach "list --porcelain" to do the same and add a "locked" attribute followed by its reason, thus making both default and porcelain format consistent. If the locked reason is not available then only "locked" is shown. The output of the "git worktree list --porcelain" becomes like so: $ git worktree list --porcelain ... worktree /path/to/locked HEAD 123abcdea123abcd123acbd123acbda123abcd12 detached locked worktree /path/to/locked-with-reason HEAD abc123abc123abc123abc123abc123abc123abc1 detached locked reason why it is locked ... In porcelain mode, if the lock reason contains special characters such as newlines, they are escaped with backslashes and the entire reason is enclosed in double quotes. For example: $ git worktree list --porcelain ... locked "worktree's path mounted in\nremovable device" ... Furthermore, let's update the documentation to state that some attributes in the porcelain format might be listed alone or together with its value depending whether the value is available or not. Thus documenting the case of the new "locked" attribute. Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk> Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Rafael Silva <rafaeloliveira.cs@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-01-27 09:03:08 +01:00
const char *reason;
printf("worktree %s%c", wt->path, line_terminator);
if (wt->is_bare)
printf("bare%c", line_terminator);
else {
printf("HEAD %s%c", oid_to_hex(&wt->head_oid), line_terminator);
if (wt->is_detached)
printf("detached%c", line_terminator);
else if (wt->head_ref)
printf("branch %s%c", wt->head_ref, line_terminator);
}
worktree: teach `list --porcelain` to annotate locked worktree Commit c57b3367be (worktree: teach `list` to annotate locked worktree, 2020-10-11) taught "git worktree list" to annotate locked worktrees by appending "locked" text to its output, however, this is not listed in the --porcelain format. Teach "list --porcelain" to do the same and add a "locked" attribute followed by its reason, thus making both default and porcelain format consistent. If the locked reason is not available then only "locked" is shown. The output of the "git worktree list --porcelain" becomes like so: $ git worktree list --porcelain ... worktree /path/to/locked HEAD 123abcdea123abcd123acbd123acbda123abcd12 detached locked worktree /path/to/locked-with-reason HEAD abc123abc123abc123abc123abc123abc123abc1 detached locked reason why it is locked ... In porcelain mode, if the lock reason contains special characters such as newlines, they are escaped with backslashes and the entire reason is enclosed in double quotes. For example: $ git worktree list --porcelain ... locked "worktree's path mounted in\nremovable device" ... Furthermore, let's update the documentation to state that some attributes in the porcelain format might be listed alone or together with its value depending whether the value is available or not. Thus documenting the case of the new "locked" attribute. Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk> Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Rafael Silva <rafaeloliveira.cs@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-01-27 09:03:08 +01:00
reason = worktree_lock_reason(wt);
if (reason) {
fputs("locked", stdout);
if (*reason) {
fputc(' ', stdout);
write_name_quoted(reason, stdout, line_terminator);
} else {
fputc(line_terminator, stdout);
}
}
worktree: teach `list --porcelain` to annotate locked worktree Commit c57b3367be (worktree: teach `list` to annotate locked worktree, 2020-10-11) taught "git worktree list" to annotate locked worktrees by appending "locked" text to its output, however, this is not listed in the --porcelain format. Teach "list --porcelain" to do the same and add a "locked" attribute followed by its reason, thus making both default and porcelain format consistent. If the locked reason is not available then only "locked" is shown. The output of the "git worktree list --porcelain" becomes like so: $ git worktree list --porcelain ... worktree /path/to/locked HEAD 123abcdea123abcd123acbd123acbda123abcd12 detached locked worktree /path/to/locked-with-reason HEAD abc123abc123abc123abc123abc123abc123abc1 detached locked reason why it is locked ... In porcelain mode, if the lock reason contains special characters such as newlines, they are escaped with backslashes and the entire reason is enclosed in double quotes. For example: $ git worktree list --porcelain ... locked "worktree's path mounted in\nremovable device" ... Furthermore, let's update the documentation to state that some attributes in the porcelain format might be listed alone or together with its value depending whether the value is available or not. Thus documenting the case of the new "locked" attribute. Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk> Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Rafael Silva <rafaeloliveira.cs@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-01-27 09:03:08 +01:00
worktree: teach `list` to annotate prunable worktree The "git worktree list" command shows the absolute path to the worktree, the commit that is checked out, the name of the branch, and a "locked" annotation if the worktree is locked, however, it does not indicate whether the worktree is prunable. The "prune" command will remove a worktree if it is prunable unless `--dry-run` option is specified. This could lead to a worktree being removed without the user realizing before it is too late, in case the user forgets to pass --dry-run for instance. If the "list" command shows which worktree is prunable, the user could verify before running "git worktree prune" and hopefully prevents the working tree to be removed "accidentally" on the worse case scenario. Let's teach "git worktree list" to show when a worktree is a prunable candidate for both default and porcelain format. In the default format a "prunable" text is appended: $ git worktree list /path/to/main aba123 [main] /path/to/linked 123abc [branch-a] /path/to/prunable ace127 (detached HEAD) prunable In the --porcelain format a prunable label is added followed by its reason: $ git worktree list --porcelain ... worktree /path/to/prunable HEAD abc1234abc1234abc1234abc1234abc1234abc12 detached prunable gitdir file points to non-existent location ... Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Rafael Silva <rafaeloliveira.cs@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-01-27 09:03:09 +01:00
reason = worktree_prune_reason(wt, expire);
if (reason)
printf("prunable %s%c", reason, line_terminator);
worktree: teach `list` to annotate prunable worktree The "git worktree list" command shows the absolute path to the worktree, the commit that is checked out, the name of the branch, and a "locked" annotation if the worktree is locked, however, it does not indicate whether the worktree is prunable. The "prune" command will remove a worktree if it is prunable unless `--dry-run` option is specified. This could lead to a worktree being removed without the user realizing before it is too late, in case the user forgets to pass --dry-run for instance. If the "list" command shows which worktree is prunable, the user could verify before running "git worktree prune" and hopefully prevents the working tree to be removed "accidentally" on the worse case scenario. Let's teach "git worktree list" to show when a worktree is a prunable candidate for both default and porcelain format. In the default format a "prunable" text is appended: $ git worktree list /path/to/main aba123 [main] /path/to/linked 123abc [branch-a] /path/to/prunable ace127 (detached HEAD) prunable In the --porcelain format a prunable label is added followed by its reason: $ git worktree list --porcelain ... worktree /path/to/prunable HEAD abc1234abc1234abc1234abc1234abc1234abc12 detached prunable gitdir file points to non-existent location ... Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Rafael Silva <rafaeloliveira.cs@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-01-27 09:03:09 +01:00
fputc(line_terminator, stdout);
}
static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
{
struct strbuf sb = STRBUF_INIT;
int cur_path_len = strlen(wt->path);
int path_adj = cur_path_len - utf8_strwidth(wt->path);
2021-01-27 09:03:10 +01:00
const char *reason;
strbuf_addf(&sb, "%-*s ", 1 + path_maxlen + path_adj, wt->path);
if (wt->is_bare)
strbuf_addstr(&sb, "(bare)");
else {
strbuf_addf(&sb, "%-*s ", abbrev_len,
find_unique_abbrev(&wt->head_oid, DEFAULT_ABBREV));
if (wt->is_detached)
strbuf_addstr(&sb, "(detached HEAD)");
else if (wt->head_ref) {
char *ref = shorten_unambiguous_ref(wt->head_ref, 0);
strbuf_addf(&sb, "[%s]", ref);
free(ref);
} else
strbuf_addstr(&sb, "(error)");
}
2021-01-27 09:03:10 +01:00
reason = worktree_lock_reason(wt);
if (verbose && reason && *reason)
strbuf_addf(&sb, "\n\tlocked: %s", reason);
else if (reason)
strbuf_addstr(&sb, " locked");
2021-01-27 09:03:10 +01:00
reason = worktree_prune_reason(wt, expire);
if (verbose && reason)
strbuf_addf(&sb, "\n\tprunable: %s", reason);
else if (reason)
worktree: teach `list` to annotate prunable worktree The "git worktree list" command shows the absolute path to the worktree, the commit that is checked out, the name of the branch, and a "locked" annotation if the worktree is locked, however, it does not indicate whether the worktree is prunable. The "prune" command will remove a worktree if it is prunable unless `--dry-run` option is specified. This could lead to a worktree being removed without the user realizing before it is too late, in case the user forgets to pass --dry-run for instance. If the "list" command shows which worktree is prunable, the user could verify before running "git worktree prune" and hopefully prevents the working tree to be removed "accidentally" on the worse case scenario. Let's teach "git worktree list" to show when a worktree is a prunable candidate for both default and porcelain format. In the default format a "prunable" text is appended: $ git worktree list /path/to/main aba123 [main] /path/to/linked 123abc [branch-a] /path/to/prunable ace127 (detached HEAD) prunable In the --porcelain format a prunable label is added followed by its reason: $ git worktree list --porcelain ... worktree /path/to/prunable HEAD abc1234abc1234abc1234abc1234abc1234abc12 detached prunable gitdir file points to non-existent location ... Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Rafael Silva <rafaeloliveira.cs@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-01-27 09:03:09 +01:00
strbuf_addstr(&sb, " prunable");
printf("%s\n", sb.buf);
strbuf_release(&sb);
}
static void measure_widths(struct worktree **wt, int *abbrev, int *maxlen)
{
int i;
for (i = 0; wt[i]; i++) {
int sha1_len;
int path_len = strlen(wt[i]->path);
if (path_len > *maxlen)
*maxlen = path_len;
sha1_len = strlen(find_unique_abbrev(&wt[i]->head_oid, *abbrev));
if (sha1_len > *abbrev)
*abbrev = sha1_len;
}
}
static int pathcmp(const void *a_, const void *b_)
{
const struct worktree *const *a = a_;
const struct worktree *const *b = b_;
return fspathcmp((*a)->path, (*b)->path);
}
static void pathsort(struct worktree **wt)
{
int n = 0;
struct worktree **p = wt;
while (*p++)
n++;
QSORT(wt, n, pathcmp);
}
static int list(int ac, const char **av, const char *prefix)
{
int porcelain = 0;
int line_terminator = '\n';
struct option options[] = {
OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
2021-01-27 09:03:10 +01:00
OPT__VERBOSE(&verbose, N_("show extended annotations and reasons, if available")),
worktree: teach `list` to annotate prunable worktree The "git worktree list" command shows the absolute path to the worktree, the commit that is checked out, the name of the branch, and a "locked" annotation if the worktree is locked, however, it does not indicate whether the worktree is prunable. The "prune" command will remove a worktree if it is prunable unless `--dry-run` option is specified. This could lead to a worktree being removed without the user realizing before it is too late, in case the user forgets to pass --dry-run for instance. If the "list" command shows which worktree is prunable, the user could verify before running "git worktree prune" and hopefully prevents the working tree to be removed "accidentally" on the worse case scenario. Let's teach "git worktree list" to show when a worktree is a prunable candidate for both default and porcelain format. In the default format a "prunable" text is appended: $ git worktree list /path/to/main aba123 [main] /path/to/linked 123abc [branch-a] /path/to/prunable ace127 (detached HEAD) prunable In the --porcelain format a prunable label is added followed by its reason: $ git worktree list --porcelain ... worktree /path/to/prunable HEAD abc1234abc1234abc1234abc1234abc1234abc12 detached prunable gitdir file points to non-existent location ... Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Rafael Silva <rafaeloliveira.cs@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-01-27 09:03:09 +01:00
OPT_EXPIRY_DATE(0, "expire", &expire,
N_("add 'prunable' annotation to worktrees older than <time>")),
OPT_SET_INT('z', NULL, &line_terminator,
N_("terminate records with a NUL character"), '\0'),
OPT_END()
};
worktree: teach `list` to annotate prunable worktree The "git worktree list" command shows the absolute path to the worktree, the commit that is checked out, the name of the branch, and a "locked" annotation if the worktree is locked, however, it does not indicate whether the worktree is prunable. The "prune" command will remove a worktree if it is prunable unless `--dry-run` option is specified. This could lead to a worktree being removed without the user realizing before it is too late, in case the user forgets to pass --dry-run for instance. If the "list" command shows which worktree is prunable, the user could verify before running "git worktree prune" and hopefully prevents the working tree to be removed "accidentally" on the worse case scenario. Let's teach "git worktree list" to show when a worktree is a prunable candidate for both default and porcelain format. In the default format a "prunable" text is appended: $ git worktree list /path/to/main aba123 [main] /path/to/linked 123abc [branch-a] /path/to/prunable ace127 (detached HEAD) prunable In the --porcelain format a prunable label is added followed by its reason: $ git worktree list --porcelain ... worktree /path/to/prunable HEAD abc1234abc1234abc1234abc1234abc1234abc12 detached prunable gitdir file points to non-existent location ... Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Rafael Silva <rafaeloliveira.cs@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-01-27 09:03:09 +01:00
expire = TIME_MAX;
ac = parse_options(ac, av, prefix, options, git_worktree_list_usage, 0);
if (ac)
usage_with_options(git_worktree_list_usage, options);
2021-01-27 09:03:10 +01:00
else if (verbose && porcelain)
die(_("options '%s' and '%s' cannot be used together"), "--verbose", "--porcelain");
else if (!line_terminator && !porcelain)
die(_("the option '%s' requires '%s'"), "-z", "--porcelain");
else {
struct worktree **worktrees = get_worktrees();
int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i;
/* sort worktrees by path but keep main worktree at top */
pathsort(worktrees + 1);
if (!porcelain)
measure_widths(worktrees, &abbrev, &path_maxlen);
for (i = 0; worktrees[i]; i++) {
if (porcelain)
show_worktree_porcelain(worktrees[i],
line_terminator);
else
show_worktree(worktrees[i], path_maxlen, abbrev);
}
free_worktrees(worktrees);
}
return 0;
}
static int lock_worktree(int ac, const char **av, const char *prefix)
{
const char *reason = "", *old_reason;
struct option options[] = {
OPT_STRING(0, "reason", &reason, N_("string"),
N_("reason for locking")),
OPT_END()
};
struct worktree **worktrees, *wt;
ac = parse_options(ac, av, prefix, options, git_worktree_lock_usage, 0);
if (ac != 1)
usage_with_options(git_worktree_lock_usage, options);
worktrees = get_worktrees();
wt = find_worktree(worktrees, prefix, av[0]);
if (!wt)
die(_("'%s' is not a working tree"), av[0]);
if (is_main_worktree(wt))
die(_("The main working tree cannot be locked or unlocked"));
old_reason = worktree_lock_reason(wt);
if (old_reason) {
if (*old_reason)
die(_("'%s' is already locked, reason: %s"),
av[0], old_reason);
die(_("'%s' is already locked"), av[0]);
}
write_file(git_common_path("worktrees/%s/locked", wt->id),
"%s", reason);
free_worktrees(worktrees);
return 0;
}
static int unlock_worktree(int ac, const char **av, const char *prefix)
{
struct option options[] = {
OPT_END()
};
struct worktree **worktrees, *wt;
int ret;
ac = parse_options(ac, av, prefix, options, git_worktree_unlock_usage, 0);
if (ac != 1)
usage_with_options(git_worktree_unlock_usage, options);
worktrees = get_worktrees();
wt = find_worktree(worktrees, prefix, av[0]);
if (!wt)
die(_("'%s' is not a working tree"), av[0]);
if (is_main_worktree(wt))
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));
free_worktrees(worktrees);
return ret;
}
static void validate_no_submodules(const struct worktree *wt)
{
treewide: always have a valid "index_state.repo" member When the "repo" member was added to "the_index" in [1] the repo_read_index() was made to populate it, but the unpopulated "the_index" variable didn't get the same treatment. Let's do that in initialize_the_repository() when we set it up, and likewise for all of the current callers initialized an empty "struct index_state". This simplifies code that needs to deal with "the_index" or a custom "struct index_state", we no longer need to second-guess this part of the "index_state" deep in the stack. A recent example of such second-guessing is the "istate->repo ? istate->repo : the_repository" code in [2]. We can now simply use "istate->repo". We're doing this by making use of the INDEX_STATE_INIT() macro (and corresponding function) added in [3], which now have mandatory "repo" arguments. Because we now call index_state_init() in repository.c's initialize_the_repository() we don't need to handle the case where we have a "repo->index" whose "repo" member doesn't match the "repo" we're setting up, i.e. the "Complete the double-reference" code in repo_read_index() being altered here. That logic was originally added in [1], and was working around the lack of what we now have in initialize_the_repository(). For "fsmonitor-settings.c" we can remove the initialization of a NULL "r" argument to "the_repository". This was added back in [4], and was needed at the time for callers that would pass us the "r" from an "istate->repo". Before this change such a change to "fsmonitor-settings.c" would segfault all over the test suite (e.g. in t0002-gitfile.sh). This change has wider eventual implications for "fsmonitor-settings.c". The reason the other lazy loading behavior in it is required (starting with "if (!r->settings.fsmonitor) ..." is because of the previously passed "r" being "NULL". I have other local changes on top of this which move its configuration reading to "prepare_repo_settings()" in "repo-settings.c", as we could now start to rely on it being called for our "r". But let's leave all of that for now, and narrowly remove this particular part of the lazy-loading. 1. 1fd9ae517c4 (repository: add repo reference to index_state, 2021-01-23) 2. ee1f0c242ef (read-cache: add index.skipHash config option, 2023-01-06) 3. 2f6b1eb794e (cache API: add a "INDEX_STATE_INIT" macro/function, add release_index(), 2023-01-12) 4. 1e0ea5c4316 (fsmonitor: config settings are repository-specific, 2022-03-25) Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Acked-by: Derrick Stolee <derrickstolee@github.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-01-17 14:57:00 +01:00
struct index_state istate = INDEX_STATE_INIT(the_repository);
struct strbuf path = STRBUF_INIT;
int i, found_submodules = 0;
if (is_directory(worktree_git_path(wt, "modules"))) {
/*
* There could be false positives, e.g. the "modules"
* directory exists but is empty. But it's a rare case and
* this simpler check is probably good enough for now.
*/
found_submodules = 1;
} else if (read_index_from(&istate, worktree_git_path(wt, "index"),
get_worktree_git_dir(wt)) > 0) {
for (i = 0; i < istate.cache_nr; i++) {
struct cache_entry *ce = istate.cache[i];
int err;
if (!S_ISGITLINK(ce->ce_mode))
continue;
strbuf_reset(&path);
strbuf_addf(&path, "%s/%s", wt->path, ce->name);
if (!is_submodule_populated_gently(path.buf, &err))
continue;
found_submodules = 1;
break;
}
}
discard_index(&istate);
strbuf_release(&path);
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)
{
int force = 0;
struct option options[] = {
OPT__FORCE(&force,
N_("force move even if worktree is dirty or locked"),
PARSE_OPT_NOCOMPLETE),
OPT_END()
};
struct worktree **worktrees, *wt;
struct strbuf dst = STRBUF_INIT;
struct strbuf errmsg = STRBUF_INIT;
const char *reason = NULL;
char *path;
ac = parse_options(ac, av, prefix, options, git_worktree_move_usage,
0);
if (ac != 2)
usage_with_options(git_worktree_move_usage, options);
path = prefix_filename(prefix, av[1]);
strbuf_addstr(&dst, path);
free(path);
worktrees = get_worktrees();
wt = find_worktree(worktrees, prefix, av[0]);
if (!wt)
die(_("'%s' is not a working tree"), av[0]);
if (is_main_worktree(wt))
die(_("'%s' is a main working tree"), av[0]);
if (is_directory(dst.buf)) {
const char *sep = find_last_dir_sep(wt->path);
if (!sep)
die(_("could not figure out destination name from '%s'"),
wt->path);
strbuf_trim_trailing_dir_sep(&dst);
strbuf_addstr(&dst, sep);
}
check_candidate_path(dst.buf, force, worktrees, "move");
validate_no_submodules(wt);
if (force < 2)
reason = worktree_lock_reason(wt);
if (reason) {
if (*reason)
die(_("cannot move a locked working tree, lock reason: %s\nuse 'move -f -f' to override or unlock first"),
reason);
die(_("cannot move a locked working tree;\nuse 'move -f -f' to override or unlock first"));
}
if (validate_worktree(wt, &errmsg, 0))
die(_("validation failed, cannot move working tree: %s"),
errmsg.buf);
strbuf_release(&errmsg);
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);
strbuf_release(&dst);
free_worktrees(worktrees);
return 0;
}
/*
* Note, "git status --porcelain" is used to determine if it's safe to
* delete a whole worktree. "git status" does not ignore user
* configuration, so if a normal "git status" shows "clean" for the
* user, then it's ok to remove it.
*
* This assumption may be a bad one. We may want to ignore
* (potentially bad) user settings and only delete a worktree when
* it's absolutely safe to do so from _our_ point of view because we
* know better.
*/
static void check_clean_worktree(struct worktree *wt,
const char *original_path)
{
struct child_process cp;
char buf[1];
int ret;
/*
* Until we sort this out, all submodules are "dirty" and
* will abort this function.
*/
validate_no_submodules(wt);
child_process_init(&cp);
strvec_pushf(&cp.env, "%s=%s/.git",
GIT_DIR_ENVIRONMENT, wt->path);
strvec_pushf(&cp.env, "%s=%s",
GIT_WORK_TREE_ENVIRONMENT, wt->path);
strvec_pushl(&cp.args, "status",
"--porcelain", "--ignore-submodules=none",
NULL);
cp.git_cmd = 1;
cp.dir = wt->path;
cp.out = -1;
ret = start_command(&cp);
if (ret)
die_errno(_("failed to run 'git status' on '%s'"),
original_path);
ret = xread(cp.out, buf, sizeof(buf));
if (ret)
die(_("'%s' contains modified or untracked files, use --force to delete it"),
original_path);
close(cp.out);
ret = finish_command(&cp);
if (ret)
die_errno(_("failed to run 'git status' on '%s', code %d"),
original_path, ret);
}
static int delete_git_work_tree(struct worktree *wt)
{
struct strbuf sb = STRBUF_INIT;
int ret = 0;
strbuf_addstr(&sb, wt->path);
if (remove_dir_recursively(&sb, 0)) {
error_errno(_("failed to delete '%s'"), sb.buf);
ret = -1;
}
strbuf_release(&sb);
return ret;
}
static int remove_worktree(int ac, const char **av, const char *prefix)
{
int force = 0;
struct option options[] = {
OPT__FORCE(&force,
N_("force removal even if worktree is dirty or locked"),
PARSE_OPT_NOCOMPLETE),
OPT_END()
};
struct worktree **worktrees, *wt;
struct strbuf errmsg = STRBUF_INIT;
const char *reason = NULL;
int ret = 0;
ac = parse_options(ac, av, prefix, options, git_worktree_remove_usage, 0);
if (ac != 1)
usage_with_options(git_worktree_remove_usage, options);
worktrees = get_worktrees();
wt = find_worktree(worktrees, prefix, av[0]);
if (!wt)
die(_("'%s' is not a working tree"), av[0]);
if (is_main_worktree(wt))
die(_("'%s' is a main working tree"), av[0]);
if (force < 2)
reason = worktree_lock_reason(wt);
if (reason) {
if (*reason)
die(_("cannot remove a locked working tree, lock reason: %s\nuse 'remove -f -f' to override or unlock first"),
reason);
die(_("cannot remove a locked working tree;\nuse 'remove -f -f' to override or unlock first"));
}
if (validate_worktree(wt, &errmsg, WT_VALIDATE_WORKTREE_MISSING_OK))
die(_("validation failed, cannot remove working tree: %s"),
errmsg.buf);
strbuf_release(&errmsg);
if (file_exists(wt->path)) {
if (!force)
check_clean_worktree(wt, av[0]);
ret |= delete_git_work_tree(wt);
}
/*
* continue on even if ret is non-zero, there's no going back
* from here.
*/
ret |= delete_git_dir(wt->id);
delete_worktrees_dir_if_empty();
free_worktrees(worktrees);
return ret;
}
static void report_repair(int iserr, const char *path, const char *msg, void *cb_data)
{
if (!iserr) {
worktree: send "chatty" messages to stderr The order in which the stdout and stderr streams are flushed is not guaranteed to be the same across platforms or `libc` implementations. This lack of determinism can lead to anomalous and potentially confusing output if normal (stdout) output is flushed after error (stderr) output. For instance, the following output which clearly indicates a failure due to a fatal error: % git worktree add ../foo bar Preparing worktree (checking out 'bar') fatal: 'bar' is already checked out at '.../wherever' has been reported[1] on Microsoft Windows to appear as: % git worktree add ../foo bar fatal: 'bar' is already checked out at '.../wherever' Preparing worktree (checking out 'bar') which may confuse the reader into thinking that the command somehow recovered and ran to completion despite the error. This problem crops up because the "chatty" status message "Preparing worktree" is sent to stdout, whereas the "fatal" error message is sent to stderr. One way to fix this would be to flush stdout manually before git-worktree reports any errors to stderr. However, common practice in Git is for "chatty" messages to be sent to stderr. Therefore, a more appropriate fix is to adjust git-worktree to conform to that practice by sending its "chatty" messages to stderr rather than stdout as is currently the case. There may be concern that relocating messages from stdout to stderr could break existing tooling, however, these messages are already internationalized, thus are unstable. And, indeed, the "Preparing worktree" message has already been the subject of somewhat significant changes in 2c27002a0a (worktree: improve message when creating a new worktree, 2018-04-24). Moreover, there is existing precedent, such as 68b939b2f0 (clone: send diagnostic messages to stderr, 2013-09-18) which likewise relocated "chatty" messages from stdout to stderr for git-clone. [1]: https://lore.kernel.org/git/CA+34VNLj6VB1kCkA=MfM7TZR+6HgqNi5-UaziAoCXacSVkch4A@mail.gmail.com/T/ Reported-by: Baruch Burstein <bmburstein@gmail.com> Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-03 04:44:19 +01:00
fprintf_ln(stderr, _("repair: %s: %s"), msg, path);
} else {
int *exit_status = (int *)cb_data;
fprintf_ln(stderr, _("error: %s: %s"), msg, path);
*exit_status = 1;
}
}
static int repair(int ac, const char **av, const char *prefix)
{
const char **p;
const char *self[] = { ".", NULL };
struct option options[] = {
OPT_END()
};
int rc = 0;
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);
worktree: teach `repair` to fix multi-directional breakage `git worktree repair` knows how to repair the two-way links between the repository and a worktree as long as a link in one or the other direction is sound. For instance, if a linked worktree is moved (without using `git worktree move`), repair is possible because the worktree still knows the location of the repository even though the repository no longer knows where the worktree is. Similarly, if the repository is moved, repair is possible since the repository still knows the locations of the worktrees even though the worktrees no longer know where the repository is. However, if both the repository and the worktrees are moved, then links are severed in both directions, and no repair is possible. This is the case even when the new worktree locations are specified as arguments to `git worktree repair`. The reason for this limitation is twofold. First, when `repair` consults the worktree's gitfile (/path/to/worktree/.git) to determine the corresponding <repo>/worktrees/<id>/gitdir file to fix, <repo> is the old path to the repository, thus it is unable to fix the `gitdir` file at its new location since it doesn't know where it is. Second, when `repair` consults <repo>/worktrees/<id>/gitdir to find the location of the worktree's gitfile (/path/to/worktree/.git), the path recorded in `gitdir` is the old location of the worktree's gitfile, thus it is unable to repair the gitfile since it doesn't know where it is. Fix these shortcomings by teaching `repair` to attempt to infer the new location of the <repo>/worktrees/<id>/gitdir file when the location recorded in the worktree's gitfile has become stale but the file is otherwise well-formed. The inference is intentionally simple-minded. For each worktree path specified as an argument, `git worktree repair` manually reads the ".git" gitfile at that location and, if it is well-formed, extracts the <id>. It then searches for a corresponding <id> in <repo>/worktrees/ and, if found, concludes that there is a reasonable match and updates <repo>/worktrees/<id>/gitdir to point at the specified worktree path. In order for <repo> to be known, `git worktree repair` must be run in the main worktree or bare repository. `git worktree repair` first attempts to repair each incoming /path/to/worktree/.git gitfile to point at the repository, and then attempts to repair outgoing <repo>/worktrees/<id>/gitdir files to point at the worktrees. This sequence was chosen arbitrarily when originally implemented since the order of fixes is immaterial as long as one side of the two-way link between the repository and a worktree is sound. However, for this new repair technique to work, the order must be reversed. This is because the new inference mechanism, when it is successful, allows the outgoing <repo>/worktrees/<id>/gitdir file to be repaired, thus fixing one side of the two-way link. Once that side is fixed, the other side can be fixed by the existing repair mechanism, hence the order of repairs is now significant. Two safeguards are employed to avoid hijacking a worktree from a different repository if the user accidentally specifies a foreign worktree as an argument. The first, as described above, is that it requires an <id> match between the repository and the worktree. That itself is not foolproof for preventing hijack, so the second safeguard is that the inference will only kick in if the worktree's /path/to/worktree/.git gitfile does not point at a repository. Signed-off-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-12-21 09:16:01 +01:00
repair_worktrees(report_repair, &rc);
return rc;
}
int cmd_worktree(int ac, const char **av, const char *prefix)
{
parse_opt_subcommand_fn *fn = NULL;
struct option options[] = {
OPT_SUBCOMMAND("add", &fn, add),
OPT_SUBCOMMAND("prune", &fn, prune),
OPT_SUBCOMMAND("list", &fn, list),
OPT_SUBCOMMAND("lock", &fn, lock_worktree),
OPT_SUBCOMMAND("unlock", &fn, unlock_worktree),
OPT_SUBCOMMAND("move", &fn, move_worktree),
OPT_SUBCOMMAND("remove", &fn, remove_worktree),
OPT_SUBCOMMAND("repair", &fn, repair),
OPT_END()
};
git_config(git_worktree_config, NULL);
if (!prefix)
prefix = "";
ac = parse_options(ac, av, prefix, options, git_worktree_usage, 0);
return fn(ac, av, prefix);
}