2023-05-16 06:33:57 +00:00
|
|
|
#include "builtin.h"
|
2023-03-21 06:25:58 +00:00
|
|
|
#include "abspath.h"
|
2023-05-17 21:48:52 +00:00
|
|
|
#include "advice.h"
|
2017-11-26 19:43:54 +00:00
|
|
|
#include "checkout.h"
|
2017-06-14 18:07:36 +00:00
|
|
|
#include "config.h"
|
2023-04-22 20:17:12 +00:00
|
|
|
#include "copy.h"
|
2015-06-29 12:51:18 +00:00
|
|
|
#include "dir.h"
|
2023-03-21 06:26:03 +00:00
|
|
|
#include "environment.h"
|
2023-03-21 06:25:54 +00:00
|
|
|
#include "gettext.h"
|
2023-02-24 00:09:27 +00:00
|
|
|
#include "hex.h"
|
2023-04-11 07:41:53 +00:00
|
|
|
#include "object-file.h"
|
2023-04-11 07:41:49 +00:00
|
|
|
#include "object-name.h"
|
2015-06-29 12:51:18 +00:00
|
|
|
#include "parse-options.h"
|
2023-05-16 06:33:59 +00:00
|
|
|
#include "path.h"
|
2020-07-28 20:23:39 +00:00
|
|
|
#include "strvec.h"
|
2015-07-17 23:00:13 +00:00
|
|
|
#include "branch.h"
|
2023-05-16 06:33:56 +00:00
|
|
|
#include "read-cache-ll.h"
|
2015-07-17 23:00:13 +00:00
|
|
|
#include "refs.h"
|
2023-05-17 21:48:58 +00:00
|
|
|
#include "remote.h"
|
2023-04-22 20:17:20 +00:00
|
|
|
#include "repository.h"
|
2015-07-06 17:30:50 +00:00
|
|
|
#include "run-command.h"
|
2021-09-26 19:03:26 +00:00
|
|
|
#include "hook.h"
|
2015-07-06 17:30:55 +00:00
|
|
|
#include "sigchain.h"
|
2019-01-05 05:08:40 +00:00
|
|
|
#include "submodule.h"
|
2015-10-08 17:01:05 +00:00
|
|
|
#include "utf8.h"
|
|
|
|
#include "worktree.h"
|
2021-01-27 08:03:08 +00:00
|
|
|
#include "quote.h"
|
2015-06-29 12:51:18 +00:00
|
|
|
|
2022-10-13 15:39:25 +00:00
|
|
|
#define BUILTIN_WORKTREE_ADD_USAGE \
|
2022-10-13 15:39:26 +00:00
|
|
|
N_("git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n" \
|
2023-05-17 21:48:47 +00:00
|
|
|
" [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]")
|
|
|
|
|
2022-10-13 15:39:25 +00:00
|
|
|
#define BUILTIN_WORKTREE_LIST_USAGE \
|
2022-10-13 15:39:26 +00:00
|
|
|
N_("git worktree list [-v | --porcelain [-z]]")
|
2022-10-13 15:39:25 +00:00
|
|
|
#define BUILTIN_WORKTREE_LOCK_USAGE \
|
2022-10-13 15:39:26 +00:00
|
|
|
N_("git worktree lock [--reason <string>] <worktree>")
|
2022-10-13 15:39:25 +00:00
|
|
|
#define BUILTIN_WORKTREE_MOVE_USAGE \
|
|
|
|
N_("git worktree move <worktree> <new-path>")
|
|
|
|
#define BUILTIN_WORKTREE_PRUNE_USAGE \
|
2022-10-13 15:39:26 +00:00
|
|
|
N_("git worktree prune [-n] [-v] [--expire <expire>]")
|
2022-10-13 15:39:25 +00:00
|
|
|
#define BUILTIN_WORKTREE_REMOVE_USAGE \
|
2022-10-13 15:39:26 +00:00
|
|
|
N_("git worktree remove [-f] <worktree>")
|
2022-10-13 15:39:25 +00:00
|
|
|
#define BUILTIN_WORKTREE_REPAIR_USAGE \
|
|
|
|
N_("git worktree repair [<path>...]")
|
|
|
|
#define BUILTIN_WORKTREE_UNLOCK_USAGE \
|
|
|
|
N_("git worktree unlock <worktree>")
|
|
|
|
|
2023-05-17 21:48:58 +00:00
|
|
|
#define WORKTREE_ADD_DWIM_ORPHAN_INFER_TEXT \
|
|
|
|
_("No possible source branch, inferring '--orphan'")
|
|
|
|
|
2023-05-17 21:48:52 +00:00
|
|
|
#define WORKTREE_ADD_ORPHAN_WITH_DASH_B_HINT_TEXT \
|
2023-11-24 03:10:42 +00:00
|
|
|
_("If you meant to create a worktree containing a new unborn branch\n" \
|
2023-05-17 21:48:52 +00:00
|
|
|
"(branch with no commits) for this repository, you can do so\n" \
|
|
|
|
"using the --orphan flag:\n" \
|
|
|
|
"\n" \
|
2023-07-26 21:42:24 +00:00
|
|
|
" git worktree add --orphan -b %s %s\n")
|
2023-05-17 21:48:52 +00:00
|
|
|
|
|
|
|
#define WORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT \
|
2023-11-24 03:10:42 +00:00
|
|
|
_("If you meant to create a worktree containing a new unborn branch\n" \
|
2023-05-17 21:48:52 +00:00
|
|
|
"(branch with no commits) for this repository, you can do so\n" \
|
|
|
|
"using the --orphan flag:\n" \
|
|
|
|
"\n" \
|
2023-07-26 21:42:24 +00:00
|
|
|
" git worktree add --orphan %s\n")
|
2023-05-17 21:48:52 +00:00
|
|
|
|
2022-10-13 15:39:25 +00:00
|
|
|
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,
|
2015-06-29 12:51:18 +00:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2015-07-17 23:00:07 +00:00
|
|
|
struct add_opts {
|
|
|
|
int force;
|
|
|
|
int detach;
|
2018-08-15 20:56:30 +00:00
|
|
|
int quiet;
|
2016-03-29 10:11:01 +00:00
|
|
|
int checkout;
|
2023-05-17 21:48:47 +00:00
|
|
|
int orphan;
|
2021-07-15 02:32:30 +00:00
|
|
|
const char *keep_locked;
|
2015-07-17 23:00:07 +00:00
|
|
|
};
|
|
|
|
|
2015-06-29 12:51:18 +00:00
|
|
|
static int show_only;
|
|
|
|
static int verbose;
|
2017-11-29 20:04:51 +00:00
|
|
|
static int guess_remote;
|
2017-04-26 19:29:31 +00:00
|
|
|
static timestamp_t expire;
|
2015-06-29 12:51:18 +00:00
|
|
|
|
config: add ctx arg to config_fn_t
Add a new "const struct config_context *ctx" arg to config_fn_t to hold
additional information about the config iteration operation.
config_context has a "struct key_value_info kvi" member that holds
metadata about the config source being read (e.g. what kind of config
source it is, the filename, etc). In this series, we're only interested
in .kvi, so we could have just used "struct key_value_info" as an arg,
but config_context makes it possible to add/adjust members in the future
without changing the config_fn_t signature. We could also consider other
ways of organizing the args (e.g. moving the config name and value into
config_context or key_value_info), but in my experiments, the
incremental benefit doesn't justify the added complexity (e.g. a
config_fn_t will sometimes invoke another config_fn_t but with a
different config value).
In subsequent commits, the .kvi member will replace the global "struct
config_reader" in config.c, making config iteration a global-free
operation. It requires much more work for the machinery to provide
meaningful values of .kvi, so for now, merely change the signature and
call sites, pass NULL as a placeholder value, and don't rely on the arg
in any meaningful way.
Most of the changes are performed by
contrib/coccinelle/config_fn_ctx.pending.cocci, which, for every
config_fn_t:
- Modifies the signature to accept "const struct config_context *ctx"
- Passes "ctx" to any inner config_fn_t, if needed
- Adds UNUSED attributes to "ctx", if needed
Most config_fn_t instances are easily identified by seeing if they are
called by the various config functions. Most of the remaining ones are
manually named in the .cocci patch. Manual cleanups are still needed,
but the majority of it is trivial; it's either adjusting config_fn_t
that the .cocci patch didn't catch, or adding forward declarations of
"struct config_context ctx" to make the signatures make sense.
The non-trivial changes are in cases where we are invoking a config_fn_t
outside of config machinery, and we now need to decide what value of
"ctx" to pass. These cases are:
- trace2/tr2_cfg.c:tr2_cfg_set_fl()
This is indirectly called by git_config_set() so that the trace2
machinery can notice the new config values and update its settings
using the tr2 config parsing function, i.e. tr2_cfg_cb().
- builtin/checkout.c:checkout_main()
This calls git_xmerge_config() as a shorthand for parsing a CLI arg.
This might be worth refactoring away in the future, since
git_xmerge_config() can call git_default_config(), which can do much
more than just parsing.
Handle them by creating a KVI_INIT macro that initializes "struct
key_value_info" to a reasonable default, and use that to construct the
"ctx" arg.
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-06-28 19:26:22 +00:00
|
|
|
static int git_worktree_config(const char *var, const char *value,
|
|
|
|
const struct config_context *ctx, void *cb)
|
2017-11-29 20:04:51 +00:00
|
|
|
{
|
|
|
|
if (!strcmp(var, "worktree.guessremote")) {
|
|
|
|
guess_remote = git_config_bool(var, value);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
config: add ctx arg to config_fn_t
Add a new "const struct config_context *ctx" arg to config_fn_t to hold
additional information about the config iteration operation.
config_context has a "struct key_value_info kvi" member that holds
metadata about the config source being read (e.g. what kind of config
source it is, the filename, etc). In this series, we're only interested
in .kvi, so we could have just used "struct key_value_info" as an arg,
but config_context makes it possible to add/adjust members in the future
without changing the config_fn_t signature. We could also consider other
ways of organizing the args (e.g. moving the config name and value into
config_context or key_value_info), but in my experiments, the
incremental benefit doesn't justify the added complexity (e.g. a
config_fn_t will sometimes invoke another config_fn_t but with a
different config value).
In subsequent commits, the .kvi member will replace the global "struct
config_reader" in config.c, making config iteration a global-free
operation. It requires much more work for the machinery to provide
meaningful values of .kvi, so for now, merely change the signature and
call sites, pass NULL as a placeholder value, and don't rely on the arg
in any meaningful way.
Most of the changes are performed by
contrib/coccinelle/config_fn_ctx.pending.cocci, which, for every
config_fn_t:
- Modifies the signature to accept "const struct config_context *ctx"
- Passes "ctx" to any inner config_fn_t, if needed
- Adds UNUSED attributes to "ctx", if needed
Most config_fn_t instances are easily identified by seeing if they are
called by the various config functions. Most of the remaining ones are
manually named in the .cocci patch. Manual cleanups are still needed,
but the majority of it is trivial; it's either adjusting config_fn_t
that the .cocci patch didn't catch, or adding forward declarations of
"struct config_context ctx" to make the signatures make sense.
The non-trivial changes are in cases where we are invoking a config_fn_t
outside of config machinery, and we now need to decide what value of
"ctx" to pass. These cases are:
- trace2/tr2_cfg.c:tr2_cfg_set_fl()
This is indirectly called by git_config_set() so that the trace2
machinery can notice the new config values and update its settings
using the tr2 config parsing function, i.e. tr2_cfg_cb().
- builtin/checkout.c:checkout_main()
This calls git_xmerge_config() as a shorthand for parsing a CLI arg.
This might be worth refactoring away in the future, since
git_xmerge_config() can call git_default_config(), which can do much
more than just parsing.
Handle them by creating a KVI_INIT macro that initializes "struct
key_value_info" to a reasonable default, and use that to construct the
"ctx" arg.
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-06-28 19:26:22 +00:00
|
|
|
return git_default_config(var, value, ctx, cb);
|
2017-11-29 20:04:51 +00:00
|
|
|
}
|
|
|
|
|
2018-08-28 21:20:20 +00:00
|
|
|
static int delete_git_dir(const char *id)
|
2018-08-28 21:20:19 +00:00
|
|
|
{
|
|
|
|
struct strbuf sb = STRBUF_INIT;
|
2018-08-28 21:20:20 +00:00
|
|
|
int ret;
|
2018-08-28 21:20:19 +00:00
|
|
|
|
2018-08-28 21:20:20 +00:00
|
|
|
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)
|
2018-08-28 21:20:19 +00:00
|
|
|
error_errno(_("failed to delete '%s'"), sb.buf);
|
|
|
|
strbuf_release(&sb);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-08-28 21:20:26 +00:00
|
|
|
static void delete_worktrees_dir_if_empty(void)
|
|
|
|
{
|
|
|
|
rmdir(git_path("worktrees")); /* ignore failed removal */
|
|
|
|
}
|
|
|
|
|
2020-06-10 06:30:45 +00:00
|
|
|
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 03:44:19 +00:00
|
|
|
fprintf_ln(stderr, _("Removing %s/%s: %s"), "worktrees", id, reason);
|
2020-06-10 06:30:45 +00:00
|
|
|
if (!show_only)
|
|
|
|
delete_git_dir(id);
|
|
|
|
}
|
|
|
|
|
2020-06-10 06:30:46 +00:00
|
|
|
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;
|
2020-06-10 06:30:47 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
2020-06-10 06:30:46 +00:00
|
|
|
/* 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");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-29 12:51:18 +00:00
|
|
|
static void prune_worktrees(void)
|
|
|
|
{
|
|
|
|
struct strbuf reason = STRBUF_INIT;
|
2020-06-10 06:30:47 +00:00
|
|
|
struct strbuf main_path = STRBUF_INIT;
|
2023-02-06 23:07:43 +00:00
|
|
|
struct string_list kept = STRING_LIST_INIT_DUP;
|
2015-06-29 12:51:18 +00:00
|
|
|
DIR *dir = opendir(git_path("worktrees"));
|
|
|
|
struct dirent *d;
|
|
|
|
if (!dir)
|
|
|
|
return;
|
2021-05-12 17:28:22 +00:00
|
|
|
while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
|
2020-06-10 06:30:46 +00:00
|
|
|
char *path;
|
2015-06-29 12:51:18 +00:00
|
|
|
strbuf_reset(&reason);
|
2021-01-19 21:27:33 +00:00
|
|
|
if (should_prune_worktree(d->d_name, &reason, &path, expire))
|
2020-06-10 06:30:46 +00:00
|
|
|
prune_worktree(d->d_name, reason.buf);
|
|
|
|
else if (path)
|
2023-02-06 23:07:43 +00:00
|
|
|
string_list_append_nodup(&kept, path)->util = xstrdup(d->d_name);
|
2015-06-29 12:51:18 +00:00
|
|
|
}
|
|
|
|
closedir(dir);
|
2020-06-10 06:30:46 +00:00
|
|
|
|
2020-06-10 06:30:47 +00:00
|
|
|
strbuf_add_absolute_path(&main_path, get_git_common_dir());
|
|
|
|
/* massage main worktree absolute path to match 'gitdir' content */
|
|
|
|
strbuf_strip_suffix(&main_path, "/.");
|
2023-02-06 23:07:43 +00:00
|
|
|
string_list_append_nodup(&kept, strbuf_detach(&main_path, NULL));
|
2020-06-10 06:30:46 +00:00
|
|
|
prune_dups(&kept);
|
|
|
|
string_list_clear(&kept, 1);
|
|
|
|
|
2015-06-29 12:51:18 +00:00
|
|
|
if (!show_only)
|
2018-08-28 21:20:26 +00:00
|
|
|
delete_worktrees_dir_if_empty();
|
2015-06-29 12:51:18 +00:00
|
|
|
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")),
|
2017-02-06 13:13:59 +00:00
|
|
|
OPT__VERBOSE(&verbose, N_("report pruned working trees")),
|
2015-06-29 12:51:18 +00:00
|
|
|
OPT_EXPIRY_DATE(0, "expire", &expire,
|
2017-02-06 13:13:59 +00:00
|
|
|
N_("expire working trees older than <time>")),
|
2015-06-29 12:51:18 +00:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2017-04-26 19:29:31 +00:00
|
|
|
expire = TIME_MAX;
|
2022-10-13 15:39:25 +00:00
|
|
|
ac = parse_options(ac, av, prefix, options, git_worktree_prune_usage,
|
|
|
|
0);
|
2015-06-29 12:51:18 +00:00
|
|
|
if (ac)
|
2022-10-13 15:39:25 +00:00
|
|
|
usage_with_options(git_worktree_prune_usage, options);
|
2015-06-29 12:51:18 +00:00
|
|
|
prune_worktrees();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-07-06 17:30:55 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-07-06 17:30:57 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-06-10 06:30:48 +00:00
|
|
|
/* check that path is viable location for worktree */
|
|
|
|
static void check_candidate_path(const char *path,
|
|
|
|
int force,
|
|
|
|
struct worktree **worktrees,
|
|
|
|
const char *cmd)
|
2018-08-28 21:20:21 +00:00
|
|
|
{
|
2018-08-28 21:20:22 +00:00
|
|
|
struct worktree *wt;
|
|
|
|
int locked;
|
|
|
|
|
2018-08-28 21:20:21 +00:00
|
|
|
if (file_exists(path) && !is_empty_dir(path))
|
|
|
|
die(_("'%s' already exists"), path);
|
2018-08-28 21:20:22 +00: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 09:08:48 +00:00
|
|
|
wt = find_worktree_by_path(worktrees, path);
|
2018-08-28 21:20:22 +00:00
|
|
|
if (!wt)
|
2020-06-10 06:30:48 +00:00
|
|
|
return;
|
2018-08-28 21:20:22 +00:00
|
|
|
|
2018-10-30 06:24:09 +00:00
|
|
|
locked = !!worktree_lock_reason(wt);
|
2020-06-10 06:30:48 +00:00
|
|
|
if ((!locked && force) || (locked && force > 1)) {
|
2018-08-28 21:20:23 +00:00
|
|
|
if (delete_git_dir(wt->id))
|
2020-06-10 06:30:48 +00:00
|
|
|
die(_("unusable worktree destination '%s'"), path);
|
|
|
|
return;
|
2018-08-28 21:20:23 +00:00
|
|
|
}
|
|
|
|
|
2018-08-28 21:20:22 +00:00
|
|
|
if (locked)
|
2020-11-20 15:09:39 +00:00
|
|
|
die(_("'%s' is a missing but locked worktree;\nuse '%s -f -f' to override, or 'unlock' and 'prune' or 'remove' to clear"), path, cmd);
|
2018-08-28 21:20:22 +00:00
|
|
|
else
|
2020-11-20 15:09:39 +00:00
|
|
|
die(_("'%s' is a missing but already registered worktree;\nuse '%s -f' to override, or 'prune' or 'remove' to clear"), path, cmd);
|
2018-08-28 21:20:21 +00:00
|
|
|
}
|
|
|
|
|
2022-02-23 14:29:11 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-02-23 14:29:10 +00:00
|
|
|
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 } };
|
|
|
|
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);
|
config API: add and use a "git_config_get()" family of functions
We already have the basic "git_config_get_value()" function and its
"repo_*" and "configset" siblings to get a given "key" and assign the
last key found to a provided "value".
But some callers don't care about that value, but just want to use the
return value of the "get_value()" function to check whether the key
exist (or another non-zero return value).
The immediate motivation for this is that a subsequent commit will
need to change all callers of the "*_get_value_multi()" family of
functions. In two cases here we (ab)used it to check whether we had
any values for the given key, but didn't care about the return value.
The rest of the callers here used various other config API functions
to do the same, all of which resolved to the same underlying functions
to provide the answer.
Some of these were using either git_config_get_string() or
git_config_get_string_tmp(), see fe4c750fb13 (submodule--helper: fix a
configure_added_submodule() leak, 2022-09-01) for a recent example. We
can now use a helper function that doesn't require a throwaway
variable.
We could have changed git_configset_get_value_multi() (and then
git_config_get_value() etc.) to accept a "NULL" as a "dest" for all
callers, but let's avoid changing the behavior of existing API
users. Having an "unused" value that we throw away internal to
config.c is cheap.
A "NULL as optional dest" pattern is also more fragile, as the intent
of the caller might be misinterpreted if he were to accidentally pass
"NULL", e.g. when "dest" is passed in from another function.
Another name for this function could have been
"*_config_key_exists()", as suggested in [1]. That would work for all
of these callers, and would currently be equivalent to this function,
as the git_configset_get_value() API normalizes all non-zero return
values to a "1".
But adding that API would set us up to lose information, as e.g. if
git_config_parse_key() in the underlying configset_find_element()
fails we'd like to return -1, not 1.
Let's change the underlying configset_find_element() function to
support this use-case, we'll make further use of it in a subsequent
commit where the git_configset_get_value_multi() function itself will
expose this new return value.
This still leaves various inconsistencies and clobbering or ignoring
of the return value in place. E.g here we're modifying
configset_add_value(), but ever since it was added in [2] we've been
ignoring its "int" return value, but as we're changing the
configset_find_element() it uses, let's have it faithfully ferry that
"ret" along.
Let's also use the "RESULT_MUST_BE_USED" macro introduced in [3] to
assert that we're checking the return value of
configset_find_element().
We're leaving the same change to configset_add_value() for some future
series. Once we start paying attention to its return value we'd need
to ferry it up as deep as do_config_from(), and would need to make
least read_{,very_}early_config() and git_protected_config() return an
"int" instead of "void". Let's leave that for now, and focus on
the *_get_*() functions.
1. 3c8687a73ee (add `config_set` API for caching config-like files, 2014-07-28)
2. https://lore.kernel.org/git/xmqqczadkq9f.fsf@gitster.g/
3. 1e8697b5c4e (submodule--helper: check repo{_submodule,}_init()
return values, 2022-09-01),
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-03-28 14:04:22 +00:00
|
|
|
if (!git_configset_get(&cs, "core.worktree") &&
|
2022-02-23 14:29:10 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-02-23 14:29:12 +00:00
|
|
|
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");
|
2022-06-02 09:09:50 +00:00
|
|
|
strvec_pushv(&cp.env, child_env->v);
|
2022-02-23 14:29:12 +00:00
|
|
|
return run_command(&cp);
|
|
|
|
}
|
|
|
|
|
2023-05-17 21:48:47 +00:00
|
|
|
static int make_worktree_orphan(const char * ref, const struct add_opts *opts,
|
|
|
|
struct strvec *child_env)
|
|
|
|
{
|
|
|
|
struct strbuf symref = STRBUF_INIT;
|
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
|
|
|
|
|
|
|
validate_new_branchname(ref, &symref, 0);
|
|
|
|
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);
|
|
|
|
strbuf_release(&symref);
|
|
|
|
cp.git_cmd = 1;
|
|
|
|
return run_command(&cp);
|
|
|
|
}
|
|
|
|
|
2015-07-17 23:00:12 +00:00
|
|
|
static int add_worktree(const char *path, const char *refname,
|
2015-07-17 23:00:07 +00:00
|
|
|
const struct add_opts *opts)
|
2015-07-06 17:30:55 +00:00
|
|
|
{
|
|
|
|
struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
|
2020-03-10 13:11:22 +00:00
|
|
|
struct strbuf sb = STRBUF_INIT, realpath = STRBUF_INIT;
|
2015-07-06 17:30:55 +00:00
|
|
|
const char *name;
|
2020-07-28 20:24:27 +00:00
|
|
|
struct strvec child_env = STRVEC_INIT;
|
2019-02-20 16:16:48 +00:00
|
|
|
unsigned int counter = 0;
|
|
|
|
int len, ret;
|
2015-07-17 23:00:13 +00:00
|
|
|
struct strbuf symref = STRBUF_INIT;
|
|
|
|
struct commit *commit = NULL;
|
2017-12-07 21:20:17 +00:00
|
|
|
int is_branch = 0;
|
2019-03-08 09:28:34 +00:00
|
|
|
struct strbuf sb_name = STRBUF_INIT;
|
2024-01-08 10:05:47 +00:00
|
|
|
struct worktree **worktrees, *wt = NULL;
|
|
|
|
struct ref_store *wt_refs;
|
2015-07-06 17:30:55 +00:00
|
|
|
|
2020-06-19 23:35:44 +00:00
|
|
|
worktrees = get_worktrees();
|
2020-06-10 06:30:48 +00:00
|
|
|
check_candidate_path(path, opts->force, worktrees, "add");
|
|
|
|
free_worktrees(worktrees);
|
|
|
|
worktrees = NULL;
|
2015-07-06 17:30:55 +00:00
|
|
|
|
2015-07-17 23:00:13 +00:00
|
|
|
/* is 'refname' a branch or commit? */
|
2016-02-15 13:35:32 +00:00
|
|
|
if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
|
2017-12-07 21:20:17 +00:00
|
|
|
ref_exists(symref.buf)) {
|
|
|
|
is_branch = 1;
|
2015-07-17 23:00:13 +00:00
|
|
|
if (!opts->force)
|
2016-04-22 13:01:33 +00:00
|
|
|
die_if_checked_out(symref.buf, 0);
|
2015-07-17 23:00:13 +00:00
|
|
|
}
|
2017-12-07 21:20:17 +00:00
|
|
|
commit = lookup_commit_reference_by_name(refname);
|
2023-05-17 21:48:47 +00:00
|
|
|
if (!commit && !opts->orphan)
|
2017-12-07 21:20:17 +00:00
|
|
|
die(_("invalid reference: %s"), refname);
|
2015-07-17 23:00:13 +00:00
|
|
|
|
2015-07-06 17:30:57 +00:00
|
|
|
name = worktree_basename(path, &len);
|
2019-03-08 09:28:34 +00:00
|
|
|
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);
|
2015-07-06 17:30:55 +00:00
|
|
|
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);
|
2019-02-20 16:16:48 +00:00
|
|
|
|
|
|
|
while (mkdir(sb_repo.buf, 0777)) {
|
2015-07-06 17:30:55 +00:00
|
|
|
counter++;
|
2019-02-20 16:16:48 +00:00
|
|
|
if ((errno != EEXIST) || !counter /* overflow */)
|
|
|
|
die_errno(_("could not create directory of '%s'"),
|
|
|
|
sb_repo.buf);
|
2015-07-06 17:30:55 +00:00
|
|
|
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);
|
2021-07-15 02:32:30 +00:00
|
|
|
if (opts->keep_locked)
|
|
|
|
write_file(sb.buf, "%s", opts->keep_locked);
|
2017-04-12 13:58:05 +00:00
|
|
|
else
|
2021-07-15 02:32:30 +00:00
|
|
|
write_file(sb.buf, _("initializing"));
|
2015-07-06 17:30:55 +00:00
|
|
|
|
|
|
|
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);
|
2020-03-10 13:11:22 +00:00
|
|
|
strbuf_realpath(&realpath, sb_git.buf, 1);
|
|
|
|
write_file(sb.buf, "%s", realpath.buf);
|
|
|
|
strbuf_realpath(&realpath, get_git_common_dir(), 1);
|
2015-08-24 20:20:39 +00:00
|
|
|
write_file(sb_git.buf, "gitdir: %s/worktrees/%s",
|
2020-03-10 13:11:22 +00:00
|
|
|
realpath.buf, name);
|
2015-07-06 17:30:55 +00:00
|
|
|
strbuf_reset(&sb);
|
|
|
|
strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
|
2015-08-24 20:20:39 +00:00
|
|
|
write_file(sb.buf, "../..");
|
2015-07-06 17:30:55 +00:00
|
|
|
|
|
|
|
/*
|
2024-01-08 10:05:47 +00:00
|
|
|
* Set up the ref store of the worktree and create the HEAD reference.
|
2015-07-06 17:30:55 +00:00
|
|
|
*/
|
2024-01-08 10:05:47 +00:00
|
|
|
wt = get_linked_worktree(name, 1);
|
|
|
|
if (!wt) {
|
|
|
|
ret = error(_("could not find created worktree '%s'"), name);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
wt_refs = get_worktree_ref_store(wt);
|
|
|
|
|
|
|
|
ret = refs_init_db(wt_refs, REFS_INIT_DB_IS_WORKTREE, &sb);
|
|
|
|
if (ret)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
if (!is_branch && commit)
|
|
|
|
ret = refs_update_ref(wt_refs, NULL, "HEAD", &commit->object.oid,
|
|
|
|
NULL, 0, UPDATE_REFS_MSG_ON_ERR);
|
|
|
|
else
|
|
|
|
ret = refs_create_symref(wt_refs, "HEAD", symref.buf, NULL);
|
|
|
|
if (ret)
|
|
|
|
goto done;
|
2015-07-06 17:30:55 +00:00
|
|
|
|
2022-02-07 21:33:02 +00:00
|
|
|
/*
|
|
|
|
* If the current worktree has sparse-checkout enabled, then copy
|
|
|
|
* the sparse-checkout patterns from the current worktree.
|
|
|
|
*/
|
2022-02-23 14:29:11 +00:00
|
|
|
if (core_apply_sparse_checkout)
|
|
|
|
copy_sparse_checkout(sb_repo.buf);
|
2022-02-07 21:33:02 +00: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.
|
|
|
|
*/
|
2023-05-26 01:33:00 +00:00
|
|
|
if (the_repository->repository_format_worktree_config)
|
2022-02-23 14:29:10 +00:00
|
|
|
copy_filtered_worktree_config(sb_repo.buf);
|
2022-02-07 21:33:02 +00:00
|
|
|
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
|
|
|
|
strvec_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
|
2015-07-17 23:00:14 +00:00
|
|
|
|
2023-05-17 21:48:47 +00:00
|
|
|
if (opts->orphan &&
|
|
|
|
(ret = make_worktree_orphan(refname, opts, &child_env)))
|
|
|
|
goto done;
|
|
|
|
|
2022-02-23 14:29:12 +00:00
|
|
|
if (opts->checkout &&
|
|
|
|
(ret = checkout_worktree(opts, &child_env)))
|
|
|
|
goto done;
|
2016-03-29 10:11:01 +00:00
|
|
|
|
|
|
|
is_junk = 0;
|
2017-06-15 23:15:49 +00:00
|
|
|
FREE_AND_NULL(junk_work_tree);
|
|
|
|
FREE_AND_NULL(junk_git_dir);
|
2016-03-29 10:11:01 +00:00
|
|
|
|
2015-07-17 23:00:14 +00:00
|
|
|
done:
|
2017-04-12 13:58:05 +00:00
|
|
|
if (ret || !opts->keep_locked) {
|
|
|
|
strbuf_reset(&sb);
|
|
|
|
strbuf_addf(&sb, "%s/locked", sb_repo.buf);
|
|
|
|
unlink_or_warn(sb.buf);
|
|
|
|
}
|
2017-12-07 21:20:17 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Hook failure does not warrant worktree deletion, so run hook after
|
|
|
|
* is_junk is cleared, but do return appropriate code when hook fails.
|
|
|
|
*/
|
2023-05-17 21:48:47 +00:00
|
|
|
if (!ret && opts->checkout && !opts->orphan) {
|
2021-12-22 03:59:36 +00:00
|
|
|
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 19:18:41 +00:00
|
|
|
}
|
2017-12-07 21:20:17 +00:00
|
|
|
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_clear(&child_env);
|
2015-07-06 17:30:55 +00:00
|
|
|
strbuf_release(&sb);
|
2015-07-17 23:00:13 +00:00
|
|
|
strbuf_release(&symref);
|
2015-07-06 17:30:55 +00:00
|
|
|
strbuf_release(&sb_repo);
|
|
|
|
strbuf_release(&sb_git);
|
2019-03-08 09:28:34 +00:00
|
|
|
strbuf_release(&sb_name);
|
2020-03-10 13:11:22 +00:00
|
|
|
strbuf_release(&realpath);
|
2024-01-08 10:05:47 +00:00
|
|
|
free_worktree(wt);
|
2015-07-06 17:30:55 +00:00
|
|
|
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 21:56:33 +00: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 03:44:19 +00: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 21:56:33 +00: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 03:44:19 +00: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 21:56:33 +00:00
|
|
|
new_branch,
|
2023-03-28 13:58:46 +00:00
|
|
|
repo_find_unique_abbrev(the_repository, &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 21:56:33 +00: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 03:44:19 +00: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 21:56:33 +00: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 03:44:19 +00: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 21:56:33 +00:00
|
|
|
branch);
|
|
|
|
else {
|
|
|
|
struct commit *commit = lookup_commit_reference_by_name(branch);
|
|
|
|
if (!commit)
|
2023-05-17 21:48:47 +00:00
|
|
|
BUG(_("unreachable: 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 03:44:19 +00:00
|
|
|
fprintf_ln(stderr, _("Preparing worktree (detached HEAD %s)"),
|
2023-03-28 13:58:46 +00:00
|
|
|
repo_find_unique_abbrev(the_repository, &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 21:56:33 +00:00
|
|
|
}
|
|
|
|
strbuf_release(&s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-17 21:48:58 +00:00
|
|
|
/**
|
|
|
|
* Callback to short circuit iteration over refs on the first reference
|
|
|
|
* corresponding to a valid oid.
|
|
|
|
*
|
|
|
|
* Returns 0 on failure and non-zero on success.
|
|
|
|
*/
|
2023-08-29 23:45:15 +00:00
|
|
|
static int first_valid_ref(const char *refname UNUSED,
|
|
|
|
const struct object_id *oid UNUSED,
|
|
|
|
int flags UNUSED,
|
|
|
|
void *cb_data UNUSED)
|
2023-05-17 21:48:58 +00:00
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Verifies HEAD and determines whether there exist any valid local references.
|
|
|
|
*
|
|
|
|
* - Checks whether HEAD points to a valid reference.
|
|
|
|
*
|
|
|
|
* - Checks whether any valid local branches exist.
|
|
|
|
*
|
2023-05-17 21:49:06 +00:00
|
|
|
* - Emits a warning if there exist any valid branches but HEAD does not point
|
|
|
|
* to a valid reference.
|
|
|
|
*
|
2023-05-17 21:48:58 +00:00
|
|
|
* Returns 1 if any of the previous checks are true, otherwise returns 0.
|
|
|
|
*/
|
|
|
|
static int can_use_local_refs(const struct add_opts *opts)
|
|
|
|
{
|
|
|
|
if (head_ref(first_valid_ref, NULL)) {
|
|
|
|
return 1;
|
|
|
|
} else if (for_each_branch_ref(first_valid_ref, NULL)) {
|
2023-05-17 21:49:06 +00:00
|
|
|
if (!opts->quiet) {
|
|
|
|
struct strbuf path = STRBUF_INIT;
|
|
|
|
struct strbuf contents = STRBUF_INIT;
|
|
|
|
|
|
|
|
strbuf_add_real_path(&path, get_worktree_git_dir(NULL));
|
|
|
|
strbuf_addstr(&path, "/HEAD");
|
|
|
|
strbuf_read_file(&contents, path.buf, 64);
|
|
|
|
strbuf_stripspace(&contents, 0);
|
|
|
|
strbuf_strip_suffix(&contents, "\n");
|
|
|
|
|
|
|
|
warning(_("HEAD points to an invalid (or orphaned) reference.\n"
|
|
|
|
"HEAD path: '%s'\n"
|
|
|
|
"HEAD contents: '%s'"),
|
|
|
|
path.buf, contents.buf);
|
|
|
|
strbuf_release(&path);
|
|
|
|
strbuf_release(&contents);
|
|
|
|
}
|
2023-05-17 21:48:58 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reports whether the necessary flags were set and whether the repository has
|
|
|
|
* remote references to attempt DWIM tracking of upstream branches.
|
|
|
|
*
|
|
|
|
* 1. Checks that `--guess-remote` was used or `worktree.guessRemote = true`.
|
|
|
|
*
|
|
|
|
* 2. Checks whether any valid remote branches exist.
|
|
|
|
*
|
|
|
|
* 3. Checks that there exists at least one remote and emits a warning/error
|
|
|
|
* if both checks 1. and 2. are false (can be bypassed with `--force`).
|
|
|
|
*
|
|
|
|
* Returns 1 if checks 1. and 2. are true, otherwise 0.
|
|
|
|
*/
|
|
|
|
static int can_use_remote_refs(const struct add_opts *opts)
|
|
|
|
{
|
|
|
|
if (!guess_remote) {
|
|
|
|
return 0;
|
|
|
|
} else if (for_each_remote_ref(first_valid_ref, NULL)) {
|
|
|
|
return 1;
|
|
|
|
} else if (!opts->force && remote_get(NULL)) {
|
|
|
|
die(_("No local or remote refs exist despite at least one remote\n"
|
2023-08-11 23:39:51 +00:00
|
|
|
"present, stopping; use 'add -f' to override or fetch a remote first"));
|
2023-05-17 21:48:58 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determines whether `--orphan` should be inferred in the evaluation of
|
|
|
|
* `worktree add path/` or `worktree add -b branch path/` and emits an error
|
|
|
|
* if the supplied arguments would produce an illegal combination when the
|
|
|
|
* `--orphan` flag is included.
|
|
|
|
*
|
|
|
|
* `opts` and `opt_track` contain the other options & flags supplied to the
|
|
|
|
* command.
|
|
|
|
*
|
|
|
|
* remote determines whether to check `can_use_remote_refs()` or not. This
|
|
|
|
* is primarily to differentiate between the basic `add` DWIM and `add -b`.
|
|
|
|
*
|
|
|
|
* Returns 1 when inferring `--orphan`, 0 otherwise, and emits an error when
|
|
|
|
* `--orphan` is inferred but doing so produces an illegal combination of
|
|
|
|
* options and flags. Additionally produces an error when remote refs are
|
|
|
|
* checked and the repo is in a state that looks like the user added a remote
|
|
|
|
* but forgot to fetch (and did not override the warning with -f).
|
|
|
|
*/
|
|
|
|
static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote)
|
|
|
|
{
|
|
|
|
if (can_use_local_refs(opts)) {
|
|
|
|
return 0;
|
|
|
|
} else if (remote && can_use_remote_refs(opts)) {
|
|
|
|
return 0;
|
|
|
|
} else if (!opts->quiet) {
|
|
|
|
fprintf_ln(stderr, WORKTREE_ADD_DWIM_ORPHAN_INFER_TEXT);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opt_track) {
|
2023-12-06 11:52:00 +00:00
|
|
|
die(_("options '%s' and '%s' cannot be used together"),
|
|
|
|
"--orphan", "--track");
|
2023-05-17 21:48:58 +00:00
|
|
|
} else if (!opts->checkout) {
|
2023-12-06 11:52:00 +00:00
|
|
|
die(_("options '%s' and '%s' cannot be used together"),
|
|
|
|
"--orphan", "--no-checkout");
|
2023-05-17 21:48:58 +00:00
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-04-24 21:56:34 +00:00
|
|
|
static const char *dwim_branch(const char *path, const char **new_branch)
|
|
|
|
{
|
|
|
|
int n;
|
2021-03-14 18:47:37 +00:00
|
|
|
int branch_exists;
|
2018-04-24 21:56:34 +00:00
|
|
|
const char *s = worktree_basename(path, &n);
|
2018-04-24 21:56:35 +00:00
|
|
|
const char *branchname = xstrndup(s, n);
|
|
|
|
struct strbuf ref = STRBUF_INIT;
|
|
|
|
|
|
|
|
UNLEAK(branchname);
|
2021-03-14 18:47:37 +00:00
|
|
|
|
|
|
|
branch_exists = !strbuf_check_branch_ref(&ref, branchname) &&
|
|
|
|
ref_exists(ref.buf);
|
|
|
|
strbuf_release(&ref);
|
|
|
|
if (branch_exists)
|
2018-04-24 21:56:35 +00:00
|
|
|
return branchname;
|
|
|
|
|
|
|
|
*new_branch = branchname;
|
2018-04-24 21:56:34 +00:00
|
|
|
if (guess_remote) {
|
|
|
|
struct object_id oid;
|
|
|
|
const char *remote =
|
2018-06-05 14:40:46 +00:00
|
|
|
unique_tracking_name(*new_branch, &oid, NULL);
|
2018-04-24 21:56:34 +00:00
|
|
|
return remote;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-07-06 17:30:50 +00:00
|
|
|
static int add(int ac, const char **av, const char *prefix)
|
|
|
|
{
|
2015-07-17 23:00:07 +00:00
|
|
|
struct add_opts opts;
|
|
|
|
const char *new_branch_force = NULL;
|
2017-03-21 01:28:49 +00:00
|
|
|
char *path;
|
|
|
|
const char *branch;
|
2018-04-24 21:56:32 +00:00
|
|
|
const char *new_branch = NULL;
|
2017-11-26 19:43:53 +00:00
|
|
|
const char *opt_track = NULL;
|
2021-07-15 02:32:30 +00:00
|
|
|
const char *lock_reason = NULL;
|
|
|
|
int keep_locked = 0;
|
2023-05-17 21:48:52 +00:00
|
|
|
int used_new_branch_options;
|
2015-07-06 17:30:50 +00:00
|
|
|
struct option options[] = {
|
2018-02-09 11:01:42 +00:00
|
|
|
OPT__FORCE(&opts.force,
|
|
|
|
N_("checkout <branch> even if already checked out in other worktree"),
|
2018-02-09 11:02:20 +00:00
|
|
|
PARSE_OPT_NOCOMPLETE),
|
2018-04-24 21:56:32 +00:00
|
|
|
OPT_STRING('b', NULL, &new_branch, N_("branch"),
|
2015-07-06 17:30:53 +00:00
|
|
|
N_("create a new branch")),
|
|
|
|
OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
|
|
|
|
N_("create or reset a branch")),
|
2023-11-24 03:10:42 +00:00
|
|
|
OPT_BOOL(0, "orphan", &opts.orphan, N_("create unborn branch")),
|
2020-09-07 00:02:21 +00:00
|
|
|
OPT_BOOL('d', "detach", &opts.detach, N_("detach HEAD at named commit")),
|
2016-03-29 10:11:01 +00:00
|
|
|
OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
|
2021-07-15 02:32:30 +00:00
|
|
|
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")),
|
2018-08-15 20:56:30 +00:00
|
|
|
OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
|
2017-11-26 19:43:53 +00:00
|
|
|
OPT_PASSTHRU(0, "track", &opt_track, NULL,
|
|
|
|
N_("set up tracking mode (see git-branch(1))"),
|
|
|
|
PARSE_OPT_NOARG | PARSE_OPT_OPTARG),
|
2017-11-29 20:04:50 +00:00
|
|
|
OPT_BOOL(0, "guess-remote", &guess_remote,
|
|
|
|
N_("try to match the new branch name with a remote-tracking branch")),
|
2015-07-06 17:30:50 +00:00
|
|
|
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 18:17:51 +00:00
|
|
|
int ret;
|
2015-07-06 17:30:50 +00:00
|
|
|
|
2015-07-17 23:00:07 +00:00
|
|
|
memset(&opts, 0, sizeof(opts));
|
2016-03-29 10:11:01 +00:00
|
|
|
opts.checkout = 1;
|
2022-10-13 15:39:25 +00:00
|
|
|
ac = parse_options(ac, av, prefix, options, git_worktree_add_usage, 0);
|
2018-04-24 21:56:32 +00:00
|
|
|
if (!!opts.detach + !!new_branch + !!new_branch_force > 1)
|
2022-01-05 20:02:15 +00:00
|
|
|
die(_("options '%s', '%s', and '%s' cannot be used together"), "-b", "-B", "--detach");
|
2023-05-17 21:48:47 +00:00
|
|
|
if (opts.detach && opts.orphan)
|
2023-12-06 11:52:00 +00:00
|
|
|
die(_("options '%s' and '%s' cannot be used together"),
|
2023-05-17 21:48:47 +00:00
|
|
|
"--orphan", "--detach");
|
|
|
|
if (opts.orphan && opt_track)
|
2023-12-06 11:52:00 +00:00
|
|
|
die(_("options '%s' and '%s' cannot be used together"),
|
|
|
|
"--orphan", "--track");
|
2023-05-17 21:48:47 +00:00
|
|
|
if (opts.orphan && !opts.checkout)
|
2023-12-06 11:52:00 +00:00
|
|
|
die(_("options '%s' and '%s' cannot be used together"),
|
|
|
|
"--orphan", "--no-checkout");
|
2023-05-17 21:48:47 +00:00
|
|
|
if (opts.orphan && ac == 2)
|
2023-12-06 11:52:01 +00:00
|
|
|
die(_("option '%s' and commit-ish cannot be used together"),
|
|
|
|
"--orphan");
|
2021-07-15 02:32:30 +00:00
|
|
|
if (lock_reason && !keep_locked)
|
2022-01-05 20:02:19 +00:00
|
|
|
die(_("the option '%s' requires '%s'"), "--reason", "--lock");
|
2021-07-15 02:32:30 +00:00
|
|
|
if (lock_reason)
|
|
|
|
opts.keep_locked = lock_reason;
|
|
|
|
else if (keep_locked)
|
|
|
|
opts.keep_locked = _("added with --lock");
|
|
|
|
|
2015-07-06 17:30:58 +00:00
|
|
|
if (ac < 1 || ac > 2)
|
2022-10-13 15:39:25 +00:00
|
|
|
usage_with_options(git_worktree_add_usage, options);
|
2015-07-06 17:30:50 +00:00
|
|
|
|
2017-03-21 01:22:28 +00:00
|
|
|
path = prefix_filename(prefix, av[0]);
|
2015-07-06 17:30:58 +00:00
|
|
|
branch = ac < 2 ? "HEAD" : av[1];
|
2023-05-17 21:48:52 +00:00
|
|
|
used_new_branch_options = new_branch || new_branch_force;
|
2015-07-06 17:30:50 +00:00
|
|
|
|
2016-05-27 13:17:08 +00:00
|
|
|
if (!strcmp(branch, "-"))
|
|
|
|
branch = "@{-1}";
|
|
|
|
|
2018-04-24 21:56:32 +00:00
|
|
|
if (new_branch_force) {
|
2016-02-15 13:35:33 +00:00
|
|
|
struct strbuf symref = STRBUF_INIT;
|
|
|
|
|
2018-04-24 21:56:32 +00:00
|
|
|
new_branch = new_branch_force;
|
2015-07-17 23:00:06 +00:00
|
|
|
|
2016-02-15 13:35:33 +00:00
|
|
|
if (!opts.force &&
|
2018-04-24 21:56:32 +00:00
|
|
|
!strbuf_check_branch_ref(&symref, new_branch) &&
|
2016-02-15 13:35:33 +00:00
|
|
|
ref_exists(symref.buf))
|
2016-04-22 13:01:33 +00:00
|
|
|
die_if_checked_out(symref.buf, 0);
|
2016-02-15 13:35:33 +00:00
|
|
|
strbuf_release(&symref);
|
|
|
|
}
|
|
|
|
|
2023-05-17 21:48:47 +00:00
|
|
|
if (opts.orphan && !new_branch) {
|
|
|
|
int n;
|
|
|
|
const char *s = worktree_basename(path, &n);
|
|
|
|
new_branch = xstrndup(s, n);
|
2023-05-17 21:49:06 +00:00
|
|
|
} else if (opts.orphan) {
|
2024-01-29 20:28:37 +00:00
|
|
|
; /* no-op */
|
2023-05-17 21:49:06 +00:00
|
|
|
} else if (opts.detach) {
|
2024-01-29 20:28:37 +00:00
|
|
|
/* Check HEAD */
|
2023-05-17 21:49:06 +00:00
|
|
|
if (!strcmp(branch, "HEAD"))
|
|
|
|
can_use_local_refs(&opts);
|
2023-05-17 21:48:58 +00:00
|
|
|
} else if (ac < 2 && new_branch) {
|
2024-01-29 20:28:37 +00:00
|
|
|
/* DWIM: Infer --orphan when repo has no refs. */
|
2023-05-17 21:48:58 +00:00
|
|
|
opts.orphan = dwim_orphan(&opts, !!opt_track, 0);
|
2023-05-17 21:48:47 +00:00
|
|
|
} else if (ac < 2) {
|
2024-01-29 20:28:37 +00:00
|
|
|
/* DWIM: Guess branch name from path. */
|
2018-04-24 21:56:34 +00:00
|
|
|
const char *s = dwim_branch(path, &new_branch);
|
|
|
|
if (s)
|
|
|
|
branch = s;
|
2015-07-06 17:30:59 +00:00
|
|
|
|
2024-01-29 20:28:37 +00:00
|
|
|
/* DWIM: Infer --orphan when repo has no refs. */
|
2023-05-17 21:48:58 +00:00
|
|
|
opts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1);
|
2023-05-17 21:48:47 +00:00
|
|
|
} else if (ac == 2) {
|
2017-11-26 19:43:54 +00:00
|
|
|
struct object_id oid;
|
|
|
|
struct commit *commit;
|
|
|
|
const char *remote;
|
|
|
|
|
|
|
|
commit = lookup_commit_reference_by_name(branch);
|
|
|
|
if (!commit) {
|
2018-06-05 14:40:46 +00:00
|
|
|
remote = unique_tracking_name(branch, &oid, NULL);
|
2017-11-26 19:43:54 +00:00
|
|
|
if (remote) {
|
2018-04-24 21:56:32 +00:00
|
|
|
new_branch = branch;
|
2017-11-26 19:43:54 +00:00
|
|
|
branch = remote;
|
|
|
|
}
|
|
|
|
}
|
2023-05-17 21:49:06 +00:00
|
|
|
|
|
|
|
if (!strcmp(branch, "HEAD"))
|
|
|
|
can_use_local_refs(&opts);
|
|
|
|
|
2017-11-26 19:43:54 +00:00
|
|
|
}
|
2023-05-17 21:48:47 +00:00
|
|
|
|
|
|
|
if (!opts.orphan && !lookup_commit_reference_by_name(branch)) {
|
2023-05-17 21:48:52 +00:00
|
|
|
int attempt_hint = !opts.quiet && (ac < 2);
|
|
|
|
if (attempt_hint && used_new_branch_options) {
|
|
|
|
advise_if_enabled(ADVICE_WORKTREE_ADD_ORPHAN,
|
|
|
|
WORKTREE_ADD_ORPHAN_WITH_DASH_B_HINT_TEXT,
|
|
|
|
new_branch, path);
|
|
|
|
} else if (attempt_hint) {
|
|
|
|
advise_if_enabled(ADVICE_WORKTREE_ADD_ORPHAN,
|
|
|
|
WORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT, path);
|
|
|
|
}
|
2023-05-17 21:48:47 +00:00
|
|
|
die(_("invalid reference: %s"), branch);
|
|
|
|
}
|
|
|
|
|
2018-08-15 20:56:30 +00:00
|
|
|
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 21:56:33 +00:00
|
|
|
|
2023-05-17 21:48:47 +00:00
|
|
|
if (opts.orphan) {
|
|
|
|
branch = new_branch;
|
|
|
|
} else if (new_branch) {
|
2016-08-05 20:38:44 +00:00
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
2015-07-17 23:00:10 +00:00
|
|
|
cp.git_cmd = 1;
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_push(&cp.args, "branch");
|
2018-04-24 21:56:32 +00:00
|
|
|
if (new_branch_force)
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_push(&cp.args, "--force");
|
2018-08-15 20:56:30 +00:00
|
|
|
if (opts.quiet)
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_push(&cp.args, "--quiet");
|
|
|
|
strvec_push(&cp.args, new_branch);
|
|
|
|
strvec_push(&cp.args, branch);
|
2017-11-26 19:43:53 +00:00
|
|
|
if (opt_track)
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_push(&cp.args, opt_track);
|
2015-07-17 23:00:10 +00:00
|
|
|
if (run_command(&cp))
|
|
|
|
return -1;
|
2018-04-24 21:56:32 +00:00
|
|
|
branch = new_branch;
|
2017-11-26 19:43:53 +00:00
|
|
|
} else if (opt_track) {
|
|
|
|
die(_("--[no-]track can only be used if a new branch is created"));
|
2015-07-06 17:30:59 +00:00
|
|
|
}
|
|
|
|
|
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 18:17:51 +00:00
|
|
|
ret = add_worktree(path, branch, &opts);
|
|
|
|
free(path);
|
|
|
|
return ret;
|
2015-07-06 17:30:50 +00:00
|
|
|
}
|
|
|
|
|
2022-03-31 16:21:28 +00:00
|
|
|
static void show_worktree_porcelain(struct worktree *wt, int line_terminator)
|
2015-10-08 17:01:05 +00:00
|
|
|
{
|
2021-01-27 08:03:08 +00:00
|
|
|
const char *reason;
|
|
|
|
|
2022-03-31 16:21:28 +00:00
|
|
|
printf("worktree %s%c", wt->path, line_terminator);
|
2015-10-08 17:01:05 +00:00
|
|
|
if (wt->is_bare)
|
2022-03-31 16:21:28 +00:00
|
|
|
printf("bare%c", line_terminator);
|
2015-10-08 17:01:05 +00:00
|
|
|
else {
|
2022-03-31 16:21:28 +00:00
|
|
|
printf("HEAD %s%c", oid_to_hex(&wt->head_oid), line_terminator);
|
2015-10-08 17:01:05 +00:00
|
|
|
if (wt->is_detached)
|
2022-03-31 16:21:28 +00:00
|
|
|
printf("detached%c", line_terminator);
|
get_worktrees() must return main worktree as first item even on error
This is required by git-worktree.txt, stating that the main worktree is
the first line (especially in --porcelain mode when we can't just change
behavior at will).
There's only one case when get_worktrees() may skip main worktree, when
parse_ref() fails. Update the code so that we keep first item as main
worktree and return something sensible in this case:
- In user-friendly mode, since we're not constraint by anything,
returning "(error)" should do the job (we already show "(detached
HEAD)" which is not machine-friendly). Actually errors should be
printed on stderr by parse_ref() (*)
- In plumbing mode, we do not show neither 'bare', 'detached' or
'branch ...', which is possible by the format description if I read
it right.
Careful readers may realize that when the local variable "head_ref" in
get_main_worktree() is emptied, add_head_info() will do nothing to
wt->head_sha1. But that's ok because head_sha1 is zero-ized in the
previous patch.
(*) Well, it does not. But it's supposed to be a stop gap implementation
until we can reuse refs code to parse "ref: " stuff in HEAD, from
resolve_refs_unsafe(). Now may be the time since refs refactoring is
mostly done.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-11-28 09:36:54 +00:00
|
|
|
else if (wt->head_ref)
|
2022-03-31 16:21:28 +00:00
|
|
|
printf("branch %s%c", wt->head_ref, line_terminator);
|
2015-10-08 17:01:05 +00:00
|
|
|
}
|
2021-01-27 08:03:08 +00:00
|
|
|
|
|
|
|
reason = worktree_lock_reason(wt);
|
2022-03-31 16:21:28 +00:00
|
|
|
if (reason) {
|
|
|
|
fputs("locked", stdout);
|
|
|
|
if (*reason) {
|
|
|
|
fputc(' ', stdout);
|
|
|
|
write_name_quoted(reason, stdout, line_terminator);
|
|
|
|
} else {
|
|
|
|
fputc(line_terminator, stdout);
|
|
|
|
}
|
|
|
|
}
|
2021-01-27 08:03:08 +00:00
|
|
|
|
2021-01-27 08:03:09 +00:00
|
|
|
reason = worktree_prune_reason(wt, expire);
|
|
|
|
if (reason)
|
2022-03-31 16:21:28 +00:00
|
|
|
printf("prunable %s%c", reason, line_terminator);
|
2021-01-27 08:03:09 +00:00
|
|
|
|
2022-03-31 16:21:28 +00:00
|
|
|
fputc(line_terminator, stdout);
|
2015-10-08 17:01:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 08:03:10 +00:00
|
|
|
const char *reason;
|
2015-10-08 17:01:05 +00:00
|
|
|
|
|
|
|
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,
|
2023-03-28 13:58:46 +00:00
|
|
|
repo_find_unique_abbrev(the_repository, &wt->head_oid, DEFAULT_ABBREV));
|
2016-11-28 09:36:53 +00:00
|
|
|
if (wt->is_detached)
|
2015-10-08 17:01:05 +00:00
|
|
|
strbuf_addstr(&sb, "(detached HEAD)");
|
2017-05-04 13:59:13 +00:00
|
|
|
else if (wt->head_ref) {
|
|
|
|
char *ref = shorten_unambiguous_ref(wt->head_ref, 0);
|
|
|
|
strbuf_addf(&sb, "[%s]", ref);
|
|
|
|
free(ref);
|
|
|
|
} else
|
get_worktrees() must return main worktree as first item even on error
This is required by git-worktree.txt, stating that the main worktree is
the first line (especially in --porcelain mode when we can't just change
behavior at will).
There's only one case when get_worktrees() may skip main worktree, when
parse_ref() fails. Update the code so that we keep first item as main
worktree and return something sensible in this case:
- In user-friendly mode, since we're not constraint by anything,
returning "(error)" should do the job (we already show "(detached
HEAD)" which is not machine-friendly). Actually errors should be
printed on stderr by parse_ref() (*)
- In plumbing mode, we do not show neither 'bare', 'detached' or
'branch ...', which is possible by the format description if I read
it right.
Careful readers may realize that when the local variable "head_ref" in
get_main_worktree() is emptied, add_head_info() will do nothing to
wt->head_sha1. But that's ok because head_sha1 is zero-ized in the
previous patch.
(*) Well, it does not. But it's supposed to be a stop gap implementation
until we can reuse refs code to parse "ref: " stuff in HEAD, from
resolve_refs_unsafe(). Now may be the time since refs refactoring is
mostly done.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-11-28 09:36:54 +00:00
|
|
|
strbuf_addstr(&sb, "(error)");
|
2015-10-08 17:01:05 +00:00
|
|
|
}
|
|
|
|
|
2021-01-27 08:03:10 +00:00
|
|
|
reason = worktree_lock_reason(wt);
|
|
|
|
if (verbose && reason && *reason)
|
|
|
|
strbuf_addf(&sb, "\n\tlocked: %s", reason);
|
|
|
|
else if (reason)
|
2020-10-11 10:11:52 +00:00
|
|
|
strbuf_addstr(&sb, " locked");
|
|
|
|
|
2021-01-27 08:03:10 +00:00
|
|
|
reason = worktree_prune_reason(wt, expire);
|
|
|
|
if (verbose && reason)
|
|
|
|
strbuf_addf(&sb, "\n\tprunable: %s", reason);
|
|
|
|
else if (reason)
|
2021-01-27 08:03:09 +00:00
|
|
|
strbuf_addstr(&sb, " prunable");
|
|
|
|
|
2020-10-11 10:11:52 +00:00
|
|
|
printf("%s\n", sb.buf);
|
2015-10-08 17:01:05 +00:00
|
|
|
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;
|
2023-03-28 13:58:46 +00:00
|
|
|
sha1_len = strlen(repo_find_unique_abbrev(the_repository, &wt[i]->head_oid, *abbrev));
|
2015-10-08 17:01:05 +00:00
|
|
|
if (sha1_len > *abbrev)
|
|
|
|
*abbrev = sha1_len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-19 23:35:43 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-10-08 17:01:05 +00:00
|
|
|
static int list(int ac, const char **av, const char *prefix)
|
|
|
|
{
|
|
|
|
int porcelain = 0;
|
2022-03-31 16:21:28 +00:00
|
|
|
int line_terminator = '\n';
|
2015-10-08 17:01:05 +00:00
|
|
|
|
|
|
|
struct option options[] = {
|
|
|
|
OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
|
2021-01-27 08:03:10 +00:00
|
|
|
OPT__VERBOSE(&verbose, N_("show extended annotations and reasons, if available")),
|
2021-01-27 08:03:09 +00:00
|
|
|
OPT_EXPIRY_DATE(0, "expire", &expire,
|
|
|
|
N_("add 'prunable' annotation to worktrees older than <time>")),
|
2022-03-31 16:21:28 +00:00
|
|
|
OPT_SET_INT('z', NULL, &line_terminator,
|
|
|
|
N_("terminate records with a NUL character"), '\0'),
|
2015-10-08 17:01:05 +00:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2021-01-27 08:03:09 +00:00
|
|
|
expire = TIME_MAX;
|
2022-10-13 15:39:25 +00:00
|
|
|
ac = parse_options(ac, av, prefix, options, git_worktree_list_usage, 0);
|
2015-10-08 17:01:05 +00:00
|
|
|
if (ac)
|
2022-10-13 15:39:25 +00:00
|
|
|
usage_with_options(git_worktree_list_usage, options);
|
2021-01-27 08:03:10 +00:00
|
|
|
else if (verbose && porcelain)
|
2022-01-05 20:02:14 +00:00
|
|
|
die(_("options '%s' and '%s' cannot be used together"), "--verbose", "--porcelain");
|
2022-03-31 16:21:28 +00:00
|
|
|
else if (!line_terminator && !porcelain)
|
|
|
|
die(_("the option '%s' requires '%s'"), "-z", "--porcelain");
|
2015-10-08 17:01:05 +00:00
|
|
|
else {
|
2020-06-19 23:35:44 +00:00
|
|
|
struct worktree **worktrees = get_worktrees();
|
2015-10-08 17:01:05 +00:00
|
|
|
int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i;
|
|
|
|
|
2020-06-19 23:35:43 +00:00
|
|
|
/* sort worktrees by path but keep main worktree at top */
|
|
|
|
pathsort(worktrees + 1);
|
|
|
|
|
2015-10-08 17:01:05 +00:00
|
|
|
if (!porcelain)
|
|
|
|
measure_widths(worktrees, &abbrev, &path_maxlen);
|
|
|
|
|
|
|
|
for (i = 0; worktrees[i]; i++) {
|
|
|
|
if (porcelain)
|
2022-03-31 16:21:28 +00:00
|
|
|
show_worktree_porcelain(worktrees[i],
|
|
|
|
line_terminator);
|
2015-10-08 17:01:05 +00:00
|
|
|
else
|
|
|
|
show_worktree(worktrees[i], path_maxlen, abbrev);
|
|
|
|
}
|
|
|
|
free_worktrees(worktrees);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-13 12:18:24 +00:00
|
|
|
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;
|
|
|
|
|
2022-10-13 15:39:25 +00:00
|
|
|
ac = parse_options(ac, av, prefix, options, git_worktree_lock_usage, 0);
|
2016-06-13 12:18:24 +00:00
|
|
|
if (ac != 1)
|
2022-10-13 15:39:25 +00:00
|
|
|
usage_with_options(git_worktree_lock_usage, options);
|
2016-06-13 12:18:24 +00:00
|
|
|
|
2020-06-19 23:35:44 +00:00
|
|
|
worktrees = get_worktrees();
|
2016-06-13 12:18:24 +00:00
|
|
|
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"));
|
|
|
|
|
2018-10-30 06:24:09 +00:00
|
|
|
old_reason = worktree_lock_reason(wt);
|
2016-06-13 12:18:24 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-06-13 12:18:25 +00:00
|
|
|
static int unlock_worktree(int ac, const char **av, const char *prefix)
|
|
|
|
{
|
|
|
|
struct option options[] = {
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
struct worktree **worktrees, *wt;
|
|
|
|
int ret;
|
|
|
|
|
2022-10-13 15:39:25 +00:00
|
|
|
ac = parse_options(ac, av, prefix, options, git_worktree_unlock_usage, 0);
|
2016-06-13 12:18:25 +00:00
|
|
|
if (ac != 1)
|
2022-10-13 15:39:25 +00:00
|
|
|
usage_with_options(git_worktree_unlock_usage, options);
|
2016-06-13 12:18:25 +00:00
|
|
|
|
2020-06-19 23:35:44 +00:00
|
|
|
worktrees = get_worktrees();
|
2016-06-13 12:18:25 +00:00
|
|
|
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"));
|
2018-10-30 06:24:09 +00:00
|
|
|
if (!worktree_lock_reason(wt))
|
2016-06-13 12:18:25 +00:00
|
|
|
die(_("'%s' is not locked"), av[0]);
|
|
|
|
ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
|
|
|
|
free_worktrees(worktrees);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-02-12 09:49:38 +00:00
|
|
|
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 13:57:00 +00:00
|
|
|
struct index_state istate = INDEX_STATE_INIT(the_repository);
|
2019-01-05 05:08:40 +00:00
|
|
|
struct strbuf path = STRBUF_INIT;
|
2018-02-12 09:49:38 +00:00
|
|
|
int i, found_submodules = 0;
|
|
|
|
|
2019-01-05 05:08:40 +00:00
|
|
|
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) {
|
2018-02-12 09:49:38 +00:00
|
|
|
for (i = 0; i < istate.cache_nr; i++) {
|
|
|
|
struct cache_entry *ce = istate.cache[i];
|
2019-01-05 05:08:40 +00:00
|
|
|
int err;
|
2018-02-12 09:49:38 +00:00
|
|
|
|
2019-01-05 05:08:40 +00:00
|
|
|
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;
|
2018-02-12 09:49:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
discard_index(&istate);
|
2019-01-05 05:08:40 +00:00
|
|
|
strbuf_release(&path);
|
2018-02-12 09:49:38 +00:00
|
|
|
|
|
|
|
if (found_submodules)
|
2018-02-12 09:49:39 +00:00
|
|
|
die(_("working trees containing submodules cannot be moved or removed"));
|
2018-02-12 09:49:38 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 09:49:36 +00:00
|
|
|
static int move_worktree(int ac, const char **av, const char *prefix)
|
|
|
|
{
|
2018-08-28 21:20:24 +00:00
|
|
|
int force = 0;
|
2018-02-12 09:49:36 +00:00
|
|
|
struct option options[] = {
|
2018-08-28 21:20:24 +00:00
|
|
|
OPT__FORCE(&force,
|
|
|
|
N_("force move even if worktree is dirty or locked"),
|
|
|
|
PARSE_OPT_NOCOMPLETE),
|
2018-02-12 09:49:36 +00:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
struct worktree **worktrees, *wt;
|
|
|
|
struct strbuf dst = STRBUF_INIT;
|
|
|
|
struct strbuf errmsg = STRBUF_INIT;
|
2018-08-28 21:20:24 +00:00
|
|
|
const char *reason = NULL;
|
2018-02-12 09:49:36 +00:00
|
|
|
char *path;
|
|
|
|
|
2022-10-13 15:39:25 +00:00
|
|
|
ac = parse_options(ac, av, prefix, options, git_worktree_move_usage,
|
|
|
|
0);
|
2018-02-12 09:49:36 +00:00
|
|
|
if (ac != 2)
|
2022-10-13 15:39:25 +00:00
|
|
|
usage_with_options(git_worktree_move_usage, options);
|
2018-02-12 09:49:36 +00:00
|
|
|
|
|
|
|
path = prefix_filename(prefix, av[1]);
|
|
|
|
strbuf_addstr(&dst, path);
|
|
|
|
free(path);
|
|
|
|
|
2020-06-19 23:35:44 +00:00
|
|
|
worktrees = get_worktrees();
|
2018-02-12 09:49:36 +00:00
|
|
|
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]);
|
2018-02-12 09:49:37 +00:00
|
|
|
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);
|
|
|
|
}
|
2020-06-10 06:30:49 +00:00
|
|
|
check_candidate_path(dst.buf, force, worktrees, "move");
|
2018-02-12 09:49:36 +00:00
|
|
|
|
2018-02-12 09:49:38 +00:00
|
|
|
validate_no_submodules(wt);
|
|
|
|
|
2018-08-28 21:20:24 +00:00
|
|
|
if (force < 2)
|
2018-10-30 06:24:09 +00:00
|
|
|
reason = worktree_lock_reason(wt);
|
2018-02-12 09:49:36 +00:00
|
|
|
if (reason) {
|
|
|
|
if (*reason)
|
2018-08-28 21:20:24 +00:00
|
|
|
die(_("cannot move a locked working tree, lock reason: %s\nuse 'move -f -f' to override or unlock first"),
|
2018-02-12 09:49:36 +00:00
|
|
|
reason);
|
2018-08-28 21:20:24 +00:00
|
|
|
die(_("cannot move a locked working tree;\nuse 'move -f -f' to override or unlock first"));
|
2018-02-12 09:49:36 +00:00
|
|
|
}
|
2018-02-12 09:49:40 +00:00
|
|
|
if (validate_worktree(wt, &errmsg, 0))
|
2018-02-12 09:49:36 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-02-12 09:49:39 +00:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
|
2020-08-27 05:25:04 +00:00
|
|
|
child_process_init(&cp);
|
2022-06-02 09:09:50 +00:00
|
|
|
strvec_pushf(&cp.env, "%s=%s/.git",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 20:26:31 +00:00
|
|
|
GIT_DIR_ENVIRONMENT, wt->path);
|
2022-06-02 09:09:50 +00:00
|
|
|
strvec_pushf(&cp.env, "%s=%s",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 20:26:31 +00:00
|
|
|
GIT_WORK_TREE_ENVIRONMENT, wt->path);
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_pushl(&cp.args, "status",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 20:26:31 +00:00
|
|
|
"--porcelain", "--ignore-submodules=none",
|
|
|
|
NULL);
|
2018-02-12 09:49:39 +00:00
|
|
|
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)
|
2019-08-13 18:02:44 +00:00
|
|
|
die(_("'%s' contains modified or untracked files, use --force to delete it"),
|
2018-02-12 09:49:39 +00:00
|
|
|
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[] = {
|
2018-04-17 18:19:39 +00:00
|
|
|
OPT__FORCE(&force,
|
2018-08-28 21:20:25 +00:00
|
|
|
N_("force removal even if worktree is dirty or locked"),
|
2018-04-17 18:19:39 +00:00
|
|
|
PARSE_OPT_NOCOMPLETE),
|
2018-02-12 09:49:39 +00:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
struct worktree **worktrees, *wt;
|
|
|
|
struct strbuf errmsg = STRBUF_INIT;
|
2018-08-28 21:20:25 +00:00
|
|
|
const char *reason = NULL;
|
2018-02-12 09:49:39 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
2022-10-13 15:39:25 +00:00
|
|
|
ac = parse_options(ac, av, prefix, options, git_worktree_remove_usage, 0);
|
2018-02-12 09:49:39 +00:00
|
|
|
if (ac != 1)
|
2022-10-13 15:39:25 +00:00
|
|
|
usage_with_options(git_worktree_remove_usage, options);
|
2018-02-12 09:49:39 +00:00
|
|
|
|
2020-06-19 23:35:44 +00:00
|
|
|
worktrees = get_worktrees();
|
2018-02-12 09:49:39 +00:00
|
|
|
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]);
|
2018-08-28 21:20:25 +00:00
|
|
|
if (force < 2)
|
2018-10-30 06:24:09 +00:00
|
|
|
reason = worktree_lock_reason(wt);
|
2018-02-12 09:49:39 +00:00
|
|
|
if (reason) {
|
|
|
|
if (*reason)
|
2018-08-28 21:20:25 +00:00
|
|
|
die(_("cannot remove a locked working tree, lock reason: %s\nuse 'remove -f -f' to override or unlock first"),
|
2018-02-12 09:49:39 +00:00
|
|
|
reason);
|
2018-08-28 21:20:25 +00:00
|
|
|
die(_("cannot remove a locked working tree;\nuse 'remove -f -f' to override or unlock first"));
|
2018-02-12 09:49:39 +00:00
|
|
|
}
|
2018-02-12 09:49:40 +00:00
|
|
|
if (validate_worktree(wt, &errmsg, WT_VALIDATE_WORKTREE_MISSING_OK))
|
2018-02-12 09:49:39 +00:00
|
|
|
die(_("validation failed, cannot remove working tree: %s"),
|
|
|
|
errmsg.buf);
|
|
|
|
strbuf_release(&errmsg);
|
|
|
|
|
2018-02-12 09:49:40 +00:00
|
|
|
if (file_exists(wt->path)) {
|
|
|
|
if (!force)
|
|
|
|
check_clean_worktree(wt, av[0]);
|
2018-02-12 09:49:39 +00:00
|
|
|
|
2018-02-12 09:49:40 +00:00
|
|
|
ret |= delete_git_work_tree(wt);
|
|
|
|
}
|
2018-02-12 09:49:39 +00:00
|
|
|
/*
|
|
|
|
* continue on even if ret is non-zero, there's no going back
|
|
|
|
* from here.
|
|
|
|
*/
|
2018-08-28 21:20:20 +00:00
|
|
|
ret |= delete_git_dir(wt->id);
|
2018-08-28 21:20:26 +00:00
|
|
|
delete_worktrees_dir_if_empty();
|
2018-02-12 09:49:39 +00:00
|
|
|
|
|
|
|
free_worktrees(worktrees);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-08-31 06:57:57 +00:00
|
|
|
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 03:44:19 +00:00
|
|
|
fprintf_ln(stderr, _("repair: %s: %s"), msg, path);
|
2020-08-31 06:57:57 +00:00
|
|
|
} else {
|
|
|
|
int *exit_status = (int *)cb_data;
|
|
|
|
fprintf_ln(stderr, _("error: %s: %s"), msg, path);
|
|
|
|
*exit_status = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-27 08:21:25 +00:00
|
|
|
static int repair(int ac, const char **av, const char *prefix)
|
|
|
|
{
|
2020-08-31 06:57:58 +00:00
|
|
|
const char **p;
|
|
|
|
const char *self[] = { ".", NULL };
|
2020-08-27 08:21:25 +00:00
|
|
|
struct option options[] = {
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
int rc = 0;
|
|
|
|
|
2022-10-13 15:39:25 +00:00
|
|
|
ac = parse_options(ac, av, prefix, options, git_worktree_repair_usage, 0);
|
2020-08-31 06:57:58 +00:00
|
|
|
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 08:16:01 +00:00
|
|
|
repair_worktrees(report_repair, &rc);
|
2020-08-27 08:21:25 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2015-06-29 12:51:18 +00:00
|
|
|
int cmd_worktree(int ac, const char **av, const char *prefix)
|
|
|
|
{
|
2022-08-19 16:04:11 +00:00
|
|
|
parse_opt_subcommand_fn *fn = NULL;
|
2015-06-29 12:51:18 +00:00
|
|
|
struct option options[] = {
|
2022-08-19 16:04:11 +00:00
|
|
|
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),
|
2015-06-29 12:51:18 +00:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2017-11-29 20:04:51 +00:00
|
|
|
git_config(git_worktree_config, NULL);
|
2016-09-27 06:49:39 +00:00
|
|
|
|
2016-05-22 09:33:56 +00:00
|
|
|
if (!prefix)
|
|
|
|
prefix = "";
|
2022-08-19 16:04:11 +00:00
|
|
|
|
2022-10-13 15:39:25 +00:00
|
|
|
ac = parse_options(ac, av, prefix, options, git_worktree_usage, 0);
|
2023-06-06 17:26:33 +00:00
|
|
|
|
|
|
|
prepare_repo_settings(the_repository);
|
|
|
|
the_repository->settings.command_requires_full_index = 0;
|
|
|
|
|
2022-08-19 16:04:11 +00:00
|
|
|
return fn(ac, av, prefix);
|
2015-06-29 12:51:18 +00:00
|
|
|
}
|