2006-10-24 05:27:45 +08:00
|
|
|
/*
|
|
|
|
* Builtin "git branch"
|
|
|
|
*
|
2007-07-12 13:52:45 +08:00
|
|
|
* Copyright (c) 2006 Kristian Høgsberg <krh@redhat.com>
|
2006-10-24 05:27:45 +08:00
|
|
|
* Based on git-branch.sh by Junio C Hamano.
|
|
|
|
*/
|
|
|
|
|
2023-05-16 14:33:57 +08:00
|
|
|
#include "builtin.h"
|
2017-06-15 02:07:36 +08:00
|
|
|
#include "config.h"
|
2006-12-20 06:34:12 +08:00
|
|
|
#include "color.h"
|
2023-04-11 15:41:57 +08:00
|
|
|
#include "editor.h"
|
2023-03-21 14:25:57 +08:00
|
|
|
#include "environment.h"
|
2006-10-24 05:27:45 +08:00
|
|
|
#include "refs.h"
|
|
|
|
#include "commit.h"
|
2023-03-21 14:25:54 +08:00
|
|
|
#include "gettext.h"
|
2023-04-11 15:41:49 +08:00
|
|
|
#include "object-name.h"
|
2007-07-11 01:50:44 +08:00
|
|
|
#include "remote.h"
|
2007-10-08 00:26:21 +08:00
|
|
|
#include "parse-options.h"
|
2008-02-08 00:40:08 +08:00
|
|
|
#include "branch.h"
|
2008-07-24 06:13:41 +08:00
|
|
|
#include "diff.h"
|
2023-05-16 14:33:59 +08:00
|
|
|
#include "path.h"
|
2008-07-24 06:13:41 +08:00
|
|
|
#include "revision.h"
|
2012-04-13 18:54:38 +08:00
|
|
|
#include "string-list.h"
|
|
|
|
#include "column.h"
|
2012-08-26 02:17:12 +08:00
|
|
|
#include "utf8.h"
|
2013-03-13 19:42:53 +08:00
|
|
|
#include "wt-status.h"
|
2015-09-24 02:11:11 +08:00
|
|
|
#include "ref-filter.h"
|
2016-03-29 17:38:39 +08:00
|
|
|
#include "worktree.h"
|
2018-05-26 21:55:24 +08:00
|
|
|
#include "help.h"
|
2018-07-21 00:33:04 +08:00
|
|
|
#include "commit-reach.h"
|
2007-10-08 00:26:21 +08:00
|
|
|
|
|
|
|
static const char * const builtin_branch_usage[] = {
|
2020-09-16 10:08:40 +08:00
|
|
|
N_("git branch [<options>] [-r | -a] [--merged] [--no-merged]"),
|
branch: add --recurse-submodules option for branch creation
To improve the submodules UX, we would like to teach Git to handle
branches in submodules. Start this process by teaching "git branch" the
--recurse-submodules option so that "git branch --recurse-submodules
topic" will create the `topic` branch in the superproject and its
submodules.
Although this commit does not introduce breaking changes, it does not
work well with existing --recurse-submodules commands because "git
branch --recurse-submodules" writes to the submodule ref store, but most
commands only consider the superproject gitlink and ignore the submodule
ref store. For example, "git checkout --recurse-submodules" will check
out the commits in the superproject gitlinks (and put the submodules in
detached HEAD) instead of checking out the submodule branches.
Because of this, this commit introduces a new configuration value,
`submodule.propagateBranches`. The plan is for Git commands to
prioritize submodule ref store information over superproject gitlinks if
this value is true. Because "git branch --recurse-submodules" writes to
submodule ref stores, for the sake of clarity, it will not function
unless this configuration value is set.
This commit also includes changes that support working with submodules
from a superproject commit because "branch --recurse-submodules" (and
future commands) need to read .gitmodules and gitlinks from the
superproject commit, but submodules are typically read from the
filesystem's .gitmodules and the index's gitlinks. These changes are:
* add a submodules_of_tree() helper that gives the relevant
information of an in-tree submodule (e.g. path and oid) and
initializes the repository
* add is_tree_submodule_active() by adding a treeish_name parameter to
is_submodule_active()
* add the "submoduleNotUpdated" advice to advise users to update the
submodules in their trees
Incidentally, fix an incorrect usage string that combined the 'list'
usage of git branch (-l) with the 'create' usage; this string has been
incorrect since its inception, a8dfd5eac4 (Make builtin-branch.c use
parse_options., 2007-10-07).
Helped-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Glen Choo <chooglen@google.com>
Reviewed-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-29 08:04:45 +08:00
|
|
|
N_("git branch [<options>] [-f] [--recurse-submodules] <branch-name> [<start-point>]"),
|
|
|
|
N_("git branch [<options>] [-l] [<pattern>...]"),
|
2015-01-13 15:44:47 +08:00
|
|
|
N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."),
|
|
|
|
N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"),
|
branch: add a --copy (-c) option to go with --move (-m)
Add the ability to --copy a branch and its reflog and configuration,
this uses the same underlying machinery as the --move (-m) option
except the reflog and configuration is copied instead of being moved.
This is useful for e.g. copying a topic branch to a new version,
e.g. work to work-2 after submitting the work topic to the list, while
preserving all the tracking info and other configuration that goes
with the branch, and unlike --move keeping the other already-submitted
branch around for reference.
Like --move, when the source branch is the currently checked out
branch the HEAD is moved to the destination branch. In the case of
--move we don't really have a choice (other than remaining on a
detached HEAD) and in order to keep the functionality consistent, we
are doing it in similar way for --copy too.
The most common usage of this feature is expected to be moving to a
new topic branch which is a copy of the current one, in that case
moving to the target branch is what the user wants, and doesn't
unexpectedly behave differently than --move would.
One outstanding caveat of this implementation is that:
git checkout maint &&
git checkout master &&
git branch -c topic &&
git checkout -
Will check out 'maint' instead of 'master'. This is because the @{-N}
feature (or its -1 shorthand "-") relies on HEAD reflogs created by
the checkout command, so in this case we'll checkout maint instead of
master, as the user might expect. What to do about that is left to a
future change.
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Sahil Dua <sahildua2305@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-06-19 05:19:16 +08:00
|
|
|
N_("git branch [<options>] (-c | -C) [<old-branch>] <new-branch>"),
|
2015-09-24 02:11:13 +08:00
|
|
|
N_("git branch [<options>] [-r | -a] [--points-at]"),
|
2017-01-10 16:49:53 +08:00
|
|
|
N_("git branch [<options>] [-r | -a] [--format]"),
|
2007-10-08 00:26:21 +08:00
|
|
|
NULL
|
|
|
|
};
|
2006-10-24 05:27:45 +08:00
|
|
|
|
|
|
|
static const char *head;
|
2017-02-22 07:47:26 +08:00
|
|
|
static struct object_id head_oid;
|
branch: add --recurse-submodules option for branch creation
To improve the submodules UX, we would like to teach Git to handle
branches in submodules. Start this process by teaching "git branch" the
--recurse-submodules option so that "git branch --recurse-submodules
topic" will create the `topic` branch in the superproject and its
submodules.
Although this commit does not introduce breaking changes, it does not
work well with existing --recurse-submodules commands because "git
branch --recurse-submodules" writes to the submodule ref store, but most
commands only consider the superproject gitlink and ignore the submodule
ref store. For example, "git checkout --recurse-submodules" will check
out the commits in the superproject gitlinks (and put the submodules in
detached HEAD) instead of checking out the submodule branches.
Because of this, this commit introduces a new configuration value,
`submodule.propagateBranches`. The plan is for Git commands to
prioritize submodule ref store information over superproject gitlinks if
this value is true. Because "git branch --recurse-submodules" writes to
submodule ref stores, for the sake of clarity, it will not function
unless this configuration value is set.
This commit also includes changes that support working with submodules
from a superproject commit because "branch --recurse-submodules" (and
future commands) need to read .gitmodules and gitlinks from the
superproject commit, but submodules are typically read from the
filesystem's .gitmodules and the index's gitlinks. These changes are:
* add a submodules_of_tree() helper that gives the relevant
information of an in-tree submodule (e.g. path and oid) and
initializes the repository
* add is_tree_submodule_active() by adding a treeish_name parameter to
is_submodule_active()
* add the "submoduleNotUpdated" advice to advise users to update the
submodules in their trees
Incidentally, fix an incorrect usage string that combined the 'list'
usage of git branch (-l) with the 'create' usage; this string has been
incorrect since its inception, a8dfd5eac4 (Make builtin-branch.c use
parse_options., 2007-10-07).
Helped-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Glen Choo <chooglen@google.com>
Reviewed-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-29 08:04:45 +08:00
|
|
|
static int recurse_submodules = 0;
|
|
|
|
static int submodule_propagate_branches = 0;
|
2006-10-24 05:27:45 +08:00
|
|
|
|
2008-02-18 15:26:03 +08:00
|
|
|
static int branch_use_color = -1;
|
2006-12-12 14:41:52 +08:00
|
|
|
static char branch_colors[][COLOR_MAXLEN] = {
|
2009-02-14 05:53:40 +08:00
|
|
|
GIT_COLOR_RESET,
|
2017-01-10 16:49:52 +08:00
|
|
|
GIT_COLOR_NORMAL, /* PLAIN */
|
|
|
|
GIT_COLOR_RED, /* REMOTE */
|
|
|
|
GIT_COLOR_NORMAL, /* LOCAL */
|
|
|
|
GIT_COLOR_GREEN, /* CURRENT */
|
|
|
|
GIT_COLOR_BLUE, /* UPSTREAM */
|
2019-04-29 13:19:43 +08:00
|
|
|
GIT_COLOR_CYAN, /* WORKTREE */
|
2006-12-12 14:41:52 +08:00
|
|
|
};
|
|
|
|
enum color_branch {
|
2009-02-14 05:53:41 +08:00
|
|
|
BRANCH_COLOR_RESET = 0,
|
|
|
|
BRANCH_COLOR_PLAIN = 1,
|
|
|
|
BRANCH_COLOR_REMOTE = 2,
|
|
|
|
BRANCH_COLOR_LOCAL = 3,
|
2013-04-15 10:37:49 +08:00
|
|
|
BRANCH_COLOR_CURRENT = 4,
|
2019-04-29 13:19:43 +08:00
|
|
|
BRANCH_COLOR_UPSTREAM = 5,
|
|
|
|
BRANCH_COLOR_WORKTREE = 6
|
2006-12-12 14:41:52 +08:00
|
|
|
};
|
|
|
|
|
2018-05-26 21:55:21 +08:00
|
|
|
static const char *color_branch_slots[] = {
|
|
|
|
[BRANCH_COLOR_RESET] = "reset",
|
|
|
|
[BRANCH_COLOR_PLAIN] = "plain",
|
|
|
|
[BRANCH_COLOR_REMOTE] = "remote",
|
|
|
|
[BRANCH_COLOR_LOCAL] = "local",
|
|
|
|
[BRANCH_COLOR_CURRENT] = "current",
|
|
|
|
[BRANCH_COLOR_UPSTREAM] = "upstream",
|
2019-04-29 13:19:43 +08:00
|
|
|
[BRANCH_COLOR_WORKTREE] = "worktree",
|
2018-05-26 21:55:21 +08:00
|
|
|
};
|
|
|
|
|
2012-04-13 18:54:38 +08:00
|
|
|
static struct string_list output = STRING_LIST_INIT_DUP;
|
|
|
|
static unsigned int colopts;
|
|
|
|
|
2018-05-26 21:55:24 +08:00
|
|
|
define_list_config_array(color_branch_slots);
|
2006-12-12 14:41:52 +08: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-29 03:26:22 +08:00
|
|
|
static int git_branch_config(const char *var, const char *value,
|
|
|
|
const struct config_context *ctx, void *cb)
|
2006-12-12 14:41:52 +08:00
|
|
|
{
|
2014-10-05 02:54:50 +08:00
|
|
|
const char *slot_name;
|
2018-08-16 17:35:08 +08:00
|
|
|
|
|
|
|
if (!strcmp(var, "branch.sort")) {
|
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(var);
|
for-each-ref: delay parsing of --sort=<atom> options
The for-each-ref family of commands invoke parsers immediately when
it sees each --sort=<atom> option, and die before even seeing the
other options on the command line when the <atom> is unrecognised.
Instead, accumulate them in a string list, and have them parsed into
a ref_sorting structure after the command line parsing is done. As
a consequence, "git branch --sort=bogus -h" used to fail to give the
brief help, which arguably may have been a feature, now does so,
which is more consistent with how other options work.
The patch is smaller than the actual extent of the "damage" to the
codebase, thanks to the fact that the original code consistently
used OPT_REF_SORT() macro to handle command line options. We only
needed to replace the variable used for the list, and implementation
of the callback function used in the macro.
The old rule was for the users of the API to:
- Declare ref_sorting and ref_sorting_tail variables;
- OPT_REF_SORT() macro will instantiate ref_sorting instance (which
may barf and die) and append it to the tail;
- Append to the tail each ref_sorting read from the configuration
by parsing in the config callback (which may barf and die);
- See if ref_sorting is null and use ref_sorting_default() instead.
Now the rule is not all that different but is simpler:
- Declare ref_sorting_options string list.
- OPT_REF_SORT() macro will append it to the string list;
- Append to the string list the sort key read from the
configuration;
- call ref_sorting_options() to turn the string list to ref_sorting
structure (which also deals with the default value).
As side effects, this change also cleans up a few issues:
- 95be717c (parse_opt_ref_sorting: always use with NONEG flag,
2019-03-20) muses that "git for-each-ref --no-sort" should simply
clear the sort keys accumulated so far; it now does.
- The implementation detail of "struct ref_sorting" and the helper
function parse_ref_sorting() can now be private to the ref-filter
API implementation.
- If you set branch.sort to a bogus value, the any "git branch"
invocation, not only the listing mode, would abort with the
original code; now it doesn't
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-10-21 03:23:53 +08:00
|
|
|
string_list_append(cb, value);
|
2018-08-16 17:35:08 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2014-10-05 02:54:50 +08:00
|
|
|
|
2013-12-01 04:55:40 +08:00
|
|
|
if (starts_with(var, "column."))
|
2012-04-13 18:54:38 +08:00
|
|
|
return git_column_config(var, value, "branch", &colopts);
|
2006-12-12 14:41:52 +08:00
|
|
|
if (!strcmp(var, "color.branch")) {
|
2011-08-18 13:03:48 +08:00
|
|
|
branch_use_color = git_config_colorbool(var, value);
|
2006-12-12 14:41:52 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2014-10-05 02:54:50 +08:00
|
|
|
if (skip_prefix(var, "color.branch.", &slot_name)) {
|
2018-05-26 21:55:21 +08:00
|
|
|
int slot = LOOKUP_CONFIG(color_branch_slots, slot_name);
|
ignore unknown color configuration
When parsing the config file, if there is a value that is
syntactically correct but unused, we generally ignore it.
This lets non-core porcelains store arbitrary information in
the config file, and it means that configuration files can
be shared between new and old versions of git (the old
versions might simply ignore certain configuration).
The one exception to this is color configuration; if we
encounter a color.{diff,branch,status}.$slot variable, we
die if it is not one of the recognized slots (presumably as
a safety valve for user misconfiguration). This behavior
has existed since 801235c (diff --color: use
$GIT_DIR/config, 2006-06-24), but hasn't yet caused a
problem. No porcelain has wanted to store extra colors, and
we once a color area (like color.diff) has been introduced,
we've never changed the set of color slots.
However, that changed recently with the addition of
color.diff.func. Now a user with color.diff.func in their
config can no longer freely switch between v1.6.6 and older
versions; the old versions will complain about the existence
of the variable.
This patch loosens the check to match the rest of
git-config; unknown color slots are simply ignored. This
doesn't fix this particular problem, as the older version
(without this patch) is the problem, but it at least
prevents it from happening again in the future.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-12-12 20:25:24 +08:00
|
|
|
if (slot < 0)
|
|
|
|
return 0;
|
2008-02-12 02:45:50 +08:00
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(var);
|
2014-10-08 03:33:09 +08:00
|
|
|
return color_parse(value, branch_colors[slot]);
|
2006-12-12 14:41:52 +08:00
|
|
|
}
|
branch: add --recurse-submodules option for branch creation
To improve the submodules UX, we would like to teach Git to handle
branches in submodules. Start this process by teaching "git branch" the
--recurse-submodules option so that "git branch --recurse-submodules
topic" will create the `topic` branch in the superproject and its
submodules.
Although this commit does not introduce breaking changes, it does not
work well with existing --recurse-submodules commands because "git
branch --recurse-submodules" writes to the submodule ref store, but most
commands only consider the superproject gitlink and ignore the submodule
ref store. For example, "git checkout --recurse-submodules" will check
out the commits in the superproject gitlinks (and put the submodules in
detached HEAD) instead of checking out the submodule branches.
Because of this, this commit introduces a new configuration value,
`submodule.propagateBranches`. The plan is for Git commands to
prioritize submodule ref store information over superproject gitlinks if
this value is true. Because "git branch --recurse-submodules" writes to
submodule ref stores, for the sake of clarity, it will not function
unless this configuration value is set.
This commit also includes changes that support working with submodules
from a superproject commit because "branch --recurse-submodules" (and
future commands) need to read .gitmodules and gitlinks from the
superproject commit, but submodules are typically read from the
filesystem's .gitmodules and the index's gitlinks. These changes are:
* add a submodules_of_tree() helper that gives the relevant
information of an in-tree submodule (e.g. path and oid) and
initializes the repository
* add is_tree_submodule_active() by adding a treeish_name parameter to
is_submodule_active()
* add the "submoduleNotUpdated" advice to advise users to update the
submodules in their trees
Incidentally, fix an incorrect usage string that combined the 'list'
usage of git branch (-l) with the 'create' usage; this string has been
incorrect since its inception, a8dfd5eac4 (Make builtin-branch.c use
parse_options., 2007-10-07).
Helped-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Glen Choo <chooglen@google.com>
Reviewed-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-29 08:04:45 +08:00
|
|
|
if (!strcmp(var, "submodule.recurse")) {
|
|
|
|
recurse_submodules = git_config_bool(var, value);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!strcasecmp(var, "submodule.propagateBranches")) {
|
|
|
|
submodule_propagate_branches = git_config_bool(var, value);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-06-29 03:26:20 +08:00
|
|
|
if (git_color_config(var, value, cb) < 0)
|
|
|
|
return -1;
|
|
|
|
|
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-29 03:26:22 +08:00
|
|
|
return git_default_config(var, value, ctx, cb);
|
2006-12-12 14:41:52 +08:00
|
|
|
}
|
|
|
|
|
2007-06-08 04:45:00 +08:00
|
|
|
static const char *branch_get_color(enum color_branch ix)
|
2006-12-12 14:41:52 +08:00
|
|
|
{
|
color: delay auto-color decision until point of use
When we read a color value either from a config file or from
the command line, we use git_config_colorbool to convert it
from the tristate always/never/auto into a single yes/no
boolean value.
This has some timing implications with respect to starting
a pager.
If we start (or decide not to start) the pager before
checking the colorbool, everything is fine. Either isatty(1)
will give us the right information, or we will properly
check for pager_in_use().
However, if we decide to start a pager after we have checked
the colorbool, things are not so simple. If stdout is a tty,
then we will have already decided to use color. However, the
user may also have configured color.pager not to use color
with the pager. In this case, we need to actually turn off
color. Unfortunately, the pager code has no idea which color
variables were turned on (and there are many of them
throughout the code, and they may even have been manipulated
after the colorbool selection by something like "--color" on
the command line).
This bug can be seen any time a pager is started after
config and command line options are checked. This has
affected "git diff" since 89d07f7 (diff: don't run pager if
user asked for a diff style exit code, 2007-08-12). It has
also affect the log family since 1fda91b (Fix 'git log'
early pager startup error case, 2010-08-24).
This patch splits the notion of parsing a colorbool and
actually checking the configuration. The "use_color"
variables now have an additional possible value,
GIT_COLOR_AUTO. Users of the variable should use the new
"want_color()" wrapper, which will lazily determine and
cache the auto-color decision.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-08-18 13:04:23 +08:00
|
|
|
if (want_color(branch_use_color))
|
2006-12-12 14:41:52 +08:00
|
|
|
return branch_colors[ix];
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2009-12-30 14:43:04 +08:00
|
|
|
static int branch_merged(int kind, const char *name,
|
|
|
|
struct commit *rev, struct commit *head_rev)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* This checks whether the merge bases of branch and HEAD (or
|
|
|
|
* the other branch this branch builds upon) contains the
|
|
|
|
* branch, which means that the branch has already been merged
|
|
|
|
* safely to HEAD (or the other branch).
|
|
|
|
*/
|
|
|
|
struct commit *reference_rev = NULL;
|
|
|
|
const char *reference_name = NULL;
|
2011-12-13 22:17:48 +08:00
|
|
|
void *reference_name_to_free = NULL;
|
2009-12-30 14:43:04 +08:00
|
|
|
int merged;
|
|
|
|
|
2015-09-24 02:11:11 +08:00
|
|
|
if (kind == FILTER_REFS_BRANCHES) {
|
2009-12-30 14:43:04 +08:00
|
|
|
struct branch *branch = branch_get(name);
|
2015-05-21 12:45:32 +08:00
|
|
|
const char *upstream = branch_get_upstream(branch, NULL);
|
2017-02-22 07:47:26 +08:00
|
|
|
struct object_id oid;
|
2009-12-30 14:43:04 +08:00
|
|
|
|
2015-05-21 12:45:28 +08:00
|
|
|
if (upstream &&
|
2011-12-13 22:17:48 +08:00
|
|
|
(reference_name = reference_name_to_free =
|
2015-05-21 12:45:28 +08:00
|
|
|
resolve_refdup(upstream, RESOLVE_REF_READING,
|
refs: convert resolve_refdup and refs_resolve_refdup to struct object_id
All of the callers already pass the hash member of struct object_id, so
update them to pass a pointer to the struct directly,
This transformation was done with an update to declaration and
definition and the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_refdup(E1, E2, E3.hash, E4)
+ resolve_refdup(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_refdup(E1, E2, E3->hash, E4)
+ resolve_refdup(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-16 06:06:55 +08:00
|
|
|
&oid, NULL)) != NULL)
|
2018-06-29 09:21:58 +08:00
|
|
|
reference_rev = lookup_commit_reference(the_repository,
|
|
|
|
&oid);
|
2009-12-30 14:43:04 +08:00
|
|
|
}
|
|
|
|
if (!reference_rev)
|
|
|
|
reference_rev = head_rev;
|
|
|
|
|
2023-03-28 21:58:47 +08:00
|
|
|
merged = reference_rev ? repo_in_merge_bases(the_repository, rev,
|
|
|
|
reference_rev) : 0;
|
2009-12-30 14:43:04 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* After the safety valve is fully redefined to "check with
|
|
|
|
* upstream, if any, otherwise with HEAD", we should just
|
2023-03-28 21:58:57 +08:00
|
|
|
* return the result of the repo_in_merge_bases() above without
|
2009-12-30 14:43:04 +08:00
|
|
|
* any of the following code, but during the transition period,
|
|
|
|
* a gentle reminder is in order.
|
|
|
|
*/
|
|
|
|
if ((head_rev != reference_rev) &&
|
2023-03-28 21:58:47 +08:00
|
|
|
(head_rev ? repo_in_merge_bases(the_repository, rev, head_rev) : 0) != merged) {
|
2009-12-30 14:43:04 +08:00
|
|
|
if (merged)
|
2011-02-23 07:41:34 +08:00
|
|
|
warning(_("deleting branch '%s' that has been merged to\n"
|
2023-10-24 00:06:56 +08:00
|
|
|
" '%s', but not yet merged to HEAD"),
|
2009-12-30 14:43:04 +08:00
|
|
|
name, reference_name);
|
|
|
|
else
|
2011-02-23 07:41:34 +08:00
|
|
|
warning(_("not deleting branch '%s' that is not yet merged to\n"
|
2023-10-24 00:06:56 +08:00
|
|
|
" '%s', even though it is merged to HEAD"),
|
2009-12-30 14:43:04 +08:00
|
|
|
name, reference_name);
|
|
|
|
}
|
2011-12-13 22:17:48 +08:00
|
|
|
free(reference_name_to_free);
|
2009-12-30 14:43:04 +08:00
|
|
|
return merged;
|
|
|
|
}
|
|
|
|
|
2012-10-18 20:02:51 +08:00
|
|
|
static int check_branch_commit(const char *branchname, const char *refname,
|
2017-02-22 07:47:26 +08:00
|
|
|
const struct object_id *oid, struct commit *head_rev,
|
2012-10-18 20:02:51 +08:00
|
|
|
int kinds, int force)
|
|
|
|
{
|
2018-06-29 09:21:58 +08:00
|
|
|
struct commit *rev = lookup_commit_reference(the_repository, oid);
|
2021-08-28 02:35:35 +08:00
|
|
|
if (!force && !rev) {
|
2023-10-24 00:06:56 +08:00
|
|
|
error(_("couldn't look up commit object for '%s'"), refname);
|
2012-10-18 20:02:51 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!force && !branch_merged(kinds, branchname, rev, head_rev)) {
|
2023-10-24 00:06:56 +08:00
|
|
|
error(_("the branch '%s' is not fully merged.\n"
|
2012-10-18 20:02:51 +08:00
|
|
|
"If you are sure you want to delete it, "
|
2023-10-24 00:06:56 +08:00
|
|
|
"run 'git branch -D %s'"), branchname, branchname);
|
2012-10-18 20:02:51 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-18 20:04:08 +08:00
|
|
|
static void delete_branch_config(const char *branchname)
|
|
|
|
{
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
strbuf_addf(&buf, "branch.%s", branchname);
|
|
|
|
if (git_config_rename_section(buf.buf, NULL) < 0)
|
2023-10-24 00:06:56 +08:00
|
|
|
warning(_("update of config-file failed"));
|
2012-10-18 20:04:08 +08:00
|
|
|
strbuf_release(&buf);
|
|
|
|
}
|
|
|
|
|
2012-03-27 07:51:06 +08:00
|
|
|
static int delete_branches(int argc, const char **argv, int force, int kinds,
|
|
|
|
int quiet)
|
2006-10-24 05:27:45 +08:00
|
|
|
{
|
2012-10-18 20:02:51 +08:00
|
|
|
struct commit *head_rev = NULL;
|
2017-02-22 07:47:26 +08:00
|
|
|
struct object_id oid;
|
2006-12-19 06:42:16 +08:00
|
|
|
char *name = NULL;
|
2012-04-30 23:33:12 +08:00
|
|
|
const char *fmt;
|
2006-10-24 05:27:45 +08:00
|
|
|
int i;
|
2006-12-19 06:42:16 +08:00
|
|
|
int ret = 0;
|
2012-04-30 23:33:12 +08:00
|
|
|
int remote_branch = 0;
|
2009-02-14 15:08:05 +08:00
|
|
|
struct strbuf bname = STRBUF_INIT;
|
2017-03-02 16:23:10 +08:00
|
|
|
unsigned allowed_interpret;
|
2021-01-21 11:23:32 +08:00
|
|
|
struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
|
|
|
|
struct string_list_item *item;
|
|
|
|
int branch_name_pos;
|
2023-04-05 19:43:20 +08:00
|
|
|
const char *fmt_remotes = "refs/remotes/%s";
|
2006-10-24 05:27:45 +08:00
|
|
|
|
2006-12-18 15:58:16 +08:00
|
|
|
switch (kinds) {
|
2015-09-24 02:11:11 +08:00
|
|
|
case FILTER_REFS_REMOTES:
|
2023-04-05 19:43:20 +08:00
|
|
|
fmt = fmt_remotes;
|
2012-04-30 23:33:12 +08:00
|
|
|
/* For subsequent UI messages */
|
|
|
|
remote_branch = 1;
|
2017-03-02 16:23:10 +08:00
|
|
|
allowed_interpret = INTERPRET_BRANCH_REMOTE;
|
2012-04-30 23:33:12 +08:00
|
|
|
|
2006-12-18 15:58:16 +08:00
|
|
|
force = 1;
|
|
|
|
break;
|
2015-09-24 02:11:11 +08:00
|
|
|
case FILTER_REFS_BRANCHES:
|
2006-12-18 15:58:16 +08:00
|
|
|
fmt = "refs/heads/%s";
|
2017-03-02 16:23:10 +08:00
|
|
|
allowed_interpret = INTERPRET_BRANCH_LOCAL;
|
2006-12-18 15:58:16 +08:00
|
|
|
break;
|
|
|
|
default:
|
2011-02-23 07:41:34 +08:00
|
|
|
die(_("cannot use -a with -d"));
|
2006-12-18 15:58:16 +08:00
|
|
|
}
|
2021-01-21 11:23:32 +08:00
|
|
|
branch_name_pos = strcspn(fmt, "%");
|
2006-10-24 05:27:45 +08:00
|
|
|
|
branch: gracefully handle '-d' on orphan HEAD
When deleting a branch, "git branch -d" has a safety check that ensures
the branch is merged to its upstream (if any), or to HEAD. To do that,
naturally we try to resolve HEAD to a commit object. If we're on an
orphan branch (i.e., HEAD points to a branch that does not yet exist),
that will fail, and we'll bail with an error:
$ git branch -d to-delete
fatal: Couldn't look up commit object for HEAD
This usually isn't that big of a deal. The deletion would fail anyway,
since the branch isn't merged to HEAD, and you'd need to use "-D" (or
"-f"). And doing so skips the HEAD resolution, courtesy of 67affd5173
(git-branch -D: make it work even when on a yet-to-be-born branch,
2006-11-24).
But there are still two problems:
1. The error message isn't very helpful. We should give the usual "not
fully merged" message, which points the user at "branch -D". That
was a problem even back in 67affd5173.
2. Even without a HEAD, these days it's still possible for the
deletion to succeed. After 67affd5173, commit 99c419c915 (branch
-d: base the "already-merged" safety on the branch it merges with,
2009-12-29) made it OK to delete a branch if it is merged to its
upstream.
We can fix both by removing the die() in delete_branches() completely,
leaving head_rev NULL in this case. It's tempting to stop there, as it
appears at first glance that the rest of the code does the right thing
with a NULL. But sadly, it's not quite true.
We end up feeding the NULL to repo_is_descendant_of(). In the
traditional code path there, we call repo_in_merge_bases_many(). It
feeds the NULL to repo_parse_commit(), which is smart enough to return
an error, and we immediately return "no, it's not a descendant".
But there's an alternate code path: if we have a commit graph with
generation numbers, we end up in can_all_from_reach(), which does
eventually try to set a flag on the NULL commit and segfaults.
So instead, we'll teach the local branch_merged() helper to treat a NULL
as "not merged". This would be a little more elegant in in_merge_bases()
itself, but that function is called in a lot of places, and it's not
clear that quietly returning "not merged" is the right thing everywhere
(I'd expect in many cases, feeding a NULL is a sign of a bug).
There are four tests here:
a. The first one confirms that deletion succeeds with an orphaned HEAD
when the branch is merged to its upstream. This is case (2) above.
b. Same, but with commit graphs enabled. Even if it is merged to
upstream, we still check head_rev so that we can say "deleting
because it's merged to upstream, even though it's not merged to
HEAD". Without the second hunk in branch_merged(), this test would
segfault in can_all_from_reach().
c. The third one confirms that we correctly say "not merged to HEAD"
when we can't resolve HEAD, and reject the deletion.
d. Same, but with commit graphs enabled. Without the first hunk in
branch_merged(), this one would segfault.
Reported-by: Martin von Zweigbergk <martinvonz@google.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
2022-11-02 13:27:49 +08:00
|
|
|
if (!force)
|
2018-06-29 09:21:58 +08:00
|
|
|
head_rev = lookup_commit_reference(the_repository, &head_oid);
|
2021-12-02 06:15:43 +08:00
|
|
|
|
2017-10-04 06:17:40 +08:00
|
|
|
for (i = 0; i < argc; i++, strbuf_reset(&bname)) {
|
2016-04-25 16:42:19 +08:00
|
|
|
char *target = NULL;
|
2012-10-18 20:07:11 +08:00
|
|
|
int flags = 0;
|
|
|
|
|
2017-03-02 16:23:10 +08:00
|
|
|
strbuf_branchname(&bname, argv[i], allowed_interpret);
|
Avoid unnecessary "if-before-free" tests.
This change removes all obvious useless if-before-free tests.
E.g., it replaces code like this:
if (some_expression)
free (some_expression);
with the now-equivalent:
free (some_expression);
It is equivalent not just because POSIX has required free(NULL)
to work for a long time, but simply because it has worked for
so long that no reasonable porting target fails the test.
Here's some evidence from nearly 1.5 years ago:
http://www.winehq.org/pipermail/wine-patches/2006-October/031544.html
FYI, the change below was prepared by running the following:
git ls-files -z | xargs -0 \
perl -0x3b -pi -e \
's/\bif\s*\(\s*(\S+?)(?:\s*!=\s*NULL)?\s*\)\s+(free\s*\(\s*\1\s*\))/$2/s'
Note however, that it doesn't handle brace-enclosed blocks like
"if (x) { free (x); }". But that's ok, since there were none like
that in git sources.
Beware: if you do use the above snippet, note that it can
produce syntactically invalid C code. That happens when the
affected "if"-statement has a matching "else".
E.g., it would transform this
if (x)
free (x);
else
foo ();
into this:
free (x);
else
foo ();
There were none of those here, either.
If you're interested in automating detection of the useless
tests, you might like the useless-if-before-free script in gnulib:
[it *does* detect brace-enclosed free statements, and has a --name=S
option to make it detect free-like functions with different names]
http://git.sv.gnu.org/gitweb/?p=gnulib.git;a=blob;f=build-aux/useless-if-before-free
Addendum:
Remove one more (in imap-send.c), spotted by Jean-Luc Herren <jlh@gmx.ch>.
Signed-off-by: Jim Meyering <meyering@redhat.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-02-01 01:26:32 +08:00
|
|
|
free(name);
|
2012-09-05 01:31:14 +08:00
|
|
|
name = mkpathdup(fmt, bname.buf);
|
2016-03-29 17:38:39 +08:00
|
|
|
|
|
|
|
if (kinds == FILTER_REFS_BRANCHES) {
|
2022-06-15 03:27:32 +08:00
|
|
|
const char *path;
|
|
|
|
if ((path = branch_checked_out(name))) {
|
2023-10-24 00:06:56 +08:00
|
|
|
error(_("cannot delete branch '%s' "
|
2023-08-08 04:42:36 +08:00
|
|
|
"used by worktree at '%s'"),
|
2022-06-15 03:27:32 +08:00
|
|
|
bname.buf, path);
|
2016-03-29 17:38:39 +08:00
|
|
|
ret = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:42:19 +08:00
|
|
|
target = resolve_refdup(name,
|
|
|
|
RESOLVE_REF_READING
|
|
|
|
| RESOLVE_REF_NO_RECURSE
|
|
|
|
| RESOLVE_REF_ALLOW_BAD_NAME,
|
refs: convert resolve_refdup and refs_resolve_refdup to struct object_id
All of the callers already pass the hash member of struct object_id, so
update them to pass a pointer to the struct directly,
This transformation was done with an update to declaration and
definition and the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_refdup(E1, E2, E3.hash, E4)
+ resolve_refdup(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_refdup(E1, E2, E3->hash, E4)
+ resolve_refdup(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-16 06:06:55 +08:00
|
|
|
&oid, &flags);
|
2014-09-12 01:34:36 +08:00
|
|
|
if (!target) {
|
2023-04-05 19:43:20 +08:00
|
|
|
if (remote_branch) {
|
2023-10-24 00:06:56 +08:00
|
|
|
error(_("remote-tracking branch '%s' not found"), bname.buf);
|
2023-04-05 19:43:20 +08:00
|
|
|
} else {
|
|
|
|
char *virtual_name = mkpathdup(fmt_remotes, bname.buf);
|
|
|
|
char *virtual_target = resolve_refdup(virtual_name,
|
|
|
|
RESOLVE_REF_READING
|
|
|
|
| RESOLVE_REF_NO_RECURSE
|
|
|
|
| RESOLVE_REF_ALLOW_BAD_NAME,
|
|
|
|
&oid, &flags);
|
|
|
|
FREE_AND_NULL(virtual_name);
|
|
|
|
|
|
|
|
if (virtual_target)
|
|
|
|
error(_("branch '%s' not found.\n"
|
|
|
|
"Did you forget --remote?"),
|
|
|
|
bname.buf);
|
|
|
|
else
|
2023-10-24 00:06:56 +08:00
|
|
|
error(_("branch '%s' not found"), bname.buf);
|
2023-04-05 19:43:20 +08:00
|
|
|
FREE_AND_NULL(virtual_target);
|
|
|
|
}
|
2006-12-19 06:42:16 +08:00
|
|
|
ret = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2006-10-24 05:27:45 +08:00
|
|
|
|
refs.c: allow listing and deleting badly named refs
We currently do not handle badly named refs well:
$ cp .git/refs/heads/master .git/refs/heads/master.....@\*@\\.
$ git branch
fatal: Reference has invalid format: 'refs/heads/master.....@*@\.'
$ git branch -D master.....@\*@\\.
error: branch 'master.....@*@\.' not found.
Users cannot recover from a badly named ref without manually finding
and deleting the loose ref file or appropriate line in packed-refs.
Making that easier will make it easier to tweak the ref naming rules
in the future, for example to forbid shell metacharacters like '`'
and '"', without putting people in a state that is hard to get out of.
So allow "branch --list" to show these refs and allow "branch -d/-D"
and "update-ref -d" to delete them. Other commands (for example to
rename refs) will continue to not handle these refs but can be changed
in later patches.
Details:
In resolving functions, refuse to resolve refs that don't pass the
git-check-ref-format(1) check unless the new RESOLVE_REF_ALLOW_BAD_NAME
flag is passed. Even with RESOLVE_REF_ALLOW_BAD_NAME, refuse to
resolve refs that escape the refs/ directory and do not match the
pattern [A-Z_]* (think "HEAD" and "MERGE_HEAD").
In locking functions, refuse to act on badly named refs unless they
are being deleted and either are in the refs/ directory or match [A-Z_]*.
Just like other invalid refs, flag resolved, badly named refs with the
REF_ISBROKEN flag, treat them as resolving to null_sha1, and skip them
in all iteration functions except for for_each_rawref.
Flag badly named refs (but not symrefs pointing to badly named refs)
with a REF_BAD_NAME flag to make it easier for future callers to
notice and handle them specially. For example, in a later patch
for-each-ref will use this flag to detect refs whose names can confuse
callers parsing for-each-ref output.
In the transaction API, refuse to create or update badly named refs,
but allow deleting them (unless they try to escape refs/ and don't match
[A-Z_]*).
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-09-04 02:45:43 +08:00
|
|
|
if (!(flags & (REF_ISSYMREF|REF_ISBROKEN)) &&
|
2017-02-22 07:47:26 +08:00
|
|
|
check_branch_commit(bname.buf, name, &oid, head_rev, kinds,
|
2012-10-18 20:02:51 +08:00
|
|
|
force)) {
|
2006-12-19 06:42:16 +08:00
|
|
|
ret = 1;
|
2016-04-25 16:42:19 +08:00
|
|
|
goto next;
|
2006-10-24 05:27:45 +08:00
|
|
|
}
|
|
|
|
|
2021-01-21 11:23:32 +08:00
|
|
|
item = string_list_append(&refs_to_delete, name);
|
|
|
|
item->util = xstrdup((flags & REF_ISBROKEN) ? "broken"
|
|
|
|
: (flags & REF_ISSYMREF) ? target
|
2023-03-28 21:58:46 +08:00
|
|
|
: repo_find_unique_abbrev(the_repository, &oid, DEFAULT_ABBREV));
|
2016-04-25 16:42:19 +08:00
|
|
|
|
|
|
|
next:
|
|
|
|
free(target);
|
2006-10-24 05:27:45 +08:00
|
|
|
}
|
|
|
|
|
2021-01-21 11:23:32 +08:00
|
|
|
if (delete_refs(NULL, &refs_to_delete, REF_NO_DEREF))
|
|
|
|
ret = 1;
|
|
|
|
|
|
|
|
for_each_string_list_item(item, &refs_to_delete) {
|
|
|
|
char *describe_ref = item->util;
|
|
|
|
char *name = item->string;
|
|
|
|
if (!ref_exists(name)) {
|
|
|
|
char *refname = name + branch_name_pos;
|
|
|
|
if (!quiet)
|
|
|
|
printf(remote_branch
|
|
|
|
? _("Deleted remote-tracking branch %s (was %s).\n")
|
|
|
|
: _("Deleted branch %s (was %s).\n"),
|
|
|
|
name + branch_name_pos, describe_ref);
|
|
|
|
|
|
|
|
delete_branch_config(refname);
|
|
|
|
}
|
|
|
|
free(describe_ref);
|
|
|
|
}
|
|
|
|
string_list_clear(&refs_to_delete, 0);
|
|
|
|
|
Avoid unnecessary "if-before-free" tests.
This change removes all obvious useless if-before-free tests.
E.g., it replaces code like this:
if (some_expression)
free (some_expression);
with the now-equivalent:
free (some_expression);
It is equivalent not just because POSIX has required free(NULL)
to work for a long time, but simply because it has worked for
so long that no reasonable porting target fails the test.
Here's some evidence from nearly 1.5 years ago:
http://www.winehq.org/pipermail/wine-patches/2006-October/031544.html
FYI, the change below was prepared by running the following:
git ls-files -z | xargs -0 \
perl -0x3b -pi -e \
's/\bif\s*\(\s*(\S+?)(?:\s*!=\s*NULL)?\s*\)\s+(free\s*\(\s*\1\s*\))/$2/s'
Note however, that it doesn't handle brace-enclosed blocks like
"if (x) { free (x); }". But that's ok, since there were none like
that in git sources.
Beware: if you do use the above snippet, note that it can
produce syntactically invalid C code. That happens when the
affected "if"-statement has a matching "else".
E.g., it would transform this
if (x)
free (x);
else
foo ();
into this:
free (x);
else
foo ();
There were none of those here, either.
If you're interested in automating detection of the useless
tests, you might like the useless-if-before-free script in gnulib:
[it *does* detect brace-enclosed free statements, and has a --name=S
option to make it detect free-like functions with different names]
http://git.sv.gnu.org/gitweb/?p=gnulib.git;a=blob;f=build-aux/useless-if-before-free
Addendum:
Remove one more (in imap-send.c), spotted by Jean-Luc Herren <jlh@gmx.ch>.
Signed-off-by: Jim Meyering <meyering@redhat.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-02-01 01:26:32 +08:00
|
|
|
free(name);
|
2017-10-04 06:17:40 +08:00
|
|
|
strbuf_release(&bname);
|
2006-12-19 06:42:16 +08:00
|
|
|
|
2017-10-04 06:17:40 +08:00
|
|
|
return ret;
|
2006-10-24 05:27:45 +08:00
|
|
|
}
|
|
|
|
|
2017-01-10 16:49:52 +08:00
|
|
|
static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
|
2008-07-02 15:52:41 +08:00
|
|
|
{
|
2017-01-10 16:49:52 +08:00
|
|
|
int i, max = 0;
|
|
|
|
for (i = 0; i < refs->nr; i++) {
|
|
|
|
struct ref_array_item *it = refs->items[i];
|
|
|
|
const char *desc = it->refname;
|
|
|
|
int w;
|
2013-04-15 10:37:49 +08:00
|
|
|
|
2017-01-10 16:49:52 +08:00
|
|
|
skip_prefix(it->refname, "refs/heads/", &desc);
|
|
|
|
skip_prefix(it->refname, "refs/remotes/", &desc);
|
|
|
|
if (it->kind == FILTER_REFS_DETACHED_HEAD) {
|
|
|
|
char *head_desc = get_head_description();
|
|
|
|
w = utf8_strwidth(head_desc);
|
|
|
|
free(head_desc);
|
|
|
|
} else
|
|
|
|
w = utf8_strwidth(desc);
|
2012-05-03 21:12:00 +08:00
|
|
|
|
2017-01-10 16:49:52 +08:00
|
|
|
if (it->kind == FILTER_REFS_REMOTES)
|
|
|
|
w += remote_bonus;
|
|
|
|
if (w > max)
|
|
|
|
max = w;
|
2012-05-03 21:12:00 +08:00
|
|
|
}
|
2017-01-10 16:49:52 +08:00
|
|
|
return max;
|
2008-07-02 15:52:41 +08:00
|
|
|
}
|
|
|
|
|
2017-01-10 16:49:52 +08:00
|
|
|
static const char *quote_literal_for_format(const char *s)
|
2011-03-16 15:10:14 +08:00
|
|
|
{
|
2017-01-10 16:49:52 +08:00
|
|
|
static struct strbuf buf = STRBUF_INIT;
|
|
|
|
|
|
|
|
strbuf_reset(&buf);
|
2023-06-18 04:41:44 +08:00
|
|
|
while (strbuf_expand_step(&buf, &s))
|
|
|
|
strbuf_addstr(&buf, "%%");
|
2017-01-10 16:49:52 +08:00
|
|
|
return buf.buf;
|
2011-03-16 15:10:14 +08:00
|
|
|
}
|
|
|
|
|
2017-01-10 16:49:52 +08:00
|
|
|
static char *build_format(struct ref_filter *filter, int maxwidth, const char *remote_prefix)
|
2006-11-24 21:45:10 +08:00
|
|
|
{
|
2017-01-10 16:49:52 +08:00
|
|
|
struct strbuf fmt = STRBUF_INIT;
|
|
|
|
struct strbuf local = STRBUF_INIT;
|
|
|
|
struct strbuf remote = STRBUF_INIT;
|
2006-11-24 21:45:10 +08:00
|
|
|
|
2019-04-29 13:19:43 +08:00
|
|
|
strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)%%(if)%%(worktreepath)%%(then)+ %s%%(else) %s%%(end)%%(end)",
|
|
|
|
branch_get_color(BRANCH_COLOR_CURRENT),
|
|
|
|
branch_get_color(BRANCH_COLOR_WORKTREE),
|
|
|
|
branch_get_color(BRANCH_COLOR_LOCAL));
|
2017-07-09 18:00:45 +08:00
|
|
|
strbuf_addf(&remote, " %s",
|
|
|
|
branch_get_color(BRANCH_COLOR_REMOTE));
|
2006-11-24 21:45:10 +08:00
|
|
|
|
2015-09-24 02:11:11 +08:00
|
|
|
if (filter->verbose) {
|
2017-03-09 06:13:09 +08:00
|
|
|
struct strbuf obname = STRBUF_INIT;
|
|
|
|
|
|
|
|
if (filter->abbrev < 0)
|
|
|
|
strbuf_addf(&obname, "%%(objectname:short)");
|
|
|
|
else if (!filter->abbrev)
|
|
|
|
strbuf_addf(&obname, "%%(objectname)");
|
|
|
|
else
|
|
|
|
strbuf_addf(&obname, "%%(objectname:short=%d)", filter->abbrev);
|
|
|
|
|
2017-01-10 16:49:52 +08:00
|
|
|
strbuf_addf(&local, "%%(align:%d,left)%%(refname:lstrip=2)%%(end)", maxwidth);
|
2017-10-01 22:44:20 +08:00
|
|
|
strbuf_addstr(&local, branch_get_color(BRANCH_COLOR_RESET));
|
2017-03-09 06:13:09 +08:00
|
|
|
strbuf_addf(&local, " %s ", obname.buf);
|
2017-01-10 16:49:52 +08:00
|
|
|
|
|
|
|
if (filter->verbose > 1)
|
2019-04-29 13:19:44 +08:00
|
|
|
{
|
|
|
|
strbuf_addf(&local, "%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)(%s%%(worktreepath)%s) %%(end)%%(end)",
|
|
|
|
branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET));
|
2017-01-10 16:49:52 +08:00
|
|
|
strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)"
|
|
|
|
"%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)",
|
|
|
|
branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET));
|
2019-04-29 13:19:44 +08:00
|
|
|
}
|
2017-01-10 16:49:52 +08:00
|
|
|
else
|
|
|
|
strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)");
|
|
|
|
|
2017-07-09 18:00:45 +08:00
|
|
|
strbuf_addf(&remote, "%%(align:%d,left)%s%%(refname:lstrip=2)%%(end)%s"
|
2017-03-09 06:13:09 +08:00
|
|
|
"%%(if)%%(symref)%%(then) -> %%(symref:short)"
|
|
|
|
"%%(else) %s %%(contents:subject)%%(end)",
|
2017-07-09 18:00:45 +08:00
|
|
|
maxwidth, quote_literal_for_format(remote_prefix),
|
2017-03-09 06:13:09 +08:00
|
|
|
branch_get_color(BRANCH_COLOR_RESET), obname.buf);
|
|
|
|
strbuf_release(&obname);
|
2012-04-13 18:54:38 +08:00
|
|
|
} else {
|
2017-01-10 16:49:52 +08:00
|
|
|
strbuf_addf(&local, "%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)",
|
|
|
|
branch_get_color(BRANCH_COLOR_RESET));
|
2017-07-09 18:00:45 +08:00
|
|
|
strbuf_addf(&remote, "%s%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)",
|
|
|
|
quote_literal_for_format(remote_prefix),
|
2017-01-10 16:49:52 +08:00
|
|
|
branch_get_color(BRANCH_COLOR_RESET));
|
2012-04-13 18:54:38 +08:00
|
|
|
}
|
2006-11-24 21:45:10 +08:00
|
|
|
|
2017-01-10 16:49:52 +08:00
|
|
|
strbuf_addf(&fmt, "%%(if:notequals=refs/remotes)%%(refname:rstrip=-2)%%(then)%s%%(else)%s%%(end)", local.buf, remote.buf);
|
2015-09-24 02:11:12 +08:00
|
|
|
|
2017-01-10 16:49:52 +08:00
|
|
|
strbuf_release(&local);
|
|
|
|
strbuf_release(&remote);
|
|
|
|
return strbuf_detach(&fmt, NULL);
|
2009-07-24 03:13:48 +08:00
|
|
|
}
|
|
|
|
|
2021-10-21 02:27:21 +08:00
|
|
|
static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting,
|
|
|
|
struct ref_format *format, struct string_list *output)
|
2006-10-24 05:27:45 +08:00
|
|
|
{
|
|
|
|
int i;
|
2015-09-24 02:11:11 +08:00
|
|
|
struct ref_array array;
|
2015-09-24 02:11:06 +08:00
|
|
|
int maxwidth = 0;
|
|
|
|
const char *remote_prefix = "";
|
2017-01-10 16:49:53 +08:00
|
|
|
char *to_free = NULL;
|
2015-09-24 02:11:06 +08:00
|
|
|
|
2015-07-08 00:06:12 +08:00
|
|
|
/*
|
2015-09-24 02:11:06 +08:00
|
|
|
* If we are listing more than just remote branches,
|
|
|
|
* then remote branches will have a "remotes/" prefix.
|
|
|
|
* We need to account for this in the width.
|
2015-07-08 00:06:12 +08:00
|
|
|
*/
|
2015-09-24 02:11:11 +08:00
|
|
|
if (filter->kind != FILTER_REFS_REMOTES)
|
2015-09-24 02:11:06 +08:00
|
|
|
remote_prefix = "remotes/";
|
2014-09-18 18:49:43 +08:00
|
|
|
|
2015-09-24 02:11:11 +08:00
|
|
|
memset(&array, 0, sizeof(array));
|
2014-09-18 18:49:43 +08:00
|
|
|
|
ref-filter: stop setting FILTER_REFS_INCLUDE_BROKEN
Of the ref-filter callers, for-each-ref and git-branch both set the
INCLUDE_BROKEN flag (but git-tag does not, which is a weird
inconsistency). But now that GIT_REF_PARANOIA is on by default, that
produces almost the same outcome for all three.
The one exception is that GIT_REF_PARANOIA will omit dangling symrefs.
That's a better behavior for these tools, as they would never include
such a symref in the main output anyway (they can't, as it doesn't point
to an object). Instead they issue a warning to stderr. But that warning
is somewhat useless; a dangling symref is a perfectly reasonable thing
to have in your repository, and is not a sign of corruption. It's much
friendlier to just quietly ignore it.
And in terms of robustness, the warning gains us little. It does not
impact the exit code of either tool. So while the warning _might_ clue
in a user that they have an unexpected broken symref, it would not help
any kind of scripted use.
This patch converts for-each-ref and git-branch to stop using the
INCLUDE_BROKEN flag. That gives them more reasonable behavior, and
harmonizes them with git-tag.
We have to change one test to adapt to the situation. t1430 tries to
trigger all of the REF_ISBROKEN behaviors from the underlying ref code.
It uses for-each-ref to do so (because there isn't any other mechanism).
That will no longer issue a warning about the symref which points to an
invalid name, as it's considered dangling (and we can instead be sure
that it's _not_ mentioned on stderr). Note that we do still complain
about the illegally named "broken..symref"; its problem is not that it's
dangling, but the name of the symref itself is illegal.
Signed-off-by: Jeff King <peff@peff.net>
Reviewed-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-09-25 02:48:05 +08:00
|
|
|
filter_refs(&array, filter, filter->kind);
|
2006-10-24 05:27:45 +08:00
|
|
|
|
2015-09-24 02:11:11 +08:00
|
|
|
if (filter->verbose)
|
|
|
|
maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
|
2006-11-22 03:31:24 +08:00
|
|
|
|
2017-07-13 23:01:18 +08:00
|
|
|
if (!format->format)
|
|
|
|
format->format = to_free = build_format(filter, maxwidth, remote_prefix);
|
ref-filter: consult want_color() before emitting colors
When color placeholders like %(color:red) are used in a
ref-filter format, we unconditionally output the colors,
even if the user has asked us for no colors. This usually
isn't a problem when the user is constructing a --format on
the command line, but it means we may do the wrong thing
when the format is fed from a script or alias. For example:
$ git config alias.b 'branch --format=%(color:green)%(refname)'
$ git b --no-color
should probably omit the green color. Likewise, running:
$ git b >branches
should probably also omit the color, just as we would for
all baked-in coloring (and as we recently started to do for
user-specified colors in --pretty formats).
This commit makes both of those cases work by teaching
the ref-filter code to consult want_color() before
outputting any color. The color flag in ref_format defaults
to "-1", which means we'll consult color.ui, which in turn
defaults to the usual isatty() check on stdout. However,
callers like git-branch which support their own color config
(and command-line options) can override that.
The new tests independently cover all three of the callers
of ref-filter (for-each-ref, tag, and branch). Even though
these seem redundant, it confirms that we've correctly
plumbed through all of the necessary config to make colors
work by default.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-07-13 23:09:32 +08:00
|
|
|
format->use_color = branch_use_color;
|
2017-07-13 22:56:10 +08:00
|
|
|
|
|
|
|
if (verify_ref_format(format))
|
|
|
|
die(_("unable to parse format string"));
|
2017-01-10 16:49:52 +08:00
|
|
|
|
for-each-ref: add ahead-behind format atom
The previous change implemented the ahead_behind() method, including an
algorithm to compute the ahead/behind values for a number of commit tips
relative to a number of commit bases. Now, integrate that algorithm as
part of 'git for-each-ref' hidden behind a new format atom,
ahead-behind. This naturally extends to 'git branch' and 'git tag'
builtins, as well.
This format allows specifying multiple bases, if so desired, and all
matching references are compared against all of those bases. For this
reason, failing to read a reference provided from these atoms results in
an error.
In order to translate the ahead_behind() method information to the
format output code in ref-filter.c, we must populate arrays of
ahead_behind_count structs. In struct ref_array, we store the full array
that will be passed to ahead_behind(). In struct ref_array_item, we
store an array of pointers that point to the relvant items within the
full array. In this way, we can pull all relevant ahead/behind values
directly when formatting output for a specific item. It also ensures the
lifetime of the ahead_behind_count structs matches the time that the
array is being used.
Add specific tests of the ahead/behind counts in t6600-test-reach.sh, as
it has an interesting repository shape. In particular, its merging
strategy and its use of different commit-graphs would demonstrate over-
counting if the ahead_behind() method did not already account for that
possibility.
Also add tests for the specific for-each-ref, branch, and tag builtins.
In the case of 'git tag', there are intersting cases that happen when
some of the selected tips are not commits. This requires careful logic
around commits_nr in the second loop of filter_ahead_behind(). Also, the
test in t7004 is carefully located to avoid being dependent on the GPG
prereq. It also avoids using the test_commit helper, as that will add
ticks to the time and disrupt the expected timestamps in later tag
tests.
Also add performance tests in a new p1300-graph-walks.sh script. This
will be useful for more uses in the future, but for now compare the
ahead-behind counting algorithm in 'git for-each-ref' to the naive
implementation by running 'git rev-list --count' processes for each
input.
For the Git source code repository, the improvement is already obvious:
Test this tree
---------------------------------------------------------------
1500.2: ahead-behind counts: git for-each-ref 0.07(0.07+0.00)
1500.3: ahead-behind counts: git branch 0.07(0.06+0.00)
1500.4: ahead-behind counts: git tag 0.07(0.06+0.00)
1500.5: ahead-behind counts: git rev-list 1.32(1.04+0.27)
But the standard performance benchmark is the Linux kernel repository,
which demosntrates a significant improvement:
Test this tree
---------------------------------------------------------------
1500.2: ahead-behind counts: git for-each-ref 0.27(0.24+0.02)
1500.3: ahead-behind counts: git branch 0.27(0.24+0.03)
1500.4: ahead-behind counts: git tag 0.28(0.27+0.01)
1500.5: ahead-behind counts: git rev-list 4.57(4.03+0.54)
The 'git rev-list' test exists in this change as a demonstration, but it
will be removed in the next change to avoid wasting time on this
comparison.
Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-03-20 19:26:54 +08:00
|
|
|
filter_ahead_behind(the_repository, format, &array);
|
2015-09-24 02:11:12 +08:00
|
|
|
ref_array_sort(sorting, &array);
|
2010-06-04 17:50:10 +08:00
|
|
|
|
2023-11-15 03:53:52 +08:00
|
|
|
if (column_active(colopts)) {
|
|
|
|
struct strbuf out = STRBUF_INIT, err = STRBUF_INIT;
|
|
|
|
|
|
|
|
assert(!filter->verbose && "--column and --verbose are incompatible");
|
|
|
|
|
|
|
|
for (i = 0; i < array.nr; i++) {
|
|
|
|
strbuf_reset(&err);
|
|
|
|
strbuf_reset(&out);
|
|
|
|
if (format_ref_array_item(array.items[i], format, &out, &err))
|
|
|
|
die("%s", err.buf);
|
|
|
|
|
|
|
|
/* format to a string_list to let print_columns() do its job */
|
2021-10-21 02:27:21 +08:00
|
|
|
string_list_append(output, out.buf);
|
2017-01-10 16:49:52 +08:00
|
|
|
}
|
2023-11-15 03:53:52 +08:00
|
|
|
|
|
|
|
strbuf_release(&err);
|
|
|
|
strbuf_release(&out);
|
|
|
|
} else {
|
|
|
|
print_formatted_ref_array(&array, format);
|
2017-01-10 16:49:52 +08:00
|
|
|
}
|
2010-06-04 17:50:11 +08:00
|
|
|
|
2015-09-24 02:11:11 +08:00
|
|
|
ref_array_clear(&array);
|
2017-01-10 16:49:53 +08:00
|
|
|
free(to_free);
|
2006-10-24 05:27:45 +08:00
|
|
|
}
|
|
|
|
|
2018-10-26 03:04:21 +08:00
|
|
|
static void print_current_branch_name(void)
|
|
|
|
{
|
|
|
|
int flags;
|
|
|
|
const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
|
|
|
|
const char *shortname;
|
|
|
|
if (!refname)
|
|
|
|
die(_("could not resolve HEAD"));
|
|
|
|
else if (!(flags & REF_ISSYMREF))
|
|
|
|
return;
|
|
|
|
else if (skip_prefix(refname, "refs/heads/", &shortname))
|
|
|
|
puts(shortname);
|
|
|
|
else
|
|
|
|
die(_("HEAD (%s) points outside of refs/heads/"), refname);
|
|
|
|
}
|
|
|
|
|
2023-03-27 06:33:09 +08:00
|
|
|
static void reject_rebase_or_bisect_branch(struct worktree **worktrees,
|
|
|
|
const char *target)
|
2016-04-22 21:01:36 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; worktrees[i]; i++) {
|
|
|
|
struct worktree *wt = worktrees[i];
|
|
|
|
|
|
|
|
if (!wt->is_detached)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (is_worktree_being_rebased(wt, target))
|
2023-10-24 00:06:56 +08:00
|
|
|
die(_("branch %s is being rebased at %s"),
|
2016-04-22 21:01:36 +08:00
|
|
|
target, wt->path);
|
|
|
|
|
|
|
|
if (is_worktree_being_bisected(wt, target))
|
2023-10-24 00:06:56 +08:00
|
|
|
die(_("branch %s is being bisected at %s"),
|
2016-04-22 21:01:36 +08:00
|
|
|
target, wt->path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-27 06:33:02 +08:00
|
|
|
/*
|
|
|
|
* Update all per-worktree HEADs pointing at the old ref to point the new ref.
|
|
|
|
* This will be used when renaming a branch. Returns 0 if successful, non-zero
|
|
|
|
* otherwise.
|
|
|
|
*/
|
2023-03-27 06:33:09 +08:00
|
|
|
static int replace_each_worktree_head_symref(struct worktree **worktrees,
|
|
|
|
const char *oldref, const char *newref,
|
2023-03-27 06:33:02 +08:00
|
|
|
const char *logmsg)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; worktrees[i]; i++) {
|
|
|
|
struct ref_store *refs;
|
|
|
|
|
|
|
|
if (worktrees[i]->is_detached)
|
|
|
|
continue;
|
|
|
|
if (!worktrees[i]->head_ref)
|
|
|
|
continue;
|
|
|
|
if (strcmp(oldref, worktrees[i]->head_ref))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
refs = get_worktree_ref_store(worktrees[i]);
|
|
|
|
if (refs_create_symref(refs, "HEAD", newref, logmsg))
|
|
|
|
ret = error(_("HEAD of working tree %s is not updated"),
|
|
|
|
worktrees[i]->path);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2016-04-22 21:01:36 +08:00
|
|
|
}
|
|
|
|
|
2023-03-27 06:33:17 +08:00
|
|
|
#define IS_HEAD 1
|
2023-03-27 06:33:27 +08:00
|
|
|
#define IS_ORPHAN 2
|
2023-03-27 06:33:17 +08:00
|
|
|
|
branch: add a --copy (-c) option to go with --move (-m)
Add the ability to --copy a branch and its reflog and configuration,
this uses the same underlying machinery as the --move (-m) option
except the reflog and configuration is copied instead of being moved.
This is useful for e.g. copying a topic branch to a new version,
e.g. work to work-2 after submitting the work topic to the list, while
preserving all the tracking info and other configuration that goes
with the branch, and unlike --move keeping the other already-submitted
branch around for reference.
Like --move, when the source branch is the currently checked out
branch the HEAD is moved to the destination branch. In the case of
--move we don't really have a choice (other than remaining on a
detached HEAD) and in order to keep the functionality consistent, we
are doing it in similar way for --copy too.
The most common usage of this feature is expected to be moving to a
new topic branch which is a copy of the current one, in that case
moving to the target branch is what the user wants, and doesn't
unexpectedly behave differently than --move would.
One outstanding caveat of this implementation is that:
git checkout maint &&
git checkout master &&
git branch -c topic &&
git checkout -
Will check out 'maint' instead of 'master'. This is because the @{-N}
feature (or its -1 shorthand "-") relies on HEAD reflogs created by
the checkout command, so in this case we'll checkout maint instead of
master, as the user might expect. What to do about that is left to a
future change.
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Sahil Dua <sahildua2305@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-06-19 05:19:16 +08:00
|
|
|
static void copy_or_rename_branch(const char *oldname, const char *newname, int copy, int force)
|
2006-11-28 22:47:40 +08:00
|
|
|
{
|
2008-11-18 04:48:37 +08:00
|
|
|
struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
|
|
|
|
struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
|
2017-12-01 13:59:33 +08:00
|
|
|
const char *interpreted_oldname = NULL;
|
|
|
|
const char *interpreted_newname = NULL;
|
2023-03-27 06:33:17 +08:00
|
|
|
int recovery = 0, oldref_usage = 0;
|
2023-03-27 06:33:09 +08:00
|
|
|
struct worktree **worktrees = get_worktrees();
|
2006-11-28 22:47:40 +08:00
|
|
|
|
check_ref_format(): tighten refname rules
This changes the rules for refnames to forbid:
(1) a refname that contains "@{" in it.
Some people and foreign SCM converter may have named their branches
as frotz@24 and we still want to keep supporting it.
However, "git branch frotz@{24}" is a disaster. It cannot even
checked out because "git checkout frotz@{24}" will interpret it as
"detach the HEAD at twenty-fourth reflog entry of the frotz branch".
(2) a refname that ends with a dot.
We already reject a path component that begins with a dot, primarily
to avoid ambiguous range interpretation. If we allowed ".B" as a
valid ref, it is unclear if "A...B" means "in dot-B but not in A" or
"either in A or B but not in both".
But for this to be complete, we need also to forbid "A." to avoid "in
B but not in A-dot". This was not a problem in the original range
notation, but we should have added this restriction when three-dot
notation was introduced.
Unlike "no dot at the beginning of any path component" rule, this
rule does not have to be "no dot at the end of any path component",
because you cannot abbreviate the tail end away, similar to you can
say "dot-B" to mean "refs/heads/dot-B".
For these reasons, it is not likely people created branches with these
names on purpose, but we have allowed such names to be used for quite some
time, and it is possible that people created such branches by mistake or
by accident.
To help people with branches with such unfortunate names to recover,
we still allow "branch -d 'bad.'" to delete such branches, and also allow
"branch -m bad. good" to rename them.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-22 04:27:31 +08:00
|
|
|
if (strbuf_check_branch_ref(&oldref, oldname)) {
|
|
|
|
/*
|
|
|
|
* Bad name --- this could be an attempt to rename a
|
|
|
|
* ref that we used to allow to be created by accident.
|
|
|
|
*/
|
2011-11-13 18:22:14 +08:00
|
|
|
if (ref_exists(oldref.buf))
|
check_ref_format(): tighten refname rules
This changes the rules for refnames to forbid:
(1) a refname that contains "@{" in it.
Some people and foreign SCM converter may have named their branches
as frotz@24 and we still want to keep supporting it.
However, "git branch frotz@{24}" is a disaster. It cannot even
checked out because "git checkout frotz@{24}" will interpret it as
"detach the HEAD at twenty-fourth reflog entry of the frotz branch".
(2) a refname that ends with a dot.
We already reject a path component that begins with a dot, primarily
to avoid ambiguous range interpretation. If we allowed ".B" as a
valid ref, it is unclear if "A...B" means "in dot-B but not in A" or
"either in A or B but not in both".
But for this to be complete, we need also to forbid "A." to avoid "in
B but not in A-dot". This was not a problem in the original range
notation, but we should have added this restriction when three-dot
notation was introduced.
Unlike "no dot at the beginning of any path component" rule, this
rule does not have to be "no dot at the end of any path component",
because you cannot abbreviate the tail end away, similar to you can
say "dot-B" to mean "refs/heads/dot-B".
For these reasons, it is not likely people created branches with these
names on purpose, but we have allowed such names to be used for quite some
time, and it is possible that people created such branches by mistake or
by accident.
To help people with branches with such unfortunate names to recover,
we still allow "branch -d 'bad.'" to delete such branches, and also allow
"branch -m bad. good" to rename them.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-22 04:27:31 +08:00
|
|
|
recovery = 1;
|
|
|
|
else
|
2023-10-24 00:06:56 +08:00
|
|
|
die(_("invalid branch name: '%s'"), oldname);
|
check_ref_format(): tighten refname rules
This changes the rules for refnames to forbid:
(1) a refname that contains "@{" in it.
Some people and foreign SCM converter may have named their branches
as frotz@24 and we still want to keep supporting it.
However, "git branch frotz@{24}" is a disaster. It cannot even
checked out because "git checkout frotz@{24}" will interpret it as
"detach the HEAD at twenty-fourth reflog entry of the frotz branch".
(2) a refname that ends with a dot.
We already reject a path component that begins with a dot, primarily
to avoid ambiguous range interpretation. If we allowed ".B" as a
valid ref, it is unclear if "A...B" means "in dot-B but not in A" or
"either in A or B but not in both".
But for this to be complete, we need also to forbid "A." to avoid "in
B but not in A-dot". This was not a problem in the original range
notation, but we should have added this restriction when three-dot
notation was introduced.
Unlike "no dot at the beginning of any path component" rule, this
rule does not have to be "no dot at the end of any path component",
because you cannot abbreviate the tail end away, similar to you can
say "dot-B" to mean "refs/heads/dot-B".
For these reasons, it is not likely people created branches with these
names on purpose, but we have allowed such names to be used for quite some
time, and it is possible that people created such branches by mistake or
by accident.
To help people with branches with such unfortunate names to recover,
we still allow "branch -d 'bad.'" to delete such branches, and also allow
"branch -m bad. good" to rename them.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-22 04:27:31 +08:00
|
|
|
}
|
2006-11-28 22:47:40 +08:00
|
|
|
|
2023-03-27 06:33:17 +08:00
|
|
|
for (int i = 0; worktrees[i]; i++) {
|
|
|
|
struct worktree *wt = worktrees[i];
|
|
|
|
|
|
|
|
if (wt->head_ref && !strcmp(oldref.buf, wt->head_ref)) {
|
|
|
|
oldref_usage |= IS_HEAD;
|
2023-03-27 06:33:27 +08:00
|
|
|
if (is_null_oid(&wt->head_oid))
|
|
|
|
oldref_usage |= IS_ORPHAN;
|
2023-03-27 06:33:17 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((copy || !(oldref_usage & IS_HEAD)) && !ref_exists(oldref.buf)) {
|
|
|
|
if (oldref_usage & IS_HEAD)
|
2023-10-24 00:06:56 +08:00
|
|
|
die(_("no commit on branch '%s' yet"), oldname);
|
2022-10-08 08:39:43 +08:00
|
|
|
else
|
2023-10-24 00:06:56 +08:00
|
|
|
die(_("no branch named '%s'"), oldname);
|
2022-10-08 08:39:43 +08:00
|
|
|
}
|
|
|
|
|
2011-11-26 10:30:02 +08:00
|
|
|
/*
|
|
|
|
* A command like "git branch -M currentbranch currentbranch" cannot
|
|
|
|
* cause the worktree to become inconsistent with HEAD, so allow it.
|
|
|
|
*/
|
2017-10-13 12:45:40 +08:00
|
|
|
if (!strcmp(oldname, newname))
|
|
|
|
validate_branchname(newname, &newref);
|
|
|
|
else
|
|
|
|
validate_new_branchname(newname, &newref, force);
|
2006-11-28 22:47:40 +08:00
|
|
|
|
2023-03-27 06:33:09 +08:00
|
|
|
reject_rebase_or_bisect_branch(worktrees, oldref.buf);
|
2016-04-22 21:01:36 +08:00
|
|
|
|
2017-12-01 13:59:33 +08:00
|
|
|
if (!skip_prefix(oldref.buf, "refs/heads/", &interpreted_oldname) ||
|
|
|
|
!skip_prefix(newref.buf, "refs/heads/", &interpreted_newname)) {
|
2018-05-02 17:38:39 +08:00
|
|
|
BUG("expected prefix missing for refs");
|
2017-12-01 13:59:33 +08:00
|
|
|
}
|
|
|
|
|
branch: add a --copy (-c) option to go with --move (-m)
Add the ability to --copy a branch and its reflog and configuration,
this uses the same underlying machinery as the --move (-m) option
except the reflog and configuration is copied instead of being moved.
This is useful for e.g. copying a topic branch to a new version,
e.g. work to work-2 after submitting the work topic to the list, while
preserving all the tracking info and other configuration that goes
with the branch, and unlike --move keeping the other already-submitted
branch around for reference.
Like --move, when the source branch is the currently checked out
branch the HEAD is moved to the destination branch. In the case of
--move we don't really have a choice (other than remaining on a
detached HEAD) and in order to keep the functionality consistent, we
are doing it in similar way for --copy too.
The most common usage of this feature is expected to be moving to a
new topic branch which is a copy of the current one, in that case
moving to the target branch is what the user wants, and doesn't
unexpectedly behave differently than --move would.
One outstanding caveat of this implementation is that:
git checkout maint &&
git checkout master &&
git branch -c topic &&
git checkout -
Will check out 'maint' instead of 'master'. This is because the @{-N}
feature (or its -1 shorthand "-") relies on HEAD reflogs created by
the checkout command, so in this case we'll checkout maint instead of
master, as the user might expect. What to do about that is left to a
future change.
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Sahil Dua <sahildua2305@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-06-19 05:19:16 +08:00
|
|
|
if (copy)
|
|
|
|
strbuf_addf(&logmsg, "Branch: copied %s to %s",
|
|
|
|
oldref.buf, newref.buf);
|
|
|
|
else
|
|
|
|
strbuf_addf(&logmsg, "Branch: renamed %s to %s",
|
|
|
|
oldref.buf, newref.buf);
|
2006-11-30 10:16:56 +08:00
|
|
|
|
2023-03-27 06:33:27 +08:00
|
|
|
if (!copy && !(oldref_usage & IS_ORPHAN) &&
|
2020-12-11 19:36:55 +08:00
|
|
|
rename_ref(oldref.buf, newref.buf, logmsg.buf))
|
2023-10-24 00:06:56 +08:00
|
|
|
die(_("branch rename failed"));
|
branch: add a --copy (-c) option to go with --move (-m)
Add the ability to --copy a branch and its reflog and configuration,
this uses the same underlying machinery as the --move (-m) option
except the reflog and configuration is copied instead of being moved.
This is useful for e.g. copying a topic branch to a new version,
e.g. work to work-2 after submitting the work topic to the list, while
preserving all the tracking info and other configuration that goes
with the branch, and unlike --move keeping the other already-submitted
branch around for reference.
Like --move, when the source branch is the currently checked out
branch the HEAD is moved to the destination branch. In the case of
--move we don't really have a choice (other than remaining on a
detached HEAD) and in order to keep the functionality consistent, we
are doing it in similar way for --copy too.
The most common usage of this feature is expected to be moving to a
new topic branch which is a copy of the current one, in that case
moving to the target branch is what the user wants, and doesn't
unexpectedly behave differently than --move would.
One outstanding caveat of this implementation is that:
git checkout maint &&
git checkout master &&
git branch -c topic &&
git checkout -
Will check out 'maint' instead of 'master'. This is because the @{-N}
feature (or its -1 shorthand "-") relies on HEAD reflogs created by
the checkout command, so in this case we'll checkout maint instead of
master, as the user might expect. What to do about that is left to a
future change.
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Sahil Dua <sahildua2305@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-06-19 05:19:16 +08:00
|
|
|
if (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf))
|
2023-10-24 00:06:56 +08:00
|
|
|
die(_("branch copy failed"));
|
2006-11-28 22:47:40 +08:00
|
|
|
|
branch: add a --copy (-c) option to go with --move (-m)
Add the ability to --copy a branch and its reflog and configuration,
this uses the same underlying machinery as the --move (-m) option
except the reflog and configuration is copied instead of being moved.
This is useful for e.g. copying a topic branch to a new version,
e.g. work to work-2 after submitting the work topic to the list, while
preserving all the tracking info and other configuration that goes
with the branch, and unlike --move keeping the other already-submitted
branch around for reference.
Like --move, when the source branch is the currently checked out
branch the HEAD is moved to the destination branch. In the case of
--move we don't really have a choice (other than remaining on a
detached HEAD) and in order to keep the functionality consistent, we
are doing it in similar way for --copy too.
The most common usage of this feature is expected to be moving to a
new topic branch which is a copy of the current one, in that case
moving to the target branch is what the user wants, and doesn't
unexpectedly behave differently than --move would.
One outstanding caveat of this implementation is that:
git checkout maint &&
git checkout master &&
git branch -c topic &&
git checkout -
Will check out 'maint' instead of 'master'. This is because the @{-N}
feature (or its -1 shorthand "-") relies on HEAD reflogs created by
the checkout command, so in this case we'll checkout maint instead of
master, as the user might expect. What to do about that is left to a
future change.
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Sahil Dua <sahildua2305@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-06-19 05:19:16 +08:00
|
|
|
if (recovery) {
|
|
|
|
if (copy)
|
2023-10-24 00:06:56 +08:00
|
|
|
warning(_("created a copy of a misnamed branch '%s'"),
|
2017-12-01 13:59:33 +08:00
|
|
|
interpreted_oldname);
|
branch: add a --copy (-c) option to go with --move (-m)
Add the ability to --copy a branch and its reflog and configuration,
this uses the same underlying machinery as the --move (-m) option
except the reflog and configuration is copied instead of being moved.
This is useful for e.g. copying a topic branch to a new version,
e.g. work to work-2 after submitting the work topic to the list, while
preserving all the tracking info and other configuration that goes
with the branch, and unlike --move keeping the other already-submitted
branch around for reference.
Like --move, when the source branch is the currently checked out
branch the HEAD is moved to the destination branch. In the case of
--move we don't really have a choice (other than remaining on a
detached HEAD) and in order to keep the functionality consistent, we
are doing it in similar way for --copy too.
The most common usage of this feature is expected to be moving to a
new topic branch which is a copy of the current one, in that case
moving to the target branch is what the user wants, and doesn't
unexpectedly behave differently than --move would.
One outstanding caveat of this implementation is that:
git checkout maint &&
git checkout master &&
git branch -c topic &&
git checkout -
Will check out 'maint' instead of 'master'. This is because the @{-N}
feature (or its -1 shorthand "-") relies on HEAD reflogs created by
the checkout command, so in this case we'll checkout maint instead of
master, as the user might expect. What to do about that is left to a
future change.
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Sahil Dua <sahildua2305@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-06-19 05:19:16 +08:00
|
|
|
else
|
2023-10-24 00:06:56 +08:00
|
|
|
warning(_("renamed a misnamed branch '%s' away"),
|
2017-12-01 13:59:33 +08:00
|
|
|
interpreted_oldname);
|
branch: add a --copy (-c) option to go with --move (-m)
Add the ability to --copy a branch and its reflog and configuration,
this uses the same underlying machinery as the --move (-m) option
except the reflog and configuration is copied instead of being moved.
This is useful for e.g. copying a topic branch to a new version,
e.g. work to work-2 after submitting the work topic to the list, while
preserving all the tracking info and other configuration that goes
with the branch, and unlike --move keeping the other already-submitted
branch around for reference.
Like --move, when the source branch is the currently checked out
branch the HEAD is moved to the destination branch. In the case of
--move we don't really have a choice (other than remaining on a
detached HEAD) and in order to keep the functionality consistent, we
are doing it in similar way for --copy too.
The most common usage of this feature is expected to be moving to a
new topic branch which is a copy of the current one, in that case
moving to the target branch is what the user wants, and doesn't
unexpectedly behave differently than --move would.
One outstanding caveat of this implementation is that:
git checkout maint &&
git checkout master &&
git branch -c topic &&
git checkout -
Will check out 'maint' instead of 'master'. This is because the @{-N}
feature (or its -1 shorthand "-") relies on HEAD reflogs created by
the checkout command, so in this case we'll checkout maint instead of
master, as the user might expect. What to do about that is left to a
future change.
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Sahil Dua <sahildua2305@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-06-19 05:19:16 +08:00
|
|
|
}
|
check_ref_format(): tighten refname rules
This changes the rules for refnames to forbid:
(1) a refname that contains "@{" in it.
Some people and foreign SCM converter may have named their branches
as frotz@24 and we still want to keep supporting it.
However, "git branch frotz@{24}" is a disaster. It cannot even
checked out because "git checkout frotz@{24}" will interpret it as
"detach the HEAD at twenty-fourth reflog entry of the frotz branch".
(2) a refname that ends with a dot.
We already reject a path component that begins with a dot, primarily
to avoid ambiguous range interpretation. If we allowed ".B" as a
valid ref, it is unclear if "A...B" means "in dot-B but not in A" or
"either in A or B but not in both".
But for this to be complete, we need also to forbid "A." to avoid "in
B but not in A-dot". This was not a problem in the original range
notation, but we should have added this restriction when three-dot
notation was introduced.
Unlike "no dot at the beginning of any path component" rule, this
rule does not have to be "no dot at the end of any path component",
because you cannot abbreviate the tail end away, similar to you can
say "dot-B" to mean "refs/heads/dot-B".
For these reasons, it is not likely people created branches with these
names on purpose, but we have allowed such names to be used for quite some
time, and it is possible that people created such branches by mistake or
by accident.
To help people with branches with such unfortunate names to recover,
we still allow "branch -d 'bad.'" to delete such branches, and also allow
"branch -m bad. good" to rename them.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-22 04:27:31 +08:00
|
|
|
|
2023-03-27 06:33:40 +08:00
|
|
|
if (!copy && (oldref_usage & IS_HEAD) &&
|
2023-03-27 06:33:09 +08:00
|
|
|
replace_each_worktree_head_symref(worktrees, oldref.buf, newref.buf,
|
|
|
|
logmsg.buf))
|
2023-10-24 00:06:56 +08:00
|
|
|
die(_("branch renamed to %s, but HEAD is not updated"), newname);
|
2007-04-06 20:13:00 +08:00
|
|
|
|
2017-02-21 09:10:35 +08:00
|
|
|
strbuf_release(&logmsg);
|
|
|
|
|
2017-12-01 13:59:33 +08:00
|
|
|
strbuf_addf(&oldsection, "branch.%s", interpreted_oldname);
|
|
|
|
strbuf_addf(&newsection, "branch.%s", interpreted_newname);
|
branch: add a --copy (-c) option to go with --move (-m)
Add the ability to --copy a branch and its reflog and configuration,
this uses the same underlying machinery as the --move (-m) option
except the reflog and configuration is copied instead of being moved.
This is useful for e.g. copying a topic branch to a new version,
e.g. work to work-2 after submitting the work topic to the list, while
preserving all the tracking info and other configuration that goes
with the branch, and unlike --move keeping the other already-submitted
branch around for reference.
Like --move, when the source branch is the currently checked out
branch the HEAD is moved to the destination branch. In the case of
--move we don't really have a choice (other than remaining on a
detached HEAD) and in order to keep the functionality consistent, we
are doing it in similar way for --copy too.
The most common usage of this feature is expected to be moving to a
new topic branch which is a copy of the current one, in that case
moving to the target branch is what the user wants, and doesn't
unexpectedly behave differently than --move would.
One outstanding caveat of this implementation is that:
git checkout maint &&
git checkout master &&
git branch -c topic &&
git checkout -
Will check out 'maint' instead of 'master'. This is because the @{-N}
feature (or its -1 shorthand "-") relies on HEAD reflogs created by
the checkout command, so in this case we'll checkout maint instead of
master, as the user might expect. What to do about that is left to a
future change.
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Sahil Dua <sahildua2305@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-06-19 05:19:16 +08:00
|
|
|
if (!copy && git_config_rename_section(oldsection.buf, newsection.buf) < 0)
|
2023-10-24 00:06:56 +08:00
|
|
|
die(_("branch is renamed, but update of config-file failed"));
|
branch: force-copy a branch to itself via @{-1} is a no-op
Since 52d59cc645 (branch: add a --copy (-c) option to go with --move
(-m), 2017-06-18) we can copy a branch to make a new branch with the
'-c' (copy) option or to overwrite an existing branch using the '-C'
(force copy) option. A no-op possibility is considered when we are
asked to copy a branch to itself, to follow the same no-op introduced
for the rename (-M) operation in 3f59481e33 (branch: allow a no-op
"branch -M <current-branch> HEAD", 2011-11-25). To check for this, in
52d59cc645 we compared the branch names provided by the user, source
(HEAD if omitted) and destination, and a match is considered as this
no-op.
Since ae5a6c3684 (checkout: implement "@{-N}" shortcut name for N-th
last branch, 2009-01-17) a branch can be specified using shortcuts like
@{-1}. This allows this usage:
$ git checkout -b test
$ git checkout -
$ git branch -C test test # no-op
$ git branch -C test @{-1} # oops
$ git branch -C @{-1} test # oops
As we are using the branch name provided by the user to do the
comparison, if one of the branches is provided using a shortcut we are
not going to have a match and a call to git_config_copy_section() will
happen. This will make a duplicate of the configuration for that
branch, and with this progression the second call will produce four
copies of the configuration, and so on.
Let's use the interpreted branch name instead for this comparison.
The rename operation is not affected.
Signed-off-by: Rubén Justo <rjusto@gmail.com>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
2022-11-17 09:36:52 +08:00
|
|
|
if (copy && strcmp(interpreted_oldname, interpreted_newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0)
|
2023-10-24 00:06:56 +08:00
|
|
|
die(_("branch is copied, but update of config-file failed"));
|
branch: force-copy a branch to itself via @{-1} is a no-op
Since 52d59cc645 (branch: add a --copy (-c) option to go with --move
(-m), 2017-06-18) we can copy a branch to make a new branch with the
'-c' (copy) option or to overwrite an existing branch using the '-C'
(force copy) option. A no-op possibility is considered when we are
asked to copy a branch to itself, to follow the same no-op introduced
for the rename (-M) operation in 3f59481e33 (branch: allow a no-op
"branch -M <current-branch> HEAD", 2011-11-25). To check for this, in
52d59cc645 we compared the branch names provided by the user, source
(HEAD if omitted) and destination, and a match is considered as this
no-op.
Since ae5a6c3684 (checkout: implement "@{-N}" shortcut name for N-th
last branch, 2009-01-17) a branch can be specified using shortcuts like
@{-1}. This allows this usage:
$ git checkout -b test
$ git checkout -
$ git branch -C test test # no-op
$ git branch -C test @{-1} # oops
$ git branch -C @{-1} test # oops
As we are using the branch name provided by the user to do the
comparison, if one of the branches is provided using a shortcut we are
not going to have a match and a call to git_config_copy_section() will
happen. This will make a duplicate of the configuration for that
branch, and with this progression the second call will produce four
copies of the configuration, and so on.
Let's use the interpreted branch name instead for this comparison.
The rename operation is not affected.
Signed-off-by: Rubén Justo <rjusto@gmail.com>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
2022-11-17 09:36:52 +08:00
|
|
|
strbuf_release(&oldref);
|
|
|
|
strbuf_release(&newref);
|
2008-11-18 04:48:37 +08:00
|
|
|
strbuf_release(&oldsection);
|
|
|
|
strbuf_release(&newsection);
|
2023-03-27 06:33:09 +08:00
|
|
|
free_worktrees(worktrees);
|
2006-11-28 22:47:40 +08:00
|
|
|
}
|
|
|
|
|
2017-04-21 05:08:41 +08:00
|
|
|
static GIT_PATH_FUNC(edit_description, "EDIT_DESCRIPTION")
|
2011-09-21 06:10:08 +08:00
|
|
|
|
|
|
|
static int edit_branch_description(const char *branch_name)
|
|
|
|
{
|
branch: do not fail a no-op --edit-desc
Imagine running "git branch --edit-description" while on a branch
without the branch description, and then exit the editor after
emptying the edit buffer, which is the way to tell the command that
you changed your mind and you do not want the description after all.
The command should just happily oblige, adding no branch description
for the current branch, and exit successfully. But it fails to do
so:
$ git init -b main
$ git commit --allow-empty -m commit
$ GIT_EDITOR=: git branch --edit-description
fatal: could not unset 'branch.main.description'
The end result is OK in that the configuration variable does not
exist in the resulting repository, but we should do better. If we
know we didn't have a description, and if we are asked not to have a
description by the editor, we can just return doing nothing.
This of course introduces TOCTOU. If you add a branch description
to the same branch from another window, while you had the editor
open to edit the description, and then exit the editor without
writing anything there, we'd end up not removing the description you
added in the other window. But you are fooling yourself in your own
repository at that point, and if it hurts, you'd be better off not
doing so ;-).
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-10-01 02:06:22 +08:00
|
|
|
int exists;
|
2011-09-21 06:10:08 +08:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
struct strbuf name = STRBUF_INIT;
|
|
|
|
|
branch: do not fail a no-op --edit-desc
Imagine running "git branch --edit-description" while on a branch
without the branch description, and then exit the editor after
emptying the edit buffer, which is the way to tell the command that
you changed your mind and you do not want the description after all.
The command should just happily oblige, adding no branch description
for the current branch, and exit successfully. But it fails to do
so:
$ git init -b main
$ git commit --allow-empty -m commit
$ GIT_EDITOR=: git branch --edit-description
fatal: could not unset 'branch.main.description'
The end result is OK in that the configuration variable does not
exist in the resulting repository, but we should do better. If we
know we didn't have a description, and if we are asked not to have a
description by the editor, we can just return doing nothing.
This of course introduces TOCTOU. If you add a branch description
to the same branch from another window, while you had the editor
open to edit the description, and then exit the editor without
writing anything there, we'd end up not removing the description you
added in the other window. But you are fooling yourself in your own
repository at that point, and if it hurts, you'd be better off not
doing so ;-).
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-10-01 02:06:22 +08:00
|
|
|
exists = !read_branch_desc(&buf, branch_name);
|
2011-09-21 06:10:08 +08:00
|
|
|
if (!buf.len || buf.buf[buf.len-1] != '\n')
|
|
|
|
strbuf_addch(&buf, '\n');
|
2023-06-07 03:48:43 +08:00
|
|
|
strbuf_commented_addf(&buf, comment_line_char,
|
2016-06-18 05:54:15 +08:00
|
|
|
_("Please edit the description for the branch\n"
|
|
|
|
" %s\n"
|
|
|
|
"Lines starting with '%c' will be stripped.\n"),
|
2013-01-17 03:18:48 +08:00
|
|
|
branch_name, comment_line_char);
|
2017-04-21 05:08:41 +08:00
|
|
|
write_file_buf(edit_description(), buf.buf, buf.len);
|
2011-09-21 06:10:08 +08:00
|
|
|
strbuf_reset(&buf);
|
2017-04-21 05:08:41 +08:00
|
|
|
if (launch_editor(edit_description(), &buf, NULL)) {
|
2011-09-21 06:10:08 +08:00
|
|
|
strbuf_release(&buf);
|
|
|
|
return -1;
|
|
|
|
}
|
2023-06-07 03:48:43 +08:00
|
|
|
strbuf_stripspace(&buf, comment_line_char);
|
2011-09-21 06:10:08 +08:00
|
|
|
|
|
|
|
strbuf_addf(&name, "branch.%s.description", branch_name);
|
branch: do not fail a no-op --edit-desc
Imagine running "git branch --edit-description" while on a branch
without the branch description, and then exit the editor after
emptying the edit buffer, which is the way to tell the command that
you changed your mind and you do not want the description after all.
The command should just happily oblige, adding no branch description
for the current branch, and exit successfully. But it fails to do
so:
$ git init -b main
$ git commit --allow-empty -m commit
$ GIT_EDITOR=: git branch --edit-description
fatal: could not unset 'branch.main.description'
The end result is OK in that the configuration variable does not
exist in the resulting repository, but we should do better. If we
know we didn't have a description, and if we are asked not to have a
description by the editor, we can just return doing nothing.
This of course introduces TOCTOU. If you add a branch description
to the same branch from another window, while you had the editor
open to edit the description, and then exit the editor without
writing anything there, we'd end up not removing the description you
added in the other window. But you are fooling yourself in your own
repository at that point, and if it hurts, you'd be better off not
doing so ;-).
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-10-01 02:06:22 +08:00
|
|
|
if (buf.len || exists)
|
|
|
|
git_config_set(name.buf, buf.len ? buf.buf : NULL);
|
2011-09-21 06:10:08 +08:00
|
|
|
strbuf_release(&name);
|
|
|
|
strbuf_release(&buf);
|
|
|
|
|
2016-02-22 19:23:25 +08:00
|
|
|
return 0;
|
2011-09-21 06:10:08 +08:00
|
|
|
}
|
|
|
|
|
2006-10-24 05:27:45 +08:00
|
|
|
int cmd_branch(int argc, const char **argv, const char *prefix)
|
|
|
|
{
|
2022-01-29 08:04:44 +08:00
|
|
|
/* possible actions */
|
|
|
|
int delete = 0, rename = 0, copy = 0, list = 0,
|
|
|
|
unset_upstream = 0, show_current = 0, edit_description = 0;
|
2012-08-20 21:47:38 +08:00
|
|
|
const char *new_upstream = NULL;
|
2022-01-29 08:04:44 +08:00
|
|
|
int noncreate_actions = 0;
|
|
|
|
/* possible options */
|
branch: add --recurse-submodules option for branch creation
To improve the submodules UX, we would like to teach Git to handle
branches in submodules. Start this process by teaching "git branch" the
--recurse-submodules option so that "git branch --recurse-submodules
topic" will create the `topic` branch in the superproject and its
submodules.
Although this commit does not introduce breaking changes, it does not
work well with existing --recurse-submodules commands because "git
branch --recurse-submodules" writes to the submodule ref store, but most
commands only consider the superproject gitlink and ignore the submodule
ref store. For example, "git checkout --recurse-submodules" will check
out the commits in the superproject gitlinks (and put the submodules in
detached HEAD) instead of checking out the submodule branches.
Because of this, this commit introduces a new configuration value,
`submodule.propagateBranches`. The plan is for Git commands to
prioritize submodule ref store information over superproject gitlinks if
this value is true. Because "git branch --recurse-submodules" writes to
submodule ref stores, for the sake of clarity, it will not function
unless this configuration value is set.
This commit also includes changes that support working with submodules
from a superproject commit because "branch --recurse-submodules" (and
future commands) need to read .gitmodules and gitlinks from the
superproject commit, but submodules are typically read from the
filesystem's .gitmodules and the index's gitlinks. These changes are:
* add a submodules_of_tree() helper that gives the relevant
information of an in-tree submodule (e.g. path and oid) and
initializes the repository
* add is_tree_submodule_active() by adding a treeish_name parameter to
is_submodule_active()
* add the "submoduleNotUpdated" advice to advise users to update the
submodules in their trees
Incidentally, fix an incorrect usage string that combined the 'list'
usage of git branch (-l) with the 'create' usage; this string has been
incorrect since its inception, a8dfd5eac4 (Make builtin-branch.c use
parse_options., 2007-10-07).
Helped-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Glen Choo <chooglen@google.com>
Reviewed-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-29 08:04:45 +08:00
|
|
|
int reflog = 0, quiet = 0, icase = 0, force = 0,
|
|
|
|
recurse_submodules_explicit = 0;
|
2008-02-20 00:24:37 +08:00
|
|
|
enum branch_track track;
|
2023-07-11 05:12:07 +08:00
|
|
|
struct ref_filter filter = REF_FILTER_INIT;
|
for-each-ref: delay parsing of --sort=<atom> options
The for-each-ref family of commands invoke parsers immediately when
it sees each --sort=<atom> option, and die before even seeing the
other options on the command line when the <atom> is unrecognised.
Instead, accumulate them in a string list, and have them parsed into
a ref_sorting structure after the command line parsing is done. As
a consequence, "git branch --sort=bogus -h" used to fail to give the
brief help, which arguably may have been a feature, now does so,
which is more consistent with how other options work.
The patch is smaller than the actual extent of the "damage" to the
codebase, thanks to the fact that the original code consistently
used OPT_REF_SORT() macro to handle command line options. We only
needed to replace the variable used for the list, and implementation
of the callback function used in the macro.
The old rule was for the users of the API to:
- Declare ref_sorting and ref_sorting_tail variables;
- OPT_REF_SORT() macro will instantiate ref_sorting instance (which
may barf and die) and append it to the tail;
- Append to the tail each ref_sorting read from the configuration
by parsing in the config callback (which may barf and die);
- See if ref_sorting is null and use ref_sorting_default() instead.
Now the rule is not all that different but is simpler:
- Declare ref_sorting_options string list.
- OPT_REF_SORT() macro will append it to the string list;
- Append to the string list the sort key read from the
configuration;
- call ref_sorting_options() to turn the string list to ref_sorting
structure (which also deals with the default value).
As side effects, this change also cleans up a few issues:
- 95be717c (parse_opt_ref_sorting: always use with NONEG flag,
2019-03-20) muses that "git for-each-ref --no-sort" should simply
clear the sort keys accumulated so far; it now does.
- The implementation detail of "struct ref_sorting" and the helper
function parse_ref_sorting() can now be private to the ref-filter
API implementation.
- If you set branch.sort to a bogus value, the any "git branch"
invocation, not only the listing mode, would abort with the
original code; now it doesn't
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-10-21 03:23:53 +08:00
|
|
|
static struct ref_sorting *sorting;
|
|
|
|
struct string_list sorting_options = STRING_LIST_INIT_DUP;
|
2017-07-13 23:01:18 +08:00
|
|
|
struct ref_format format = REF_FORMAT_INIT;
|
2007-10-08 00:26:21 +08:00
|
|
|
|
|
|
|
struct option options[] = {
|
2012-08-20 20:31:55 +08:00
|
|
|
OPT_GROUP(N_("Generic options")),
|
2015-09-24 02:11:11 +08:00
|
|
|
OPT__VERBOSE(&filter.verbose,
|
2012-08-20 20:31:55 +08:00
|
|
|
N_("show hash and subject, give twice for upstream branch")),
|
|
|
|
OPT__QUIET(&quiet, N_("suppress informational messages")),
|
2022-01-20 20:35:54 +08:00
|
|
|
OPT_CALLBACK_F('t', "track", &track, "(direct|inherit)",
|
branch: add flags and config to inherit tracking
It can be helpful when creating a new branch to use the existing
tracking configuration from the branch point. However, there is
currently not a method to automatically do so.
Teach git-{branch,checkout,switch} an "inherit" argument to the
"--track" option. When this is set, creating a new branch will cause the
tracking configuration to default to the configuration of the branch
point, if set.
For example, if branch "main" tracks "origin/main", and we run
`git checkout --track=inherit -b feature main`, then branch "feature"
will track "origin/main". Thus, `git status` will show us how far
ahead/behind we are from origin, and `git pull` will pull from origin.
This is particularly useful when creating branches across many
submodules, such as with `git submodule foreach ...` (or if running with
a patch such as [1], which we use at $job), as it avoids having to
manually set tracking info for each submodule.
Since we've added an argument to "--track", also add "--track=direct" as
another way to explicitly get the original "--track" behavior ("--track"
without an argument still works as well).
Finally, teach branch.autoSetupMerge a new "inherit" option. When this
is set, "--track=inherit" becomes the default behavior.
[1]: https://lore.kernel.org/git/20180927221603.148025-1-sbeller@google.com/
Signed-off-by: Josh Steadmon <steadmon@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-21 11:30:23 +08:00
|
|
|
N_("set branch tracking configuration"),
|
2022-01-19 04:49:46 +08:00
|
|
|
PARSE_OPT_OPTARG,
|
branch: add flags and config to inherit tracking
It can be helpful when creating a new branch to use the existing
tracking configuration from the branch point. However, there is
currently not a method to automatically do so.
Teach git-{branch,checkout,switch} an "inherit" argument to the
"--track" option. When this is set, creating a new branch will cause the
tracking configuration to default to the configuration of the branch
point, if set.
For example, if branch "main" tracks "origin/main", and we run
`git checkout --track=inherit -b feature main`, then branch "feature"
will track "origin/main". Thus, `git status` will show us how far
ahead/behind we are from origin, and `git pull` will pull from origin.
This is particularly useful when creating branches across many
submodules, such as with `git submodule foreach ...` (or if running with
a patch such as [1], which we use at $job), as it avoids having to
manually set tracking info for each submodule.
Since we've added an argument to "--track", also add "--track=direct" as
another way to explicitly get the original "--track" behavior ("--track"
without an argument still works as well).
Finally, teach branch.autoSetupMerge a new "inherit" option. When this
is set, "--track=inherit" becomes the default behavior.
[1]: https://lore.kernel.org/git/20180927221603.148025-1-sbeller@google.com/
Signed-off-by: Josh Steadmon <steadmon@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-21 11:30:23 +08:00
|
|
|
parse_opt_tracking_mode),
|
2018-05-20 23:42:58 +08:00
|
|
|
OPT_SET_INT_F(0, "set-upstream", &track, N_("do not use"),
|
|
|
|
BRANCH_TRACK_OVERRIDE, PARSE_OPT_HIDDEN),
|
2016-04-09 04:02:45 +08:00
|
|
|
OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")),
|
2019-12-08 17:26:47 +08:00
|
|
|
OPT_BOOL(0, "unset-upstream", &unset_upstream, N_("unset the upstream info")),
|
2012-08-20 20:31:55 +08:00
|
|
|
OPT__COLOR(&branch_use_color, N_("use colored output")),
|
2023-07-19 02:27:49 +08:00
|
|
|
OPT_SET_INT_F('r', "remotes", &filter.kind, N_("act on remote-tracking branches"),
|
|
|
|
FILTER_REFS_REMOTES,
|
|
|
|
PARSE_OPT_NONEG),
|
2015-09-24 02:11:11 +08:00
|
|
|
OPT_CONTAINS(&filter.with_commit, N_("print only branches that contain the commit")),
|
ref-filter: add --no-contains option to tag/branch/for-each-ref
Change the tag, branch & for-each-ref commands to have a --no-contains
option in addition to their longstanding --contains options.
This allows for finding the last-good rollout tag given a known-bad
<commit>. Given a hypothetically bad commit cf5c7253e0, the git
version to revert to can be found with this hacky two-liner:
(git tag -l 'v[0-9]*'; git tag -l --contains cf5c7253e0 'v[0-9]*') |
sort | uniq -c | grep -E '^ *1 ' | awk '{print $2}' | tail -n 10
With this new --no-contains option the same can be achieved with:
git tag -l --no-contains cf5c7253e0 'v[0-9]*' | sort | tail -n 10
As the filtering machinery is shared between the tag, branch &
for-each-ref commands, implement this for those commands too. A
practical use for this with "branch" is e.g. finding branches which
were branched off between v2.8.0 and v2.10.0:
git branch --contains v2.8.0 --no-contains v2.10.0
The "describe" command also has a --contains option, but its semantics
are unrelated to what tag/branch/for-each-ref use --contains for. A
--no-contains option for "describe" wouldn't make any sense, other
than being exactly equivalent to not supplying --contains at all,
which would be confusing at best.
Add a --without option to "tag" as an alias for --no-contains, for
consistency with --with and --contains. The --with option is
undocumented, and possibly the only user of it is
Junio (<xmqqefy71iej.fsf@gitster.mtv.corp.google.com>). But it's
trivial to support, so let's do that.
The additions to the the test suite are inverse copies of the
corresponding --contains tests. With this change --no-contains for
tag, branch & for-each-ref is just as well tested as the existing
--contains option.
In addition to those tests, add a test for "tag" which asserts that
--no-contains won't find tree/blob tags, which is slightly
unintuitive, but consistent with how --contains works & is documented.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-25 02:40:57 +08:00
|
|
|
OPT_NO_CONTAINS(&filter.no_commit, N_("print only branches that don't contain the commit")),
|
2015-09-24 02:11:11 +08:00
|
|
|
OPT_WITH(&filter.with_commit, N_("print only branches that contain the commit")),
|
ref-filter: add --no-contains option to tag/branch/for-each-ref
Change the tag, branch & for-each-ref commands to have a --no-contains
option in addition to their longstanding --contains options.
This allows for finding the last-good rollout tag given a known-bad
<commit>. Given a hypothetically bad commit cf5c7253e0, the git
version to revert to can be found with this hacky two-liner:
(git tag -l 'v[0-9]*'; git tag -l --contains cf5c7253e0 'v[0-9]*') |
sort | uniq -c | grep -E '^ *1 ' | awk '{print $2}' | tail -n 10
With this new --no-contains option the same can be achieved with:
git tag -l --no-contains cf5c7253e0 'v[0-9]*' | sort | tail -n 10
As the filtering machinery is shared between the tag, branch &
for-each-ref commands, implement this for those commands too. A
practical use for this with "branch" is e.g. finding branches which
were branched off between v2.8.0 and v2.10.0:
git branch --contains v2.8.0 --no-contains v2.10.0
The "describe" command also has a --contains option, but its semantics
are unrelated to what tag/branch/for-each-ref use --contains for. A
--no-contains option for "describe" wouldn't make any sense, other
than being exactly equivalent to not supplying --contains at all,
which would be confusing at best.
Add a --without option to "tag" as an alias for --no-contains, for
consistency with --with and --contains. The --with option is
undocumented, and possibly the only user of it is
Junio (<xmqqefy71iej.fsf@gitster.mtv.corp.google.com>). But it's
trivial to support, so let's do that.
The additions to the the test suite are inverse copies of the
corresponding --contains tests. With this change --no-contains for
tag, branch & for-each-ref is just as well tested as the existing
--contains option.
In addition to those tests, add a test for "tag" which asserts that
--no-contains won't find tree/blob tags, which is slightly
unintuitive, but consistent with how --contains works & is documented.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-03-25 02:40:57 +08:00
|
|
|
OPT_WITHOUT(&filter.no_commit, N_("print only branches that don't contain the commit")),
|
2015-09-24 02:11:11 +08:00
|
|
|
OPT__ABBREV(&filter.abbrev),
|
2007-10-08 00:26:21 +08:00
|
|
|
|
2012-08-20 20:31:55 +08:00
|
|
|
OPT_GROUP(N_("Specific git-branch actions:")),
|
2023-07-19 02:27:49 +08:00
|
|
|
OPT_SET_INT_F('a', "all", &filter.kind, N_("list both remote-tracking and local branches"),
|
|
|
|
FILTER_REFS_REMOTES | FILTER_REFS_BRANCHES,
|
|
|
|
PARSE_OPT_NONEG),
|
2012-08-20 20:31:55 +08:00
|
|
|
OPT_BIT('d', "delete", &delete, N_("delete fully merged branch"), 1),
|
|
|
|
OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2),
|
|
|
|
OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1),
|
|
|
|
OPT_BIT('M', NULL, &rename, N_("move/rename a branch, even if target exists"), 2),
|
2023-11-15 03:53:50 +08:00
|
|
|
OPT_BOOL(0, "omit-empty", &format.array_opts.omit_empty,
|
2023-04-08 01:53:16 +08:00
|
|
|
N_("do not output a newline after empty formatted refs")),
|
branch: add a --copy (-c) option to go with --move (-m)
Add the ability to --copy a branch and its reflog and configuration,
this uses the same underlying machinery as the --move (-m) option
except the reflog and configuration is copied instead of being moved.
This is useful for e.g. copying a topic branch to a new version,
e.g. work to work-2 after submitting the work topic to the list, while
preserving all the tracking info and other configuration that goes
with the branch, and unlike --move keeping the other already-submitted
branch around for reference.
Like --move, when the source branch is the currently checked out
branch the HEAD is moved to the destination branch. In the case of
--move we don't really have a choice (other than remaining on a
detached HEAD) and in order to keep the functionality consistent, we
are doing it in similar way for --copy too.
The most common usage of this feature is expected to be moving to a
new topic branch which is a copy of the current one, in that case
moving to the target branch is what the user wants, and doesn't
unexpectedly behave differently than --move would.
One outstanding caveat of this implementation is that:
git checkout maint &&
git checkout master &&
git branch -c topic &&
git checkout -
Will check out 'maint' instead of 'master'. This is because the @{-N}
feature (or its -1 shorthand "-") relies on HEAD reflogs created by
the checkout command, so in this case we'll checkout maint instead of
master, as the user might expect. What to do about that is left to a
future change.
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Sahil Dua <sahildua2305@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-06-19 05:19:16 +08:00
|
|
|
OPT_BIT('c', "copy", ©, N_("copy a branch and its reflog"), 1),
|
|
|
|
OPT_BIT('C', NULL, ©, N_("copy a branch, even if target exists"), 2),
|
2018-06-22 17:24:59 +08:00
|
|
|
OPT_BOOL('l', "list", &list, N_("list branch names")),
|
2018-10-26 03:04:21 +08:00
|
|
|
OPT_BOOL(0, "show-current", &show_current, N_("show current branch name")),
|
branch: deprecate "-l" option
The "-l" option is short for "--create-reflog". This has
caused much confusion over the years. Most people expect it
to work as "--list", because that would match the other
"mode" options like -d/--delete and -m/--move, as well as
the similar -l/--list option of git-tag.
Adding to the confusion, using "-l" _appears_ to work as
"--list" in some cases:
$ git branch -l
* master
because the branch command defaults to listing (so even
trying to specify --list in the command above is redundant).
But that may bite the user later when they add a pattern,
like:
$ git branch -l foo
which does not return an empty list, but in fact creates a
new branch (with a reflog, naturally) called "foo".
It's also probably quite uncommon for people to actually use
"-l" to create a reflog. Since 0bee591869 (Enable reflogs by
default in any repository with a working directory.,
2006-12-14), this is the default in non-bare repositories.
So it's rather unfortunate that the feature squats on the
short-and-sweet "-l" (which was only added in 3a4b3f269c
(Create/delete branch ref logs., 2006-05-19), meaning there
were only 7 months where it was actually useful).
Let's deprecate "-l" in hopes of eventually re-purposing it
to "--list".
Note that we issue the warning only when we're not in list
mode. This means that people for whom it works as a happy
accident, namely:
$ git branch -l
master
won't see the warning at all. And when we eventually switch
to it meaning "--list", that will just continue to work.
We do the issue the warning for these important cases:
- when we are actually creating a branch, in case the user
really did mean it as "--create-reflog"
- when we are in some _other_ mode, like deletion. There
the "-l" is a noop for now, but it will eventually
conflict with any other mode request, and the user
should be told that this is changing.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-06-22 17:24:14 +08:00
|
|
|
OPT_BOOL(0, "create-reflog", &reflog, N_("create the branch's reflog")),
|
2013-08-03 19:51:19 +08:00
|
|
|
OPT_BOOL(0, "edit-description", &edit_description,
|
|
|
|
N_("edit the description for the branch")),
|
2018-02-09 19:01:47 +08:00
|
|
|
OPT__FORCE(&force, N_("force creation, move/rename, deletion"), PARSE_OPT_NOCOMPLETE),
|
2015-09-24 02:11:11 +08:00
|
|
|
OPT_MERGED(&filter, N_("print only branches that are merged")),
|
|
|
|
OPT_NO_MERGED(&filter, N_("print only branches that are not merged")),
|
2012-08-20 20:31:55 +08:00
|
|
|
OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")),
|
for-each-ref: delay parsing of --sort=<atom> options
The for-each-ref family of commands invoke parsers immediately when
it sees each --sort=<atom> option, and die before even seeing the
other options on the command line when the <atom> is unrecognised.
Instead, accumulate them in a string list, and have them parsed into
a ref_sorting structure after the command line parsing is done. As
a consequence, "git branch --sort=bogus -h" used to fail to give the
brief help, which arguably may have been a feature, now does so,
which is more consistent with how other options work.
The patch is smaller than the actual extent of the "damage" to the
codebase, thanks to the fact that the original code consistently
used OPT_REF_SORT() macro to handle command line options. We only
needed to replace the variable used for the list, and implementation
of the callback function used in the macro.
The old rule was for the users of the API to:
- Declare ref_sorting and ref_sorting_tail variables;
- OPT_REF_SORT() macro will instantiate ref_sorting instance (which
may barf and die) and append it to the tail;
- Append to the tail each ref_sorting read from the configuration
by parsing in the config callback (which may barf and die);
- See if ref_sorting is null and use ref_sorting_default() instead.
Now the rule is not all that different but is simpler:
- Declare ref_sorting_options string list.
- OPT_REF_SORT() macro will append it to the string list;
- Append to the string list the sort key read from the
configuration;
- call ref_sorting_options() to turn the string list to ref_sorting
structure (which also deals with the default value).
As side effects, this change also cleans up a few issues:
- 95be717c (parse_opt_ref_sorting: always use with NONEG flag,
2019-03-20) muses that "git for-each-ref --no-sort" should simply
clear the sort keys accumulated so far; it now does.
- The implementation detail of "struct ref_sorting" and the helper
function parse_ref_sorting() can now be private to the ref-filter
API implementation.
- If you set branch.sort to a bogus value, the any "git branch"
invocation, not only the listing mode, would abort with the
original code; now it doesn't
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-10-21 03:23:53 +08:00
|
|
|
OPT_REF_SORT(&sorting_options),
|
Use OPT_CALLBACK and OPT_CALLBACK_F
In the codebase, there are many options which use OPTION_CALLBACK in a
plain ol' struct definition. However, we have the OPT_CALLBACK and
OPT_CALLBACK_F macros which are meant to abstract these plain struct
definitions away. These macros are useful as they semantically signal to
developers that these are just normal callback option with nothing fancy
happening.
Replace plain struct definitions of OPTION_CALLBACK with OPT_CALLBACK or
OPT_CALLBACK_F where applicable. The heavy lifting was done using the
following (disgusting) shell script:
#!/bin/sh
do_replacement () {
tr '\n' '\r' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\s*0,\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK(\1,\2,\3,\4,\5,\6)/g' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK_F(\1,\2,\3,\4,\5,\6,\7)/g' |
tr '\r' '\n'
}
for f in $(git ls-files \*.c)
do
do_replacement <"$f" >"$f.tmp"
mv "$f.tmp" "$f"
done
The result was manually inspected and then reformatted to match the
style of the surrounding code. Finally, using
`git grep OPTION_CALLBACK \*.c`, leftover results which were not handled
by the script were manually transformed.
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-28 16:36:28 +08:00
|
|
|
OPT_CALLBACK(0, "points-at", &filter.points_at, N_("object"),
|
|
|
|
N_("print only branches of the object"), parse_opt_object_name),
|
2016-12-04 10:52:25 +08:00
|
|
|
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
|
branch: add --recurse-submodules option for branch creation
To improve the submodules UX, we would like to teach Git to handle
branches in submodules. Start this process by teaching "git branch" the
--recurse-submodules option so that "git branch --recurse-submodules
topic" will create the `topic` branch in the superproject and its
submodules.
Although this commit does not introduce breaking changes, it does not
work well with existing --recurse-submodules commands because "git
branch --recurse-submodules" writes to the submodule ref store, but most
commands only consider the superproject gitlink and ignore the submodule
ref store. For example, "git checkout --recurse-submodules" will check
out the commits in the superproject gitlinks (and put the submodules in
detached HEAD) instead of checking out the submodule branches.
Because of this, this commit introduces a new configuration value,
`submodule.propagateBranches`. The plan is for Git commands to
prioritize submodule ref store information over superproject gitlinks if
this value is true. Because "git branch --recurse-submodules" writes to
submodule ref stores, for the sake of clarity, it will not function
unless this configuration value is set.
This commit also includes changes that support working with submodules
from a superproject commit because "branch --recurse-submodules" (and
future commands) need to read .gitmodules and gitlinks from the
superproject commit, but submodules are typically read from the
filesystem's .gitmodules and the index's gitlinks. These changes are:
* add a submodules_of_tree() helper that gives the relevant
information of an in-tree submodule (e.g. path and oid) and
initializes the repository
* add is_tree_submodule_active() by adding a treeish_name parameter to
is_submodule_active()
* add the "submoduleNotUpdated" advice to advise users to update the
submodules in their trees
Incidentally, fix an incorrect usage string that combined the 'list'
usage of git branch (-l) with the 'create' usage; this string has been
incorrect since its inception, a8dfd5eac4 (Make builtin-branch.c use
parse_options., 2007-10-07).
Helped-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Glen Choo <chooglen@google.com>
Reviewed-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-29 08:04:45 +08:00
|
|
|
OPT_BOOL(0, "recurse-submodules", &recurse_submodules_explicit, N_("recurse through submodules")),
|
2017-07-13 23:01:18 +08:00
|
|
|
OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
|
2007-10-08 00:26:21 +08:00
|
|
|
OPT_END(),
|
|
|
|
};
|
2006-10-24 05:27:45 +08:00
|
|
|
|
2017-01-10 16:49:51 +08:00
|
|
|
setup_ref_filter_porcelain_msg();
|
|
|
|
|
2015-09-24 02:11:11 +08:00
|
|
|
filter.kind = FILTER_REFS_BRANCHES;
|
|
|
|
filter.abbrev = -1;
|
|
|
|
|
2010-10-22 14:42:58 +08:00
|
|
|
if (argc == 2 && !strcmp(argv[1], "-h"))
|
|
|
|
usage_with_options(builtin_branch_usage, options);
|
|
|
|
|
ref-filter.c: really don't sort when using --no-sort
When '--no-sort' is passed to 'for-each-ref', 'tag', and 'branch', the
printed refs are still sorted by ascending refname. Change the handling of
sort options in these commands so that '--no-sort' to truly disables
sorting.
'--no-sort' does not disable sorting in these commands is because their
option parsing does not distinguish between "the absence of '--sort'"
(and/or values for tag.sort & branch.sort) and '--no-sort'. Both result in
an empty 'sorting_options' string list, which is parsed by
'ref_sorting_options()' to create the 'struct ref_sorting *' for the
command. If the string list is empty, 'ref_sorting_options()' interprets
that as "the absence of '--sort'" and returns the default ref sorting
structure (equivalent to "refname" sort).
To handle '--no-sort' properly while preserving the "refname" sort in the
"absence of --sort'" case, first explicitly add "refname" to the string list
*before* parsing options. This alone doesn't actually change any behavior,
since 'compare_refs()' already falls back on comparing refnames if two refs
are equal w.r.t all other sort keys.
Now that the string list is populated by default, '--no-sort' is the only
way to empty the 'sorting_options' string list. Update
'ref_sorting_options()' to return a NULL 'struct ref_sorting *' if the
string list is empty, and add a condition to 'ref_array_sort()' to skip the
sort altogether if the sort structure is NULL. Note that other functions
using 'struct ref_sorting *' do not need any changes because they already
ignore NULL values.
Finally, remove the condition around sorting in 'ls-remote', since it's no
longer necessary. Unlike 'for-each-ref' et. al., it does *not* do any
sorting by default. This default is preserved by simply leaving its sort key
string list empty before parsing options; if no additional sort keys are
set, 'struct ref_sorting *' is NULL and sorting is skipped.
Signed-off-by: Victoria Dye <vdye@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-11-15 03:53:49 +08:00
|
|
|
/*
|
|
|
|
* Try to set sort keys from config. If config does not set any,
|
|
|
|
* fall back on default (refname) sorting.
|
|
|
|
*/
|
for-each-ref: delay parsing of --sort=<atom> options
The for-each-ref family of commands invoke parsers immediately when
it sees each --sort=<atom> option, and die before even seeing the
other options on the command line when the <atom> is unrecognised.
Instead, accumulate them in a string list, and have them parsed into
a ref_sorting structure after the command line parsing is done. As
a consequence, "git branch --sort=bogus -h" used to fail to give the
brief help, which arguably may have been a feature, now does so,
which is more consistent with how other options work.
The patch is smaller than the actual extent of the "damage" to the
codebase, thanks to the fact that the original code consistently
used OPT_REF_SORT() macro to handle command line options. We only
needed to replace the variable used for the list, and implementation
of the callback function used in the macro.
The old rule was for the users of the API to:
- Declare ref_sorting and ref_sorting_tail variables;
- OPT_REF_SORT() macro will instantiate ref_sorting instance (which
may barf and die) and append it to the tail;
- Append to the tail each ref_sorting read from the configuration
by parsing in the config callback (which may barf and die);
- See if ref_sorting is null and use ref_sorting_default() instead.
Now the rule is not all that different but is simpler:
- Declare ref_sorting_options string list.
- OPT_REF_SORT() macro will append it to the string list;
- Append to the string list the sort key read from the
configuration;
- call ref_sorting_options() to turn the string list to ref_sorting
structure (which also deals with the default value).
As side effects, this change also cleans up a few issues:
- 95be717c (parse_opt_ref_sorting: always use with NONEG flag,
2019-03-20) muses that "git for-each-ref --no-sort" should simply
clear the sort keys accumulated so far; it now does.
- The implementation detail of "struct ref_sorting" and the helper
function parse_ref_sorting() can now be private to the ref-filter
API implementation.
- If you set branch.sort to a bogus value, the any "git branch"
invocation, not only the listing mode, would abort with the
original code; now it doesn't
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-10-21 03:23:53 +08:00
|
|
|
git_config(git_branch_config, &sorting_options);
|
ref-filter.c: really don't sort when using --no-sort
When '--no-sort' is passed to 'for-each-ref', 'tag', and 'branch', the
printed refs are still sorted by ascending refname. Change the handling of
sort options in these commands so that '--no-sort' to truly disables
sorting.
'--no-sort' does not disable sorting in these commands is because their
option parsing does not distinguish between "the absence of '--sort'"
(and/or values for tag.sort & branch.sort) and '--no-sort'. Both result in
an empty 'sorting_options' string list, which is parsed by
'ref_sorting_options()' to create the 'struct ref_sorting *' for the
command. If the string list is empty, 'ref_sorting_options()' interprets
that as "the absence of '--sort'" and returns the default ref sorting
structure (equivalent to "refname" sort).
To handle '--no-sort' properly while preserving the "refname" sort in the
"absence of --sort'" case, first explicitly add "refname" to the string list
*before* parsing options. This alone doesn't actually change any behavior,
since 'compare_refs()' already falls back on comparing refnames if two refs
are equal w.r.t all other sort keys.
Now that the string list is populated by default, '--no-sort' is the only
way to empty the 'sorting_options' string list. Update
'ref_sorting_options()' to return a NULL 'struct ref_sorting *' if the
string list is empty, and add a condition to 'ref_array_sort()' to skip the
sort altogether if the sort structure is NULL. Note that other functions
using 'struct ref_sorting *' do not need any changes because they already
ignore NULL values.
Finally, remove the condition around sorting in 'ls-remote', since it's no
longer necessary. Unlike 'for-each-ref' et. al., it does *not* do any
sorting by default. This default is preserved by simply leaving its sort key
string list empty before parsing options; if no additional sort keys are
set, 'struct ref_sorting *' is NULL and sorting is skipped.
Signed-off-by: Victoria Dye <vdye@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-11-15 03:53:49 +08:00
|
|
|
if (!sorting_options.nr)
|
|
|
|
string_list_append(&sorting_options, "refname");
|
2008-02-18 15:26:03 +08:00
|
|
|
|
2008-02-20 00:24:37 +08:00
|
|
|
track = git_branch_track;
|
2006-11-28 22:47:40 +08:00
|
|
|
|
refs: convert resolve_refdup and refs_resolve_refdup to struct object_id
All of the callers already pass the hash member of struct object_id, so
update them to pass a pointer to the struct directly,
This transformation was done with an update to declaration and
definition and the following semantic patch:
@@
expression E1, E2, E3, E4;
@@
- resolve_refdup(E1, E2, E3.hash, E4)
+ resolve_refdup(E1, E2, &E3, E4)
@@
expression E1, E2, E3, E4;
@@
- resolve_refdup(E1, E2, E3->hash, E4)
+ resolve_refdup(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-10-16 06:06:55 +08:00
|
|
|
head = resolve_refdup("HEAD", 0, &head_oid, NULL);
|
2006-10-24 05:27:45 +08:00
|
|
|
if (!head)
|
2023-10-24 00:06:56 +08:00
|
|
|
die(_("failed to resolve HEAD as a valid ref"));
|
2014-10-05 02:54:50 +08:00
|
|
|
if (!strcmp(head, "HEAD"))
|
2015-09-24 02:11:11 +08:00
|
|
|
filter.detached = 1;
|
2014-10-05 02:54:50 +08:00
|
|
|
else if (!skip_prefix(head, "refs/heads/", &head))
|
|
|
|
die(_("HEAD not found below refs/heads!"));
|
2012-04-13 18:54:38 +08:00
|
|
|
|
2009-05-24 02:53:12 +08:00
|
|
|
argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
|
|
|
|
0);
|
2011-08-28 22:54:31 +08:00
|
|
|
|
2018-10-26 03:04:21 +08:00
|
|
|
if (!delete && !rename && !copy && !edit_description && !new_upstream &&
|
|
|
|
!show_current && !unset_upstream && argc == 0)
|
2011-08-28 22:54:31 +08:00
|
|
|
list = 1;
|
|
|
|
|
2020-09-16 10:08:40 +08:00
|
|
|
if (filter.with_commit || filter.no_commit ||
|
|
|
|
filter.reachable_from || filter.unreachable_from || filter.points_at.nr)
|
2013-01-31 14:46:11 +08:00
|
|
|
list = 1;
|
|
|
|
|
2022-01-29 08:04:44 +08:00
|
|
|
noncreate_actions = !!delete + !!rename + !!copy + !!new_upstream +
|
|
|
|
!!show_current + !!list + !!edit_description +
|
|
|
|
!!unset_upstream;
|
|
|
|
if (noncreate_actions > 1)
|
2008-07-09 08:55:47 +08:00
|
|
|
usage_with_options(builtin_branch_usage, options);
|
2006-10-24 05:27:45 +08:00
|
|
|
|
branch: add --recurse-submodules option for branch creation
To improve the submodules UX, we would like to teach Git to handle
branches in submodules. Start this process by teaching "git branch" the
--recurse-submodules option so that "git branch --recurse-submodules
topic" will create the `topic` branch in the superproject and its
submodules.
Although this commit does not introduce breaking changes, it does not
work well with existing --recurse-submodules commands because "git
branch --recurse-submodules" writes to the submodule ref store, but most
commands only consider the superproject gitlink and ignore the submodule
ref store. For example, "git checkout --recurse-submodules" will check
out the commits in the superproject gitlinks (and put the submodules in
detached HEAD) instead of checking out the submodule branches.
Because of this, this commit introduces a new configuration value,
`submodule.propagateBranches`. The plan is for Git commands to
prioritize submodule ref store information over superproject gitlinks if
this value is true. Because "git branch --recurse-submodules" writes to
submodule ref stores, for the sake of clarity, it will not function
unless this configuration value is set.
This commit also includes changes that support working with submodules
from a superproject commit because "branch --recurse-submodules" (and
future commands) need to read .gitmodules and gitlinks from the
superproject commit, but submodules are typically read from the
filesystem's .gitmodules and the index's gitlinks. These changes are:
* add a submodules_of_tree() helper that gives the relevant
information of an in-tree submodule (e.g. path and oid) and
initializes the repository
* add is_tree_submodule_active() by adding a treeish_name parameter to
is_submodule_active()
* add the "submoduleNotUpdated" advice to advise users to update the
submodules in their trees
Incidentally, fix an incorrect usage string that combined the 'list'
usage of git branch (-l) with the 'create' usage; this string has been
incorrect since its inception, a8dfd5eac4 (Make builtin-branch.c use
parse_options., 2007-10-07).
Helped-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Glen Choo <chooglen@google.com>
Reviewed-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-29 08:04:45 +08:00
|
|
|
if (recurse_submodules_explicit) {
|
|
|
|
if (!submodule_propagate_branches)
|
|
|
|
die(_("branch with --recurse-submodules can only be used if submodule.propagateBranches is enabled"));
|
|
|
|
if (noncreate_actions)
|
|
|
|
die(_("--recurse-submodules can only be used to create branches"));
|
|
|
|
}
|
|
|
|
|
|
|
|
recurse_submodules =
|
|
|
|
(recurse_submodules || recurse_submodules_explicit) &&
|
|
|
|
submodule_propagate_branches;
|
|
|
|
|
2015-09-24 02:11:11 +08:00
|
|
|
if (filter.abbrev == -1)
|
|
|
|
filter.abbrev = DEFAULT_ABBREV;
|
2016-12-04 10:52:25 +08:00
|
|
|
filter.ignore_case = icase;
|
|
|
|
|
2012-04-13 18:54:38 +08:00
|
|
|
finalize_colopts(&colopts, -1);
|
2015-09-24 02:11:11 +08:00
|
|
|
if (filter.verbose) {
|
2012-04-13 18:54:38 +08:00
|
|
|
if (explicitly_enable_column(colopts))
|
2022-01-06 04:02:16 +08:00
|
|
|
die(_("options '%s' and '%s' cannot be used together"), "--column", "--verbose");
|
2012-04-13 18:54:38 +08:00
|
|
|
colopts = 0;
|
|
|
|
}
|
2011-07-01 14:06:08 +08:00
|
|
|
|
2014-12-09 00:28:45 +08:00
|
|
|
if (force) {
|
|
|
|
delete *= 2;
|
|
|
|
rename *= 2;
|
branch: add a --copy (-c) option to go with --move (-m)
Add the ability to --copy a branch and its reflog and configuration,
this uses the same underlying machinery as the --move (-m) option
except the reflog and configuration is copied instead of being moved.
This is useful for e.g. copying a topic branch to a new version,
e.g. work to work-2 after submitting the work topic to the list, while
preserving all the tracking info and other configuration that goes
with the branch, and unlike --move keeping the other already-submitted
branch around for reference.
Like --move, when the source branch is the currently checked out
branch the HEAD is moved to the destination branch. In the case of
--move we don't really have a choice (other than remaining on a
detached HEAD) and in order to keep the functionality consistent, we
are doing it in similar way for --copy too.
The most common usage of this feature is expected to be moving to a
new topic branch which is a copy of the current one, in that case
moving to the target branch is what the user wants, and doesn't
unexpectedly behave differently than --move would.
One outstanding caveat of this implementation is that:
git checkout maint &&
git checkout master &&
git branch -c topic &&
git checkout -
Will check out 'maint' instead of 'master'. This is because the @{-N}
feature (or its -1 shorthand "-") relies on HEAD reflogs created by
the checkout command, so in this case we'll checkout maint instead of
master, as the user might expect. What to do about that is left to a
future change.
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Sahil Dua <sahildua2305@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-06-19 05:19:16 +08:00
|
|
|
copy *= 2;
|
2014-12-09 00:28:45 +08:00
|
|
|
}
|
|
|
|
|
2017-11-19 23:03:49 +08:00
|
|
|
if (list)
|
2017-11-19 23:03:50 +08:00
|
|
|
setup_auto_pager("branch", 1);
|
2017-11-19 23:03:49 +08:00
|
|
|
|
2023-06-17 14:41:22 +08:00
|
|
|
UNLEAK(sorting_options);
|
|
|
|
|
2013-01-28 09:18:14 +08:00
|
|
|
if (delete) {
|
|
|
|
if (!argc)
|
|
|
|
die(_("branch name required"));
|
2015-09-24 02:11:11 +08:00
|
|
|
return delete_branches(argc, argv, delete > 1, filter.kind, quiet);
|
2018-10-26 03:04:21 +08:00
|
|
|
} else if (show_current) {
|
|
|
|
print_current_branch_name();
|
|
|
|
return 0;
|
2013-01-28 09:18:14 +08:00
|
|
|
} else if (list) {
|
2021-01-06 18:01:35 +08:00
|
|
|
/* git branch --list also shows HEAD when it is detached */
|
2015-09-24 02:11:11 +08:00
|
|
|
if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)
|
|
|
|
filter.kind |= FILTER_REFS_DETACHED_HEAD;
|
|
|
|
filter.name_patterns = argv;
|
2016-12-04 10:52:25 +08:00
|
|
|
/*
|
|
|
|
* If no sorting parameter is given then we default to sorting
|
|
|
|
* by 'refname'. This would give us an alphabetically sorted
|
|
|
|
* array with the 'HEAD' ref at the beginning followed by
|
2018-06-07 19:53:36 +08:00
|
|
|
* local branches 'refs/heads/...' and finally remote-tracking
|
2016-12-04 10:52:25 +08:00
|
|
|
* branches 'refs/remotes/...'.
|
|
|
|
*/
|
for-each-ref: delay parsing of --sort=<atom> options
The for-each-ref family of commands invoke parsers immediately when
it sees each --sort=<atom> option, and die before even seeing the
other options on the command line when the <atom> is unrecognised.
Instead, accumulate them in a string list, and have them parsed into
a ref_sorting structure after the command line parsing is done. As
a consequence, "git branch --sort=bogus -h" used to fail to give the
brief help, which arguably may have been a feature, now does so,
which is more consistent with how other options work.
The patch is smaller than the actual extent of the "damage" to the
codebase, thanks to the fact that the original code consistently
used OPT_REF_SORT() macro to handle command line options. We only
needed to replace the variable used for the list, and implementation
of the callback function used in the macro.
The old rule was for the users of the API to:
- Declare ref_sorting and ref_sorting_tail variables;
- OPT_REF_SORT() macro will instantiate ref_sorting instance (which
may barf and die) and append it to the tail;
- Append to the tail each ref_sorting read from the configuration
by parsing in the config callback (which may barf and die);
- See if ref_sorting is null and use ref_sorting_default() instead.
Now the rule is not all that different but is simpler:
- Declare ref_sorting_options string list.
- OPT_REF_SORT() macro will append it to the string list;
- Append to the string list the sort key read from the
configuration;
- call ref_sorting_options() to turn the string list to ref_sorting
structure (which also deals with the default value).
As side effects, this change also cleans up a few issues:
- 95be717c (parse_opt_ref_sorting: always use with NONEG flag,
2019-03-20) muses that "git for-each-ref --no-sort" should simply
clear the sort keys accumulated so far; it now does.
- The implementation detail of "struct ref_sorting" and the helper
function parse_ref_sorting() can now be private to the ref-filter
API implementation.
- If you set branch.sort to a bogus value, the any "git branch"
invocation, not only the listing mode, would abort with the
original code; now it doesn't
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-10-21 03:23:53 +08:00
|
|
|
sorting = ref_sorting_options(&sorting_options);
|
2021-01-07 17:51:51 +08:00
|
|
|
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
|
branch: sort detached HEAD based on a flag
Change the ref-filter sorting of detached HEAD to check the
FILTER_REFS_DETACHED_HEAD flag, instead of relying on the ref
description filled-in by get_head_description() to start with "(",
which in turn we expect to ASCII-sort before any other reference.
For context, we'd like the detached line to appear first at the start
of "git branch -l", e.g.:
$ git branch -l
* (HEAD detached at <hash>)
master
This doesn't change that, but improves on a fix made in
28438e84e04 (ref-filter: sort detached HEAD lines firstly, 2019-06-18)
and gives the Chinese translation the ability to use its preferred
punctuation marks again.
In Chinese the fullwidth versions of punctuation like "()" are
typically written as (U+FF08 fullwidth left parenthesis), (U+FF09
fullwidth right parenthesis) instead[1]. This form is used in both
po/zh_{CN,TW}.po in most cases where "()" is translated in a string.
Aside from that improvement to the Chinese translation, it also just
makes for cleaner code that we mark any special cases in the ref_array
we're sorting with flags and make the sort function aware of them,
instead of piggy-backing on the general-case of strcmp() doing the
right thing.
As seen in the amended tests this made reverse sorting a bit more
consistent. Before this we'd sometimes sort this message in the
middle, now it's consistently at the beginning or end, depending on
whether we're doing a normal or reverse sort. Having it at the end
doesn't make much sense either, but at least it behaves consistently
now. A follow-up commit will make this behavior under reverse sorting
even better.
I'm removing the "TRANSLATORS" comments that were in the old code
while I'm at it. Those were added in d4919bb288e (ref-filter: move
get_head_description() from branch.c, 2017-01-10). I think it's
obvious from context, string and translation memory in typical
translation tools that these are the same or similar string.
1. https://en.wikipedia.org/wiki/Chinese_punctuation#Marks_similar_to_European_punctuation
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-01-07 17:51:52 +08:00
|
|
|
ref_sorting_set_sort_flags_all(
|
|
|
|
sorting, REF_SORTING_DETACHED_HEAD_FIRST, 1);
|
2021-10-21 02:27:21 +08:00
|
|
|
print_ref_list(&filter, sorting, &format, &output);
|
2012-04-13 18:54:38 +08:00
|
|
|
print_columns(&output, colopts, NULL);
|
|
|
|
string_list_clear(&output, 0);
|
2021-10-21 02:27:21 +08:00
|
|
|
ref_sorting_release(sorting);
|
2023-07-11 05:12:13 +08:00
|
|
|
ref_filter_clear(&filter);
|
2015-09-25 02:09:08 +08:00
|
|
|
return 0;
|
2018-10-16 22:19:20 +08:00
|
|
|
} else if (edit_description) {
|
2011-09-21 06:10:08 +08:00
|
|
|
const char *branch_name;
|
2012-02-06 09:13:36 +08:00
|
|
|
struct strbuf branch_ref = STRBUF_INIT;
|
2022-10-11 07:24:58 +08:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
int ret = 1; /* assume failure */
|
2012-02-06 09:13:36 +08:00
|
|
|
|
2013-01-28 09:18:13 +08:00
|
|
|
if (!argc) {
|
2015-09-24 02:11:11 +08:00
|
|
|
if (filter.detached)
|
2023-10-24 00:06:56 +08:00
|
|
|
die(_("cannot give description to detached HEAD"));
|
2011-09-21 06:10:08 +08:00
|
|
|
branch_name = head;
|
2022-10-11 07:24:58 +08:00
|
|
|
} else if (argc == 1) {
|
|
|
|
strbuf_branchname(&buf, argv[0], INTERPRET_BRANCH_LOCAL);
|
|
|
|
branch_name = buf.buf;
|
|
|
|
} else {
|
2013-01-28 09:18:15 +08:00
|
|
|
die(_("cannot edit description of more than one branch"));
|
2022-10-11 07:24:58 +08:00
|
|
|
}
|
2012-02-06 09:13:36 +08:00
|
|
|
|
|
|
|
strbuf_addf(&branch_ref, "refs/heads/%s", branch_name);
|
2022-10-11 07:24:58 +08:00
|
|
|
if (!ref_exists(branch_ref.buf))
|
2023-03-27 06:33:17 +08:00
|
|
|
error((!argc || branch_checked_out(branch_ref.buf))
|
2023-10-24 00:06:56 +08:00
|
|
|
? _("no commit on branch '%s' yet")
|
|
|
|
: _("no branch named '%s'"),
|
2022-10-11 07:24:58 +08:00
|
|
|
branch_name);
|
|
|
|
else if (!edit_branch_description(branch_name))
|
|
|
|
ret = 0; /* happy */
|
|
|
|
|
2012-02-06 09:13:36 +08:00
|
|
|
strbuf_release(&branch_ref);
|
2022-10-11 07:24:58 +08:00
|
|
|
strbuf_release(&buf);
|
2012-02-06 09:13:36 +08:00
|
|
|
|
2022-10-11 07:24:58 +08:00
|
|
|
return ret;
|
2022-10-26 07:01:29 +08:00
|
|
|
} else if (copy || rename) {
|
2013-03-31 09:27:44 +08:00
|
|
|
if (!argc)
|
|
|
|
die(_("branch name required"));
|
2022-10-26 07:01:29 +08:00
|
|
|
else if ((argc == 1) && filter.detached)
|
2023-10-24 00:06:56 +08:00
|
|
|
die(copy? _("cannot copy the current branch while not on any")
|
|
|
|
: _("cannot rename the current branch while not on any"));
|
2013-03-31 09:27:44 +08:00
|
|
|
else if (argc == 1)
|
2022-10-26 07:01:29 +08:00
|
|
|
copy_or_rename_branch(head, argv[0], copy, copy + rename > 1);
|
2011-11-03 00:17:12 +08:00
|
|
|
else if (argc == 2)
|
2022-10-26 07:01:29 +08:00
|
|
|
copy_or_rename_branch(argv[0], argv[1], copy, copy + rename > 1);
|
2011-11-03 00:17:12 +08:00
|
|
|
else
|
2022-10-26 07:01:29 +08:00
|
|
|
die(copy? _("too many branches for a copy operation")
|
|
|
|
: _("too many arguments for a rename operation"));
|
2012-08-20 21:47:38 +08:00
|
|
|
} else if (new_upstream) {
|
2022-10-11 07:24:58 +08:00
|
|
|
struct branch *branch;
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
2012-08-20 21:47:38 +08:00
|
|
|
|
2022-10-11 07:24:58 +08:00
|
|
|
if (!argc)
|
|
|
|
branch = branch_get(NULL);
|
|
|
|
else if (argc == 1) {
|
|
|
|
strbuf_branchname(&buf, argv[0], INTERPRET_BRANCH_LOCAL);
|
|
|
|
branch = branch_get(buf.buf);
|
|
|
|
} else
|
2017-08-21 21:36:08 +08:00
|
|
|
die(_("too many arguments to set new upstream"));
|
2013-02-23 20:22:27 +08:00
|
|
|
|
|
|
|
if (!branch) {
|
|
|
|
if (!argc || !strcmp(argv[0], "HEAD"))
|
|
|
|
die(_("could not set upstream of HEAD to %s when "
|
2023-10-24 00:06:56 +08:00
|
|
|
"it does not point to any branch"),
|
2013-02-23 20:22:27 +08:00
|
|
|
new_upstream);
|
|
|
|
die(_("no such branch '%s'"), argv[0]);
|
|
|
|
}
|
|
|
|
|
2022-10-08 08:39:43 +08:00
|
|
|
if (!ref_exists(branch->refname)) {
|
2023-03-27 06:33:17 +08:00
|
|
|
if (!argc || branch_checked_out(branch->refname))
|
2023-10-24 00:06:56 +08:00
|
|
|
die(_("no commit on branch '%s' yet"), branch->name);
|
2012-08-20 21:47:38 +08:00
|
|
|
die(_("branch '%s' does not exist"), branch->name);
|
2022-10-08 08:39:43 +08:00
|
|
|
}
|
2012-08-20 21:47:38 +08:00
|
|
|
|
2022-01-29 08:04:41 +08:00
|
|
|
dwim_and_setup_tracking(the_repository, branch->name,
|
|
|
|
new_upstream, BRANCH_TRACK_OVERRIDE,
|
|
|
|
quiet);
|
2022-10-11 07:24:58 +08:00
|
|
|
strbuf_release(&buf);
|
2012-08-31 01:23:12 +08:00
|
|
|
} else if (unset_upstream) {
|
2022-10-11 07:24:58 +08:00
|
|
|
struct branch *branch;
|
2012-08-31 01:23:12 +08:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
|
2022-10-11 07:24:58 +08:00
|
|
|
if (!argc)
|
|
|
|
branch = branch_get(NULL);
|
|
|
|
else if (argc == 1) {
|
|
|
|
strbuf_branchname(&buf, argv[0], INTERPRET_BRANCH_LOCAL);
|
|
|
|
branch = branch_get(buf.buf);
|
|
|
|
} else
|
2017-08-21 21:36:08 +08:00
|
|
|
die(_("too many arguments to unset upstream"));
|
2013-02-23 20:22:27 +08:00
|
|
|
|
|
|
|
if (!branch) {
|
|
|
|
if (!argc || !strcmp(argv[0], "HEAD"))
|
|
|
|
die(_("could not unset upstream of HEAD when "
|
2023-10-24 00:06:56 +08:00
|
|
|
"it does not point to any branch"));
|
2013-02-23 20:22:27 +08:00
|
|
|
die(_("no such branch '%s'"), argv[0]);
|
|
|
|
}
|
|
|
|
|
2013-10-31 17:25:38 +08:00
|
|
|
if (!branch_has_merge_config(branch))
|
2023-10-24 00:06:56 +08:00
|
|
|
die(_("branch '%s' has no upstream information"), branch->name);
|
2012-08-31 01:23:12 +08:00
|
|
|
|
2022-10-11 07:24:58 +08:00
|
|
|
strbuf_reset(&buf);
|
2012-08-31 01:23:12 +08:00
|
|
|
strbuf_addf(&buf, "branch.%s.remote", branch->name);
|
2020-11-26 06:12:49 +08:00
|
|
|
git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);
|
2012-08-31 01:23:12 +08:00
|
|
|
strbuf_reset(&buf);
|
|
|
|
strbuf_addf(&buf, "branch.%s.merge", branch->name);
|
2020-11-26 06:12:49 +08:00
|
|
|
git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);
|
2012-08-31 01:23:12 +08:00
|
|
|
strbuf_release(&buf);
|
2022-01-29 08:04:44 +08:00
|
|
|
} else if (!noncreate_actions && argc > 0 && argc <= 2) {
|
branch: add --recurse-submodules option for branch creation
To improve the submodules UX, we would like to teach Git to handle
branches in submodules. Start this process by teaching "git branch" the
--recurse-submodules option so that "git branch --recurse-submodules
topic" will create the `topic` branch in the superproject and its
submodules.
Although this commit does not introduce breaking changes, it does not
work well with existing --recurse-submodules commands because "git
branch --recurse-submodules" writes to the submodule ref store, but most
commands only consider the superproject gitlink and ignore the submodule
ref store. For example, "git checkout --recurse-submodules" will check
out the commits in the superproject gitlinks (and put the submodules in
detached HEAD) instead of checking out the submodule branches.
Because of this, this commit introduces a new configuration value,
`submodule.propagateBranches`. The plan is for Git commands to
prioritize submodule ref store information over superproject gitlinks if
this value is true. Because "git branch --recurse-submodules" writes to
submodule ref stores, for the sake of clarity, it will not function
unless this configuration value is set.
This commit also includes changes that support working with submodules
from a superproject commit because "branch --recurse-submodules" (and
future commands) need to read .gitmodules and gitlinks from the
superproject commit, but submodules are typically read from the
filesystem's .gitmodules and the index's gitlinks. These changes are:
* add a submodules_of_tree() helper that gives the relevant
information of an in-tree submodule (e.g. path and oid) and
initializes the repository
* add is_tree_submodule_active() by adding a treeish_name parameter to
is_submodule_active()
* add the "submoduleNotUpdated" advice to advise users to update the
submodules in their trees
Incidentally, fix an incorrect usage string that combined the 'list'
usage of git branch (-l) with the 'create' usage; this string has been
incorrect since its inception, a8dfd5eac4 (Make builtin-branch.c use
parse_options., 2007-10-07).
Helped-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Glen Choo <chooglen@google.com>
Reviewed-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-29 08:04:45 +08:00
|
|
|
const char *branch_name = argv[0];
|
|
|
|
const char *start_name = argc == 2 ? argv[1] : head;
|
|
|
|
|
2015-09-24 02:11:11 +08:00
|
|
|
if (filter.kind != FILTER_REFS_BRANCHES)
|
2023-10-24 00:06:56 +08:00
|
|
|
die(_("the -a, and -r, options to 'git branch' do not take a branch name.\n"
|
2019-05-29 07:16:05 +08:00
|
|
|
"Did you mean to use: -a|-r --list <pattern>?"));
|
2012-08-31 01:23:13 +08:00
|
|
|
|
|
|
|
if (track == BRANCH_TRACK_OVERRIDE)
|
2023-10-24 00:06:56 +08:00
|
|
|
die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead"));
|
2012-08-31 01:23:13 +08:00
|
|
|
|
branch: add --recurse-submodules option for branch creation
To improve the submodules UX, we would like to teach Git to handle
branches in submodules. Start this process by teaching "git branch" the
--recurse-submodules option so that "git branch --recurse-submodules
topic" will create the `topic` branch in the superproject and its
submodules.
Although this commit does not introduce breaking changes, it does not
work well with existing --recurse-submodules commands because "git
branch --recurse-submodules" writes to the submodule ref store, but most
commands only consider the superproject gitlink and ignore the submodule
ref store. For example, "git checkout --recurse-submodules" will check
out the commits in the superproject gitlinks (and put the submodules in
detached HEAD) instead of checking out the submodule branches.
Because of this, this commit introduces a new configuration value,
`submodule.propagateBranches`. The plan is for Git commands to
prioritize submodule ref store information over superproject gitlinks if
this value is true. Because "git branch --recurse-submodules" writes to
submodule ref stores, for the sake of clarity, it will not function
unless this configuration value is set.
This commit also includes changes that support working with submodules
from a superproject commit because "branch --recurse-submodules" (and
future commands) need to read .gitmodules and gitlinks from the
superproject commit, but submodules are typically read from the
filesystem's .gitmodules and the index's gitlinks. These changes are:
* add a submodules_of_tree() helper that gives the relevant
information of an in-tree submodule (e.g. path and oid) and
initializes the repository
* add is_tree_submodule_active() by adding a treeish_name parameter to
is_submodule_active()
* add the "submoduleNotUpdated" advice to advise users to update the
submodules in their trees
Incidentally, fix an incorrect usage string that combined the 'list'
usage of git branch (-l) with the 'create' usage; this string has been
incorrect since its inception, a8dfd5eac4 (Make builtin-branch.c use
parse_options., 2007-10-07).
Helped-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Glen Choo <chooglen@google.com>
Reviewed-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-01-29 08:04:45 +08:00
|
|
|
if (recurse_submodules) {
|
|
|
|
create_branches_recursively(the_repository, branch_name,
|
|
|
|
start_name, NULL, force,
|
|
|
|
reflog, quiet, track, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
create_branch(the_repository, branch_name, start_name, force, 0,
|
|
|
|
reflog, quiet, track, 0);
|
2009-12-30 22:45:31 +08:00
|
|
|
} else
|
2007-10-08 00:26:21 +08:00
|
|
|
usage_with_options(builtin_branch_usage, options);
|
2006-10-24 05:27:45 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|