Code clean-up for "-Wunused-parameter" build.
* jk/unused-post-2.40-part2:
parse-options: drop parse_opt_unknown_cb()
t/helper: mark unused argv/argc arguments
mark "argv" as unused when we check argc
builtins: mark unused prefix parameters
builtins: annotate always-empty prefix parameters
builtins: always pass prefix to parse_options()
fast-import: fix file access when run from subdir
* Lift the limitation that colored prompts can only be used with
PROMPT_COMMAND mode.
+ * "git blame --contents=<file> <rev> -- <path>" used to be forbidden,
+ but now it finds the origins of lines starting at <file> contents
+ through the history that leads to <rev>.
+
+ * "git pack-redundant" gave a warning when run, as the command has
+ outlived its usefulness long ago and is nominated for future
+ removal. Now we escalate to give an error.
Performance, Internal Implementation, Development Support etc.
correctly with groff, which has not been necessary since docbook
1.76 from 2010.
+ * Code clean-up to include and/or uninclude parse-options.h file as
+ needed.
+
Fixes since v2.40
-----------------
filesystem. Replace all calls to it with a git_time() wrapper and
(merge 370ddcbc89 pe/time-use-gettimeofday later to maint).
+ * Code clean-up to use designated initializers in parse-options API.
+ (merge 353e6d4554 sg/parse-options-h-initializers later to maint).
+
+ * A recent-ish change to allow unicode character classes to be used
+ with "grep -P" triggered a JIT bug in older pcre2 libraries.
+ The problematic change in Git built with these older libraries has
+ been disabled to work around the bug.
+ (merge 14b9a04479 mk/workaround-pcre-jit-ucp-bug later to maint).
+
+ * The wildmatch library code unlearns exponential behaviour it
+ acquired some time ago since it was borrowed from rsync.
+ (merge 3dc0b7f0dc pw/wildmatch-fixes later to maint).
+
+ * The index files can become corrupt under certain conditions when
+ the split-index feature is in use, especially together with
+ fsmonitor, which have been corrected.
+ (merge 061dd722dc js/split-index-fixes later to maint).
+
+ * Document what the pathname-looking strings in "rev-list --object"
+ output are for and what they mean.
+ (merge 15364d2a3c jk/document-rev-list-object-name later to maint).
+
* Other code cleanup, docfix, build fix, etc.
(merge f7111175df as/doc-markup-fix later to maint).
(merge 90ff7c9898 fc/test-aggregation-clean-up later to maint).
+ (merge 9b0c7f308a jc/am-doc-refer-to-format-patch later to maint).
+ (merge b10cbdac4c bb/unicode-width-table-15 later to maint).
+ (merge 3457b50e8c ab/retire-scripted-add-p later to maint).
+ (merge d52fcf493b ds/p2000-fix-grep-sparse later to maint).
+ (merge ec063d2591 ss/hashmap-typofix later to maint).
+ (merge 1aaed69d11 rs/archive-mtime later to maint).
+ (merge 2da2cc9b28 ob/rollback-after-commit-lock-failure later to maint).
+ (merge 54dbd0933b ob/sequencer-save-head-simplify later to maint).
+ (merge a93cbe8d78 ar/test-cleanup-unused-file-creation later to maint).
manual page.
--contents <file>::
- When <rev> is not specified, the command annotates the
- changes starting backwards from the working tree copy.
- This flag makes the command pretend as if the working
- tree copy has the contents of the named file (specify
- `-` to make the command read from the standard input).
+ Pretend the file being annotated has a commit with the
+ contents from the named file and a parent of <rev>,
+ defaulting to HEAD when no <rev> is specified. You may
+ specify '-' to make the command read from the standard
+ input for the file contents.
--date <format>::
Specifies the format used to output dates. If --date is not
rebase.forkPoint::
If set to false set `--no-fork-point` option by default.
+
+rebase.rebaseMerges::
+ Whether and how to set the `--rebase-merges` option by default. Can
+ be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
+ true or to `no-rebase-cousins` is equivalent to
+ `--rebase-merges=no-rebase-cousins`, setting to `rebase-cousins` is
+ equivalent to `--rebase-merges=rebase-cousins`, and setting to false is
+ equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
+ command line, with or without an argument, overrides any
+ `rebase.rebaseMerges` configuration.
-----------
Splits mail messages in a mailbox into commit log message,
authorship information and patches, and applies them to the
-current branch.
+current branch. You could think of it as a reverse operation
+of linkgit:git-format-patch[1] run on a branch with a straight
+history without merges.
OPTIONS
-------
SEE ALSO
--------
-linkgit:git-apply[1].
+linkgit:git-apply[1],
+linkgit:git-format-patch[1].
GIT
---
[-L <range>] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
[--ignore-rev <rev>] [--ignore-revs-file <file>]
[--color-lines] [--color-by-age] [--progress] [--abbrev=<n>]
- [<rev> | --contents <file> | --reverse <rev>..<rev>] [--] <file>
+ [ --contents <file> ] [<rev> | --reverse <rev>..<rev>] [--] <file>
DESCRIPTION
-----------
--------
[verse]
'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
- [(--sort=<key>)...] [--format=<format>] [<pattern>...]
+ [(--sort=<key>)...] [--format=<format>]
+ [ --stdin | <pattern>... ]
[--points-at=<object>]
[--merged[=<object>]] [--no-merged[=<object>]]
[--contains[=<object>]] [--no-contains[=<object>]]
literally, in the latter case matching completely or from the
beginning up to a slash.
+--stdin::
+ If `--stdin` is supplied, then the list of patterns is read from
+ standard input instead of from the argument list.
+
--count=<count>::
By default the command shows all refs that match
`<pattern>`. This option makes it stop after showing
out, if it is checked out in any linked worktree. Empty string
otherwise.
+ahead-behind:<committish>::
+ Two integers, separated by a space, demonstrating the number of
+ commits ahead and behind, respectively, when comparing the output
+ ref to the `<committish>` specified in the format.
+
In addition to the above, for commit and tag objects, the header
field names (`tree`, `parent`, `object`, `type`, and `tag`) can
be used to specify the value in the header field.
[verse]
'git pack-redundant' [--verbose] [--alt-odb] (--all | <pack-filename>...)
+WARNING
+-------
+`git pack-redundant` has been deprecated and is scheduled for removal in
+a future version of Git. Because it can only remove entire duplicate
+packs and not individual duplicate objects, it is generally not a useful
+tool for reducing repository size. You are better off using `git gc` to
+do so, which will put objects into a new pack, removing duplicates.
+
+Running `pack-redundant` without the `--i-still-use-this` flag will fail
+in this release. If you believe you have a use case for which
+`pack-redundant` is better suited and oppose this removal, please
+contact the Git mailing list at git@vger.kernel.org. More information
+about the list is available at https://git-scm.com/community.
+
DESCRIPTION
-----------
This program computes which packs in your repository
-r::
--rebase-merges[=(rebase-cousins|no-rebase-cousins)]::
+--no-rebase-merges::
By default, a rebase will simply drop merge commits from the todo
list, and put the rebased commits into a single, linear branch.
With `--rebase-merges`, the rebase will instead try to preserve
the branching structure within the commits that are to be rebased,
by recreating the merge commits. Any resolved merge conflicts or
manual amendments in these merge commits will have to be
- resolved/re-applied manually.
+ resolved/re-applied manually. `--no-rebase-merges` can be used to
+ countermand both the `rebase.rebaseMerges` config option and a previous
+ `--rebase-merges`.
+
-By default, or when `no-rebase-cousins` was specified, commits which do not
-have `<upstream>` as direct ancestor will keep their original branch point,
-i.e. commits that would be excluded by linkgit:git-log[1]'s
-`--ancestry-path` option will keep their original ancestry by default. If
-the `rebase-cousins` mode is turned on, such commits are instead rebased
-onto `<upstream>` (or `<onto>`, if specified).
+When rebasing merges, there are two modes: `rebase-cousins` and
+`no-rebase-cousins`. If the mode is not specified, it defaults to
+`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have
+`<upstream>` as direct ancestor will keep their original branch point, i.e.
+commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`
+option will keep their original ancestry by default. In `rebase-cousins` mode,
+such commits are instead rebased onto `<upstream>` (or `<onto>`, if
+specified).
+
It is currently only possible to recreate the merge commits using the
`ort` merge strategy; different merge strategies can be used only via
Print the object IDs of any object referenced by the listed
commits. `--objects foo ^bar` thus means ``send me
all object IDs which I need to download if I have the commit
- object _bar_ but not _foo_''.
+ object _bar_ but not _foo_''. See also `--object-names` below.
--in-commit-order::
Print tree and blob ids in order of the commits. The tree
--object-names::
Only useful with `--objects`; print the names of the object IDs
- that are found. This is the default behavior.
+ that are found. This is the default behavior. Note that the
+ "name" of each object is ambiguous, and mostly intended as a
+ hint for packing objects. In particular: no distinction is made between
+ the names of tags, trees, and blobs; path names may be modified
+ to remove newlines; and if an object would appear multiple times
+ with different names, only one name is shown.
--no-object-names::
Only useful with `--objects`; does not print the names of the object
static struct commit *fake_working_tree_commit(struct repository *r,
struct diff_options *opt,
const char *path,
- const char *contents_from)
+ const char *contents_from,
+ struct object_id *oid)
{
struct commit *commit;
struct blame_origin *origin;
struct commit_list **parent_tail, *parent;
- struct object_id head_oid;
struct strbuf buf = STRBUF_INIT;
const char *ident;
time_t now;
commit->date = now;
parent_tail = &commit->parents;
- if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
- die("no such ref: HEAD");
-
- parent_tail = append_parent(r, parent_tail, &head_oid);
+ parent_tail = append_parent(r, parent_tail, oid);
append_merge_parents(r, parent_tail);
verify_working_tree_path(r, commit, path);
sb->commits.compare = compare_commits_by_reverse_commit_date;
}
- if (sb->final && sb->contents_from)
- die(_("cannot use --contents with final commit object name"));
-
if (sb->reverse && sb->revs->first_parent_only)
sb->revs->children.name = NULL;
- if (!sb->final) {
+ if (sb->contents_from || !sb->final) {
+ struct object_id head_oid, *parent_oid;
+
/*
- * "--not A B -- path" without anything positive;
- * do not default to HEAD, but use the working tree
- * or "--contents".
+ * Build a fake commit at the top of the history, when
+ * (1) "git blame [^A] --path", i.e. with no positive end
+ * of the history range, in which case we build such
+ * a fake commit on top of the HEAD to blame in-tree
+ * modifications.
+ * (2) "git blame --contents=file [A] -- path", with or
+ * without positive end of the history range but with
+ * --contents, in which case we pretend that there is
+ * a fake commit on top of the positive end (defaulting to
+ * HEAD) that has the given contents in the path.
*/
+ if (sb->final) {
+ parent_oid = &sb->final->object.oid;
+ } else {
+ if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
+ die("no such ref: HEAD");
+ parent_oid = &head_oid;
+ }
+
setup_work_tree();
sb->final = fake_working_tree_commit(sb->repo,
&sb->revs->diffopt,
- sb->path, sb->contents_from);
+ sb->path, sb->contents_from,
+ parent_oid);
add_pending_object(sb->revs, &(sb->final->object), ":");
}
if (verify_ref_format(format))
die(_("unable to parse format string"));
+ filter_ahead_behind(the_repository, format, &array);
ref_array_sort(sorting, &array);
for (i = 0; i < array.nr; i++) {
static uint32_t last_idnum;
struct anonymized_entry {
struct hashmap_entry hash;
- const char *anon;
+ char *anon;
const char orig[FLEX_ARRAY];
};
return strcmp(a->orig, b->orig);
}
+static struct anonymized_entry *add_anonymized_entry(struct hashmap *map,
+ unsigned hash,
+ const char *orig, size_t len,
+ char *anon)
+{
+ struct anonymized_entry *ret, *old;
+
+ if (!map->cmpfn)
+ hashmap_init(map, anonymized_entry_cmp, NULL, 0);
+
+ FLEX_ALLOC_MEM(ret, orig, orig, len);
+ hashmap_entry_init(&ret->hash, hash);
+ ret->anon = anon;
+ old = hashmap_put_entry(map, ret, hash);
+
+ if (old) {
+ free(old->anon);
+ free(old);
+ }
+
+ return ret;
+}
+
/*
* Basically keep a cache of X->Y so that we can repeatedly replace
* the same anonymized string with another. The actual generation
* is farmed out to the generate function.
*/
static const char *anonymize_str(struct hashmap *map,
- char *(*generate)(void *),
- const char *orig, size_t len,
- void *data)
+ char *(*generate)(void),
+ const char *orig, size_t len)
{
struct anonymized_entry_key key;
struct anonymized_entry *ret;
- if (!map->cmpfn)
- hashmap_init(map, anonymized_entry_cmp, NULL, 0);
-
hashmap_entry_init(&key.hash, memhash(orig, len));
key.orig = orig;
key.orig_len = len;
/* First check if it's a token the user configured manually... */
- if (anonymized_seeds.cmpfn)
- ret = hashmap_get_entry(&anonymized_seeds, &key, hash, &key);
- else
- ret = NULL;
+ ret = hashmap_get_entry(&anonymized_seeds, &key, hash, &key);
/* ...otherwise check if we've already seen it in this context... */
if (!ret)
ret = hashmap_get_entry(map, &key, hash, &key);
/* ...and finally generate a new mapping if necessary */
- if (!ret) {
- FLEX_ALLOC_MEM(ret, orig, orig, len);
- hashmap_entry_init(&ret->hash, key.hash.hash);
- ret->anon = generate(data);
- hashmap_put(map, &ret->hash);
- }
+ if (!ret)
+ ret = add_anonymized_entry(map, key.hash.hash,
+ orig, len, generate());
return ret->anon;
}
*/
static void anonymize_path(struct strbuf *out, const char *path,
struct hashmap *map,
- char *(*generate)(void *))
+ char *(*generate)(void))
{
while (*path) {
const char *end_of_component = strchrnul(path, '/');
size_t len = end_of_component - path;
- const char *c = anonymize_str(map, generate, path, len, NULL);
+ const char *c = anonymize_str(map, generate, path, len);
strbuf_addstr(out, c);
path = end_of_component;
if (*path)
printf("%s", path);
}
-static char *anonymize_path_component(void *data)
+static char *anonymize_path_component(void)
{
static int counter;
struct strbuf out = STRBUF_INIT;
}
}
-static char *generate_fake_oid(void *data)
+static char *generate_fake_oid(void)
{
static uint32_t counter = 1; /* avoid null oid */
const unsigned hashsz = the_hash_algo->rawsz;
{
static struct hashmap objs;
size_t len = strlen(oid_hex);
- return anonymize_str(&objs, generate_fake_oid, oid_hex, len, NULL);
+ return anonymize_str(&objs, generate_fake_oid, oid_hex, len);
}
static void show_filemodify(struct diff_queue_struct *q,
return bol;
}
-static char *anonymize_ref_component(void *data)
+static char *anonymize_ref_component(void)
{
static int counter;
struct strbuf out = STRBUF_INIT;
* We do not even bother to cache commit messages, as they are unlikely
* to be repeated verbatim, and it is not that interesting when they are.
*/
-static char *anonymize_commit_message(const char *old)
+static char *anonymize_commit_message(void)
{
static int counter;
return xstrfmt("subject %d\n\nbody\n", counter++);
}
-static char *anonymize_ident(void *data)
+static char *anonymize_ident(void)
{
static int counter;
struct strbuf out = STRBUF_INIT;
len = split.mail_end - split.name_begin;
ident = anonymize_str(&idents, anonymize_ident,
- split.name_begin, len, NULL);
+ split.name_begin, len);
strbuf_addstr(out, ident);
strbuf_addch(out, ' ');
strbuf_add(out, split.date_begin, split.tz_end - split.date_begin);
mark_next_object(&commit->object);
if (anonymize) {
- reencoded = anonymize_commit_message(message);
+ reencoded = anonymize_commit_message();
} else if (encoding) {
switch(reencode_mode) {
case REENCODE_YES:
show_progress();
}
-static char *anonymize_tag(void *data)
+static char *anonymize_tag(void)
{
static int counter;
struct strbuf out = STRBUF_INIT;
if (message) {
static struct hashmap tags;
message = anonymize_str(&tags, anonymize_tag,
- message, message_size, NULL);
+ message, message_size);
message_size = strlen(message);
}
}
}
}
-static char *anonymize_seed(void *data)
-{
- return xstrdup(data);
-}
-
static int parse_opt_anonymize_map(const struct option *opt,
const char *arg, int unset)
{
if (!keylen || !*value)
return error(_("--anonymize-map token cannot be empty"));
- anonymize_str(map, anonymize_seed, arg, keylen, (void *)value);
+ add_anonymized_entry(map, memhash(arg, keylen), arg, keylen,
+ xstrdup(value));
return 0;
}
#include "object.h"
#include "parse-options.h"
#include "ref-filter.h"
+#include "strvec.h"
+#include "commit-reach.h"
static char const * const for_each_ref_usage[] = {
N_("git for-each-ref [<options>] [<pattern>]"),
struct ref_format format = REF_FORMAT_INIT;
struct strbuf output = STRBUF_INIT;
struct strbuf err = STRBUF_INIT;
+ int from_stdin = 0;
+ struct strvec vec = STRVEC_INIT;
struct option opts[] = {
OPT_BIT('s', "shell", &format.quote_style,
OPT_CONTAINS(&filter.with_commit, N_("print only refs which contain the commit")),
OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")),
OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
+ OPT_BOOL(0, "stdin", &from_stdin, N_("read reference patterns from stdin")),
OPT_END(),
};
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
filter.ignore_case = icase;
- filter.name_patterns = argv;
+ if (from_stdin) {
+ struct strbuf line = STRBUF_INIT;
+
+ if (argv[0])
+ die(_("unknown arguments supplied with --stdin"));
+
+ while (strbuf_getline(&line, stdin) != EOF)
+ strvec_push(&vec, line.buf);
+
+ strbuf_release(&line);
+
+ /* vec.v is NULL-terminated, just like 'argv'. */
+ filter.name_patterns = vec.v;
+ } else {
+ filter.name_patterns = argv;
+ }
+
filter.match_as_path = 1;
filter_refs(&array, &filter, FILTER_REFS_ALL);
+ filter_ahead_behind(the_repository, &format, &array);
+
ref_array_sort(sorting, &array);
if (!maxcount || array.nr < maxcount)
free_commit_list(filter.with_commit);
free_commit_list(filter.no_commit);
ref_sorting_release(sorting);
+ strvec_clear(&vec);
return 0;
}
#include "ref-filter.h"
#include "remote.h"
#include "refs.h"
+#include "parse-options.h"
static const char * const ls_remote_usage[] = {
N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
#include "shallow.h"
#include "promisor-remote.h"
#include "pack-mtimes.h"
+#include "parse-options.h"
/*
* Objects we are going to pack are collected in the `to_pack` structure.
"option, '--i-still-use-this', on the command line\n"
"and let us know you still use it by sending an e-mail\n"
"to <git@vger.kernel.org>. Thanks.\n"), stderr);
+ die(_("refusing to run without --i-still-use-this"));
}
if (load_all_packs)
int fork_point;
int update_refs;
int config_autosquash;
+ int config_rebase_merges;
int config_update_refs;
};
.allow_empty_message = 1, \
.autosquash = -1, \
.config_autosquash = -1, \
+ .rebase_merges = -1, \
+ .config_rebase_merges = -1, \
.update_refs = -1, \
.config_update_refs = -1, \
}
return status ? -1 : 0;
}
+static void parse_rebase_merges_value(struct rebase_options *options, const char *value)
+{
+ if (!strcmp("no-rebase-cousins", value))
+ options->rebase_cousins = 0;
+ else if (!strcmp("rebase-cousins", value))
+ options->rebase_cousins = 1;
+ else
+ die(_("Unknown rebase-merges mode: %s"), value);
+}
+
static int rebase_config(const char *var, const char *value, void *data)
{
struct rebase_options *opts = data;
return 0;
}
+ if (!strcmp(var, "rebase.rebasemerges")) {
+ opts->config_rebase_merges = git_parse_maybe_bool(value);
+ if (opts->config_rebase_merges < 0) {
+ opts->config_rebase_merges = 1;
+ parse_rebase_merges_value(opts, value);
+ } else {
+ opts->rebase_cousins = 0;
+ }
+ return 0;
+ }
+
if (!strcmp(var, "rebase.updaterefs")) {
opts->config_update_refs = git_config_bool(var, value);
return 0;
return 0;
}
+static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)
+{
+ struct rebase_options *options = opt->value;
+
+ options->rebase_merges = !unset;
+ options->rebase_cousins = 0;
+
+ if (arg) {
+ if (!*arg) {
+ warning(_("--rebase-merges with an empty string "
+ "argument is deprecated and will stop "
+ "working in a future version of Git. Use "
+ "--rebase-merges without an argument "
+ "instead, which does the same thing."));
+ return 0;
+ }
+ parse_rebase_merges_value(options, arg);
+ }
+
+ return 0;
+}
+
static void NORETURN error_on_missing_default_upstream(void)
{
struct branch *current_branch = branch_get(NULL);
struct object_id branch_base;
int ignore_whitespace = 0;
const char *gpg_sign = NULL;
- const char *rebase_merges = NULL;
struct string_list strategy_options = STRING_LIST_INIT_NODUP;
struct object_id squash_onto;
char *squash_onto_name = NULL;
&options.allow_empty_message,
N_("allow rebasing commits with empty messages"),
PARSE_OPT_HIDDEN),
- {OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
- N_("mode"),
+ OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"),
N_("try to rebase merges instead of skipping them"),
- PARSE_OPT_OPTARG, NULL, (intptr_t)""},
+ PARSE_OPT_OPTARG, parse_opt_rebase_merges),
OPT_BOOL(0, "fork-point", &options.fork_point,
N_("use 'merge-base --fork-point' to refine upstream")),
OPT_STRING('s', "strategy", &options.strategy,
if (options.exec.nr)
imply_merge(&options, "--exec");
- if (rebase_merges) {
- if (!*rebase_merges)
- ; /* default mode; do nothing */
- else if (!strcmp("rebase-cousins", rebase_merges))
- options.rebase_cousins = 1;
- else if (strcmp("no-rebase-cousins", rebase_merges))
- die(_("Unknown mode: %s"), rebase_merges);
- options.rebase_merges = 1;
- imply_merge(&options, "--rebase-merges");
- }
-
if (options.type == REBASE_APPLY) {
if (ignore_whitespace)
strvec_push(&options.git_am_opts,
"cannot be used together"));
else if (options.autosquash == -1 && options.config_autosquash == 1)
die(_("apply options are incompatible with rebase.autoSquash. Consider adding --no-autosquash"));
+ else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
+ die(_("apply options are incompatible with rebase.rebaseMerges. Consider adding --no-rebase-merges"));
else if (options.update_refs == -1 && options.config_update_refs == 1)
die(_("apply options are incompatible with rebase.updateRefs. Consider adding --no-update-refs"));
else
options.update_refs = (options.update_refs >= 0) ? options.update_refs :
((options.config_update_refs >= 0) ? options.config_update_refs : 0);
+ if (options.rebase_merges == 1)
+ imply_merge(&options, "--rebase-merges");
+ options.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :
+ ((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);
+
if (options.autosquash == 1)
imply_merge(&options, "--autosquash");
options.autosquash = (options.autosquash >= 0) ? options.autosquash :
#include "commit-reach.h"
#include "worktree.h"
#include "shallow.h"
+#include "parse-options.h"
static const char * const receive_pack_usage[] = {
N_("git receive-pack <git-dir>"),
#include "reachable.h"
#include "worktree.h"
#include "reflog.h"
+#include "parse-options.h"
#define BUILTIN_REFLOG_SHOW_USAGE \
N_("git reflog [show] [<log-options>] [<ref>]")
#include "gpg-interface.h"
#include "gettext.h"
#include "protocol.h"
+#include "parse-options.h"
static const char * const send_pack_usage[] = {
N_("git send-pack [--mirror] [--dry-run] [--force]\n"
die(_("unable to parse format string"));
filter->with_commit_tag_algo = 1;
filter_refs(&array, filter, FILTER_REFS_TAGS);
+ filter_ahead_behind(the_repository, format, &array);
ref_array_sort(sorting, &array);
for (i = 0; i < array.nr; i++) {
struct commit_graph_data *data =
commit_graph_data_slab_peek(&commit_graph_data_slab, c);
- if (!data)
- return GENERATION_NUMBER_INFINITY;
- else if (data->graph_pos == COMMIT_NOT_FROM_GRAPH)
- return GENERATION_NUMBER_INFINITY;
+ if (data && data->generation)
+ return data->generation;
- return data->generation;
+ return GENERATION_NUMBER_INFINITY;
}
static struct commit_graph_data *commit_graph_data_at(const struct commit *c)
stop_progress(&ctx->progress);
}
-static void compute_topological_levels(struct write_commit_graph_context *ctx)
+struct compute_generation_info {
+ struct repository *r;
+ struct packed_commit_list *commits;
+ struct progress *progress;
+ int progress_cnt;
+
+ timestamp_t (*get_generation)(struct commit *c, void *data);
+ void (*set_generation)(struct commit *c, timestamp_t gen, void *data);
+ void *data;
+};
+
+static timestamp_t compute_generation_from_max(struct commit *c,
+ timestamp_t max_gen,
+ int generation_version)
+{
+ switch (generation_version) {
+ case 1: /* topological levels */
+ if (max_gen > GENERATION_NUMBER_V1_MAX - 1)
+ max_gen = GENERATION_NUMBER_V1_MAX - 1;
+ return max_gen + 1;
+
+ case 2: /* corrected commit date */
+ if (c->date && c->date > max_gen)
+ max_gen = c->date - 1;
+ return max_gen + 1;
+
+ default:
+ BUG("attempting unimplemented version");
+ }
+}
+
+static void compute_reachable_generation_numbers(
+ struct compute_generation_info *info,
+ int generation_version)
{
int i;
struct commit_list *list = NULL;
- if (ctx->report_progress)
- ctx->progress = start_delayed_progress(
- _("Computing commit graph topological levels"),
- ctx->commits.nr);
- for (i = 0; i < ctx->commits.nr; i++) {
- struct commit *c = ctx->commits.list[i];
- uint32_t level;
-
- repo_parse_commit(ctx->r, c);
- level = *topo_level_slab_at(ctx->topo_levels, c);
+ for (i = 0; i < info->commits->nr; i++) {
+ struct commit *c = info->commits->list[i];
+ timestamp_t gen;
+ repo_parse_commit(info->r, c);
+ gen = info->get_generation(c, info->data);
+ display_progress(info->progress, info->progress_cnt + 1);
- display_progress(ctx->progress, i + 1);
- if (level != GENERATION_NUMBER_ZERO)
+ if (gen != GENERATION_NUMBER_ZERO && gen != GENERATION_NUMBER_INFINITY)
continue;
commit_list_insert(c, &list);
struct commit *current = list->item;
struct commit_list *parent;
int all_parents_computed = 1;
- uint32_t max_level = 0;
+ uint32_t max_gen = 0;
for (parent = current->parents; parent; parent = parent->next) {
- repo_parse_commit(ctx->r, parent->item);
- level = *topo_level_slab_at(ctx->topo_levels, parent->item);
+ repo_parse_commit(info->r, parent->item);
+ gen = info->get_generation(parent->item, info->data);
- if (level == GENERATION_NUMBER_ZERO) {
+ if (gen == GENERATION_NUMBER_ZERO) {
all_parents_computed = 0;
commit_list_insert(parent->item, &list);
break;
}
- if (level > max_level)
- max_level = level;
+ if (gen > max_gen)
+ max_gen = gen;
}
if (all_parents_computed) {
pop_commit(&list);
-
- if (max_level > GENERATION_NUMBER_V1_MAX - 1)
- max_level = GENERATION_NUMBER_V1_MAX - 1;
- *topo_level_slab_at(ctx->topo_levels, current) = max_level + 1;
+ gen = compute_generation_from_max(
+ current, max_gen,
+ generation_version);
+ info->set_generation(current, gen, info->data);
}
}
}
+}
+
+static timestamp_t get_topo_level(struct commit *c, void *data)
+{
+ struct write_commit_graph_context *ctx = data;
+ return *topo_level_slab_at(ctx->topo_levels, c);
+}
+
+static void set_topo_level(struct commit *c, timestamp_t t, void *data)
+{
+ struct write_commit_graph_context *ctx = data;
+ *topo_level_slab_at(ctx->topo_levels, c) = (uint32_t)t;
+}
+
+static void compute_topological_levels(struct write_commit_graph_context *ctx)
+{
+ struct compute_generation_info info = {
+ .r = ctx->r,
+ .commits = &ctx->commits,
+ .get_generation = get_topo_level,
+ .set_generation = set_topo_level,
+ .data = ctx,
+ };
+
+ if (ctx->report_progress)
+ info.progress = ctx->progress
+ = start_delayed_progress(
+ _("Computing commit graph topological levels"),
+ ctx->commits.nr);
+
+ compute_reachable_generation_numbers(&info, 1);
+
stop_progress(&ctx->progress);
}
+static timestamp_t get_generation_from_graph_data(struct commit *c, void *data)
+{
+ return commit_graph_data_at(c)->generation;
+}
+
+static void set_generation_v2(struct commit *c, timestamp_t t, void *data)
+{
+ struct commit_graph_data *g = commit_graph_data_at(c);
+ g->generation = t;
+}
+
static void compute_generation_numbers(struct write_commit_graph_context *ctx)
{
int i;
- struct commit_list *list = NULL;
+ struct compute_generation_info info = {
+ .r = ctx->r,
+ .commits = &ctx->commits,
+ .get_generation = get_generation_from_graph_data,
+ .set_generation = set_generation_v2,
+ .data = ctx,
+ };
if (ctx->report_progress)
- ctx->progress = start_delayed_progress(
+ info.progress = ctx->progress
+ = start_delayed_progress(
_("Computing commit graph generation numbers"),
ctx->commits.nr);
}
}
- for (i = 0; i < ctx->commits.nr; i++) {
- struct commit *c = ctx->commits.list[i];
- timestamp_t corrected_commit_date;
-
- repo_parse_commit(ctx->r, c);
- corrected_commit_date = commit_graph_data_at(c)->generation;
-
- display_progress(ctx->progress, i + 1);
- if (corrected_commit_date != GENERATION_NUMBER_ZERO)
- continue;
-
- commit_list_insert(c, &list);
- while (list) {
- struct commit *current = list->item;
- struct commit_list *parent;
- int all_parents_computed = 1;
- timestamp_t max_corrected_commit_date = 0;
-
- for (parent = current->parents; parent; parent = parent->next) {
- repo_parse_commit(ctx->r, parent->item);
- corrected_commit_date = commit_graph_data_at(parent->item)->generation;
-
- if (corrected_commit_date == GENERATION_NUMBER_ZERO) {
- all_parents_computed = 0;
- commit_list_insert(parent->item, &list);
- break;
- }
-
- if (corrected_commit_date > max_corrected_commit_date)
- max_corrected_commit_date = corrected_commit_date;
- }
-
- if (all_parents_computed) {
- pop_commit(&list);
-
- if (current->date && current->date > max_corrected_commit_date)
- max_corrected_commit_date = current->date - 1;
- commit_graph_data_at(current)->generation = max_corrected_commit_date + 1;
- }
- }
- }
+ compute_reachable_generation_numbers(&info, 2);
for (i = 0; i < ctx->commits.nr; i++) {
struct commit *c = ctx->commits.list[i];
stop_progress(&ctx->progress);
}
+static void set_generation_in_graph_data(struct commit *c, timestamp_t t,
+ void *data)
+{
+ commit_graph_data_at(c)->generation = t;
+}
+
+/*
+ * After this method, all commits reachable from those in the given
+ * list will have non-zero, non-infinite generation numbers.
+ */
+void ensure_generations_valid(struct repository *r,
+ struct commit **commits, size_t nr)
+{
+ int generation_version = get_configured_generation_version(r);
+ struct packed_commit_list list = {
+ .list = commits,
+ .alloc = nr,
+ .nr = nr,
+ };
+ struct compute_generation_info info = {
+ .r = r,
+ .commits = &list,
+ .get_generation = get_generation_from_graph_data,
+ .set_generation = set_generation_in_graph_data,
+ };
+
+ compute_reachable_generation_numbers(&info, generation_version);
+}
+
static void trace2_bloom_filter_write_statistics(struct write_commit_graph_context *ctx)
{
trace2_data_intmax("commit-graph", ctx->r, "filter-computed",
*/
timestamp_t commit_graph_generation(const struct commit *);
uint32_t commit_graph_position(const struct commit *);
+
+/*
+ * After this method, all commits reachable from those in the given
+ * list will have non-zero, non-infinite generation numbers.
+ */
+void ensure_generations_valid(struct repository *r,
+ struct commit **commits, size_t nr);
+
#endif
#include "revision.h"
#include "tag.h"
#include "commit-reach.h"
+#include "ewah/ewok.h"
/* Remember to update object flag allocation in object.h */
#define PARENT1 (1u<<16)
return found_commits;
}
+
+define_commit_slab(bit_arrays, struct bitmap *);
+static struct bit_arrays bit_arrays;
+
+static void insert_no_dup(struct prio_queue *queue, struct commit *c)
+{
+ if (c->object.flags & PARENT2)
+ return;
+ prio_queue_put(queue, c);
+ c->object.flags |= PARENT2;
+}
+
+static struct bitmap *get_bit_array(struct commit *c, int width)
+{
+ struct bitmap **bitmap = bit_arrays_at(&bit_arrays, c);
+ if (!*bitmap)
+ *bitmap = bitmap_word_alloc(width);
+ return *bitmap;
+}
+
+static void free_bit_array(struct commit *c)
+{
+ struct bitmap **bitmap = bit_arrays_at(&bit_arrays, c);
+ if (!*bitmap)
+ return;
+ bitmap_free(*bitmap);
+ *bitmap = NULL;
+}
+
+void ahead_behind(struct repository *r,
+ struct commit **commits, size_t commits_nr,
+ struct ahead_behind_count *counts, size_t counts_nr)
+{
+ struct prio_queue queue = { .compare = compare_commits_by_gen_then_commit_date };
+ size_t width = DIV_ROUND_UP(commits_nr, BITS_IN_EWORD);
+
+ if (!commits_nr || !counts_nr)
+ return;
+
+ for (size_t i = 0; i < counts_nr; i++) {
+ counts[i].ahead = 0;
+ counts[i].behind = 0;
+ }
+
+ ensure_generations_valid(r, commits, commits_nr);
+
+ init_bit_arrays(&bit_arrays);
+
+ for (size_t i = 0; i < commits_nr; i++) {
+ struct commit *c = commits[i];
+ struct bitmap *bitmap = get_bit_array(c, width);
+
+ bitmap_set(bitmap, i);
+ insert_no_dup(&queue, c);
+ }
+
+ while (queue_has_nonstale(&queue)) {
+ struct commit *c = prio_queue_get(&queue);
+ struct commit_list *p;
+ struct bitmap *bitmap_c = get_bit_array(c, width);
+
+ for (size_t i = 0; i < counts_nr; i++) {
+ int reach_from_tip = !!bitmap_get(bitmap_c, counts[i].tip_index);
+ int reach_from_base = !!bitmap_get(bitmap_c, counts[i].base_index);
+
+ if (reach_from_tip ^ reach_from_base) {
+ if (reach_from_base)
+ counts[i].behind++;
+ else
+ counts[i].ahead++;
+ }
+ }
+
+ for (p = c->parents; p; p = p->next) {
+ struct bitmap *bitmap_p;
+
+ repo_parse_commit(r, p->item);
+
+ bitmap_p = get_bit_array(p->item, width);
+ bitmap_or(bitmap_p, bitmap_c);
+
+ /*
+ * If this parent is reachable from every starting
+ * commit, then none of its ancestors can contribute
+ * to the ahead/behind count. Mark it as STALE, so
+ * we can stop the walk when every commit in the
+ * queue is STALE.
+ */
+ if (bitmap_popcount(bitmap_p) == commits_nr)
+ p->item->object.flags |= STALE;
+
+ insert_no_dup(&queue, p->item);
+ }
+
+ free_bit_array(c);
+ }
+
+ /* STALE is used here, PARENT2 is used by insert_no_dup(). */
+ repo_clear_commit_marks(r, PARENT2 | STALE);
+ clear_bit_arrays(&bit_arrays);
+ clear_prio_queue(&queue);
+}
+
+struct commit_and_index {
+ struct commit *commit;
+ unsigned int index;
+ timestamp_t generation;
+};
+
+static int compare_commit_and_index_by_generation(const void *va, const void *vb)
+{
+ const struct commit_and_index *a = (const struct commit_and_index *)va;
+ const struct commit_and_index *b = (const struct commit_and_index *)vb;
+
+ if (a->generation > b->generation)
+ return 1;
+ if (a->generation < b->generation)
+ return -1;
+ return 0;
+}
+
+void tips_reachable_from_bases(struct repository *r,
+ struct commit_list *bases,
+ struct commit **tips, size_t tips_nr,
+ int mark)
+{
+ struct commit_and_index *commits;
+ size_t min_generation_index = 0;
+ timestamp_t min_generation;
+ struct commit_list *stack = NULL;
+
+ if (!bases || !tips || !tips_nr)
+ return;
+
+ /*
+ * Do a depth-first search starting at 'bases' to search for the
+ * tips. Stop at the lowest (un-found) generation number. When
+ * finding the lowest commit, increase the minimum generation
+ * number to the next lowest (un-found) generation number.
+ */
+
+ CALLOC_ARRAY(commits, tips_nr);
+
+ for (size_t i = 0; i < tips_nr; i++) {
+ commits[i].commit = tips[i];
+ commits[i].index = i;
+ commits[i].generation = commit_graph_generation(tips[i]);
+ }
+
+ /* Sort with generation number ascending. */
+ QSORT(commits, tips_nr, compare_commit_and_index_by_generation);
+ min_generation = commits[0].generation;
+
+ while (bases) {
+ repo_parse_commit(r, bases->item);
+ commit_list_insert(bases->item, &stack);
+ bases = bases->next;
+ }
+
+ while (stack) {
+ int explored_all_parents = 1;
+ struct commit_list *p;
+ struct commit *c = stack->item;
+ timestamp_t c_gen = commit_graph_generation(c);
+
+ /* Does it match any of our tips? */
+ for (size_t j = min_generation_index; j < tips_nr; j++) {
+ if (c_gen < commits[j].generation)
+ break;
+
+ if (commits[j].commit == c) {
+ tips[commits[j].index]->object.flags |= mark;
+
+ if (j == min_generation_index) {
+ unsigned int k = j + 1;
+ while (k < tips_nr &&
+ (tips[commits[k].index]->object.flags & mark))
+ k++;
+
+ /* Terminate early if all found. */
+ if (k >= tips_nr)
+ goto done;
+
+ min_generation_index = k;
+ min_generation = commits[k].generation;
+ }
+ }
+ }
+
+ for (p = c->parents; p; p = p->next) {
+ repo_parse_commit(r, p->item);
+
+ /* Have we already explored this parent? */
+ if (p->item->object.flags & SEEN)
+ continue;
+
+ /* Is it below the current minimum generation? */
+ if (commit_graph_generation(p->item) < min_generation)
+ continue;
+
+ /* Ok, we will explore from here on. */
+ p->item->object.flags |= SEEN;
+ explored_all_parents = 0;
+ commit_list_insert(p->item, &stack);
+ break;
+ }
+
+ if (explored_all_parents)
+ pop_commit(&stack);
+ }
+
+done:
+ free(commits);
+ repo_clear_commit_marks(r, SEEN);
+}
struct commit **to, int nr_to,
unsigned int reachable_flag);
+struct ahead_behind_count {
+ /**
+ * As input, the *_index members indicate which positions in
+ * the 'tips' array correspond to the tip and base of this
+ * comparison.
+ */
+ size_t tip_index;
+ size_t base_index;
+
+ /**
+ * These values store the computed counts for each side of the
+ * symmetric difference:
+ *
+ * 'ahead' stores the number of commits reachable from the tip
+ * and not reachable from the base.
+ *
+ * 'behind' stores the number of commits reachable from the base
+ * and not reachable from the tip.
+ */
+ unsigned int ahead;
+ unsigned int behind;
+};
+
+/*
+ * Given an array of commits and an array of ahead_behind_count pairs,
+ * compute the ahead/behind counts for each pair.
+ */
+void ahead_behind(struct repository *r,
+ struct commit **commits, size_t commits_nr,
+ struct ahead_behind_count *counts, size_t counts_nr);
+
+/*
+ * For all tip commits, add 'mark' to their flags if and only if they
+ * are reachable from one of the commits in 'bases'.
+ */
+void tips_reachable_from_bases(struct repository *r,
+ struct commit_list *bases,
+ struct commit **tips, size_t tips_nr,
+ int mark);
+
#endif
comment_line_char = value[0];
auto_comment_line_char = 0;
} else
- return error(_("core.commentChar should only be one character"));
+ return error(_("core.commentChar should only be one ASCII character"));
return 0;
}
#include "strvec.h"
#include "object-store.h"
#include "packfile.h"
+#include "parse-options.h"
struct archive_dir {
const char *path;
#define DIAGNOSE_H
#include "strbuf.h"
-#include "parse-options.h"
+
+struct option;
enum diagnose_mode {
DIAGNOSE_NONE,
!(ce->ce_flags & CE_FSMONITOR_VALID)) {
if (S_ISGITLINK(ce->ce_mode))
return;
- istate->cache_changed = 1;
+ istate->cache_changed |= FSMONITOR_CHANGED;
ce->ce_flags |= CE_FSMONITOR_VALID;
trace_printf_key(&trace_fsmonitor, "mark_fsmonitor_clean '%s'", ce->name);
}
if (!opt->ignore_locale && is_utf8_locale() && !literal)
options |= (PCRE2_UTF | PCRE2_UCP | PCRE2_MATCH_INVALID_UTF);
+#ifndef GIT_PCRE2_VERSION_10_35_OR_HIGHER
+ /*
+ * Work around a JIT bug related to invalid Unicode character handling
+ * fixed in 10.35:
+ * https://github.com/PCRE2Project/pcre2/commit/c21bd977547d
+ */
+ options &= ~PCRE2_UCP;
+#endif
+
#ifndef GIT_PCRE2_VERSION_10_36_OR_HIGHER
/* Work around https://bugs.exim.org/show_bug.cgi?id=2642 fixed in 10.36 */
if (PCRE2_MATCH_INVALID_UTF && options & (PCRE2_UTF | PCRE2_CASELESS))
#if (PCRE2_MAJOR >= 10 && PCRE2_MINOR >= 36) || PCRE2_MAJOR >= 11
#define GIT_PCRE2_VERSION_10_36_OR_HIGHER
#endif
+#if (PCRE2_MAJOR >= 10 && PCRE2_MINOR >= 35) || PCRE2_MAJOR >= 11
+#define GIT_PCRE2_VERSION_10_35_OR_HIGHER
+#endif
#if (PCRE2_MAJOR >= 10 && PCRE2_MINOR >= 34) || PCRE2_MAJOR >= 11
#define GIT_PCRE2_VERSION_10_34_OR_HIGHER
#endif
#define hashmap_clear(map) hashmap_clear_(map, -1)
/*
- * Similar to hashmap_clear(), except that the table is no deallocated; it
+ * Similar to hashmap_clear(), except that the table is not deallocated; it
* is merely zeroed out but left the same size as before. If the hashmap
* will be reused, this avoids the overhead of deallocating and
* reallocating map->table. As with hashmap_clear(), you may need to free
}
static int is_running_queue;
-static int fill_active_slot(void *unused)
+static int fill_active_slot(void *data UNUSED)
{
struct transfer_request *request;
static void process_object_response(void *callback_data);
-static void start_object_request(struct walker *walker,
- struct object_request *obj_req)
+static void start_object_request(struct object_request *obj_req)
{
struct active_request_slot *slot;
struct http_object_request *req;
obj_req->repo =
obj_req->repo->next;
release_http_object_request(obj_req->req);
- start_object_request(walker, obj_req);
+ start_object_request(obj_req);
return;
}
}
free(obj_req);
}
-static int fill_active_slot(struct walker *walker)
+static int fill_active_slot(void *data UNUSED)
{
struct object_request *obj_req;
struct list_head *pos, *tmp, *head = &object_queue_head;
if (has_object_file(&obj_req->oid))
obj_req->state = COMPLETE;
else {
- start_object_request(walker, obj_req);
+ start_object_request(obj_req);
return 1;
}
}
walker->cleanup = cleanup;
walker->data = data;
- add_fill_function(walker, (int (*)(void *)) fill_active_slot);
+ add_fill_function(NULL, fill_active_slot);
return walker;
}
#include "promisor-remote.h"
#include "trace.h"
#include "url.h"
+#include "parse-options.h"
static int parse_combine_filter(
struct list_objects_filter_options *filter_options,
#define LIST_OBJECTS_FILTER_OPTIONS_H
#include "object.h"
-#include "parse-options.h"
#include "string-list.h"
#include "strbuf.h"
+struct option;
+
/*
* The list of defined filters for list-objects.
*/
#include "mailmap.h"
#include "object-store.h"
-#define DEBUG_MAILMAP 0
-#if DEBUG_MAILMAP
-#define debug_mm(...) fprintf(stderr, __VA_ARGS__)
-#define debug_str(X) ((X) ? (X) : "(none)")
-#else
-__attribute__((format (printf, 1, 2)))
-static inline void debug_mm(const char *format, ...) {}
-static inline const char *debug_str(const char *s) { return s; }
-#endif
-
const char *git_mailmap_file;
const char *git_mailmap_blob;
struct string_list namemap;
};
-static void free_mailmap_info(void *p, const char *s)
+static void free_mailmap_info(void *p, const char *s UNUSED)
{
struct mailmap_info *mi = (struct mailmap_info *)p;
- debug_mm("mailmap: -- complex: '%s' -> '%s' <%s>\n",
- s, debug_str(mi->name), debug_str(mi->email));
free(mi->name);
free(mi->email);
free(mi);
}
-static void free_mailmap_entry(void *p, const char *s)
+static void free_mailmap_entry(void *p, const char *s UNUSED)
{
struct mailmap_entry *me = (struct mailmap_entry *)p;
- debug_mm("mailmap: removing entries for <%s>, with %"PRIuMAX" sub-entries\n",
- s, (uintmax_t)me->namemap.nr);
- debug_mm("mailmap: - simple: '%s' <%s>\n",
- debug_str(me->name), debug_str(me->email));
free(me->name);
free(me->email);
}
if (!old_name) {
- debug_mm("mailmap: adding (simple) entry for '%s'\n", old_email);
-
/* Replace current name and new email for simple entry */
if (new_name) {
free(me->name);
}
} else {
struct mailmap_info *mi = xcalloc(1, sizeof(struct mailmap_info));
- debug_mm("mailmap: adding (complex) entry for '%s'\n", old_email);
mi->name = xstrdup_or_null(new_name);
mi->email = xstrdup_or_null(new_email);
string_list_insert(&me->namemap, old_name)->util = mi;
}
-
- debug_mm("mailmap: '%s' <%s> -> '%s' <%s>\n",
- debug_str(old_name), old_email,
- debug_str(new_name), debug_str(new_email));
}
static char *parse_name_and_email(char *buffer, char **name,
void clear_mailmap(struct string_list *map)
{
- debug_mm("mailmap: clearing %"PRIuMAX" entries...\n",
- (uintmax_t)map->nr);
map->strdup_strings = 1;
string_list_clear_func(map, free_mailmap_entry);
- debug_mm("mailmap: cleared\n");
}
/*
struct string_list_item *item;
struct mailmap_entry *me;
- debug_mm("map_user: map '%.*s' <%.*s>\n",
- (int)*namelen, debug_str(*name),
- (int)*emaillen, debug_str(*email));
-
item = lookup_prefix(map, *email, *emaillen);
if (item) {
me = (struct mailmap_entry *)item->util;
}
if (item) {
struct mailmap_info *mi = (struct mailmap_info *)item->util;
- if (mi->name == NULL && mi->email == NULL) {
- debug_mm("map_user: -- (no simple mapping)\n");
+ if (mi->name == NULL && mi->email == NULL)
return 0;
- }
if (mi->email) {
*email = mi->email;
*emaillen = strlen(*email);
*name = mi->name;
*namelen = strlen(*name);
}
- debug_mm("map_user: to '%.*s' <%.*s>\n",
- (int)*namelen, debug_str(*name),
- (int)*emaillen, debug_str(*email));
return 1;
}
- debug_mm("map_user: --\n");
return 0;
}
parse_opt_subcommand_fn *subcommand_fn;
};
-#define OPT_BIT_F(s, l, v, h, b, f) { OPTION_BIT, (s), (l), (v), NULL, (h), \
- PARSE_OPT_NOARG|(f), NULL, (b) }
-#define OPT_COUNTUP_F(s, l, v, h, f) { OPTION_COUNTUP, (s), (l), (v), NULL, \
- (h), PARSE_OPT_NOARG|(f) }
-#define OPT_SET_INT_F(s, l, v, h, i, f) { OPTION_SET_INT, (s), (l), (v), NULL, \
- (h), PARSE_OPT_NOARG | (f), NULL, (i) }
+#define OPT_BIT_F(s, l, v, h, b, f) { \
+ .type = OPTION_BIT, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .help = (h), \
+ .flags = PARSE_OPT_NOARG|(f), \
+ .callback = NULL, \
+ .defval = (b), \
+}
+#define OPT_COUNTUP_F(s, l, v, h, f) { \
+ .type = OPTION_COUNTUP, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .help = (h), \
+ .flags = PARSE_OPT_NOARG|(f), \
+}
+#define OPT_SET_INT_F(s, l, v, h, i, f) { \
+ .type = OPTION_SET_INT, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .help = (h), \
+ .flags = PARSE_OPT_NOARG | (f), \
+ .defval = (i), \
+}
#define OPT_BOOL_F(s, l, v, h, f) OPT_SET_INT_F(s, l, v, h, 1, f)
-#define OPT_CALLBACK_F(s, l, v, a, h, f, cb) \
- { OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), (cb) }
-#define OPT_STRING_F(s, l, v, a, h, f) { OPTION_STRING, (s), (l), (v), (a), (h), (f) }
-#define OPT_INTEGER_F(s, l, v, h, f) { OPTION_INTEGER, (s), (l), (v), N_("n"), (h), (f) }
+#define OPT_CALLBACK_F(s, l, v, a, h, f, cb) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = (a), \
+ .help = (h), \
+ .flags = (f), \
+ .callback = (cb), \
+}
+#define OPT_STRING_F(s, l, v, a, h, f) { \
+ .type = OPTION_STRING, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = (a), \
+ .help = (h), \
+ .flags = (f), \
+}
+#define OPT_INTEGER_F(s, l, v, h, f) { \
+ .type = OPTION_INTEGER, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = N_("n"), \
+ .help = (h), \
+ .flags = (f), \
+}
-#define OPT_END() { OPTION_END }
-#define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
+#define OPT_END() { \
+ .type = OPTION_END, \
+}
+#define OPT_GROUP(h) { \
+ .type = OPTION_GROUP, \
+ .help = (h), \
+}
#define OPT_BIT(s, l, v, h, b) OPT_BIT_F(s, l, v, h, b, 0)
-#define OPT_BITOP(s, l, v, h, set, clear) { OPTION_BITOP, (s), (l), (v), NULL, (h), \
- PARSE_OPT_NOARG|PARSE_OPT_NONEG, NULL, \
- (set), NULL, (clear) }
-#define OPT_NEGBIT(s, l, v, h, b) { OPTION_NEGBIT, (s), (l), (v), NULL, \
- (h), PARSE_OPT_NOARG, NULL, (b) }
+#define OPT_BITOP(s, l, v, h, set, clear) { \
+ .type = OPTION_BITOP, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .help = (h), \
+ .flags = PARSE_OPT_NOARG|PARSE_OPT_NONEG, \
+ .defval = (set), \
+ .extra = (clear), \
+}
+#define OPT_NEGBIT(s, l, v, h, b) { \
+ .type = OPTION_NEGBIT, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .help = (h), \
+ .flags = PARSE_OPT_NOARG, \
+ .defval = (b), \
+}
#define OPT_COUNTUP(s, l, v, h) OPT_COUNTUP_F(s, l, v, h, 0)
#define OPT_SET_INT(s, l, v, h, i) OPT_SET_INT_F(s, l, v, h, i, 0)
#define OPT_BOOL(s, l, v, h) OPT_BOOL_F(s, l, v, h, 0)
-#define OPT_HIDDEN_BOOL(s, l, v, h) { OPTION_SET_INT, (s), (l), (v), NULL, \
- (h), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1}
-#define OPT_CMDMODE_F(s, l, v, h, i, f) { OPTION_SET_INT, (s), (l), (v), NULL, \
- (h), PARSE_OPT_CMDMODE|PARSE_OPT_NOARG|PARSE_OPT_NONEG | (f), NULL, (i) }
+#define OPT_HIDDEN_BOOL(s, l, v, h) { \
+ .type = OPTION_SET_INT, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .help = (h), \
+ .flags = PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, \
+ .defval = 1, \
+}
+#define OPT_CMDMODE_F(s, l, v, h, i, f) { \
+ .type = OPTION_SET_INT, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .help = (h), \
+ .flags = PARSE_OPT_CMDMODE|PARSE_OPT_NOARG|PARSE_OPT_NONEG | (f), \
+ .defval = (i), \
+}
#define OPT_CMDMODE(s, l, v, h, i) OPT_CMDMODE_F(s, l, v, h, i, 0)
#define OPT_INTEGER(s, l, v, h) OPT_INTEGER_F(s, l, v, h, 0)
-#define OPT_MAGNITUDE(s, l, v, h) { OPTION_MAGNITUDE, (s), (l), (v), \
- N_("n"), (h), PARSE_OPT_NONEG }
+#define OPT_MAGNITUDE(s, l, v, h) { \
+ .type = OPTION_MAGNITUDE, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = N_("n"), \
+ .help = (h), \
+ .flags = PARSE_OPT_NONEG, \
+}
#define OPT_STRING(s, l, v, a, h) OPT_STRING_F(s, l, v, a, h, 0)
-#define OPT_STRING_LIST(s, l, v, a, h) \
- { OPTION_CALLBACK, (s), (l), (v), (a), \
- (h), 0, &parse_opt_string_list }
-#define OPT_UYN(s, l, v, h) { OPTION_CALLBACK, (s), (l), (v), NULL, \
- (h), PARSE_OPT_NOARG, &parse_opt_tertiary }
-#define OPT_EXPIRY_DATE(s, l, v, h) \
- { OPTION_CALLBACK, (s), (l), (v), N_("expiry-date"),(h), 0, \
- parse_opt_expiry_date_cb }
-#define OPT_CALLBACK(s, l, v, a, h, f) OPT_CALLBACK_F(s, l, v, a, h, 0, f)
-#define OPT_NUMBER_CALLBACK(v, h, f) \
- { OPTION_NUMBER, 0, NULL, (v), NULL, (h), \
- PARSE_OPT_NOARG | PARSE_OPT_NONEG, (f) }
-#define OPT_FILENAME(s, l, v, h) { OPTION_FILENAME, (s), (l), (v), \
- N_("file"), (h) }
-#define OPT_COLOR_FLAG(s, l, v, h) \
- { OPTION_CALLBACK, (s), (l), (v), N_("when"), (h), PARSE_OPT_OPTARG, \
- parse_opt_color_flag_cb, (intptr_t)"always" }
-
-#define OPT_NOOP_NOARG(s, l) \
- { OPTION_CALLBACK, (s), (l), NULL, NULL, \
- N_("no-op (backward compatibility)"), \
- PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, parse_opt_noop_cb }
-
-#define OPT_ALIAS(s, l, source_long_name) \
- { OPTION_ALIAS, (s), (l), (source_long_name) }
+#define OPT_STRING_LIST(s, l, v, a, h) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = (a), \
+ .help = (h), \
+ .callback = &parse_opt_string_list, \
+}
+#define OPT_UYN(s, l, v, h) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .help = (h), \
+ .flags = PARSE_OPT_NOARG, \
+ .callback = &parse_opt_tertiary, \
+}
+#define OPT_EXPIRY_DATE(s, l, v, h) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = N_("expiry-date"), \
+ .help = (h), \
+ .callback = parse_opt_expiry_date_cb, \
+}
+#define OPT_CALLBACK(s, l, v, a, h, cb) OPT_CALLBACK_F(s, l, v, a, h, 0, cb)
+#define OPT_NUMBER_CALLBACK(v, h, cb) { \
+ .type = OPTION_NUMBER, \
+ .value = (v), \
+ .help = (h), \
+ .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, \
+ .callback = (cb), \
+}
+#define OPT_FILENAME(s, l, v, h) { \
+ .type = OPTION_FILENAME, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = N_("file"), \
+ .help = (h), \
+}
+#define OPT_COLOR_FLAG(s, l, v, h) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = N_("when"), \
+ .help = (h), \
+ .flags = PARSE_OPT_OPTARG, \
+ .callback = parse_opt_color_flag_cb, \
+ .defval = (intptr_t)"always", \
+}
+
+#define OPT_NOOP_NOARG(s, l) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .help = N_("no-op (backward compatibility)"), \
+ .flags = PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, \
+ .callback = parse_opt_noop_cb, \
+}
+
+#define OPT_ALIAS(s, l, source_long_name) { \
+ .type = OPTION_ALIAS, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (source_long_name), \
+}
#define OPT_SUBCOMMAND_F(l, v, fn, f) { \
.type = OPTION_SUBCOMMAND, \
.long_name = (l), \
.value = (v), \
.flags = (f), \
- .subcommand_fn = (fn) }
+ .subcommand_fn = (fn), \
+}
#define OPT_SUBCOMMAND(l, v, fn) OPT_SUBCOMMAND_F((l), (v), (fn), 0)
/*
#define OPT__VERBOSE(var, h) OPT_COUNTUP('v', "verbose", (var), (h))
#define OPT__QUIET(var, h) OPT_COUNTUP('q', "quiet", (var), (h))
-#define OPT__VERBOSITY(var) \
- { OPTION_CALLBACK, 'v', "verbose", (var), NULL, N_("be more verbose"), \
- PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \
- { OPTION_CALLBACK, 'q', "quiet", (var), NULL, N_("be more quiet"), \
- PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }
+#define OPT__VERBOSITY(var) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = 'v', \
+ .long_name = "verbose", \
+ .value = (var), \
+ .help = N_("be more verbose"), \
+ .flags = PARSE_OPT_NOARG, \
+ .callback = &parse_opt_verbosity_cb, \
+}, { \
+ .type = OPTION_CALLBACK, \
+ .short_name = 'q', \
+ .long_name = "quiet", \
+ .value = (var), \
+ .help = N_("be more quiet"), \
+ .flags = PARSE_OPT_NOARG, \
+ .callback = &parse_opt_verbosity_cb, \
+}
#define OPT__DRY_RUN(var, h) OPT_BOOL('n', "dry-run", (var), (h))
#define OPT__FORCE(var, h, f) OPT_COUNTUP_F('f', "force", (var), (h), (f))
-#define OPT__ABBREV(var) \
- { OPTION_CALLBACK, 0, "abbrev", (var), N_("n"), \
- N_("use <n> digits to display object names"), \
- PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }
+#define OPT__ABBREV(var) { \
+ .type = OPTION_CALLBACK, \
+ .long_name = "abbrev", \
+ .value = (var), \
+ .argh = N_("n"), \
+ .help = N_("use <n> digits to display object names"), \
+ .flags = PARSE_OPT_OPTARG, \
+ .callback = &parse_opt_abbrev_cb, \
+}
#define OPT__SUPER_PREFIX(var) \
OPT_STRING_F(0, "super-prefix", (var), N_("prefix"), \
N_("prefixed path to initial superproject"), PARSE_OPT_HIDDEN)
#define OPT__COLOR(var, h) \
OPT_COLOR_FLAG(0, "color", (var), (h))
-#define OPT_COLUMN(s, l, v, h) \
- { OPTION_CALLBACK, (s), (l), (v), N_("style"), (h), PARSE_OPT_OPTARG, parseopt_column_callback }
-#define OPT_PASSTHRU(s, l, v, a, h, f) \
- { OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), parse_opt_passthru }
-#define OPT_PASSTHRU_ARGV(s, l, v, a, h, f) \
- { OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), parse_opt_passthru_argv }
-#define _OPT_CONTAINS_OR_WITH(name, variable, help, flag) \
- { OPTION_CALLBACK, 0, name, (variable), N_("commit"), (help), \
- PARSE_OPT_LASTARG_DEFAULT | flag, \
- parse_opt_commits, (intptr_t) "HEAD" \
- }
+#define OPT_COLUMN(s, l, v, h) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = N_("style"), \
+ .help = (h), \
+ .flags = PARSE_OPT_OPTARG, \
+ .callback = parseopt_column_callback, \
+}
+#define OPT_PASSTHRU(s, l, v, a, h, f) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = (a), \
+ .help = (h), \
+ .flags = (f), \
+ .callback = parse_opt_passthru, \
+}
+#define OPT_PASSTHRU_ARGV(s, l, v, a, h, f) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = (a), \
+ .help = (h), \
+ .flags = (f), \
+ .callback = parse_opt_passthru_argv, \
+}
+#define _OPT_CONTAINS_OR_WITH(l, v, h, f) { \
+ .type = OPTION_CALLBACK, \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = N_("commit"), \
+ .help = (h), \
+ .flags = PARSE_OPT_LASTARG_DEFAULT | (f), \
+ .callback = parse_opt_commits, \
+ .defval = (intptr_t) "HEAD", \
+}
#define OPT_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("contains", v, h, PARSE_OPT_NONEG)
#define OPT_NO_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("no-contains", v, h, PARSE_OPT_NONEG)
#define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
return !git_config_get_index_threads(&val) && val != 1;
}
+enum write_extensions {
+ WRITE_NO_EXTENSION = 0,
+ WRITE_SPLIT_INDEX_EXTENSION = 1<<0,
+ WRITE_CACHE_TREE_EXTENSION = 1<<1,
+ WRITE_RESOLVE_UNDO_EXTENSION = 1<<2,
+ WRITE_UNTRACKED_CACHE_EXTENSION = 1<<3,
+ WRITE_FSMONITOR_EXTENSION = 1<<4,
+};
+#define WRITE_ALL_EXTENSIONS ((enum write_extensions)-1)
+
/*
* On success, `tempfile` is closed. If it is the temporary file
* of a `struct lock_file`, we will therefore effectively perform
* rely on it.
*/
static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
- int strip_extensions, unsigned flags)
+ enum write_extensions write_extensions, unsigned flags)
{
uint64_t start = getnanotime();
struct hashfile *f;
return -1;
}
- if (!strip_extensions && istate->split_index &&
- !is_null_oid(&istate->split_index->base_oid)) {
+ if (write_extensions & WRITE_SPLIT_INDEX_EXTENSION &&
+ istate->split_index) {
struct strbuf sb = STRBUF_INIT;
if (istate->sparse_index)
if (err)
return -1;
}
- if (!strip_extensions && !drop_cache_tree && istate->cache_tree) {
+ if (write_extensions & WRITE_CACHE_TREE_EXTENSION &&
+ !drop_cache_tree && istate->cache_tree) {
struct strbuf sb = STRBUF_INIT;
cache_tree_write(&sb, istate->cache_tree);
if (err)
return -1;
}
- if (!strip_extensions && istate->resolve_undo) {
+ if (write_extensions & WRITE_RESOLVE_UNDO_EXTENSION &&
+ istate->resolve_undo) {
struct strbuf sb = STRBUF_INIT;
resolve_undo_write(&sb, istate->resolve_undo);
if (err)
return -1;
}
- if (!strip_extensions && istate->untracked) {
+ if (write_extensions & WRITE_UNTRACKED_CACHE_EXTENSION &&
+ istate->untracked) {
struct strbuf sb = STRBUF_INIT;
write_untracked_extension(&sb, istate->untracked);
if (err)
return -1;
}
- if (!strip_extensions && istate->fsmonitor_last_update) {
+ if (write_extensions & WRITE_FSMONITOR_EXTENSION &&
+ istate->fsmonitor_last_update) {
struct strbuf sb = STRBUF_INIT;
write_fsmonitor_extension(&sb, istate);
return commit_lock_file(lk);
}
-static int do_write_locked_index(struct index_state *istate, struct lock_file *lock,
- unsigned flags)
+static int do_write_locked_index(struct index_state *istate,
+ struct lock_file *lock,
+ unsigned flags,
+ enum write_extensions write_extensions)
{
int ret;
int was_full = istate->sparse_index == INDEX_EXPANDED;
*/
trace2_region_enter_printf("index", "do_write_index", the_repository,
"%s", get_lock_file_path(lock));
- ret = do_write_index(istate, lock->tempfile, 0, flags);
+ ret = do_write_index(istate, lock->tempfile, write_extensions, flags);
trace2_region_leave_printf("index", "do_write_index", the_repository,
"%s", get_lock_file_path(lock));
{
int ret;
prepare_to_write_split_index(istate);
- ret = do_write_locked_index(istate, lock, flags);
+ ret = do_write_locked_index(istate, lock, flags, WRITE_ALL_EXTENSIONS);
finish_writing_split_index(istate);
return ret;
}
trace2_region_enter_printf("index", "shared/do_write_index",
the_repository, "%s", get_tempfile_path(*temp));
- ret = do_write_index(si->base, *temp, 1, flags);
+ ret = do_write_index(si->base, *temp, WRITE_NO_EXTENSION, flags);
trace2_region_leave_printf("index", "shared/do_write_index",
the_repository, "%s", get_tempfile_path(*temp));
if ((!si && !test_split_index_env) ||
alternate_index_output ||
(istate->cache_changed & ~EXTMASK)) {
- if (si)
- oidclr(&si->base_oid);
- ret = do_write_locked_index(istate, lock, flags);
+ ret = do_write_locked_index(istate, lock, flags,
+ ~WRITE_SPLIT_INDEX_EXTENSION);
goto out;
}
/* Same initial permissions as the main .git/index file */
temp = mks_tempfile_sm(git_path("sharedindex_XXXXXX"), 0, 0666);
if (!temp) {
- oidclr(&si->base_oid);
- ret = do_write_locked_index(istate, lock, flags);
+ ret = do_write_locked_index(istate, lock, flags,
+ ~WRITE_SPLIT_INDEX_EXTENSION);
goto out;
}
ret = write_shared_index(istate, &temp, flags);
ATOM_THEN,
ATOM_ELSE,
ATOM_REST,
+ ATOM_AHEADBEHIND,
};
/*
return 0;
}
+static int ahead_behind_atom_parser(struct ref_format *format, struct used_atom *atom,
+ const char *arg, struct strbuf *err)
+{
+ struct string_list_item *item;
+
+ if (!arg)
+ return strbuf_addf_ret(err, -1, _("expected format: %%(ahead-behind:<committish>)"));
+
+ item = string_list_append(&format->bases, arg);
+ item->util = lookup_commit_reference_by_name(arg);
+ if (!item->util)
+ die("failed to find '%s'", arg);
+
+ return 0;
+}
+
static int head_atom_parser(struct ref_format *format UNUSED,
struct used_atom *atom,
const char *arg, struct strbuf *err)
[ATOM_THEN] = { "then", SOURCE_NONE },
[ATOM_ELSE] = { "else", SOURCE_NONE },
[ATOM_REST] = { "rest", SOURCE_NONE, FIELD_STR, rest_atom_parser },
+ [ATOM_AHEADBEHIND] = { "ahead-behind", SOURCE_OTHER, FIELD_STR, ahead_behind_atom_parser },
/*
* Please update $__git_ref_fieldlist in git-completion.bash
* when you add new atoms
struct object *obj;
int i;
struct object_info empty = OBJECT_INFO_INIT;
+ int ahead_behind_atoms = 0;
CALLOC_ARRAY(ref->value, used_atom_cnt);
else
v->s = xstrdup("");
continue;
+ } else if (atom_type == ATOM_AHEADBEHIND) {
+ if (ref->counts) {
+ const struct ahead_behind_count *count;
+ count = ref->counts[ahead_behind_atoms++];
+ v->s = xstrfmt("%d %d", count->ahead, count->behind);
+ } else {
+ /* Not a commit. */
+ v->s = xstrdup("");
+ }
+ continue;
} else
continue;
free((char *)item->value[i].s);
free(item->value);
}
+ free(item->counts);
free(item);
}
free_worktrees(ref_to_worktree_map.worktrees);
ref_to_worktree_map.worktrees = NULL;
}
+
+ FREE_AND_NULL(array->counts);
}
#define EXCLUDE_REACHED 0
struct commit_list *check_reachable,
int include_reached)
{
- struct rev_info revs;
int i, old_nr;
struct commit **to_clear;
- struct commit_list *cr;
if (!check_reachable)
return;
CALLOC_ARRAY(to_clear, array->nr);
-
- repo_init_revisions(the_repository, &revs, NULL);
-
for (i = 0; i < array->nr; i++) {
struct ref_array_item *item = array->items[i];
- add_pending_object(&revs, &item->commit->object, item->refname);
to_clear[i] = item->commit;
}
- for (cr = check_reachable; cr; cr = cr->next) {
- struct commit *merge_commit = cr->item;
- merge_commit->object.flags |= UNINTERESTING;
- add_pending_object(&revs, &merge_commit->object, "");
- }
-
- revs.limited = 1;
- if (prepare_revision_walk(&revs))
- die(_("revision walk setup failed"));
+ tips_reachable_from_bases(the_repository,
+ check_reachable,
+ to_clear, array->nr,
+ UNINTERESTING);
old_nr = array->nr;
array->nr = 0;
clear_commit_marks(merge_commit, ALL_REV_FLAGS);
}
- release_revisions(&revs);
free(to_clear);
}
+void filter_ahead_behind(struct repository *r,
+ struct ref_format *format,
+ struct ref_array *array)
+{
+ struct commit **commits;
+ size_t commits_nr = format->bases.nr + array->nr;
+
+ if (!format->bases.nr || !array->nr)
+ return;
+
+ ALLOC_ARRAY(commits, commits_nr);
+ for (size_t i = 0; i < format->bases.nr; i++)
+ commits[i] = format->bases.items[i].util;
+
+ ALLOC_ARRAY(array->counts, st_mult(format->bases.nr, array->nr));
+
+ commits_nr = format->bases.nr;
+ array->counts_nr = 0;
+ for (size_t i = 0; i < array->nr; i++) {
+ const char *name = array->items[i]->refname;
+ commits[commits_nr] = lookup_commit_reference_by_name(name);
+
+ if (!commits[commits_nr])
+ continue;
+
+ CALLOC_ARRAY(array->items[i]->counts, format->bases.nr);
+ for (size_t j = 0; j < format->bases.nr; j++) {
+ struct ahead_behind_count *count;
+ count = &array->counts[array->counts_nr++];
+ count->tip_index = commits_nr;
+ count->base_index = j;
+
+ array->items[i]->counts[j] = count;
+ }
+ commits_nr++;
+ }
+
+ ahead_behind(r, commits, commits_nr, array->counts, array->counts_nr);
+ free(commits);
+}
+
/*
* API for filtering a set of refs. Based on the type of refs the user
* has requested, we iterate through those refs and apply filters
#include "oid-array.h"
#include "refs.h"
#include "commit.h"
-#include "parse-options.h"
+#include "string-list.h"
/* Quoting styles */
#define QUOTE_NONE 0
struct atom_value;
struct ref_sorting;
+struct ahead_behind_count;
+struct option;
enum ref_sorting_order {
REF_SORTING_REVERSE = 1<<0,
const char *symref;
struct commit *commit;
struct atom_value *value;
+ struct ahead_behind_count **counts;
+
char refname[FLEX_ARRAY];
};
int nr, alloc;
struct ref_array_item **items;
struct rev_info *revs;
+
+ struct ahead_behind_count *counts;
+ size_t counts_nr;
};
struct ref_filter {
/* Internal state to ref-filter */
int need_color_reset_at_eol;
+
+ /* List of bases for ahead-behind counts. */
+ struct string_list bases;
};
-#define REF_FORMAT_INIT { .use_color = -1 }
+#define REF_FORMAT_INIT { \
+ .use_color = -1, \
+ .bases = STRING_LIST_INIT_DUP, \
+}
/* Macros for checking --merged and --no-merged options */
#define _OPT_MERGED_NO_MERGED(option, filter, h) \
const char *refname,
const struct object_id *oid);
+/*
+ * If the provided format includes ahead-behind atoms, then compute the
+ * ahead-behind values for the array of filtered references. Must be
+ * called after filter_refs() but before outputting the formatted refs.
+ *
+ * If this is not called, then any ahead-behind atoms will be blank.
+ */
+void filter_ahead_behind(struct repository *r,
+ struct ref_format *format,
+ struct ref_array *array);
+
#endif /* REF_FILTER_H */
#include "commit-reach.h"
#include "advice.h"
#include "connect.h"
+#include "parse-options.h"
enum map_direction { FROM_SRC, FROM_DST };
#ifndef REMOTE_H
#define REMOTE_H
-#include "parse-options.h"
#include "hashmap.h"
#include "refspec.h"
+struct option;
struct transport_ls_refs_options;
/**
#include "json-writer.h"
#include "list-objects-filter-options.h"
#include "resolve-undo.h"
+#include "parse-options.h"
volatile show_early_output_fn_t show_early_output;
#define REVISION_H
#include "commit.h"
-#include "parse-options.h"
#include "grep.h"
#include "notes.h"
#include "pretty.h"
struct saved_parents;
struct bloom_key;
struct bloom_filter_settings;
+struct option;
+struct parse_opt_ctx_t;
define_shared_commit_slab(revision_sources, char *);
struct rev_cmdline_info {
#include "gpg-interface.h"
#include "cache.h"
#include "shallow.h"
+#include "parse-options.h"
int option_parse_push_signed(const struct option *opt,
const char *arg, int unset)
static int save_head(const char *head)
{
- struct lock_file head_lock = LOCK_INIT;
- struct strbuf buf = STRBUF_INIT;
- int fd;
- ssize_t written;
-
- fd = hold_lock_file_for_update(&head_lock, git_path_head_file(), 0);
- if (fd < 0)
- return error_errno(_("could not lock HEAD"));
- strbuf_addf(&buf, "%s\n", head);
- written = write_in_full(fd, buf.buf, buf.len);
- strbuf_release(&buf);
- if (written < 0) {
- error_errno(_("could not write to '%s'"), git_path_head_file());
- rollback_lock_file(&head_lock);
- return -1;
- }
- if (commit_lock_file(&head_lock) < 0)
- return error(_("failed to finalize '%s'"), git_path_head_file());
- return 0;
+ return write_message(head, strlen(head), git_path_head_file(), 1);
}
static int rollback_is_safe(void)
}
if (commit_lock_file(&lock) < 0) {
strbuf_release(&buf);
- rollback_lock_file(&lock);
return error(_("failed to finalize '%s'"), filename);
}
check_count A 2
'
+test_expect_success 'blame with --contents' '
+ check_count --contents=file A 2
+'
+
+test_expect_success 'blame with --contents changed' '
+ echo "1A quick brown fox jumps over the" >contents &&
+ echo "another lazy dog" >>contents &&
+ check_count --contents=contents A 1 "Not Committed Yet" 1
+'
+
test_expect_success 'blame in a bare repo without starting commit' '
git clone --bare . bare.git &&
(
check_count A 2 B 2
'
+test_expect_success 'blame with --contents and revision' '
+ check_count -h testTag --contents=file A 2 "Not Committed Yet" 2
+'
+
test_expect_success 'setup B1 lines (branch1)' '
git checkout -b branch1 main &&
echo "3A slow green fox jumps into the" >>file &&
--- /dev/null
+#!/bin/sh
+
+test_description='Commit walk performance tests'
+. ./perf-lib.sh
+
+test_perf_large_repo
+
+test_expect_success 'setup' '
+ git for-each-ref --format="%(refname)" "refs/heads/*" "refs/tags/*" >allrefs &&
+ sort -r allrefs | head -n 50 >refs &&
+ for ref in $(cat refs)
+ do
+ git branch -f ref-$ref $ref &&
+ echo ref-$ref ||
+ return 1
+ done >branches &&
+ for ref in $(cat refs)
+ do
+ git tag -f tag-$ref $ref &&
+ echo tag-$ref ||
+ return 1
+ done >tags &&
+ git commit-graph write --reachable
+'
+
+test_perf 'ahead-behind counts: git for-each-ref' '
+ git for-each-ref --format="%(ahead-behind:HEAD)" --stdin <refs
+'
+
+test_perf 'ahead-behind counts: git branch' '
+ xargs git branch -l --format="%(ahead-behind:HEAD)" <branches
+'
+
+test_perf 'ahead-behind counts: git tag' '
+ xargs git tag -l --format="%(ahead-behind:HEAD)" <tags
+'
+
+test_perf 'contains: git for-each-ref --merged' '
+ git for-each-ref --merged=HEAD --stdin <refs
+'
+
+test_perf 'contains: git branch --merged' '
+ xargs git branch --merged=HEAD <branches
+'
+
+test_perf 'contains: git tag --merged' '
+ xargs git tag --merged=HEAD <tags
+'
+
+test_done
test_perf_on_all git checkout-index -f --all
test_perf_on_all git update-index --add --remove $SPARSE_CONE/a
test_perf_on_all "git rm -f $SPARSE_CONE/a && git checkout HEAD -- $SPARSE_CONE/a"
-test_perf_on_all git grep --cached --sparse bogus -- "f2/f1/f1/*"
+test_perf_on_all git grep --cached bogus -- "f2/f1/f1/*"
test_done
git ls-files -s &&
read_tree_u_must_succeed --reset -u HEAD &&
git ls-files -s >actual &&
- ! test -f old
+ ! test -f old &&
+ test_cmp expect actual
'
test_expect_success 'two-way reset should remove remnants too' '
git ls-files -s &&
read_tree_u_must_succeed --reset -u HEAD HEAD &&
git ls-files -s >actual &&
- ! test -f old
+ ! test -f old &&
+ test_cmp expect actual
'
test_expect_success 'Porcelain reset should remove remnants too' '
git ls-files -s &&
git reset --hard &&
git ls-files -s >actual &&
- ! test -f old
+ ! test -f old &&
+ test_cmp expect actual
'
test_expect_success 'Porcelain checkout -f should remove remnants too' '
git ls-files -s &&
git checkout -f &&
git ls-files -s >actual &&
- ! test -f old
+ ! test -f old &&
+ test_cmp expect actual
'
test_expect_success 'Porcelain checkout -f HEAD should remove remnants too' '
git ls-files -s &&
git checkout -f HEAD &&
git ls-files -s >actual &&
- ! test -f old
+ ! test -f old &&
+ test_cmp expect actual
'
test_done
fatal: Not a valid object name $(test_oid deadbeef_short)
EOF
test_must_fail git cat-file $arg1 $arg2 $(test_oid deadbeef_short) >out 2>err.actual &&
- test_must_be_empty out
+ test_must_be_empty out &&
+ test_cmp expect.err err.actual
'
test_expect_success "cat-file $arg1 $arg2 error on missing full OID" '
'
test_expect_success 'mktree refuses to read ls-tree -r output (1)' '
- test_must_fail git mktree <all >actual
+ test_must_fail git mktree <all
'
test_expect_success 'mktree refuses to read ls-tree -r output (2)' '
- test_must_fail git mktree <all.withsub >actual
+ test_must_fail git mktree <all.withsub
'
test_done
test_expect_success 'gitdir selection on unsupported repo' '
# Make sure it would stop at test2, not trash
- test_expect_code 1 git -C test2 config core.repositoryformatversion >actual
+ test_expect_code 1 git -C test2 config core.repositoryformatversion
'
test_expect_success 'gitdir not required mode' '
EOF
git update-ref --stdin <stdin >actual &&
printf "%s: ok\n" start commit start commit >expect &&
+ test_cmp expect actual &&
test_must_fail git show-ref --verify refs/heads/create-and-delete
'
commit
EOF
test_must_fail git update-ref --stdin <stdin >actual &&
+ printf "%s: ok\n" start >expect &&
+ test_cmp expect actual &&
test_must_fail git show-ref --verify refs/heads/restart
'
git update-ref $prefix/foo $C &&
git pack-refs --all &&
git update-ref $prefix/foo $D &&
- git for-each-ref $prefix >unchanged &&
# Now try to update the reference, but hold the `packed-refs` lock
# for a while to see what happens while the process is blocked:
: >.git/packed-refs.lock &&
git add @{yesterday} &&
git commit -m "funny reflog file" &&
git hash-object @{yesterday} >expect &&
- git rev-parse HEAD:@{yesterday} >actual
+ git rev-parse HEAD:@{yesterday} >actual &&
+ test_cmp expect actual
'
test_expect_success '@{upstream}-parsing does not look beyond colon' '
git add @{upstream} &&
git commit -m "funny upstream file" &&
git hash-object @{upstream} >expect &&
- git rev-parse HEAD:@{upstream} >actual
+ git rev-parse HEAD:@{upstream} >actual &&
+ test_cmp expect actual
'
test_done
match 0 1 0 1 'z' '[Z-y]'
match 1 1 1 1 'Z' '[Z-y]'
+test_expect_success 'matching does not exhibit exponential behavior' '
+ {
+ test-tool wildmatch wildmatch \
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab \
+ "*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a" &
+ pid=$!
+ } &&
+ sleep 2 &&
+ ! kill $!
+'
+
test_done
test_cmp expect actual
'
+test_expect_success 'git branch --format with ahead-behind' '
+ cat >expect <<-\EOF &&
+ (HEAD detached from fromtag) 0 0
+ refs/heads/ambiguous 0 0
+ refs/heads/branch-one 1 0
+ refs/heads/branch-two 0 0
+ refs/heads/main 1 0
+ refs/heads/ref-to-branch 1 0
+ refs/heads/ref-to-remote 1 0
+ EOF
+ git branch --format="%(refname) %(ahead-behind:HEAD)" >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'git branch with --format=%(rest) must fail' '
test_must_fail git branch --format="%(rest)" >actual
'
test_must_fail git rebase $opt --reapply-cherry-picks A
"
+ test_expect_success "$opt incompatible with --rebase-merges" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --rebase-merges A
+ "
+
test_expect_success "$opt incompatible with --update-refs" "
git checkout B^0 &&
test_must_fail git rebase $opt --update-refs A
grep -e --no-autosquash err
"
+ test_expect_success "$opt incompatible with rebase.rebaseMerges" "
+ git checkout B^0 &&
+ test_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&
+ grep -e --no-rebase-merges err
+ "
+
test_expect_success "$opt incompatible with rebase.updateRefs" "
git checkout B^0 &&
test_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&
git -c rebase.autosquash=true rebase --no-autosquash $opt A
"
+ test_expect_success "$opt okay with overridden rebase.rebaseMerges" "
+ test_when_finished \"git reset --hard B^0\" &&
+ git checkout B^0 &&
+ git -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A
+ "
+
test_expect_success "$opt okay with overridden rebase.updateRefs" "
test_when_finished \"git reset --hard B^0\" &&
git checkout B^0 &&
EOF
'
+test_expect_success '--no-rebase-merges countermands --rebase-merges' '
+ git checkout -b no-rebase-merges E &&
+ git rebase --rebase-merges --no-rebase-merges C &&
+ test_cmp_graph C.. <<-\EOF
+ * B
+ * D
+ o C
+ EOF
+'
+
test_expect_success 'do not rebase cousins unless asked for' '
git checkout -b cousins main &&
before="$(git rev-parse --verify HEAD)" &&
EOF
'
+test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
+ test_config rebase.rebaseMerges rebase-cousins &&
+ git checkout -b config-rebase-cousins main &&
+ git rebase HEAD^ &&
+ test_cmp_graph HEAD^.. <<-\EOF
+ * Merge the topic branch '\''onebranch'\''
+ |\
+ | * D
+ | * G
+ |/
+ o H
+ EOF
+'
+
+test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '
+ test_config rebase.rebaseMerges no-rebase-cousins &&
+ git checkout -b override-config-no-rebase-cousins E &&
+ git rebase --no-rebase-merges C &&
+ test_cmp_graph C.. <<-\EOF
+ * B
+ * D
+ o C
+ EOF
+'
+
+test_expect_success '--rebase-merges overrides rebase.rebaseMerges=rebase-cousins' '
+ test_config rebase.rebaseMerges rebase-cousins &&
+ git checkout -b override-config-rebase-cousins E &&
+ before="$(git rev-parse --verify HEAD)" &&
+ test_tick &&
+ git rebase --rebase-merges C &&
+ test_cmp_rev HEAD $before
+'
+
test_expect_success 'refs/rewritten/* is worktree-local' '
git worktree add wt &&
cat >wt/script-from-scratch <<-\EOF &&
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
-if test_have_prereq !PERL
-then
- skip_all='skipping add -i (scripted) tests, perl not available'
- test_done
-fi
-
diff_cmp () {
for x
do
'
check_tar b
+check_mtime b a/a 1117231200
test_expect_success 'git archive --mtime' '
git archive --mtime=2002-02-02T02:02:02-0200 HEAD >with_mtime.tar
test_cmp_bin b.tar b5-nick.tar
'
-test_expect_success 'validate file modification time' '
- mkdir extract &&
- "$TAR" xf b.tar -C extract a/a &&
- test-tool chmtime --get extract/a/a >b.mtime &&
- echo "1117231200" >expected.mtime &&
- test_cmp expected.mtime b.mtime
-'
-
test_expect_success 'git get-tar-commit-id' '
git get-tar-commit-id <b.tar >actual &&
git rev-parse HEAD >expect &&
test_expect_success 'detect incorrect generation number' '
corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\01" \
- "non-zero generation number"
+ "commit-graph generation for commit"
'
test_expect_success 'detect incorrect commit date' '
graph_git_behavior 'overflow 2' repo left right
+test_expect_success 'single commit with generation data exceeding UINT32_MAX' '
+ git init repo-uint32-max &&
+ cd repo-uint32-max &&
+ test_commit --date "@4294967297 +0000" 1 &&
+ git commit-graph write --reachable &&
+ graph_read_expect 1 "generation_data" &&
+ git commit-graph verify
+'
+
test_done
# Note that leading and trailing whitespace is important to correctly
# simulate a continuation/folded header.
- printf "">$CHALLENGE &&
- printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >$CHALLENGE &&
- printf " \r\n" >>$CHALLENGE &&
- printf " param2=\"value2\"\r\n" >>$CHALLENGE &&
- printf "WWW-Authenticate: Bearer authorize_uri=\"id.example.com\"\r\n" >>$CHALLENGE &&
- printf " p=1\r\n" >>$CHALLENGE &&
- printf " \r\n" >>$CHALLENGE &&
- printf " q=0\r\n" >>$CHALLENGE &&
- printf "WWW-Authenticate: Basic realm=\"example.com\"\r\n" >>$CHALLENGE &&
+ printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >"$CHALLENGE" &&
+ printf " \r\n" >>"$CHALLENGE" &&
+ printf " param2=\"value2\"\r\n" >>"$CHALLENGE" &&
+ printf "WWW-Authenticate: Bearer authorize_uri=\"id.example.com\"\r\n" >>"$CHALLENGE" &&
+ printf " p=1\r\n" >>"$CHALLENGE" &&
+ printf " \r\n" >>"$CHALLENGE" &&
+ printf " q=0\r\n" >>"$CHALLENGE" &&
+ printf "WWW-Authenticate: Basic realm=\"example.com\"\r\n" >>"$CHALLENGE" &&
test_config_global credential.helper test-helper &&
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
# Note that leading and trailing whitespace is important to correctly
# simulate a continuation/folded header.
- printf "">$CHALLENGE &&
- printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >$CHALLENGE &&
- printf " \r\n" >>$CHALLENGE &&
- printf "\tparam2=\"value2\"\r\n" >>$CHALLENGE &&
- printf "WWW-Authenticate: Basic realm=\"example.com\"" >>$CHALLENGE &&
+ printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >"$CHALLENGE" &&
+ printf " \r\n" >>"$CHALLENGE" &&
+ printf "\tparam2=\"value2\"\r\n" >>"$CHALLENGE" &&
+ printf "WWW-Authenticate: Basic realm=\"example.com\"" >>"$CHALLENGE" &&
test_config_global credential.helper test-helper &&
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
sig_crlf=${sig_crlf%dummy}
test_atom refs/tags/fake-sig-crlf contents:signature "$sig_crlf"
+test_expect_success 'git for-each-ref --stdin: empty' '
+ >in &&
+ git for-each-ref --format="%(refname)" --stdin <in >actual &&
+ git for-each-ref --format="%(refname)" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git for-each-ref --stdin: fails if extra args' '
+ >in &&
+ test_must_fail git for-each-ref --format="%(refname)" \
+ --stdin refs/heads/extra <in 2>err &&
+ grep "unknown arguments supplied with --stdin" err
+'
+
+test_expect_success 'git for-each-ref --stdin: matches' '
+ cat >in <<-EOF &&
+ refs/tags/multi*
+ refs/heads/amb*
+ EOF
+
+ cat >expect <<-EOF &&
+ refs/heads/ambiguous
+ refs/tags/multi-ref1-100000-user1
+ refs/tags/multi-ref1-100000-user2
+ refs/tags/multi-ref1-200000-user1
+ refs/tags/multi-ref1-200000-user2
+ refs/tags/multi-ref2-100000-user1
+ refs/tags/multi-ref2-100000-user2
+ refs/tags/multi-ref2-200000-user1
+ refs/tags/multi-ref2-200000-user2
+ refs/tags/multiline
+ EOF
+
+ git for-each-ref --format="%(refname)" --stdin <in >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git for-each-ref with non-existing refs' '
+ cat >in <<-EOF &&
+ refs/heads/this-ref-does-not-exist
+ refs/tags/bogus
+ EOF
+
+ git for-each-ref --format="%(refname)" --stdin <in >actual &&
+ test_must_be_empty actual &&
+
+ xargs git for-each-ref --format="%(refname)" <in >actual &&
+ test_must_be_empty actual
+'
+
test_done
test_must_be_empty brief-err
'
+test_expect_success 'ahead-behind requires an argument' '
+ test_must_fail git for-each-ref \
+ --format="%(ahead-behind)" 2>err &&
+ echo "fatal: expected format: %(ahead-behind:<committish>)" >expect &&
+ test_cmp expect err
+'
+
+test_expect_success 'missing ahead-behind base' '
+ test_must_fail git for-each-ref \
+ --format="%(ahead-behind:refs/heads/missing)" 2>err &&
+ echo "fatal: failed to find '\''refs/heads/missing'\''" >expect &&
+ test_cmp expect err
+'
+
test_done
test_all_modes get_reachable_subset
'
+test_expect_success 'for-each-ref ahead-behind:linear' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-1-3
+ refs/heads/commit-1-5
+ refs/heads/commit-1-8
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1 0 8
+ refs/heads/commit-1-3 0 6
+ refs/heads/commit-1-5 0 4
+ refs/heads/commit-1-8 0 1
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-1-9)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:all' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-2-4
+ refs/heads/commit-4-2
+ refs/heads/commit-4-4
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1 0 24
+ refs/heads/commit-2-4 0 17
+ refs/heads/commit-4-2 0 17
+ refs/heads/commit-4-4 0 9
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-5-5)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:some' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-5-3
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1 0 53
+ refs/heads/commit-4-8 8 30
+ refs/heads/commit-5-3 0 39
+ refs/heads/commit-9-9 27 0
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-9-6)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:some, multibase' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-5-3
+ refs/heads/commit-7-8
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1 0 53 0 53
+ refs/heads/commit-4-8 8 30 0 22
+ refs/heads/commit-5-3 0 39 0 39
+ refs/heads/commit-7-8 14 12 8 6
+ refs/heads/commit-9-9 27 0 27 0
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-9-6) %(ahead-behind:commit-6-9)" \
+ --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:none' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-7-5
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-4-8 16 16
+ refs/heads/commit-7-5 7 4
+ refs/heads/commit-9-9 49 0
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-8-4)" --stdin
+'
+
+test_expect_success 'for-each-ref merged:linear' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-1-3
+ refs/heads/commit-1-5
+ refs/heads/commit-1-8
+ refs/heads/commit-2-1
+ refs/heads/commit-5-1
+ refs/heads/commit-9-1
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-1-3
+ refs/heads/commit-1-5
+ refs/heads/commit-1-8
+ EOF
+ run_all_modes git for-each-ref --merged=commit-1-9 \
+ --format="%(refname)" --stdin
+'
+
+test_expect_success 'for-each-ref merged:all' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-2-4
+ refs/heads/commit-4-2
+ refs/heads/commit-4-4
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-2-4
+ refs/heads/commit-4-2
+ refs/heads/commit-4-4
+ EOF
+ run_all_modes git for-each-ref --merged=commit-5-5 \
+ --format="%(refname)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:some' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-5-3
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-5-3
+ EOF
+ run_all_modes git for-each-ref --merged=commit-9-6 \
+ --format="%(refname)" --stdin
+'
+
+test_expect_success 'for-each-ref merged:some, multibase' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-5-3
+ refs/heads/commit-7-8
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-4-8
+ refs/heads/commit-5-3
+ EOF
+ run_all_modes git for-each-ref \
+ --merged=commit-5-8 \
+ --merged=commit-8-5 \
+ --format="%(refname)" \
+ --stdin
+'
+
+test_expect_success 'for-each-ref merged:none' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-7-5
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ >expect &&
+ run_all_modes git for-each-ref --merged=commit-8-4 \
+ --format="%(refname)" --stdin
+'
+
test_done
test_cmp expect actual
'
+# Run this before doing any signing, so the test has the same results
+# regardless of the GPG prereq.
+test_expect_success 'git tag --format with ahead-behind' '
+ test_when_finished git reset --hard tag-one-line &&
+ git commit --allow-empty -m "left" &&
+ git tag -a -m left tag-left &&
+ git reset --hard HEAD~1 &&
+ git commit --allow-empty -m "right" &&
+ git tag -a -m left tag-right &&
+
+ # Use " !" at the end to demonstrate whitespace
+ # around empty ahead-behind token for tag-blob.
+ cat >expect <<-EOF &&
+ refs/tags/tag-blob !
+ refs/tags/tag-left 1 1 !
+ refs/tags/tag-lines 0 1 !
+ refs/tags/tag-one-line 0 1 !
+ refs/tags/tag-right 0 0 !
+ refs/tags/tag-zero-lines 0 1 !
+ EOF
+ git tag -l --format="%(refname) %(ahead-behind:HEAD) !" >actual 2>err &&
+ grep "refs/tags/tag" actual >actual.focus &&
+ test_cmp expect actual.focus &&
+
+ # Error reported for tags that point to non-commits.
+ grep "error: object [0-9a-f]* is a blob, not a commit" err
+'
+
# trying to verify annotated non-signed tags:
test_expect_success GPG \
grep -E "^event: nfd/d_${utf8_nfc}/?$" ./unicode.trace
'
+test_expect_success 'split-index and FSMonitor work well together' '
+ git init split-index &&
+ test_when_finished "git -C \"$PWD/split-index\" \
+ fsmonitor--daemon stop" &&
+ (
+ cd split-index &&
+ git config core.splitIndex true &&
+ # force split-index in most cases
+ git config splitIndex.maxPercentChange 99 &&
+ git config core.fsmonitor true &&
+
+ # Create the following commit topology:
+ #
+ # * merge three
+ # |\
+ # | * three
+ # * | merge two
+ # |\|
+ # | * two
+ # * | one
+ # |/
+ # * 5a5efd7 initial
+
+ test_commit initial &&
+ test_commit two &&
+ test_commit three &&
+ git reset --hard initial &&
+ test_commit one &&
+ test_tick &&
+ git merge two &&
+ test_tick &&
+ git merge three &&
+
+ git rebase --force-rebase -r one
+ )
+'
+
test_done
test_expect_success 'export anonymized stream' '
git fast-export --anonymize --all \
--anonymize-map=retain-me \
+ --anonymize-map=xyzzy:should-not-appear \
--anonymize-map=xyzzy:custom-name \
--anonymize-map=other \
>stream
test_expect_success 'stream contains user-specified names' '
grep retain-me stream &&
+ ! grep should-not-appear stream &&
grep custom-name stream
'
}
static int fetch_refs_from_bundle(struct transport *transport,
- int nr_heads, struct ref **to_fetch)
+ int nr_heads UNUSED,
+ struct ref **to_fetch UNUSED)
{
struct bundle_transport_data *data = transport->data;
struct strvec extra_index_pack_args = STRVEC_INIT;
{ 0x0E47, 0x0E4E },
{ 0x0EB1, 0x0EB1 },
{ 0x0EB4, 0x0EBC },
-{ 0x0EC8, 0x0ECD },
+{ 0x0EC8, 0x0ECE },
{ 0x0F18, 0x0F19 },
{ 0x0F35, 0x0F35 },
{ 0x0F37, 0x0F37 },
{ 0x10AE5, 0x10AE6 },
{ 0x10D24, 0x10D27 },
{ 0x10EAB, 0x10EAC },
+{ 0x10EFD, 0x10EFF },
{ 0x10F46, 0x10F50 },
{ 0x10F82, 0x10F85 },
{ 0x11001, 0x11001 },
{ 0x11234, 0x11234 },
{ 0x11236, 0x11237 },
{ 0x1123E, 0x1123E },
+{ 0x11241, 0x11241 },
{ 0x112DF, 0x112DF },
{ 0x112E3, 0x112EA },
{ 0x11300, 0x11301 },
{ 0x11D95, 0x11D95 },
{ 0x11D97, 0x11D97 },
{ 0x11EF3, 0x11EF4 },
-{ 0x13430, 0x13438 },
+{ 0x11F00, 0x11F01 },
+{ 0x11F36, 0x11F3A },
+{ 0x11F40, 0x11F40 },
+{ 0x11F42, 0x11F42 },
+{ 0x13430, 0x13440 },
+{ 0x13447, 0x13455 },
{ 0x16AF0, 0x16AF4 },
{ 0x16B30, 0x16B36 },
{ 0x16F4F, 0x16F4F },
{ 0x1E01B, 0x1E021 },
{ 0x1E023, 0x1E024 },
{ 0x1E026, 0x1E02A },
+{ 0x1E08F, 0x1E08F },
{ 0x1E130, 0x1E136 },
{ 0x1E2AE, 0x1E2AE },
{ 0x1E2EC, 0x1E2EF },
+{ 0x1E4EC, 0x1E4EF },
{ 0x1E8D0, 0x1E8D6 },
{ 0x1E944, 0x1E94A },
{ 0xE0001, 0xE0001 },
{ 0x1AFF5, 0x1AFFB },
{ 0x1AFFD, 0x1AFFE },
{ 0x1B000, 0x1B122 },
+{ 0x1B132, 0x1B132 },
{ 0x1B150, 0x1B152 },
+{ 0x1B155, 0x1B155 },
{ 0x1B164, 0x1B167 },
{ 0x1B170, 0x1B2FB },
{ 0x1F004, 0x1F004 },
{ 0x1F6CC, 0x1F6CC },
{ 0x1F6D0, 0x1F6D2 },
{ 0x1F6D5, 0x1F6D7 },
-{ 0x1F6DD, 0x1F6DF },
+{ 0x1F6DC, 0x1F6DF },
{ 0x1F6EB, 0x1F6EC },
{ 0x1F6F4, 0x1F6FC },
{ 0x1F7E0, 0x1F7EB },
{ 0x1F90C, 0x1F93A },
{ 0x1F93C, 0x1F945 },
{ 0x1F947, 0x1F9FF },
-{ 0x1FA70, 0x1FA74 },
-{ 0x1FA78, 0x1FA7C },
-{ 0x1FA80, 0x1FA86 },
-{ 0x1FA90, 0x1FAAC },
-{ 0x1FAB0, 0x1FABA },
-{ 0x1FAC0, 0x1FAC5 },
-{ 0x1FAD0, 0x1FAD9 },
-{ 0x1FAE0, 0x1FAE7 },
-{ 0x1FAF0, 0x1FAF6 },
+{ 0x1FA70, 0x1FA7C },
+{ 0x1FA80, 0x1FA88 },
+{ 0x1FA90, 0x1FABD },
+{ 0x1FABF, 0x1FAC5 },
+{ 0x1FACE, 0x1FADB },
+{ 0x1FAE0, 0x1FAE8 },
+{ 0x1FAF0, 0x1FAF8 },
{ 0x20000, 0x2FFFD },
{ 0x30000, 0x3FFFD }
};
* avoid having to create a new one.
*/
o->internal.result.split_index = o->src_index->split_index;
+ if (o->src_index->cache_changed & SPLIT_INDEX_ORDERED)
+ o->internal.result.cache_changed |= SPLIT_INDEX_ORDERED;
o->internal.result.split_index->refcount++;
} else {
o->internal.result.split_index =
typedef unsigned char uchar;
+/* Internal return values */
+#define WM_ABORT_ALL -1
+#define WM_ABORT_TO_STARSTAR -2
+
/* What character marks an inverted character class? */
#define NEGATE_CLASS '!'
#define NEGATE_CLASS2 '^'
continue;
case '*':
if (*++p == '*') {
- const uchar *prev_p = p - 2;
+ const uchar *prev_p = p;
while (*++p == '*') {}
if (!(flags & WM_PATHNAME))
/* without WM_PATHNAME, '*' == '**' */
match_slash = 1;
- else if ((prev_p < pattern || *prev_p == '/') &&
+ else if ((prev_p - pattern < 2 || *(prev_p - 2) == '/') &&
(*p == '\0' || *p == '/' ||
(p[0] == '\\' && p[1] == '/'))) {
/*
* only if there are no more slash characters. */
if (!match_slash) {
if (strchr((char *)text, '/'))
- return WM_NOMATCH;
+ return WM_ABORT_TO_STARSTAR;
}
return WM_MATCH;
} else if (!match_slash && *p == '/') {
*/
const char *slash = strchr((char*)text, '/');
if (!slash)
- return WM_NOMATCH;
+ return WM_ABORT_ALL;
text = (const uchar*)slash;
/* the slash is consumed by the top-level for loop */
break;
break;
text++;
}
- if (t_ch != p_ch)
- return WM_NOMATCH;
+ if (t_ch != p_ch) {
+ if (match_slash)
+ return WM_ABORT_ALL;
+ else
+ return WM_ABORT_TO_STARSTAR;
+ }
}
if ((matched = dowild(p, text, flags)) != WM_NOMATCH) {
if (!match_slash || matched != WM_ABORT_TO_STARSTAR)
/* Match the "pattern" against the "text" string. */
int wildmatch(const char *pattern, const char *text, unsigned int flags)
{
- return dowild((const uchar*)pattern, (const uchar*)text, flags);
+ int res = dowild((const uchar*)pattern, (const uchar*)text, flags);
+ return res == WM_MATCH ? WM_MATCH : WM_NOMATCH;
}
#define WM_NOMATCH 1
#define WM_MATCH 0
-#define WM_ABORT_ALL -1
-#define WM_ABORT_TO_STARSTAR -2
int wildmatch(const char *pattern, const char *text, unsigned int flags);
#endif