]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'jk/weather-balloon-require-variadic-macro'
authorJunio C Hamano <gitster@pobox.com>
Sat, 6 Feb 2021 00:40:46 +0000 (16:40 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sat, 6 Feb 2021 00:40:46 +0000 (16:40 -0800)
We've carried compatibility codepaths for compilers without
variadic macros for quite some time, but the world may be ready for
them to be removed.  Force compilation failure on exotic platforms
where variadic macros are not available to find out who screams in
such a way that we can easily revert if it turns out that the world
is not yet ready.

* jk/weather-balloon-require-variadic-macro:
  git-compat-util: always enable variadic macros

53 files changed:
.github/workflows/main.yml
Documentation/RelNotes/2.31.0.txt
Documentation/diff-generate-patch.txt
Documentation/diff-options.txt
Documentation/git-fsck.txt
Documentation/git-log.txt
Documentation/git-ls-files.txt
Documentation/git-show.txt
Documentation/rev-list-options.txt
Documentation/technical/index-format.txt
Documentation/technical/packfile-uri.txt
Makefile
builtin/branch.c
builtin/describe.c
builtin/diff-files.c
builtin/diff.c
builtin/gc.c
builtin/log.c
builtin/ls-files.c
builtin/merge.c
builtin/pack-objects.c
builtin/show-ref.c
builtin/tag.c
cache-tree.c
commit-graph.c
diff-merges.c [new file with mode: 0644]
diff-merges.h [new file with mode: 0644]
fmt-merge-msg.c
log-tree.c
ls-refs.c
merge-ort.c
ref-filter.c
refs.c
refs.h
revision.c
revision.h
run-command.h
t/annotate-tests.sh
t/helper/test-ref-store.c
t/perf/p5303-many-packs.sh
t/t1405-main-ref-store.sh
t/t1406-submodule-ref-store.sh
t/t3012-ls-files-dedup.sh [new file with mode: 0755]
t/t4013-diff-various.sh
t/t4013/diff.log_--cc_-m_-p_master [new file with mode: 0644]
t/t4013/diff.log_--diff-merges=first-parent_master [new file with mode: 0644]
t/t4013/diff.log_-c_-m_-p_master [new file with mode: 0644]
t/t4013/diff.log_-p_--diff-merges=first-parent_master [new file with mode: 0644]
t/t7104-reset-hard.sh
t/t7900-maintenance.sh
tree-walk.c
unpack-trees.c
upload-pack.c

index aef66436484af18fb91504f8c9701de7b25e7059..f6885e88ee6b969177cb130d1e18449d35c201e3 100644 (file)
@@ -123,6 +123,7 @@ jobs:
     runs-on: windows-latest
     needs: [windows-build]
     strategy:
+      fail-fast: false
       matrix:
         nr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
     steps:
@@ -227,6 +228,7 @@ jobs:
     runs-on: windows-latest
     needs: [vs-build, windows-build]
     strategy:
+      fail-fast: false
       matrix:
         nr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
     steps:
@@ -272,6 +274,7 @@ jobs:
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
     strategy:
+      fail-fast: false
       matrix:
         vector:
           - jobname: linux-clang
@@ -309,6 +312,7 @@ jobs:
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
     strategy:
+      fail-fast: false
       matrix:
         vector:
         - jobname: linux-musl
index 7f53db7de9d0dd143495d15be1b7ad403ed31b57..2a722e1e0f18cc7de4368a0187bd62afd27f506c 100644 (file)
@@ -75,6 +75,11 @@ Performance, Internal Implementation, Development Support etc.
  * "git fetch" learns to treat ref updates atomically in all-or-none
    fashion, just like "git push" does, with the new "--atomic" option.
 
+ * The peel_ref() API has been replaced with peel_iterated_oid().
+
+ * The .use_shell flag in struct child_process that is passed to
+   run_command() API has been clarified with a bit more documentation.
+
 
 Fixes since v2.30
 -----------------
@@ -127,6 +132,13 @@ Fixes since v2.30
    does.
    (merge c9e3a4e76d jk/log-cherry-pick-duplicate-patches later to maint).
 
+ * Documentation for "git fsck" lost stale bits that has become
+   incorrect.
+   (merge 28cc00a13d ab/fsck-doc-fix later to maint).
+
+ * Doc fix for packfile URI feature.
+   (merge bfc2a36ff2 jt/packfile-as-uri-doc later to maint).
+
  * Other code cleanup, docfix, build fix, etc.
    (merge 505a276596 pk/subsub-fetch-fix-take-2 later to maint).
    (merge 33fc56253b fc/t6030-bisect-reset-removes-auxiliary-files later to maint).
@@ -146,3 +158,4 @@ Fixes since v2.30
    (merge 4eb56b56e7 bc/doc-status-short later to maint).
    (merge a4a1ca22ef tb/local-clone-race-doc later to maint).
    (merge 6a8c89d053 ma/more-opaque-lock-file later to maint).
+   (merge 4a5ec7d166 js/skip-dashed-built-ins-from-config-mak later to maint).
index b10ff4caa6c52aefc485fec9e71e19c6121946bf..2db8eacc3ec74ba13bd6652867bcee99abf6f31b 100644 (file)
@@ -81,9 +81,9 @@ Combined diff format
 Any diff-generating command can take the `-c` or `--cc` option to
 produce a 'combined diff' when showing a merge. This is the default
 format when showing merges with linkgit:git-diff[1] or
-linkgit:git-show[1]. Note also that you can give the `-m` option to any
-of these commands to force generation of diffs with individual parents
-of a merge.
+linkgit:git-show[1]. Note also that you can give suitable
+`--diff-merges` option to any of these commands to force generation of
+diffs in specific format.
 
 A "combined diff" format looks like this:
 
index 746b144c76ba15f48b58d77ad88b88379f83529e..e5733ccb2d1a7a2e6ad419f20a019f526c5d475a 100644 (file)
@@ -33,6 +33,57 @@ endif::git-diff[]
        show the patch by default, or to cancel the effect of `--patch`.
 endif::git-format-patch[]
 
+ifdef::git-log[]
+--diff-merges=(off|none|first-parent|1|separate|m|combined|c|dense-combined|cc)::
+--no-diff-merges::
+       Specify diff format to be used for merge commits. Default is
+       {diff-merges-default} unless `--first-parent` is in use, in which case
+       `first-parent` is the default.
++
+--diff-merges=(off|none):::
+--no-diff-merges:::
+       Disable output of diffs for merge commits. Useful to override
+       implied value.
++
+--diff-merges=first-parent:::
+--diff-merges=1:::
+       This option makes merge commits show the full diff with
+       respect to the first parent only.
++
+--diff-merges=separate:::
+--diff-merges=m:::
+-m:::
+       This makes merge commits show the full diff with respect to
+       each of the parents. Separate log entry and diff is generated
+       for each parent. `-m` doesn't produce any output without `-p`.
++
+--diff-merges=combined:::
+--diff-merges=c:::
+-c:::
+       With this option, diff output for a merge commit shows the
+       differences from each of the parents to the merge result
+       simultaneously instead of showing pairwise diff between a
+       parent and the result one at a time. Furthermore, it lists
+       only files which were modified from all parents. `-c` implies
+       `-p`.
++
+--diff-merges=dense-combined:::
+--diff-merges=cc:::
+--cc:::
+       With this option the output produced by
+       `--diff-merges=combined` is further compressed by omitting
+       uninteresting hunks whose contents in the parents have only
+       two variants and the merge result picks one of them without
+       modification.  `--cc` implies `-p`.
+
+--combined-all-paths::
+       This flag causes combined diffs (used for merge commits) to
+       list the name of the file from all parents.  It thus only has
+       effect when `--diff-merges=[dense-]combined` is in use, and
+       is likely only useful if filename changes are detected (i.e.
+       when either rename or copy detection have been requested).
+endif::git-log[]
+
 -U<n>::
 --unified=<n>::
        Generate diffs with <n> lines of context instead of
index d72d15be5babb2a108cf37e8ae396ae587baf1cf..bd596619c0d3d8a841bc3e033e9a3ff14c11a514 100644 (file)
@@ -129,14 +129,6 @@ using 'git commit-graph verify'. See linkgit:git-commit-graph[1].
 Extracted Diagnostics
 ---------------------
 
-expect dangling commits - potential heads - due to lack of head information::
-       You haven't specified any nodes as heads so it won't be
-       possible to differentiate between un-parented commits and
-       root nodes.
-
-missing sha1 directory '<dir>'::
-       The directory holding the sha1 objects is missing.
-
 unreachable <type> <object>::
        The <type> object <object>, isn't actually referred to directly
        or indirectly in any of the trees or commits seen. This can
index dd189a353a443e8d1b18ae95ece7d7d24cceee3d..1bbf865a1b2d4f3157395d5f82a6bb47f4029b93 100644 (file)
@@ -107,47 +107,15 @@ DIFF FORMATTING
 By default, `git log` does not generate any diff output. The options
 below can be used to show the changes made by each commit.
 
-Note that unless one of `-c`, `--cc`, or `-m` is given, merge commits
-will never show a diff, even if a diff format like `--patch` is
-selected, nor will they match search options like `-S`. The exception is
-when `--first-parent` is in use, in which merges are treated like normal
-single-parent commits (this can be overridden by providing a
-combined-diff option or with `--no-diff-merges`).
-
--c::
-       With this option, diff output for a merge commit
-       shows the differences from each of the parents to the merge result
-       simultaneously instead of showing pairwise diff between a parent
-       and the result one at a time. Furthermore, it lists only files
-       which were modified from all parents.
-
---cc::
-       This flag implies the `-c` option and further compresses the
-       patch output by omitting uninteresting hunks whose contents in
-       the parents have only two variants and the merge result picks
-       one of them without modification.
-
---combined-all-paths::
-       This flag causes combined diffs (used for merge commits) to
-       list the name of the file from all parents.  It thus only has
-       effect when -c or --cc are specified, and is likely only
-       useful if filename changes are detected (i.e. when either
-       rename or copy detection have been requested).
-
--m::
-       This flag makes the merge commits show the full diff like
-       regular commits; for each merge parent, a separate log entry
-       and diff is generated. An exception is that only diff against
-       the first parent is shown when `--first-parent` option is given;
-       in that case, the output represents the changes the merge
-       brought _into_ the then-current branch.
-
---diff-merges=off::
---no-diff-merges::
-       Disable output of diffs for merge commits (default). Useful to
-       override `-m`, `-c`, or `--cc`.
+Note that unless one of `--diff-merges` variants (including short
+`-m`, `-c`, and `--cc` options) is explicitly given, merge commits
+will not show a diff, even if a diff format like `--patch` is
+selected, nor will they match search options like `-S`. The exception
+is when `--first-parent` is in use, in which case `first-parent` is
+the default format.
 
 :git-log: 1
+:diff-merges-default: `off`
 include::diff-options.txt[]
 
 include::diff-generate-patch.txt[]
index 0a3b5265b3457988bfad25b6b89c7622dd726760..6d11ab506b7c2e0990c93822970cd079e34b6c04 100644 (file)
@@ -13,6 +13,7 @@ SYNOPSIS
                (--[cached|deleted|others|ignored|stage|unmerged|killed|modified])*
                (-[c|d|o|i|s|u|k|m])*
                [--eol]
+               [--deduplicate]
                [-x <pattern>|--exclude=<pattern>]
                [-X <file>|--exclude-from=<file>]
                [--exclude-per-directory=<file>]
@@ -80,6 +81,13 @@ OPTIONS
        \0 line termination on output and do not quote filenames.
        See OUTPUT below for more information.
 
+--deduplicate::
+       When only filenames are shown, suppress duplicates that may
+       come from having multiple stages during a merge, or giving
+       `--deleted` and `--modified` option at the same time.
+       When any of the `-t`, `--unmerged`, or `--stage` option is
+       in use, this option has no effect.
+
 -x <pattern>::
 --exclude=<pattern>::
        Skip untracked files matching pattern.
index fcf528c1b30d8045592d117ccc3d5f9248d0edbd..2b1bc7288d5f6fd27b92ed8119ef5dfbc89b9d1d 100644 (file)
@@ -45,10 +45,13 @@ include::pretty-options.txt[]
 include::pretty-formats.txt[]
 
 
-COMMON DIFF OPTIONS
--------------------
+DIFF FORMATTING
+---------------
+The options below can be used to change the way `git show` generates
+diff output.
 
 :git-log: 1
+:diff-merges-default: `dense-combined`
 include::diff-options.txt[]
 
 include::diff-generate-patch.txt[]
index 002379056a072cb87f7c65bbcaedaa4ebed7387a..96cc89d157d21f0cbf9cee35db0c49bc4cb18dae 100644 (file)
@@ -130,6 +130,11 @@ parents) and `--max-parents=-1` (negative numbers denote no upper limit).
        this option allows you to ignore the individual commits
        brought in to your history by such a merge.
 
+ifdef::git-log[]
+       This option also changes default diff format for merge commits
+       to `first-parent`, see `--diff-merges=first-parent` for details.
+endif::git-log[]
+
 --not::
        Reverses the meaning of the '{caret}' prefix (or lack thereof)
        for all following revision specifiers, up to the next `--not`.
index 69edf46c031683a7288ec5efd3aaf499fb8bda55..b633482b1bdff109015649258b248b60ecf36ee8 100644 (file)
@@ -26,7 +26,7 @@ Git index format
      Extensions are identified by signature. Optional extensions can
      be ignored if Git does not understand them.
 
-     Git currently supports cached tree and resolve undo extensions.
+     Git currently supports cache tree and resolve undo extensions.
 
      4-byte extension signature. If the first byte is 'A'..'Z' the
      extension is optional and can be ignored.
@@ -136,14 +136,35 @@ Git index format
 
 == Extensions
 
-=== Cached tree
-
-  Cached tree extension contains pre-computed hashes for trees that can
-  be derived from the index. It helps speed up tree object generation
-  from index for a new commit.
-
-  When a path is updated in index, the path must be invalidated and
-  removed from tree cache.
+=== Cache tree
+
+  Since the index does not record entries for directories, the cache
+  entries cannot describe tree objects that already exist in the object
+  database for regions of the index that are unchanged from an existing
+  commit. The cache tree extension stores a recursive tree structure that
+  describes the trees that already exist and completely match sections of
+  the cache entries. This speeds up tree object generation from the index
+  for a new commit by only computing the trees that are "new" to that
+  commit. It also assists when comparing the index to another tree, such
+  as `HEAD^{tree}`, since sections of the index can be skipped when a tree
+  comparison demonstrates equality.
+
+  The recursive tree structure uses nodes that store a number of cache
+  entries, a list of subnodes, and an object ID (OID). The OID references
+  the existing tree for that node, if it is known to exist. The subnodes
+  correspond to subdirectories that themselves have cache tree nodes. The
+  number of cache entries corresponds to the number of cache entries in
+  the index that describe paths within that tree's directory.
+
+  The extension tracks the full directory structure in the cache tree
+  extension, but this is generally smaller than the full cache entry list.
+
+  When a path is updated in index, Git invalidates all nodes of the
+  recursive cache tree corresponding to the parent directories of that
+  path. We store these tree nodes as being "invalid" by using "-1" as the
+  number of cache entries. Invalid nodes still store a span of index
+  entries, allowing Git to focus its efforts when reconstructing a full
+  cache tree.
 
   The signature for this extension is { 'T', 'R', 'E', 'E' }.
 
@@ -174,7 +195,8 @@ Git index format
   first entry represents the root level of the repository, followed by the
   first subtree--let's call this A--of the root level (with its name
   relative to the root level), followed by the first subtree of A (with
-  its name relative to A), ...
+  its name relative to A), and so on. The specified number of subtrees
+  indicates when the current level of the recursive stack is complete.
 
 === Resolve undo
 
index 318713abc37199826aa97eb1a040465c4dddf230..f7eabc6c76838d6577d14058c3ad43b69f3cf4f4 100644 (file)
@@ -37,8 +37,11 @@ at least so that we can test the client.
 This is the implementation: a feature, marked experimental, that allows the
 server to be configured by one or more `uploadpack.blobPackfileUri=<sha1>
 <uri>` entries. Whenever the list of objects to be sent is assembled, all such
-blobs are excluded, replaced with URIs. The client will download those URIs,
-expecting them to each point to packfiles containing single blobs.
+blobs are excluded, replaced with URIs. As noted in "Future work" below, the
+server can evolve in the future to support excluding other objects (or other
+implementations of servers could be made that support excluding other objects)
+without needing a protocol change, so clients should not expect that packfiles
+downloaded in this way only contain single blobs.
 
 Client design
 -------------
index 4edfda3e009d5b124d06de1ff59d277f250e8bf2..b797033c5834981a77cefa7a4d77cc6eac1c5ac5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -777,20 +777,6 @@ BUILT_INS += git-status$X
 BUILT_INS += git-switch$X
 BUILT_INS += git-whatchanged$X
 
-# what 'all' will build and 'install' will install in gitexecdir,
-# excluding programs for built-in commands
-ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
-ALL_COMMANDS_TO_INSTALL = $(ALL_PROGRAMS)
-ifeq (,$(SKIP_DASHED_BUILT_INS))
-ALL_COMMANDS_TO_INSTALL += $(BUILT_INS)
-else
-# git-upload-pack, git-receive-pack and git-upload-archive are special: they
-# are _expected_ to be present in the `bin/` directory in their dashed form.
-ALL_COMMANDS_TO_INSTALL += git-receive-pack$(X)
-ALL_COMMANDS_TO_INSTALL += git-upload-archive$(X)
-ALL_COMMANDS_TO_INSTALL += git-upload-pack$(X)
-endif
-
 # what 'all' will build but not install in gitexecdir
 OTHER_PROGRAMS = git$X
 
@@ -874,6 +860,7 @@ LIB_OBJS += date.o
 LIB_OBJS += decorate.o
 LIB_OBJS += delta-islands.o
 LIB_OBJS += diff-delta.o
+LIB_OBJS += diff-merges.o
 LIB_OBJS += diff-lib.o
 LIB_OBJS += diff-no-index.o
 LIB_OBJS += diff.o
@@ -1226,6 +1213,20 @@ ifdef DEVELOPER
 include config.mak.dev
 endif
 
+# what 'all' will build and 'install' will install in gitexecdir,
+# excluding programs for built-in commands
+ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
+ALL_COMMANDS_TO_INSTALL = $(ALL_PROGRAMS)
+ifeq (,$(SKIP_DASHED_BUILT_INS))
+ALL_COMMANDS_TO_INSTALL += $(BUILT_INS)
+else
+# git-upload-pack, git-receive-pack and git-upload-archive are special: they
+# are _expected_ to be present in the `bin/` directory in their dashed form.
+ALL_COMMANDS_TO_INSTALL += git-receive-pack$(X)
+ALL_COMMANDS_TO_INSTALL += git-upload-archive$(X)
+ALL_COMMANDS_TO_INSTALL += git-upload-pack$(X)
+endif
+
 ALL_CFLAGS = $(DEVELOPER_CFLAGS) $(CPPFLAGS) $(CFLAGS)
 ALL_LDFLAGS = $(LDFLAGS)
 
index 8c0b428104dcda765ec177172190c7a2807473c0..bcc00bcf182ddf53ec4215e8362edf1c391d72e8 100644 (file)
@@ -202,6 +202,9 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
        int remote_branch = 0;
        struct strbuf bname = STRBUF_INIT;
        unsigned allowed_interpret;
+       struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
+       struct string_list_item *item;
+       int branch_name_pos;
 
        switch (kinds) {
        case FILTER_REFS_REMOTES:
@@ -219,6 +222,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
        default:
                die(_("cannot use -a with -d"));
        }
+       branch_name_pos = strcspn(fmt, "%");
 
        if (!force) {
                head_rev = lookup_commit_reference(the_repository, &head_oid);
@@ -265,30 +269,35 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                        goto next;
                }
 
-               if (delete_ref(NULL, name, is_null_oid(&oid) ? NULL : &oid,
-                              REF_NO_DEREF)) {
-                       error(remote_branch
-                             ? _("Error deleting remote-tracking branch '%s'")
-                             : _("Error deleting branch '%s'"),
-                             bname.buf);
-                       ret = 1;
-                       goto next;
-               }
-               if (!quiet) {
-                       printf(remote_branch
-                              ? _("Deleted remote-tracking branch %s (was %s).\n")
-                              : _("Deleted branch %s (was %s).\n"),
-                              bname.buf,
-                              (flags & REF_ISBROKEN) ? "broken"
-                              : (flags & REF_ISSYMREF) ? target
-                              : find_unique_abbrev(&oid, DEFAULT_ABBREV));
-               }
-               delete_branch_config(bname.buf);
+               item = string_list_append(&refs_to_delete, name);
+               item->util = xstrdup((flags & REF_ISBROKEN) ? "broken"
+                                   : (flags & REF_ISSYMREF) ? target
+                                   : find_unique_abbrev(&oid, DEFAULT_ABBREV));
 
        next:
                free(target);
        }
 
+       if (delete_refs(NULL, &refs_to_delete, REF_NO_DEREF))
+               ret = 1;
+
+       for_each_string_list_item(item, &refs_to_delete) {
+               char *describe_ref = item->util;
+               char *name = item->string;
+               if (!ref_exists(name)) {
+                       char *refname = name + branch_name_pos;
+                       if (!quiet)
+                               printf(remote_branch
+                                       ? _("Deleted remote-tracking branch %s (was %s).\n")
+                                       : _("Deleted branch %s (was %s).\n"),
+                                       name + branch_name_pos, describe_ref);
+
+                       delete_branch_config(refname);
+               }
+               free(describe_ref);
+       }
+       string_list_clear(&refs_to_delete, 0);
+
        free(name);
        strbuf_release(&bname);
 
index 7668591d575acc3fe744763b7e53128c76aaf862..40482d8e9f855b6248a865559b12acc27ab84e22 100644 (file)
@@ -194,7 +194,7 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi
        }
 
        /* Is it annotated? */
-       if (!peel_ref(path, &peeled)) {
+       if (!peel_iterated_oid(oid, &peeled)) {
                is_annotated = !oideq(oid, &peeled);
        } else {
                oidcpy(&peeled, oid);
index 1e352dd8f77c281e41d702af81378cc9e213aaad..4742a4559b21beb38092e9e35c7f8c2e951455be 100644 (file)
@@ -7,6 +7,7 @@
 #include "cache.h"
 #include "config.h"
 #include "diff.h"
+#include "diff-merges.h"
 #include "commit.h"
 #include "revision.h"
 #include "builtin.h"
@@ -69,9 +70,9 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
         * was not asked to.  "diff-files -c -p" should not densify
         * (the user should ask with "diff-files --cc" explicitly).
         */
-       if (rev.max_count == -1 && !rev.combine_merges &&
+       if (rev.max_count == -1 &&
            (rev.diffopt.output_format & DIFF_FORMAT_PATCH))
-               rev.combine_merges = rev.dense_combined_merges = 1;
+               diff_merges_set_dense_combined_if_unset(&rev);
 
        if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
                perror("read_cache_preload");
index 780c33877f85b63c8ecf6bffbe6f7e2c5e91f5aa..5cfe1717e8deeabf45d7ebcaea078ddab1c7e410 100644 (file)
@@ -13,6 +13,7 @@
 #include "blob.h"
 #include "tag.h"
 #include "diff.h"
+#include "diff-merges.h"
 #include "diffcore.h"
 #include "revision.h"
 #include "log-tree.h"
@@ -216,8 +217,8 @@ static int builtin_diff_combined(struct rev_info *revs,
        if (argc > 1)
                usage(builtin_diff_usage);
 
-       if (!revs->dense_combined_merges && !revs->combine_merges)
-               revs->dense_combined_merges = revs->combine_merges = 1;
+       diff_merges_set_dense_combined_if_unset(revs);
+
        for (i = 1; i < ents; i++)
                oid_array_append(&parents, &ent[i].item->oid);
        diff_tree_combined(&ent[0].item->oid, &parents, revs);
@@ -265,9 +266,9 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv
         * dense one, --cc can be explicitly asked for, or just rely
         * on the default).
         */
-       if (revs->max_count == -1 && !revs->combine_merges &&
+       if (revs->max_count == -1 &&
            (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
-               revs->combine_merges = revs->dense_combined_merges = 1;
+               diff_merges_set_dense_combined_if_unset(revs);
 
        setup_work_tree();
        if (read_cache_preload(&revs->diffopt.pathspec) < 0) {
index ae34362e935b59aaa994ce200325cd3275389b26..4c40594d660ebb21c2cf0223b21af88ea81f872c 100644 (file)
@@ -769,7 +769,7 @@ static int dfs_on_ref(const char *refname,
        struct commit_list *stack = NULL;
        struct commit *commit;
 
-       if (!peel_ref(refname, &peeled))
+       if (!peel_iterated_oid(oid, &peeled))
                oid = &peeled;
        if (oid_object_info(the_repository, oid, NULL) != OBJ_COMMIT)
                return 0;
@@ -897,6 +897,12 @@ static int maintenance_task_prefetch(struct maintenance_run_opts *opts)
        struct string_list_item *item;
        struct string_list remotes = STRING_LIST_INIT_DUP;
 
+       git_config_set_multivar_gently("log.excludedecoration",
+                                       "refs/prefetch/",
+                                       "refs/prefetch/",
+                                       CONFIG_FLAGS_FIXED_VALUE |
+                                       CONFIG_FLAGS_MULTI_REPLACE);
+
        if (for_each_remote(append_remote, &remotes)) {
                error(_("failed to fill remotes"));
                result = 1;
index fd282def43f3a7b65e1596fc84978cec8473ff38..d0cbaaf68a083f0cfa3294804f606b5cb6dff5c4 100644 (file)
@@ -12,6 +12,7 @@
 #include "color.h"
 #include "commit.h"
 #include "diff.h"
+#include "diff-merges.h"
 #include "revision.h"
 #include "log-tree.h"
 #include "builtin.h"
@@ -607,15 +608,10 @@ static int show_tree_object(const struct object_id *oid,
 static void show_setup_revisions_tweak(struct rev_info *rev,
                                       struct setup_revision_opt *opt)
 {
-       if (rev->ignore_merges < 0) {
-               /* There was no "-m" variant on the command line */
-               rev->ignore_merges = 0;
-               if (!rev->first_parent_only && !rev->combine_merges) {
-                       /* No "--first-parent", "-c", or "--cc" */
-                       rev->combine_merges = 1;
-                       rev->dense_combined_merges = 1;
-               }
-       }
+       if (rev->first_parent_only)
+               diff_merges_default_to_first_parent(rev);
+       else
+               diff_merges_default_to_dense_combined(rev);
        if (!rev->diffopt.output_format)
                rev->diffopt.output_format = DIFF_FORMAT_PATCH;
 }
@@ -736,12 +732,8 @@ static void log_setup_revisions_tweak(struct rev_info *rev,
            rev->prune_data.nr == 1)
                rev->diffopt.flags.follow_renames = 1;
 
-       /* Turn --cc/-c into -p --cc/-c when -p was not given */
-       if (!rev->diffopt.output_format && rev->combine_merges)
-               rev->diffopt.output_format = DIFF_FORMAT_PATCH;
-
-       if (rev->first_parent_only && rev->ignore_merges < 0)
-               rev->ignore_merges = 0;
+       if (rev->first_parent_only)
+               diff_merges_default_to_first_parent(rev);
 }
 
 int cmd_log(int argc, const char **argv, const char *prefix)
index c8eae899b82a83ebc3487ffa99ba2bcf73d3369f..f6f9e483b27e183e29e9c409669c6b23d5590cfc 100644 (file)
@@ -35,6 +35,7 @@ static int line_terminator = '\n';
 static int debug_mode;
 static int show_eol;
 static int recurse_submodules;
+static int skipping_duplicates;
 
 static const char *prefix;
 static int max_prefix_len;
@@ -312,45 +313,59 @@ static void show_files(struct repository *repo, struct dir_struct *dir)
                if (show_killed)
                        show_killed_files(repo->index, dir);
        }
-       if (show_cached || show_stage) {
-               for (i = 0; i < repo->index->cache_nr; i++) {
-                       const struct cache_entry *ce = repo->index->cache[i];
 
-                       construct_fullname(&fullname, repo, ce);
+       if (!(show_cached || show_stage || show_deleted || show_modified))
+               return;
+       for (i = 0; i < repo->index->cache_nr; i++) {
+               const struct cache_entry *ce = repo->index->cache[i];
+               struct stat st;
+               int stat_err;
 
-                       if ((dir->flags & DIR_SHOW_IGNORED) &&
-                           !ce_excluded(dir, repo->index, fullname.buf, ce))
-                               continue;
-                       if (show_unmerged && !ce_stage(ce))
-                               continue;
-                       if (ce->ce_flags & CE_UPDATE)
-                               continue;
+               construct_fullname(&fullname, repo, ce);
+
+               if ((dir->flags & DIR_SHOW_IGNORED) &&
+                       !ce_excluded(dir, repo->index, fullname.buf, ce))
+                       continue;
+               if (ce->ce_flags & CE_UPDATE)
+                       continue;
+               if ((show_cached || show_stage) &&
+                   (!show_unmerged || ce_stage(ce))) {
                        show_ce(repo, dir, ce, fullname.buf,
                                ce_stage(ce) ? tag_unmerged :
                                (ce_skip_worktree(ce) ? tag_skip_worktree :
                                 tag_cached));
+                       if (skipping_duplicates)
+                               goto skip_to_next_name;
                }
-       }
-       if (show_deleted || show_modified) {
-               for (i = 0; i < repo->index->cache_nr; i++) {
-                       const struct cache_entry *ce = repo->index->cache[i];
-                       struct stat st;
-                       int err;
 
-                       construct_fullname(&fullname, repo, ce);
-
-                       if ((dir->flags & DIR_SHOW_IGNORED) &&
-                           !ce_excluded(dir, repo->index, fullname.buf, ce))
-                               continue;
-                       if (ce->ce_flags & CE_UPDATE)
-                               continue;
-                       if (ce_skip_worktree(ce))
-                               continue;
-                       err = lstat(fullname.buf, &st);
-                       if (show_deleted && err)
-                               show_ce(repo, dir, ce, fullname.buf, tag_removed);
-                       if (show_modified && ie_modified(repo->index, ce, &st, 0))
-                               show_ce(repo, dir, ce, fullname.buf, tag_modified);
+               if (!(show_deleted || show_modified))
+                       continue;
+               if (ce_skip_worktree(ce))
+                       continue;
+               stat_err = lstat(fullname.buf, &st);
+               if (stat_err && (errno != ENOENT && errno != ENOTDIR))
+                       error_errno("cannot lstat '%s'", fullname.buf);
+               if (stat_err && show_deleted) {
+                       show_ce(repo, dir, ce, fullname.buf, tag_removed);
+                       if (skipping_duplicates)
+                               goto skip_to_next_name;
+               }
+               if (show_modified &&
+                   (stat_err || ie_modified(repo->index, ce, &st, 0))) {
+                       show_ce(repo, dir, ce, fullname.buf, tag_modified);
+                       if (skipping_duplicates)
+                               goto skip_to_next_name;
+               }
+               continue;
+
+skip_to_next_name:
+               {
+                       int j;
+                       struct cache_entry **cache = repo->index->cache;
+                       for (j = i + 1; j < repo->index->cache_nr; j++)
+                               if (strcmp(ce->name, cache[j]->name))
+                                       break;
+                       i = j - 1; /* compensate for the for loop */
                }
        }
 
@@ -578,6 +593,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
                        N_("pretend that paths removed since <tree-ish> are still present")),
                OPT__ABBREV(&abbrev),
                OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")),
+               OPT_BOOL(0, "deduplicate", &skipping_duplicates,
+                        N_("suppress duplicate entries")),
                OPT_END()
        };
 
@@ -617,6 +634,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
                 * you also show the stage information.
                 */
                show_stage = 1;
+       if (show_tag || show_stage)
+               skipping_duplicates = 0;
        if (dir.exclude_per_dir)
                exc_given = 1;
 
index 1cff730715394fa5874c12323bba653ffe5b8abe..eb00b273e668e2725447a2d6a0edc5ad2d67ed67 100644 (file)
@@ -14,6 +14,7 @@
 #include "lockfile.h"
 #include "run-command.h"
 #include "diff.h"
+#include "diff-merges.h"
 #include "refs.h"
 #include "refspec.h"
 #include "commit.h"
@@ -409,7 +410,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
        printf(_("Squash commit -- not updating HEAD\n"));
 
        repo_init_revisions(the_repository, &rev, NULL);
-       rev.ignore_merges = 1;
+       diff_merges_suppress(&rev);
        rev.commit_format = CMIT_FMT_MEDIUM;
 
        commit->object.flags |= UNINTERESTING;
index 5b0c4489e27d48f2af3729c991e258c3150c2be0..13cde5896aac80ab43408b1e89916832b1e8ec9c 100644 (file)
@@ -634,7 +634,7 @@ static int mark_tagged(const char *path, const struct object_id *oid, int flag,
 
        if (entry)
                entry->tagged = 1;
-       if (!peel_ref(path, &peeled)) {
+       if (!peel_iterated_oid(oid, &peeled)) {
                entry = packlist_find(&to_pack, &peeled);
                if (entry)
                        entry->tagged = 1;
@@ -2814,13 +2814,11 @@ static void add_tag_chain(const struct object_id *oid)
        }
 }
 
-static int add_ref_tag(const char *path, const struct object_id *oid, int flag, void *cb_data)
+static int add_ref_tag(const char *tag, const struct object_id *oid, int flag, void *cb_data)
 {
        struct object_id peeled;
 
-       if (starts_with(path, "refs/tags/") && /* is a tag? */
-           !peel_ref(path, &peeled)    && /* peelable? */
-           obj_is_packed(&peeled)) /* object packed? */
+       if (!peel_iterated_oid(oid, &peeled) && obj_is_packed(&peeled))
                add_tag_chain(oid);
        return 0;
 }
@@ -3751,7 +3749,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        }
        cleanup_preferred_base();
        if (include_tag && nr_result)
-               for_each_ref(add_ref_tag, NULL);
+               for_each_tag_ref(add_ref_tag, NULL);
        stop_progress(&progress_state);
        trace2_region_leave("pack-objects", "enumerate-objects",
                            the_repository);
index ae60b4acf2f444631d477104b7c5bc15e5311658..7f8a5332f838ac532c6323825662c2a2a0576db6 100644 (file)
@@ -40,7 +40,7 @@ static void show_one(const char *refname, const struct object_id *oid)
        if (!deref_tags)
                return;
 
-       if (!peel_ref(refname, &peeled)) {
+       if (!peel_iterated_oid(oid, &peeled)) {
                hex = find_unique_abbrev(&peeled, abbrev);
                printf("%s %s^{}\n", hex, refname);
        }
index 24d35b746d19d04d6f04c46f30ac1c0cb3d4baa2..e8b85eefd878b5b2fd1eafe0627d811ecd49b8ea 100644 (file)
@@ -72,10 +72,10 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
 }
 
 typedef int (*each_tag_name_fn)(const char *name, const char *ref,
-                               const struct object_id *oid, const void *cb_data);
+                               const struct object_id *oid, void *cb_data);
 
 static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
-                            const void *cb_data)
+                            void *cb_data)
 {
        const char **p;
        struct strbuf ref = STRBUF_INIT;
@@ -97,18 +97,42 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
        return had_error;
 }
 
-static int delete_tag(const char *name, const char *ref,
-                     const struct object_id *oid, const void *cb_data)
+static int collect_tags(const char *name, const char *ref,
+                       const struct object_id *oid, void *cb_data)
 {
-       if (delete_ref(NULL, ref, oid, 0))
-               return 1;
-       printf(_("Deleted tag '%s' (was %s)\n"), name,
-              find_unique_abbrev(oid, DEFAULT_ABBREV));
+       struct string_list *ref_list = cb_data;
+
+       string_list_append(ref_list, ref);
+       ref_list->items[ref_list->nr - 1].util = oiddup(oid);
        return 0;
 }
 
+static int delete_tags(const char **argv)
+{
+       int result;
+       struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
+       struct string_list_item *item;
+
+       result = for_each_tag_name(argv, collect_tags, (void *)&refs_to_delete);
+       if (delete_refs(NULL, &refs_to_delete, REF_NO_DEREF))
+               result = 1;
+
+       for_each_string_list_item(item, &refs_to_delete) {
+               const char *name = item->string;
+               struct object_id *oid = item->util;
+               if (!ref_exists(name))
+                       printf(_("Deleted tag '%s' (was %s)\n"),
+                               item->string + 10,
+                               find_unique_abbrev(oid, DEFAULT_ABBREV));
+
+               free(oid);
+       }
+       string_list_clear(&refs_to_delete, 0);
+       return result;
+}
+
 static int verify_tag(const char *name, const char *ref,
-                     const struct object_id *oid, const void *cb_data)
+                     const struct object_id *oid, void *cb_data)
 {
        int flags;
        const struct ref_format *format = cb_data;
@@ -512,7 +536,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        if (filter.reachable_from || filter.unreachable_from)
                die(_("--merged and --no-merged options are only allowed in list mode"));
        if (cmdmode == 'd')
-               return for_each_tag_name(argv, delete_tag, NULL);
+               return delete_tags(argv);
        if (cmdmode == 'v') {
                if (format.format && verify_ref_format(&format))
                        usage_with_options(git_tag_usage, options);
index a537a806c16e0352e4347a6bd9473457c78377bf..3f1a8d4f1b7e0807d50db20fa2dbda31c80e843e 100644 (file)
@@ -185,12 +185,14 @@ static int verify_cache(struct cache_entry **cache,
                 * the cache is sorted.  Also path can appear only once,
                 * which means conflicting one would immediately follow.
                 */
-               const char *this_name = cache[i]->name;
-               const char *next_name = cache[i+1]->name;
-               int this_len = strlen(this_name);
-               if (this_len < strlen(next_name) &&
-                   strncmp(this_name, next_name, this_len) == 0 &&
-                   next_name[this_len] == '/') {
+               const struct cache_entry *this_ce = cache[i];
+               const struct cache_entry *next_ce = cache[i + 1];
+               const char *this_name = this_ce->name;
+               const char *next_name = next_ce->name;
+               int this_len = ce_namelen(this_ce);
+               if (this_len < ce_namelen(next_ce) &&
+                   next_name[this_len] == '/' &&
+                   strncmp(this_name, next_name, this_len) == 0) {
                        if (10 < ++funny) {
                                fprintf(stderr, "...\n");
                                break;
@@ -442,7 +444,9 @@ int cache_tree_update(struct index_state *istate, int flags)
        if (i)
                return i;
        trace_performance_enter();
+       trace2_region_enter("cache_tree", "update", the_repository);
        i = update_one(it, cache, entries, "", 0, &skip, flags);
+       trace2_region_leave("cache_tree", "update", the_repository);
        trace_performance_leave("cache_tree_update");
        if (i < 0)
                return i;
@@ -492,7 +496,9 @@ static void write_one(struct strbuf *buffer, struct cache_tree *it,
 
 void cache_tree_write(struct strbuf *sb, struct cache_tree *root)
 {
+       trace2_region_enter("cache_tree", "write", the_repository);
        write_one(sb, root, "", 0);
+       trace2_region_leave("cache_tree", "write", the_repository);
 }
 
 static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)
@@ -581,9 +587,16 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)
 
 struct cache_tree *cache_tree_read(const char *buffer, unsigned long size)
 {
+       struct cache_tree *result;
+
        if (buffer[0])
                return NULL; /* not the whole tree */
-       return read_one(&buffer, &size);
+
+       trace2_region_enter("cache_tree", "read", the_repository);
+       result = read_one(&buffer, &size);
+       trace2_region_leave("cache_tree", "read", the_repository);
+
+       return result;
 }
 
 static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *path)
@@ -733,10 +746,13 @@ void prime_cache_tree(struct repository *r,
                      struct index_state *istate,
                      struct tree *tree)
 {
+       trace2_region_enter("cache-tree", "prime_cache_tree", the_repository);
        cache_tree_free(&istate->cache_tree);
        istate->cache_tree = cache_tree();
+
        prime_cache_tree_rec(r, istate->cache_tree, tree);
        istate->cache_changed |= CACHE_TREE_CHANGED;
+       trace2_region_leave("cache-tree", "prime_cache_tree", the_repository);
 }
 
 /*
index f3486ec18f1b884fe65c74ac7ad75eb77fdcbb85..b8c1b034a47f5c71dbf6762008f735035427915d 100644 (file)
@@ -1458,7 +1458,7 @@ static int add_ref_to_set(const char *refname,
        struct object_id peeled;
        struct refs_cb_data *data = (struct refs_cb_data *)cb_data;
 
-       if (!peel_ref(refname, &peeled))
+       if (!peel_iterated_oid(oid, &peeled))
                oid = &peeled;
        if (oid_object_info(the_repository, oid, NULL) == OBJ_COMMIT)
                oidset_insert(data->commits, oid);
diff --git a/diff-merges.c b/diff-merges.c
new file mode 100644 (file)
index 0000000..146bb50
--- /dev/null
@@ -0,0 +1,146 @@
+#include "diff-merges.h"
+
+#include "revision.h"
+
+static void suppress(struct rev_info *revs)
+{
+       revs->separate_merges = 0;
+       revs->first_parent_merges = 0;
+       revs->combine_merges = 0;
+       revs->dense_combined_merges = 0;
+       revs->combined_all_paths = 0;
+       revs->combined_imply_patch = 0;
+       revs->merges_need_diff = 0;
+}
+
+static void set_separate(struct rev_info *revs)
+{
+       suppress(revs);
+       revs->separate_merges = 1;
+}
+
+static void set_first_parent(struct rev_info *revs)
+{
+       set_separate(revs);
+       revs->first_parent_merges = 1;
+}
+
+static void set_m(struct rev_info *revs)
+{
+       /*
+        * To "diff-index", "-m" means "match missing", and to the "log"
+        * family of commands, it means "show full diff for merges". Set
+        * both fields appropriately.
+        */
+       set_separate(revs);
+       revs->match_missing = 1;
+}
+
+static void set_combined(struct rev_info *revs)
+{
+       suppress(revs);
+       revs->combine_merges = 1;
+       revs->dense_combined_merges = 0;
+}
+
+static void set_dense_combined(struct rev_info *revs)
+{
+       suppress(revs);
+       revs->combine_merges = 1;
+       revs->dense_combined_merges = 1;
+}
+
+static void set_diff_merges(struct rev_info *revs, const char *optarg)
+{
+       if (!strcmp(optarg, "off") || !strcmp(optarg, "none")) {
+               suppress(revs);
+               /* Return early to leave revs->merges_need_diff unset */
+               return;
+       }
+
+       if (!strcmp(optarg, "1") || !strcmp(optarg, "first-parent"))
+               set_first_parent(revs);
+       else if (!strcmp(optarg, "m") || !strcmp(optarg, "separate"))
+               set_separate(revs);
+       else if (!strcmp(optarg, "c") || !strcmp(optarg, "combined"))
+               set_combined(revs);
+       else if (!strcmp(optarg, "cc") || !strcmp(optarg, "dense-combined"))
+               set_dense_combined(revs);
+       else
+               die(_("unknown value for --diff-merges: %s"), optarg);
+
+       /* The flag is cleared by set_xxx() functions, so don't move this up */
+       revs->merges_need_diff = 1;
+}
+
+/*
+ * Public functions. They are in the order they are called.
+ */
+
+int diff_merges_parse_opts(struct rev_info *revs, const char **argv)
+{
+       int argcount = 1;
+       const char *optarg;
+       const char *arg = argv[0];
+
+       if (!strcmp(arg, "-m")) {
+               set_m(revs);
+       } else if (!strcmp(arg, "-c")) {
+               set_combined(revs);
+               revs->combined_imply_patch = 1;
+       } else if (!strcmp(arg, "--cc")) {
+               set_dense_combined(revs);
+               revs->combined_imply_patch = 1;
+       } else if (!strcmp(arg, "--no-diff-merges")) {
+               suppress(revs);
+       } else if (!strcmp(arg, "--combined-all-paths")) {
+               revs->combined_all_paths = 1;
+       } else if ((argcount = parse_long_opt("diff-merges", argv, &optarg))) {
+               set_diff_merges(revs, optarg);
+       } else
+               return 0;
+
+       revs->explicit_diff_merges = 1;
+       return argcount;
+}
+
+void diff_merges_suppress(struct rev_info *revs)
+{
+       suppress(revs);
+}
+
+void diff_merges_default_to_first_parent(struct rev_info *revs)
+{
+       if (!revs->explicit_diff_merges)
+               revs->separate_merges = 1;
+       if (revs->separate_merges)
+               revs->first_parent_merges = 1;
+}
+
+void diff_merges_default_to_dense_combined(struct rev_info *revs)
+{
+       if (!revs->explicit_diff_merges)
+               set_dense_combined(revs);
+}
+
+void diff_merges_set_dense_combined_if_unset(struct rev_info *revs)
+{
+       if (!revs->combine_merges)
+               set_dense_combined(revs);
+}
+
+void diff_merges_setup_revs(struct rev_info *revs)
+{
+       if (revs->combine_merges == 0)
+               revs->dense_combined_merges = 0;
+       if (revs->separate_merges == 0)
+               revs->first_parent_merges = 0;
+       if (revs->combined_all_paths && !revs->combine_merges)
+               die("--combined-all-paths makes no sense without -c or --cc");
+       if (revs->combined_imply_patch)
+               revs->diff = 1;
+       if (revs->combined_imply_patch || revs->merges_need_diff) {
+               if (!revs->diffopt.output_format)
+                       revs->diffopt.output_format = DIFF_FORMAT_PATCH;
+       }
+}
diff --git a/diff-merges.h b/diff-merges.h
new file mode 100644 (file)
index 0000000..659467c
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef DIFF_MERGES_H
+#define DIFF_MERGES_H
+
+/*
+ * diff-merges - utility module to handle command-line options for
+ * selection of particular diff format of merge commits
+ * representation.
+ */
+
+struct rev_info;
+
+int diff_merges_parse_opts(struct rev_info *revs, const char **argv);
+
+void diff_merges_suppress(struct rev_info *revs);
+
+void diff_merges_default_to_first_parent(struct rev_info *revs);
+
+void diff_merges_default_to_dense_combined(struct rev_info *revs);
+
+void diff_merges_set_dense_combined_if_unset(struct rev_info *revs);
+
+void diff_merges_setup_revs(struct rev_info *revs);
+
+#endif
index 9a664a4a5832daba2a69e1d91d996eddb1f8bb56..46f6015c447dad5d2a6768bf92274cf728725846 100644 (file)
@@ -2,6 +2,7 @@
 #include "refs.h"
 #include "object-store.h"
 #include "diff.h"
+#include "diff-merges.h"
 #include "revision.h"
 #include "tag.h"
 #include "string-list.h"
@@ -670,7 +671,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
                head = lookup_commit_or_die(&head_oid, "HEAD");
                repo_init_revisions(the_repository, &rev, NULL);
                rev.commit_format = CMIT_FMT_ONELINE;
-               rev.ignore_merges = 1;
+               diff_merges_suppress(&rev);
                rev.limited = 1;
 
                strbuf_complete_line(out);
index fd0dde97ec324ff6611fa5a7685d4b2831350b3c..e048467650770510b41afb92678d0a807c34764d 100644 (file)
@@ -899,15 +899,21 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
        int showed_log;
        struct commit_list *parents;
        struct object_id *oid;
+       int is_merge;
+       int all_need_diff = opt->diff || opt->diffopt.flags.exit_with_status;
 
-       if (!opt->diff && !opt->diffopt.flags.exit_with_status)
+       if (!all_need_diff && !opt->merges_need_diff)
                return 0;
 
        parse_commit_or_die(commit);
        oid = get_commit_tree_oid(commit);
 
-       /* Root commit? */
        parents = get_saved_parents(opt, commit);
+       is_merge = parents && parents->next;
+       if (!is_merge && !all_need_diff)
+               return 0;
+
+       /* Root commit? */
        if (!parents) {
                if (opt->show_root_diff) {
                        diff_root_tree_oid(oid, "", &opt->diffopt);
@@ -916,16 +922,16 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
                return !opt->loginfo;
        }
 
-       /* More than one parent? */
-       if (parents->next) {
-               if (opt->ignore_merges)
-                       return 0;
-               else if (opt->combine_merges)
+       if (is_merge) {
+               if (opt->combine_merges)
                        return do_diff_combined(opt, commit);
-               else if (!opt->first_parent_only) {
-                       /* If we show multiple diffs, show the parent info */
-                       log->parent = parents->item;
-               }
+               if (opt->separate_merges) {
+                       if (!opt->first_parent_merges) {
+                               /* Show parent info for multiple diffs */
+                               log->parent = parents->item;
+                       }
+               } else
+                       return 0;
        }
 
        showed_log = 0;
@@ -941,7 +947,7 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
 
                /* Set up the log info for the next parent, if any.. */
                parents = parents->next;
-               if (!parents || opt->first_parent_only)
+               if (!parents || opt->first_parent_merges)
                        break;
                log->parent = parents->item;
                opt->loginfo = log;
index a1e0b473e44720421a71adeaf6aebb4ceb782f6e..5ff5473869fa19aa08d655cb1dcc7485d71dbbbd 100644 (file)
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -63,7 +63,7 @@ static int send_ref(const char *refname, const struct object_id *oid,
 
        if (data->peel) {
                struct object_id peeled;
-               if (!peel_ref(refname, &peeled))
+               if (!peel_iterated_oid(oid, &peeled))
                        strbuf_addf(&refline, " peeled:%s", oid_to_hex(&peeled));
        }
 
@@ -90,6 +90,7 @@ int ls_refs(struct repository *r, struct strvec *keys,
        struct ls_refs_data data;
 
        memset(&data, 0, sizeof(data));
+       strvec_init(&data.prefixes);
 
        git_config(ls_refs_config, NULL);
 
@@ -109,7 +110,10 @@ int ls_refs(struct repository *r, struct strvec *keys,
                die(_("expected flush after ls-refs arguments"));
 
        head_ref_namespaced(send_ref, &data);
-       for_each_namespaced_ref(send_ref, &data);
+       if (!data.prefixes.nr)
+               strvec_push(&data.prefixes, "");
+       for_each_fullref_in_prefixes(get_git_namespace(), data.prefixes.v,
+                                    send_ref, &data, 0);
        packet_flush(1);
        strvec_clear(&data.prefixes);
        return 0;
index d36a92b59b76fe49424ae97e4cd2777badb7e318..6900ab9e7fcc6c7d595885dd90039828e04083d5 100644 (file)
 #include "diff.h"
 #include "diffcore.h"
 #include "dir.h"
+#include "ll-merge.h"
 #include "object-store.h"
+#include "revision.h"
 #include "strmap.h"
+#include "submodule.h"
 #include "tree.h"
 #include "unpack-trees.h"
 #include "xdiff-interface.h"
@@ -349,6 +352,25 @@ static int err(struct merge_options *opt, const char *err, ...)
        return -1;
 }
 
+static void format_commit(struct strbuf *sb,
+                         int indent,
+                         struct commit *commit)
+{
+       struct merge_remote_desc *desc;
+       struct pretty_print_context ctx = {0};
+       ctx.abbrev = DEFAULT_ABBREV;
+
+       strbuf_addchars(sb, ' ', indent);
+       desc = merge_remote_util(commit);
+       if (desc) {
+               strbuf_addf(sb, "virtual %s\n", desc->name);
+               return;
+       }
+
+       format_commit_message(commit, "%h %s", sb, &ctx);
+       strbuf_addch(sb, '\n');
+}
+
 __attribute__((format (printf, 4, 5)))
 static void path_msg(struct merge_options *opt,
                     const char *path,
@@ -370,6 +392,36 @@ static void path_msg(struct merge_options *opt,
        strbuf_addch(sb, '\n');
 }
 
+/* add a string to a strbuf, but converting "/" to "_" */
+static void add_flattened_path(struct strbuf *out, const char *s)
+{
+       size_t i = out->len;
+       strbuf_addstr(out, s);
+       for (; i < out->len; i++)
+               if (out->buf[i] == '/')
+                       out->buf[i] = '_';
+}
+
+static char *unique_path(struct strmap *existing_paths,
+                        const char *path,
+                        const char *branch)
+{
+       struct strbuf newpath = STRBUF_INIT;
+       int suffix = 0;
+       size_t base_len;
+
+       strbuf_addf(&newpath, "%s~", path);
+       add_flattened_path(&newpath, branch);
+
+       base_len = newpath.len;
+       while (strmap_contains(existing_paths, newpath.buf)) {
+               strbuf_setlen(&newpath, base_len);
+               strbuf_addf(&newpath, "_%d", suffix++);
+       }
+
+       return strbuf_detach(&newpath, NULL);
+}
+
 /*** Function Grouping: functions related to collect_merge_info() ***/
 
 static void setup_path_info(struct merge_options *opt,
@@ -628,6 +680,249 @@ static int collect_merge_info(struct merge_options *opt,
 
 /*** Function Grouping: functions related to threeway content merges ***/
 
+static int find_first_merges(struct repository *repo,
+                            const char *path,
+                            struct commit *a,
+                            struct commit *b,
+                            struct object_array *result)
+{
+       int i, j;
+       struct object_array merges = OBJECT_ARRAY_INIT;
+       struct commit *commit;
+       int contains_another;
+
+       char merged_revision[GIT_MAX_HEXSZ + 2];
+       const char *rev_args[] = { "rev-list", "--merges", "--ancestry-path",
+                                  "--all", merged_revision, NULL };
+       struct rev_info revs;
+       struct setup_revision_opt rev_opts;
+
+       memset(result, 0, sizeof(struct object_array));
+       memset(&rev_opts, 0, sizeof(rev_opts));
+
+       /* get all revisions that merge commit a */
+       xsnprintf(merged_revision, sizeof(merged_revision), "^%s",
+                 oid_to_hex(&a->object.oid));
+       repo_init_revisions(repo, &revs, NULL);
+       rev_opts.submodule = path;
+       /* FIXME: can't handle linked worktrees in submodules yet */
+       revs.single_worktree = path != NULL;
+       setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
+
+       /* save all revisions from the above list that contain b */
+       if (prepare_revision_walk(&revs))
+               die("revision walk setup failed");
+       while ((commit = get_revision(&revs)) != NULL) {
+               struct object *o = &(commit->object);
+               if (in_merge_bases(b, commit))
+                       add_object_array(o, NULL, &merges);
+       }
+       reset_revision_walk();
+
+       /* Now we've got all merges that contain a and b. Prune all
+        * merges that contain another found merge and save them in
+        * result.
+        */
+       for (i = 0; i < merges.nr; i++) {
+               struct commit *m1 = (struct commit *) merges.objects[i].item;
+
+               contains_another = 0;
+               for (j = 0; j < merges.nr; j++) {
+                       struct commit *m2 = (struct commit *) merges.objects[j].item;
+                       if (i != j && in_merge_bases(m2, m1)) {
+                               contains_another = 1;
+                               break;
+                       }
+               }
+
+               if (!contains_another)
+                       add_object_array(merges.objects[i].item, NULL, result);
+       }
+
+       object_array_clear(&merges);
+       return result->nr;
+}
+
+static int merge_submodule(struct merge_options *opt,
+                          const char *path,
+                          const struct object_id *o,
+                          const struct object_id *a,
+                          const struct object_id *b,
+                          struct object_id *result)
+{
+       struct commit *commit_o, *commit_a, *commit_b;
+       int parent_count;
+       struct object_array merges;
+       struct strbuf sb = STRBUF_INIT;
+
+       int i;
+       int search = !opt->priv->call_depth;
+
+       /* store fallback answer in result in case we fail */
+       oidcpy(result, opt->priv->call_depth ? o : a);
+
+       /* we can not handle deletion conflicts */
+       if (is_null_oid(o))
+               return 0;
+       if (is_null_oid(a))
+               return 0;
+       if (is_null_oid(b))
+               return 0;
+
+       if (add_submodule_odb(path)) {
+               path_msg(opt, path, 0,
+                        _("Failed to merge submodule %s (not checked out)"),
+                        path);
+               return 0;
+       }
+
+       if (!(commit_o = lookup_commit_reference(opt->repo, o)) ||
+           !(commit_a = lookup_commit_reference(opt->repo, a)) ||
+           !(commit_b = lookup_commit_reference(opt->repo, b))) {
+               path_msg(opt, path, 0,
+                        _("Failed to merge submodule %s (commits not present)"),
+                        path);
+               return 0;
+       }
+
+       /* check whether both changes are forward */
+       if (!in_merge_bases(commit_o, commit_a) ||
+           !in_merge_bases(commit_o, commit_b)) {
+               path_msg(opt, path, 0,
+                        _("Failed to merge submodule %s "
+                          "(commits don't follow merge-base)"),
+                        path);
+               return 0;
+       }
+
+       /* Case #1: a is contained in b or vice versa */
+       if (in_merge_bases(commit_a, commit_b)) {
+               oidcpy(result, b);
+               path_msg(opt, path, 1,
+                        _("Note: Fast-forwarding submodule %s to %s"),
+                        path, oid_to_hex(b));
+               return 1;
+       }
+       if (in_merge_bases(commit_b, commit_a)) {
+               oidcpy(result, a);
+               path_msg(opt, path, 1,
+                        _("Note: Fast-forwarding submodule %s to %s"),
+                        path, oid_to_hex(a));
+               return 1;
+       }
+
+       /*
+        * Case #2: There are one or more merges that contain a and b in
+        * the submodule. If there is only one, then present it as a
+        * suggestion to the user, but leave it marked unmerged so the
+        * user needs to confirm the resolution.
+        */
+
+       /* Skip the search if makes no sense to the calling context.  */
+       if (!search)
+               return 0;
+
+       /* find commit which merges them */
+       parent_count = find_first_merges(opt->repo, path, commit_a, commit_b,
+                                        &merges);
+       switch (parent_count) {
+       case 0:
+               path_msg(opt, path, 0, _("Failed to merge submodule %s"), path);
+               break;
+
+       case 1:
+               format_commit(&sb, 4,
+                             (struct commit *)merges.objects[0].item);
+               path_msg(opt, path, 0,
+                        _("Failed to merge submodule %s, but a possible merge "
+                          "resolution exists:\n%s\n"),
+                        path, sb.buf);
+               path_msg(opt, path, 1,
+                        _("If this is correct simply add it to the index "
+                          "for example\n"
+                          "by using:\n\n"
+                          "  git update-index --cacheinfo 160000 %s \"%s\"\n\n"
+                          "which will accept this suggestion.\n"),
+                        oid_to_hex(&merges.objects[0].item->oid), path);
+               strbuf_release(&sb);
+               break;
+       default:
+               for (i = 0; i < merges.nr; i++)
+                       format_commit(&sb, 4,
+                                     (struct commit *)merges.objects[i].item);
+               path_msg(opt, path, 0,
+                        _("Failed to merge submodule %s, but multiple "
+                          "possible merges exist:\n%s"), path, sb.buf);
+               strbuf_release(&sb);
+       }
+
+       object_array_clear(&merges);
+       return 0;
+}
+
+static int merge_3way(struct merge_options *opt,
+                     const char *path,
+                     const struct object_id *o,
+                     const struct object_id *a,
+                     const struct object_id *b,
+                     const char *pathnames[3],
+                     const int extra_marker_size,
+                     mmbuffer_t *result_buf)
+{
+       mmfile_t orig, src1, src2;
+       struct ll_merge_options ll_opts = {0};
+       char *base, *name1, *name2;
+       int merge_status;
+
+       ll_opts.renormalize = opt->renormalize;
+       ll_opts.extra_marker_size = extra_marker_size;
+       ll_opts.xdl_opts = opt->xdl_opts;
+
+       if (opt->priv->call_depth) {
+               ll_opts.virtual_ancestor = 1;
+               ll_opts.variant = 0;
+       } else {
+               switch (opt->recursive_variant) {
+               case MERGE_VARIANT_OURS:
+                       ll_opts.variant = XDL_MERGE_FAVOR_OURS;
+                       break;
+               case MERGE_VARIANT_THEIRS:
+                       ll_opts.variant = XDL_MERGE_FAVOR_THEIRS;
+                       break;
+               default:
+                       ll_opts.variant = 0;
+                       break;
+               }
+       }
+
+       assert(pathnames[0] && pathnames[1] && pathnames[2] && opt->ancestor);
+       if (pathnames[0] == pathnames[1] && pathnames[1] == pathnames[2]) {
+               base  = mkpathdup("%s", opt->ancestor);
+               name1 = mkpathdup("%s", opt->branch1);
+               name2 = mkpathdup("%s", opt->branch2);
+       } else {
+               base  = mkpathdup("%s:%s", opt->ancestor, pathnames[0]);
+               name1 = mkpathdup("%s:%s", opt->branch1,  pathnames[1]);
+               name2 = mkpathdup("%s:%s", opt->branch2,  pathnames[2]);
+       }
+
+       read_mmblob(&orig, o);
+       read_mmblob(&src1, a);
+       read_mmblob(&src2, b);
+
+       merge_status = ll_merge(result_buf, path, &orig, base,
+                               &src1, name1, &src2, name2,
+                               opt->repo->index, &ll_opts);
+
+       free(base);
+       free(name1);
+       free(name2);
+       free(orig.ptr);
+       free(src1.ptr);
+       free(src2.ptr);
+       return merge_status;
+}
+
 static int handle_content_merge(struct merge_options *opt,
                                const char *path,
                                const struct version_info *o,
@@ -637,7 +932,130 @@ static int handle_content_merge(struct merge_options *opt,
                                const int extra_marker_size,
                                struct version_info *result)
 {
-       die("Not yet implemented");
+       /*
+        * path is the target location where we want to put the file, and
+        * is used to determine any normalization rules in ll_merge.
+        *
+        * The normal case is that path and all entries in pathnames are
+        * identical, though renames can affect which path we got one of
+        * the three blobs to merge on various sides of history.
+        *
+        * extra_marker_size is the amount to extend conflict markers in
+        * ll_merge; this is neeed if we have content merges of content
+        * merges, which happens for example with rename/rename(2to1) and
+        * rename/add conflicts.
+        */
+       unsigned clean = 1;
+
+       /*
+        * handle_content_merge() needs both files to be of the same type, i.e.
+        * both files OR both submodules OR both symlinks.  Conflicting types
+        * needs to be handled elsewhere.
+        */
+       assert((S_IFMT & a->mode) == (S_IFMT & b->mode));
+
+       /* Merge modes */
+       if (a->mode == b->mode || a->mode == o->mode)
+               result->mode = b->mode;
+       else {
+               /* must be the 100644/100755 case */
+               assert(S_ISREG(a->mode));
+               result->mode = a->mode;
+               clean = (b->mode == o->mode);
+               /*
+                * FIXME: If opt->priv->call_depth && !clean, then we really
+                * should not make result->mode match either a->mode or
+                * b->mode; that causes t6036 "check conflicting mode for
+                * regular file" to fail.  It would be best to use some other
+                * mode, but we'll confuse all kinds of stuff if we use one
+                * where S_ISREG(result->mode) isn't true, and if we use
+                * something like 0100666, then tree-walk.c's calls to
+                * canon_mode() will just normalize that to 100644 for us and
+                * thus not solve anything.
+                *
+                * Figure out if there's some kind of way we can work around
+                * this...
+                */
+       }
+
+       /*
+        * Trivial oid merge.
+        *
+        * Note: While one might assume that the next four lines would
+        * be unnecessary due to the fact that match_mask is often
+        * setup and already handled, renames don't always take care
+        * of that.
+        */
+       if (oideq(&a->oid, &b->oid) || oideq(&a->oid, &o->oid))
+               oidcpy(&result->oid, &b->oid);
+       else if (oideq(&b->oid, &o->oid))
+               oidcpy(&result->oid, &a->oid);
+
+       /* Remaining rules depend on file vs. submodule vs. symlink. */
+       else if (S_ISREG(a->mode)) {
+               mmbuffer_t result_buf;
+               int ret = 0, merge_status;
+               int two_way;
+
+               /*
+                * If 'o' is different type, treat it as null so we do a
+                * two-way merge.
+                */
+               two_way = ((S_IFMT & o->mode) != (S_IFMT & a->mode));
+
+               merge_status = merge_3way(opt, path,
+                                         two_way ? &null_oid : &o->oid,
+                                         &a->oid, &b->oid,
+                                         pathnames, extra_marker_size,
+                                         &result_buf);
+
+               if ((merge_status < 0) || !result_buf.ptr)
+                       ret = err(opt, _("Failed to execute internal merge"));
+
+               if (!ret &&
+                   write_object_file(result_buf.ptr, result_buf.size,
+                                     blob_type, &result->oid))
+                       ret = err(opt, _("Unable to add %s to database"),
+                                 path);
+
+               free(result_buf.ptr);
+               if (ret)
+                       return -1;
+               clean &= (merge_status == 0);
+               path_msg(opt, path, 1, _("Auto-merging %s"), path);
+       } else if (S_ISGITLINK(a->mode)) {
+               int two_way = ((S_IFMT & o->mode) != (S_IFMT & a->mode));
+               clean = merge_submodule(opt, pathnames[0],
+                                       two_way ? &null_oid : &o->oid,
+                                       &a->oid, &b->oid, &result->oid);
+               if (opt->priv->call_depth && two_way && !clean) {
+                       result->mode = o->mode;
+                       oidcpy(&result->oid, &o->oid);
+               }
+       } else if (S_ISLNK(a->mode)) {
+               if (opt->priv->call_depth) {
+                       clean = 0;
+                       result->mode = o->mode;
+                       oidcpy(&result->oid, &o->oid);
+               } else {
+                       switch (opt->recursive_variant) {
+                       case MERGE_VARIANT_NORMAL:
+                               clean = 0;
+                               oidcpy(&result->oid, &a->oid);
+                               break;
+                       case MERGE_VARIANT_OURS:
+                               oidcpy(&result->oid, &a->oid);
+                               break;
+                       case MERGE_VARIANT_THEIRS:
+                               oidcpy(&result->oid, &b->oid);
+                               break;
+                       }
+               }
+       } else
+               BUG("unsupported object type in the tree: %06o for %s",
+                   a->mode, path);
+
+       return clean;
 }
 
 /*** Function Grouping: functions related to detect_and_process_renames(), ***
@@ -1366,6 +1784,8 @@ static void process_entry(struct merge_options *opt,
                          struct conflict_info *ci,
                          struct directory_versions *dir_metadata)
 {
+       int df_file_index = 0;
+
        VERIFY_CI(ci);
        assert(ci->filemask >= 0 && ci->filemask <= 7);
        /* ci->match_mask == 7 was handled in collect_merge_info_callback() */
@@ -1380,14 +1800,108 @@ static void process_entry(struct merge_options *opt,
                assert(ci->df_conflict);
        }
 
-       if (ci->df_conflict) {
-               die("Not yet implemented.");
+       if (ci->df_conflict && ci->merged.result.mode == 0) {
+               int i;
+
+               /*
+                * directory no longer in the way, but we do have a file we
+                * need to place here so we need to clean away the "directory
+                * merges to nothing" result.
+                */
+               ci->df_conflict = 0;
+               assert(ci->filemask != 0);
+               ci->merged.clean = 0;
+               ci->merged.is_null = 0;
+               /* and we want to zero out any directory-related entries */
+               ci->match_mask = (ci->match_mask & ~ci->dirmask);
+               ci->dirmask = 0;
+               for (i = MERGE_BASE; i <= MERGE_SIDE2; i++) {
+                       if (ci->filemask & (1 << i))
+                               continue;
+                       ci->stages[i].mode = 0;
+                       oidcpy(&ci->stages[i].oid, &null_oid);
+               }
+       } else if (ci->df_conflict && ci->merged.result.mode != 0) {
+               /*
+                * This started out as a D/F conflict, and the entries in
+                * the competing directory were not removed by the merge as
+                * evidenced by write_completed_directory() writing a value
+                * to ci->merged.result.mode.
+                */
+               struct conflict_info *new_ci;
+               const char *branch;
+               const char *old_path = path;
+               int i;
+
+               assert(ci->merged.result.mode == S_IFDIR);
+
+               /*
+                * If filemask is 1, we can just ignore the file as having
+                * been deleted on both sides.  We do not want to overwrite
+                * ci->merged.result, since it stores the tree for all the
+                * files under it.
+                */
+               if (ci->filemask == 1) {
+                       ci->filemask = 0;
+                       return;
+               }
+
+               /*
+                * This file still exists on at least one side, and we want
+                * the directory to remain here, so we need to move this
+                * path to some new location.
+                */
+               new_ci = xcalloc(1, sizeof(*new_ci));
+               /* We don't really want new_ci->merged.result copied, but it'll
+                * be overwritten below so it doesn't matter.  We also don't
+                * want any directory mode/oid values copied, but we'll zero
+                * those out immediately.  We do want the rest of ci copied.
+                */
+               memcpy(new_ci, ci, sizeof(*ci));
+               new_ci->match_mask = (new_ci->match_mask & ~new_ci->dirmask);
+               new_ci->dirmask = 0;
+               for (i = MERGE_BASE; i <= MERGE_SIDE2; i++) {
+                       if (new_ci->filemask & (1 << i))
+                               continue;
+                       /* zero out any entries related to directories */
+                       new_ci->stages[i].mode = 0;
+                       oidcpy(&new_ci->stages[i].oid, &null_oid);
+               }
+
+               /*
+                * Find out which side this file came from; note that we
+                * cannot just use ci->filemask, because renames could cause
+                * the filemask to go back to 7.  So we use dirmask, then
+                * pick the opposite side's index.
+                */
+               df_file_index = (ci->dirmask & (1 << 1)) ? 2 : 1;
+               branch = (df_file_index == 1) ? opt->branch1 : opt->branch2;
+               path = unique_path(&opt->priv->paths, path, branch);
+               strmap_put(&opt->priv->paths, path, new_ci);
+
+               path_msg(opt, path, 0,
+                        _("CONFLICT (file/directory): directory in the way "
+                          "of %s from %s; moving it to %s instead."),
+                        old_path, branch, path);
+
+               /*
+                * Zero out the filemask for the old ci.  At this point, ci
+                * was just an entry for a directory, so we don't need to
+                * do anything more with it.
+                */
+               ci->filemask = 0;
+
+               /*
+                * Now note that we're working on the new entry (path was
+                * updated above.
+                */
+               ci = new_ci;
        }
 
        /*
         * NOTE: Below there is a long switch-like if-elseif-elseif... block
         *       which the code goes through even for the df_conflict cases
-        *       above.  Well, it will once we don't die-not-implemented above.
+        *       above.
         */
        if (ci->match_mask) {
                ci->merged.clean = 1;
@@ -1411,21 +1925,142 @@ static void process_entry(struct merge_options *opt,
        } else if (ci->filemask >= 6 &&
                   (S_IFMT & ci->stages[1].mode) !=
                   (S_IFMT & ci->stages[2].mode)) {
-               /*
-                * Two different items from (file/submodule/symlink)
-                */
-               die("Not yet implemented.");
+               /* Two different items from (file/submodule/symlink) */
+               if (opt->priv->call_depth) {
+                       /* Just use the version from the merge base */
+                       ci->merged.clean = 0;
+                       oidcpy(&ci->merged.result.oid, &ci->stages[0].oid);
+                       ci->merged.result.mode = ci->stages[0].mode;
+                       ci->merged.is_null = (ci->merged.result.mode == 0);
+               } else {
+                       /* Handle by renaming one or both to separate paths. */
+                       unsigned o_mode = ci->stages[0].mode;
+                       unsigned a_mode = ci->stages[1].mode;
+                       unsigned b_mode = ci->stages[2].mode;
+                       struct conflict_info *new_ci;
+                       const char *a_path = NULL, *b_path = NULL;
+                       int rename_a = 0, rename_b = 0;
+
+                       new_ci = xmalloc(sizeof(*new_ci));
+
+                       if (S_ISREG(a_mode))
+                               rename_a = 1;
+                       else if (S_ISREG(b_mode))
+                               rename_b = 1;
+                       else {
+                               rename_a = 1;
+                               rename_b = 1;
+                       }
+
+                       path_msg(opt, path, 0,
+                                _("CONFLICT (distinct types): %s had different "
+                                  "types on each side; renamed %s of them so "
+                                  "each can be recorded somewhere."),
+                                path,
+                                (rename_a && rename_b) ? _("both") : _("one"));
+
+                       ci->merged.clean = 0;
+                       memcpy(new_ci, ci, sizeof(*new_ci));
+
+                       /* Put b into new_ci, removing a from stages */
+                       new_ci->merged.result.mode = ci->stages[2].mode;
+                       oidcpy(&new_ci->merged.result.oid, &ci->stages[2].oid);
+                       new_ci->stages[1].mode = 0;
+                       oidcpy(&new_ci->stages[1].oid, &null_oid);
+                       new_ci->filemask = 5;
+                       if ((S_IFMT & b_mode) != (S_IFMT & o_mode)) {
+                               new_ci->stages[0].mode = 0;
+                               oidcpy(&new_ci->stages[0].oid, &null_oid);
+                               new_ci->filemask = 4;
+                       }
+
+                       /* Leave only a in ci, fixing stages. */
+                       ci->merged.result.mode = ci->stages[1].mode;
+                       oidcpy(&ci->merged.result.oid, &ci->stages[1].oid);
+                       ci->stages[2].mode = 0;
+                       oidcpy(&ci->stages[2].oid, &null_oid);
+                       ci->filemask = 3;
+                       if ((S_IFMT & a_mode) != (S_IFMT & o_mode)) {
+                               ci->stages[0].mode = 0;
+                               oidcpy(&ci->stages[0].oid, &null_oid);
+                               ci->filemask = 2;
+                       }
+
+                       /* Insert entries into opt->priv_paths */
+                       assert(rename_a || rename_b);
+                       if (rename_a) {
+                               a_path = unique_path(&opt->priv->paths,
+                                                    path, opt->branch1);
+                               strmap_put(&opt->priv->paths, a_path, ci);
+                       }
+
+                       if (rename_b)
+                               b_path = unique_path(&opt->priv->paths,
+                                                    path, opt->branch2);
+                       else
+                               b_path = path;
+                       strmap_put(&opt->priv->paths, b_path, new_ci);
+
+                       if (rename_a && rename_b) {
+                               strmap_remove(&opt->priv->paths, path, 0);
+                               /*
+                                * We removed path from opt->priv->paths.  path
+                                * will also eventually need to be freed, but
+                                * it may still be used by e.g.  ci->pathnames.
+                                * So, store it in another string-list for now.
+                                */
+                               string_list_append(&opt->priv->paths_to_free,
+                                                  path);
+                       }
+
+                       /*
+                        * Do special handling for b_path since process_entry()
+                        * won't be called on it specially.
+                        */
+                       strmap_put(&opt->priv->conflicted, b_path, new_ci);
+                       record_entry_for_tree(dir_metadata, b_path,
+                                             &new_ci->merged);
+
+                       /*
+                        * Remaining code for processing this entry should
+                        * think in terms of processing a_path.
+                        */
+                       if (a_path)
+                               path = a_path;
+               }
        } else if (ci->filemask >= 6) {
-               /*
-                * TODO: Needs a two-way or three-way content merge, but we're
-                * just being lazy and copying the version from HEAD and
-                * leaving it as conflicted.
-                */
-               ci->merged.clean = 0;
-               ci->merged.result.mode = ci->stages[1].mode;
-               oidcpy(&ci->merged.result.oid, &ci->stages[1].oid);
-               /* When we fix above, we'll call handle_content_merge() */
-               (void)handle_content_merge;
+               /* Need a two-way or three-way content merge */
+               struct version_info merged_file;
+               unsigned clean_merge;
+               struct version_info *o = &ci->stages[0];
+               struct version_info *a = &ci->stages[1];
+               struct version_info *b = &ci->stages[2];
+
+               clean_merge = handle_content_merge(opt, path, o, a, b,
+                                                  ci->pathnames,
+                                                  opt->priv->call_depth * 2,
+                                                  &merged_file);
+               ci->merged.clean = clean_merge &&
+                                  !ci->df_conflict && !ci->path_conflict;
+               ci->merged.result.mode = merged_file.mode;
+               ci->merged.is_null = (merged_file.mode == 0);
+               oidcpy(&ci->merged.result.oid, &merged_file.oid);
+               if (clean_merge && ci->df_conflict) {
+                       assert(df_file_index == 1 || df_file_index == 2);
+                       ci->filemask = 1 << df_file_index;
+                       ci->stages[df_file_index].mode = merged_file.mode;
+                       oidcpy(&ci->stages[df_file_index].oid, &merged_file.oid);
+               }
+               if (!clean_merge) {
+                       const char *reason = _("content");
+                       if (ci->filemask == 6)
+                               reason = _("add/add");
+                       if (S_ISGITLINK(merged_file.mode))
+                               reason = _("submodule");
+                       path_msg(opt, path, 0,
+                                _("CONFLICT (%s): Merge conflict in %s"),
+                                reason, path);
+               }
        } else if (ci->filemask == 3 || ci->filemask == 5) {
                /* Modify/delete */
                const char *modify_branch, *delete_branch;
index ee337df232a5025f474128a4e6331dd8eb167f92..fd994e18744cebd22e68fd1d969fb0f621d2362e 100644 (file)
@@ -1920,64 +1920,6 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname)
        return match_pattern(filter, refname);
 }
 
-static int qsort_strcmp(const void *va, const void *vb)
-{
-       const char *a = *(const char **)va;
-       const char *b = *(const char **)vb;
-
-       return strcmp(a, b);
-}
-
-static void find_longest_prefixes_1(struct string_list *out,
-                                 struct strbuf *prefix,
-                                 const char **patterns, size_t nr)
-{
-       size_t i;
-
-       for (i = 0; i < nr; i++) {
-               char c = patterns[i][prefix->len];
-               if (!c || is_glob_special(c)) {
-                       string_list_append(out, prefix->buf);
-                       return;
-               }
-       }
-
-       i = 0;
-       while (i < nr) {
-               size_t end;
-
-               /*
-               * Set "end" to the index of the element _after_ the last one
-               * in our group.
-               */
-               for (end = i + 1; end < nr; end++) {
-                       if (patterns[i][prefix->len] != patterns[end][prefix->len])
-                               break;
-               }
-
-               strbuf_addch(prefix, patterns[i][prefix->len]);
-               find_longest_prefixes_1(out, prefix, patterns + i, end - i);
-               strbuf_setlen(prefix, prefix->len - 1);
-
-               i = end;
-       }
-}
-
-static void find_longest_prefixes(struct string_list *out,
-                                 const char **patterns)
-{
-       struct strvec sorted = STRVEC_INIT;
-       struct strbuf prefix = STRBUF_INIT;
-
-       strvec_pushv(&sorted, patterns);
-       QSORT(sorted.v, sorted.nr, qsort_strcmp);
-
-       find_longest_prefixes_1(out, &prefix, sorted.v, sorted.nr);
-
-       strvec_clear(&sorted);
-       strbuf_release(&prefix);
-}
-
 /*
  * This is the same as for_each_fullref_in(), but it tries to iterate
  * only over the patterns we'll care about. Note that it _doesn't_ do a full
@@ -1988,10 +1930,6 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
                                       void *cb_data,
                                       int broken)
 {
-       struct string_list prefixes = STRING_LIST_INIT_DUP;
-       struct string_list_item *prefix;
-       int ret;
-
        if (!filter->match_as_path) {
                /*
                 * in this case, the patterns are applied after
@@ -2015,16 +1953,8 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
                return for_each_fullref_in("", cb, cb_data, broken);
        }
 
-       find_longest_prefixes(&prefixes, filter->name_patterns);
-
-       for_each_string_list_item(prefix, &prefixes) {
-               ret = for_each_fullref_in(prefix->string, cb, cb_data, broken);
-               if (ret)
-                       break;
-       }
-
-       string_list_clear(&prefixes, 0);
-       return ret;
+       return for_each_fullref_in_prefixes(NULL, filter->name_patterns,
+                                           cb, cb_data, broken);
 }
 
 /*
diff --git a/refs.c b/refs.c
index 03968ad787f470688ef64d4a1765bb5a0d8d051f..a665ed5e10acb65f821cfed9e6586c277bdceb48 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1564,6 +1564,93 @@ int for_each_rawref(each_ref_fn fn, void *cb_data)
        return refs_for_each_rawref(get_main_ref_store(the_repository), fn, cb_data);
 }
 
+static int qsort_strcmp(const void *va, const void *vb)
+{
+       const char *a = *(const char **)va;
+       const char *b = *(const char **)vb;
+
+       return strcmp(a, b);
+}
+
+static void find_longest_prefixes_1(struct string_list *out,
+                                 struct strbuf *prefix,
+                                 const char **patterns, size_t nr)
+{
+       size_t i;
+
+       for (i = 0; i < nr; i++) {
+               char c = patterns[i][prefix->len];
+               if (!c || is_glob_special(c)) {
+                       string_list_append(out, prefix->buf);
+                       return;
+               }
+       }
+
+       i = 0;
+       while (i < nr) {
+               size_t end;
+
+               /*
+               * Set "end" to the index of the element _after_ the last one
+               * in our group.
+               */
+               for (end = i + 1; end < nr; end++) {
+                       if (patterns[i][prefix->len] != patterns[end][prefix->len])
+                               break;
+               }
+
+               strbuf_addch(prefix, patterns[i][prefix->len]);
+               find_longest_prefixes_1(out, prefix, patterns + i, end - i);
+               strbuf_setlen(prefix, prefix->len - 1);
+
+               i = end;
+       }
+}
+
+static void find_longest_prefixes(struct string_list *out,
+                                 const char **patterns)
+{
+       struct strvec sorted = STRVEC_INIT;
+       struct strbuf prefix = STRBUF_INIT;
+
+       strvec_pushv(&sorted, patterns);
+       QSORT(sorted.v, sorted.nr, qsort_strcmp);
+
+       find_longest_prefixes_1(out, &prefix, sorted.v, sorted.nr);
+
+       strvec_clear(&sorted);
+       strbuf_release(&prefix);
+}
+
+int for_each_fullref_in_prefixes(const char *namespace,
+                                const char **patterns,
+                                each_ref_fn fn, void *cb_data,
+                                unsigned int broken)
+{
+       struct string_list prefixes = STRING_LIST_INIT_DUP;
+       struct string_list_item *prefix;
+       struct strbuf buf = STRBUF_INIT;
+       int ret = 0, namespace_len;
+
+       find_longest_prefixes(&prefixes, patterns);
+
+       if (namespace)
+               strbuf_addstr(&buf, namespace);
+       namespace_len = buf.len;
+
+       for_each_string_list_item(prefix, &prefixes) {
+               strbuf_addstr(&buf, prefix->string);
+               ret = for_each_fullref_in(buf.buf, fn, cb_data, broken);
+               if (ret)
+                       break;
+               strbuf_setlen(&buf, namespace_len);
+       }
+
+       string_list_clear(&prefixes, 0);
+       strbuf_release(&buf);
+       return ret;
+}
+
 static int refs_read_special_head(struct ref_store *ref_store,
                                  const char *refname, struct object_id *oid,
                                  struct strbuf *referent, unsigned int *type)
@@ -1916,31 +2003,14 @@ int refs_pack_refs(struct ref_store *refs, unsigned int flags)
        return refs->be->pack_refs(refs, flags);
 }
 
-int refs_peel_ref(struct ref_store *refs, const char *refname,
-                 struct object_id *oid)
+int peel_iterated_oid(const struct object_id *base, struct object_id *peeled)
 {
-       int flag;
-       struct object_id base;
-
-       if (current_ref_iter && current_ref_iter->refname == refname) {
-               struct object_id peeled;
-
-               if (ref_iterator_peel(current_ref_iter, &peeled))
-                       return -1;
-               oidcpy(oid, &peeled);
-               return 0;
-       }
-
-       if (refs_read_ref_full(refs, refname,
-                              RESOLVE_REF_READING, &base, &flag))
-               return -1;
+       if (current_ref_iter &&
+           (current_ref_iter->oid == base ||
+            oideq(current_ref_iter->oid, base)))
+               return ref_iterator_peel(current_ref_iter, peeled);
 
-       return peel_object(&base, oid);
-}
-
-int peel_ref(const char *refname, struct object_id *oid)
-{
-       return refs_peel_ref(get_main_ref_store(the_repository), refname, oid);
+       return peel_object(base, peeled);
 }
 
 int refs_create_symref(struct ref_store *refs,
diff --git a/refs.h b/refs.h
index ff05d2e9fe27cfc3486c42a1cfcf99941eaf9774..48970dfc7e0f0d6263a3faca9d92aa887a60d0e4 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -118,16 +118,16 @@ int is_branch(const char *refname);
 int refs_init_db(struct strbuf *err);
 
 /*
- * If refname is a non-symbolic reference that refers to a tag object,
- * and the tag can be (recursively) dereferenced to a non-tag object,
- * store the object ID of the referred-to object to oid and return 0.
- * If any of these conditions are not met, return a non-zero value.
- * Symbolic references are considered unpeelable, even if they
- * ultimately resolve to a peelable tag.
+ * Return the peeled value of the oid currently being iterated via
+ * for_each_ref(), etc. This is equivalent to calling:
+ *
+ *   peel_object(oid, &peeled);
+ *
+ * with the "oid" value given to the each_ref_fn callback, except
+ * that some ref storage may be able to answer the query without
+ * actually loading the object in memory.
  */
-int refs_peel_ref(struct ref_store *refs, const char *refname,
-                 struct object_id *oid);
-int peel_ref(const char *refname, struct object_id *oid);
+int peel_iterated_oid(const struct object_id *base, struct object_id *peeled);
 
 /**
  * Resolve refname in the nested "gitlink" repository in the specified
@@ -347,6 +347,15 @@ int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
 int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,
                        unsigned int broken);
 
+/**
+ * iterate all refs in "patterns" by partitioning patterns into disjoint sets
+ * and iterating the longest-common prefix of each set.
+ *
+ * callers should be prepared to ignore references that they did not ask for.
+ */
+int for_each_fullref_in_prefixes(const char *namespace, const char **patterns,
+                                each_ref_fn fn, void *cb_data,
+                                unsigned int broken);
 /**
  * iterate refs from the respective area.
  */
index 0b5c723140148ab08816b7bde9d45715ae8371d6..c7a0e8d3d78d630f9564bdf7e5abbd144bdabd33 100644 (file)
@@ -5,6 +5,7 @@
 #include "tree.h"
 #include "commit.h"
 #include "diff.h"
+#include "diff-merges.h"
 #include "refs.h"
 #include "revision.h"
 #include "repository.h"
@@ -1808,7 +1809,6 @@ void repo_init_revisions(struct repository *r,
 
        revs->repo = r;
        revs->abbrev = DEFAULT_ABBREV;
-       revs->ignore_merges = -1;
        revs->simplify_history = 1;
        revs->pruning.repo = r;
        revs->pruning.flags.recursive = 1;
@@ -2343,34 +2343,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->diff = 1;
                revs->diffopt.flags.recursive = 1;
                revs->diffopt.flags.tree_in_recursive = 1;
-       } else if (!strcmp(arg, "-m")) {
-               /*
-                * To "diff-index", "-m" means "match missing", and to the "log"
-                * family of commands, it means "show full diff for merges". Set
-                * both fields appropriately.
-                */
-               revs->ignore_merges = 0;
-               revs->match_missing = 1;
-       } else if ((argcount = parse_long_opt("diff-merges", argv, &optarg))) {
-               if (!strcmp(optarg, "off")) {
-                       revs->ignore_merges = 1;
-               } else {
-                       die(_("unknown value for --diff-merges: %s"), optarg);
-               }
+       } else if ((argcount = diff_merges_parse_opts(revs, argv))) {
                return argcount;
-       } else if (!strcmp(arg, "--no-diff-merges")) {
-               revs->ignore_merges = 1;
-       } else if (!strcmp(arg, "-c")) {
-               revs->diff = 1;
-               revs->dense_combined_merges = 0;
-               revs->combine_merges = 1;
-       } else if (!strcmp(arg, "--combined-all-paths")) {
-               revs->diff = 1;
-               revs->combined_all_paths = 1;
-       } else if (!strcmp(arg, "--cc")) {
-               revs->diff = 1;
-               revs->dense_combined_merges = 1;
-               revs->combine_merges = 1;
        } else if (!strcmp(arg, "-v")) {
                revs->verbose_header = 1;
        } else if (!strcmp(arg, "--pretty")) {
@@ -2867,12 +2841,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                        copy_pathspec(&revs->diffopt.pathspec,
                                      &revs->prune_data);
        }
-       if (revs->combine_merges && revs->ignore_merges < 0)
-               revs->ignore_merges = 0;
-       if (revs->ignore_merges < 0)
-               revs->ignore_merges = 1;
-       if (revs->combined_all_paths && !revs->combine_merges)
-               die("--combined-all-paths makes no sense without -c or --cc");
+
+       diff_merges_setup_revs(revs);
 
        revs->diffopt.abbrev = revs->abbrev;
 
index 086ff10280db586f2c714f989b10e4e8b082cf03..e6be3c845e66eb0733531adfa4bc6845417003a9 100644 (file)
@@ -191,11 +191,16 @@ struct rev_info {
                        match_missing:1,
                        no_commit_id:1,
                        verbose_header:1,
+                       always_show_header:1,
+                       /* Diff-merge flags */
+                       explicit_diff_merges: 1,
+                       merges_need_diff: 1,
+                       separate_merges: 1,
                        combine_merges:1,
                        combined_all_paths:1,
+                       combined_imply_patch:1,
                        dense_combined_merges:1,
-                       always_show_header:1;
-       int             ignore_merges:2;
+                       first_parent_merges:1;
 
        /* Format info */
        int             show_notes;
index 6472b38bde448cd256697b0b3cbd962cdf3da333..d08414a92e734c8b0768b4dc142be8508d522d0d 100644 (file)
@@ -126,8 +126,15 @@ struct child_process {
         */
        unsigned silent_exec_failure:1;
 
-       unsigned stdout_to_stderr:1;
+       /**
+        * Run the command from argv[0] using a shell (but note that we may
+        * still optimize out the shell call if the command contains no
+        * metacharacters). Note that further arguments to the command in
+        * argv[1], etc, do not need to be shell-quoted.
+        */
        unsigned use_shell:1;
+
+       unsigned stdout_to_stderr:1;
        unsigned clean_on_exit:1;
        unsigned wait_after_clean:1;
        void (*clean_on_exit_handler)(struct child_process *process);
index ee5d2d4cf81972bfc82c9d74af7e7b5341f5d24b..29ce89090d8554a1766c31e9b90cbb387d45c6f7 100644 (file)
@@ -483,12 +483,12 @@ test_expect_success 'setup -L :funcname with userdiff driver' '
        echo "fortran-* diff=fortran" >.gitattributes &&
        fortran_file=fortran-external-function &&
        orig_file="$TEST_DIRECTORY/t4018/$fortran_file" &&
-       cp $orig_file . &&
-       git add $fortran_file &&
+       cp "$orig_file" . &&
+       git add "$fortran_file" &&
        GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" \
        git commit -m "add fortran file" &&
-       sed -e "s/ChangeMe/IWasChanged/" <"$orig_file" >$fortran_file &&
-       git add $fortran_file &&
+       sed -e "s/ChangeMe/IWasChanged/" <"$orig_file" >"$fortran_file" &&
+       git add "$fortran_file" &&
        GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" \
        git commit -m "change fortran file"
 '
index 759e69dc549ea1d87343d9890b28066515858aea..bba5f841c6ab00362aa81313fcf3ef3fa7ddfe9d 100644 (file)
@@ -72,18 +72,6 @@ static int cmd_pack_refs(struct ref_store *refs, const char **argv)
        return refs_pack_refs(refs, flags);
 }
 
-static int cmd_peel_ref(struct ref_store *refs, const char **argv)
-{
-       const char *refname = notnull(*argv++, "refname");
-       struct object_id oid;
-       int ret;
-
-       ret = refs_peel_ref(refs, refname, &oid);
-       if (!ret)
-               puts(oid_to_hex(&oid));
-       return ret;
-}
-
 static int cmd_create_symref(struct ref_store *refs, const char **argv)
 {
        const char *refname = notnull(*argv++, "refname");
@@ -255,7 +243,6 @@ struct command {
 
 static struct command commands[] = {
        { "pack-refs", cmd_pack_refs },
-       { "peel-ref", cmd_peel_ref },
        { "create-symref", cmd_create_symref },
        { "delete-refs", cmd_delete_refs },
        { "rename-ref", cmd_rename_ref },
index f4c2ab0584707d7930476566882d3124b18e52e5..ce0c42cc9f72d6ff610c5fa0608353beb64fa3df 100755 (executable)
@@ -21,10 +21,14 @@ repack_into_n () {
        mkdir staging &&
 
        git rev-list --first-parent HEAD |
-       sed -n '1~5p' |
-       head -n "$1" |
-       perl -e 'print reverse <>' \
-       >pushes
+       perl -e '
+               my $n = shift;
+               while (<>) {
+                       last unless @commits < $n;
+                       push @commits, $_ if $. % 5 == 1;
+               }
+               print reverse @commits;
+       ' "$1" >pushes
 
        # create base packfile
        head -n 1 pushes |
index 602e21957b5aa87854f73bedaddf2e8c4221181c..a237d9880ead84fac4e36c1a34082452d06f0fff 100755 (executable)
@@ -17,13 +17,6 @@ test_expect_success 'pack_refs(PACK_REFS_ALL | PACK_REFS_PRUNE)' '
        N=`find .git/refs -type f | wc -l`
 '
 
-test_expect_success 'peel_ref(new-tag)' '
-       git rev-parse HEAD >expected &&
-       git tag -a -m new-tag new-tag HEAD &&
-       $RUN peel-ref refs/tags/new-tag >actual &&
-       test_cmp expected actual
-'
-
 test_expect_success 'create_symref(FOO, refs/heads/main)' '
        $RUN create-symref FOO refs/heads/main nothing &&
        echo refs/heads/main >expected &&
@@ -32,6 +25,7 @@ test_expect_success 'create_symref(FOO, refs/heads/main)' '
 '
 
 test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' '
+       git tag -a -m new-tag new-tag HEAD &&
        git rev-parse FOO -- &&
        git rev-parse refs/tags/new-tag -- &&
        m=$(git rev-parse main) &&
index a68c8f11a24664b9995b12d4f75073ba5eabb4aa..0a87058971ee3f601f928a1d3078c25ac0a60ce2 100755 (executable)
@@ -14,7 +14,8 @@ test_expect_success 'setup' '
        (
                cd sub &&
                test_commit first &&
-               git checkout -b new-main
+               git checkout -b new-main &&
+               git tag -a -m new-tag new-tag HEAD
        )
 '
 
@@ -22,13 +23,6 @@ test_expect_success 'pack_refs() not allowed' '
        test_must_fail $RUN pack-refs 3
 '
 
-test_expect_success 'peel_ref(new-tag)' '
-       git -C sub rev-parse HEAD >expected &&
-       git -C sub tag -a -m new-tag new-tag HEAD &&
-       $RUN peel-ref refs/tags/new-tag >actual &&
-       test_cmp expected actual
-'
-
 test_expect_success 'create_symref() not allowed' '
        test_must_fail $RUN create-symref FOO refs/heads/main nothing
 '
diff --git a/t/t3012-ls-files-dedup.sh b/t/t3012-ls-files-dedup.sh
new file mode 100755 (executable)
index 0000000..2682b1f
--- /dev/null
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+test_description='git ls-files --deduplicate test'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       >a.txt &&
+       >b.txt &&
+       >delete.txt &&
+       git add a.txt b.txt delete.txt &&
+       git commit -m base &&
+       echo a >a.txt &&
+       echo b >b.txt &&
+       echo delete >delete.txt &&
+       git add a.txt b.txt delete.txt &&
+       git commit -m tip &&
+       git tag tip &&
+       git reset --hard HEAD^ &&
+       echo change >a.txt &&
+       git commit -a -m side &&
+       git tag side
+'
+
+test_expect_success 'git ls-files --deduplicate to show unique unmerged path' '
+       test_must_fail git merge tip &&
+       git ls-files --deduplicate >actual &&
+       cat >expect <<-\EOF &&
+       a.txt
+       b.txt
+       delete.txt
+       EOF
+       test_cmp expect actual &&
+       git merge --abort
+'
+
+test_expect_success 'git ls-files -d -m --deduplicate with different display options' '
+       git reset --hard side &&
+       test_must_fail git merge tip &&
+       rm delete.txt &&
+       git ls-files -d -m --deduplicate >actual &&
+       cat >expect <<-\EOF &&
+       a.txt
+       delete.txt
+       EOF
+       test_cmp expect actual &&
+       git ls-files -d -m -t --deduplicate >actual &&
+       cat >expect <<-\EOF &&
+       C a.txt
+       C a.txt
+       C a.txt
+       R delete.txt
+       C delete.txt
+       EOF
+       test_cmp expect actual &&
+       git ls-files -d -m -c --deduplicate >actual &&
+       cat >expect <<-\EOF &&
+       a.txt
+       b.txt
+       delete.txt
+       EOF
+       test_cmp expect actual &&
+       git merge --abort
+'
+
+test_done
index 45f68ebcdb504efe2230a188849c8b94129d33f4..ce6aa3914fe74f037eb2451a88e36803594b0e78 100755 (executable)
@@ -178,6 +178,7 @@ process_diffs () {
 V=$(git version | sed -e 's/^git version //' -e 's/\./\\./g')
 while read magic cmd
 do
+       status=success
        case "$magic" in
        '' | '#'*)
                continue ;;
@@ -186,6 +187,10 @@ do
                label="$magic-$cmd"
                case "$magic" in
                noellipses) ;;
+               failure)
+                       status=failure
+                       magic=
+                       label="$cmd" ;;
                *)
                        BUG "unknown magic $magic" ;;
                esac ;;
@@ -198,7 +203,7 @@ do
        expect="$TEST_DIRECTORY/t4013/diff.$test"
        actual="$pfx-diff.$test"
 
-       test_expect_success "git $cmd # magic is ${magic:-(not used)}" '
+       test_expect_$status "git $cmd # magic is ${magic:-(not used)}" '
                {
                        echo "$ git $cmd"
                        case "$magic" in
@@ -326,8 +331,12 @@ log --no-diff-merges -p --first-parent master
 log --diff-merges=off -p --first-parent master
 log --first-parent --diff-merges=off -p master
 log -p --first-parent master
+log -p --diff-merges=first-parent master
+log --diff-merges=first-parent master
 log -m -p --first-parent master
 log -m -p master
+log --cc -m -p master
+log -c -m -p master
 log -SF master
 log -S F master
 log -SF -p master
diff --git a/t/t4013/diff.log_--cc_-m_-p_master b/t/t4013/diff.log_--cc_-m_-p_master
new file mode 100644 (file)
index 0000000..7c217cf
--- /dev/null
@@ -0,0 +1,200 @@
+$ git log --cc -m -p master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index cead32e..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -4,3 +4,5 @@ C
+ D
+ E
+ F
++1
++2
+diff --git a/file0 b/file0
+index b414108..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -4,3 +4,6 @@
+ 4
+ 5
+ 6
++A
++B
++C
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index 7289e35..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,4 +1,8 @@
+ A
+ B
++C
++D
++E
++F
+ 1
+ 2
+diff --git a/file0 b/file0
+index f4615da..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -1,6 +1,9 @@
+ 1
+ 2
+ 3
++4
++5
++6
+ A
+ B
+ C
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+diff --git a/file3 b/file3
+deleted file mode 100644
+index 7289e35..0000000
+--- a/file3
++++ /dev/null
+@@ -1,4 +0,0 @@
+-A
+-B
+-1
+-2
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4013/diff.log_--diff-merges=first-parent_master b/t/t4013/diff.log_--diff-merges=first-parent_master
new file mode 100644 (file)
index 0000000..fa63a55
--- /dev/null
@@ -0,0 +1,56 @@
+$ git log --diff-merges=first-parent master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index cead32e..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -4,3 +4,5 @@ C
+ D
+ E
+ F
++1
++2
+diff --git a/file0 b/file0
+index b414108..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -4,3 +4,6 @@
+ 4
+ 5
+ 6
++A
++B
++C
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4013/diff.log_-c_-m_-p_master b/t/t4013/diff.log_-c_-m_-p_master
new file mode 100644 (file)
index 0000000..b660f3d
--- /dev/null
@@ -0,0 +1,200 @@
+$ git log -c -m -p master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index cead32e..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -4,3 +4,5 @@ C
+ D
+ E
+ F
++1
++2
+diff --git a/file0 b/file0
+index b414108..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -4,3 +4,6 @@
+ 4
+ 5
+ 6
++A
++B
++C
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index 7289e35..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,4 +1,8 @@
+ A
+ B
++C
++D
++E
++F
+ 1
+ 2
+diff --git a/file0 b/file0
+index f4615da..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -1,6 +1,9 @@
+ 1
+ 2
+ 3
++4
++5
++6
+ A
+ B
+ C
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+diff --git a/file3 b/file3
+deleted file mode 100644
+index 7289e35..0000000
+--- a/file3
++++ /dev/null
+@@ -1,4 +0,0 @@
+-A
+-B
+-1
+-2
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4013/diff.log_-p_--diff-merges=first-parent_master b/t/t4013/diff.log_-p_--diff-merges=first-parent_master
new file mode 100644 (file)
index 0000000..9538a27
--- /dev/null
@@ -0,0 +1,137 @@
+$ git log -p --diff-merges=first-parent master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index cead32e..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -4,3 +4,5 @@ C
+ D
+ E
+ F
++1
++2
+diff --git a/file0 b/file0
+index b414108..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -4,3 +4,6 @@
+ 4
+ 5
+ 6
++A
++B
++C
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
index 16faa0781373bb3769ce9f0fa0a09be8c2b2ed81..7948ec392b3599a3884d97ae21f5deece84e638c 100755 (executable)
@@ -33,7 +33,7 @@ test_expect_success 'reset --hard should restore unmerged ones' '
 
 '
 
-test_expect_success 'reset --hard did not corrupt index or cached-tree' '
+test_expect_success 'reset --hard did not corrupt index or cache-tree' '
 
        T=$(git write-tree) &&
        rm -f .git/index &&
index 1074009cc05b29df866a0aee701470b59e35270c..78ccf4b33f87c98d8b795ea7e31b6c29fc8837fb 100755 (executable)
@@ -149,7 +149,31 @@ test_expect_success 'prefetch multiple remotes' '
        git log prefetch/remote2/two &&
        git fetch --all &&
        test_cmp_rev refs/remotes/remote1/one refs/prefetch/remote1/one &&
-       test_cmp_rev refs/remotes/remote2/two refs/prefetch/remote2/two
+       test_cmp_rev refs/remotes/remote2/two refs/prefetch/remote2/two &&
+
+       test_cmp_config refs/prefetch/ log.excludedecoration &&
+       git log --oneline --decorate --all >log &&
+       ! grep "prefetch" log
+'
+
+test_expect_success 'prefetch and existing log.excludeDecoration values' '
+       git config --unset-all log.excludeDecoration &&
+       git config log.excludeDecoration refs/remotes/remote1/ &&
+       git maintenance run --task=prefetch &&
+
+       git config --get-all log.excludeDecoration >out &&
+       grep refs/remotes/remote1/ out &&
+       grep refs/prefetch/ out &&
+
+       git log --oneline --decorate --all >log &&
+       ! grep "prefetch" log &&
+       ! grep "remote1" log &&
+       grep "remote2" log &&
+
+       # a second run does not change the config
+       git maintenance run --task=prefetch &&
+       git log --oneline --decorate --all >log2 &&
+       test_cmp log log2
 '
 
 test_expect_success 'loose-objects task' '
@@ -232,6 +256,13 @@ test_expect_success 'incremental-repack task' '
        HEAD
        ^HEAD~1
        EOF
+
+       # Delete refs that have not been repacked in these packs.
+       git for-each-ref --format="delete %(refname)" \
+               refs/prefetch refs/tags >refs &&
+       git update-ref --stdin <refs &&
+
+       # Replace the object directory with this pack layout.
        rm -f $packDir/pack-* &&
        rm -f $packDir/loose-* &&
        ls $packDir/*.pack >packs-before &&
index 0160294712b4c91a02f950af377d8fdf6fa4491b..2d6226d5f1883669e9fb775914512dea2ed0dd7a 100644 (file)
@@ -4,6 +4,7 @@
 #include "object-store.h"
 #include "tree.h"
 #include "pathspec.h"
+#include "json-writer.h"
 
 static const char *get_mode(const char *str, unsigned int *modep)
 {
@@ -167,6 +168,25 @@ int tree_entry_gently(struct tree_desc *desc, struct name_entry *entry)
        return 1;
 }
 
+static int traverse_trees_atexit_registered;
+static int traverse_trees_count;
+static int traverse_trees_cur_depth;
+static int traverse_trees_max_depth;
+
+static void trace2_traverse_trees_statistics_atexit(void)
+{
+       struct json_writer jw = JSON_WRITER_INIT;
+
+       jw_object_begin(&jw, 0);
+       jw_object_intmax(&jw, "traverse_trees_count", traverse_trees_count);
+       jw_object_intmax(&jw, "traverse_trees_max_depth", traverse_trees_max_depth);
+       jw_end(&jw);
+
+       trace2_data_json("traverse_trees", the_repository, "statistics", &jw);
+
+       jw_release(&jw);
+}
+
 void setup_traverse_info(struct traverse_info *info, const char *base)
 {
        size_t pathlen = strlen(base);
@@ -180,6 +200,11 @@ void setup_traverse_info(struct traverse_info *info, const char *base)
        info->namelen = pathlen;
        if (pathlen)
                info->prev = &dummy;
+
+       if (trace2_is_enabled() && !traverse_trees_atexit_registered) {
+               atexit(trace2_traverse_trees_statistics_atexit);
+               traverse_trees_atexit_registered = 1;
+       }
 }
 
 char *make_traverse_path(char *path, size_t pathlen,
@@ -416,6 +441,12 @@ int traverse_trees(struct index_state *istate,
        int interesting = 1;
        char *traverse_path;
 
+       traverse_trees_count++;
+       traverse_trees_cur_depth++;
+
+       if (traverse_trees_cur_depth > traverse_trees_max_depth)
+               traverse_trees_max_depth = traverse_trees_cur_depth;
+
        if (n >= ARRAY_SIZE(entry))
                BUG("traverse_trees() called with too many trees (%d)", n);
 
@@ -515,6 +546,8 @@ int traverse_trees(struct index_state *istate,
        free(traverse_path);
        info->traverse_path = NULL;
        strbuf_release(&base);
+
+       traverse_trees_cur_depth--;
        return error;
 }
 
index 323280dd48b2cb1e7f6e113f58c12d9332574ec4..af6e9b9c2fd558dc625ddd8f63b54c9e8d489e56 100644 (file)
@@ -1580,6 +1580,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES);
 
        trace_performance_enter();
+       trace2_region_enter("unpack_trees", "unpack_trees", the_repository);
+
        if (!core_apply_sparse_checkout || !o->update)
                o->skip_sparse_checkout = 1;
        if (!o->skip_sparse_checkout && !o->pl) {
@@ -1653,7 +1655,9 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                }
 
                trace_performance_enter();
+               trace2_region_enter("unpack_trees", "traverse_trees", the_repository);
                ret = traverse_trees(o->src_index, len, t, &info);
+               trace2_region_leave("unpack_trees", "traverse_trees", the_repository);
                trace_performance_leave("traverse_trees");
                if (ret < 0)
                        goto return_failed;
@@ -1741,6 +1745,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 done:
        if (free_pattern_list)
                clear_pattern_list(&pl);
+       trace2_region_leave("unpack_trees", "unpack_trees", the_repository);
        trace_performance_leave("unpack_trees");
        return ret;
 
index 3b66bf92ba8ec074aea13e21d630cbce4a605aea..4ab55ce2b594ded6f46a90efc31f87aa6f435b2d 100644 (file)
@@ -1232,7 +1232,7 @@ static int send_ref(const char *refname, const struct object_id *oid,
                packet_write_fmt(1, "%s %s\n", oid_to_hex(oid), refname_nons);
        }
        capabilities = NULL;
-       if (!peel_ref(refname, &peeled))
+       if (!peel_iterated_oid(oid, &peeled))
                packet_write_fmt(1, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
        return 0;
 }