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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "cache.h"
|
2017-06-15 02:07:36 +08:00
|
|
|
#include "config.h"
|
2006-12-20 06:34:12 +08:00
|
|
|
#include "color.h"
|
2006-10-24 05:27:45 +08:00
|
|
|
#include "refs.h"
|
|
|
|
#include "commit.h"
|
|
|
|
#include "builtin.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"
|
|
|
|
#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
|
|
|
|
2008-05-15 01:46:53 +08:00
|
|
|
static int git_branch_config(const char *var, const char *value, 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;
|
|
|
|
}
|
|
|
|
|
2017-10-14 01:24:31 +08:00
|
|
|
return git_color_default_config(var, value, 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;
|
|
|
|
|
2012-08-28 05:46:01 +08:00
|
|
|
merged = in_merge_bases(rev, reference_rev);
|
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
|
|
|
|
* return the result of the in_merge_bases() above without
|
|
|
|
* any of the following code, but during the transition period,
|
|
|
|
* a gentle reminder is in order.
|
|
|
|
*/
|
|
|
|
if ((head_rev != reference_rev) &&
|
2012-08-28 05:46:01 +08:00
|
|
|
in_merge_bases(rev, head_rev) != 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"
|
2011-04-02 08:55:55 +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"
|
|
|
|
" '%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) {
|
2012-10-18 20:02:51 +08:00
|
|
|
error(_("Couldn't look up commit object for '%s'"), refname);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!force && !branch_merged(kinds, branchname, rev, head_rev)) {
|
|
|
|
error(_("The branch '%s' is not fully merged.\n"
|
|
|
|
"If you are sure you want to delete it, "
|
|
|
|
"run 'git branch -D %s'."), branchname, branchname);
|
|
|
|
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)
|
|
|
|
warning(_("Update of config-file failed"));
|
|
|
|
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;
|
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:
|
2006-12-18 15:58:16 +08:00
|
|
|
fmt = "refs/remotes/%s";
|
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
|
|
|
|
2006-11-25 15:10:23 +08:00
|
|
|
if (!force) {
|
2018-06-29 09:21:58 +08:00
|
|
|
head_rev = lookup_commit_reference(the_repository, &head_oid);
|
2006-11-25 15:10:23 +08:00
|
|
|
if (!head_rev)
|
2011-02-23 07:41:34 +08:00
|
|
|
die(_("Couldn't look up commit object for HEAD"));
|
2006-11-25 15:10:23 +08:00
|
|
|
}
|
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))) {
|
2016-03-29 17:38:39 +08:00
|
|
|
error(_("Cannot delete branch '%s' "
|
|
|
|
"checked out 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) {
|
2012-04-30 23:33:12 +08:00
|
|
|
error(remote_branch
|
2015-05-07 03:01:55 +08:00
|
|
|
? _("remote-tracking branch '%s' not found.")
|
2012-04-30 23:33:12 +08:00
|
|
|
: _("branch '%s' not found."), bname.buf);
|
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
|
|
|
|
: find_unique_abbrev(&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);
|
|
|
|
while (*s) {
|
|
|
|
const char *ep = strchrnul(s, '%');
|
|
|
|
if (s < ep)
|
|
|
|
strbuf_add(&buf, s, ep - s);
|
|
|
|
if (*ep == '%') {
|
|
|
|
strbuf_addstr(&buf, "%%");
|
|
|
|
s = ep + 1;
|
|
|
|
} else {
|
|
|
|
s = ep;
|
|
|
|
}
|
2011-03-16 15:10:14 +08:00
|
|
|
}
|
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;
|
2021-04-21 00:52:11 +08:00
|
|
|
struct strbuf out = STRBUF_INIT;
|
|
|
|
struct strbuf err = STRBUF_INIT;
|
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
|
|
|
|
2015-09-24 02:11:12 +08:00
|
|
|
ref_array_sort(sorting, &array);
|
2010-06-04 17:50:10 +08:00
|
|
|
|
2017-01-10 16:49:52 +08:00
|
|
|
for (i = 0; i < array.nr; i++) {
|
2021-04-21 00:52:11 +08:00
|
|
|
strbuf_reset(&err);
|
|
|
|
strbuf_reset(&out);
|
2018-03-29 20:49:45 +08:00
|
|
|
if (format_ref_array_item(array.items[i], format, &out, &err))
|
|
|
|
die("%s", err.buf);
|
2017-01-10 16:49:52 +08:00
|
|
|
if (column_active(colopts)) {
|
|
|
|
assert(!filter->verbose && "--column and --verbose are incompatible");
|
|
|
|
/* 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
|
|
|
} else {
|
|
|
|
fwrite(out.buf, 1, out.len, stdout);
|
|
|
|
putchar('\n');
|
|
|
|
}
|
|
|
|
}
|
2010-06-04 17:50:11 +08:00
|
|
|
|
2021-04-21 00:52:11 +08:00
|
|
|
strbuf_release(&err);
|
|
|
|
strbuf_release(&out);
|
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);
|
|
|
|
}
|
|
|
|
|
2016-04-22 21:01:36 +08:00
|
|
|
static void reject_rebase_or_bisect_branch(const char *target)
|
|
|
|
{
|
2020-06-20 07:35:44 +08:00
|
|
|
struct worktree **worktrees = get_worktrees();
|
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))
|
|
|
|
die(_("Branch %s is being rebased at %s"),
|
|
|
|
target, wt->path);
|
|
|
|
|
|
|
|
if (is_worktree_being_bisected(wt, target))
|
|
|
|
die(_("Branch %s is being bisected at %s"),
|
|
|
|
target, wt->path);
|
|
|
|
}
|
|
|
|
|
|
|
|
free_worktrees(worktrees);
|
|
|
|
}
|
|
|
|
|
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;
|
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
|
|
|
int recovery = 0;
|
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
|
2011-02-23 07:41:34 +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
|
|
|
|
2022-10-08 08:39:43 +08:00
|
|
|
if ((copy || strcmp(head, oldname)) && !ref_exists(oldref.buf)) {
|
|
|
|
if (copy && !strcmp(head, oldname))
|
|
|
|
die(_("No commit on branch '%s' yet."), oldname);
|
|
|
|
else
|
|
|
|
die(_("No branch named '%s'."), oldname);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2016-04-22 21:01:36 +08:00
|
|
|
reject_rebase_or_bisect_branch(oldref.buf);
|
|
|
|
|
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
|
|
|
|
2020-12-11 19:36:55 +08:00
|
|
|
if (!copy &&
|
|
|
|
(!head || strcmp(oldname, head) || !is_null_oid(&head_oid)) &&
|
|
|
|
rename_ref(oldref.buf, newref.buf, logmsg.buf))
|
2011-02-23 07:41:34 +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))
|
|
|
|
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)
|
2017-11-19 01:26:47 +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
|
|
|
|
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
|
|
|
|
branch: fix "copy" to never touch HEAD
When creating a new branch B by copying the branch A that happens to
be the current branch, it also updates HEAD to point at the new
branch. It probably was made this way because "git branch -c A B"
piggybacked its implementation on "git branch -m A B",
This does not match the usual expectation. If I were sitting on a
blue chair, and somebody comes and repaints it to red, I would
accept ending up sitting on a chair that is now red (I am also OK to
stand, instead, as there no longer is my favourite blue chair). But
if somebody creates a new red chair, modelling it after the blue
chair I am sitting on, I do not expect to be booted off of the blue
chair and ending up on sitting on the new red one.
Let's fix this before it hits 'next'. Those who want to create a
new branch and switch to it can do "git checkout B" after doing a
"git branch -c B", and if that operation is so useful and deserves a
short-hand way to do so, perhaps extend "git checkout -b B" to copy
configurations while creating the new branch B.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-09-22 11:24:50 +08:00
|
|
|
if (!copy &&
|
|
|
|
replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
|
2011-02-23 07:41:34 +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);
|
2008-11-18 04:48:37 +08:00
|
|
|
strbuf_release(&oldref);
|
2017-12-01 13:59:33 +08:00
|
|
|
strbuf_addf(&newsection, "branch.%s", interpreted_newname);
|
2008-11-18 04:48:37 +08:00
|
|
|
strbuf_release(&newref);
|
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)
|
2011-02-23 07:41:34 +08:00
|
|
|
die(_("Branch is renamed, but update of config-file 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 && strcmp(oldname, newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0)
|
|
|
|
die(_("Branch is copied, but update of config-file failed"));
|
2008-11-18 04:48:37 +08:00
|
|
|
strbuf_release(&oldsection);
|
|
|
|
strbuf_release(&newsection);
|
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');
|
2013-01-17 03:18:48 +08:00
|
|
|
strbuf_commented_addf(&buf,
|
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;
|
|
|
|
}
|
2015-10-16 23:16:42 +08:00
|
|
|
strbuf_stripspace(&buf, 1);
|
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;
|
2015-09-24 02:11:11 +08:00
|
|
|
struct ref_filter filter;
|
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")),
|
2015-09-24 02:11:11 +08:00
|
|
|
OPT_SET_INT('r', "remotes", &filter.kind, N_("act on remote-tracking branches"),
|
|
|
|
FILTER_REFS_REMOTES),
|
|
|
|
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:")),
|
2015-09-24 02:11:11 +08:00
|
|
|
OPT_SET_INT('a', "all", &filter.kind, N_("list both remote-tracking and local branches"),
|
|
|
|
FILTER_REFS_REMOTES | FILTER_REFS_BRANCHES),
|
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),
|
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
|
|
|
memset(&filter, 0, sizeof(filter));
|
|
|
|
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);
|
|
|
|
|
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);
|
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)
|
2011-02-23 07:41:34 +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
|
|
|
|
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);
|
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)
|
2013-01-28 09:18:16 +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))
|
2022-10-26 06:57:18 +08:00
|
|
|
error((!argc || !strcmp(head, branch_name))
|
2022-10-11 07:24:58 +08:00
|
|
|
? _("No commit on branch '%s' yet.")
|
|
|
|
: _("No branch named '%s'."),
|
|
|
|
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)
|
|
|
|
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 "
|
|
|
|
"it does not point to any branch."),
|
|
|
|
new_upstream);
|
|
|
|
die(_("no such branch '%s'"), argv[0]);
|
|
|
|
}
|
|
|
|
|
2022-10-08 08:39:43 +08:00
|
|
|
if (!ref_exists(branch->refname)) {
|
|
|
|
if (!argc || !strcmp(head, branch->name))
|
|
|
|
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 "
|
|
|
|
"it does not point to any branch."));
|
|
|
|
die(_("no such branch '%s'"), argv[0]);
|
|
|
|
}
|
|
|
|
|
2013-10-31 17:25:38 +08:00
|
|
|
if (!branch_has_merge_config(branch))
|
2012-08-31 01:23:12 +08:00
|
|
|
die(_("Branch '%s' has no upstream information"), branch->name);
|
|
|
|
|
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)
|
2019-05-29 07:16:05 +08:00
|
|
|
die(_("The -a, and -r, options to 'git branch' do not take a branch name.\n"
|
|
|
|
"Did you mean to use: -a|-r --list <pattern>?"));
|
2012-08-31 01:23:13 +08:00
|
|
|
|
|
|
|
if (track == BRANCH_TRACK_OVERRIDE)
|
2017-08-17 10:54:24 +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;
|
|
|
|
}
|