]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'jk/unused-post-2.40-part2'
authorJunio C Hamano <gitster@pobox.com>
Thu, 6 Apr 2023 20:38:28 +0000 (13:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 6 Apr 2023 20:38:28 +0000 (13:38 -0700)
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

77 files changed:
Documentation/RelNotes/2.41.0.txt
Documentation/blame-options.txt
Documentation/config/rebase.txt
Documentation/git-am.txt
Documentation/git-blame.txt
Documentation/git-for-each-ref.txt
Documentation/git-pack-redundant.txt
Documentation/git-rebase.txt
Documentation/rev-list-options.txt
blame.c
builtin/branch.c
builtin/fast-export.c
builtin/for-each-ref.c
builtin/ls-remote.c
builtin/pack-objects.c
builtin/pack-redundant.c
builtin/rebase.c
builtin/receive-pack.c
builtin/reflog.c
builtin/send-pack.c
builtin/tag.c
commit-graph.c
commit-graph.h
commit-reach.c
commit-reach.h
config.c
diagnose.c
diagnose.h
fsmonitor.h
grep.c
grep.h
hashmap.h
http-push.c
http-walker.c
list-objects-filter-options.c
list-objects-filter-options.h
mailmap.c
parse-options.h
read-cache.c
ref-filter.c
ref-filter.h
remote.c
remote.h
revision.c
revision.h
send-pack.c
sequencer.c
t/annotate-tests.sh
t/perf/p1500-graph-walks.sh [new file with mode: 0755]
t/perf/p2000-sparse-operations.sh
t/t1005-read-tree-reset.sh
t/t1006-cat-file.sh
t/t1010-mktree.sh
t/t1302-repo-version.sh
t/t1400-update-ref.sh
t/t1404-update-ref-errors.sh
t/t1507-rev-parse-upstream.sh
t/t3070-wildmatch.sh
t/t3203-branch-output.sh
t/t3422-rebase-incompatible-options.sh
t/t3430-rebase-merges.sh
t/t3701-add-interactive.sh
t/t5000-tar-tree.sh
t/t5318-commit-graph.sh
t/t5328-commit-graph-64bit-time.sh
t/t5563-simple-http-auth.sh
t/t6300-for-each-ref.sh
t/t6301-for-each-ref-errors.sh
t/t6600-test-reach.sh
t/t7004-tag.sh
t/t7527-builtin-fsmonitor.sh
t/t9351-fast-export-anonymize.sh
transport.c
unicode-width.h
unpack-trees.c
wildmatch.c
wildmatch.h

index 8ba6951338a886e9bab236c7957770f64b7a6a6c..1e9b6a66e848a68acdc00c442bda36ef36d1d485 100644 (file)
@@ -38,6 +38,13 @@ UI, Workflows & Features
  * 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.
 
@@ -56,6 +63,9 @@ 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
 -----------------
@@ -123,6 +133,37 @@ 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).
index 9a663535f443c0f46a0a244c4dc454acdf5ffdef..95599bd6e5f48f4ead1f04bcdb0b733b74a16316 100644 (file)
@@ -64,11 +64,11 @@ include::line-range-format.txt[]
        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
index f19bd0e04079051d6be21ee461d1a98c9a632629..afaf6dad99b5542ab06c31e4276a695128ee6b8a 100644 (file)
@@ -67,3 +67,13 @@ rebase.rescheduleFailedExec::
 
 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.
index 0c1dfb3c98b440f87e561a0d44c240a41d80dc14..900be198b14e933d19f87febd9860cb0c67a6bf0 100644 (file)
@@ -24,7 +24,9 @@ DESCRIPTION
 -----------
 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
 -------
@@ -273,7 +275,8 @@ include::config/am.txt[]
 
 SEE ALSO
 --------
-linkgit:git-apply[1].
+linkgit:git-apply[1],
+linkgit:git-format-patch[1].
 
 GIT
 ---
index 4400a17330b4204227050b76d380bc84b017628a..f69a871a96f7589015c723ccdfdd95c6c1d610ed 100644 (file)
@@ -12,7 +12,7 @@ SYNOPSIS
            [-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
 -----------
index 6da899c62964275a97854c0b338f83f51db4bdfe..0713e49b4992e270455a02f5fd3cb93089dc0809 100644 (file)
@@ -9,7 +9,8 @@ SYNOPSIS
 --------
 [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>]]
@@ -32,6 +33,10 @@ OPTIONS
        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
@@ -217,6 +222,11 @@ worktreepath::
        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.
index 99ef13839d49cdaa93c1b5a4b523ace18ce454db..13c3eb5ec96c94970171c96f0c503cba418ab1d8 100644 (file)
@@ -11,6 +11,20 @@ SYNOPSIS
 [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
index 9a295bcee45f4f817c5b319789a32beb22e5f07c..e7b39ad244a4bebc90e9c1974e4239ae771c56d4 100644 (file)
@@ -529,20 +529,25 @@ See also INCOMPATIBLE OPTIONS below.
 
 -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
index 90c73d6708b1d7dc90f50380c20521a1f713ff90..3000888a90852f6bd95eee5b40dc2f184ddd3dc9 100644 (file)
@@ -890,7 +890,7 @@ ifdef::git-rev-list[]
        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
@@ -920,7 +920,12 @@ ifdef::git-rev-list[]
 
 --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
diff --git a/blame.c b/blame.c
index e45d8a3bf92a4a3aac39ed41226a01b870254a15..63ce7eb6301a92c785fdfebf16d5452be437a623 100644 (file)
--- a/blame.c
+++ b/blame.c
@@ -177,12 +177,12 @@ static void set_commit_buffer_from_strbuf(struct repository *r,
 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;
@@ -198,10 +198,7 @@ static struct commit *fake_working_tree_commit(struct repository *r,
        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);
 
@@ -2772,22 +2769,37 @@ void setup_scoreboard(struct blame_scoreboard *sb,
                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), ":");
        }
 
index f63fd45edb96b513c24e1a9cd7c8a55e5a5f7b13..0554d7cebb335eb97f375fe5b4d0772fd9715e62 100644 (file)
@@ -448,6 +448,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
        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++) {
index 78493c6d2bf877e6f47b6ad0b80f11b40f36da7b..f3cc548686180fb12faee8cd717e99eb2e2d61b7 100644 (file)
@@ -110,7 +110,7 @@ static struct decoration idnums;
 static uint32_t last_idnum;
 struct anonymized_entry {
        struct hashmap_entry hash;
-       const char *anon;
+       char *anon;
        const char orig[FLEX_ARRAY];
 };
 
@@ -139,43 +139,56 @@ static int anonymized_entry_cmp(const void *cmp_data UNUSED,
        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;
 }
@@ -188,12 +201,12 @@ static const char *anonymize_str(struct hashmap *map,
  */
 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)
@@ -368,7 +381,7 @@ static void print_path_1(const char *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;
@@ -390,7 +403,7 @@ static void print_path(const char *path)
        }
 }
 
-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;
@@ -406,7 +419,7 @@ static const char *anonymize_oid(const char *oid_hex)
 {
        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,
@@ -503,7 +516,7 @@ static const char *find_encoding(const char *begin, const char *end)
        return bol;
 }
 
-static char *anonymize_ref_component(void *data)
+static char *anonymize_ref_component(void)
 {
        static int counter;
        struct strbuf out = STRBUF_INIT;
@@ -543,13 +556,13 @@ static const char *anonymize_refname(const char *refname)
  * 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;
@@ -592,7 +605,7 @@ static void anonymize_ident_line(const char **beg, const char **end)
 
                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);
@@ -670,7 +683,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
 
        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:
@@ -733,7 +746,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
        show_progress();
 }
 
-static char *anonymize_tag(void *data)
+static char *anonymize_tag(void)
 {
        static int counter;
        struct strbuf out = STRBUF_INIT;
@@ -795,7 +808,7 @@ static void handle_tag(const char *name, struct tag *tag)
                if (message) {
                        static struct hashmap tags;
                        message = anonymize_str(&tags, anonymize_tag,
-                                               message, message_size, NULL);
+                                               message, message_size);
                        message_size = strlen(message);
                }
        }
@@ -1126,11 +1139,6 @@ static void handle_deletes(void)
        }
 }
 
-static char *anonymize_seed(void *data)
-{
-       return xstrdup(data);
-}
-
 static int parse_opt_anonymize_map(const struct option *opt,
                                   const char *arg, int unset)
 {
@@ -1152,7 +1160,8 @@ static int parse_opt_anonymize_map(const struct option *opt,
        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;
 }
index 6f62f40d1263f44f8977125d9d54eca76f101685..6b3d07ef40965b41b66b90dd4566d09717247ee9 100644 (file)
@@ -5,6 +5,8 @@
 #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>]"),
@@ -25,6 +27,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
        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,
@@ -49,6 +53,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
                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(),
        };
 
@@ -75,9 +80,27 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
        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)
@@ -97,5 +120,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
        free_commit_list(filter.with_commit);
        free_commit_list(filter.no_commit);
        ref_sorting_release(sorting);
+       strvec_clear(&vec);
        return 0;
 }
index 2dfbd8ed9b9b2f49a705b106f6a044493ea662cc..a9de0575ce5685329385658711fa84cf9d4d1984 100644 (file)
@@ -5,6 +5,7 @@
 #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"
index 545b8bddc8e6e6209ec373fdfde2b18f781c6e62..7e9e20172a22abeb800a62ac4c8527038832a74c 100644 (file)
@@ -39,6 +39,7 @@
 #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.
index 104d10877b4f0210b6fcee995704e04105f4dcea..d50c2d6693a8617497ef4fc4f1f0c912f8a192bd 100644 (file)
@@ -604,6 +604,7 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix UNUSED)
                        "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)
index 5b7b908b66b446bc65259c812184329ad95f22fa..beebeb8f52f9815bd9951d122cc55460f82756c8 100644 (file)
@@ -124,6 +124,7 @@ struct rebase_options {
        int fork_point;
        int update_refs;
        int config_autosquash;
+       int config_rebase_merges;
        int config_update_refs;
 };
 
@@ -141,6 +142,8 @@ struct rebase_options {
                .allow_empty_message = 1,               \
                .autosquash = -1,                       \
                .config_autosquash = -1,                \
+               .rebase_merges = -1,                    \
+               .config_rebase_merges = -1,             \
                .update_refs = -1,                      \
                .config_update_refs = -1,               \
        }
@@ -772,6 +775,16 @@ static int run_specific_rebase(struct rebase_options *opts)
        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;
@@ -801,6 +814,17 @@ static int rebase_config(const char *var, const char *value, void *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;
@@ -981,6 +1005,28 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
        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);
@@ -1036,7 +1082,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        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;
@@ -1138,10 +1183,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                           &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,
@@ -1437,17 +1481,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        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,
@@ -1515,6 +1548,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                                          "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
@@ -1527,6 +1562,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        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 :
index cccdb73807c9cba721dd7e70c5c674d6dd86f882..554cda269d0110340097c5b09d4f6651bd093c44 100644 (file)
@@ -31,6 +31,7 @@
 #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>"),
index 270681dcdf40e3c7ff0bf2969340eb1b981cf8e8..9b000bb6bcaf0fe8f243e01b236bdeb341b1ca6e 100644 (file)
@@ -4,6 +4,7 @@
 #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>]")
index 91c03df3f3828ee86588c84fafcaa5f8057f719f..640125fe955de2deff642b2d40a29a69263ff27b 100644 (file)
@@ -16,6 +16,7 @@
 #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"
index adcaa547b0d57094fa811f5f81e3a601cc05e2b2..7e2f686600ab0e49437fd20c1963fe709977139b 100644 (file)
@@ -67,6 +67,7 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
                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++) {
index 5e6098ff356b937f7790e2c8dfe5634ebc5f56be..8fce79b39b7286adf8ab54416d978e9e3df69cd5 100644 (file)
@@ -117,12 +117,10 @@ timestamp_t commit_graph_generation(const struct commit *c)
        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)
@@ -1447,24 +1445,52 @@ static void close_reachable(struct write_commit_graph_context *ctx)
        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);
@@ -1472,41 +1498,91 @@ static void compute_topological_levels(struct write_commit_graph_context *ctx)
                        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);
 
@@ -1518,47 +1594,7 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
                }
        }
 
-       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];
@@ -1569,6 +1605,35 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
        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",
index bb88bec7aa3d8f0d56b262b21b3a2ea47b969b52..83aaa1dbb929c4f16acbe8ccb2593d104af2b7f0 100644 (file)
@@ -189,4 +189,12 @@ struct commit_graph_data {
  */
 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
index 7c0c39fd2864c2db72b81adc4c0b1b6d7cf0337e..d28cc96eb04922246057998ecc1ba015dfc69520 100644 (file)
@@ -10,6 +10,7 @@
 #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)
@@ -947,3 +948,218 @@ struct commit_list *get_reachable_subset(struct commit **from, int nr_from,
 
        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);
+}
index 148b56fea50629607d2f0dd196cbd4dec08fdcfd..d6321ae700e740b87e0bf70f894ee068bd089777 100644 (file)
@@ -104,4 +104,44 @@ struct commit_list *get_reachable_subset(struct commit **from, int nr_from,
                                         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
index d0aff55fa66b7f57f29a958092ce898034547ec3..0b2679994e1a387241edbc3db710ea383a6dc1db 100644 (file)
--- a/config.c
+++ b/config.c
@@ -1686,7 +1686,7 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
                        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;
        }
 
index 5b398f0cffcf776af4cc424f863ba01db83d7de1..998f517c48869836c4c634ef9f0c129cbe2d274a 100644 (file)
@@ -8,6 +8,7 @@
 #include "strvec.h"
 #include "object-store.h"
 #include "packfile.h"
+#include "parse-options.h"
 
 struct archive_dir {
        const char *path;
index 7a4951a7863bcea8ac37e2d40d656b794f9bc925..f525219ab0cf9b36249892c847e731b952b081af 100644 (file)
@@ -2,7 +2,8 @@
 #define DIAGNOSE_H
 
 #include "strbuf.h"
-#include "parse-options.h"
+
+struct option;
 
 enum diagnose_mode {
        DIAGNOSE_NONE,
index edf7ce5203b1a850e26c4b81cdb565350cd7f748..778707b131ba01f8a800ab4f3253b1b841709d1d 100644 (file)
@@ -86,7 +86,7 @@ static inline void mark_fsmonitor_valid(struct index_state *istate, struct cache
            !(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);
        }
diff --git a/grep.c b/grep.c
index 68e9328dfd49708e871cabb878801d9e0fffaa6b..25dc2ec78841b9e97103c267f37db665bb4d4149 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -321,6 +321,15 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt
        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))
diff --git a/grep.h b/grep.h
index 6075f997e68f5594f771123ae62655181278d38e..c59592e3bdba5205befb38b5a19881ba3c6c9ce2 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -7,6 +7,9 @@
 #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
index 7251687d730d608437a8ed24e4551cf9344fd7f4..e2cf9c78d84ff3046844af498b9539d1edb83445 100644 (file)
--- a/hashmap.h
+++ b/hashmap.h
@@ -270,7 +270,7 @@ void hashmap_clear_(struct hashmap *map, ssize_t offset);
 #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
index 88aa045ecba453eaab7f56c02c54a972ab07fd7c..2d01b430e74a457b458f13e78f8d5a07c8aa38ae 100644 (file)
@@ -602,7 +602,7 @@ static void finish_request(struct transfer_request *request)
 }
 
 static int is_running_queue;
-static int fill_active_slot(void *unused)
+static int fill_active_slot(void *data UNUSED)
 {
        struct transfer_request *request;
 
index c3e902c40e654d86b8e2cefe2d8ccd6a475d3e5e..93a4a98a3d3264cd04a5f3f5bf06b1fa503ff896 100644 (file)
@@ -53,8 +53,7 @@ static void fetch_alternates(struct walker *walker, const char *base);
 
 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;
@@ -111,7 +110,7 @@ static void process_object_response(void *callback_data)
                        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;
                }
        }
@@ -128,7 +127,7 @@ static void release_object_request(struct object_request *obj_req)
        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;
@@ -139,7 +138,7 @@ static int fill_active_slot(struct walker *walker)
                        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;
                        }
                }
@@ -614,7 +613,7 @@ struct walker *get_http_walker(const char *url)
        walker->cleanup = cleanup;
        walker->data = data;
 
-       add_fill_function(walker, (int (*)(void *)) fill_active_slot);
+       add_fill_function(NULL, fill_active_slot);
 
        return walker;
 }
index 1d25a5737db24d8eb2e0fb918e0a0d0347ba8ca6..72aeb089cc94633f5e7af5917d9398888d143ff9 100644 (file)
@@ -11,6 +11,7 @@
 #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,
index ef03b45132e87ae1f1951e86c5d38084c4467b76..727c986122fc710b72bc827f1a18cb05b2fb0622 100644 (file)
@@ -2,10 +2,11 @@
 #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.
  */
index da2589b08229813963347115a001d8ce6f45ae32..22e90129ea816e557cc70ba629e0e3c77c6981d2 100644 (file)
--- a/mailmap.c
+++ b/mailmap.c
@@ -3,16 +3,6 @@
 #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;
 
@@ -30,23 +20,17 @@ struct mailmap_entry {
        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);
@@ -93,8 +77,6 @@ static void add_mapping(struct string_list *map,
        }
 
        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);
@@ -106,15 +88,10 @@ static void add_mapping(struct string_list *map,
                }
        } 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,
@@ -250,11 +227,8 @@ int read_mailmap(struct string_list *map)
 
 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");
 }
 
 /*
@@ -315,10 +289,6 @@ int map_user(struct string_list *map,
        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;
@@ -336,10 +306,8 @@ int map_user(struct string_list *map,
        }
        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);
@@ -348,11 +316,7 @@ int map_user(struct string_list *map,
                                *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;
 }
index 60b0a758d6b59086f6d52520461a44a2a8516983..fcb58c4043f3e1248c6e253ea0a2d33731f9f742 100644 (file)
@@ -158,71 +158,202 @@ struct option {
        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)
 
 /*
@@ -355,34 +486,80 @@ int parse_opt_tracking_mode(const struct option *, const char *, int);
 
 #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)
index 1bcf6732718770923f62046a31563c770c0ad706..12564bbd90ce6f3512d0e574b5cbc6550e99fb74 100644 (file)
@@ -2903,6 +2903,16 @@ static int record_ieot(void)
        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
@@ -2911,7 +2921,7 @@ static int record_ieot(void)
  * 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;
@@ -3084,8 +3094,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                        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)
@@ -3099,7 +3109,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                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);
@@ -3109,7 +3120,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                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);
@@ -3120,7 +3132,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                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);
@@ -3131,7 +3144,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                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);
@@ -3205,8 +3219,10 @@ static int commit_locked_index(struct lock_file *lk)
                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;
@@ -3224,7 +3240,7 @@ static int do_write_locked_index(struct index_state *istate, struct lock_file *l
         */
        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));
 
@@ -3253,7 +3269,7 @@ static int write_split_index(struct index_state *istate,
 {
        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;
 }
@@ -3328,7 +3344,7 @@ static int write_shared_index(struct index_state *istate,
 
        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));
 
@@ -3405,9 +3421,8 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
        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;
        }
 
@@ -3433,8 +3448,8 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
                /* 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);
index ed802778da7208377d9054f2de1805f91be39509..25831e3336470a73656135c31b0a8100fc6971a8 100644 (file)
@@ -158,6 +158,7 @@ enum atom_type {
        ATOM_THEN,
        ATOM_ELSE,
        ATOM_REST,
+       ATOM_AHEADBEHIND,
 };
 
 /*
@@ -600,6 +601,22 @@ static int rest_atom_parser(struct ref_format *format,
        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)
@@ -660,6 +677,7 @@ static struct {
        [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
@@ -1866,6 +1884,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
        struct object *obj;
        int i;
        struct object_info empty = OBJECT_INFO_INIT;
+       int ahead_behind_atoms = 0;
 
        CALLOC_ARRAY(ref->value, used_atom_cnt);
 
@@ -1996,6 +2015,16 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
                        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;
 
@@ -2346,6 +2375,7 @@ static void free_array_item(struct ref_array_item *item)
                        free((char *)item->value[i].s);
                free(item->value);
        }
+       free(item->counts);
        free(item);
 }
 
@@ -2374,6 +2404,8 @@ void ref_array_clear(struct ref_array *array)
                free_worktrees(ref_to_worktree_map.worktrees);
                ref_to_worktree_map.worktrees = NULL;
        }
+
+       FREE_AND_NULL(array->counts);
 }
 
 #define EXCLUDE_REACHED 0
@@ -2382,33 +2414,22 @@ static void reach_filter(struct ref_array *array,
                         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;
@@ -2432,10 +2453,50 @@ static void reach_filter(struct ref_array *array,
                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
index aa0eea4ecf591edd0620dc47db0e0bfc3e5c4208..ae51ace3a2099427daee953a1b778542d94361ec 100644 (file)
@@ -4,7 +4,7 @@
 #include "oid-array.h"
 #include "refs.h"
 #include "commit.h"
-#include "parse-options.h"
+#include "string-list.h"
 
 /* Quoting styles */
 #define QUOTE_NONE 0
@@ -24,6 +24,8 @@
 
 struct atom_value;
 struct ref_sorting;
+struct ahead_behind_count;
+struct option;
 
 enum ref_sorting_order {
        REF_SORTING_REVERSE = 1<<0,
@@ -40,6 +42,8 @@ struct ref_array_item {
        const char *symref;
        struct commit *commit;
        struct atom_value *value;
+       struct ahead_behind_count **counts;
+
        char refname[FLEX_ARRAY];
 };
 
@@ -47,6 +51,9 @@ struct ref_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 {
@@ -80,9 +87,15 @@ struct ref_format {
 
        /* 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) \
@@ -143,4 +156,15 @@ struct ref_array_item *ref_array_push(struct ref_array *array,
                                      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  */
index b04e5da33831a75b59dfd81383b03825370d29ac..641b083d90d6a4cf56886dd188273204fc11bf47 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -17,6 +17,7 @@
 #include "commit-reach.h"
 #include "advice.h"
 #include "connect.h"
+#include "parse-options.h"
 
 enum map_direction { FROM_SRC, FROM_DST };
 
index 5b38ee20b843bbb90655677c28132a9523411bd0..73638cefeb102d6dd9a6777645156a2034594240 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -1,10 +1,10 @@
 #ifndef REMOTE_H
 #define REMOTE_H
 
-#include "parse-options.h"
 #include "hashmap.h"
 #include "refspec.h"
 
+struct option;
 struct transport_ls_refs_options;
 
 /**
index e4c066e90b7b40c223a7292a381c2d4b2a0d200c..8b1ecf07fcc6969aa571b8e5a570c9f08c6774f8 100644 (file)
@@ -37,6 +37,7 @@
 #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;
 
index ab71443696d527d3f4def94ba67aea1c743eb3da..649f817f399f4f8fd58eebc728c13dd2fdd8a4e3 100644 (file)
@@ -2,7 +2,6 @@
 #define REVISION_H
 
 #include "commit.h"
-#include "parse-options.h"
 #include "grep.h"
 #include "notes.h"
 #include "pretty.h"
@@ -62,6 +61,8 @@ struct string_list;
 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 {
index 423a5cfe22d140026474695340488ae1423a6317..0d0519116260cb851bb50df0f375b0c5e8e6dc64 100644 (file)
@@ -17,6 +17,7 @@
 #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)
index 3be23d7ca20510179664fcc58c4a6856e0cbd776..ca7c228c9f40951b92f027795a5bde7404a386bc 100644 (file)
@@ -3187,25 +3187,7 @@ static int create_seq_dir(struct repository *r)
 
 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)
@@ -3676,7 +3658,6 @@ static int safe_append(const char *filename, const char *fmt, ...)
        }
        if (commit_lock_file(&lock) < 0) {
                strbuf_release(&buf);
-               rollback_lock_file(&lock);
                return error(_("failed to finalize '%s'"), filename);
        }
 
index f1b9a6ce4daee67c7aadfd2a9d19cb1c4033224c..b35be20cf327249874a48d91681f381f873ad176 100644 (file)
@@ -72,6 +72,16 @@ test_expect_success 'blame 1 author' '
        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 &&
        (
@@ -98,6 +108,10 @@ test_expect_success 'blame 2 authors' '
        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 &&
diff --git a/t/perf/p1500-graph-walks.sh b/t/perf/p1500-graph-walks.sh
new file mode 100755 (executable)
index 0000000..e14e762
--- /dev/null
@@ -0,0 +1,50 @@
+#!/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
index 3242cfe91a096fef06d11cc589bb1ab415135e96..e4a132f5934488be8530dfdfcef03ea9fbc642d7 100755 (executable)
@@ -124,6 +124,6 @@ test_perf_on_all git read-tree -mu HEAD
 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
index 12e30d77d096d5108396f90ecd4f9088c0f27413..26be4a2b5a007afa7ef1a9bd4438d16173c6aad5 100755 (executable)
@@ -41,7 +41,8 @@ test_expect_success 'reset should remove remnants from a failed merge' '
        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' '
@@ -56,7 +57,8 @@ 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' '
@@ -71,7 +73,8 @@ 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' '
@@ -86,7 +89,8 @@ 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' '
@@ -101,7 +105,8 @@ 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
index 2d875b17d8a94a45b9789e3466139adbd6b4c680..8eac74b59c2faa9c6e9f2396214264861c8416c1 100755 (executable)
@@ -603,7 +603,8 @@ do
                        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" '
index 3c0819452653878ae51779798ce90712dc30a901..22875ba598c2a82bba6c7504c420c690441f7167 100755 (executable)
@@ -60,11 +60,11 @@ test_expect_success 'allow missing object with --missing' '
 '
 
 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
index 70389fa2ebbfa47ad410ca5c30bb294b6cf0753f..179474fa651e159bf68714620ab4f1be2c259fe5 100755 (executable)
@@ -37,7 +37,7 @@ test_expect_success 'gitdir selection on normal repos' '
 
 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' '
index cf58cf025cd2af621f6d58cdaf02d3be0480190c..4d66cd7f4a1fce8ebdaa42a37e7bd8f03853645e 100755 (executable)
@@ -1568,6 +1568,7 @@ test_expect_success 'transaction can create and delete' '
        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
 '
 
@@ -1595,6 +1596,8 @@ test_expect_success 'transaction cannot restart ongoing transaction' '
        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
 '
 
index b5606d93b52bf44366a51f3d4b6dd19e7a8816ab..937ae0d73347c3d0797fd28b5c3bfa83baf55632 100755 (executable)
@@ -551,7 +551,6 @@ test_expect_success REFFILES 'no bogus intermediate values during delete' '
        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 &&
index 549eb315a9037845e1dedea14ccb16b943714649..d94c72c672b05e2fe3fb7f3a99d907b2021795e7 100755 (executable)
@@ -263,7 +263,8 @@ test_expect_success '@{reflog}-parsing does not look beyond colon' '
        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' '
@@ -271,7 +272,8 @@ 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
index 5d871fde960a032bbf5523dc2fee578745d1479d..4dd42df38c25615e0833b754d1a2c3132ef0bf1d 100755 (executable)
@@ -431,4 +431,15 @@ match 1 1 1 1 'a' '[B-a]'
 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
index d34d77f89348d86518375a66e277bb118ff29c22..1c0f7ea24e7573b54d9f1354179a3a3e7ce792a0 100755 (executable)
@@ -337,6 +337,20 @@ test_expect_success 'git branch --format option' '
        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
 '
index 4711b37a28863e50a06e098bcbcd9417951c9b4d..2eba00bdf5898531744c8f77429a8f4d1ad1b91d 100755 (executable)
@@ -85,6 +85,11 @@ test_rebase_am_only () {
                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
@@ -101,6 +106,12 @@ test_rebase_am_only () {
                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 &&
@@ -113,6 +124,12 @@ test_rebase_am_only () {
                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 &&
index fa2a06c19f0ff9890733817d066778c7634ab523..f03599c63b969d6216f7370e1a24b0160bc6ab69 100755 (executable)
@@ -250,6 +250,16 @@ test_expect_success 'with a branch tip that was cherry-picked already' '
        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)" &&
@@ -268,6 +278,40 @@ test_expect_success 'do not rebase cousins unless asked for' '
        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 &&
index b8aaece860d8854e6215cbc1f80e59f4c1d413c6..3982b6b49dc45673bf6dcf1cb984a2ff8fedf252 100755 (executable)
@@ -7,12 +7,6 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 . ./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
index 918a2fc7c69432c26e3135460d62f9b27f0e3dbd..f0bd70dbd6aa7468c7812f36d3af755b91ccb3fb 100755 (executable)
@@ -185,6 +185,7 @@ test_expect_success 'git archive' '
 '
 
 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
@@ -257,14 +258,6 @@ test_expect_success 'git archive --remote with configured remote' '
        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 &&
index 049c5fc8ead328860ef7ed38dea410f4524107c9..b6e12115786fda5b3b89ad63f2761192593c7f1b 100755 (executable)
@@ -630,7 +630,7 @@ test_expect_success 'detect incorrect generation number' '
 
 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' '
index 093f0c067af6801e3334152fe65b7c4495830d30..57e4d9c6998c2b690881984f5809fba6525bd4cc 100755 (executable)
@@ -63,4 +63,13 @@ test_expect_success 'set up and verify repo with generation data overflow chunk'
 
 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
index ccf7e54b073b64c57f40920dade79c8abf13a539..f45a43b4b5e5897145c6f9da1244ee992886b355 100755 (executable)
@@ -252,15 +252,14 @@ test_expect_success 'access using basic auth with wwwauth header empty continuat
 
        # 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" &&
@@ -298,11 +297,10 @@ test_expect_success 'access using basic auth with wwwauth header mixed line-endi
 
        # 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" &&
index c466fd989f168b0fc696e83516474f11040a58be..6614469d2d63eff2a2cc474d5bc311c64104eeda 100755 (executable)
@@ -1464,4 +1464,54 @@ sig_crlf="$(printf "%s" "$sig" | append_cr; echo dummy)"
 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
index bfda1f46ad25f49c7d51ce2e5853b1818314010f..2667dd13fe33893086e2bdf14a94a6f3c8648d84 100755 (executable)
@@ -54,4 +54,18 @@ test_expect_success 'Missing objects are reported correctly' '
        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
index 338a9c46a24b02caf471da4afb1cff14a75b6f2f..b330945f497840bc723c7dba4ea237babf09b0d4 100755 (executable)
@@ -443,4 +443,173 @@ test_expect_success 'get_reachable_subset:none' '
        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
index 9aa1660651b8a96397ce2a83cb3d107c8b7d43dc..04a4b44183d70e4c77d4fe7fae9243fd71b34598 100755 (executable)
@@ -792,6 +792,34 @@ test_expect_success 'annotations for blobs are empty' '
        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 \
index 4c0327b2bb27af53e24b4158a63f6d2aec9ae19e..0c241d6c148d7b5ebc9a9b3aaf44723fb1028e8b 100755 (executable)
@@ -995,4 +995,41 @@ test_expect_success !UNICODE_COMPOSITION_SENSITIVE 'Unicode nfc/nfd' '
        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
index 77047e250dc2c862182619f717b6b96206a38ec7..156a6474847cf6e5bc86df2212ce98211986e6d6 100755 (executable)
@@ -25,6 +25,7 @@ test_expect_success 'setup simple repo' '
 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
@@ -41,6 +42,7 @@ test_expect_success 'stream omits path names' '
 
 test_expect_success 'stream contains user-specified names' '
        grep retain-me stream &&
+       ! grep should-not-appear stream &&
        grep custom-name stream
 '
 
index fa9bc3be0850648b28c4ede889d956326fc9fe45..c5e201f6a3ef71d37d90b557d1ebe5341286b83b 100644 (file)
@@ -170,7 +170,8 @@ static struct ref *get_refs_from_bundle(struct transport *transport,
 }
 
 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;
index 97c851b27df4a7ea2bc33362a9d2f9dce38c0522..e15fb0455bbd9d6b5fa113ec9bb5cea4f6b6d375 100644 (file)
@@ -94,7 +94,7 @@ static const struct interval zero_width[] = {
 { 0x0E47, 0x0E4E },
 { 0x0EB1, 0x0EB1 },
 { 0x0EB4, 0x0EBC },
-{ 0x0EC8, 0x0ECD },
+{ 0x0EC8, 0x0ECE },
 { 0x0F18, 0x0F19 },
 { 0x0F35, 0x0F35 },
 { 0x0F37, 0x0F37 },
@@ -228,6 +228,7 @@ static const struct interval zero_width[] = {
 { 0x10AE5, 0x10AE6 },
 { 0x10D24, 0x10D27 },
 { 0x10EAB, 0x10EAC },
+{ 0x10EFD, 0x10EFF },
 { 0x10F46, 0x10F50 },
 { 0x10F82, 0x10F85 },
 { 0x11001, 0x11001 },
@@ -252,6 +253,7 @@ static const struct interval zero_width[] = {
 { 0x11234, 0x11234 },
 { 0x11236, 0x11237 },
 { 0x1123E, 0x1123E },
+{ 0x11241, 0x11241 },
 { 0x112DF, 0x112DF },
 { 0x112E3, 0x112EA },
 { 0x11300, 0x11301 },
@@ -313,7 +315,12 @@ static const struct interval zero_width[] = {
 { 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 },
@@ -339,9 +346,11 @@ static const struct interval zero_width[] = {
 { 0x1E01B, 0x1E021 },
 { 0x1E023, 0x1E024 },
 { 0x1E026, 0x1E02A },
+{ 0x1E08F, 0x1E08F },
 { 0x1E130, 0x1E136 },
 { 0x1E2AE, 0x1E2AE },
 { 0x1E2EC, 0x1E2EF },
+{ 0x1E4EC, 0x1E4EF },
 { 0x1E8D0, 0x1E8D6 },
 { 0x1E944, 0x1E94A },
 { 0xE0001, 0xE0001 },
@@ -417,7 +426,9 @@ static const struct interval double_width[] = {
 { 0x1AFF5, 0x1AFFB },
 { 0x1AFFD, 0x1AFFE },
 { 0x1B000, 0x1B122 },
+{ 0x1B132, 0x1B132 },
 { 0x1B150, 0x1B152 },
+{ 0x1B155, 0x1B155 },
 { 0x1B164, 0x1B167 },
 { 0x1B170, 0x1B2FB },
 { 0x1F004, 0x1F004 },
@@ -451,7 +462,7 @@ static const struct interval double_width[] = {
 { 0x1F6CC, 0x1F6CC },
 { 0x1F6D0, 0x1F6D2 },
 { 0x1F6D5, 0x1F6D7 },
-{ 0x1F6DD, 0x1F6DF },
+{ 0x1F6DC, 0x1F6DF },
 { 0x1F6EB, 0x1F6EC },
 { 0x1F6F4, 0x1F6FC },
 { 0x1F7E0, 0x1F7EB },
@@ -459,15 +470,13 @@ static const struct interval double_width[] = {
 { 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 }
 };
index a75fb9f05b4775e8135436a8f915b1a310cad5b6..09d96f8ba1ae57183060fff196cb7896f8ec5bbe 100644 (file)
@@ -1926,6 +1926,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                 * 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 =
index 42e38e3459049385ff2d6040fede7c757b0d35bb..8ea29141bd7c5233a25c89aa55ceeafcf7e7529d 100644 (file)
 
 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  '^'
@@ -83,12 +87,12 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
                        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] == '/'))) {
                                        /*
@@ -114,7 +118,7 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
                                 * 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 == '/') {
@@ -125,7 +129,7 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
                                 */
                                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;
@@ -153,8 +157,12 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
                                                        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)
@@ -274,5 +282,6 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
 /* 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;
 }
index 599369629824ec5f2bbb8e8affb19e8cc9058cc3..0c890cb56ba303db40a4c4f3244cc4a672303ac1 100644 (file)
@@ -6,8 +6,6 @@
 
 #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