]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'jc/cat-file-batch-default-format-optim'
authorJunio C Hamano <gitster@pobox.com>
Wed, 23 Mar 2022 21:09:31 +0000 (14:09 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 23 Mar 2022 21:09:31 +0000 (14:09 -0700)
Optimize away strbuf_expand() call with a hardcoded formatting logic
specific for the default format in the --batch and --batch-check
options of "git cat-file".

* jc/cat-file-batch-default-format-optim:
  cat-file: skip expanding default format

240 files changed:
Documentation/CodingGuidelines
Documentation/Makefile
Documentation/MyFirstObjectWalk.txt
Documentation/RelNotes/2.36.0.txt
Documentation/config.txt
Documentation/config/repack.txt
Documentation/config/sparse.txt [new file with mode: 0644]
Documentation/git-bundle.txt
Documentation/git-cat-file.txt
Documentation/git-help.txt
Documentation/git-index-pack.txt
Documentation/git-maintenance.txt
Documentation/git-read-tree.txt
Documentation/git-remote.txt
Documentation/git-sparse-checkout.txt
Documentation/git-update-index.txt
Documentation/gitattributes.txt
Documentation/technical/bundle-format.txt
Documentation/technical/commit-graph-format.txt
Documentation/technical/reftable.txt
Makefile
add-interactive.c
apply.c
apply.h
attr.c
attr.h
banned.h
block-sha1/sha1.c
builtin/bundle.c
builtin/cat-file.c
builtin/checkout.c
builtin/clone.c
builtin/commit-graph.c
builtin/commit.c
builtin/config.c
builtin/fast-export.c
builtin/fast-import.c
builtin/fetch.c
builtin/gc.c
builtin/grep.c
builtin/hash-object.c
builtin/help.c
builtin/index-pack.c
builtin/merge-base.c
builtin/merge-recursive.c
builtin/merge.c
builtin/mktag.c
builtin/mktree.c
builtin/name-rev.c
builtin/notes.c
builtin/pack-objects.c
builtin/read-tree.c
builtin/receive-pack.c
builtin/reflog.c
builtin/remote.c
builtin/repack.c
builtin/replace.c
builtin/rev-list.c
builtin/shortlog.c
builtin/sparse-checkout.c
builtin/stash.c
builtin/submodule--helper.c
builtin/tag.c
builtin/unpack-objects.c
bulk-checkin.c
bundle.c
bundle.h
cache-tree.c
cache.h
commit-graph.c
commit-graph.h
commit.c
commit.h
compat/mingw.c
compat/terminal.c
compat/terminal.h
config.c
config.mak.uname
contrib/scalar/Makefile
contrib/scalar/t/Makefile
convert.c
credential.c
diff.c
diffcore-rename.c
dir.c
environment.c
git-compat-util.h
git-submodule.sh
gitweb/gitweb.perl
gpg-interface.c
help.c
help.h
hook.c
hook.h
http-push.c
list-objects-filter-options.c
list-objects-filter-options.h
list-objects.c
list-objects.h
log-tree.c
mailmap.c
match-trees.c
merge-ort.c
merge-recursive.c
notes-cache.c
notes.c
object-file.c
object-store.h
object.c
object.h
pack-bitmap.c
pack-bitmap.h
pack-check.c
path.h
range-diff.c
reachable.c
read-cache.c
reflog.c [new file with mode: 0644]
reflog.h [new file with mode: 0644]
refs.c
refs.h
refs/debug.c
refs/files-backend.c
refs/packed-backend.c
refs/refs-internal.h
reftable/block.c
reftable/block_test.c
reftable/reader.c
reftable/readwrite_test.c
reftable/reftable-writer.h
reftable/writer.c
remote-curl.c
remote.c
repository.c
repository.h
revision.c
revision.h
sequencer.c
shared.mak [new file with mode: 0644]
sparse-index.c
sparse-index.h
strbuf.c
string-list.h
t/Makefile
t/helper/test-read-graph.c
t/helper/test-run-command.c
t/interop/Makefile
t/lib-commit-graph.sh [new file with mode: 0755]
t/lib-gpg.sh
t/perf/Makefile
t/perf/p2000-sparse-operations.sh
t/t0000-basic.sh
t/t0002-gitfile.sh
t/t0003-attributes.sh
t/t0012-help.sh
t/t0022-crlf-rename.sh
t/t0025-crlf-renormalize.sh
t/t0027-auto-crlf.sh
t/t0030-stripspace.sh
t/t0050-filesystem.sh
t/t0410-partial-clone.sh
t/t1001-read-tree-m-2way.sh
t/t1002-read-tree-m-u-2way.sh
t/t1003-read-tree-prefix.sh
t/t1006-cat-file.sh
t/t1011-read-tree-sparse-checkout.sh
t/t1090-sparse-checkout-scope.sh
t/t1092-sparse-checkout-compatibility.sh
t/t1410-reflog.sh
t/t1503-rev-parse-verify.sh
t/t2012-checkout-last.sh
t/t2200-add-update.sh
t/t3302-notes-index-expensive.sh
t/t3303-notes-subtrees.sh
t/t3305-notes-fanout.sh
t/t3705-add-sparse-checkout.sh
t/t3903-stash.sh
t/t4018/kotlin-class [new file with mode: 0644]
t/t4018/kotlin-enum-class [new file with mode: 0644]
t/t4018/kotlin-fun [new file with mode: 0644]
t/t4018/kotlin-inheritace-class [new file with mode: 0644]
t/t4018/kotlin-inline-class [new file with mode: 0644]
t/t4018/kotlin-interface [new file with mode: 0644]
t/t4018/kotlin-nested-fun [new file with mode: 0644]
t/t4018/kotlin-public-class [new file with mode: 0644]
t/t4018/kotlin-sealed-class [new file with mode: 0644]
t/t4020-diff-external.sh
t/t4027-diff-submodule.sh
t/t4034-diff-words.sh
t/t4034/kotlin/expect [new file with mode: 0644]
t/t4034/kotlin/post [new file with mode: 0644]
t/t4034/kotlin/pre [new file with mode: 0644]
t/t4123-apply-shrink.sh
t/t4128-apply-root.sh
t/t4202-log.sh
t/t4216-log-bloom.sh
t/t5300-pack-object.sh
t/t5318-commit-graph.sh
t/t5324-split-commit-graph.sh
t/t5328-commit-graph-64bit-time.sh [new file with mode: 0755]
t/t5503-tagfollow.sh
t/t5505-remote.sh
t/t5510-fetch.sh
t/t6005-rev-list-count.sh
t/t6012-rev-list-simplify.sh
t/t6020-bundle-misc.sh
t/t6102-rev-list-unexpected-objects.sh
t/t6120-describe.sh
t/t6407-merge-binary.sh
t/t6423-merge-rename-directories.sh
t/t6428-merge-conflicts-sparse.sh
t/t7012-skip-worktree-writing.sh
t/t7063-status-untracked-cache.sh
t/t7103-reset-bare.sh
t/t7406-submodule-update.sh
t/t7408-submodule-reference.sh
t/t7519-status-fsmonitor.sh
t/t7700-repack.sh
t/t7812-grep-icase-non-ascii.sh
t/t7817-grep-sparse-checkout.sh
t/t9502-gitweb-standalone-parse-output.sh
t/test-lib.sh
templates/Makefile
trace.c
trace.h
trace2.c
trace2.h
transport.c
tree-walk.c
unpack-trees.c
upload-pack.c
urlmatch.c
urlmatch.h
usage.c
userdiff.c
wt-status.c
xdiff/xdiffi.c
xdiff/xhistogram.c
xdiff/xmerge.c
xdiff/xpatience.c

index c37c43186ea80e2f11cd1b66c8ed5eaea1c904e3..1a7bc4591cd4b924e066eeecd9c89534d11f6eb5 100644 (file)
@@ -217,6 +217,9 @@ For C programs:
    . since mid 2017 with 512f41cf, we have been using designated
      initializers for array (e.g. "int array[10] = { [5] = 2 }").
 
+   . since early 2021 with 765dc168882, we have been using variadic
+     macros, mostly for printf-like trace and debug macros.
+
    These used to be forbidden, but we have not heard any breakage
    report, and they are assumed to be safe.
 
index ed656db2ae90c1f5e0498867ec170395f6898b1f..1eb9192dae825fdbe40e6149a0f73359c1cde814 100644 (file)
@@ -1,3 +1,6 @@
+# Import tree-wide shared Makefile behavior and libraries
+include ../shared.mak
+
 # Guard against environment variables
 MAN1_TXT =
 MAN5_TXT =
@@ -215,38 +218,6 @@ DEFAULT_EDITOR_SQ = $(subst ','\'',$(DEFAULT_EDITOR))
 ASCIIDOC_EXTRA += -a 'git-default-editor=$(DEFAULT_EDITOR_SQ)'
 endif
 
-QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
-QUIET_SUBDIR1  =
-
-ifneq ($(findstring $(MAKEFLAGS),w),w)
-PRINT_DIR = --no-print-directory
-else # "make -w"
-NO_SUBDIR = :
-endif
-
-ifneq ($(findstring $(MAKEFLAGS),s),s)
-ifndef V
-       QUIET           = @
-       QUIET_ASCIIDOC  = @echo '   ' ASCIIDOC $@;
-       QUIET_XMLTO     = @echo '   ' XMLTO $@;
-       QUIET_DB2TEXI   = @echo '   ' DB2TEXI $@;
-       QUIET_MAKEINFO  = @echo '   ' MAKEINFO $@;
-       QUIET_DBLATEX   = @echo '   ' DBLATEX $@;
-       QUIET_XSLTPROC  = @echo '   ' XSLTPROC $@;
-       QUIET_GEN       = @echo '   ' GEN $@;
-       QUIET_STDERR    = 2> /dev/null
-       QUIET_SUBDIR0   = +@subdir=
-       QUIET_SUBDIR1   = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
-                         $(MAKE) $(PRINT_DIR) -C $$subdir
-
-       QUIET_LINT_GITLINK      = @echo '   ' LINT GITLINK $<;
-       QUIET_LINT_MANSEC       = @echo '   ' LINT MAN SEC $<;
-       QUIET_LINT_MANEND       = @echo '   ' LINT MAN END $<;
-
-       export V
-endif
-endif
-
 all: html man
 
 html: $(DOC_HTML)
@@ -463,25 +434,11 @@ quick-install-html: require-htmlrepo
 print-man1:
        @for i in $(MAN1_TXT); do echo $$i; done
 
-## Lint: Common
-.build:
-       $(QUIET)mkdir $@
-.build/lint-docs: | .build
-       $(QUIET)mkdir $@
-
 ## Lint: gitlink
-.build/lint-docs/gitlink: | .build/lint-docs
-       $(QUIET)mkdir $@
-.build/lint-docs/gitlink/howto: | .build/lint-docs/gitlink
-       $(QUIET)mkdir $@
-.build/lint-docs/gitlink/config: | .build/lint-docs/gitlink
-       $(QUIET)mkdir $@
 LINT_DOCS_GITLINK = $(patsubst %.txt,.build/lint-docs/gitlink/%.ok,$(HOWTO_TXT) $(DOC_DEP_TXT))
-$(LINT_DOCS_GITLINK): | .build/lint-docs/gitlink
-$(LINT_DOCS_GITLINK): | .build/lint-docs/gitlink/howto
-$(LINT_DOCS_GITLINK): | .build/lint-docs/gitlink/config
 $(LINT_DOCS_GITLINK): lint-gitlink.perl
 $(LINT_DOCS_GITLINK): .build/lint-docs/gitlink/%.ok: %.txt
+       $(call mkdir_p_parent_template)
        $(QUIET_LINT_GITLINK)$(PERL_PATH) lint-gitlink.perl \
                $< \
                $(HOWTO_TXT) $(DOC_DEP_TXT) \
@@ -492,23 +449,18 @@ $(LINT_DOCS_GITLINK): .build/lint-docs/gitlink/%.ok: %.txt
 lint-docs-gitlink: $(LINT_DOCS_GITLINK)
 
 ## Lint: man-end-blurb
-.build/lint-docs/man-end-blurb: | .build/lint-docs
-       $(QUIET)mkdir $@
 LINT_DOCS_MAN_END_BLURB = $(patsubst %.txt,.build/lint-docs/man-end-blurb/%.ok,$(MAN_TXT))
-$(LINT_DOCS_MAN_END_BLURB): | .build/lint-docs/man-end-blurb
 $(LINT_DOCS_MAN_END_BLURB): lint-man-end-blurb.perl
 $(LINT_DOCS_MAN_END_BLURB): .build/lint-docs/man-end-blurb/%.ok: %.txt
+       $(call mkdir_p_parent_template)
        $(QUIET_LINT_MANEND)$(PERL_PATH) lint-man-end-blurb.perl $< >$@
 .PHONY: lint-docs-man-end-blurb
-lint-docs-man-end-blurb: $(LINT_DOCS_MAN_END_BLURB)
 
 ## Lint: man-section-order
-.build/lint-docs/man-section-order: | .build/lint-docs
-       $(QUIET)mkdir $@
 LINT_DOCS_MAN_SECTION_ORDER = $(patsubst %.txt,.build/lint-docs/man-section-order/%.ok,$(MAN_TXT))
-$(LINT_DOCS_MAN_SECTION_ORDER): | .build/lint-docs/man-section-order
 $(LINT_DOCS_MAN_SECTION_ORDER): lint-man-section-order.perl
 $(LINT_DOCS_MAN_SECTION_ORDER): .build/lint-docs/man-section-order/%.ok: %.txt
+       $(call mkdir_p_parent_template)
        $(QUIET_LINT_MANSEC)$(PERL_PATH) lint-man-section-order.perl $< >$@
 .PHONY: lint-docs-man-section-order
 lint-docs-man-section-order: $(LINT_DOCS_MAN_SECTION_ORDER)
@@ -524,7 +476,4 @@ doc-l10n install-l10n::
        $(MAKE) -C po $@
 endif
 
-# Delete the target file on error
-.DELETE_ON_ERROR:
-
 .PHONY: FORCE
index ca267941f3ecfefa1c7e9023c6801bdbed03e4fc..8d9e85566e642ecc47620cc4622414d587d3b993 100644 (file)
@@ -522,24 +522,25 @@ function shows that the all-object walk is being performed by
 `traverse_commit_list()` or `traverse_commit_list_filtered()`. Those two
 functions reside in `list-objects.c`; examining the source shows that, despite
 the name, these functions traverse all kinds of objects. Let's have a look at
-the arguments to `traverse_commit_list_filtered()`, which are a superset of the
-arguments to the unfiltered version.
+the arguments to `traverse_commit_list()`.
 
-- `struct list_objects_filter_options *filter_options`: This is a struct which
-  stores a filter-spec as outlined in `Documentation/rev-list-options.txt`.
-- `struct rev_info *revs`: This is the `rev_info` used for the walk.
+- `struct rev_info *revs`: This is the `rev_info` used for the walk. If
+  its `filter` member is not `NULL`, then `filter` contains information for
+  how to filter the object list.
 - `show_commit_fn show_commit`: A callback which will be used to handle each
   individual commit object.
 - `show_object_fn show_object`: A callback which will be used to handle each
   non-commit object (so each blob, tree, or tag).
 - `void *show_data`: A context buffer which is passed in turn to `show_commit`
   and `show_object`.
+
+In addition, `traverse_commit_list_filtered()` has an additional paramter:
+
 - `struct oidset *omitted`: A linked-list of object IDs which the provided
   filter caused to be omitted.
 
-It looks like this `traverse_commit_list_filtered()` uses callbacks we provide
-instead of needing us to call it repeatedly ourselves. Cool! Let's add the
-callbacks first.
+It looks like these methods use callbacks we provide instead of needing us
+to call it repeatedly ourselves. Cool! Let's add the callbacks first.
 
 For the sake of this tutorial, we'll simply keep track of how many of each kind
 of object we find. At file scope in `builtin/walken.c` add the following
@@ -712,20 +713,9 @@ help understand. In our case, that means we omit trees and blobs not directly
 referenced by `HEAD` or `HEAD`'s history, because we begin the walk with only
 `HEAD` in the `pending` list.)
 
-First, we'll need to `#include "list-objects-filter-options.h"` and set up the
-`struct list_objects_filter_options` at the top of the function.
-
-----
-static void walken_object_walk(struct rev_info *rev)
-{
-       struct list_objects_filter_options filter_options = { 0 };
-
-       ...
-----
-
 For now, we are not going to track the omitted objects, so we'll replace those
 parameters with `NULL`. For the sake of simplicity, we'll add a simple
-build-time branch to use our filter or not. Replace the line calling
+build-time branch to use our filter or not. Preface the line calling
 `traverse_commit_list()` with the following, which will remind us which kind of
 walk we've just performed:
 
@@ -733,19 +723,17 @@ walk we've just performed:
        if (0) {
                /* Unfiltered: */
                trace_printf(_("Unfiltered object walk.\n"));
-               traverse_commit_list(rev, walken_show_commit,
-                               walken_show_object, NULL);
        } else {
                trace_printf(
                        _("Filtered object walk with filterspec 'tree:1'.\n"));
-               parse_list_objects_filter(&filter_options, "tree:1");
-
-               traverse_commit_list_filtered(&filter_options, rev,
-                       walken_show_commit, walken_show_object, NULL, NULL);
+               CALLOC_ARRAY(rev->filter, 1);
+               parse_list_objects_filter(rev->filter, "tree:1");
        }
+       traverse_commit_list(rev, walken_show_commit,
+                            walken_show_object, NULL);
 ----
 
-`struct list_objects_filter_options` is usually built directly from a command
+The `rev->filter` member is usually built directly from a command
 line argument, so the module provides an easy way to build one from a string.
 Even though we aren't taking user input right now, we can still build one with
 a hardcoded string using `parse_list_objects_filter()`.
@@ -784,7 +772,7 @@ object:
 ----
        ...
 
-               traverse_commit_list_filtered(&filter_options, rev,
+               traverse_commit_list_filtered(rev,
                        walken_show_commit, walken_show_object, NULL, &omitted);
 
        ...
index dcb39fe56cb38337ae0440da180c0b5ea2bdc774..f1449eb9266d6f1ed25b80da8d05c186941117b6 100644 (file)
@@ -16,7 +16,13 @@ Backward compatibility warts
 
 Note to those who build from the source
 
- *
+ * Since Git 2.31, our source assumed that the compiler you use to
+   build Git supports variadic macros, with an easy-to-use escape
+   hatch to allow compilation without variadic macros with an request
+   to report that you had to use the escape hatch to the list.
+   Because we haven't heard from anybody who actually needed to use
+   the escape hatch, it has been removed, making support of variadic
+   macros a hard requirement.
 
 
 UI, Workflows & Features
@@ -52,6 +58,26 @@ UI, Workflows & Features
  * The error message given by "git switch HEAD~4" has been clarified
    to suggest the "--detach" option that is required.
 
+ * In sparse-checkouts, files mis-marked as missing from the working tree
+   could lead to later problems.  Such files were hard to discover, and
+   harder to correct.  Automatically detecting and correcting the marking
+   of such files has been added to avoid these problems.
+
+ * "git cat-file" learns "--batch-command" mode, which is a more
+   flexible interface than the existing "--batch" or "--batch-check"
+   modes, to allow different kinds of inquiries made.
+
+ * The level of verbose output from the ort backend during inner merge
+   has been aligned to that of the recursive backend.
+
+ * "git remote rename A B", depending on the number of remote-tracking
+   refs involved, takes long time renaming them.  The command has been
+   taught to show progress bar while making the user wait.
+
+ * Bundle file format gets extended to allow a partial bundle,
+   filtered by similar criteria you would give when making a
+   partial/lazy clone.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -90,6 +116,32 @@ Performance, Internal Implementation, Development Support etc.
  * Use designated initializers we started using in mid 2017 in more
    parts of the codebase that are relatively quiescent.
 
+ * Improve failure case behaviour of xdiff library when memory
+   allocation fails.
+
+ * General clean-up in reftable implementation, including
+   clarification of the API documentation, tightening the code to
+   honor documented length limit, etc.
+
+ * Remove the escape hatch we added when we introduced the weather
+   balloon to use variadic macros unconditionally, to make it official
+   that we now have a hard dependency on the feature.
+
+ * Makefile refactoring with a bit of suffixes rule stripping to
+   optimize the runtime overhead.
+
+ * "git stash drop" is reimplemented as an internal call to
+   reflog_delete() function, instead of invoking "git reflog delete"
+   via run_command() API.
+
+ * Count string_list items in size_t, not "unsigned int".
+
+ * The single-key interactive operation used by "git add -p" has been
+   made more robust.
+
+ * Remove unneeded <meta http-equiv=content-type...> from gitweb
+   output.
+
 
 Fixes since v2.35
 -----------------
@@ -253,6 +305,35 @@ Fixes since v2.35
    recorded the last level component of the branch name, which has
    been corrected.
 
+ * "git fetch" can make two separate fetches, but ref updates coming
+   from them were in two separate ref transactions under "--atomic",
+   which has been corrected.
+
+ * Check the return value from parse_tree_indirect() to turn segfaults
+   into calls to die().
+   (merge 8d2eaf649a gc/parse-tree-indirect-errors later to maint).
+
+ * Newer version of GPGSM changed its output in a backward
+   incompatible way to break our code that parses its output.  It also
+   added more processes our tests need to kill when cleaning up.
+   Adjustments have been made to accommodate these changes.
+   (merge b0b70d54c4 fs/gpgsm-update later to maint).
+
+ * The untracked cache newly computed weren't written back to the
+   on-disk index file when there is no other change to the index,
+   which has been corrected.
+
+ * "git config -h" did not describe the "--type" option correctly.
+   (merge 5445124fad mf/fix-type-in-config-h later to maint).
+
+ * The way generation number v2 in the commit-graph files are
+   (not) handled has been corrected.
+   (merge 6dbf4b8172 ds/commit-graph-gen-v2-fixes later to maint).
+
+ * The method to trigger malloc check used in our tests no longer work
+   with newer versions of glibc.
+   (merge baedc59543 ep/test-malloc-check-with-glibc-2.34 later to maint).
+
  * Other code cleanup, docfix, build fix, etc.
    (merge cfc5cf428b jc/find-header later to maint).
    (merge 40e7cfdd46 jh/p4-fix-use-of-process-error-exception later to maint).
@@ -277,3 +358,5 @@ Fixes since v2.35
    (merge 332acc248d ds/mailmap later to maint).
    (merge 04bf052eef ab/grep-patterntype later to maint).
    (merge 6ee36364eb ab/diff-free-more later to maint).
+   (merge 63a36017fe nj/read-tree-doc-reffix later to maint).
+   (merge eed36fce38 sm/no-git-in-upstream-of-pipe-in-tests later to maint).
index bf3e512921fe75744ead531d3fa8252aa7039885..f0fb25a371c1a8e193ad5074862d8748d5a45622 100644 (file)
@@ -503,6 +503,8 @@ include::config/sequencer.txt[]
 
 include::config/showbranch.txt[]
 
+include::config/sparse.txt[]
+
 include::config/splitindex.txt[]
 
 include::config/ssh.txt[]
index 9c413e177e02c6f28865ed79d84de3c5ccf43dd9..41ac6953c87eb55f14b01471a5b6013bb318859e 100644 (file)
@@ -25,3 +25,8 @@ repack.writeBitmaps::
        space and extra time spent on the initial repack.  This has
        no effect if multiple packfiles are created.
        Defaults to true on bare repos, false otherwise.
+
+repack.updateServerInfo::
+       If set to false, linkgit:git-repack[1] will not run
+       linkgit:git-update-server-info[1]. Defaults to true. Can be overridden
+       when true by the `-n` option of linkgit:git-repack[1].
diff --git a/Documentation/config/sparse.txt b/Documentation/config/sparse.txt
new file mode 100644 (file)
index 0000000..aff49a8
--- /dev/null
@@ -0,0 +1,27 @@
+sparse.expectFilesOutsideOfPatterns::
+       Typically with sparse checkouts, files not matching any
+       sparsity patterns are marked with a SKIP_WORKTREE bit in the
+       index and are missing from the working tree.  Accordingly, Git
+       will ordinarily check whether files with the SKIP_WORKTREE bit
+       are in fact present in the working tree contrary to
+       expectations.  If Git finds any, it marks those paths as
+       present by clearing the relevant SKIP_WORKTREE bits.  This
+       option can be used to tell Git that such
+       present-despite-skipped files are expected and to stop
+       checking for them.
++
+The default is `false`, which allows Git to automatically recover
+from the list of files in the index and working tree falling out of
+sync.
++
+Set this to `true` if you are in a setup where some external factor
+relieves Git of the responsibility for maintaining the consistency
+between the presence of working tree files and sparsity patterns.  For
+example, if you have a Git-aware virtual file system that has a robust
+mechanism for keeping the working tree and the sparsity patterns up to
+date based on access patterns.
++
+Regardless of this setting, Git does not check for
+present-despite-skipped files unless sparse checkout is enabled, so
+this config option has no effect unless `core.sparseCheckout` is
+`true`.
index 72ab81390525cd23aa8b63a43d0070986620db17..ac4c4352aae8ae12bdaf9f10413eb6ac12a7eb6d 100644 (file)
@@ -75,8 +75,11 @@ verify <file>::
        cleanly to the current repository.  This includes checks on the
        bundle format itself as well as checking that the prerequisite
        commits exist and are fully linked in the current repository.
-       'git bundle' prints a list of missing commits, if any, and exits
-       with a non-zero status.
+       Information about additional capabilities, such as "object filter",
+       is printed. See "Capabilities" in link:technical/bundle-format.html
+       for more information. Finally, 'git bundle' prints a list of
+       missing commits, if any. The exit code is zero for success, but
+       will be nonzero if the bundle file is invalid.
 
 list-heads <file>::
        Lists the references defined in the bundle.  If followed by a
index bef76f4dd060dd7fdd4efee44f53b34a3c546c13..70c5b4f12d1a9d22dcb53c6b9d12db0530559aee 100644 (file)
@@ -96,6 +96,33 @@ OPTIONS
        need to specify the path, separated by whitespace.  See the
        section `BATCH OUTPUT` below for details.
 
+--batch-command::
+--batch-command=<format>::
+       Enter a command mode that reads commands and arguments from stdin. May
+       only be combined with `--buffer`, `--textconv` or `--filters`. In the
+       case of `--textconv` or `--filters`, the input lines also need to specify
+       the path, separated by whitespace. See the section `BATCH OUTPUT` below
+       for details.
++
+`--batch-command` recognizes the following commands:
++
+--
+contents <object>::
+       Print object contents for object reference `<object>`. This corresponds to
+       the output of `--batch`.
+
+info <object>::
+       Print object info for object reference `<object>`. This corresponds to the
+       output of `--batch-check`.
+
+flush::
+       Used with `--buffer` to execute all preceding commands that were issued
+       since the beginning or since the last flush was issued. When `--buffer`
+       is used, no output will come until a `flush` is issued. When `--buffer`
+       is not used, commands are flushed each time without issuing `flush`.
+--
++
+
 --batch-all-objects::
        Instead of reading a list of objects on stdin, perform the
        requested batch operation on all objects in the repository and
@@ -110,7 +137,7 @@ OPTIONS
        that a process can interactively read and write from
        `cat-file`. With this option, the output uses normal stdio
        buffering; this is much more efficient when invoking
-       `--batch-check` on a large number of objects.
+       `--batch-check` or `--batch-command` on a large number of objects.
 
 --unordered::
        When `--batch-all-objects` is in use, visit objects in an
@@ -202,6 +229,13 @@ from stdin, one per line, and print information about them. By default,
 the whole line is considered as an object, as if it were fed to
 linkgit:git-rev-parse[1].
 
+When `--batch-command` is given, `cat-file` will read commands from stdin,
+one per line, and print information based on the command given. With
+`--batch-command`, the `info` command followed by an object will print
+information about the object the same way `--batch-check` would, and the
+`contents` command followed by an object prints contents in the same way
+`--batch` would.
+
 You can specify the information shown for each object by using a custom
 `<format>`. The `<format>` is copied literally to stdout for each
 object, with placeholders of the form `%(atom)` expanded, followed by a
@@ -237,9 +271,9 @@ newline. The available atoms are:
 If no format is specified, the default format is `%(objectname)
 %(objecttype) %(objectsize)`.
 
-If `--batch` is specified, the object information is followed by the
-object contents (consisting of `%(objectsize)` bytes), followed by a
-newline.
+If `--batch` is specified, or if `--batch-command` is used with the `contents`
+command, the object information is followed by the object contents (consisting
+of `%(objectsize)` bytes), followed by a newline.
 
 For example, `--batch` without a custom format would produce:
 
index 44ea63cc6d3f1943d8144a872bb1fa84089f837d..239c68db457098cae526e4691061c7373b61266a 100644 (file)
@@ -8,8 +8,8 @@ git-help - Display help information about Git
 SYNOPSIS
 --------
 [verse]
-'git help' [-a|--all [--[no-]verbose]]
-          [[-i|--info] [-m|--man] [-w|--web]] [<command>|<guide>]
+'git help' [-a|--all] [--[no-]verbose] [--[no-]external-commands] [--[no-]aliases]
+'git help' [[-i|--info] [-m|--man] [-w|--web]] [<command>|<guide>]
 'git help' [-g|--guides]
 'git help' [-c|--config]
 
@@ -46,8 +46,15 @@ OPTIONS
 -------
 -a::
 --all::
-       Prints all the available commands on the standard output. This
-       option overrides any given command or guide name.
+       Prints all the available commands on the standard output.
+
+--no-external-commands::
+       When used with `--all`, exclude the listing of external "git-*"
+       commands found in the `$PATH`.
+
+--no-aliases::
+       When used with `--all`, exclude the listing of configured
+       aliases.
 
 --verbose::
        When used with `--all` print description for all recognized
index 1f1e3592251259960f164094c3f82bf9eae455cc..4e71c256ecb08fe44ef656c85b7246e6f2f458b1 100644 (file)
@@ -122,6 +122,14 @@ This option cannot be used with --stdin.
 +
 include::object-format-disclaimer.txt[]
 
+--promisor[=<message>]::
+       Before committing the pack-index, create a .promisor file for this
+       pack. Particularly helpful when writing a promisor pack with --fix-thin
+       since the name of the pack is not final until the pack has been fully
+       written. If a `<message>` is provided, then that content will be
+       written to the .promisor file for future reference. See
+       link:technical/partial-clone.html[partial clone] for more information.
+
 NOTES
 -----
 
index e2cfb68ab57907f640a618409533a98c7427af1d..e56bad28c6551f0f0a1990f245da4f551df74178 100644 (file)
@@ -10,6 +10,8 @@ SYNOPSIS
 --------
 [verse]
 'git maintenance' run [<options>]
+'git maintenance' start [--scheduler=<scheduler>]
+'git maintenance' (stop|register|unregister)
 
 
 DESCRIPTION
@@ -29,6 +31,24 @@ Git repository.
 SUBCOMMANDS
 -----------
 
+run::
+       Run one or more maintenance tasks. If one or more `--task` options
+       are specified, then those tasks are run in that order. Otherwise,
+       the tasks are determined by which `maintenance.<task>.enabled`
+       config options are true. By default, only `maintenance.gc.enabled`
+       is true.
+
+start::
+       Start running maintenance on the current repository. This performs
+       the same config updates as the `register` subcommand, then updates
+       the background scheduler to run `git maintenance run --scheduled`
+       on an hourly basis.
+
+stop::
+       Halt the background maintenance schedule. The current repository
+       is not removed from the list of maintained repositories, in case
+       the background maintenance is restarted later.
+
 register::
        Initialize Git config values so any scheduled maintenance will
        start running on this repository. This adds the repository to the
@@ -55,24 +75,6 @@ task:
 setting `maintenance.auto = false` in the current repository. This config
 setting will remain after a `git maintenance unregister` command.
 
-run::
-       Run one or more maintenance tasks. If one or more `--task` options
-       are specified, then those tasks are run in that order. Otherwise,
-       the tasks are determined by which `maintenance.<task>.enabled`
-       config options are true. By default, only `maintenance.gc.enabled`
-       is true.
-
-start::
-       Start running maintenance on the current repository. This performs
-       the same config updates as the `register` subcommand, then updates
-       the background scheduler to run `git maintenance run --scheduled`
-       on an hourly basis.
-
-stop::
-       Halt the background maintenance schedule. The current repository
-       is not removed from the list of maintained repositories, in case
-       the background maintenance is restarted later.
-
 unregister::
        Remove the current repository from background maintenance. This
        only removes the repository from the configured list. It does not
index 8c3aceb832475f25f5712709040033ab2f5fab59..a5356a230bb64a1ae5a71934262df541b0edf7c6 100644 (file)
@@ -375,9 +375,14 @@ have finished your work-in-progress), attempt the merge again.
 SPARSE CHECKOUT
 ---------------
 
+Note: The `update-index` and `read-tree` primitives for supporting the
+skip-worktree bit predated the introduction of
+linkgit:git-sparse-checkout[1].  Users are encouraged to use
+`sparse-checkout` in preference to these low-level primitives.
+
 "Sparse checkout" allows populating the working directory sparsely.
-It uses the skip-worktree bit (see linkgit:git-update-index[1]) to tell
-Git whether a file in the working directory is worth looking at.
+It uses the skip-worktree bit (see linkgit:git-update-index[1]) to
+tell Git whether a file in the working directory is worth looking at.
 
 'git read-tree' and other merge-based commands ('git merge', 'git
 checkout'...) can help maintaining the skip-worktree bitmap and working
@@ -385,7 +390,8 @@ directory update. `$GIT_DIR/info/sparse-checkout` is used to
 define the skip-worktree reference bitmap. When 'git read-tree' needs
 to update the working directory, it resets the skip-worktree bit in the index
 based on this file, which uses the same syntax as .gitignore files.
-If an entry matches a pattern in this file, skip-worktree will not be
+If an entry matches a pattern in this file, or the entry corresponds to
+a file present in the working tree, then skip-worktree will not be
 set on that entry. Otherwise, skip-worktree will be set.
 
 Then it compares the new skip-worktree value with the previous one. If
@@ -420,8 +426,8 @@ support.
 
 SEE ALSO
 --------
-linkgit:git-write-tree[1]; linkgit:git-ls-files[1];
-linkgit:gitignore[5]; linkgit:git-sparse-checkout[1];
+linkgit:git-write-tree[1], linkgit:git-ls-files[1],
+linkgit:gitignore[5], linkgit:git-sparse-checkout[1]
 
 GIT
 ---
index 2bebc32566b692ba616d4148b31e934cba0a7bad..cde9614e362854f4aa091348ef91ba69283cec0c 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 [verse]
 'git remote' [-v | --verbose]
 'git remote add' [-t <branch>] [-m <master>] [-f] [--[no-]tags] [--mirror=(fetch|push)] <name> <URL>
-'git remote rename' <old> <new>
+'git remote rename' [--[no-]progress] <old> <new>
 'git remote remove' <name>
 'git remote set-head' <name> (-a | --auto | -d | --delete | <branch>)
 'git remote set-branches' [--add] <name> <branch>...
index 94dad137b96849599b2485c1f454bf4305cbbee2..88e55f432f3a6c83333d9404105d6ae436f180d2 100644 (file)
@@ -3,9 +3,7 @@ git-sparse-checkout(1)
 
 NAME
 ----
-git-sparse-checkout - Initialize and modify the sparse-checkout
-configuration, which reduces the checkout to a set of paths
-given by a list of patterns.
+git-sparse-checkout - Reduce your working tree to a subset of tracked files
 
 
 SYNOPSIS
@@ -17,8 +15,20 @@ SYNOPSIS
 DESCRIPTION
 -----------
 
-Initialize and modify the sparse-checkout configuration, which reduces
-the checkout to a set of paths given by a list of patterns.
+This command is used to create sparse checkouts, which means that it
+changes the working tree from having all tracked files present, to only
+have a subset of them.  It can also switch which subset of files are
+present, or undo and go back to having all tracked files present in the
+working copy.
+
+The subset of files is chosen by providing a list of directories in
+cone mode (which is recommended), or by providing a list of patterns
+in non-cone mode.
+
+When in a sparse-checkout, other Git commands behave a bit differently.
+For example, switching branches will not update paths outside the
+sparse-checkout directories/patterns, and `git commit -a` will not record
+paths outside the sparse-checkout directories/patterns as deleted.
 
 THIS COMMAND IS EXPERIMENTAL. ITS BEHAVIOR, AND THE BEHAVIOR OF OTHER
 COMMANDS IN THE PRESENCE OF SPARSE-CHECKOUTS, WILL LIKELY CHANGE IN
@@ -28,7 +38,7 @@ THE FUTURE.
 COMMANDS
 --------
 'list'::
-       Describe the patterns in the sparse-checkout file.
+       Describe the directories or patterns in the sparse-checkout file.
 
 'set'::
        Enable the necessary sparse-checkout config settings
@@ -46,20 +56,26 @@ the 'set' subcommand are stored in the worktree-specific sparse-checkout
 file. See linkgit:git-worktree[1] and the documentation of
 `extensions.worktreeConfig` in linkgit:git-config[1] for more details.
 +
-When the `--stdin` option is provided, the patterns are read from
-standard in as a newline-delimited list instead of from the arguments.
+When the `--stdin` option is provided, the directories or patterns are
+read from standard in as a newline-delimited list instead of from the
+arguments.
 +
 When `--cone` is passed or `core.sparseCheckoutCone` is enabled, the
-input list is considered a list of directories instead of
-sparse-checkout patterns.  This allows for better performance with a
-limited set of patterns (see 'CONE PATTERN SET' below).  Note that the
-set command will write patterns to the sparse-checkout file to include
-all files contained in those directories (recursively) as well as
-files that are siblings of ancestor directories. The input format
-matches the output of `git ls-tree --name-only`.  This includes
-interpreting pathnames that begin with a double quote (") as C-style
-quoted strings.  This may become the default in the future; --no-cone
-can be passed to request non-cone mode.
+input list is considered a list of directories.  This allows for
+better performance with a limited set of patterns (see 'CONE PATTERN
+SET' below).  The input format matches the output of `git ls-tree
+--name-only`.  This includes interpreting pathnames that begin with a
+double quote (") as C-style quoted strings.  Note that the set command
+will write patterns to the sparse-checkout file to include all files
+contained in those directories (recursively) as well as files that are
+siblings of ancestor directories. This may become the default in the
+future; --no-cone can be passed to request non-cone mode.
++
+When `--no-cone` is passed or `core.sparseCheckoutCone` is not enabled,
+the input list is considered a list of patterns.  This mode is harder
+to use and less performant, and is thus not recommended.  See the
+"Sparse Checkout" section of linkgit:git-read-tree[1] and the "Pattern
+Set" sections below for more details.
 +
 Use the `--[no-]sparse-index` option to use a sparse index (the
 default is to not use it).  A sparse index reduces the size of the
@@ -77,11 +93,10 @@ understand the sparse directory entries index extension and may fail to
 interact with your repository until it is disabled.
 
 'add'::
-       Update the sparse-checkout file to include additional patterns.
-       By default, these patterns are read from the command-line arguments,
-       but they can be read from stdin using the `--stdin` option. When
-       `core.sparseCheckoutCone` is enabled, the given patterns are interpreted
-       as directory names as in the 'set' subcommand.
+       Update the sparse-checkout file to include additional directories
+       (in cone mode) or patterns (in non-cone mode).  By default, these
+       directories or patterns are read from the command-line arguments,
+       but they can be read from stdin using the `--stdin` option.
 
 'reapply'::
        Reapply the sparsity pattern rules to paths in the working tree.
@@ -125,13 +140,14 @@ decreased in utility.
 SPARSE CHECKOUT
 ---------------
 
-"Sparse checkout" allows populating the working directory sparsely.
-It uses the skip-worktree bit (see linkgit:git-update-index[1]) to tell
-Git whether a file in the working directory is worth looking at. If
-the skip-worktree bit is set, then the file is ignored in the working
-directory. Git will avoid populating the contents of those files, which
-makes a sparse checkout helpful when working in a repository with many
-files, but only a few are important to the current user.
+"Sparse checkout" allows populating the working directory sparsely.  It
+uses the skip-worktree bit (see linkgit:git-update-index[1]) to tell Git
+whether a file in the working directory is worth looking at. If the
+skip-worktree bit is set, and the file is not present in the working tree,
+then its absence is ignored. Git will avoid populating the contents of
+those files, which makes a sparse checkout helpful when working in a
+repository with many files, but only a few are important to the current
+user.
 
 The `$GIT_DIR/info/sparse-checkout` file is used to define the
 skip-worktree reference bitmap. When Git updates the working
index 2853f168d976857581163f2ac057110ddb501829..568dbfe76b8e86488faefbefc2fdf27153d61fb8 100644 (file)
@@ -351,6 +351,10 @@ unchanged".  Note that "assume unchanged" bit is *not* set if
 the index (use `git update-index --really-refresh` if you want
 to mark them as "assume unchanged").
 
+Sometimes users confuse the assume-unchanged bit with the
+skip-worktree bit.  See the final paragraph in the "Skip-worktree bit"
+section below for an explanation of the differences.
+
 
 EXAMPLES
 --------
@@ -392,22 +396,47 @@ M foo.c
 SKIP-WORKTREE BIT
 -----------------
 
-Skip-worktree bit can be defined in one (long) sentence: When reading
-an entry, if it is marked as skip-worktree, then Git pretends its
-working directory version is up to date and read the index version
-instead.
-
-To elaborate, "reading" means checking for file existence, reading
-file attributes or file content. The working directory version may be
-present or absent. If present, its content may match against the index
-version or not. Writing is not affected by this bit, content safety
-is still first priority. Note that Git _can_ update working directory
-file, that is marked skip-worktree, if it is safe to do so (i.e.
-working directory version matches index version)
+Skip-worktree bit can be defined in one (long) sentence: Tell git to
+avoid writing the file to the working directory when reasonably
+possible, and treat the file as unchanged when it is not
+present in the working directory.
+
+Note that not all git commands will pay attention to this bit, and
+some only partially support it.
+
+The update-index flags and the read-tree capabilities relating to the
+skip-worktree bit predated the introduction of the
+linkgit:git-sparse-checkout[1] command, which provides a much easier
+way to configure and handle the skip-worktree bits.  If you want to
+reduce your working tree to only deal with a subset of the files in
+the repository, we strongly encourage the use of
+linkgit:git-sparse-checkout[1] in preference to the low-level
+update-index and read-tree primitives.
+
+The primary purpose of the skip-worktree bit is to enable sparse
+checkouts, i.e. to have working directories with only a subset of
+paths present.  When the skip-worktree bit is set, Git commands (such
+as `switch`, `pull`, `merge`) will avoid writing these files.
+However, these commands will sometimes write these files anyway in
+important cases such as conflicts during a merge or rebase.  Git
+commands will also avoid treating the lack of such files as an
+intentional deletion; for example `git add -u` will not not stage a
+deletion for these files and `git commit -a` will not make a commit
+deleting them either.
 
 Although this bit looks similar to assume-unchanged bit, its goal is
-different from assume-unchanged bit's. Skip-worktree also takes
-precedence over assume-unchanged bit when both are set.
+different.  The assume-unchanged bit is for leaving the file in the
+working tree but having Git omit checking it for changes and presuming
+that the file has not been changed (though if it can determine without
+stat'ing the file that it has changed, it is free to record the
+changes).  skip-worktree tells Git to ignore the absence of the file,
+avoid updating it when possible with commands that normally update
+much of the working directory (e.g. `checkout`, `switch`, `pull`,
+etc.), and not have its absence be recorded in commits.  Note that in
+sparse checkouts (setup by `git sparse-checkout` or by configuring
+core.sparseCheckout to true), if a file is marked as skip-worktree in
+the index but is found in the working tree, Git will clear the
+skip-worktree bit for that file.
 
 SPLIT INDEX
 -----------
index a71dad267404bc61f21b4e235acf5a975115d8b4..4b36d51beb66f08bff4576ed3afcbc9a6d78d63a 100644 (file)
@@ -829,6 +829,8 @@ patterns are available:
 
 - `java` suitable for source code in the Java language.
 
+- `kotlin` suitable for source code in the Kotlin language.
+
 - `markdown` suitable for Markdown documents.
 
 - `matlab` suitable for source code in the MATLAB and Octave languages.
index bac558d049a36f4b4fbb132c7aa1dd5af4770102..b9be8644cf5d53161b7190f580be935b28d402a3 100644 (file)
@@ -71,6 +71,11 @@ and the Git bundle v2 format cannot represent a shallow clone repository.
 == Capabilities
 
 Because there is no opportunity for negotiation, unknown capabilities cause 'git
-bundle' to abort.  The only known capability is `object-format`, which specifies
-the hash algorithm in use, and can take the same values as the
-`extensions.objectFormat` configuration value.
+bundle' to abort.
+
+* `object-format` specifies the hash algorithm in use, and can take the same
+  values as the `extensions.objectFormat` configuration value.
+
+* `filter` specifies an object filter as in the `--filter` option in
+  linkgit:git-rev-list[1]. The resulting pack-file must be marked as a
+  `.promisor` pack-file after it is unbundled.
index 87971c27dd73f45e1c630435c5d2935449d79759..484b185ba98bdc00efc147851abdcbe4958ee21f 100644 (file)
@@ -93,7 +93,7 @@ CHUNK DATA:
       2 bits of the lowest byte, storing the 33rd and 34th bit of the
       commit time.
 
-  Generation Data (ID: {'G', 'D', 'A', 'T' }) (N * 4 bytes) [Optional]
+  Generation Data (ID: {'G', 'D', 'A', '2' }) (N * 4 bytes) [Optional]
     * This list of 4-byte values store corrected commit date offsets for the
       commits, arranged in the same order as commit data chunk.
     * If the corrected commit date offset cannot be stored within 31 bits,
@@ -104,7 +104,7 @@ CHUNK DATA:
       by compatible versions of Git and in case of split commit-graph chains,
       the topmost layer also has Generation Data chunk.
 
-  Generation Data Overflow (ID: {'G', 'D', 'O', 'V' }) [Optional]
+  Generation Data Overflow (ID: {'G', 'D', 'O', '2' }) [Optional]
     * This list of 8-byte values stores the corrected commit date offsets
       for commits with corrected commit date offsets that cannot be
       stored within 31 bits.
@@ -156,3 +156,11 @@ CHUNK DATA:
 TRAILER:
 
        H-byte HASH-checksum of all of the above.
+
+== Historical Notes:
+
+The Generation Data (GDA2) and Generation Data Overflow (GDO2) chunks have
+the number '2' in their chunk IDs because a previous version of Git wrote
+possibly erroneous data in these chunks with the IDs "GDAT" and "GDOV". By
+changing the IDs, newer versions of Git will silently ignore those older
+chunks and write the new information without trusting the incorrect data.
index d7c3b645cfb058d00bcca798a99e51096f56f065..6a67cc4174f820931a25bbd40c83959237b2983c 100644 (file)
@@ -443,7 +443,7 @@ Obj block format
 Object blocks are optional. Writers may choose to omit object blocks,
 especially if readers will not use the object name to ref mapping.
 
-Object blocks use unique, abbreviated 2-32 object name keys, mapping to
+Object blocks use unique, abbreviated 2-31 byte object name keys, mapping to
 ref blocks containing references pointing to that object directly, or as
 the peeled value of an annotated tag. Like ref blocks, object blocks use
 the file's standard block size. The abbreviation length is available in
index 6f0b4b775fece6acd20348e318a8cff3e254bcb2..70f0a004e75614a1afc1ccae07c5564cced4f758 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,9 @@
 # The default target of this Makefile is...
 all::
 
+# Import tree-wide shared Makefile behavior and libraries
+include shared.mak
+
 # Define V=1 to have a more verbose compile.
 #
 # Define SHELL_PATH to a POSIX shell if your /bin/sh is broken.
@@ -830,12 +833,33 @@ GENERATED_H += hook-list.h
 .PHONY: generated-hdrs
 generated-hdrs: $(GENERATED_H)
 
-LIB_H := $(sort $(patsubst ./%,%,$(shell git ls-files '*.h' ':!t/' ':!Documentation/' 2>/dev/null || \
+## Exhaustive lists of our source files, either dynamically generated,
+## or hardcoded.
+SOURCES_CMD = ( \
+       git ls-files \
+               '*.[hcS]' \
+               '*.sh' \
+               ':!*[tp][0-9][0-9][0-9][0-9]*' \
+               ':!contrib' \
+               2>/dev/null || \
        $(FIND) . \
-       -name .git -prune -o \
-       -name t -prune -o \
-       -name Documentation -prune -o \
-       -name '*.h' -print)))
+               \( -name .git -type d -prune \) \
+               -o \( -name '[tp][0-9][0-9][0-9][0-9]*' -prune \) \
+               -o \( -name contrib -type d -prune \) \
+               -o \( -name build -type d -prune \) \
+               -o \( -name 'trash*' -type d -prune \) \
+               -o \( -name '*.[hcS]' -type f -print \) \
+               -o \( -name '*.sh' -type f -print \) \
+               | sed -e 's|^\./||' \
+       )
+FOUND_SOURCE_FILES := $(shell $(SOURCES_CMD))
+
+FOUND_C_SOURCES = $(filter %.c,$(FOUND_SOURCE_FILES))
+FOUND_H_SOURCES = $(filter %.h,$(FOUND_SOURCE_FILES))
+
+COCCI_SOURCES = $(filter-out $(THIRD_PARTY_SOURCES),$(FOUND_C_SOURCES))
+
+LIB_H = $(FOUND_H_SOURCES)
 
 LIB_OBJS += abspath.o
 LIB_OBJS += add-interactive.o
@@ -989,6 +1013,7 @@ LIB_OBJS += rebase-interactive.o
 LIB_OBJS += rebase.o
 LIB_OBJS += ref-filter.o
 LIB_OBJS += reflog-walk.o
+LIB_OBJS += reflog.o
 LIB_OBJS += refs.o
 LIB_OBJS += refs/debug.o
 LIB_OBJS += refs/files-backend.o
@@ -1265,10 +1290,6 @@ endif
 ALL_CFLAGS = $(DEVELOPER_CFLAGS) $(CPPFLAGS) $(CFLAGS)
 ALL_LDFLAGS = $(LDFLAGS)
 
-comma := ,
-empty :=
-space := $(empty) $(empty)
-
 ifdef SANITIZE
 SANITIZERS := $(foreach flag,$(subst $(comma),$(space),$(SANITIZE)),$(flag))
 BASIC_CFLAGS += -fsanitize=$(SANITIZE) -fno-sanitize-recover=$(SANITIZE)
@@ -1981,39 +2002,6 @@ ifndef PAGER_ENV
 PAGER_ENV = LESS=FRX LV=-c
 endif
 
-QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
-QUIET_SUBDIR1  =
-
-ifneq ($(findstring w,$(MAKEFLAGS)),w)
-PRINT_DIR = --no-print-directory
-else # "make -w"
-NO_SUBDIR = :
-endif
-
-ifneq ($(findstring s,$(MAKEFLAGS)),s)
-ifndef V
-       QUIET_CC       = @echo '   ' CC $@;
-       QUIET_AR       = @echo '   ' AR $@;
-       QUIET_LINK     = @echo '   ' LINK $@;
-       QUIET_BUILT_IN = @echo '   ' BUILTIN $@;
-       QUIET_GEN      = @echo '   ' GEN $@;
-       QUIET_LNCP     = @echo '   ' LN/CP $@;
-       QUIET_XGETTEXT = @echo '   ' XGETTEXT $@;
-       QUIET_MSGFMT   = @echo '   ' MSGFMT $@;
-       QUIET_GCOV     = @echo '   ' GCOV $@;
-       QUIET_SP       = @echo '   ' SP $<;
-       QUIET_HDR      = @echo '   ' HDR $(<:hcc=h);
-       QUIET_RC       = @echo '   ' RC $@;
-       QUIET_SPATCH   = @echo '   ' SPATCH $<;
-       QUIET_SUBDIR0  = +@subdir=
-       QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
-                        $(MAKE) $(PRINT_DIR) -C $$subdir
-       export V
-       export QUIET_GEN
-       export QUIET_BUILT_IN
-endif
-endif
-
 ifdef NO_INSTALL_HARDLINKS
        export NO_INSTALL_HARDLINKS
 endif
@@ -2194,16 +2182,6 @@ shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell
 strip: $(PROGRAMS) git$X
        $(STRIP) $(STRIP_OPTS) $^
 
-### Flags affecting all rules
-
-# A GNU make extension since gmake 3.72 (released in late 1994) to
-# remove the target of rules if commands in those rules fail. The
-# default is to only do that if make itself receives a signal. Affects
-# all targets, see:
-#
-#    info make --index-search=.DELETE_ON_ERROR
-.DELETE_ON_ERROR:
-
 ### Target-specific flags and dependencies
 
 # The generic compilation pattern rule and automatically
@@ -2566,8 +2544,6 @@ ASM_SRC := $(wildcard $(OBJECTS:o=S))
 ASM_OBJ := $(ASM_SRC:S=o)
 C_OBJ := $(filter-out $(ASM_OBJ),$(OBJECTS))
 
-.SUFFIXES:
-
 $(C_OBJ): %.o: %.c GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir)
        $(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(compdb_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<
 $(ASM_OBJ): %.o: %.S GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir)
@@ -2770,7 +2746,8 @@ all:: $(MOFILES)
 endif
 
 po/build/locale/%/LC_MESSAGES/git.mo: po/%.po
-       $(QUIET_MSGFMT)mkdir -p $(dir $@) && $(MSGFMT) -o $@ $<
+       $(call mkdir_p_parent_template)
+       $(QUIET_MSGFMT)$(MSGFMT) -o $@ $<
 
 LIB_PERL := $(wildcard perl/Git.pm perl/Git/*.pm perl/Git/*/*.pm perl/Git/*/*/*.pm)
 LIB_PERL_GEN := $(patsubst perl/%.pm,perl/build/lib/%.pm,$(LIB_PERL))
@@ -2786,35 +2763,16 @@ NO_PERL_CPAN_FALLBACKS_SQ = $(subst ','\'',$(NO_PERL_CPAN_FALLBACKS))
 endif
 
 perl/build/lib/%.pm: perl/%.pm GIT-PERL-DEFINES
-       $(QUIET_GEN)mkdir -p $(dir $@) && \
+       $(call mkdir_p_parent_template)
+       $(QUIET_GEN) \
        sed -e 's|@@LOCALEDIR@@|$(perl_localedir_SQ)|g' \
            -e 's|@@NO_GETTEXT@@|$(NO_GETTEXT_SQ)|g' \
            -e 's|@@NO_PERL_CPAN_FALLBACKS@@|$(NO_PERL_CPAN_FALLBACKS_SQ)|g' \
        < $< > $@
 
 perl/build/man/man3/Git.3pm: perl/Git.pm
-       $(QUIET_GEN)mkdir -p $(dir $@) && \
-       pod2man $< $@
-
-FIND_SOURCE_FILES = ( \
-       git ls-files \
-               '*.[hcS]' \
-               '*.sh' \
-               ':!*[tp][0-9][0-9][0-9][0-9]*' \
-               ':!contrib' \
-               2>/dev/null || \
-       $(FIND) . \
-               \( -name .git -type d -prune \) \
-               -o \( -name '[tp][0-9][0-9][0-9][0-9]*' -prune \) \
-               -o \( -name contrib -type d -prune \) \
-               -o \( -name build -type d -prune \) \
-               -o \( -name 'trash*' -type d -prune \) \
-               -o \( -name '*.[hcS]' -type f -print \) \
-               -o \( -name '*.sh' -type f -print \) \
-               | sed -e 's|^\./||' \
-       )
-
-FOUND_SOURCE_FILES = $(shell $(FIND_SOURCE_FILES))
+       $(call mkdir_p_parent_template)
+       $(QUIET_GEN)pod2man $< $@
 
 $(ETAGS_TARGET): $(FOUND_SOURCE_FILES)
        $(QUIET_GEN)$(RM) $@+ && \
@@ -2948,7 +2906,7 @@ test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(
 all:: $(TEST_PROGRAMS) $(test_bindir_programs)
 
 bin-wrappers/%: wrap-for-bin.sh
-       @mkdir -p bin-wrappers
+       $(call mkdir_p_parent_template)
        $(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
             -e 's|@@BUILD_DIR@@|$(shell pwd)|' \
             -e 's|@@PROG@@|$(patsubst test-%,t/helper/test-%$(X),$(@F))$(patsubst git%,$(X),$(filter $(@F),$(BINDIR_PROGRAMS_NEED_X)))|' < $< > $@ && \
@@ -3025,9 +2983,6 @@ check: $(GENERATED_H)
                exit 1; \
        fi
 
-FOUND_C_SOURCES = $(filter %.c,$(FOUND_SOURCE_FILES))
-COCCI_SOURCES = $(filter-out $(THIRD_PARTY_SOURCES),$(FOUND_C_SOURCES))
-
 %.cocci.patch: %.cocci $(COCCI_SOURCES)
        $(QUIET_SPATCH) \
        if test $(SPATCH_BATCH_SIZE) = 0; then \
index e1ab39cce30350dad4c1eacaad2f5f0153b4486d..72472103017920ac227641042f05383a40e5c673 100644 (file)
@@ -70,6 +70,8 @@ void init_add_i_state(struct add_i_state *s, struct repository *r)
                              &s->interactive_diff_algorithm);
 
        git_config_get_bool("interactive.singlekey", &s->use_single_key);
+       if (s->use_single_key)
+               setbuf(stdin, NULL);
 }
 
 void clear_add_i_state(struct add_i_state *s)
diff --git a/apply.c b/apply.c
index 0912307bd91a5005d9a4b57e2a4324541e247edc..d19c26d332e7908b364be4e2eddea4c2f1bde0b5 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -219,13 +219,18 @@ static void free_fragment_list(struct fragment *list)
        }
 }
 
-static void free_patch(struct patch *patch)
+void release_patch(struct patch *patch)
 {
        free_fragment_list(patch->fragments);
        free(patch->def_name);
        free(patch->old_name);
        free(patch->new_name);
        free(patch->result);
+}
+
+static void free_patch(struct patch *patch)
+{
+       release_patch(patch);
        free(patch);
 }
 
@@ -3159,7 +3164,7 @@ static int apply_binary(struct apply_state *state,
                 * See if the old one matches what the patch
                 * applies to.
                 */
-               hash_object_file(the_hash_algo, img->buf, img->len, blob_type,
+               hash_object_file(the_hash_algo, img->buf, img->len, OBJ_BLOB,
                                 &oid);
                if (strcmp(oid_to_hex(&oid), patch->old_oid_prefix))
                        return error(_("the patch applies to '%s' (%s), "
@@ -3205,7 +3210,7 @@ static int apply_binary(struct apply_state *state,
                                     name);
 
                /* verify that the result matches */
-               hash_object_file(the_hash_algo, img->buf, img->len, blob_type,
+               hash_object_file(the_hash_algo, img->buf, img->len, OBJ_BLOB,
                                 &oid);
                if (strcmp(oid_to_hex(&oid), patch->new_oid_prefix))
                        return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"),
@@ -3594,7 +3599,7 @@ static int try_threeway(struct apply_state *state,
 
        /* Preimage the patch was prepared for */
        if (patch->is_new)
-               write_object_file("", 0, blob_type, &pre_oid);
+               write_object_file("", 0, OBJ_BLOB, &pre_oid);
        else if (get_oid(patch->old_oid_prefix, &pre_oid) ||
                 read_blob_object(&buf, &pre_oid, patch->old_mode))
                return error(_("repository lacks the necessary blob to perform 3-way merge."));
@@ -3610,7 +3615,7 @@ static int try_threeway(struct apply_state *state,
                return -1;
        }
        /* post_oid is theirs */
-       write_object_file(tmp_image.buf, tmp_image.len, blob_type, &post_oid);
+       write_object_file(tmp_image.buf, tmp_image.len, OBJ_BLOB, &post_oid);
        clear_image(&tmp_image);
 
        /* our_oid is ours */
@@ -3623,7 +3628,7 @@ static int try_threeway(struct apply_state *state,
                        return error(_("cannot read the current contents of '%s'"),
                                     patch->old_name);
        }
-       write_object_file(tmp_image.buf, tmp_image.len, blob_type, &our_oid);
+       write_object_file(tmp_image.buf, tmp_image.len, OBJ_BLOB, &our_oid);
        clear_image(&tmp_image);
 
        /* in-core three-way merge between post and our using pre as base */
@@ -4323,7 +4328,7 @@ static int add_index_file(struct apply_state *state,
                        }
                        fill_stat_cache_info(state->repo->index, ce, &st);
                }
-               if (write_object_file(buf, size, blob_type, &ce->oid) < 0) {
+               if (write_object_file(buf, size, OBJ_BLOB, &ce->oid) < 0) {
                        discard_cache_entry(ce);
                        return error(_("unable to create backing store "
                                       "for newly created file %s"), path);
diff --git a/apply.h b/apply.h
index 4052da50c0658cf6434b262bba6f557459e7950c..b9f18ce87d1e0374a04aaaf785592c752dba6058 100644 (file)
--- a/apply.h
+++ b/apply.h
@@ -173,6 +173,8 @@ int parse_git_diff_header(struct strbuf *root,
                          unsigned int size,
                          struct patch *patch);
 
+void release_patch(struct patch *patch);
+
 /*
  * Some aspects of the apply behavior are controlled by the following
  * bits in the "options" parameter passed to apply_all_patches().
diff --git a/attr.c b/attr.c
index 79adaa50ea1e099fa7f8bee28efa6f7f7d92223b..1626cade8bd4e4084fe05f01ba32c26286a75cf9 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -14,7 +14,6 @@
 #include "utf8.h"
 #include "quote.h"
 #include "thread-utils.h"
-#include "dir.h"
 
 const char git_attr__true[] = "(builtin)true";
 const char git_attr__false[] = "\0(builtin)false";
diff --git a/attr.h b/attr.h
index 3732505edae806a8e847434905fe8cff09b6b063..3fb40cced08359b0fa93f6da155917e62668558e 100644 (file)
--- a/attr.h
+++ b/attr.h
@@ -121,7 +121,6 @@ struct git_attr;
 /* opaque structures used internally for attribute collection */
 struct all_attrs_item;
 struct attr_stack;
-struct index_state;
 
 /*
  * Given a string, return the gitattribute object that
index 7ab4f2e49219bdb38e4fe4e5b85d5a0cc3765566..6ccf46bc197e8cce014421f803e07e1f8f9d4963 100644 (file)
--- a/banned.h
+++ b/banned.h
 
 #undef sprintf
 #undef vsprintf
-#ifdef HAVE_VARIADIC_MACROS
 #define sprintf(...) BANNED(sprintf)
 #define vsprintf(...) BANNED(vsprintf)
-#else
-#define sprintf(buf,fmt,arg) BANNED(sprintf)
-#define vsprintf(buf,fmt,arg) BANNED(vsprintf)
-#endif
 
 #undef gmtime
 #define gmtime(t) BANNED(gmtime)
index 1bb6e7c069035bbf688d12cf8bcffa58790da5fc..5974cd7dd3cc128a7d0f7fd9a5f5c7c9c972de15 100644 (file)
 
 #include "sha1.h"
 
-#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
-
-/*
- * Force usage of rol or ror by selecting the one with the smaller constant.
- * It _can_ generate slightly smaller code (a constant of 1 is special), but
- * perhaps more importantly it's possibly faster on any uarch that does a
- * rotate with a loop.
- */
-
-#define SHA_ASM(op, x, n) ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; })
-#define SHA_ROL(x,n)   SHA_ASM("rol", x, n)
-#define SHA_ROR(x,n)   SHA_ASM("ror", x, n)
-
-#else
-
 #define SHA_ROT(X,l,r) (((X) << (l)) | ((X) >> (r)))
 #define SHA_ROL(X,n)   SHA_ROT(X,n,32-(n))
 #define SHA_ROR(X,n)   SHA_ROT(X,32-(n),n)
 
-#endif
-
 /*
  * If you have 32 registers or more, the compiler can (and should)
  * try to change the array[] accesses into registers. However, on
index 5a85d7cd0fe48da00cd4d844576b29af7c3c9afc..2adad545a2e972221869703658e98be0c514d005 100644 (file)
@@ -93,6 +93,7 @@ static int cmd_bundle_create(int argc, const char **argv, const char *prefix) {
        if (!startup_info->have_repository)
                die(_("Need a repository to create a bundle."));
        ret = !!create_bundle(the_repository, bundle_file, argc, argv, &pack_opts, version);
+       strvec_clear(&pack_opts);
        free(bundle_file);
        return ret;
 }
index 650920eba136123e113640439666be104dec3dd7..50cf38999d10125428e4c558383582af4f191b95 100644 (file)
 #include "object-store.h"
 #include "promisor-remote.h"
 
+enum batch_mode {
+       BATCH_MODE_CONTENTS,
+       BATCH_MODE_INFO,
+       BATCH_MODE_QUEUE_AND_DISPATCH,
+};
+
 struct batch_options {
        int enabled;
        int follow_symlinks;
-       int print_contents;
+       enum batch_mode batch_mode;
        int buffer_output;
        int all_objects;
        int unordered;
-       int cmdmode; /* may be 'w' or 'c' for --filters or --textconv */
+       int transform_mode; /* may be 'w' or 'c' for --filters or --textconv */
        const char *format;
 };
 
@@ -150,7 +156,10 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
                break;
 
        case 0:
-               if (type_from_string(exp_type) == OBJ_BLOB) {
+       {
+               enum object_type exp_type_id = type_from_string(exp_type);
+
+               if (exp_type_id == OBJ_BLOB) {
                        struct object_id blob_oid;
                        if (oid_object_info(the_repository, &oid, NULL) == OBJ_TAG) {
                                char *buffer = read_object_file(&oid, &type,
@@ -172,10 +181,10 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
                         * fall-back to the usual case.
                         */
                }
-               buf = read_object_with_reference(the_repository,
-                                                &oid, exp_type, &size, NULL);
+               buf = read_object_with_reference(the_repository, &oid,
+                                                exp_type_id, &size, NULL);
                break;
-
+       }
        default:
                die("git cat-file: unknown option: %s", exp_type);
        }
@@ -302,19 +311,19 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
        if (data->type == OBJ_BLOB) {
                if (opt->buffer_output)
                        fflush(stdout);
-               if (opt->cmdmode) {
+               if (opt->transform_mode) {
                        char *contents;
                        unsigned long size;
 
                        if (!data->rest)
                                die("missing path for '%s'", oid_to_hex(oid));
 
-                       if (opt->cmdmode == 'w') {
+                       if (opt->transform_mode == 'w') {
                                if (filter_object(data->rest, 0100644, oid,
                                                  &contents, &size))
                                        die("could not convert '%s' %s",
                                            oid_to_hex(oid), data->rest);
-                       } else if (opt->cmdmode == 'c') {
+                       } else if (opt->transform_mode == 'c') {
                                enum object_type type;
                                if (!textconv_object(the_repository,
                                                     data->rest, 0100644, oid,
@@ -326,7 +335,7 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
                                        die("could not convert '%s' %s",
                                            oid_to_hex(oid), data->rest);
                        } else
-                               BUG("invalid cmdmode: %c", opt->cmdmode);
+                               BUG("invalid transform_mode: %c", opt->transform_mode);
                        batch_write(opt, contents, size);
                        free(contents);
                } else {
@@ -399,7 +408,7 @@ static void batch_object_write(const char *obj_name,
 
        batch_write(opt, scratch->buf, scratch->len);
 
-       if (opt->print_contents) {
+       if (opt->batch_mode == BATCH_MODE_CONTENTS) {
                print_object_or_die(opt, data);
                batch_write(opt, "\n", 1);
        }
@@ -521,6 +530,135 @@ static int batch_unordered_packed(const struct object_id *oid,
                                      data);
 }
 
+typedef void (*parse_cmd_fn_t)(struct batch_options *, const char *,
+                              struct strbuf *, struct expand_data *);
+
+struct queued_cmd {
+       parse_cmd_fn_t fn;
+       char *line;
+};
+
+static void parse_cmd_contents(struct batch_options *opt,
+                            const char *line,
+                            struct strbuf *output,
+                            struct expand_data *data)
+{
+       opt->batch_mode = BATCH_MODE_CONTENTS;
+       batch_one_object(line, output, opt, data);
+}
+
+static void parse_cmd_info(struct batch_options *opt,
+                          const char *line,
+                          struct strbuf *output,
+                          struct expand_data *data)
+{
+       opt->batch_mode = BATCH_MODE_INFO;
+       batch_one_object(line, output, opt, data);
+}
+
+static void dispatch_calls(struct batch_options *opt,
+               struct strbuf *output,
+               struct expand_data *data,
+               struct queued_cmd *cmd,
+               int nr)
+{
+       int i;
+
+       if (!opt->buffer_output)
+               die(_("flush is only for --buffer mode"));
+
+       for (i = 0; i < nr; i++)
+               cmd[i].fn(opt, cmd[i].line, output, data);
+
+       fflush(stdout);
+}
+
+static void free_cmds(struct queued_cmd *cmd, size_t *nr)
+{
+       size_t i;
+
+       for (i = 0; i < *nr; i++)
+               FREE_AND_NULL(cmd[i].line);
+
+       *nr = 0;
+}
+
+
+static const struct parse_cmd {
+       const char *name;
+       parse_cmd_fn_t fn;
+       unsigned takes_args;
+} commands[] = {
+       { "contents", parse_cmd_contents, 1},
+       { "info", parse_cmd_info, 1},
+       { "flush", NULL, 0},
+};
+
+static void batch_objects_command(struct batch_options *opt,
+                                   struct strbuf *output,
+                                   struct expand_data *data)
+{
+       struct strbuf input = STRBUF_INIT;
+       struct queued_cmd *queued_cmd = NULL;
+       size_t alloc = 0, nr = 0;
+
+       while (!strbuf_getline(&input, stdin)) {
+               int i;
+               const struct parse_cmd *cmd = NULL;
+               const char *p = NULL, *cmd_end;
+               struct queued_cmd call = {0};
+
+               if (!input.len)
+                       die(_("empty command in input"));
+               if (isspace(*input.buf))
+                       die(_("whitespace before command: '%s'"), input.buf);
+
+               for (i = 0; i < ARRAY_SIZE(commands); i++) {
+                       if (!skip_prefix(input.buf, commands[i].name, &cmd_end))
+                               continue;
+
+                       cmd = &commands[i];
+                       if (cmd->takes_args) {
+                               if (*cmd_end != ' ')
+                                       die(_("%s requires arguments"),
+                                           commands[i].name);
+
+                               p = cmd_end + 1;
+                       } else if (*cmd_end) {
+                               die(_("%s takes no arguments"),
+                                   commands[i].name);
+                       }
+
+                       break;
+               }
+
+               if (!cmd)
+                       die(_("unknown command: '%s'"), input.buf);
+
+               if (!strcmp(cmd->name, "flush")) {
+                       dispatch_calls(opt, output, data, queued_cmd, nr);
+                       free_cmds(queued_cmd, &nr);
+               } else if (!opt->buffer_output) {
+                       cmd->fn(opt, p, output, data);
+               } else {
+                       ALLOC_GROW(queued_cmd, nr + 1, alloc);
+                       call.fn = cmd->fn;
+                       call.line = xstrdup_or_null(p);
+                       queued_cmd[nr++] = call;
+               }
+       }
+
+       if (opt->buffer_output &&
+           nr &&
+           !git_env_bool("GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT", 0)) {
+               dispatch_calls(opt, output, data, queued_cmd, nr);
+               free_cmds(queued_cmd, &nr);
+       }
+
+       free(queued_cmd);
+       strbuf_release(&input);
+}
+
 #define DEFAULT_FORMAT "%(objectname) %(objecttype) %(objectsize)"
 
 static int batch_objects(struct batch_options *opt)
@@ -544,7 +682,7 @@ static int batch_objects(struct batch_options *opt)
                      &data);
        data.mark_query = 0;
        strbuf_release(&output);
-       if (opt->cmdmode)
+       if (opt->transform_mode)
                data.split_on_whitespace = 1;
 
        if (opt->format && !strcmp(opt->format, DEFAULT_FORMAT))
@@ -553,7 +691,7 @@ static int batch_objects(struct batch_options *opt)
         * If we are printing out the object, then always fill in the type,
         * since we will want to decide whether or not to stream.
         */
-       if (opt->print_contents)
+       if (opt->batch_mode == BATCH_MODE_CONTENTS)
                data.info.typep = &data.type;
 
        if (opt->all_objects) {
@@ -607,6 +745,11 @@ static int batch_objects(struct batch_options *opt)
        save_warning = warn_on_object_refname_ambiguity;
        warn_on_object_refname_ambiguity = 0;
 
+       if (opt->batch_mode == BATCH_MODE_QUEUE_AND_DISPATCH) {
+               batch_objects_command(opt, &output, &data);
+               goto cleanup;
+       }
+
        while (strbuf_getline(&input, stdin) != EOF) {
                if (data.split_on_whitespace) {
                        /*
@@ -625,6 +768,7 @@ static int batch_objects(struct batch_options *opt)
                batch_one_object(input.buf, &output, opt, &data);
        }
 
+ cleanup:
        strbuf_release(&input);
        strbuf_release(&output);
        warn_on_object_refname_ambiguity = save_warning;
@@ -652,7 +796,16 @@ static int batch_option_callback(const struct option *opt,
        }
 
        bo->enabled = 1;
-       bo->print_contents = !strcmp(opt->long_name, "batch");
+
+       if (!strcmp(opt->long_name, "batch"))
+               bo->batch_mode = BATCH_MODE_CONTENTS;
+       else if (!strcmp(opt->long_name, "batch-check"))
+               bo->batch_mode = BATCH_MODE_INFO;
+       else if (!strcmp(opt->long_name, "batch-command"))
+               bo->batch_mode = BATCH_MODE_QUEUE_AND_DISPATCH;
+       else
+               BUG("%s given to batch-option-callback", opt->long_name);
+
        bo->format = arg;
 
        return 0;
@@ -671,7 +824,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
                N_("git cat-file <type> <object>"),
                N_("git cat-file (-e | -p) <object>"),
                N_("git cat-file (-t | -s) [--allow-unknown-type] <object>"),
-               N_("git cat-file (--batch | --batch-check) [--batch-all-objects]\n"
+               N_("git cat-file (--batch | --batch-check | --batch-command) [--batch-all-objects]\n"
                   "             [--buffer] [--follow-symlinks] [--unordered]\n"
                   "             [--textconv | --filters]"),
                N_("git cat-file (--textconv | --filters)\n"
@@ -700,6 +853,10 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
                        N_("like --batch, but don't emit <contents>"),
                        PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
                        batch_option_callback),
+               OPT_CALLBACK_F(0, "batch-command", &batch, N_("format"),
+                       N_("read commands from stdin"),
+                       PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
+                       batch_option_callback),
                OPT_CMDMODE(0, "batch-all-objects", &opt,
                            N_("with --batch[-check]: ignores stdin, batches all known objects"), 'b'),
                /* Batch-specific options */
@@ -759,7 +916,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
        /* Return early if we're in batch mode? */
        if (batch.enabled) {
                if (opt_cw)
-                       batch.cmdmode = opt;
+                       batch.transform_mode = opt;
                else if (opt && opt != 'b')
                        usage_msg_optf(_("'-%c' is incompatible with batch mode"),
                                       usage, options, opt);
index 9244827ca0281fade95f35b3d21c10a8c5df3a57..797681481d10b552e645237c99a8d9bff7df766a 100644 (file)
@@ -303,7 +303,7 @@ static int checkout_merged(int pos, const struct checkout *state,
         * (it also writes the merge result to the object database even
         * when it may contain conflicts).
         */
-       if (write_object_file(result_buf.ptr, result_buf.size, blob_type, &oid))
+       if (write_object_file(result_buf.ptr, result_buf.size, OBJ_BLOB, &oid))
                die(_("Unable to add merge result for '%s'"), path);
        free(result_buf.ptr);
        ce = make_transient_cache_entry(mode, &oid, path, 2, ce_mem_pool);
@@ -738,6 +738,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
                struct tree_desc trees[2];
                struct tree *tree;
                struct unpack_trees_options topts;
+               const struct object_id *old_commit_oid;
 
                memset(&topts, 0, sizeof(topts));
                topts.head_idx = -1;
@@ -765,9 +766,15 @@ static int merge_working_tree(const struct checkout_opts *opts,
                                       &new_branch_info->commit->object.oid :
                                       &new_branch_info->oid, NULL);
                topts.preserve_ignored = !opts->overwrite_ignore;
-               tree = parse_tree_indirect(old_branch_info->commit ?
-                                          &old_branch_info->commit->object.oid :
-                                          the_hash_algo->empty_tree);
+
+               old_commit_oid = old_branch_info->commit ?
+                       &old_branch_info->commit->object.oid :
+                       the_hash_algo->empty_tree;
+               tree = parse_tree_indirect(old_commit_oid);
+               if (!tree)
+                       die(_("unable to parse commit %s"),
+                               oid_to_hex(old_commit_oid));
+
                init_tree_desc(&trees[0], tree->buffer, tree->size);
                parse_tree(new_tree);
                tree = new_tree;
index a572cda50309b36c797ca39661fe95e073fa317b..52316563795fe34638b3ae22263ebf4e5f456ca5 100644 (file)
@@ -33,6 +33,7 @@
 #include "packfile.h"
 #include "list-objects-filter-options.h"
 #include "hook.h"
+#include "bundle.h"
 
 /*
  * Overall FIXMEs:
@@ -700,6 +701,8 @@ static int checkout(int submodule_progress, int filter_submodules)
        init_checkout_metadata(&opts.meta, head, &oid, NULL);
 
        tree = parse_tree_indirect(&oid);
+       if (!tree)
+               die(_("unable to parse commit %s"), oid_to_hex(&oid));
        parse_tree(tree);
        init_tree_desc(&t, tree->buffer, tree->size);
        if (unpack_trees(1, &t, &opts) < 0)
@@ -1170,6 +1173,18 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                warning(_("--local is ignored"));
        transport->cloning = 1;
 
+       if (is_bundle) {
+               struct bundle_header header = BUNDLE_HEADER_INIT;
+               int fd = read_bundle_header(path, &header);
+               int has_filter = header.filter.choice != LOFC_DISABLED;
+
+               if (fd > 0)
+                       close(fd);
+               bundle_header_release(&header);
+               if (has_filter)
+                       die(_("cannot clone from filtered bundle"));
+       }
+
        transport_set_option(transport, TRANS_OPT_KEEP, "yes");
 
        if (reject_shallow)
index 4247fbde95af2772f69f9160db07db039c44cda4..51c4040ea6c879bda3631343643f3a52063a1bdc 100644 (file)
@@ -192,7 +192,7 @@ static int git_commit_graph_write_config(const char *var, const char *value,
 
 static int graph_write(int argc, const char **argv)
 {
-       struct string_list pack_indexes = STRING_LIST_INIT_NODUP;
+       struct string_list pack_indexes = STRING_LIST_INIT_DUP;
        struct strbuf buf = STRBUF_INIT;
        struct oidset commits = OIDSET_INIT;
        struct object_directory *odb = NULL;
@@ -273,8 +273,8 @@ static int graph_write(int argc, const char **argv)
 
        if (opts.stdin_packs) {
                while (strbuf_getline(&buf, stdin) != EOF)
-                       string_list_append(&pack_indexes,
-                                          strbuf_detach(&buf, NULL));
+                       string_list_append_nodup(&pack_indexes,
+                                                strbuf_detach(&buf, NULL));
        } else if (opts.stdin_commits) {
                oidset_init(&commits, 0);
                if (opts.progress)
index 8b8bdad39593fc23df6ce0af43961b782e0199d3..009a1de0a3d3f69e05a802d7d2356935018b0f81 100644 (file)
@@ -726,11 +726,13 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);
        int old_display_comment_prefix;
        int merge_contains_scissors = 0;
+       int invoked_hook;
 
        /* This checks and barfs if author is badly specified */
        determine_author_info(author_ident);
 
-       if (!no_verify && run_commit_hook(use_editor, index_file, "pre-commit", NULL))
+       if (!no_verify && run_commit_hook(use_editor, index_file, &invoked_hook,
+                                         "pre-commit", NULL))
                return 0;
 
        if (squash_message) {
@@ -1053,10 +1055,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                return 0;
        }
 
-       if (!no_verify && hook_exists("pre-commit")) {
+       if (!no_verify && invoked_hook) {
                /*
-                * Re-read the index as pre-commit hook could have updated it,
-                * and write it out as a tree.  We must do this before we invoke
+                * Re-read the index as the pre-commit-commit hook was invoked
+                * and could have updated it. We must do this before we invoke
                 * the editor and after we invoke run_status above.
                 */
                discard_cache();
@@ -1068,7 +1070,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                return 0;
        }
 
-       if (run_commit_hook(use_editor, index_file, "prepare-commit-msg",
+       if (run_commit_hook(use_editor, index_file, NULL, "prepare-commit-msg",
                            git_path_commit_editmsg(), hook_arg1, hook_arg2, NULL))
                return 0;
 
@@ -1085,7 +1087,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        }
 
        if (!no_verify &&
-           run_commit_hook(use_editor, index_file, "commit-msg", git_path_commit_editmsg(), NULL)) {
+           run_commit_hook(use_editor, index_file, NULL, "commit-msg",
+                           git_path_commit_editmsg(), NULL)) {
                return 0;
        }
 
@@ -1841,7 +1844,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 
        repo_rerere(the_repository, 0);
        run_auto_maintenance(quiet);
-       run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
+       run_commit_hook(use_editor, get_index_file(), NULL, "post-commit",
+                       NULL);
        if (amend && !no_post_rewrite) {
                commit_post_rewrite(the_repository, current_head, &oid);
        }
index 542d8d02b2b00a8675d6bff9245019d0251d043f..e7b88a9c08dc17055dd201eb5f985a77b6150604 100644 (file)
@@ -151,7 +151,7 @@ static struct option builtin_config_options[] = {
        OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
        OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
        OPT_GROUP(N_("Type")),
-       OPT_CALLBACK('t', "type", &type, "", N_("value is given this type"), option_parse_type),
+       OPT_CALLBACK('t', "type", &type, N_("type"), N_("value is given this type"), option_parse_type),
        OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL),
        OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT),
        OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
@@ -612,7 +612,7 @@ static int get_urlmatch(const char *var, const char *url)
 
                strbuf_release(&matched->value);
        }
-       string_list_clear(&config.vars, 1);
+       urlmatch_config_release(&config);
        string_list_clear(&values, 1);
        free(config.url.url);
 
index 510139e9b54ecadf81d91a8a22297fd0ed3b9872..a7d72697fbac1a521a86dfc63e02f8ba113bb68e 100644 (file)
@@ -300,7 +300,7 @@ static void export_blob(const struct object_id *oid)
                if (!buf)
                        die("could not read blob %s", oid_to_hex(oid));
                if (check_object_signature(the_repository, oid, buf, size,
-                                          type_name(type), NULL) < 0)
+                                          type) < 0)
                        die("oid mismatch in blob %s", oid_to_hex(oid));
                object = parse_object_buffer(the_repository, oid, type,
                                             size, buf, &eaten);
index b7105fcad9bae67c93c8087a8206ec962793a4ca..ad045c7c2d8b9510f50dc3a9a94d859b40940e43 100644 (file)
@@ -944,8 +944,8 @@ static int store_object(
        git_hash_ctx c;
        git_zstream s;
 
-       hdrlen = xsnprintf((char *)hdr, sizeof(hdr), "%s %lu",
-                          type_name(type), (unsigned long)dat->len) + 1;
+       hdrlen = format_object_header((char *)hdr, sizeof(hdr), type,
+                                     dat->len);
        the_hash_algo->init_fn(&c);
        the_hash_algo->update_fn(&c, hdr, hdrlen);
        the_hash_algo->update_fn(&c, dat->buf, dat->len);
@@ -1098,7 +1098,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)
        hashfile_checkpoint(pack_file, &checkpoint);
        offset = checkpoint.offset;
 
-       hdrlen = xsnprintf((char *)out_buf, out_sz, "blob %" PRIuMAX, len) + 1;
+       hdrlen = format_object_header((char *)out_buf, out_sz, OBJ_BLOB, len);
 
        the_hash_algo->init_fn(&c);
        the_hash_algo->update_fn(&c, out_buf, hdrlen);
@@ -2490,7 +2490,7 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa
                unsigned long size;
                char *buf = read_object_with_reference(the_repository,
                                                       &commit_oid,
-                                                      commit_type, &size,
+                                                      OBJ_COMMIT, &size,
                                                       &commit_oid);
                if (!buf || size < the_hash_algo->hexsz + 6)
                        die("Not a valid commit: %s", p);
@@ -2562,7 +2562,7 @@ static void parse_from_existing(struct branch *b)
                char *buf;
 
                buf = read_object_with_reference(the_repository,
-                                                &b->oid, commit_type, &size,
+                                                &b->oid, OBJ_COMMIT, &size,
                                                 &b->oid);
                parse_from_commit(b, buf, size);
                free(buf);
@@ -2658,7 +2658,7 @@ static struct hash_list *parse_merge(unsigned int *count)
                        unsigned long size;
                        char *buf = read_object_with_reference(the_repository,
                                                               &n->oid,
-                                                              commit_type,
+                                                              OBJ_COMMIT,
                                                               &size, &n->oid);
                        if (!buf || size < the_hash_algo->hexsz + 6)
                                die("Not a valid commit: %s", from);
index 95832ba1dfd00f6da07a53509aac20f6e8b70295..4d12c2fd4dd35c564887e498483414aac2233315 100644 (file)
@@ -349,7 +349,19 @@ static void clear_item(struct refname_hash_entry *item)
        item->ignore = 1;
 }
 
+
+static void add_already_queued_tags(const char *refname,
+                                   const struct object_id *old_oid,
+                                   const struct object_id *new_oid,
+                                   void *cb_data)
+{
+       struct hashmap *queued_tags = cb_data;
+       if (starts_with(refname, "refs/tags/") && new_oid)
+               (void) refname_hash_add(queued_tags, refname, new_oid);
+}
+
 static void find_non_local_tags(const struct ref *refs,
+                               struct ref_transaction *transaction,
                                struct ref **head,
                                struct ref ***tail)
 {
@@ -367,6 +379,16 @@ static void find_non_local_tags(const struct ref *refs,
        create_fetch_oidset(head, &fetch_oids);
 
        for_each_ref(add_one_refname, &existing_refs);
+
+       /*
+        * If we already have a transaction, then we need to filter out all
+        * tags which have already been queued up.
+        */
+       if (transaction)
+               ref_transaction_for_each_queued_update(transaction,
+                                                      add_already_queued_tags,
+                                                      &existing_refs);
+
        for (ref = refs; ref; ref = ref->next) {
                if (!starts_with(ref->name, "refs/tags/"))
                        continue;
@@ -600,7 +622,7 @@ static struct ref *get_ref_map(struct remote *remote,
                /* also fetch all tags */
                get_fetch_map(remote_refs, tag_refspec, &tail, 0);
        else if (tags == TAGS_DEFAULT && *autotags)
-               find_non_local_tags(remote_refs, &ref_map, &tail);
+               find_non_local_tags(remote_refs, NULL, &ref_map, &tail);
 
        /* Now append any refs to be updated opportunistically: */
        *tail = orefs;
@@ -1083,23 +1105,18 @@ N_("it took %.2f seconds to check forced updates; you can use\n"
    "to avoid this check\n");
 
 static int store_updated_refs(const char *raw_url, const char *remote_name,
-                             int connectivity_checked, struct ref *ref_map,
-                             struct worktree **worktrees)
+                             int connectivity_checked,
+                             struct ref_transaction *transaction, struct ref *ref_map,
+                             struct fetch_head *fetch_head, struct worktree **worktrees)
 {
-       struct fetch_head fetch_head;
        int url_len, i, rc = 0;
        struct strbuf note = STRBUF_INIT, err = STRBUF_INIT;
-       struct ref_transaction *transaction = NULL;
        const char *what, *kind;
        struct ref *rm;
        char *url;
        int want_status;
        int summary_width = 0;
 
-       rc = open_fetch_head(&fetch_head);
-       if (rc)
-               return -1;
-
        if (verbosity >= 0)
                summary_width = transport_summary_width(ref_map);
 
@@ -1118,14 +1135,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                }
        }
 
-       if (atomic_fetch) {
-               transaction = ref_transaction_begin(&err);
-               if (!transaction) {
-                       error("%s", err.buf);
-                       goto abort;
-               }
-       }
-
        prepare_format_display(ref_map);
 
        /*
@@ -1137,7 +1146,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
             want_status <= FETCH_HEAD_IGNORE;
             want_status++) {
                for (rm = ref_map; rm; rm = rm->next) {
-                       struct commit *commit = NULL;
                        struct ref *ref = NULL;
 
                        if (rm->status == REF_STATUS_REJECT_SHALLOW) {
@@ -1148,21 +1156,34 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                        }
 
                        /*
-                        * References in "refs/tags/" are often going to point
-                        * to annotated tags, which are not part of the
-                        * commit-graph. We thus only try to look up refs in
-                        * the graph which are not in that namespace to not
-                        * regress performance in repositories with many
-                        * annotated tags.
+                        * When writing FETCH_HEAD we need to determine whether
+                        * we already have the commit or not. If not, then the
+                        * reference is not for merge and needs to be written
+                        * to the reflog after other commits which we already
+                        * have. We're not interested in this property though
+                        * in case FETCH_HEAD is not to be updated, so we can
+                        * skip the classification in that case.
                         */
-                       if (!starts_with(rm->name, "refs/tags/"))
-                               commit = lookup_commit_in_graph(the_repository, &rm->old_oid);
-                       if (!commit) {
-                               commit = lookup_commit_reference_gently(the_repository,
-                                                                       &rm->old_oid,
-                                                                       1);
-                               if (!commit)
-                                       rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
+                       if (fetch_head->fp) {
+                               struct commit *commit = NULL;
+
+                               /*
+                                * References in "refs/tags/" are often going to point
+                                * to annotated tags, which are not part of the
+                                * commit-graph. We thus only try to look up refs in
+                                * the graph which are not in that namespace to not
+                                * regress performance in repositories with many
+                                * annotated tags.
+                                */
+                               if (!starts_with(rm->name, "refs/tags/"))
+                                       commit = lookup_commit_in_graph(the_repository, &rm->old_oid);
+                               if (!commit) {
+                                       commit = lookup_commit_reference_gently(the_repository,
+                                                                               &rm->old_oid,
+                                                                               1);
+                                       if (!commit)
+                                               rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
+                               }
                        }
 
                        if (rm->fetch_head_status != want_status)
@@ -1209,7 +1230,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                                strbuf_addf(&note, "'%s' of ", what);
                        }
 
-                       append_fetch_head(&fetch_head, &rm->old_oid,
+                       append_fetch_head(fetch_head, &rm->old_oid,
                                          rm->fetch_head_status,
                                          note.buf, url, url_len);
 
@@ -1241,17 +1262,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                }
        }
 
-       if (!rc && transaction) {
-               rc = ref_transaction_commit(transaction, &err);
-               if (rc) {
-                       error("%s", err.buf);
-                       goto abort;
-               }
-       }
-
-       if (!rc)
-               commit_fetch_head(&fetch_head);
-
        if (rc & STORE_REF_ERROR_DF_CONFLICT)
                error(_("some local refs could not be updated; try running\n"
                      " 'git remote prune %s' to remove any old, conflicting "
@@ -1269,9 +1279,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
  abort:
        strbuf_release(&note);
        strbuf_release(&err);
-       ref_transaction_free(transaction);
        free(url);
-       close_fetch_head(&fetch_head);
        return rc;
 }
 
@@ -1311,7 +1319,9 @@ static int check_exist_and_connected(struct ref *ref_map)
 }
 
 static int fetch_and_consume_refs(struct transport *transport,
+                                 struct ref_transaction *transaction,
                                  struct ref *ref_map,
+                                 struct fetch_head *fetch_head,
                                  struct worktree **worktrees)
 {
        int connectivity_checked = 1;
@@ -1334,7 +1344,8 @@ static int fetch_and_consume_refs(struct transport *transport,
 
        trace2_region_enter("fetch", "consume_refs", the_repository);
        ret = store_updated_refs(transport->url, transport->remote->name,
-                                connectivity_checked, ref_map, worktrees);
+                                connectivity_checked, transaction, ref_map,
+                                fetch_head, worktrees);
        trace2_region_leave("fetch", "consume_refs", the_repository);
 
 out:
@@ -1342,11 +1353,14 @@ out:
        return ret;
 }
 
-static int prune_refs(struct refspec *rs, struct ref *ref_map,
+static int prune_refs(struct refspec *rs,
+                     struct ref_transaction *transaction,
+                     struct ref *ref_map,
                      const char *raw_url)
 {
        int url_len, i, result = 0;
        struct ref *ref, *stale_refs = get_stale_heads(rs, ref_map);
+       struct strbuf err = STRBUF_INIT;
        char *url;
        const char *dangling_msg = dry_run
                ? _("   (%s will become dangling)")
@@ -1366,13 +1380,22 @@ static int prune_refs(struct refspec *rs, struct ref *ref_map,
                url_len = i - 3;
 
        if (!dry_run) {
-               struct string_list refnames = STRING_LIST_INIT_NODUP;
+               if (transaction) {
+                       for (ref = stale_refs; ref; ref = ref->next) {
+                               result = ref_transaction_delete(transaction, ref->name, NULL, 0,
+                                                               "fetch: prune", &err);
+                               if (result)
+                                       goto cleanup;
+                       }
+               } else {
+                       struct string_list refnames = STRING_LIST_INIT_NODUP;
 
-               for (ref = stale_refs; ref; ref = ref->next)
-                       string_list_append(&refnames, ref->name);
+                       for (ref = stale_refs; ref; ref = ref->next)
+                               string_list_append(&refnames, ref->name);
 
-               result = delete_refs("fetch: prune", &refnames, 0);
-               string_list_clear(&refnames, 0);
+                       result = delete_refs("fetch: prune", &refnames, 0);
+                       string_list_clear(&refnames, 0);
+               }
        }
 
        if (verbosity >= 0) {
@@ -1393,6 +1416,8 @@ static int prune_refs(struct refspec *rs, struct ref *ref_map,
                }
        }
 
+cleanup:
+       strbuf_release(&err);
        free(url);
        free_refs(stale_refs);
        return result;
@@ -1507,10 +1532,13 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
        return transport;
 }
 
-static void backfill_tags(struct transport *transport, struct ref *ref_map,
-                         struct worktree **worktrees)
+static int backfill_tags(struct transport *transport,
+                        struct ref_transaction *transaction,
+                        struct ref *ref_map,
+                        struct fetch_head *fetch_head,
+                        struct worktree **worktrees)
 {
-       int cannot_reuse;
+       int retcode, cannot_reuse;
 
        /*
         * Once we have set TRANS_OPT_DEEPEN_SINCE, we can't unset it
@@ -1529,18 +1557,21 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map,
        transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
        transport_set_option(transport, TRANS_OPT_DEPTH, "0");
        transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
-       fetch_and_consume_refs(transport, ref_map, worktrees);
+       retcode = fetch_and_consume_refs(transport, transaction, ref_map, fetch_head, worktrees);
 
        if (gsecondary) {
                transport_disconnect(gsecondary);
                gsecondary = NULL;
        }
+
+       return retcode;
 }
 
 static int do_fetch(struct transport *transport,
                    struct refspec *rs)
 {
-       struct ref *ref_map;
+       struct ref_transaction *transaction = NULL;
+       struct ref *ref_map = NULL;
        int autotags = (transport->remote->fetch_tags == 1);
        int retcode = 0;
        const struct ref *remote_refs;
@@ -1548,6 +1579,8 @@ static int do_fetch(struct transport *transport,
                TRANSPORT_LS_REFS_OPTIONS_INIT;
        int must_list_refs = 1;
        struct worktree **worktrees = get_worktrees();
+       struct fetch_head fetch_head = { 0 };
+       struct strbuf err = STRBUF_INIT;
 
        if (tags == TAGS_DEFAULT) {
                if (transport->remote->fetch_tags == 2)
@@ -1605,6 +1638,18 @@ static int do_fetch(struct transport *transport,
        if (!update_head_ok)
                check_not_current_branch(ref_map, worktrees);
 
+       retcode = open_fetch_head(&fetch_head);
+       if (retcode)
+               goto cleanup;
+
+       if (atomic_fetch) {
+               transaction = ref_transaction_begin(&err);
+               if (!transaction) {
+                       retcode = error("%s", err.buf);
+                       goto cleanup;
+               }
+       }
+
        if (tags == TAGS_DEFAULT && autotags)
                transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
        if (prune) {
@@ -1614,21 +1659,61 @@ static int do_fetch(struct transport *transport,
                 * don't care whether --tags was specified.
                 */
                if (rs->nr) {
-                       retcode = prune_refs(rs, ref_map, transport->url);
+                       retcode = prune_refs(rs, transaction, ref_map, transport->url);
                } else {
                        retcode = prune_refs(&transport->remote->fetch,
-                                            ref_map,
+                                            transaction, ref_map,
                                             transport->url);
                }
                if (retcode != 0)
                        retcode = 1;
        }
-       if (fetch_and_consume_refs(transport, ref_map, worktrees)) {
-               free_refs(ref_map);
+
+       if (fetch_and_consume_refs(transport, transaction, ref_map, &fetch_head, worktrees)) {
                retcode = 1;
                goto cleanup;
        }
 
+       /*
+        * If neither --no-tags nor --tags was specified, do automated tag
+        * following.
+        */
+       if (tags == TAGS_DEFAULT && autotags) {
+               struct ref *tags_ref_map = NULL, **tail = &tags_ref_map;
+
+               find_non_local_tags(remote_refs, transaction, &tags_ref_map, &tail);
+               if (tags_ref_map) {
+                       /*
+                        * If backfilling of tags fails then we want to tell
+                        * the user so, but we have to continue regardless to
+                        * populate upstream information of the references we
+                        * have already fetched above. The exception though is
+                        * when `--atomic` is passed: in that case we'll abort
+                        * the transaction and don't commit anything.
+                        */
+                       if (backfill_tags(transport, transaction, tags_ref_map,
+                                         &fetch_head, worktrees))
+                               retcode = 1;
+               }
+
+               free_refs(tags_ref_map);
+       }
+
+       if (transaction) {
+               if (retcode)
+                       goto cleanup;
+
+               retcode = ref_transaction_commit(transaction, &err);
+               if (retcode) {
+                       error("%s", err.buf);
+                       ref_transaction_free(transaction);
+                       transaction = NULL;
+                       goto cleanup;
+               }
+       }
+
+       commit_fetch_head(&fetch_head);
+
        if (set_upstream) {
                struct branch *branch = branch_get("HEAD");
                struct ref *rm;
@@ -1648,7 +1733,7 @@ static int do_fetch(struct transport *transport,
                        if (!rm->peer_ref) {
                                if (source_ref) {
                                        warning(_("multiple branches detected, incompatible with --set-upstream"));
-                                       goto skip;
+                                       goto cleanup;
                                } else {
                                        source_ref = rm;
                                }
@@ -1662,7 +1747,7 @@ static int do_fetch(struct transport *transport,
                                warning(_("could not set upstream of HEAD to '%s' from '%s' when "
                                          "it does not point to any branch."),
                                        shortname, transport->remote->name);
-                               goto skip;
+                               goto cleanup;
                        }
 
                        if (!strcmp(source_ref->name, "HEAD") ||
@@ -1682,21 +1767,16 @@ static int do_fetch(struct transport *transport,
                                  "you need to specify exactly one branch with the --set-upstream option"));
                }
        }
-skip:
-       free_refs(ref_map);
 
-       /* if neither --no-tags nor --tags was specified, do automated tag
-        * following ... */
-       if (tags == TAGS_DEFAULT && autotags) {
-               struct ref **tail = &ref_map;
-               ref_map = NULL;
-               find_non_local_tags(remote_refs, &ref_map, &tail);
-               if (ref_map)
-                       backfill_tags(transport, ref_map, worktrees);
-               free_refs(ref_map);
+cleanup:
+       if (retcode && transaction) {
+               ref_transaction_abort(transaction, &err);
+               error("%s", err.buf);
        }
 
-cleanup:
+       close_fetch_head(&fetch_head);
+       strbuf_release(&err);
+       free_refs(ref_map);
        free_worktrees(worktrees);
        return retcode;
 }
index ffaf0daf5d9b565e0be4c35e586ad1054f981061..b335cffa33561fa80f268260a7edbc7af9ae4136 100644 (file)
@@ -30,7 +30,6 @@
 #include "promisor-remote.h"
 #include "refs.h"
 #include "remote.h"
-#include "object-store.h"
 #include "exec-cmd.h"
 #include "hook.h"
 
index f1a924eadebf45ec6bcc32c22ad89c0716a5be24..bcb07ea7f75ba35079d0093248df0647292665a1 100644 (file)
@@ -484,7 +484,7 @@ static int grep_submodule(struct grep_opt *opt,
                object_type = oid_object_info(subrepo, oid, NULL);
                obj_read_unlock();
                data = read_object_with_reference(subrepo,
-                                                 oid, tree_type,
+                                                 oid, OBJ_TREE,
                                                  &size, NULL);
                if (!data)
                        die(_("unable to read tree (%s)"), oid_to_hex(oid));
@@ -653,7 +653,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
                int hit, len;
 
                data = read_object_with_reference(opt->repo,
-                                                 &obj->oid, tree_type,
+                                                 &obj->oid, OBJ_TREE,
                                                  &size, NULL);
                if (!data)
                        die(_("unable to read tree (%s)"), oid_to_hex(&obj->oid));
index 083784928894b012fd68648f238cef57ae3eaa8f..fbae878c2b951599fcfb82021185c67cc942b5ca 100644 (file)
@@ -25,7 +25,7 @@ static int hash_literally(struct object_id *oid, int fd, const char *type, unsig
        if (strbuf_read(&buf, fd, 4096) < 0)
                ret = -1;
        else
-               ret = hash_object_file_literally(buf.buf, buf.len, type, oid,
+               ret = write_object_file_literally(buf.buf, buf.len, type, oid,
                                                 flags);
        strbuf_release(&buf);
        return ret;
index b4f2ad3f94eea9175cf5855aaee16934ac040bf6..222f994f863cba4226e867bf0f1022e9d551e308 100644 (file)
@@ -51,9 +51,14 @@ static const char *html_path;
 static int verbose = 1;
 static enum help_format help_format = HELP_FORMAT_NONE;
 static int exclude_guides;
+static int show_external_commands = -1;
+static int show_aliases = -1;
 static struct option builtin_help_options[] = {
        OPT_CMDMODE('a', "all", &cmd_mode, N_("print all available commands"),
                    HELP_ACTION_ALL),
+       OPT_BOOL(0, "external-commands", &show_external_commands,
+                N_("show external commands in --all")),
+       OPT_BOOL(0, "aliases", &show_aliases, N_("show aliases in --all")),
        OPT_HIDDEN_BOOL(0, "exclude-guides", &exclude_guides, N_("exclude guides")),
        OPT_SET_INT('m', "man", &help_format, N_("show man page"), HELP_FORMAT_MAN),
        OPT_SET_INT('w', "web", &help_format, N_("show manual in web browser"),
@@ -75,8 +80,8 @@ static struct option builtin_help_options[] = {
 };
 
 static const char * const builtin_help_usage[] = {
-       N_("git help [-a|--all] [--[no-]verbose]]\n"
-          "         [[-i|--info] [-m|--man] [-w|--web]] [<command>]"),
+       "git help [-a|--all] [--[no-]verbose]] [--[no-]external-commands] [--[no-]aliases]",
+       N_("git help [[-i|--info] [-m|--man] [-w|--web]] [<command>]"),
        "git help [-g|--guides]",
        "git help [-c|--config]",
        NULL
@@ -574,11 +579,40 @@ static const char *check_git_cmd(const char* cmd)
        return cmd;
 }
 
-static void no_extra_argc(int argc)
+static void no_help_format(const char *opt_mode, enum help_format fmt)
+{
+       const char *opt_fmt;
+
+       switch (fmt) {
+       case HELP_FORMAT_NONE:
+               return;
+       case HELP_FORMAT_MAN:
+               opt_fmt = "--man";
+               break;
+       case HELP_FORMAT_INFO:
+               opt_fmt = "--info";
+               break;
+       case HELP_FORMAT_WEB:
+               opt_fmt = "--web";
+               break;
+       default:
+               BUG("unreachable");
+       }
+
+       usage_msg_optf(_("options '%s' and '%s' cannot be used together"),
+                      builtin_help_usage, builtin_help_options, opt_mode,
+                      opt_fmt);
+}
+
+static void opt_mode_usage(int argc, const char *opt_mode,
+                          enum help_format fmt)
 {
        if (argc)
-               usage_msg_opt(_("this option doesn't take any other arguments"),
-                             builtin_help_usage, builtin_help_options);
+               usage_msg_optf(_("the '%s' option doesn't take any non-option arguments"),
+                              builtin_help_usage, builtin_help_options,
+                              opt_mode);
+
+       no_help_format(opt_mode, fmt);
 }
 
 int cmd_help(int argc, const char **argv, const char *prefix)
@@ -591,11 +625,19 @@ int cmd_help(int argc, const char **argv, const char *prefix)
                        builtin_help_usage, 0);
        parsed_help_format = help_format;
 
+       if (cmd_mode != HELP_ACTION_ALL &&
+           (show_external_commands >= 0 ||
+            show_aliases >= 0))
+               usage_msg_opt(_("the '--no-[external-commands|aliases]' options can only be used with '--all'"),
+                             builtin_help_usage, builtin_help_options);
+
        switch (cmd_mode) {
        case HELP_ACTION_ALL:
+               opt_mode_usage(argc, "--all", help_format);
                if (verbose) {
                        setup_pager();
-                       list_all_cmds_help();
+                       list_all_cmds_help(show_external_commands,
+                                          show_aliases);
                        return 0;
                }
                printf(_("usage: %s%s"), _(git_usage_string), "\n\n");
@@ -604,20 +646,21 @@ int cmd_help(int argc, const char **argv, const char *prefix)
                printf("%s\n", _(git_more_info_string));
                break;
        case HELP_ACTION_GUIDES:
-               no_extra_argc(argc);
+               opt_mode_usage(argc, "--guides", help_format);
                list_guides_help();
                printf("%s\n", _(git_more_info_string));
                return 0;
        case HELP_ACTION_CONFIG_FOR_COMPLETION:
-               no_extra_argc(argc);
+               opt_mode_usage(argc, "--config-for-completion", help_format);
                list_config_help(SHOW_CONFIG_VARS);
                return 0;
        case HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION:
-               no_extra_argc(argc);
+               opt_mode_usage(argc, "--config-sections-for-completion",
+                              help_format);
                list_config_help(SHOW_CONFIG_SECTIONS);
                return 0;
        case HELP_ACTION_CONFIG:
-               no_extra_argc(argc);
+               opt_mode_usage(argc, "--config", help_format);
                setup_pager();
                list_config_help(SHOW_CONFIG_HUMAN);
                printf("\n%s\n", _("'git help config' for more information"));
index c45273de3b124942553647ae0f216b4e6a3bb3d1..8a5a6447442fe3ad1c2d16db8d7305ae877525a0 100644 (file)
@@ -453,8 +453,7 @@ static void *unpack_entry_data(off_t offset, unsigned long size,
        int hdrlen;
 
        if (!is_delta_type(type)) {
-               hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %"PRIuMAX,
-                                  type_name(type),(uintmax_t)size) + 1;
+               hdrlen = format_object_header(hdr, sizeof(hdr), type, size);
                the_hash_algo->init_fn(&c);
                the_hash_algo->update_fn(&c, hdr, hdrlen);
        } else
@@ -583,7 +582,7 @@ static void *unpack_data(struct object_entry *obj,
                if (!n)
                        die(Q_("premature end of pack file, %"PRIuMAX" byte missing",
                               "premature end of pack file, %"PRIuMAX" bytes missing",
-                              (unsigned int)len),
+                              len),
                            (uintmax_t)len);
                from += n;
                len -= n;
@@ -975,7 +974,7 @@ static struct base_data *resolve_delta(struct object_entry *delta_obj,
        if (!result_data)
                bad_object(delta_obj->idx.offset, _("failed to apply delta"));
        hash_object_file(the_hash_algo, result_data, result_size,
-                        type_name(delta_obj->real_type), &delta_obj->idx.oid);
+                        delta_obj->real_type, &delta_obj->idx.oid);
        sha1_object(result_data, NULL, result_size, delta_obj->real_type,
                    &delta_obj->idx.oid);
 
@@ -1113,6 +1112,7 @@ static void *threaded_second_pass(void *data)
                        list_add(&child->list, &work_head);
                        base_cache_used += child->size;
                        prune_base_data(NULL);
+                       free_base_data(child);
                } else {
                        /*
                         * This child does not have its own children. It may be
@@ -1135,6 +1135,7 @@ static void *threaded_second_pass(void *data)
 
                                p = next_p;
                        }
+                       FREE_AND_NULL(child);
                }
                work_unlock();
        }
@@ -1417,9 +1418,8 @@ static void fix_unresolved_deltas(struct hashfile *f)
                if (!data)
                        continue;
 
-               if (check_object_signature(the_repository, &d->oid,
-                                          data, size,
-                                          type_name(type), NULL))
+               if (check_object_signature(the_repository, &d->oid, data, size,
+                                          type) < 0)
                        die(_("local object %s is corrupt"), oid_to_hex(&d->oid));
 
                /*
@@ -1428,6 +1428,7 @@ static void fix_unresolved_deltas(struct hashfile *f)
                 * object).
                 */
                append_obj_to_pack(f, d->oid.hash, data, size, type);
+               free(data);
                threaded_second_pass(NULL);
 
                display_progress(progress, nr_resolved_deltas);
@@ -1707,6 +1708,7 @@ static void show_pack_info(int stat_only)
                          i + 1,
                          chain_histogram[i]);
        }
+       free(chain_histogram);
 }
 
 int cmd_index_pack(int argc, const char **argv, const char *prefix)
@@ -1936,6 +1938,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        if (do_fsck_object && fsck_finish(&fsck_options))
                die(_("fsck error in pack objects"));
 
+       free(opts.anomaly);
        free(objects);
        strbuf_release(&index_name_buf);
        strbuf_release(&rev_index_name_buf);
index 26b84980dbd795372f3a4f8e6bc841d8f14bbcb2..a11f8c6e4bb04a25a8763ba9930ab86079a1f14c 100644 (file)
@@ -138,6 +138,7 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)
        int rev_nr = 0;
        int show_all = 0;
        int cmdmode = 0;
+       int ret;
 
        struct option options[] = {
                OPT_BOOL('a', "all", &show_all, N_("output all common ancestors")),
@@ -186,5 +187,7 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)
        ALLOC_ARRAY(rev, argc);
        while (argc-- > 0)
                rev[rev_nr++] = get_commit_reference(*argv++);
-       return show_merge_base(rev, rev_nr, show_all);
+       ret = show_merge_base(rev, rev_nr, show_all);
+       free(rev);
+       return ret;
 }
index a4bfd8fc51d6b26edda7a1c8afca4946f6ff485d..b9acbf5d3427e7dd88533351db5d2fc245cc25f6 100644 (file)
@@ -58,7 +58,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
                                   "Ignoring %s.",
                                   "cannot handle more than %d bases. "
                                   "Ignoring %s.",
-                                   (int)ARRAY_SIZE(bases)-1),
+                                   ARRAY_SIZE(bases)-1),
                                (int)ARRAY_SIZE(bases)-1, argv[i]);
        }
        if (argc - i != 3) /* "--" "<head>" "<remote>" */
index a94a03384ae6da259a1c6abf5fcd5b4b88b8325a..f178f5a3ee18c71d2acda9455b020f9c03da9509 100644 (file)
@@ -845,15 +845,20 @@ static void prepare_to_commit(struct commit_list *remoteheads)
        struct strbuf msg = STRBUF_INIT;
        const char *index_file = get_index_file();
 
-       if (!no_verify && run_commit_hook(0 < option_edit, index_file, "pre-merge-commit", NULL))
-               abort_commit(remoteheads, NULL);
-       /*
-        * Re-read the index as pre-merge-commit hook could have updated it,
-        * and write it out as a tree.  We must do this before we invoke
-        * the editor and after we invoke run_status above.
-        */
-       if (hook_exists("pre-merge-commit"))
-               discard_cache();
+       if (!no_verify) {
+               int invoked_hook;
+
+               if (run_commit_hook(0 < option_edit, index_file, &invoked_hook,
+                                   "pre-merge-commit", NULL))
+                       abort_commit(remoteheads, NULL);
+               /*
+                * Re-read the index as pre-merge-commit hook could have updated it,
+                * and write it out as a tree.  We must do this before we invoke
+                * the editor and after we invoke run_status above.
+                */
+               if (invoked_hook)
+                       discard_cache();
+       }
        read_cache_from(index_file);
        strbuf_addbuf(&msg, &merge_msg);
        if (squash)
@@ -875,7 +880,8 @@ static void prepare_to_commit(struct commit_list *remoteheads)
                append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);
        write_merge_heads(remoteheads);
        write_file_buf(git_path_merge_msg(the_repository), msg.buf, msg.len);
-       if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg",
+       if (run_commit_hook(0 < option_edit, get_index_file(), NULL,
+                           "prepare-commit-msg",
                            git_path_merge_msg(the_repository), "merge", NULL))
                abort_commit(remoteheads, NULL);
        if (0 < option_edit) {
@@ -884,7 +890,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)
        }
 
        if (!no_verify && run_commit_hook(0 < option_edit, get_index_file(),
-                                         "commit-msg",
+                                         NULL, "commit-msg",
                                          git_path_merge_msg(the_repository), NULL))
                abort_commit(remoteheads, NULL);
 
index c7b905c614be6a4b6b8760d461fcfb2d35ce2927..5d22909122d195873f2427812084dc8bd4319180 100644 (file)
@@ -61,9 +61,8 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)
                    type_name(*tagged_type), type_name(type));
 
        repl = lookup_replace_object(the_repository, tagged_oid);
-       ret = check_object_signature(the_repository, repl,
-                                    buffer, size, type_name(*tagged_type),
-                                    NULL);
+       ret = check_object_signature(the_repository, repl, buffer, size,
+                                    *tagged_type);
        free(buffer);
 
        return ret;
@@ -97,10 +96,10 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)
                                &tagged_oid, &tagged_type))
                die(_("tag on stdin did not pass our strict fsck check"));
 
-       if (verify_object_in_tag(&tagged_oid, &tagged_type))
+       if (verify_object_in_tag(&tagged_oid, &tagged_type) < 0)
                die(_("tag on stdin did not refer to a valid object"));
 
-       if (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)
+       if (write_object_file(buf.buf, buf.len, OBJ_TAG, &result) < 0)
                die(_("unable to write tag file"));
 
        strbuf_release(&buf);
index 8bdaada922a2158c469c18a3128b267eafd671c5..902edba6d2cb43d69addf875a0e5a58e3abe879d 100644 (file)
@@ -58,7 +58,7 @@ static void write_tree(struct object_id *oid)
                strbuf_add(&buf, ent->oid.hash, the_hash_algo->rawsz);
        }
 
-       write_object_file(buf.buf, buf.len, tree_type, oid);
+       write_object_file(buf.buf, buf.len, OBJ_TREE, oid);
        strbuf_release(&buf);
 }
 
index 929591269ddf7f6a2ff4c7542bf409ac2bcf751d..c59b5699fe80ece6bab54173222a265e6ee5f6d9 100644 (file)
@@ -9,6 +9,7 @@
 #include "prio-queue.h"
 #include "hash-lookup.h"
 #include "commit-slab.h"
+#include "commit-graph.h"
 
 /*
  * One day.  See the 'name a rev shortly after epoch' test in t6120 when
@@ -26,9 +27,58 @@ struct rev_name {
 
 define_commit_slab(commit_rev_name, struct rev_name);
 
+static timestamp_t generation_cutoff = GENERATION_NUMBER_INFINITY;
 static timestamp_t cutoff = TIME_MAX;
 static struct commit_rev_name rev_names;
 
+/* Disable the cutoff checks entirely */
+static void disable_cutoff(void)
+{
+       generation_cutoff = 0;
+       cutoff = 0;
+}
+
+/* Cutoff searching any commits older than this one */
+static void set_commit_cutoff(struct commit *commit)
+{
+
+       if (cutoff > commit->date)
+               cutoff = commit->date;
+
+       if (generation_cutoff) {
+               timestamp_t generation = commit_graph_generation(commit);
+
+               if (generation_cutoff > generation)
+                       generation_cutoff = generation;
+       }
+}
+
+/* adjust the commit date cutoff with a slop to allow for slightly incorrect
+ * commit timestamps in case of clock skew.
+ */
+static void adjust_cutoff_timestamp_for_slop(void)
+{
+       if (cutoff) {
+               /* check for undeflow */
+               if (cutoff > TIME_MIN + CUTOFF_DATE_SLOP)
+                       cutoff = cutoff - CUTOFF_DATE_SLOP;
+               else
+                       cutoff = TIME_MIN;
+       }
+}
+
+/* Check if a commit is before the cutoff. Prioritize generation numbers
+ * first, but use the commit timestamp if we lack generation data.
+ */
+static int commit_is_before_cutoff(struct commit *commit)
+{
+       if (generation_cutoff < GENERATION_NUMBER_INFINITY)
+               return generation_cutoff &&
+                       commit_graph_generation(commit) < generation_cutoff;
+
+       return commit->date < cutoff;
+}
+
 /* How many generations are maximally preferred over _one_ merge traversal? */
 #define MERGE_TRAVERSAL_WEIGHT 65535
 
@@ -151,7 +201,7 @@ static void name_rev(struct commit *start_commit,
        struct rev_name *start_name;
 
        parse_commit(start_commit);
-       if (start_commit->date < cutoff)
+       if (commit_is_before_cutoff(start_commit))
                return;
 
        start_name = create_or_update_name(start_commit, taggerdate, 0, 0,
@@ -181,7 +231,7 @@ static void name_rev(struct commit *start_commit,
                        int generation, distance;
 
                        parse_commit(parent);
-                       if (parent->date < cutoff)
+                       if (commit_is_before_cutoff(parent))
                                continue;
 
                        if (parent_number > 1) {
@@ -568,7 +618,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                usage_with_options(name_rev_usage, opts);
        }
        if (all || annotate_stdin)
-               cutoff = 0;
+               disable_cutoff();
 
        for (; argc; argc--, argv++) {
                struct object_id oid;
@@ -596,10 +646,8 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                        continue;
                }
 
-               if (commit) {
-                       if (cutoff > commit->date)
-                               cutoff = commit->date;
-               }
+               if (commit)
+                       set_commit_cutoff(commit);
 
                if (peel_tag) {
                        if (!commit) {
@@ -612,13 +660,8 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                add_object_array(object, *argv, &revs);
        }
 
-       if (cutoff) {
-               /* check for undeflow */
-               if (cutoff > TIME_MIN + CUTOFF_DATE_SLOP)
-                       cutoff = cutoff - CUTOFF_DATE_SLOP;
-               else
-                       cutoff = TIME_MIN;
-       }
+       adjust_cutoff_timestamp_for_slop();
+
        for_each_ref(name_ref, &data);
        name_tips();
 
index f99593a1853df1ee4bdcf60e3e904cbe55c474f2..a3d0d15a227f2142f2d9d5fddbe0017d0e7a5c7f 100644 (file)
@@ -199,7 +199,7 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
 
 static void write_note_data(struct note_data *d, struct object_id *oid)
 {
-       if (write_object_file(d->buf.buf, d->buf.len, blob_type, oid)) {
+       if (write_object_file(d->buf.buf, d->buf.len, OBJ_BLOB, oid)) {
                int status = die_message(_("unable to write note object"));
 
                if (d->edit_path)
index 178e611f09ddcddd9f5652cf25db258877e04d34..829ca359cf9c4553c8658229ac85ba087e8c9b12 100644 (file)
@@ -1802,7 +1802,7 @@ static void add_preferred_base(struct object_id *oid)
                return;
 
        data = read_object_with_reference(the_repository, oid,
-                                         tree_type, &size, &tree_oid);
+                                         OBJ_TREE, &size, &tree_oid);
        if (!data)
                return;
 
@@ -3651,7 +3651,7 @@ static int pack_options_allow_reuse(void)
 
 static int get_object_list_from_bitmap(struct rev_info *revs)
 {
-       if (!(bitmap_git = prepare_bitmap_walk(revs, &filter_options, 0)))
+       if (!(bitmap_git = prepare_bitmap_walk(revs, 0)))
                return -1;
 
        if (pack_options_allow_reuse() &&
@@ -3727,6 +3727,7 @@ static void get_object_list(int ac, const char **av)
        repo_init_revisions(the_repository, &revs, NULL);
        save_commit_buffer = 0;
        setup_revisions(ac, av, &revs, &s_r_opt);
+       list_objects_filter_copy(&revs.filter, &filter_options);
 
        /* make sure shallows are read */
        is_repository_shallow(the_repository);
@@ -3777,9 +3778,9 @@ static void get_object_list(int ac, const char **av)
 
        if (!fn_show_object)
                fn_show_object = show_object;
-       traverse_commit_list_filtered(&filter_options, &revs,
-                                     show_commit, fn_show_object, NULL,
-                                     NULL);
+       traverse_commit_list(&revs,
+                            show_commit, fn_show_object,
+                            NULL);
 
        if (unpack_unreachable_expiration) {
                revs.ignore_missing_links = 1;
index 2109c4c9e5c1c747ea08c8e2152b610c5159ddbe..9f1f33e95466a4b2ff4f99a3b5e95878e40c6d38 100644 (file)
@@ -160,15 +160,22 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
        argc = parse_options(argc, argv, cmd_prefix, read_tree_options,
                             read_tree_usage, 0);
 
-       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
-
        prefix_set = opts.prefix ? 1 : 0;
        if (1 < opts.merge + opts.reset + prefix_set)
                die("Which one? -m, --reset, or --prefix?");
 
+       /* Prefix should not start with a directory separator */
+       if (opts.prefix && opts.prefix[0] == '/')
+               die("Invalid prefix, prefix cannot start with '/'");
+
        if (opts.reset)
                opts.reset = UNPACK_RESET_OVERWRITE_UNTRACKED;
 
+       prepare_repo_settings(the_repository);
+       the_repository->settings.command_requires_full_index = 0;
+
+       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+
        /*
         * NEEDSWORK
         *
@@ -210,6 +217,9 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
        if (opts.merge && !opts.index_only)
                setup_work_tree();
 
+       if (opts.skip_sparse_checkout)
+               ensure_full_index(&the_index);
+
        if (opts.merge) {
                switch (stage - 1) {
                case 0:
index d10aeb7e78fb70e32fb6268505c85cf86ca2a212..9aabffa1afb646f0513068de3d6366a7bc58809a 100644 (file)
@@ -749,7 +749,7 @@ static void prepare_push_cert_sha1(struct child_process *proc)
                int bogs /* beginning_of_gpg_sig */;
 
                already_done = 1;
-               if (write_object_file(push_cert.buf, push_cert.len, "blob",
+               if (write_object_file(push_cert.buf, push_cert.len, OBJ_BLOB,
                                      &push_cert_oid))
                        oidclr(&push_cert_oid);
 
@@ -813,13 +813,14 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed,
        proc.trace2_hook_name = hook_name;
 
        if (feed_state->push_options) {
-               int i;
+               size_t i;
                for (i = 0; i < feed_state->push_options->nr; i++)
                        strvec_pushf(&proc.env_array,
-                                    "GIT_PUSH_OPTION_%d=%s", i,
+                                    "GIT_PUSH_OPTION_%"PRIuMAX"=%s",
+                                    (uintmax_t)i,
                                     feed_state->push_options->items[i].string);
-               strvec_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT=%d",
-                            feed_state->push_options->nr);
+               strvec_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT=%"PRIuMAX"",
+                            (uintmax_t)feed_state->push_options->nr);
        } else
                strvec_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT");
 
@@ -1408,10 +1409,12 @@ static const char *push_to_deploy(unsigned char *sha1,
 static const char *push_to_checkout_hook = "push-to-checkout";
 
 static const char *push_to_checkout(unsigned char *hash,
+                                   int *invoked_hook,
                                    struct strvec *env,
                                    const char *work_tree)
 {
        struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
+       opt.invoked_hook = invoked_hook;
 
        strvec_pushf(env, "GIT_WORK_TREE=%s", absolute_path(work_tree));
        strvec_pushv(&opt.env, env->v);
@@ -1426,6 +1429,7 @@ static const char *update_worktree(unsigned char *sha1, const struct worktree *w
 {
        const char *retval, *git_dir;
        struct strvec env = STRVEC_INIT;
+       int invoked_hook;
 
        if (!worktree || !worktree->path)
                BUG("worktree->path must be non-NULL");
@@ -1436,10 +1440,9 @@ static const char *update_worktree(unsigned char *sha1, const struct worktree *w
 
        strvec_pushf(&env, "GIT_DIR=%s", absolute_path(git_dir));
 
-       if (!hook_exists(push_to_checkout_hook))
+       retval = push_to_checkout(sha1, &invoked_hook, &env, worktree->path);
+       if (!invoked_hook)
                retval = push_to_deploy(sha1, &env, worktree->path);
-       else
-               retval = push_to_checkout(sha1, &env, worktree->path);
 
        strvec_clear(&env);
        return retval;
index 016466852f178e35dacb2557831c49d3b56e0225..9407f835cb6fe5c1d921b312a441eff84db961bf 100644 (file)
@@ -1,16 +1,9 @@
 #include "builtin.h"
 #include "config.h"
-#include "lockfile.h"
-#include "object-store.h"
-#include "repository.h"
-#include "commit.h"
-#include "refs.h"
-#include "dir.h"
-#include "tree-walk.h"
-#include "diff.h"
 #include "revision.h"
 #include "reachable.h"
 #include "worktree.h"
+#include "reflog.h"
 
 static const char reflog_exists_usage[] =
 N_("git reflog exists <ref>");
@@ -18,404 +11,11 @@ N_("git reflog exists <ref>");
 static timestamp_t default_reflog_expire;
 static timestamp_t default_reflog_expire_unreachable;
 
-struct cmd_reflog_expire_cb {
-       int stalefix;
-       int explicit_expiry;
-       timestamp_t expire_total;
-       timestamp_t expire_unreachable;
-       int recno;
-};
-
-struct expire_reflog_policy_cb {
-       enum {
-               UE_NORMAL,
-               UE_ALWAYS,
-               UE_HEAD
-       } unreachable_expire_kind;
-       struct commit_list *mark_list;
-       unsigned long mark_limit;
-       struct cmd_reflog_expire_cb cmd;
-       struct commit *tip_commit;
-       struct commit_list *tips;
-       unsigned int dry_run:1;
-};
-
 struct worktree_reflogs {
        struct worktree *worktree;
        struct string_list reflogs;
 };
 
-/* Remember to update object flag allocation in object.h */
-#define INCOMPLETE     (1u<<10)
-#define STUDYING       (1u<<11)
-#define REACHABLE      (1u<<12)
-
-static int tree_is_complete(const struct object_id *oid)
-{
-       struct tree_desc desc;
-       struct name_entry entry;
-       int complete;
-       struct tree *tree;
-
-       tree = lookup_tree(the_repository, oid);
-       if (!tree)
-               return 0;
-       if (tree->object.flags & SEEN)
-               return 1;
-       if (tree->object.flags & INCOMPLETE)
-               return 0;
-
-       if (!tree->buffer) {
-               enum object_type type;
-               unsigned long size;
-               void *data = read_object_file(oid, &type, &size);
-               if (!data) {
-                       tree->object.flags |= INCOMPLETE;
-                       return 0;
-               }
-               tree->buffer = data;
-               tree->size = size;
-       }
-       init_tree_desc(&desc, tree->buffer, tree->size);
-       complete = 1;
-       while (tree_entry(&desc, &entry)) {
-               if (!has_object_file(&entry.oid) ||
-                   (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) {
-                       tree->object.flags |= INCOMPLETE;
-                       complete = 0;
-               }
-       }
-       free_tree_buffer(tree);
-
-       if (complete)
-               tree->object.flags |= SEEN;
-       return complete;
-}
-
-static int commit_is_complete(struct commit *commit)
-{
-       struct object_array study;
-       struct object_array found;
-       int is_incomplete = 0;
-       int i;
-
-       /* early return */
-       if (commit->object.flags & SEEN)
-               return 1;
-       if (commit->object.flags & INCOMPLETE)
-               return 0;
-       /*
-        * Find all commits that are reachable and are not marked as
-        * SEEN.  Then make sure the trees and blobs contained are
-        * complete.  After that, mark these commits also as SEEN.
-        * If some of the objects that are needed to complete this
-        * commit are missing, mark this commit as INCOMPLETE.
-        */
-       memset(&study, 0, sizeof(study));
-       memset(&found, 0, sizeof(found));
-       add_object_array(&commit->object, NULL, &study);
-       add_object_array(&commit->object, NULL, &found);
-       commit->object.flags |= STUDYING;
-       while (study.nr) {
-               struct commit *c;
-               struct commit_list *parent;
-
-               c = (struct commit *)object_array_pop(&study);
-               if (!c->object.parsed && !parse_object(the_repository, &c->object.oid))
-                       c->object.flags |= INCOMPLETE;
-
-               if (c->object.flags & INCOMPLETE) {
-                       is_incomplete = 1;
-                       break;
-               }
-               else if (c->object.flags & SEEN)
-                       continue;
-               for (parent = c->parents; parent; parent = parent->next) {
-                       struct commit *p = parent->item;
-                       if (p->object.flags & STUDYING)
-                               continue;
-                       p->object.flags |= STUDYING;
-                       add_object_array(&p->object, NULL, &study);
-                       add_object_array(&p->object, NULL, &found);
-               }
-       }
-       if (!is_incomplete) {
-               /*
-                * make sure all commits in "found" array have all the
-                * necessary objects.
-                */
-               for (i = 0; i < found.nr; i++) {
-                       struct commit *c =
-                               (struct commit *)found.objects[i].item;
-                       if (!tree_is_complete(get_commit_tree_oid(c))) {
-                               is_incomplete = 1;
-                               c->object.flags |= INCOMPLETE;
-                       }
-               }
-               if (!is_incomplete) {
-                       /* mark all found commits as complete, iow SEEN */
-                       for (i = 0; i < found.nr; i++)
-                               found.objects[i].item->flags |= SEEN;
-               }
-       }
-       /* clear flags from the objects we traversed */
-       for (i = 0; i < found.nr; i++)
-               found.objects[i].item->flags &= ~STUDYING;
-       if (is_incomplete)
-               commit->object.flags |= INCOMPLETE;
-       else {
-               /*
-                * If we come here, we have (1) traversed the ancestry chain
-                * from the "commit" until we reach SEEN commits (which are
-                * known to be complete), and (2) made sure that the commits
-                * encountered during the above traversal refer to trees that
-                * are complete.  Which means that we know *all* the commits
-                * we have seen during this process are complete.
-                */
-               for (i = 0; i < found.nr; i++)
-                       found.objects[i].item->flags |= SEEN;
-       }
-       /* free object arrays */
-       object_array_clear(&study);
-       object_array_clear(&found);
-       return !is_incomplete;
-}
-
-static int keep_entry(struct commit **it, struct object_id *oid)
-{
-       struct commit *commit;
-
-       if (is_null_oid(oid))
-               return 1;
-       commit = lookup_commit_reference_gently(the_repository, oid, 1);
-       if (!commit)
-               return 0;
-
-       /*
-        * Make sure everything in this commit exists.
-        *
-        * We have walked all the objects reachable from the refs
-        * and cache earlier.  The commits reachable by this commit
-        * must meet SEEN commits -- and then we should mark them as
-        * SEEN as well.
-        */
-       if (!commit_is_complete(commit))
-               return 0;
-       *it = commit;
-       return 1;
-}
-
-/*
- * Starting from commits in the cb->mark_list, mark commits that are
- * reachable from them.  Stop the traversal at commits older than
- * the expire_limit and queue them back, so that the caller can call
- * us again to restart the traversal with longer expire_limit.
- */
-static void mark_reachable(struct expire_reflog_policy_cb *cb)
-{
-       struct commit_list *pending;
-       timestamp_t expire_limit = cb->mark_limit;
-       struct commit_list *leftover = NULL;
-
-       for (pending = cb->mark_list; pending; pending = pending->next)
-               pending->item->object.flags &= ~REACHABLE;
-
-       pending = cb->mark_list;
-       while (pending) {
-               struct commit_list *parent;
-               struct commit *commit = pop_commit(&pending);
-               if (commit->object.flags & REACHABLE)
-                       continue;
-               if (parse_commit(commit))
-                       continue;
-               commit->object.flags |= REACHABLE;
-               if (commit->date < expire_limit) {
-                       commit_list_insert(commit, &leftover);
-                       continue;
-               }
-               commit->object.flags |= REACHABLE;
-               parent = commit->parents;
-               while (parent) {
-                       commit = parent->item;
-                       parent = parent->next;
-                       if (commit->object.flags & REACHABLE)
-                               continue;
-                       commit_list_insert(commit, &pending);
-               }
-       }
-       cb->mark_list = leftover;
-}
-
-static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit, struct object_id *oid)
-{
-       /*
-        * We may or may not have the commit yet - if not, look it
-        * up using the supplied sha1.
-        */
-       if (!commit) {
-               if (is_null_oid(oid))
-                       return 0;
-
-               commit = lookup_commit_reference_gently(the_repository, oid,
-                                                       1);
-
-               /* Not a commit -- keep it */
-               if (!commit)
-                       return 0;
-       }
-
-       /* Reachable from the current ref?  Don't prune. */
-       if (commit->object.flags & REACHABLE)
-               return 0;
-
-       if (cb->mark_list && cb->mark_limit) {
-               cb->mark_limit = 0; /* dig down to the root */
-               mark_reachable(cb);
-       }
-
-       return !(commit->object.flags & REACHABLE);
-}
-
-/*
- * Return true iff the specified reflog entry should be expired.
- */
-static int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
-                                   const char *email, timestamp_t timestamp, int tz,
-                                   const char *message, void *cb_data)
-{
-       struct expire_reflog_policy_cb *cb = cb_data;
-       struct commit *old_commit, *new_commit;
-
-       if (timestamp < cb->cmd.expire_total)
-               return 1;
-
-       old_commit = new_commit = NULL;
-       if (cb->cmd.stalefix &&
-           (!keep_entry(&old_commit, ooid) || !keep_entry(&new_commit, noid)))
-               return 1;
-
-       if (timestamp < cb->cmd.expire_unreachable) {
-               switch (cb->unreachable_expire_kind) {
-               case UE_ALWAYS:
-                       return 1;
-               case UE_NORMAL:
-               case UE_HEAD:
-                       if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid))
-                               return 1;
-                       break;
-               }
-       }
-
-       if (cb->cmd.recno && --(cb->cmd.recno) == 0)
-               return 1;
-
-       return 0;
-}
-
-static int should_expire_reflog_ent_verbose(struct object_id *ooid,
-                                           struct object_id *noid,
-                                           const char *email,
-                                           timestamp_t timestamp, int tz,
-                                           const char *message, void *cb_data)
-{
-       struct expire_reflog_policy_cb *cb = cb_data;
-       int expire;
-
-       expire = should_expire_reflog_ent(ooid, noid, email, timestamp, tz,
-                                         message, cb);
-
-       if (!expire)
-               printf("keep %s", message);
-       else if (cb->dry_run)
-               printf("would prune %s", message);
-       else
-               printf("prune %s", message);
-
-       return expire;
-}
-
-static int push_tip_to_list(const char *refname, const struct object_id *oid,
-                           int flags, void *cb_data)
-{
-       struct commit_list **list = cb_data;
-       struct commit *tip_commit;
-       if (flags & REF_ISSYMREF)
-               return 0;
-       tip_commit = lookup_commit_reference_gently(the_repository, oid, 1);
-       if (!tip_commit)
-               return 0;
-       commit_list_insert(tip_commit, list);
-       return 0;
-}
-
-static int is_head(const char *refname)
-{
-       switch (ref_type(refname)) {
-       case REF_TYPE_OTHER_PSEUDOREF:
-       case REF_TYPE_MAIN_PSEUDOREF:
-               if (parse_worktree_ref(refname, NULL, NULL, &refname))
-                       BUG("not a worktree ref: %s", refname);
-               break;
-       default:
-               break;
-       }
-       return !strcmp(refname, "HEAD");
-}
-
-static void reflog_expiry_prepare(const char *refname,
-                                 const struct object_id *oid,
-                                 void *cb_data)
-{
-       struct expire_reflog_policy_cb *cb = cb_data;
-       struct commit_list *elem;
-       struct commit *commit = NULL;
-
-       if (!cb->cmd.expire_unreachable || is_head(refname)) {
-               cb->unreachable_expire_kind = UE_HEAD;
-       } else {
-               commit = lookup_commit(the_repository, oid);
-               cb->unreachable_expire_kind = commit ? UE_NORMAL : UE_ALWAYS;
-       }
-
-       if (cb->cmd.expire_unreachable <= cb->cmd.expire_total)
-               cb->unreachable_expire_kind = UE_ALWAYS;
-
-       switch (cb->unreachable_expire_kind) {
-       case UE_ALWAYS:
-               return;
-       case UE_HEAD:
-               for_each_ref(push_tip_to_list, &cb->tips);
-               for (elem = cb->tips; elem; elem = elem->next)
-                       commit_list_insert(elem->item, &cb->mark_list);
-               break;
-       case UE_NORMAL:
-               commit_list_insert(commit, &cb->mark_list);
-               /* For reflog_expiry_cleanup() below */
-               cb->tip_commit = commit;
-       }
-       cb->mark_limit = cb->cmd.expire_total;
-       mark_reachable(cb);
-}
-
-static void reflog_expiry_cleanup(void *cb_data)
-{
-       struct expire_reflog_policy_cb *cb = cb_data;
-       struct commit_list *elem;
-
-       switch (cb->unreachable_expire_kind) {
-       case UE_ALWAYS:
-               return;
-       case UE_HEAD:
-               for (elem = cb->tips; elem; elem = elem->next)
-                       clear_commit_marks(elem->item, REACHABLE);
-               free_commit_list(cb->tips);
-               break;
-       case UE_NORMAL:
-               clear_commit_marks(cb->tip_commit, REACHABLE);
-               break;
-       }
-}
-
 static int collect_reflog(const char *ref, const struct object_id *oid, int unused, void *cb_data)
 {
        struct worktree_reflogs *cb = cb_data;
@@ -704,16 +304,6 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
        return status;
 }
 
-static int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
-               const char *email, timestamp_t timestamp, int tz,
-               const char *message, void *cb_data)
-{
-       struct cmd_reflog_expire_cb *cb = cb_data;
-       if (!cb->expire_total || timestamp < cb->expire_total)
-               cb->recno++;
-       return 0;
-}
-
 static const char * reflog_delete_usage[] = {
        N_("git reflog delete [--rewrite] [--updateref] "
           "[--dry-run | -n] [--verbose] <refs>..."),
@@ -722,11 +312,10 @@ static const char * reflog_delete_usage[] = {
 
 static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
 {
-       struct cmd_reflog_expire_cb cmd = { 0 };
        int i, status = 0;
        unsigned int flags = 0;
        int verbose = 0;
-       reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
+
        const struct option options[] = {
                OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"),
                        EXPIRE_REFLOGS_DRY_RUN),
@@ -742,48 +331,12 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
 
        argc = parse_options(argc, argv, prefix, options, reflog_delete_usage, 0);
 
-       if (verbose)
-               should_prune_fn = should_expire_reflog_ent_verbose;
-
        if (argc < 1)
                return error(_("no reflog specified to delete"));
 
-       for (i = 0; i < argc; i++) {
-               const char *spec = strstr(argv[i], "@{");
-               char *ep, *ref;
-               int recno;
-               struct expire_reflog_policy_cb cb = {
-                       .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
-               };
-
-               if (!spec) {
-                       status |= error(_("not a reflog: %s"), argv[i]);
-                       continue;
-               }
+       for (i = 0; i < argc; i++)
+               status |= reflog_delete(argv[i], flags, verbose);
 
-               if (!dwim_log(argv[i], spec - argv[i], NULL, &ref)) {
-                       status |= error(_("no reflog for '%s'"), argv[i]);
-                       continue;
-               }
-
-               recno = strtoul(spec + 2, &ep, 10);
-               if (*ep == '}') {
-                       cmd.recno = -recno;
-                       for_each_reflog_ent(ref, count_reflog_ent, &cmd);
-               } else {
-                       cmd.expire_total = approxidate(spec + 2);
-                       for_each_reflog_ent(ref, count_reflog_ent, &cmd);
-                       cmd.expire_total = 0;
-               }
-
-               cb.cmd = cmd;
-               status |= reflog_expire(ref, flags,
-                                       reflog_expiry_prepare,
-                                       should_prune_fn,
-                                       reflog_expiry_cleanup,
-                                       &cb);
-               free(ref);
-       }
        return status;
 }
 
index 6f27ddc47bd25158bcc63ec64870af0540309e6d..5f4cde9d784e310e939dc7952e4096e45f60cbb7 100644 (file)
 #include "object-store.h"
 #include "strvec.h"
 #include "commit-reach.h"
+#include "progress.h"
 
 static const char * const builtin_remote_usage[] = {
        "git remote [-v | --verbose]",
        N_("git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--mirror=<fetch|push>] <name> <url>"),
-       N_("git remote rename <old> <new>"),
+       N_("git remote rename [--[no-]progress] <old> <new>"),
        N_("git remote remove <name>"),
        N_("git remote set-head <name> (-a | --auto | -d | --delete | <branch>)"),
        N_("git remote [-v | --verbose] show [-n] <name>"),
@@ -36,7 +37,7 @@ static const char * const builtin_remote_add_usage[] = {
 };
 
 static const char * const builtin_remote_rename_usage[] = {
-       N_("git remote rename <old> <new>"),
+       N_("git remote rename [--[no-]progress] <old> <new>"),
        NULL
 };
 
@@ -571,6 +572,7 @@ struct rename_info {
        const char *old_name;
        const char *new_name;
        struct string_list *remote_branches;
+       uint32_t symrefs_nr;
 };
 
 static int read_remote_branches(const char *refname,
@@ -587,10 +589,12 @@ static int read_remote_branches(const char *refname,
                item = string_list_append(rename->remote_branches, refname);
                symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
                                            NULL, &flag);
-               if (symref && (flag & REF_ISSYMREF))
+               if (symref && (flag & REF_ISSYMREF)) {
                        item->util = xstrdup(symref);
-               else
+                       rename->symrefs_nr++;
+               } else {
                        item->util = NULL;
+               }
        }
        strbuf_release(&buf);
 
@@ -674,7 +678,9 @@ static void handle_push_default(const char* old_name, const char* new_name)
 
 static int mv(int argc, const char **argv)
 {
+       int show_progress = isatty(2);
        struct option options[] = {
+               OPT_BOOL(0, "progress", &show_progress, N_("force progress reporting")),
                OPT_END()
        };
        struct remote *oldremote, *newremote;
@@ -682,14 +688,19 @@ static int mv(int argc, const char **argv)
                old_remote_context = STRBUF_INIT;
        struct string_list remote_branches = STRING_LIST_INIT_DUP;
        struct rename_info rename;
-       int i, refspec_updated = 0;
+       int i, refs_renamed_nr = 0, refspec_updated = 0;
+       struct progress *progress = NULL;
+
+       argc = parse_options(argc, argv, NULL, options,
+                            builtin_remote_rename_usage, 0);
 
-       if (argc != 3)
+       if (argc != 2)
                usage_with_options(builtin_remote_rename_usage, options);
 
-       rename.old_name = argv[1];
-       rename.new_name = argv[2];
+       rename.old_name = argv[0];
+       rename.new_name = argv[1];
        rename.remote_branches = &remote_branches;
+       rename.symrefs_nr = 0;
 
        oldremote = remote_get(rename.old_name);
        if (!remote_is_configured(oldremote, 1)) {
@@ -764,15 +775,26 @@ static int mv(int argc, const char **argv)
         * the new symrefs.
         */
        for_each_ref(read_remote_branches, &rename);
+       if (show_progress) {
+               /*
+                * Count symrefs twice, since "renaming" them is done by
+                * deleting and recreating them in two separate passes.
+                */
+               progress = start_progress(_("Renaming remote references"),
+                                         rename.remote_branches->nr + rename.symrefs_nr);
+       }
        for (i = 0; i < remote_branches.nr; i++) {
                struct string_list_item *item = remote_branches.items + i;
-               int flag = 0;
+               struct strbuf referent = STRBUF_INIT;
 
-               read_ref_full(item->string, RESOLVE_REF_READING, NULL, &flag);
-               if (!(flag & REF_ISSYMREF))
+               if (refs_read_symbolic_ref(get_main_ref_store(the_repository), item->string,
+                                          &referent))
                        continue;
                if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF))
                        die(_("deleting '%s' failed"), item->string);
+
+               strbuf_release(&referent);
+               display_progress(progress, ++refs_renamed_nr);
        }
        for (i = 0; i < remote_branches.nr; i++) {
                struct string_list_item *item = remote_branches.items + i;
@@ -788,6 +810,7 @@ static int mv(int argc, const char **argv)
                                item->string, buf.buf);
                if (rename_ref(item->string, buf.buf, buf2.buf))
                        die(_("renaming '%s' failed"), item->string);
+               display_progress(progress, ++refs_renamed_nr);
        }
        for (i = 0; i < remote_branches.nr; i++) {
                struct string_list_item *item = remote_branches.items + i;
@@ -807,7 +830,9 @@ static int mv(int argc, const char **argv)
                                item->string, buf.buf);
                if (create_symref(buf.buf, buf2.buf, buf3.buf))
                        die(_("creating '%s' failed"), buf.buf);
+               display_progress(progress, ++refs_renamed_nr);
        }
+       stop_progress(&progress);
        string_list_clear(&remote_branches, 1);
 
        handle_push_default(rename.old_name, rename.new_name);
index da1e364a756b9f8c74f38f8ec596798cf3ec38ad..d1a563d5b65666b68596ad353a945e8e11c6ac70 100644 (file)
@@ -22,6 +22,7 @@ static int delta_base_offset = 1;
 static int pack_kept_objects = -1;
 static int write_bitmaps = -1;
 static int use_delta_islands;
+static int run_update_server_info = 1;
 static char *packdir, *packtmp_name, *packtmp;
 
 static const char *const git_repack_usage[] = {
@@ -54,6 +55,10 @@ static int repack_config(const char *var, const char *value, void *cb)
                use_delta_islands = git_config_bool(var, value);
                return 0;
        }
+       if (strcmp(var, "repack.updateserverinfo") == 0) {
+               run_update_server_info = git_config_bool(var, value);
+               return 0;
+       }
        return git_default_config(var, value, cb);
 }
 
@@ -620,7 +625,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        const char *unpack_unreachable = NULL;
        int keep_unreachable = 0;
        struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
-       int no_update_server_info = 0;
        struct pack_objects_args po_args = {NULL};
        int geometric_factor = 0;
        int write_midx = 0;
@@ -637,8 +641,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                                N_("pass --no-reuse-delta to git-pack-objects")),
                OPT_BOOL('F', NULL, &po_args.no_reuse_object,
                                N_("pass --no-reuse-object to git-pack-objects")),
-               OPT_BOOL('n', NULL, &no_update_server_info,
-                               N_("do not run git-update-server-info")),
+               OPT_NEGBIT('n', NULL, &run_update_server_info,
+                               N_("do not run git-update-server-info"), 1),
                OPT__QUIET(&po_args.quiet, N_("be quiet")),
                OPT_BOOL('l', "local", &po_args.local,
                                N_("pass --local to git-pack-objects")),
@@ -939,7 +943,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                        prune_shallow(PRUNE_QUICK);
        }
 
-       if (!no_update_server_info)
+       if (run_update_server_info)
                update_server_info(0);
        remove_temporary_files();
 
index ac92337c0ec55e1b97352020f56346b28ad3f8d7..5068f4f0b2acf8e4b0f342319999cedffccc4223 100644 (file)
@@ -409,7 +409,7 @@ static int check_one_mergetag(struct commit *commit,
        int i;
 
        hash_object_file(the_hash_algo, extra->value, extra->len,
-                        type_name(OBJ_TAG), &tag_oid);
+                        OBJ_TAG, &tag_oid);
        tag = lookup_tag(the_repository, &tag_oid);
        if (!tag)
                return error(_("bad mergetag in commit '%s'"), ref);
@@ -474,7 +474,7 @@ static int create_graft(int argc, const char **argv, int force, int gentle)
                return -1;
        }
 
-       if (write_object_file(buf.buf, buf.len, commit_type, &new_oid)) {
+       if (write_object_file(buf.buf, buf.len, OBJ_COMMIT, &new_oid)) {
                strbuf_release(&buf);
                return error(_("could not write replacement commit for: '%s'"),
                             old_ref);
index 38528c7f15668534d66b358d2d3a0e3bb831b446..572da1472e5adf933c7680339ed3409cf62f3e24 100644 (file)
@@ -62,7 +62,6 @@ static const char rev_list_usage[] =
 static struct progress *progress;
 static unsigned progress_counter;
 
-static struct list_objects_filter_options filter_options;
 static struct oidset omitted_objects;
 static int arg_print_omitted; /* print objects omitted by filter */
 
@@ -400,7 +399,6 @@ static inline int parse_missing_action_value(const char *value)
 }
 
 static int try_bitmap_count(struct rev_info *revs,
-                           struct list_objects_filter_options *filter,
                            int filter_provided_objects)
 {
        uint32_t commit_count = 0,
@@ -436,7 +434,7 @@ static int try_bitmap_count(struct rev_info *revs,
         */
        max_count = revs->max_count;
 
-       bitmap_git = prepare_bitmap_walk(revs, filter, filter_provided_objects);
+       bitmap_git = prepare_bitmap_walk(revs, filter_provided_objects);
        if (!bitmap_git)
                return -1;
 
@@ -453,7 +451,6 @@ static int try_bitmap_count(struct rev_info *revs,
 }
 
 static int try_bitmap_traversal(struct rev_info *revs,
-                               struct list_objects_filter_options *filter,
                                int filter_provided_objects)
 {
        struct bitmap_index *bitmap_git;
@@ -465,7 +462,7 @@ static int try_bitmap_traversal(struct rev_info *revs,
        if (revs->max_count >= 0)
                return -1;
 
-       bitmap_git = prepare_bitmap_walk(revs, filter, filter_provided_objects);
+       bitmap_git = prepare_bitmap_walk(revs, filter_provided_objects);
        if (!bitmap_git)
                return -1;
 
@@ -475,7 +472,6 @@ static int try_bitmap_traversal(struct rev_info *revs,
 }
 
 static int try_bitmap_disk_usage(struct rev_info *revs,
-                                struct list_objects_filter_options *filter,
                                 int filter_provided_objects)
 {
        struct bitmap_index *bitmap_git;
@@ -483,7 +479,7 @@ static int try_bitmap_disk_usage(struct rev_info *revs,
        if (!show_disk_usage)
                return -1;
 
-       bitmap_git = prepare_bitmap_walk(revs, filter, filter_provided_objects);
+       bitmap_git = prepare_bitmap_walk(revs, filter_provided_objects);
        if (!bitmap_git)
                return -1;
 
@@ -595,17 +591,6 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                        show_progress = arg;
                        continue;
                }
-
-               if (skip_prefix(arg, ("--" CL_ARG__FILTER "="), &arg)) {
-                       parse_list_objects_filter(&filter_options, arg);
-                       if (filter_options.choice && !revs.blob_objects)
-                               die(_("object filtering requires --objects"));
-                       continue;
-               }
-               if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) {
-                       list_objects_filter_set_no_filter(&filter_options);
-                       continue;
-               }
                if (!strcmp(arg, "--filter-provided-objects")) {
                        filter_provided_objects = 1;
                        continue;
@@ -688,11 +673,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                progress = start_delayed_progress(show_progress, 0);
 
        if (use_bitmap_index) {
-               if (!try_bitmap_count(&revs, &filter_options, filter_provided_objects))
+               if (!try_bitmap_count(&revs, filter_provided_objects))
                        return 0;
-               if (!try_bitmap_disk_usage(&revs, &filter_options, filter_provided_objects))
+               if (!try_bitmap_disk_usage(&revs, filter_provided_objects))
                        return 0;
-               if (!try_bitmap_traversal(&revs, &filter_options, filter_provided_objects))
+               if (!try_bitmap_traversal(&revs, filter_provided_objects))
                        return 0;
        }
 
@@ -733,7 +718,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                oidset_init(&missing_objects, DEFAULT_OIDSET_SIZE);
 
        traverse_commit_list_filtered(
-               &filter_options, &revs, show_commit, show_object, &info,
+               &revs, show_commit, show_object, &info,
                (arg_print_omitted ? &omitted_objects : NULL));
 
        if (arg_print_omitted) {
index 228d782754af33350a51ad80d855151aa7f8a6d1..26c5c0cf93545777301c27f02279450347425fde 100644 (file)
@@ -435,7 +435,7 @@ static void add_wrapped_shortlog_msg(struct strbuf *sb, const char *s,
 
 void shortlog_output(struct shortlog *log)
 {
-       int i, j;
+       size_t i, j;
        struct strbuf sb = STRBUF_INIT;
 
        if (log->sort_by_number)
@@ -448,10 +448,10 @@ void shortlog_output(struct shortlog *log)
                                (int)UTIL_TO_INT(item), item->string);
                } else {
                        struct string_list *onelines = item->util;
-                       fprintf(log->file, "%s (%d):\n",
-                               item->string, onelines->nr);
-                       for (j = onelines->nr - 1; j >= 0; j--) {
-                               const char *msg = onelines->items[j].string;
+                       fprintf(log->file, "%s (%"PRIuMAX"):\n",
+                               item->string, (uintmax_t)onelines->nr);
+                       for (j = onelines->nr; j >= 1; j--) {
+                               const char *msg = onelines->items[j - 1].string;
 
                                if (log->wrap_lines) {
                                        strbuf_reset(&sb);
index 5518ed47f6c25664c038004657479c89c9acc382..0217d44c5b1c409c858a8df2f9c9b623a78b4804 100644 (file)
@@ -8,7 +8,6 @@
 #include "run-command.h"
 #include "strbuf.h"
 #include "string-list.h"
-#include "cache.h"
 #include "cache-tree.h"
 #include "lockfile.h"
 #include "resolve-undo.h"
@@ -329,11 +328,11 @@ static int write_patterns_and_update(struct pattern_list *pl)
 
        fd = hold_lock_file_for_update(&lk, sparse_filename,
                                      LOCK_DIE_ON_ERROR);
+       free(sparse_filename);
 
        result = update_working_directory(pl);
        if (result) {
                rollback_lock_file(&lk);
-               free(sparse_filename);
                clear_pattern_list(pl);
                update_working_directory(NULL);
                return result;
@@ -349,7 +348,6 @@ static int write_patterns_and_update(struct pattern_list *pl)
        fflush(fp);
        commit_lock_file(&lk);
 
-       free(sparse_filename);
        clear_pattern_list(pl);
 
        return 0;
index 3e8af210fdee6e5901497e5c572d8bc4da23502a..ccdfdab44be6c492a6b1942354afab14850a0c00 100644 (file)
@@ -16,7 +16,7 @@
 #include "log-tree.h"
 #include "diffcore.h"
 #include "exec-cmd.h"
-#include "entry.h"
+#include "reflog.h"
 
 #define INCLUDE_ALL_FILES 2
 
@@ -634,20 +634,9 @@ static int reflog_is_empty(const char *refname)
 
 static int do_drop_stash(struct stash_info *info, int quiet)
 {
-       int ret;
-       struct child_process cp_reflog = CHILD_PROCESS_INIT;
-
-       /*
-        * reflog does not provide a simple function for deleting refs. One will
-        * need to be added to avoid implementing too much reflog code here
-        */
-
-       cp_reflog.git_cmd = 1;
-       strvec_pushl(&cp_reflog.args, "reflog", "delete", "--updateref",
-                    "--rewrite", NULL);
-       strvec_push(&cp_reflog.args, info->revision.buf);
-       ret = run_command(&cp_reflog);
-       if (!ret) {
+       if (!reflog_delete(info->revision.buf,
+                          EXPIRE_REFLOGS_REWRITE | EXPIRE_REFLOGS_UPDATE_REF,
+                          0)) {
                if (!quiet)
                        printf_ln(_("Dropped %s (%s)"), info->revision.buf,
                                  oid_to_hex(&info->w_commit));
index d8638434dcb8ebd1487b7a1e04a9ddb8148195bf..5301612d24b0b122717b197bda6e2a455e51e923 100644 (file)
 typedef void (*each_submodule_fn)(const struct cache_entry *list_item,
                                  void *cb_data);
 
-static char *get_default_remote(void)
+static char *repo_get_default_remote(struct repository *repo)
 {
        char *dest = NULL, *ret;
        struct strbuf sb = STRBUF_INIT;
-       const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+       struct ref_store *store = get_main_ref_store(repo);
+       const char *refname = refs_resolve_ref_unsafe(store, "HEAD", 0, NULL,
+                                                     NULL);
 
        if (!refname)
                die(_("No such ref: %s"), "HEAD");
@@ -48,7 +50,7 @@ static char *get_default_remote(void)
                die(_("Expecting a full ref name, got %s"), refname);
 
        strbuf_addf(&sb, "branch.%s.remote", refname);
-       if (git_config_get_string(sb.buf, &dest))
+       if (repo_config_get_string(repo, sb.buf, &dest))
                ret = xstrdup("origin");
        else
                ret = dest;
@@ -57,19 +59,17 @@ static char *get_default_remote(void)
        return ret;
 }
 
-static int print_default_remote(int argc, const char **argv, const char *prefix)
+static char *get_default_remote_submodule(const char *module_path)
 {
-       char *remote;
-
-       if (argc != 1)
-               die(_("submodule--helper print-default-remote takes no arguments"));
+       struct repository subrepo;
 
-       remote = get_default_remote();
-       if (remote)
-               printf("%s\n", remote);
+       repo_submodule_init(&subrepo, the_repository, module_path, null_oid());
+       return repo_get_default_remote(&subrepo);
+}
 
-       free(remote);
-       return 0;
+static char *get_default_remote(void)
+{
+       return repo_get_default_remote(the_repository);
 }
 
 static int starts_with_dot_slash(const char *str)
@@ -247,11 +247,10 @@ static int resolve_relative_url_test(int argc, const char **argv, const char *pr
        return 0;
 }
 
-/* the result should be freed by the caller. */
-static char *get_submodule_displaypath(const char *path, const char *prefix)
+static char *do_get_submodule_displaypath(const char *path,
+                                         const char *prefix,
+                                         const char *super_prefix)
 {
-       const char *super_prefix = get_super_prefix();
-
        if (prefix && super_prefix) {
                BUG("cannot have prefix '%s' and superprefix '%s'",
                    prefix, super_prefix);
@@ -267,6 +266,13 @@ static char *get_submodule_displaypath(const char *path, const char *prefix)
        }
 }
 
+/* the result should be freed by the caller. */
+static char *get_submodule_displaypath(const char *path, const char *prefix)
+{
+       const char *super_prefix = get_super_prefix();
+       return do_get_submodule_displaypath(path, prefix, super_prefix);
+}
+
 static char *compute_rev_name(const char *sub_path, const char* object_id)
 {
        struct strbuf sb = STRBUF_INIT;
@@ -588,18 +594,22 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
 
 struct init_cb {
        const char *prefix;
+       const char *superprefix;
        unsigned int flags;
 };
 #define INIT_CB_INIT { 0 }
 
 static void init_submodule(const char *path, const char *prefix,
-                          unsigned int flags)
+                          const char *superprefix, unsigned int flags)
 {
        const struct submodule *sub;
        struct strbuf sb = STRBUF_INIT;
        char *upd = NULL, *url = NULL, *displaypath;
 
-       displaypath = get_submodule_displaypath(path, prefix);
+       /* try superprefix from the environment, if it is not passed explicitly */
+       if (!superprefix)
+               superprefix = get_super_prefix();
+       displaypath = do_get_submodule_displaypath(path, prefix, superprefix);
 
        sub = submodule_from_path(the_repository, null_oid(), path);
 
@@ -673,7 +683,7 @@ static void init_submodule(const char *path, const char *prefix,
 static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data)
 {
        struct init_cb *info = cb_data;
-       init_submodule(list_item->name, info->prefix, info->flags);
+       init_submodule(list_item->name, info->prefix, info->superprefix, info->flags);
 }
 
 static int module_init(int argc, const char **argv, const char *prefix)
@@ -1343,9 +1353,8 @@ static void sync_submodule(const char *path, const char *prefix,
 {
        const struct submodule *sub;
        char *remote_key = NULL;
-       char *sub_origin_url, *super_config_url, *displaypath;
+       char *sub_origin_url, *super_config_url, *displaypath, *default_remote;
        struct strbuf sb = STRBUF_INIT;
-       struct child_process cp = CHILD_PROCESS_INIT;
        char *sub_config_path = NULL;
 
        if (!is_submodule_active(the_repository, path))
@@ -1384,21 +1393,15 @@ static void sync_submodule(const char *path, const char *prefix,
        if (!is_submodule_populated_gently(path, NULL))
                goto cleanup;
 
-       prepare_submodule_repo_env(&cp.env_array);
-       cp.git_cmd = 1;
-       cp.dir = path;
-       strvec_pushl(&cp.args, "submodule--helper",
-                    "print-default-remote", NULL);
-
        strbuf_reset(&sb);
-       if (capture_command(&cp, &sb, 0))
+       default_remote = get_default_remote_submodule(path);
+       if (!default_remote)
                die(_("failed to get the default remote for submodule '%s'"),
                      path);
 
-       strbuf_strip_suffix(&sb, "\n");
-       remote_key = xstrfmt("remote.%s.url", sb.buf);
+       remote_key = xstrfmt("remote.%s.url", default_remote);
+       free(default_remote);
 
-       strbuf_reset(&sb);
        submodule_to_gitdir(&sb, path);
        strbuf_addstr(&sb, "/config");
 
@@ -1957,29 +1960,6 @@ static void determine_submodule_update_strategy(struct repository *r,
        free(key);
 }
 
-static int module_update_module_mode(int argc, const char **argv, const char *prefix)
-{
-       const char *path, *update = NULL;
-       int just_cloned;
-       struct submodule_update_strategy update_strategy = { .type = SM_UPDATE_CHECKOUT };
-
-       if (argc < 3 || argc > 4)
-               die("submodule--helper update-module-clone expects <just-cloned> <path> [<update>]");
-
-       just_cloned = git_config_int("just_cloned", argv[1]);
-       path = argv[2];
-
-       if (argc == 4)
-               update = argv[3];
-
-       determine_submodule_update_strategy(the_repository,
-                                           just_cloned, path, update,
-                                           &update_strategy);
-       fputs(submodule_strategy_to_string(&update_strategy), stdout);
-
-       return 0;
-}
-
 struct update_clone_data {
        const struct submodule *sub;
        struct object_id oid;
@@ -2020,6 +2000,7 @@ struct submodule_update_clone {
        int failed_clones_nr, failed_clones_alloc;
 
        int max_jobs;
+       unsigned int init;
 };
 #define SUBMODULE_UPDATE_CLONE_INIT { \
        .list = MODULE_LIST_INIT, \
@@ -2038,10 +2019,11 @@ struct update_data {
        struct object_id suboid;
        struct submodule_update_strategy update_strategy;
        int depth;
-       unsigned int force: 1;
-       unsigned int quiet: 1;
-       unsigned int nofetch: 1;
-       unsigned int just_cloned: 1;
+       unsigned int force;
+       unsigned int quiet;
+       unsigned int nofetch;
+       unsigned int just_cloned;
+       unsigned int remote;
 };
 #define UPDATE_DATA_INIT { .update_strategy = SUBMODULE_UPDATE_STRATEGY_INIT }
 
@@ -2475,6 +2457,16 @@ static int do_run_update_procedure(struct update_data *ud)
        return run_update_command(ud, subforce);
 }
 
+/*
+ * NEEDSWORK: As we convert "git submodule update" to C,
+ * update_submodule2() will invoke more and more functions, making it
+ * difficult to preserve the function ordering without forward
+ * declarations.
+ *
+ * When the conversion is complete, this forward declaration will be
+ * unnecessary and should be removed.
+ */
+static int update_submodule2(struct update_data *update_data);
 static void update_submodule(struct update_clone_data *ucd)
 {
        fprintf(stdout, "dummy %s %d\t%s\n",
@@ -2518,6 +2510,8 @@ static int update_clone(int argc, const char **argv, const char *prefix)
        int ret;
 
        struct option module_update_clone_options[] = {
+               OPT_BOOL(0, "init", &suc.init,
+                        N_("initialize uninitialized submodules before update")),
                OPT_STRING(0, "prefix", &prefix,
                           N_("path"),
                           N_("path into the working tree")),
@@ -2551,7 +2545,12 @@ static int update_clone(int argc, const char **argv, const char *prefix)
        };
 
        const char *const git_submodule_helper_usage[] = {
-               N_("git submodule--helper update-clone [--prefix=<path>] [<path>...]"),
+               N_("git submodule [--quiet] update"
+               " [--init [--filter=<filter-spec>]] [--remote]"
+               " [-N|--no-fetch] [-f|--force]"
+               " [--checkout|--merge|--rebase]"
+               " [--[no-]recommend-shallow] [--reference <repository>]"
+               " [--recursive] [--[no-]single-branch] [--] [<path>...]"),
                NULL
        };
        suc.prefix = prefix;
@@ -2562,6 +2561,19 @@ static int update_clone(int argc, const char **argv, const char *prefix)
        memset(&filter_options, 0, sizeof(filter_options));
        argc = parse_options(argc, argv, prefix, module_update_clone_options,
                             git_submodule_helper_usage, 0);
+
+       if (filter_options.choice && !suc.init) {
+               /*
+                * NEEDSWORK: Don't use usage_with_options() because the
+                * usage string is for "git submodule update", but the
+                * options are for "git submodule--helper update-clone".
+                *
+                * This will no longer be an issue when "update-clone"
+                * is replaced by "git submodule--helper update".
+                */
+               usage(git_submodule_helper_usage[0]);
+       }
+
        suc.filter_options = &filter_options;
 
        if (update)
@@ -2576,6 +2588,29 @@ static int update_clone(int argc, const char **argv, const char *prefix)
        if (pathspec.nr)
                suc.warn_if_uninitialized = 1;
 
+       if (suc.init) {
+               struct module_list list = MODULE_LIST_INIT;
+               struct init_cb info = INIT_CB_INIT;
+
+               if (module_list_compute(argc, argv, suc.prefix,
+                                       &pathspec, &list) < 0)
+                       return 1;
+
+               /*
+                * If there are no path args and submodule.active is set then,
+                * by default, only initialize 'active' modules.
+                */
+               if (!argc && git_config_get_value_multi("submodule.active"))
+                       module_list_active(&list);
+
+               info.prefix = suc.prefix;
+               info.superprefix = suc.recursive_prefix;
+               if (suc.quiet)
+                       info.flags |= OPT_QUIET;
+
+               for_each_listed_submodule(&list, init_submodule_cb, &info);
+       }
+
        ret = update_submodules(&suc);
        list_objects_filter_release(&filter_options);
        return ret;
@@ -2583,16 +2618,17 @@ static int update_clone(int argc, const char **argv, const char *prefix)
 
 static int run_update_procedure(int argc, const char **argv, const char *prefix)
 {
-       int force = 0, quiet = 0, nofetch = 0, just_cloned = 0;
        char *prefixed_path, *update = NULL;
        struct update_data update_data = UPDATE_DATA_INIT;
 
        struct option options[] = {
-               OPT__QUIET(&quiet, N_("suppress output for update by rebase or merge")),
-               OPT__FORCE(&force, N_("force checkout updates"), 0),
-               OPT_BOOL('N', "no-fetch", &nofetch,
+               OPT__QUIET(&update_data.quiet,
+                          N_("suppress output for update by rebase or merge")),
+               OPT__FORCE(&update_data.force, N_("force checkout updates"),
+                          0),
+               OPT_BOOL('N', "no-fetch", &update_data.nofetch,
                         N_("don't fetch new objects from the remote site")),
-               OPT_BOOL(0, "just-cloned", &just_cloned,
+               OPT_BOOL(0, "just-cloned", &update_data.just_cloned,
                         N_("overrides update mode in case the repository is a fresh clone")),
                OPT_INTEGER(0, "depth", &update_data.depth, N_("depth for shallow fetch")),
                OPT_STRING(0, "prefix", &prefix,
@@ -2607,9 +2643,8 @@ static int run_update_procedure(int argc, const char **argv, const char *prefix)
                OPT_CALLBACK_F(0, "oid", &update_data.oid, N_("sha1"),
                               N_("SHA1 expected by superproject"), PARSE_OPT_NONEG,
                               parse_opt_object_id),
-               OPT_CALLBACK_F(0, "suboid", &update_data.suboid, N_("subsha1"),
-                              N_("SHA1 of submodule's HEAD"), PARSE_OPT_NONEG,
-                              parse_opt_object_id),
+               OPT_BOOL(0, "remote", &update_data.remote,
+                        N_("use SHA-1 of submodule's remote tracking branch")),
                OPT_END()
        };
 
@@ -2623,10 +2658,6 @@ static int run_update_procedure(int argc, const char **argv, const char *prefix)
        if (argc != 1)
                usage_with_options(usage, options);
 
-       update_data.force = !!force;
-       update_data.quiet = !!quiet;
-       update_data.nofetch = !!nofetch;
-       update_data.just_cloned = !!just_cloned;
        update_data.sm_path = argv[0];
 
        if (update_data.recursive_prefix)
@@ -2641,11 +2672,7 @@ static int run_update_procedure(int argc, const char **argv, const char *prefix)
                                            &update_data.update_strategy);
 
        free(prefixed_path);
-
-       if (!oideq(&update_data.oid, &update_data.suboid) || update_data.force)
-               return do_run_update_procedure(&update_data);
-
-       return 3;
+       return update_submodule2(&update_data);
 }
 
 static int resolve_relative_path(int argc, const char **argv, const char *prefix)
@@ -2697,23 +2724,6 @@ static const char *remote_submodule_branch(const char *path)
        return branch;
 }
 
-static int resolve_remote_submodule_branch(int argc, const char **argv,
-               const char *prefix)
-{
-       const char *ret;
-       struct strbuf sb = STRBUF_INIT;
-       if (argc != 2)
-               die("submodule--helper remote-branch takes exactly one arguments, got %d", argc);
-
-       ret = remote_submodule_branch(argv[1]);
-       if (!ret)
-               die("submodule %s doesn't exist", argv[1]);
-
-       printf("%s", ret);
-       strbuf_release(&sb);
-       return 0;
-}
-
 static int push_check(int argc, const char **argv, const char *prefix)
 {
        struct remote *remote;
@@ -2791,17 +2801,11 @@ static int push_check(int argc, const char **argv, const char *prefix)
        return 0;
 }
 
-static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
+static void ensure_core_worktree(const char *path)
 {
-       const char *path;
        const char *cw;
        struct repository subrepo;
 
-       if (argc != 2)
-               BUG("submodule--helper ensure-core-worktree <path>");
-
-       path = argv[1];
-
        if (repo_submodule_init(&subrepo, the_repository, path, null_oid()))
                die(_("could not get a repository handle for submodule '%s'"), path);
 
@@ -2821,8 +2825,6 @@ static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
                free(abs_path);
                strbuf_release(&sb);
        }
-
-       return 0;
 }
 
 static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
@@ -3045,6 +3047,42 @@ static int module_create_branch(int argc, const char **argv, const char *prefix)
                                    force, reflog, quiet, track, dry_run);
        return 0;
 }
+
+/* NEEDSWORK: this is a temporary name until we delete update_submodule() */
+static int update_submodule2(struct update_data *update_data)
+{
+       ensure_core_worktree(update_data->sm_path);
+       if (update_data->just_cloned)
+               oidcpy(&update_data->suboid, null_oid());
+       else if (resolve_gitlink_ref(update_data->sm_path, "HEAD", &update_data->suboid))
+               die(_("Unable to find current revision in submodule path '%s'"),
+                       update_data->displaypath);
+
+       if (update_data->remote) {
+               char *remote_name = get_default_remote_submodule(update_data->sm_path);
+               const char *branch = remote_submodule_branch(update_data->sm_path);
+               char *remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch);
+
+               if (!update_data->nofetch) {
+                       if (fetch_in_submodule(update_data->sm_path, update_data->depth,
+                                             0, NULL))
+                               die(_("Unable to fetch in submodule path '%s'"),
+                                   update_data->sm_path);
+               }
+
+               if (resolve_gitlink_ref(update_data->sm_path, remote_ref, &update_data->oid))
+                       die(_("Unable to find %s revision in submodule path '%s'"),
+                           remote_ref, update_data->sm_path);
+
+               free(remote_ref);
+       }
+
+       if (!oideq(&update_data->oid, &update_data->suboid) || update_data->force)
+               return do_run_update_procedure(update_data);
+
+       return 3;
+}
+
 struct add_data {
        const char *prefix;
        const char *branch;
@@ -3309,6 +3347,7 @@ static int module_add(int argc, const char **argv, const char *prefix)
 {
        int force = 0, quiet = 0, progress = 0, dissociate = 0;
        struct add_data add_data = ADD_DATA_INIT;
+       char *to_free = NULL;
 
        struct option options[] = {
                OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
@@ -3360,7 +3399,8 @@ static int module_add(int argc, const char **argv, const char *prefix)
                              "of the working tree"));
 
                /* dereference source url relative to parent's url */
-               add_data.realrepo = resolve_relative_url(add_data.repo, NULL, 1);
+               to_free = resolve_relative_url(add_data.repo, NULL, 1);
+               add_data.realrepo = to_free;
        } else if (is_dir_sep(add_data.repo[0]) || strchr(add_data.repo, ':')) {
                add_data.realrepo = add_data.repo;
        } else {
@@ -3413,6 +3453,7 @@ static int module_add(int argc, const char **argv, const char *prefix)
        }
        configure_added_submodule(&add_data);
        free(add_data.sm_path);
+       free(to_free);
 
        return 0;
 }
@@ -3430,20 +3471,16 @@ static struct cmd_struct commands[] = {
        {"name", module_name, 0},
        {"clone", module_clone, 0},
        {"add", module_add, SUPPORT_SUPER_PREFIX},
-       {"update-module-mode", module_update_module_mode, 0},
        {"update-clone", update_clone, 0},
        {"run-update-procedure", run_update_procedure, 0},
-       {"ensure-core-worktree", ensure_core_worktree, 0},
        {"relative-path", resolve_relative_path, 0},
        {"resolve-relative-url-test", resolve_relative_url_test, 0},
        {"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
        {"init", module_init, SUPPORT_SUPER_PREFIX},
        {"status", module_status, SUPPORT_SUPER_PREFIX},
-       {"print-default-remote", print_default_remote, 0},
        {"sync", module_sync, SUPPORT_SUPER_PREFIX},
        {"deinit", module_deinit, 0},
        {"summary", module_summary, SUPPORT_SUPER_PREFIX},
-       {"remote-branch", resolve_remote_submodule_branch, 0},
        {"push-check", push_check, 0},
        {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
        {"is-active", is_active, 0},
index 2479da07049ace8c64c0461210ab6d77798f73aa..e5a8f856936a1e494d91aa8ebfa2ff9f64f375d8 100644 (file)
@@ -239,7 +239,7 @@ static int build_tag_object(struct strbuf *buf, int sign, struct object_id *resu
 {
        if (sign && do_sign(buf) < 0)
                return error(_("unable to sign the tag"));
-       if (write_object_file(buf->buf, buf->len, tag_type, result) < 0)
+       if (write_object_file(buf->buf, buf->len, OBJ_TAG, result) < 0)
                return error(_("unable to write tag file"));
        return 0;
 }
index 4a9466295ba10de5752cca92fa08b1a28461777b..dbeb0680a5856300e3a14db9387193a5fe65a0fc 100644 (file)
@@ -177,7 +177,7 @@ static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf)
        struct object_id oid;
 
        if (write_object_file(obj_buf->buffer, obj_buf->size,
-                             type_name(obj->type), &oid) < 0)
+                             obj->type, &oid) < 0)
                die("failed to write object %s", oid_to_hex(&obj->oid));
        obj->flags |= FLAG_WRITTEN;
 }
@@ -243,7 +243,7 @@ static void write_object(unsigned nr, enum object_type type,
                         void *buf, unsigned long size)
 {
        if (!strict) {
-               if (write_object_file(buf, size, type_name(type),
+               if (write_object_file(buf, size, type,
                                      &obj_list[nr].oid) < 0)
                        die("failed to write object");
                added_object(nr, type, buf, size);
@@ -251,7 +251,7 @@ static void write_object(unsigned nr, enum object_type type,
                obj_list[nr].obj = NULL;
        } else if (type == OBJ_BLOB) {
                struct blob *blob;
-               if (write_object_file(buf, size, type_name(type),
+               if (write_object_file(buf, size, type,
                                      &obj_list[nr].oid) < 0)
                        die("failed to write object");
                added_object(nr, type, buf, size);
@@ -266,7 +266,7 @@ static void write_object(unsigned nr, enum object_type type,
        } else {
                struct object *obj;
                int eaten;
-               hash_object_file(the_hash_algo, buf, size, type_name(type),
+               hash_object_file(the_hash_algo, buf, size, type,
                                 &obj_list[nr].oid);
                added_object(nr, type, buf, size);
                obj = parse_object_buffer(the_repository, &obj_list[nr].oid,
index 8785b2ac80699255ae8abd0801922ba025235309..85b3ebaf971087f2863b7d3bc6da5ee7be3317cb 100644 (file)
@@ -220,8 +220,8 @@ static int deflate_to_pack(struct bulk_checkin_state *state,
        if (seekback == (off_t) -1)
                return error("cannot find the current offset");
 
-       header_len = xsnprintf((char *)obuf, sizeof(obuf), "%s %" PRIuMAX,
-                              type_name(type), (uintmax_t)size) + 1;
+       header_len = format_object_header((char *)obuf, sizeof(obuf),
+                                         type, size);
        the_hash_algo->init_fn(&ctx);
        the_hash_algo->update_fn(&ctx, obuf, header_len);
 
index a0bb687b0f4ea30d44883dcb9d4de5bf1e5963ea..e359370cfcdc7d92077f1e3403d7f6529189de67 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -11,7 +11,7 @@
 #include "run-command.h"
 #include "refs.h"
 #include "strvec.h"
-
+#include "list-objects-filter-options.h"
 
 static const char v2_bundle_signature[] = "# v2 git bundle\n";
 static const char v3_bundle_signature[] = "# v3 git bundle\n";
@@ -33,6 +33,7 @@ void bundle_header_release(struct bundle_header *header)
 {
        string_list_clear(&header->prerequisites, 1);
        string_list_clear(&header->references, 1);
+       list_objects_filter_release(&header->filter);
 }
 
 static int parse_capability(struct bundle_header *header, const char *capability)
@@ -45,6 +46,10 @@ static int parse_capability(struct bundle_header *header, const char *capability
                header->hash_algo = &hash_algos[algo];
                return 0;
        }
+       if (skip_prefix(capability, "filter=", &arg)) {
+               parse_list_objects_filter(&header->filter, arg);
+               return 0;
+       }
        return error(_("unknown capability '%s'"), capability);
 }
 
@@ -220,6 +225,8 @@ int verify_bundle(struct repository *r,
        req_nr = revs.pending.nr;
        setup_revisions(2, argv, &revs, NULL);
 
+       list_objects_filter_copy(&revs.filter, &header->filter);
+
        if (prepare_revision_walk(&revs))
                die(_("revision walk setup failed"));
 
@@ -255,18 +262,24 @@ int verify_bundle(struct repository *r,
 
                r = &header->references;
                printf_ln(Q_("The bundle contains this ref:",
-                            "The bundle contains these %d refs:",
+                            "The bundle contains these %"PRIuMAX" refs:",
                             r->nr),
-                         r->nr);
+                         (uintmax_t)r->nr);
                list_refs(r, 0, NULL);
+
+               if (header->filter.choice) {
+                       printf_ln("The bundle uses this filter: %s",
+                                 list_objects_filter_spec(&header->filter));
+               }
+
                r = &header->prerequisites;
                if (!r->nr) {
                        printf_ln(_("The bundle records a complete history."));
                } else {
                        printf_ln(Q_("The bundle requires this ref:",
-                                    "The bundle requires these %d refs:",
+                                    "The bundle requires these %"PRIuMAX" refs:",
                                     r->nr),
-                                 r->nr);
+                                 (uintmax_t)r->nr);
                        list_refs(r, 0, NULL);
                }
        }
@@ -319,6 +332,9 @@ static int write_pack_data(int bundle_fd, struct rev_info *revs, struct strvec *
                     "--stdout", "--thin", "--delta-base-offset",
                     NULL);
        strvec_pushv(&pack_objects.args, pack_options->v);
+       if (revs->filter.choice)
+               strvec_pushf(&pack_objects.args, "--filter=%s",
+                            list_objects_filter_spec(&revs->filter));
        pack_objects.in = -1;
        pack_objects.out = bundle_fd;
        pack_objects.git_cmd = 1;
@@ -486,10 +502,37 @@ int create_bundle(struct repository *r, const char *path,
        int bundle_to_stdout;
        int ref_count = 0;
        struct rev_info revs, revs_copy;
-       int min_version = the_hash_algo == &hash_algos[GIT_HASH_SHA1] ? 2 : 3;
+       int min_version = 2;
        struct bundle_prerequisites_info bpi;
        int i;
 
+       /* init revs to list objects for pack-objects later */
+       save_commit_buffer = 0;
+       repo_init_revisions(r, &revs, NULL);
+
+       /*
+        * Pre-initialize the '--objects' flag so we can parse a
+        * --filter option successfully.
+        */
+       revs.tree_objects = revs.blob_objects = 1;
+
+       argc = setup_revisions(argc, argv, &revs, NULL);
+
+       /*
+        * Reasons to require version 3:
+        *
+        * 1. @object-format is required because our hash algorithm is not
+        *    SHA1.
+        * 2. @filter is required because we parsed an object filter.
+        */
+       if (the_hash_algo != &hash_algos[GIT_HASH_SHA1] || revs.filter.choice)
+               min_version = 3;
+
+       if (argc > 1) {
+               error(_("unrecognized argument: %s"), argv[1]);
+               goto err;
+       }
+
        bundle_to_stdout = !strcmp(path, "-");
        if (bundle_to_stdout)
                bundle_fd = 1;
@@ -512,17 +555,14 @@ int create_bundle(struct repository *r, const char *path,
                write_or_die(bundle_fd, capability, strlen(capability));
                write_or_die(bundle_fd, the_hash_algo->name, strlen(the_hash_algo->name));
                write_or_die(bundle_fd, "\n", 1);
-       }
 
-       /* init revs to list objects for pack-objects later */
-       save_commit_buffer = 0;
-       repo_init_revisions(r, &revs, NULL);
-
-       argc = setup_revisions(argc, argv, &revs, NULL);
-
-       if (argc > 1) {
-               error(_("unrecognized argument: %s"), argv[1]);
-               goto err;
+               if (revs.filter.choice) {
+                       const char *value = expand_list_objects_filter_spec(&revs.filter);
+                       capability = "@filter=";
+                       write_or_die(bundle_fd, capability, strlen(capability));
+                       write_or_die(bundle_fd, value, strlen(value));
+                       write_or_die(bundle_fd, "\n", 1);
+               }
        }
 
        /* save revs.pending in revs_copy for later use */
@@ -544,6 +584,12 @@ int create_bundle(struct repository *r, const char *path,
                die("revision walk setup failed");
        bpi.fd = bundle_fd;
        bpi.pending = &revs_copy.pending;
+
+       /*
+        * Remove any object walking here. We only care about commits and
+        * tags here. The revs_copy has the right instances of these values.
+        */
+       revs.blob_objects = revs.tree_objects = 0;
        traverse_commit_list(&revs, write_bundle_prerequisites, NULL, &bpi);
        object_array_remove_duplicates(&revs_copy.pending);
 
@@ -574,6 +620,10 @@ int unbundle(struct repository *r, struct bundle_header *header,
        struct child_process ip = CHILD_PROCESS_INIT;
        strvec_pushl(&ip.args, "index-pack", "--fix-thin", "--stdin", NULL);
 
+       /* If there is a filter, then we need to create the promisor pack. */
+       if (header->filter.choice)
+               strvec_push(&ip.args, "--promisor=from-bundle");
+
        if (extra_index_pack_args) {
                strvec_pushv(&ip.args, extra_index_pack_args->v);
                strvec_clear(extra_index_pack_args);
index 06009fe6b1f00b939350ba88ba7bf8f72565de4c..7fef2108f436861726ca4efb6937f9d434e61d7e 100644 (file)
--- a/bundle.h
+++ b/bundle.h
@@ -4,12 +4,14 @@
 #include "strvec.h"
 #include "cache.h"
 #include "string-list.h"
+#include "list-objects-filter-options.h"
 
 struct bundle_header {
        unsigned version;
        struct string_list prerequisites;
        struct string_list references;
        const struct git_hash_algo *hash_algo;
+       struct list_objects_filter_options filter;
 };
 
 #define BUNDLE_HEADER_INIT \
index 65ca99336136f1f50695f06c6b404859b13cc0a3..6752f69d515e12f7bd8c98ac2e27548b68da46d5 100644 (file)
@@ -432,15 +432,15 @@ static int update_one(struct cache_tree *it,
        if (repair) {
                struct object_id oid;
                hash_object_file(the_hash_algo, buffer.buf, buffer.len,
-                                tree_type, &oid);
+                                OBJ_TREE, &oid);
                if (has_object_file_with_flags(&oid, OBJECT_INFO_SKIP_FETCH_OBJECT))
                        oidcpy(&it->oid, &oid);
                else
                        to_invalidate = 1;
        } else if (dryrun) {
                hash_object_file(the_hash_algo, buffer.buf, buffer.len,
-                                tree_type, &it->oid);
-       } else if (write_object_file_flags(buffer.buf, buffer.len, tree_type,
+                                OBJ_TREE, &it->oid);
+       } else if (write_object_file_flags(buffer.buf, buffer.len, OBJ_TREE,
                                           &it->oid, flags & WRITE_TREE_SILENT
                                           ? HASH_SILENT : 0)) {
                strbuf_release(&buffer);
@@ -948,7 +948,7 @@ static int verify_one(struct repository *r,
                strbuf_addf(&tree_buf, "%o %.*s%c", mode, entlen, name, '\0');
                strbuf_add(&tree_buf, oid->hash, r->hash_algo->rawsz);
        }
-       hash_object_file(r->hash_algo, tree_buf.buf, tree_buf.len, tree_type,
+       hash_object_file(r->hash_algo, tree_buf.buf, tree_buf.len, OBJ_TREE,
                         &new_oid);
        if (!oideq(&new_oid, &it->oid))
                BUG("cache-tree for path %.*s does not match. "
diff --git a/cache.h b/cache.h
index 04d4d2db25cc50795ac025d3d0ee1b6c73bc4ecc..0bc0a37cecbfdcec436dbd26546d2d78190e0173 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1003,6 +1003,7 @@ extern const char *core_fsmonitor;
 
 extern int core_apply_sparse_checkout;
 extern int core_sparse_checkout_cone;
+extern int sparse_expect_files_outside_of_patterns;
 
 /*
  * Returns the boolean value of $GIT_OPTIONAL_LOCKS (or the default value).
@@ -1319,9 +1320,23 @@ enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
 struct object_info;
 int parse_loose_header(const char *hdr, struct object_info *oi);
 
+/**
+ * With in-core object data in "buf", rehash it to make sure the
+ * object name actually matches "oid" to detect object corruption.
+ *
+ * A negative value indicates an error, usually that the OID is not
+ * what we expected, but it might also indicate another error.
+ */
 int check_object_signature(struct repository *r, const struct object_id *oid,
-                          void *buf, unsigned long size, const char *type,
-                          struct object_id *real_oidp);
+                          void *map, unsigned long size,
+                          enum object_type type);
+
+/**
+ * A streaming version of check_object_signature().
+ * Try reading the object named with "oid" using
+ * the streaming interface and rehash it to do the same.
+ */
+int stream_object_signature(struct repository *r, const struct object_id *oid);
 
 int finalize_object_file(const char *tmpfile, const char *filename);
 
@@ -1548,7 +1563,7 @@ int cache_name_stage_compare(const char *name1, int len1, int stage1, const char
 
 void *read_object_with_reference(struct repository *r,
                                 const struct object_id *oid,
-                                const char *required_type,
+                                enum object_type required_type,
                                 unsigned long *size,
                                 struct object_id *oid_ret);
 
index 265c010122e8edefc141f1dec8f506078bbbee19..adffd020dd4bfa8229228a22da2485ae576ee8da 100644 (file)
@@ -39,8 +39,8 @@ void git_test_write_commit_graph_or_die(void)
 #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
 #define GRAPH_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
 #define GRAPH_CHUNKID_DATA 0x43444154 /* "CDAT" */
-#define GRAPH_CHUNKID_GENERATION_DATA 0x47444154 /* "GDAT" */
-#define GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW 0x47444f56 /* "GDOV" */
+#define GRAPH_CHUNKID_GENERATION_DATA 0x47444132 /* "GDA2" */
+#define GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW 0x47444f32 /* "GDO2" */
 #define GRAPH_CHUNKID_EXTRAEDGES 0x45444745 /* "EDGE" */
 #define GRAPH_CHUNKID_BLOOMINDEXES 0x42494458 /* "BIDX" */
 #define GRAPH_CHUNKID_BLOOMDATA 0x42444154 /* "BDAT" */
@@ -407,6 +407,9 @@ struct commit_graph *parse_commit_graph(struct repository *r,
                        &graph->chunk_generation_data);
                pair_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW,
                        &graph->chunk_generation_data_overflow);
+
+               if (graph->chunk_generation_data)
+                       graph->read_generation_data = 1;
        }
 
        if (r->settings.commit_graph_read_changed_paths) {
@@ -803,7 +806,7 @@ static void fill_commit_graph_info(struct commit *item, struct commit_graph *g,
                                die(_("commit-graph requires overflow generation data but has none"));
 
                        offset_pos = offset ^ CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW;
-                       graph_data->generation = get_be64(g->chunk_generation_data_overflow + 8 * offset_pos);
+                       graph_data->generation = item->date + get_be64(g->chunk_generation_data_overflow + 8 * offset_pos);
                } else
                        graph_data->generation = item->date + offset;
        } else
@@ -1556,12 +1559,16 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
                                if (current->date && current->date > max_corrected_commit_date)
                                        max_corrected_commit_date = current->date - 1;
                                commit_graph_data_at(current)->generation = max_corrected_commit_date + 1;
-
-                               if (commit_graph_data_at(current)->generation - current->date > GENERATION_NUMBER_V2_OFFSET_MAX)
-                                       ctx->num_generation_data_overflows++;
                        }
                }
        }
+
+       for (i = 0; i < ctx->commits.nr; i++) {
+               struct commit *c = ctx->commits.list[i];
+               timestamp_t offset = commit_graph_data_at(c)->generation - c->date;
+               if (offset > GENERATION_NUMBER_V2_OFFSET_MAX)
+                       ctx->num_generation_data_overflows++;
+       }
        stop_progress(&ctx->progress);
 }
 
@@ -1679,21 +1686,22 @@ int write_commit_graph_reachable(struct object_directory *odb,
 }
 
 static int fill_oids_from_packs(struct write_commit_graph_context *ctx,
-                               struct string_list *pack_indexes)
+                               const struct string_list *pack_indexes)
 {
        uint32_t i;
        struct strbuf progress_title = STRBUF_INIT;
        struct strbuf packname = STRBUF_INIT;
        int dirlen;
+       int ret = 0;
 
        strbuf_addf(&packname, "%s/pack/", ctx->odb->path);
        dirlen = packname.len;
        if (ctx->report_progress) {
                strbuf_addf(&progress_title,
-                           Q_("Finding commits for commit graph in %d pack",
-                              "Finding commits for commit graph in %d packs",
+                           Q_("Finding commits for commit graph in %"PRIuMAX" pack",
+                              "Finding commits for commit graph in %"PRIuMAX" packs",
                               pack_indexes->nr),
-                           pack_indexes->nr);
+                           (uintmax_t)pack_indexes->nr);
                ctx->progress = start_delayed_progress(progress_title.buf, 0);
                ctx->progress_done = 0;
        }
@@ -1703,12 +1711,12 @@ static int fill_oids_from_packs(struct write_commit_graph_context *ctx,
                strbuf_addstr(&packname, pack_indexes->items[i].string);
                p = add_packed_git(packname.buf, packname.len, 1);
                if (!p) {
-                       error(_("error adding pack %s"), packname.buf);
-                       return -1;
+                       ret = error(_("error adding pack %s"), packname.buf);
+                       goto cleanup;
                }
                if (open_pack_index(p)) {
-                       error(_("error opening index for %s"), packname.buf);
-                       return -1;
+                       ret = error(_("error opening index for %s"), packname.buf);
+                       goto cleanup;
                }
                for_each_object_in_pack(p, add_packed_commits, ctx,
                                        FOR_EACH_OBJECT_PACK_ORDER);
@@ -1716,11 +1724,12 @@ static int fill_oids_from_packs(struct write_commit_graph_context *ctx,
                free(p);
        }
 
+cleanup:
        stop_progress(&ctx->progress);
        strbuf_release(&progress_title);
        strbuf_release(&packname);
 
-       return 0;
+       return ret;
 }
 
 static int fill_oids_from_commits(struct write_commit_graph_context *ctx,
@@ -1852,6 +1861,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
 
                hold_lock_file_for_update_mode(&lk, lock_name,
                                               LOCK_DIE_ON_ERROR, 0444);
+               free(lock_name);
 
                fd = git_mkstemp_mode(ctx->graph_name, 0444);
                if (fd < 0) {
@@ -1976,6 +1986,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
                } else {
                        char *graph_name = get_commit_graph_filename(ctx->odb);
                        unlink(graph_name);
+                       free(graph_name);
                }
 
                ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1] = xstrdup(hash_to_hex(file_hash));
@@ -2259,7 +2270,7 @@ out:
 }
 
 int write_commit_graph(struct object_directory *odb,
-                      struct string_list *pack_indexes,
+                      const struct string_list *const pack_indexes,
                       struct oidset *commits,
                       enum commit_graph_write_flags flags,
                       const struct commit_graph_opts *opts)
index 04a94e18302d8d2e9b91933497cfb868a3cf3c12..2e3ac35237e919790555bae317bd580cf065b727 100644 (file)
@@ -142,7 +142,7 @@ int write_commit_graph_reachable(struct object_directory *odb,
                                 enum commit_graph_write_flags flags,
                                 const struct commit_graph_opts *opts);
 int write_commit_graph(struct object_directory *odb,
-                      struct string_list *pack_indexes,
+                      const struct string_list *pack_indexes,
                       struct oidset *commits,
                       enum commit_graph_write_flags flags,
                       const struct commit_graph_opts *opts);
index d400f5dfa2b1f9016f90c5aee9665b1f08316048..98b2e556653e6002d5f4afe31cc013d82e0da042 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -1568,7 +1568,7 @@ int commit_tree_extended(const char *msg, size_t msg_len,
                goto out;
        }
 
-       result = write_object_file(buffer.buf, buffer.len, commit_type, ret);
+       result = write_object_file(buffer.buf, buffer.len, OBJ_COMMIT, ret);
 out:
        strbuf_release(&buffer);
        return result;
@@ -1713,7 +1713,7 @@ size_t ignore_non_trailer(const char *buf, size_t len)
 }
 
 int run_commit_hook(int editor_is_used, const char *index_file,
-                   const char *name, ...)
+                   int *invoked_hook, const char *name, ...)
 {
        struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
        va_list args;
index 38cc5426615fdf1efd5b407e7507991db45100c2..3b174135bcff3e720139e687aadacf733e414bb6 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -369,7 +369,8 @@ int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused)
 int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void *unused);
 
 LAST_ARG_MUST_BE_NULL
-int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...);
+int run_commit_hook(int editor_is_used, const char *index_file,
+                   int *invoked_hook, const char *name, ...);
 
 /* Sign a commit or tag buffer, storing the result in a header. */
 int sign_with_header(struct strbuf *buf, const char *keyid);
index 03af369b2b972fdce2476b256ef3679ec88e025e..58f347d6ae5d88e9ecd6968166e2bb815a4de8ae 100644 (file)
@@ -961,9 +961,11 @@ static inline void time_t_to_filetime(time_t t, FILETIME *ft)
 int mingw_utime (const char *file_name, const struct utimbuf *times)
 {
        FILETIME mft, aft;
-       int fh, rc;
+       int rc;
        DWORD attrs;
        wchar_t wfilename[MAX_PATH];
+       HANDLE osfilehandle;
+
        if (xutftowcs_path(wfilename, file_name) < 0)
                return -1;
 
@@ -975,7 +977,17 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
                SetFileAttributesW(wfilename, attrs & ~FILE_ATTRIBUTE_READONLY);
        }
 
-       if ((fh = _wopen(wfilename, O_RDWR | O_BINARY)) < 0) {
+       osfilehandle = CreateFileW(wfilename,
+                                  FILE_WRITE_ATTRIBUTES,
+                                  0 /*FileShare.None*/,
+                                  NULL,
+                                  OPEN_EXISTING,
+                                  (attrs != INVALID_FILE_ATTRIBUTES &&
+                                       (attrs & FILE_ATTRIBUTE_DIRECTORY)) ?
+                                       FILE_FLAG_BACKUP_SEMANTICS : 0,
+                                  NULL);
+       if (osfilehandle == INVALID_HANDLE_VALUE) {
+               errno = err_win_to_posix(GetLastError());
                rc = -1;
                goto revert_attrs;
        }
@@ -987,12 +999,15 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
                GetSystemTimeAsFileTime(&mft);
                aft = mft;
        }
-       if (!SetFileTime((HANDLE)_get_osfhandle(fh), NULL, &aft, &mft)) {
+
+       if (!SetFileTime(osfilehandle, NULL, &aft, &mft)) {
                errno = EINVAL;
                rc = -1;
        } else
                rc = 0;
-       close(fh);
+
+       if (osfilehandle != INVALID_HANDLE_VALUE)
+               CloseHandle(osfilehandle);
 
 revert_attrs:
        if (attrs != INVALID_FILE_ATTRIBUTES &&
index 5b903e7c7e3e32c25e59efcc151217d00d243d65..3620184e790ca93b29fd403ba884f337ca5ec4a1 100644 (file)
@@ -11,7 +11,7 @@
 static void restore_term_on_signal(int sig)
 {
        restore_term();
-       sigchain_pop(sig);
+       /* restore_term calls sigchain_pop_common */
        raise(sig);
 }
 
@@ -31,14 +31,20 @@ void restore_term(void)
        tcsetattr(term_fd, TCSAFLUSH, &old_term);
        close(term_fd);
        term_fd = -1;
+       sigchain_pop_common();
 }
 
 int save_term(int full_duplex)
 {
        if (term_fd < 0)
                term_fd = open("/dev/tty", O_RDWR);
+       if (term_fd < 0)
+               return -1;
+       if (tcgetattr(term_fd, &old_term) < 0)
+               return -1;
+       sigchain_push_common(restore_term_on_signal);
 
-       return (term_fd < 0) ? -1 : tcgetattr(term_fd, &old_term);
+       return 0;
 }
 
 static int disable_bits(tcflag_t bits)
@@ -49,12 +55,16 @@ static int disable_bits(tcflag_t bits)
                goto error;
 
        t = old_term;
-       sigchain_push_common(restore_term_on_signal);
 
        t.c_lflag &= ~bits;
+       if (bits & ICANON) {
+               t.c_cc[VMIN] = 1;
+               t.c_cc[VTIME] = 0;
+       }
        if (!tcsetattr(term_fd, TCSAFLUSH, &t))
                return 0;
 
+       sigchain_pop_common();
 error:
        close(term_fd);
        term_fd = -1;
@@ -100,6 +110,8 @@ void restore_term(void)
                return;
        }
 
+       sigchain_pop_common();
+
        if (hconin == INVALID_HANDLE_VALUE)
                return;
 
@@ -134,6 +146,7 @@ int save_term(int full_duplex)
 
        GetConsoleMode(hconin, &cmode_in);
        use_stty = 0;
+       sigchain_push_common(restore_term_on_signal);
        return 0;
 error:
        CloseHandle(hconin);
@@ -150,7 +163,11 @@ static int disable_bits(DWORD bits)
 
                if (bits & ENABLE_LINE_INPUT) {
                        string_list_append(&stty_restore, "icanon");
-                       strvec_push(&cp.args, "-icanon");
+                       /*
+                        * POSIX allows VMIN and VTIME to overlap with VEOF and
+                        * VEOL - let's hope that is not the case on windows.
+                        */
+                       strvec_pushl(&cp.args, "-icanon", "min", "1", "time", "0", NULL);
                }
 
                if (bits & ENABLE_ECHO_INPUT) {
@@ -177,10 +194,10 @@ static int disable_bits(DWORD bits)
        if (save_term(0) < 0)
                return -1;
 
-       sigchain_push_common(restore_term_on_signal);
        if (!SetConsoleMode(hconin, cmode_in & ~bits)) {
                CloseHandle(hconin);
                hconin = INVALID_HANDLE_VALUE;
+               sigchain_pop_common();
                return -1;
        }
 
@@ -385,7 +402,7 @@ int read_key_without_echo(struct strbuf *buf)
 
                        ch = getchar();
                        if (ch == EOF)
-                               return 0;
+                               break;
                        strbuf_addch(buf, ch);
                }
        }
index e1770c575b2c5c501d3fbf3bcbe3873d288a1c92..0fb9fa147c4ff0cb4ee509ecae1d4bb5c7cc9050 100644 (file)
@@ -1,7 +1,15 @@
 #ifndef COMPAT_TERMINAL_H
 #define COMPAT_TERMINAL_H
 
+/*
+ * Save the terminal attributes so they can be restored later by a
+ * call to restore_term(). Note that every successful call to
+ * save_term() must be matched by a call to restore_term() even if the
+ * attributes have not been changed. Returns 0 on success, -1 on
+ * failure.
+ */
 int save_term(int full_duplex);
+/* Restore the terminal attributes that were saved with save_term() */
 void restore_term(void);
 
 char *git_terminal_prompt(const char *prompt, int echo);
index 383b1a4885ba92cdf25447f078406bb4680c787d..e78397725c955e3976fab4016c37cc3870f26ea7 100644 (file)
--- a/config.c
+++ b/config.c
@@ -1654,6 +1654,17 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
        return platform_core_config(var, value, cb);
 }
 
+static int git_default_sparse_config(const char *var, const char *value)
+{
+       if (!strcmp(var, "sparse.expectfilesoutsideofpatterns")) {
+               sparse_expect_files_outside_of_patterns = git_config_bool(var, value);
+               return 0;
+       }
+
+       /* Add other config variables here and to Documentation/config/sparse.txt. */
+       return 0;
+}
+
 static int git_default_i18n_config(const char *var, const char *value)
 {
        if (!strcmp(var, "i18n.commitencoding"))
@@ -1785,6 +1796,9 @@ int git_default_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (starts_with(var, "sparse."))
+               return git_default_sparse_config(var, value);
+
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
 }
index 4352ea39e9b9b7e2103560729202c63cb1fd90d4..7727b707b743a0a467331e5068004e70ad5a5a76 100644 (file)
@@ -727,7 +727,6 @@ vcxproj:
        git diff-index --cached --quiet HEAD --
 
        # Make .vcxproj files and add them
-       unset QUIET_GEN QUIET_BUILT_IN; \
        perl contrib/buildsystems/generate -g Vcxproj
        git add -f git.sln {*,*/lib,t/helper/*}/*.vcxproj
 
index 231b1ee17963c5d41a1667428ea7e64f8ac285c3..5e86d78e19b8f69338ddc6a75ef195ea8c8ecbd8 100644 (file)
@@ -1,18 +1,8 @@
-QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
-QUIET_SUBDIR1  =
-
-ifneq ($(findstring s,$(MAKEFLAGS)),s)
-ifndef V
-       QUIET_GEN      = @echo '   ' GEN $@;
-       QUIET_SUBDIR0  = +@subdir=
-       QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
-                        $(MAKE) $(PRINT_DIR) -C $$subdir
-else
-       export V
-endif
-endif
-
-all:
+# The default target of this Makefile is...
+all::
+
+# Import tree-wide shared Makefile behavior and libraries
+include ../../shared.mak
 
 include ../../config.mak.uname
 -include ../../config.mak.autogen
index 6170672bb371f98cdb4216591382298b2c0a2dbf..01e82e56d15629abd0444646341c1a9d639fc310 100644 (file)
@@ -1,3 +1,6 @@
+# Import tree-wide shared Makefile behavior and libraries
+include ../../../shared.mak
+
 # Run scalar tests
 #
 # Copyright (c) 2005,2021 Junio C Hamano, Johannes Schindelin
index 3d53a75a7842c6eaaf984673b63a55a42cf7f76c..8e39731efb0bd26e9aa6c6341f049f2965e659f2 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -1159,7 +1159,7 @@ static int ident_to_worktree(const char *src, size_t len,
        /* are we "faking" in place editing ? */
        if (src == buf->buf)
                to_free = strbuf_detach(buf, NULL);
-       hash_object_file(the_hash_algo, src, len, "blob", &oid);
+       hash_object_file(the_hash_algo, src, len, OBJ_BLOB, &oid);
 
        strbuf_grow(buf, len + cnt * (the_hash_algo->hexsz + 3));
        for (;;) {
index e7240f3f636f9d687836b7fe6df65b548ce285fd..f6389a50684a6e99bad1fe39b04a96fb99abc726 100644 (file)
@@ -130,6 +130,7 @@ static void credential_apply_config(struct credential *c)
        git_config(urlmatch_config_entry, &config);
        string_list_clear(&config.vars, 1);
        free(normalized_url);
+       urlmatch_config_release(&config);
        strbuf_release(&url);
 
        c->configured = 1;
diff --git a/diff.c b/diff.c
index 2bd5e0d81726c43c95ba1e69d3f287e691c9f606..6b22946cd0eac228a58142041765fa17a2697ed1 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -2150,6 +2150,7 @@ static void diff_words_flush(struct emit_callback *ecbdata)
 
                for (i = 0; i < wol->nr; i++)
                        free((void *)wol->buf[i].line);
+               free(wol->buf);
 
                wol->nr = 0;
        }
index bebd4ed6a42a1612f666f9221010479bc1263ccd..c0422d9e709a65d5a77d1444fcff0a4d313299bd 100644 (file)
@@ -261,7 +261,7 @@ static unsigned int hash_filespec(struct repository *r,
                if (diff_populate_filespec(r, filespec, NULL))
                        return 0;
                hash_object_file(r->hash_algo, filespec->data, filespec->size,
-                                "blob", &filespec->oid);
+                                OBJ_BLOB, &filespec->oid);
        }
        return oidhash(&filespec->oid);
 }
diff --git a/dir.c b/dir.c
index 79a5f6918c815152cdb1ae3b74a3ea2756a9d363..f2b0f242101b290d4fc4615dda7b0e1a892947d3 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -1113,7 +1113,7 @@ static int add_patterns(const char *fname, const char *base, int baselen,
                                       &istate->cache[pos]->oid);
                        else
                                hash_object_file(the_hash_algo, buf, size,
-                                                "blob", &oid_stat->oid);
+                                                OBJ_BLOB, &oid_stat->oid);
                        fill_stat_data(&oid_stat->stat, &st);
                        oid_stat->valid = 1;
                }
@@ -1463,10 +1463,11 @@ static int path_in_sparse_checkout_1(const char *path,
        const char *end, *slash;
 
        /*
-        * We default to accepting a path if there are no patterns or
-        * they are of the wrong type.
+        * We default to accepting a path if the path is empty, there are no
+        * patterns, or the patterns are of the wrong type.
         */
-       if (init_sparse_checkout_patterns(istate) ||
+       if (!*path ||
+           init_sparse_checkout_patterns(istate) ||
            (require_cone_mode &&
             !istate->sparse_checkout_patterns->use_cone_patterns))
                return 1;
@@ -2781,7 +2782,8 @@ void remove_untracked_cache(struct index_state *istate)
 
 static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *dir,
                                                      int base_len,
-                                                     const struct pathspec *pathspec)
+                                                     const struct pathspec *pathspec,
+                                                     struct index_state *istate)
 {
        struct untracked_cache_dir *root;
        static int untracked_cache_disabled = -1;
@@ -2845,8 +2847,11 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d
                return NULL;
        }
 
-       if (!dir->untracked->root)
+       if (!dir->untracked->root) {
+               /* Untracked cache existed but is not initialized; fix that */
                FLEX_ALLOC_STR(dir->untracked->root, name, "");
+               istate->cache_changed |= UNTRACKED_CHANGED;
+       }
 
        /* Validate $GIT_DIR/info/exclude and core.excludesfile */
        root = dir->untracked->root;
@@ -2916,7 +2921,7 @@ int read_directory(struct dir_struct *dir, struct index_state *istate,
                return dir->nr;
        }
 
-       untracked = validate_untracked_cache(dir, len, pathspec);
+       untracked = validate_untracked_cache(dir, len, pathspec, istate);
        if (!untracked)
                /*
                 * make sure untracked cache code path is disabled,
index fd0501e77a5b8d96dad8b5fe3714b74f13786be7..fb55bf61290641f260448a5d7bd78c7a59aee4d4 100644 (file)
@@ -70,6 +70,7 @@ char *notes_ref_name;
 int grafts_replace_parents = 1;
 int core_apply_sparse_checkout;
 int core_sparse_checkout_cone;
+int sparse_expect_files_outside_of_patterns;
 int merge_log_config = -1;
 int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
 unsigned long pack_size_limit_cfg;
index 876907b9df4461059422797aea45385bf1d05de9..e50e2fafaec9d7cf378495bbf80af95cbb3d1d54 100644 (file)
@@ -534,9 +534,7 @@ void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
 /*
  * Let callers be aware of the constant return value; this can help
  * gcc with -Wuninitialized analysis. We restrict this trick to gcc, though,
- * because some compilers may not support variadic macros. Since we're only
- * trying to help gcc, anyway, it's OK; other compilers will fall back to
- * using the function as usual.
+ * because other compilers may be confused by this.
  */
 #if defined(__GNUC__)
 static inline int const_error(void)
@@ -1258,24 +1256,12 @@ static inline int regexec_buf(const regex_t *preg, const char *buf, size_t size,
 #endif
 #endif
 
-/*
- * This is always defined as a first step towards making the use of variadic
- * macros unconditional. If it causes compilation problems on your platform,
- * please report it to the Git mailing list at git@vger.kernel.org.
- */
-#define HAVE_VARIADIC_MACROS 1
-
 /* usage.c: only to be used for testing BUG() implementation (see test-tool) */
 extern int BUG_exit_code;
 
-#ifdef HAVE_VARIADIC_MACROS
 __attribute__((format (printf, 3, 4))) NORETURN
 void BUG_fl(const char *file, int line, const char *fmt, ...);
 #define BUG(...) BUG_fl(__FILE__, __LINE__, __VA_ARGS__)
-#else
-__attribute__((format (printf, 1, 2))) NORETURN
-void BUG(const char *fmt, ...);
-#endif
 
 /*
  * Preserves errno, prints a message, but gives no warning for ENOENT.
index 87772ac89174fa7d2d47262f164c2dbc9f854ba7..aa8bdfca9def032152e613730e416e5f8243d167 100755 (executable)
@@ -247,20 +247,6 @@ cmd_deinit()
        git ${wt_prefix:+-C "$wt_prefix"} submodule--helper deinit ${GIT_QUIET:+--quiet} ${force:+--force} ${deinit_all:+--all} -- "$@"
 }
 
-# usage: fetch_in_submodule <module_path> [<depth>] [<sha1>]
-# Because arguments are positional, use an empty string to omit <depth>
-# but include <sha1>.
-fetch_in_submodule () (
-       sanitize_submodule_env &&
-       cd "$1" &&
-       if test $# -eq 3
-       then
-               echo "$3" | git fetch ${GIT_QUIET:+--quiet} --stdin ${2:+"$2"}
-       else
-               git fetch ${GIT_QUIET:+--quiet} ${2:+"$2"}
-       fi
-)
-
 #
 # Update each submodule path to correct revision, using clone and checkout as needed
 #
@@ -370,19 +356,11 @@ cmd_update()
                shift
        done
 
-       if test -n "$filter" && test "$init" != "1"
-       then
-               usage
-       fi
-
-       if test -n "$init"
-       then
-               cmd_init "--" "$@" || return
-       fi
-
        {
-       git submodule--helper update-clone ${GIT_QUIET:+--quiet} \
+       git ${wt_prefix:+-C "$wt_prefix"} submodule--helper update-clone \
+               ${GIT_QUIET:+--quiet} \
                ${progress:+"--progress"} \
+               ${init:+--init} \
                ${wt_prefix:+--prefix "$wt_prefix"} \
                ${prefix:+--recursive-prefix "$prefix"} \
                ${update:+--update "$update"} \
@@ -402,33 +380,11 @@ cmd_update()
        do
                die_if_unmatched "$quickabort" "$sha1"
 
-               git submodule--helper ensure-core-worktree "$sm_path" || exit 1
-
                displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
 
-               if test $just_cloned -eq 1
+               if test $just_cloned -eq 0
                then
-                       subsha1=
-               else
                        just_cloned=
-                       subsha1=$(sanitize_submodule_env; cd "$sm_path" &&
-                               git rev-parse --verify HEAD) ||
-                       die "fatal: $(eval_gettext "Unable to find current revision in submodule path '\$displaypath'")"
-               fi
-
-               if test -n "$remote"
-               then
-                       branch=$(git submodule--helper remote-branch "$sm_path")
-                       if test -z "$nofetch"
-                       then
-                               # Fetch remote before determining tracking $sha1
-                               fetch_in_submodule "$sm_path" $depth ||
-                               die "fatal: $(eval_gettext "Unable to fetch in submodule path '\$sm_path'")"
-                       fi
-                       remote_name=$(sanitize_submodule_env; cd "$sm_path" && git submodule--helper print-default-remote)
-                       sha1=$(sanitize_submodule_env; cd "$sm_path" &&
-                               git rev-parse --verify "${remote_name}/${branch}") ||
-                       die "fatal: $(eval_gettext "Unable to find current \${remote_name}/\${branch} revision in submodule path '\$sm_path'")"
                fi
 
                out=$(git submodule--helper run-update-procedure \
@@ -441,7 +397,7 @@ cmd_update()
                          ${update:+--update "$update"} \
                          ${prefix:+--recursive-prefix "$prefix"} \
                          ${sha1:+--oid "$sha1"} \
-                         ${subsha1:+--suboid "$subsha1"} \
+                         ${remote:+--remote} \
                          "--" \
                          "$sm_path")
 
index fbd1c20a232bb098509e0e25981de5016dea555f..606b50104c6b9b3beda8a3029fcd30dd553508d9 100755 (executable)
@@ -4213,8 +4213,7 @@ sub git_header_html {
        my %opts = @_;
 
        my $title = get_page_title();
-       my $content_type = get_content_type_html();
-       print $cgi->header(-type=>$content_type, -charset => 'utf-8',
+       print $cgi->header(-type=>get_content_type_html(), -charset => 'utf-8',
                           -status=> $status, -expires => $expires)
                unless ($opts{'-no_http_header'});
        my $mod_perl_version = $ENV{'MOD_PERL'} ? " $ENV{'MOD_PERL'}" : '';
@@ -4225,7 +4224,6 @@ sub git_header_html {
 <!-- git web interface version $version, (C) 2005-2006, Kay Sievers <kay.sievers\@vrfy.org>, Christian Gierke -->
 <!-- git core binaries version $git_version -->
 <head>
-<meta http-equiv="content-type" content="$content_type; charset=utf-8"/>
 <meta name="generator" content="gitweb/$version git/$git_version$mod_perl_version"/>
 <meta name="robots" content="index, nofollow"/>
 <title>$title</title>
index aa50224e67c031c892ee66d02bdf37fa1790648a..280f1fa1a58233f032cce7f2fe8bf4317f1e0ab8 100644 (file)
@@ -934,6 +934,7 @@ static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature,
        struct child_process gpg = CHILD_PROCESS_INIT;
        int ret;
        size_t bottom;
+       const char *cp;
        struct strbuf gpg_status = STRBUF_INIT;
 
        strvec_pushl(&gpg.args,
@@ -953,7 +954,13 @@ static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature,
                           signature, 1024, &gpg_status, 0);
        sigchain_pop(SIGPIPE);
 
-       ret |= !strstr(gpg_status.buf, "\n[GNUPG:] SIG_CREATED ");
+       for (cp = gpg_status.buf;
+            cp && (cp = strstr(cp, "[GNUPG:] SIG_CREATED "));
+            cp++) {
+               if (cp == gpg_status.buf || cp[-1] == '\n')
+                       break; /* found */
+       }
+       ret |= !cp;
        strbuf_release(&gpg_status);
        if (ret)
                return error(_("gpg failed to sign the data"));
diff --git a/help.c b/help.c
index 71444906ddfb24c6604a2da547cb94675553bcb0..afd3af241244787bef41279bf8d0ce30a2a993e8 100644 (file)
--- a/help.c
+++ b/help.c
@@ -124,7 +124,9 @@ static void print_cmd_by_category(const struct category_description *catdesc,
                uint32_t mask = catdesc[i].category;
                const char *desc = catdesc[i].desc;
 
-               printf("\n%s\n", _(desc));
+               if (i)
+                       putchar('\n');
+               puts(_(desc));
                print_command_list(cmds, mask, longest);
        }
        free(cmds);
@@ -317,7 +319,7 @@ void list_commands(struct cmdnames *main_cmds, struct cmdnames *other_cmds)
        }
 
        if (other_cmds->cnt) {
-               printf_ln(_("git commands available from elsewhere on your $PATH"));
+               puts(_("git commands available from elsewhere on your $PATH"));
                putchar('\n');
                pretty_print_cmdnames(other_cmds, colopts);
                putchar('\n');
@@ -327,6 +329,7 @@ void list_commands(struct cmdnames *main_cmds, struct cmdnames *other_cmds)
 void list_common_cmds_help(void)
 {
        puts(_("These are common Git commands used in various situations:"));
+       putchar('\n');
        print_cmd_by_category(common_categories, NULL);
 }
 
@@ -432,15 +435,10 @@ static int get_alias(const char *var, const char *value, void *data)
        return 0;
 }
 
-void list_all_cmds_help(void)
+static void list_all_cmds_help_external_commands(void)
 {
        struct string_list others = STRING_LIST_INIT_DUP;
-       struct string_list alias_list = STRING_LIST_INIT_DUP;
-       struct cmdname_help *aliases;
-       int i, longest;
-
-       printf_ln(_("See 'git help <command>' to read about a specific subcommand"));
-       print_cmd_by_category(main_categories, &longest);
+       int i;
 
        list_all_other_cmds(&others);
        if (others.nr)
@@ -448,6 +446,13 @@ void list_all_cmds_help(void)
        for (i = 0; i < others.nr; i++)
                printf("   %s\n", others.items[i].string);
        string_list_clear(&others, 0);
+}
+
+static void list_all_cmds_help_aliases(int longest)
+{
+       struct string_list alias_list = STRING_LIST_INIT_DUP;
+       struct cmdname_help *aliases;
+       int i;
 
        git_config(get_alias, &alias_list);
        string_list_sort(&alias_list);
@@ -473,6 +478,20 @@ void list_all_cmds_help(void)
        string_list_clear(&alias_list, 1);
 }
 
+void list_all_cmds_help(int show_external_commands, int show_aliases)
+{
+       int longest;
+
+       puts(_("See 'git help <command>' to read about a specific subcommand"));
+       putchar('\n');
+       print_cmd_by_category(main_categories, &longest);
+
+       if (show_external_commands)
+               list_all_cmds_help_external_commands();
+       if (show_aliases)
+               list_all_cmds_help_aliases(longest);
+}
+
 int is_in_cmdlist(struct cmdnames *c, const char *s)
 {
        int i;
diff --git a/help.h b/help.h
index 9d383f1a0b22a9a6d29be5b21e7fe09fcae5ddb8..971a3ad855acdc2d02697993ed8a7730626b8572 100644 (file)
--- a/help.h
+++ b/help.h
@@ -20,7 +20,7 @@ static inline void mput_char(char c, unsigned int num)
 }
 
 void list_common_cmds_help(void);
-void list_all_cmds_help(void);
+void list_all_cmds_help(int show_external_commands, int show_aliases);
 void list_guides_help(void);
 
 void list_all_main_cmds(struct string_list *list);
diff --git a/hook.c b/hook.c
index 69a215b2c3c2c3b95a799a56325de9111873b39e..1d51be3b77a713bcd45428988c97841156a513cf 100644 (file)
--- a/hook.c
+++ b/hook.c
@@ -96,9 +96,13 @@ static int notify_hook_finished(int result,
                                void *pp_task_cb)
 {
        struct hook_cb_data *hook_cb = pp_cb;
+       struct run_hooks_opt *opt = hook_cb->options;
 
        hook_cb->rc |= result;
 
+       if (opt->invoked_hook)
+               *opt->invoked_hook = 1;
+
        return 0;
 }
 
@@ -123,6 +127,9 @@ int run_hooks_opt(const char *hook_name, struct run_hooks_opt *options)
        if (!options)
                BUG("a struct run_hooks_opt must be provided to run_hooks");
 
+       if (options->invoked_hook)
+               *options->invoked_hook = 0;
+
        if (!hook_path && !options->error_if_missing)
                goto cleanup;
 
diff --git a/hook.h b/hook.h
index 18d90aedf14066eeac2fc93144d02ea8a6545207..4258b13da0d7c3c88a81b79eb25e8cc6a29dc6b6 100644 (file)
--- a/hook.h
+++ b/hook.h
@@ -18,6 +18,18 @@ struct run_hooks_opt
         * translates to "struct child_process"'s "dir" member.
         */
        const char *dir;
+
+       /**
+        * A pointer which if provided will be set to 1 or 0 depending
+        * on if a hook was started, regardless of whether or not that
+        * was successful. I.e. if the underlying start_command() was
+        * successful this will be set to 1.
+        *
+        * Used for avoiding TOCTOU races in code that would otherwise
+        * call hook_exist() after a "maybe hook run" to see if a hook
+        * was invoked.
+        */
+       int *invoked_hook;
 };
 
 #define RUN_HOOKS_OPT_INIT { \
index 3309aaf004a4db175bc4c0b94b5c63efebcb30e3..f0c044dcf7661a37b2a03f59e11f9e0bcefba0ea 100644 (file)
@@ -363,7 +363,7 @@ static void start_put(struct transfer_request *request)
        git_zstream stream;
 
        unpacked = read_object_file(&request->obj->oid, &type, &len);
-       hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %"PRIuMAX , type_name(type), (uintmax_t)len) + 1;
+       hdrlen = format_object_header(hdr, sizeof(hdr), type, len);
 
        /* Set it up */
        git_deflate_init(&stream, zlib_compression_level);
index fd8d59f653ab9b04cc709f9d0f4b00f407bf7ae9..f02d8df1422f61077dc9e032befe68e755e88c56 100644 (file)
@@ -40,22 +40,7 @@ const char *list_object_filter_config_name(enum list_objects_filter_choice c)
        BUG("list_object_filter_config_name: invalid argument '%d'", c);
 }
 
-/*
- * Parse value of the argument to the "filter" keyword.
- * On the command line this looks like:
- *       --filter=<arg>
- * and in the pack protocol as:
- *       "filter" SP <arg>
- *
- * The filter keyword will be used by many commands.
- * See Documentation/rev-list-options.txt for allowed values for <arg>.
- *
- * Capture the given arg as the "filter_spec".  This can be forwarded to
- * subordinate commands when necessary (although it's better to pass it through
- * expand_list_objects_filter_spec() first).  We also "intern" the arg for the
- * convenience of the current command.
- */
-static int gently_parse_list_objects_filter(
+int gently_parse_list_objects_filter(
        struct list_objects_filter_options *filter_options,
        const char *arg,
        struct strbuf *errbuf)
@@ -415,3 +400,22 @@ void partial_clone_get_default_filter_spec(
                                         &errbuf);
        strbuf_release(&errbuf);
 }
+
+void list_objects_filter_copy(
+       struct list_objects_filter_options *dest,
+       const struct list_objects_filter_options *src)
+{
+       int i;
+       struct string_list_item *item;
+
+       /* Copy everything. We will overwrite the pointers shortly. */
+       memcpy(dest, src, sizeof(struct list_objects_filter_options));
+
+       string_list_init_dup(&dest->filter_spec);
+       for_each_string_list_item(item, &src->filter_spec)
+               string_list_append(&dest->filter_spec, item->string);
+
+       ALLOC_ARRAY(dest->sub, dest->sub_alloc);
+       for (i = 0; i < src->sub_nr; i++)
+               list_objects_filter_copy(&dest->sub[i], &src->sub[i]);
+}
index da5b6737e27e36a6e198a59a070cd30f17179ceb..2eb6c983949db0354b0041f10c36e24dc2885116 100644 (file)
@@ -72,6 +72,26 @@ struct list_objects_filter_options {
 /* Normalized command line arguments */
 #define CL_ARG__FILTER "filter"
 
+/*
+ * Parse value of the argument to the "filter" keyword.
+ * On the command line this looks like:
+ *       --filter=<arg>
+ * and in the pack protocol as:
+ *       "filter" SP <arg>
+ *
+ * The filter keyword will be used by many commands.
+ * See Documentation/rev-list-options.txt for allowed values for <arg>.
+ *
+ * Capture the given arg as the "filter_spec".  This can be forwarded to
+ * subordinate commands when necessary (although it's better to pass it through
+ * expand_list_objects_filter_spec() first).  We also "intern" the arg for the
+ * convenience of the current command.
+ */
+int gently_parse_list_objects_filter(
+       struct list_objects_filter_options *filter_options,
+       const char *arg,
+       struct strbuf *errbuf);
+
 void list_objects_filter_die_if_populated(
        struct list_objects_filter_options *filter_options);
 
@@ -132,4 +152,8 @@ void partial_clone_get_default_filter_spec(
        struct list_objects_filter_options *filter_options,
        const char *remote);
 
+void list_objects_filter_copy(
+       struct list_objects_filter_options *dest,
+       const struct list_objects_filter_options *src);
+
 #endif /* LIST_OBJECTS_FILTER_OPTIONS_H */
index 2f623f8211534dabe732b1da140a2512a5a5fcf5..250d9de41cb56072e95420530b203ce417191a2b 100644 (file)
@@ -21,6 +21,23 @@ struct traversal_context {
        struct filter *filter;
 };
 
+static void show_commit(struct traversal_context *ctx,
+                       struct commit *commit)
+{
+       if (!ctx->show_commit)
+               return;
+       ctx->show_commit(commit, ctx->show_data);
+}
+
+static void show_object(struct traversal_context *ctx,
+                       struct object *object,
+                       const char *name)
+{
+       if (!ctx->show_object)
+               return;
+       ctx->show_object(object, name, ctx->show_data);
+}
+
 static void process_blob(struct traversal_context *ctx,
                         struct blob *blob,
                         struct strbuf *path,
@@ -60,7 +77,7 @@ static void process_blob(struct traversal_context *ctx,
        if (r & LOFR_MARK_SEEN)
                obj->flags |= SEEN;
        if (r & LOFR_DO_SHOW)
-               ctx->show_object(obj, path->buf, ctx->show_data);
+               show_object(ctx, obj, path->buf);
        strbuf_setlen(path, pathlen);
 }
 
@@ -194,7 +211,7 @@ static void process_tree(struct traversal_context *ctx,
        if (r & LOFR_MARK_SEEN)
                obj->flags |= SEEN;
        if (r & LOFR_DO_SHOW)
-               ctx->show_object(obj, base->buf, ctx->show_data);
+               show_object(ctx, obj, base->buf);
        if (base->len)
                strbuf_addch(base, '/');
 
@@ -210,7 +227,7 @@ static void process_tree(struct traversal_context *ctx,
        if (r & LOFR_MARK_SEEN)
                obj->flags |= SEEN;
        if (r & LOFR_DO_SHOW)
-               ctx->show_object(obj, base->buf, ctx->show_data);
+               show_object(ctx, obj, base->buf);
 
        strbuf_setlen(base, baselen);
        free_tree_buffer(tree);
@@ -228,7 +245,7 @@ static void process_tag(struct traversal_context *ctx,
        if (r & LOFR_MARK_SEEN)
                tag->object.flags |= SEEN;
        if (r & LOFR_DO_SHOW)
-               ctx->show_object(&tag->object, name, ctx->show_data);
+               show_object(ctx, &tag->object, name);
 }
 
 static void mark_edge_parents_uninteresting(struct commit *commit,
@@ -402,7 +419,7 @@ static void do_traverse(struct traversal_context *ctx)
                if (r & LOFR_MARK_SEEN)
                        commit->object.flags |= SEEN;
                if (r & LOFR_DO_SHOW)
-                       ctx->show_commit(commit, ctx->show_data);
+                       show_commit(ctx, commit);
 
                if (ctx->revs->tree_blobs_in_commit_order)
                        /*
@@ -416,35 +433,25 @@ static void do_traverse(struct traversal_context *ctx)
        strbuf_release(&csp);
 }
 
-void traverse_commit_list(struct rev_info *revs,
-                         show_commit_fn show_commit,
-                         show_object_fn show_object,
-                         void *show_data)
-{
-       struct traversal_context ctx;
-       ctx.revs = revs;
-       ctx.show_commit = show_commit;
-       ctx.show_object = show_object;
-       ctx.show_data = show_data;
-       ctx.filter = NULL;
-       do_traverse(&ctx);
-}
-
 void traverse_commit_list_filtered(
-       struct list_objects_filter_options *filter_options,
        struct rev_info *revs,
        show_commit_fn show_commit,
        show_object_fn show_object,
        void *show_data,
        struct oidset *omitted)
 {
-       struct traversal_context ctx;
+       struct traversal_context ctx = {
+               .revs = revs,
+               .show_object = show_object,
+               .show_commit = show_commit,
+               .show_data = show_data,
+       };
+
+       if (revs->filter.choice)
+               ctx.filter = list_objects_filter__init(omitted, &revs->filter);
 
-       ctx.revs = revs;
-       ctx.show_object = show_object;
-       ctx.show_commit = show_commit;
-       ctx.show_data = show_data;
-       ctx.filter = list_objects_filter__init(omitted, filter_options);
        do_traverse(&ctx);
-       list_objects_filter__free(ctx.filter);
+
+       if (ctx.filter)
+               list_objects_filter__free(ctx.filter);
 }
index a952680e46671db2543bc4abff78a2d898cd1408..9eaf4de844950017a947df7efbcceaf9f0d9feb5 100644 (file)
@@ -7,7 +7,6 @@ struct rev_info;
 
 typedef void (*show_commit_fn)(struct commit *, void *);
 typedef void (*show_object_fn)(struct object *, const char *, void *);
-void traverse_commit_list(struct rev_info *, show_commit_fn, show_object_fn, void *);
 
 typedef void (*show_edge_fn)(struct commit *);
 void mark_edges_uninteresting(struct rev_info *revs,
@@ -18,11 +17,20 @@ struct oidset;
 struct list_objects_filter_options;
 
 void traverse_commit_list_filtered(
-       struct list_objects_filter_options *filter_options,
        struct rev_info *revs,
        show_commit_fn show_commit,
        show_object_fn show_object,
        void *show_data,
        struct oidset *omitted);
 
+static inline void traverse_commit_list(
+       struct rev_info *revs,
+       show_commit_fn show_commit,
+       show_object_fn show_object,
+       void *show_data)
+{
+       traverse_commit_list_filtered(revs, show_commit,
+                                     show_object, show_data, NULL);
+}
+
 #endif /* LIST_OBJECTS_H */
index 25165e2a915e0394cbfc2347d5f3eca83842beff..38e5cccc1a1889c186554f037975d780551b218b 100644 (file)
@@ -565,7 +565,7 @@ static int show_one_mergetag(struct commit *commit,
        struct strbuf signature = STRBUF_INIT;
 
        hash_object_file(the_hash_algo, extra->value, extra->len,
-                        type_name(OBJ_TAG), &oid);
+                        OBJ_TAG, &oid);
        tag = lookup_tag(the_repository, &oid);
        if (!tag)
                return -1; /* error message already given */
index 40ce152024d7dc3cf6d387dc5d0c1a47542129bd..7befdc5e4835d533b994f75ee42709937a5d046c 100644 (file)
--- a/mailmap.c
+++ b/mailmap.c
@@ -43,8 +43,8 @@ static void free_mailmap_info(void *p, const char *s)
 static void free_mailmap_entry(void *p, const char *s)
 {
        struct mailmap_entry *me = (struct mailmap_entry *)p;
-       debug_mm("mailmap: removing entries for <%s>, with %d sub-entries\n",
-                s, me->namemap.nr);
+       debug_mm("mailmap: removing entries for <%s>, with %"PRIuMAX" sub-entries\n",
+                s, (uintmax_t)me->namemap.nr);
        debug_mm("mailmap: - simple: '%s' <%s>\n",
                 debug_str(me->name), debug_str(me->email));
 
@@ -250,7 +250,8 @@ int read_mailmap(struct string_list *map)
 
 void clear_mailmap(struct string_list *map)
 {
-       debug_mm("mailmap: clearing %d entries...\n", map->nr);
+       debug_mm("mailmap: clearing %"PRIuMAX" entries...\n",
+                (uintmax_t)map->nr);
        map->strdup_strings = 1;
        string_list_clear_func(map, free_mailmap_entry);
        debug_mm("mailmap: cleared\n");
index df413989fa85cb007f3c7d88375283b6243687ca..49398e599fe3afdc88f481608fb58b0887b019e3 100644 (file)
@@ -235,7 +235,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,
                rewrite_with = oid2;
        }
        hashcpy(rewrite_here, rewrite_with->hash);
-       status = write_object_file(buf, sz, tree_type, result);
+       status = write_object_file(buf, sz, OBJ_TREE, result);
        free(buf);
        return status;
 }
index ff739d4b360627a18aaeac87e630c8d0b6ed57e2..8545354dafd0600e02eb1bb509d8f9bedd23d1c9 100644 (file)
@@ -639,8 +639,9 @@ static void path_msg(struct merge_options *opt,
 
        if (opt->record_conflict_msgs_as_headers && omittable_hint)
                return; /* Do not record mere hints in headers */
-       if (opt->record_conflict_msgs_as_headers && opt->priv->call_depth)
-               return; /* Do not record inner merge issues in headers */
+       if (opt->priv->call_depth && opt->verbosity < 5)
+               return; /* Ignore messages from inner merges */
+
        sb = strmap_get(&opt->priv->output, path);
        if (!sb) {
                sb = xmalloc(sizeof(*sb));
@@ -1937,7 +1938,7 @@ static int handle_content_merge(struct merge_options *opt,
 
                if (!ret &&
                    write_object_file(result_buf.ptr, result_buf.size,
-                                     blob_type, &result->oid))
+                                     OBJ_BLOB, &result->oid))
                        ret = err(opt, _("Unable to add %s to database"),
                                  path);
 
@@ -3391,7 +3392,7 @@ static void write_tree(struct object_id *result_oid,
        }
 
        /* Write this object file out, and record in result_oid */
-       write_object_file(buf.buf, buf.len, tree_type, result_oid);
+       write_object_file(buf.buf, buf.len, OBJ_TREE, result_oid);
        strbuf_release(&buf);
 }
 
@@ -4063,8 +4064,8 @@ static void process_entries(struct merge_options *opt,
        trace2_region_enter("merge", "process_entries cleanup", opt->repo);
        if (dir_metadata.offsets.nr != 1 ||
            (uintptr_t)dir_metadata.offsets.items[0].util != 0) {
-               printf("dir_metadata.offsets.nr = %d (should be 1)\n",
-                      dir_metadata.offsets.nr);
+               printf("dir_metadata.offsets.nr = %"PRIuMAX" (should be 1)\n",
+                      (uintmax_t)dir_metadata.offsets.nr);
                printf("dir_metadata.offsets.items[0].util = %u (should be 0)\n",
                       (unsigned)(uintptr_t)dir_metadata.offsets.items[0].util);
                fflush(stdout);
index 9ec1e6d043a2f85f61422138c97b379e1a5b70c9..1ee6364e8b16b17295258667e70b77ea1f1fffa4 100644 (file)
@@ -1376,7 +1376,7 @@ static int merge_mode_and_contents(struct merge_options *opt,
 
                        if (!ret &&
                            write_object_file(result_buf.ptr, result_buf.size,
-                                             blob_type, &result->blob.oid))
+                                             OBJ_BLOB, &result->blob.oid))
                                ret = err(opt, _("Unable to add %s to database"),
                                          a->path);
 
index 2473314d686858493c6730e895e7106e44a462f9..9dfd251a8151d4205297b5c27064a01acf448f69 100644 (file)
@@ -92,7 +92,7 @@ int notes_cache_put(struct notes_cache *c, struct object_id *key_oid,
 {
        struct object_id value_oid;
 
-       if (write_object_file(data, size, "blob", &value_oid) < 0)
+       if (write_object_file(data, size, OBJ_BLOB, &value_oid) < 0)
                return -1;
        return add_note(&c->tree, key_oid, &value_oid, NULL);
 }
diff --git a/notes.c b/notes.c
index f87dac40684030a70566a618ee3dbcedd97bc641..7452e71cc8dd289c7ace9361a6c6e090b8b113f9 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -675,7 +675,7 @@ static int tree_write_stack_finish_subtree(struct tree_write_stack *tws)
                ret = tree_write_stack_finish_subtree(n);
                if (ret)
                        return ret;
-               ret = write_object_file(n->buf.buf, n->buf.len, tree_type, &s);
+               ret = write_object_file(n->buf.buf, n->buf.len, OBJ_TREE, &s);
                if (ret)
                        return ret;
                strbuf_release(&n->buf);
@@ -836,7 +836,7 @@ int combine_notes_concatenate(struct object_id *cur_oid,
        free(new_msg);
 
        /* create a new blob object from buf */
-       ret = write_object_file(buf, buf_len, blob_type, cur_oid);
+       ret = write_object_file(buf, buf_len, OBJ_BLOB, cur_oid);
        free(buf);
        return ret;
 }
@@ -916,7 +916,7 @@ int combine_notes_cat_sort_uniq(struct object_id *cur_oid,
                                 string_list_join_lines_helper, &buf))
                goto out;
 
-       ret = write_object_file(buf.buf, buf.len, blob_type, cur_oid);
+       ret = write_object_file(buf.buf, buf.len, OBJ_BLOB, cur_oid);
 
 out:
        strbuf_release(&buf);
@@ -1192,7 +1192,7 @@ int write_notes_tree(struct notes_tree *t, struct object_id *result)
        ret = for_each_note(t, flags, write_each_note, &cb_data) ||
              write_each_non_note_until(NULL, &cb_data) ||
              tree_write_stack_finish_subtree(&root) ||
-             write_object_file(root.buf.buf, root.buf.len, tree_type, result);
+             write_object_file(root.buf.buf, root.buf.len, OBJ_TREE, result);
        strbuf_release(&root.buf);
        return ret;
 }
index 03bd6a3baf3ed35f8636b0318f165c4017455f59..bdc5cbdd3868612fc7e0fb43d6268aaa1420b3f6 100644 (file)
@@ -1049,35 +1049,50 @@ void *xmmap(void *start, size_t length,
        return ret;
 }
 
-/*
- * With an in-core object data in "map", rehash it to make sure the
- * object name actually matches "oid" to detect object corruption.
- * With "map" == NULL, try reading the object named with "oid" using
- * the streaming interface and rehash it to do the same.
- */
+static int format_object_header_literally(char *str, size_t size,
+                                         const char *type, size_t objsize)
+{
+       return xsnprintf(str, size, "%s %"PRIuMAX, type, (uintmax_t)objsize) + 1;
+}
+
+int format_object_header(char *str, size_t size, enum object_type type,
+                        size_t objsize)
+{
+       const char *name = type_name(type);
+
+       if (!name)
+               BUG("could not get a type name for 'enum object_type' value %d", type);
+
+       return format_object_header_literally(str, size, name, objsize);
+}
+
 int check_object_signature(struct repository *r, const struct object_id *oid,
-                          void *map, unsigned long size, const char *type,
-                          struct object_id *real_oidp)
+                          void *buf, unsigned long size,
+                          enum object_type type)
 {
-       struct object_id tmp;
-       struct object_id *real_oid = real_oidp ? real_oidp : &tmp;
+       struct object_id real_oid;
+
+       hash_object_file(r->hash_algo, buf, size, type, &real_oid);
+
+       return !oideq(oid, &real_oid) ? -1 : 0;
+}
+
+int stream_object_signature(struct repository *r, const struct object_id *oid)
+{
+       struct object_id real_oid;
+       unsigned long size;
        enum object_type obj_type;
        struct git_istream *st;
        git_hash_ctx c;
        char hdr[MAX_HEADER_LEN];
        int hdrlen;
 
-       if (map) {
-               hash_object_file(r->hash_algo, map, size, type, real_oid);
-               return !oideq(oid, real_oid) ? -1 : 0;
-       }
-
        st = open_istream(r, oid, &obj_type, &size, NULL);
        if (!st)
                return -1;
 
        /* Generate the header */
-       hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %"PRIuMAX , type_name(obj_type), (uintmax_t)size) + 1;
+       hdrlen = format_object_header(hdr, sizeof(hdr), obj_type, size);
 
        /* Sha1.. */
        r->hash_algo->init_fn(&c);
@@ -1094,9 +1109,9 @@ int check_object_signature(struct repository *r, const struct object_id *oid,
                        break;
                r->hash_algo->update_fn(&c, buf, readlen);
        }
-       r->hash_algo->final_oid_fn(real_oid, &c);
+       r->hash_algo->final_oid_fn(&real_oid, &c);
        close_istream(st);
-       return !oideq(oid, real_oid) ? -1 : 0;
+       return !oideq(oid, &real_oid) ? -1 : 0;
 }
 
 int git_open_cloexec(const char *name, int flags)
@@ -1662,7 +1677,7 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
 {
        struct cached_object *co;
 
-       hash_object_file(the_hash_algo, buf, len, type_name(type), oid);
+       hash_object_file(the_hash_algo, buf, len, type, oid);
        if (has_object_file_with_flags(oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
            find_cached_object(oid))
                return 0;
@@ -1722,16 +1737,15 @@ void *read_object_file_extended(struct repository *r,
 
 void *read_object_with_reference(struct repository *r,
                                 const struct object_id *oid,
-                                const char *required_type_name,
+                                enum object_type required_type,
                                 unsigned long *size,
                                 struct object_id *actual_oid_return)
 {
-       enum object_type type, required_type;
+       enum object_type type;
        void *buffer;
        unsigned long isize;
        struct object_id actual_oid;
 
-       required_type = type_from_string(required_type_name);
        oidcpy(&actual_oid, oid);
        while (1) {
                int ref_length = -1;
@@ -1769,21 +1783,40 @@ void *read_object_with_reference(struct repository *r,
        }
 }
 
+static void hash_object_body(const struct git_hash_algo *algo, git_hash_ctx *c,
+                            const void *buf, unsigned long len,
+                            struct object_id *oid,
+                            char *hdr, int *hdrlen)
+{
+       algo->init_fn(c);
+       algo->update_fn(c, hdr, *hdrlen);
+       algo->update_fn(c, buf, len);
+       algo->final_oid_fn(oid, c);
+}
+
 static void write_object_file_prepare(const struct git_hash_algo *algo,
                                      const void *buf, unsigned long len,
-                                     const char *type, struct object_id *oid,
+                                     enum object_type type, struct object_id *oid,
                                      char *hdr, int *hdrlen)
 {
        git_hash_ctx c;
 
        /* Generate the header */
-       *hdrlen = xsnprintf(hdr, *hdrlen, "%s %"PRIuMAX , type, (uintmax_t)len)+1;
+       *hdrlen = format_object_header(hdr, *hdrlen, type, len);
 
        /* Sha1.. */
-       algo->init_fn(&c);
-       algo->update_fn(&c, hdr, *hdrlen);
-       algo->update_fn(&c, buf, len);
-       algo->final_oid_fn(oid, &c);
+       hash_object_body(algo, &c, buf, len, oid, hdr, hdrlen);
+}
+
+static void write_object_file_prepare_literally(const struct git_hash_algo *algo,
+                                     const void *buf, unsigned long len,
+                                     const char *type, struct object_id *oid,
+                                     char *hdr, int *hdrlen)
+{
+       git_hash_ctx c;
+
+       *hdrlen = format_object_header_literally(hdr, *hdrlen, type, len);
+       hash_object_body(algo, &c, buf, len, oid, hdr, hdrlen);
 }
 
 /*
@@ -1836,14 +1869,21 @@ static int write_buffer(int fd, const void *buf, size_t len)
        return 0;
 }
 
-int hash_object_file(const struct git_hash_algo *algo, const void *buf,
-                    unsigned long len, const char *type,
-                    struct object_id *oid)
+static void hash_object_file_literally(const struct git_hash_algo *algo,
+                                      const void *buf, unsigned long len,
+                                      const char *type, struct object_id *oid)
 {
        char hdr[MAX_HEADER_LEN];
        int hdrlen = sizeof(hdr);
-       write_object_file_prepare(algo, buf, len, type, oid, hdr, &hdrlen);
-       return 0;
+
+       write_object_file_prepare_literally(algo, buf, len, type, oid, hdr, &hdrlen);
+}
+
+void hash_object_file(const struct git_hash_algo *algo, const void *buf,
+                     unsigned long len, enum object_type type,
+                     struct object_id *oid)
+{
+       hash_object_file_literally(algo, buf, len, type_name(type), oid);
 }
 
 /* Finalize a file on disk, and close it. */
@@ -1998,7 +2038,7 @@ static int freshen_packed_object(const struct object_id *oid)
 }
 
 int write_object_file_flags(const void *buf, unsigned long len,
-                           const char *type, struct object_id *oid,
+                           enum object_type type, struct object_id *oid,
                            unsigned flags)
 {
        char hdr[MAX_HEADER_LEN];
@@ -2014,9 +2054,9 @@ int write_object_file_flags(const void *buf, unsigned long len,
        return write_loose_object(oid, hdr, hdrlen, buf, len, 0, flags);
 }
 
-int hash_object_file_literally(const void *buf, unsigned long len,
-                              const char *type, struct object_id *oid,
-                              unsigned flags)
+int write_object_file_literally(const void *buf, unsigned long len,
+                               const char *type, struct object_id *oid,
+                               unsigned flags)
 {
        char *header;
        int hdrlen, status = 0;
@@ -2024,8 +2064,8 @@ int hash_object_file_literally(const void *buf, unsigned long len,
        /* type string, SP, %lu of the length plus NUL must fit this */
        hdrlen = strlen(type) + MAX_HEADER_LEN;
        header = xmalloc(hdrlen);
-       write_object_file_prepare(the_hash_algo, buf, len, type, oid, header,
-                                 &hdrlen);
+       write_object_file_prepare_literally(the_hash_algo, buf, len, type,
+                                           oid, header, &hdrlen);
 
        if (!(flags & HASH_WRITE_OBJECT))
                goto cleanup;
@@ -2052,7 +2092,7 @@ int force_object_loose(const struct object_id *oid, time_t mtime)
        buf = read_object(the_repository, oid, &type, &len);
        if (!buf)
                return error(_("cannot read object for %s"), oid_to_hex(oid));
-       hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %"PRIuMAX , type_name(type), (uintmax_t)len) + 1;
+       hdrlen = format_object_header(hdr, sizeof(hdr), type, len);
        ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime, 0);
        free(buf);
 
@@ -2118,7 +2158,8 @@ static int index_mem(struct index_state *istate,
                     enum object_type type,
                     const char *path, unsigned flags)
 {
-       int ret, re_allocated = 0;
+       int ret = 0;
+       int re_allocated = 0;
        int write_object = flags & HASH_WRITE_OBJECT;
 
        if (!type)
@@ -2145,10 +2186,9 @@ static int index_mem(struct index_state *istate,
        }
 
        if (write_object)
-               ret = write_object_file(buf, size, type_name(type), oid);
+               ret = write_object_file(buf, size, type, oid);
        else
-               ret = hash_object_file(the_hash_algo, buf, size,
-                                      type_name(type), oid);
+               hash_object_file(the_hash_algo, buf, size, type, oid);
        if (re_allocated)
                free(buf);
        return ret;
@@ -2160,7 +2200,7 @@ static int index_stream_convert_blob(struct index_state *istate,
                                     const char *path,
                                     unsigned flags)
 {
-       int ret;
+       int ret = 0;
        const int write_object = flags & HASH_WRITE_OBJECT;
        struct strbuf sbuf = STRBUF_INIT;
 
@@ -2171,11 +2211,11 @@ static int index_stream_convert_blob(struct index_state *istate,
                                 get_conv_flags(flags));
 
        if (write_object)
-               ret = write_object_file(sbuf.buf, sbuf.len, type_name(OBJ_BLOB),
+               ret = write_object_file(sbuf.buf, sbuf.len, OBJ_BLOB,
                                        oid);
        else
-               ret = hash_object_file(the_hash_algo, sbuf.buf, sbuf.len,
-                                      type_name(OBJ_BLOB), oid);
+               hash_object_file(the_hash_algo, sbuf.buf, sbuf.len, OBJ_BLOB,
+                                oid);
        strbuf_release(&sbuf);
        return ret;
 }
@@ -2294,8 +2334,8 @@ int index_path(struct index_state *istate, struct object_id *oid,
                        return error_errno("readlink(\"%s\")", path);
                if (!(flags & HASH_WRITE_OBJECT))
                        hash_object_file(the_hash_algo, sb.buf, sb.len,
-                                        blob_type, oid);
-               else if (write_object_file(sb.buf, sb.len, blob_type, oid))
+                                        OBJ_BLOB, oid);
+               else if (write_object_file(sb.buf, sb.len, OBJ_BLOB, oid))
                        rc = error(_("%s: failed to insert into database"), path);
                strbuf_release(&sb);
                break;
@@ -2599,9 +2639,10 @@ int read_loose_object(const char *path,
                        git_inflate_end(&stream);
                        goto out;
                }
-               if (check_object_signature(the_repository, expected_oid,
+               hash_object_file_literally(the_repository->hash_algo,
                                           *contents, *size,
-                                          oi->type_name->buf, real_oid))
+                                          oi->type_name->buf, real_oid);
+               if (!oideq(expected_oid, real_oid))
                        goto out;
        }
 
index 6f89482df030cb28c91a7d2620903473e661370f..bd2322ed8ce3368d8b06ab570f2f1f4c2217d757 100644 (file)
@@ -245,22 +245,22 @@ static inline void *repo_read_object_file(struct repository *r,
 /* Read and unpack an object file into memory, write memory to an object file */
 int oid_object_info(struct repository *r, const struct object_id *, unsigned long *);
 
-int hash_object_file(const struct git_hash_algo *algo, const void *buf,
-                    unsigned long len, const char *type,
-                    struct object_id *oid);
+void hash_object_file(const struct git_hash_algo *algo, const void *buf,
+                     unsigned long len, enum object_type type,
+                     struct object_id *oid);
 
 int write_object_file_flags(const void *buf, unsigned long len,
-                           const char *type, struct object_id *oid,
+                           enum object_type type, struct object_id *oid,
                            unsigned flags);
 static inline int write_object_file(const void *buf, unsigned long len,
-                                   const char *type, struct object_id *oid)
+                                   enum object_type type, struct object_id *oid)
 {
        return write_object_file_flags(buf, len, type, oid, 0);
 }
 
-int hash_object_file_literally(const void *buf, unsigned long len,
-                              const char *type, struct object_id *oid,
-                              unsigned flags);
+int write_object_file_literally(const void *buf, unsigned long len,
+                               const char *type, struct object_id *oid,
+                               unsigned flags);
 
 /*
  * Add an object file to the in-memory object store, without writing it
@@ -331,6 +331,14 @@ int repo_has_object_file_with_flags(struct repository *r,
  */
 int has_loose_object_nonlocal(const struct object_id *);
 
+/**
+ * format_object_header() is a thin wrapper around s xsnprintf() that
+ * writes the initial "<type> <obj-len>" part of the loose object
+ * header. It returns the size that snprintf() returns + 1.
+ */
+int format_object_header(char *str, size_t size, enum object_type type,
+                        size_t objsize);
+
 void assert_oid_type(const struct object_id *oid, enum object_type expect);
 
 /*
index c37501fc1202d17bf1002d39b06afed864c11be3..588b8156f1d634a5cb9c25038961ca9a0d28d255 100644 (file)
--- a/object.c
+++ b/object.c
@@ -279,7 +279,7 @@ struct object *parse_object(struct repository *r, const struct object_id *oid)
        if ((obj && obj->type == OBJ_BLOB && repo_has_object_file(r, oid)) ||
            (!obj && repo_has_object_file(r, oid) &&
             oid_object_info(r, oid, NULL) == OBJ_BLOB)) {
-               if (check_object_signature(r, repl, NULL, 0, NULL, NULL) < 0) {
+               if (stream_object_signature(r, repl) < 0) {
                        error(_("hash mismatch %s"), oid_to_hex(oid));
                        return NULL;
                }
@@ -289,8 +289,7 @@ struct object *parse_object(struct repository *r, const struct object_id *oid)
 
        buffer = repo_read_object_file(r, oid, &type, &size);
        if (buffer) {
-               if (check_object_signature(r, repl, buffer, size,
-                                          type_name(type), NULL) < 0) {
+               if (check_object_signature(r, repl, buffer, size, type) < 0) {
                        free(buffer);
                        error(_("hash mismatch %s"), oid_to_hex(repl));
                        return NULL;
index cb556ab7753d5ddb01bc9421bbf490c0e684769b..a2219464c2b31043692fb9c0d224fdf0b1c2c253 100644 (file)
--- a/object.h
+++ b/object.h
@@ -75,7 +75,7 @@ struct object_array {
  * builtin/fsck.c:           0--3
  * builtin/gc.c:             0
  * builtin/index-pack.c:                                     2021
- * builtin/reflog.c:                   10--12
+ * reflog.c:                           10--12
  * builtin/show-branch.c:    0-------------------------------------------26
  * builtin/unpack-objects.c:                                 2021
  */
index 9c666cdb8bd9203f0749b54b6c012ba33896341e..97909d48da381f81622d36fb5889fd5d7013b04a 100644 (file)
@@ -739,8 +739,7 @@ static int add_commit_to_bitmap(struct bitmap_index *bitmap_git,
 static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
                                   struct rev_info *revs,
                                   struct object_list *roots,
-                                  struct bitmap *seen,
-                                  struct list_objects_filter_options *filter)
+                                  struct bitmap *seen)
 {
        struct bitmap *base = NULL;
        int needs_walk = 0;
@@ -823,9 +822,9 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
                show_data.bitmap_git = bitmap_git;
                show_data.base = base;
 
-               traverse_commit_list_filtered(filter, revs,
-                                             show_commit, show_object,
-                                             &show_data, NULL);
+               traverse_commit_list(revs,
+                                    show_commit, show_object,
+                                    &show_data);
 
                revs->include_check = NULL;
                revs->include_check_obj = NULL;
@@ -1219,7 +1218,6 @@ static int can_filter_bitmap(struct list_objects_filter_options *filter)
 }
 
 struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
-                                        struct list_objects_filter_options *filter,
                                         int filter_provided_objects)
 {
        unsigned int i;
@@ -1240,7 +1238,7 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
        if (revs->prune)
                return NULL;
 
-       if (!can_filter_bitmap(filter))
+       if (!can_filter_bitmap(&revs->filter))
                return NULL;
 
        /* try to open a bitmapped pack, but don't parse it yet
@@ -1297,8 +1295,7 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
 
        if (haves) {
                revs->ignore_missing_links = 1;
-               haves_bitmap = find_objects(bitmap_git, revs, haves, NULL,
-                                           filter);
+               haves_bitmap = find_objects(bitmap_git, revs, haves, NULL);
                reset_revision_walk();
                revs->ignore_missing_links = 0;
 
@@ -1306,8 +1303,7 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
                        BUG("failed to perform bitmap walk");
        }
 
-       wants_bitmap = find_objects(bitmap_git, revs, wants, haves_bitmap,
-                                   filter);
+       wants_bitmap = find_objects(bitmap_git, revs, wants, haves_bitmap);
 
        if (!wants_bitmap)
                BUG("failed to perform bitmap walk");
@@ -1315,8 +1311,10 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
        if (haves_bitmap)
                bitmap_and_not(wants_bitmap, haves_bitmap);
 
-       filter_bitmap(bitmap_git, (filter && filter_provided_objects) ? NULL : wants,
-                     wants_bitmap, filter);
+       filter_bitmap(bitmap_git,
+                     (revs->filter.choice && filter_provided_objects) ? NULL : wants,
+                     wants_bitmap,
+                     &revs->filter);
 
        bitmap_git->result = wants_bitmap;
        bitmap_git->haves = haves_bitmap;
index 19a63fa1abc6921df7470b84aa7275c0e7863fac..3d3ddd77345002f3075047b1f0bf545a4e229157 100644 (file)
@@ -10,7 +10,6 @@
 struct commit;
 struct repository;
 struct rev_info;
-struct list_objects_filter_options;
 
 static const char BITMAP_IDX_SIGNATURE[] = {'B', 'I', 'T', 'M'};
 
@@ -54,7 +53,6 @@ void test_bitmap_walk(struct rev_info *revs);
 int test_bitmap_commits(struct repository *r);
 int test_bitmap_hashes(struct repository *r);
 struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
-                                        struct list_objects_filter_options *filter,
                                         int filter_provided_objects);
 uint32_t midx_preferred_pack(struct bitmap_index *bitmap_git);
 int reuse_partial_packfile_from_bitmap(struct bitmap_index *,
index 3f418e3a6afb8ec7a8b401dcca89d5550813748f..bfb593ba7261a18a8283604f4e1cfdd10eb978f6 100644 (file)
@@ -127,7 +127,7 @@ static int verify_packfile(struct repository *r,
 
                if (type == OBJ_BLOB && big_file_threshold <= size) {
                        /*
-                        * Let check_object_signature() check it with
+                        * Let stream_object_signature() check it with
                         * the streaming interface; no point slurping
                         * the data in-core only to discard.
                         */
@@ -142,8 +142,11 @@ static int verify_packfile(struct repository *r,
                        err = error("cannot unpack %s from %s at offset %"PRIuMAX"",
                                    oid_to_hex(&oid), p->pack_name,
                                    (uintmax_t)entries[i].offset);
-               else if (check_object_signature(r, &oid, data, size,
-                                               type_name(type), NULL))
+               else if (data && check_object_signature(r, &oid, data, size,
+                                                       type) < 0)
+                       err = error("packed %s from %s is corrupt",
+                                   oid_to_hex(&oid), p->pack_name);
+               else if (!data && stream_object_signature(r, &oid) < 0)
                        err = error("packed %s from %s is corrupt",
                                    oid_to_hex(&oid), p->pack_name);
                else if (fn) {
diff --git a/path.h b/path.h
index b68691a86b80417a82e5124aef37b9c8a9a02c95..0a59c85a62eda03acd290fa564b7e197288e26ad 100644 (file)
--- a/path.h
+++ b/path.h
@@ -169,20 +169,6 @@ void report_linked_checkout_garbage(void);
                return r->cached_paths.var; \
        }
 
-struct path_cache {
-       const char *squash_msg;
-       const char *merge_msg;
-       const char *merge_rr;
-       const char *merge_mode;
-       const char *merge_head;
-       const char *merge_autostash;
-       const char *auto_merge;
-       const char *fetch_head;
-       const char *shallow;
-};
-
-#define PATH_CACHE_INIT { 0 }
-
 const char *git_path_squash_msg(struct repository *r);
 const char *git_path_merge_msg(struct repository *r);
 const char *git_path_merge_rr(struct repository *r);
index 30a4de5c2d8a1447dc23d6b5d61278df0f796bbb..b72eb9fdbee8d8d7473c973973e88eb09e15f723 100644 (file)
@@ -40,6 +40,7 @@ static int read_patches(const char *range, struct string_list *list,
        char *line, *current_filename = NULL;
        ssize_t len;
        size_t size;
+       int ret = -1;
 
        strvec_pushl(&cp.args, "log", "--no-color", "-p", "--no-merges",
                     "--reverse", "--date-order", "--decorate=no",
@@ -68,10 +69,10 @@ static int read_patches(const char *range, struct string_list *list,
        if (strbuf_read(&contents, cp.out, 0) < 0) {
                error_errno(_("could not read `log` output"));
                finish_command(&cp);
-               return -1;
+               goto cleanup;
        }
        if (finish_command(&cp))
-               return -1;
+               goto cleanup;
 
        line = contents.buf;
        size = contents.len;
@@ -95,12 +96,9 @@ static int read_patches(const char *range, struct string_list *list,
                        CALLOC_ARRAY(util, 1);
                        if (get_oid(p, &util->oid)) {
                                error(_("could not parse commit '%s'"), p);
-                               free(util);
-                               free(current_filename);
+                               FREE_AND_NULL(util);
                                string_list_clear(list, 1);
-                               strbuf_release(&buf);
-                               strbuf_release(&contents);
-                               return -1;
+                               goto cleanup;
                        }
                        util->matching = -1;
                        in_header = 1;
@@ -111,11 +109,8 @@ static int read_patches(const char *range, struct string_list *list,
                        error(_("could not parse first line of `log` output: "
                                "did not start with 'commit ': '%s'"),
                              line);
-                       free(current_filename);
                        string_list_clear(list, 1);
-                       strbuf_release(&buf);
-                       strbuf_release(&contents);
-                       return -1;
+                       goto cleanup;
                }
 
                if (starts_with(line, "diff --git")) {
@@ -136,12 +131,9 @@ static int read_patches(const char *range, struct string_list *list,
                        if (len < 0) {
                                error(_("could not parse git header '%.*s'"),
                                      orig_len, line);
-                               free(util);
-                               free(current_filename);
+                               FREE_AND_NULL(util);
                                string_list_clear(list, 1);
-                               strbuf_release(&buf);
-                               strbuf_release(&contents);
-                               return -1;
+                               goto cleanup;
                        }
                        strbuf_addstr(&buf, " ## ");
                        if (patch.is_new > 0)
@@ -165,6 +157,7 @@ static int read_patches(const char *range, struct string_list *list,
                                            patch.old_mode, patch.new_mode);
 
                        strbuf_addstr(&buf, " ##");
+                       release_patch(&patch);
                } else if (in_header) {
                        if (starts_with(line, "Author: ")) {
                                strbuf_addstr(&buf, " ## Metadata ##\n");
@@ -218,6 +211,9 @@ static int read_patches(const char *range, struct string_list *list,
                strbuf_addch(&buf, '\n');
                util->diffsize++;
        }
+
+       ret = 0;
+cleanup:
        strbuf_release(&contents);
 
        if (util)
@@ -225,7 +221,7 @@ static int read_patches(const char *range, struct string_list *list,
        strbuf_release(&buf);
        free(current_filename);
 
-       return 0;
+       return ret;
 }
 
 static int patch_util_cmp(const void *dummy, const struct patch_util *a,
index 84e3d0d75ed05fbae051bb031a5e54d600def1e0..b9f4ad886eff72f75eaa45c40d8a75c6ae2d2008 100644 (file)
@@ -205,7 +205,7 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
        cp.progress = progress;
        cp.count = 0;
 
-       bitmap_git = prepare_bitmap_walk(revs, NULL, 0);
+       bitmap_git = prepare_bitmap_walk(revs, 0);
        if (bitmap_git) {
                traverse_bitmap_commit_list(bitmap_git, revs, mark_object_seen);
                free_bitmap_index(bitmap_git);
index 79b9b99ebf7d65b6663014deaadea12a039db29d..1ad56d02e1d2494ed1852f260a6e8ad8ba6192ee 100644 (file)
@@ -736,7 +736,7 @@ static struct cache_entry *create_alias_ce(struct index_state *istate,
 void set_object_name_for_intent_to_add_entry(struct cache_entry *ce)
 {
        struct object_id oid;
-       if (write_object_file("", 0, blob_type, &oid))
+       if (write_object_file("", 0, OBJ_BLOB, &oid))
                die(_("cannot create an empty blob in the object database"));
        oidcpy(&ce->oid, &oid);
 }
diff --git a/reflog.c b/reflog.c
new file mode 100644 (file)
index 0000000..47ba862
--- /dev/null
+++ b/reflog.c
@@ -0,0 +1,434 @@
+#include "cache.h"
+#include "object-store.h"
+#include "reflog.h"
+#include "refs.h"
+#include "revision.h"
+#include "worktree.h"
+
+/* Remember to update object flag allocation in object.h */
+#define INCOMPLETE     (1u<<10)
+#define STUDYING       (1u<<11)
+#define REACHABLE      (1u<<12)
+
+static int tree_is_complete(const struct object_id *oid)
+{
+       struct tree_desc desc;
+       struct name_entry entry;
+       int complete;
+       struct tree *tree;
+
+       tree = lookup_tree(the_repository, oid);
+       if (!tree)
+               return 0;
+       if (tree->object.flags & SEEN)
+               return 1;
+       if (tree->object.flags & INCOMPLETE)
+               return 0;
+
+       if (!tree->buffer) {
+               enum object_type type;
+               unsigned long size;
+               void *data = read_object_file(oid, &type, &size);
+               if (!data) {
+                       tree->object.flags |= INCOMPLETE;
+                       return 0;
+               }
+               tree->buffer = data;
+               tree->size = size;
+       }
+       init_tree_desc(&desc, tree->buffer, tree->size);
+       complete = 1;
+       while (tree_entry(&desc, &entry)) {
+               if (!has_object_file(&entry.oid) ||
+                   (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) {
+                       tree->object.flags |= INCOMPLETE;
+                       complete = 0;
+               }
+       }
+       free_tree_buffer(tree);
+
+       if (complete)
+               tree->object.flags |= SEEN;
+       return complete;
+}
+
+static int commit_is_complete(struct commit *commit)
+{
+       struct object_array study;
+       struct object_array found;
+       int is_incomplete = 0;
+       int i;
+
+       /* early return */
+       if (commit->object.flags & SEEN)
+               return 1;
+       if (commit->object.flags & INCOMPLETE)
+               return 0;
+       /*
+        * Find all commits that are reachable and are not marked as
+        * SEEN.  Then make sure the trees and blobs contained are
+        * complete.  After that, mark these commits also as SEEN.
+        * If some of the objects that are needed to complete this
+        * commit are missing, mark this commit as INCOMPLETE.
+        */
+       memset(&study, 0, sizeof(study));
+       memset(&found, 0, sizeof(found));
+       add_object_array(&commit->object, NULL, &study);
+       add_object_array(&commit->object, NULL, &found);
+       commit->object.flags |= STUDYING;
+       while (study.nr) {
+               struct commit *c;
+               struct commit_list *parent;
+
+               c = (struct commit *)object_array_pop(&study);
+               if (!c->object.parsed && !parse_object(the_repository, &c->object.oid))
+                       c->object.flags |= INCOMPLETE;
+
+               if (c->object.flags & INCOMPLETE) {
+                       is_incomplete = 1;
+                       break;
+               }
+               else if (c->object.flags & SEEN)
+                       continue;
+               for (parent = c->parents; parent; parent = parent->next) {
+                       struct commit *p = parent->item;
+                       if (p->object.flags & STUDYING)
+                               continue;
+                       p->object.flags |= STUDYING;
+                       add_object_array(&p->object, NULL, &study);
+                       add_object_array(&p->object, NULL, &found);
+               }
+       }
+       if (!is_incomplete) {
+               /*
+                * make sure all commits in "found" array have all the
+                * necessary objects.
+                */
+               for (i = 0; i < found.nr; i++) {
+                       struct commit *c =
+                               (struct commit *)found.objects[i].item;
+                       if (!tree_is_complete(get_commit_tree_oid(c))) {
+                               is_incomplete = 1;
+                               c->object.flags |= INCOMPLETE;
+                       }
+               }
+               if (!is_incomplete) {
+                       /* mark all found commits as complete, iow SEEN */
+                       for (i = 0; i < found.nr; i++)
+                               found.objects[i].item->flags |= SEEN;
+               }
+       }
+       /* clear flags from the objects we traversed */
+       for (i = 0; i < found.nr; i++)
+               found.objects[i].item->flags &= ~STUDYING;
+       if (is_incomplete)
+               commit->object.flags |= INCOMPLETE;
+       else {
+               /*
+                * If we come here, we have (1) traversed the ancestry chain
+                * from the "commit" until we reach SEEN commits (which are
+                * known to be complete), and (2) made sure that the commits
+                * encountered during the above traversal refer to trees that
+                * are complete.  Which means that we know *all* the commits
+                * we have seen during this process are complete.
+                */
+               for (i = 0; i < found.nr; i++)
+                       found.objects[i].item->flags |= SEEN;
+       }
+       /* free object arrays */
+       object_array_clear(&study);
+       object_array_clear(&found);
+       return !is_incomplete;
+}
+
+static int keep_entry(struct commit **it, struct object_id *oid)
+{
+       struct commit *commit;
+
+       if (is_null_oid(oid))
+               return 1;
+       commit = lookup_commit_reference_gently(the_repository, oid, 1);
+       if (!commit)
+               return 0;
+
+       /*
+        * Make sure everything in this commit exists.
+        *
+        * We have walked all the objects reachable from the refs
+        * and cache earlier.  The commits reachable by this commit
+        * must meet SEEN commits -- and then we should mark them as
+        * SEEN as well.
+        */
+       if (!commit_is_complete(commit))
+               return 0;
+       *it = commit;
+       return 1;
+}
+
+/*
+ * Starting from commits in the cb->mark_list, mark commits that are
+ * reachable from them.  Stop the traversal at commits older than
+ * the expire_limit and queue them back, so that the caller can call
+ * us again to restart the traversal with longer expire_limit.
+ */
+static void mark_reachable(struct expire_reflog_policy_cb *cb)
+{
+       struct commit_list *pending;
+       timestamp_t expire_limit = cb->mark_limit;
+       struct commit_list *leftover = NULL;
+
+       for (pending = cb->mark_list; pending; pending = pending->next)
+               pending->item->object.flags &= ~REACHABLE;
+
+       pending = cb->mark_list;
+       while (pending) {
+               struct commit_list *parent;
+               struct commit *commit = pop_commit(&pending);
+               if (commit->object.flags & REACHABLE)
+                       continue;
+               if (parse_commit(commit))
+                       continue;
+               commit->object.flags |= REACHABLE;
+               if (commit->date < expire_limit) {
+                       commit_list_insert(commit, &leftover);
+                       continue;
+               }
+               commit->object.flags |= REACHABLE;
+               parent = commit->parents;
+               while (parent) {
+                       commit = parent->item;
+                       parent = parent->next;
+                       if (commit->object.flags & REACHABLE)
+                               continue;
+                       commit_list_insert(commit, &pending);
+               }
+       }
+       cb->mark_list = leftover;
+}
+
+static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit, struct object_id *oid)
+{
+       /*
+        * We may or may not have the commit yet - if not, look it
+        * up using the supplied sha1.
+        */
+       if (!commit) {
+               if (is_null_oid(oid))
+                       return 0;
+
+               commit = lookup_commit_reference_gently(the_repository, oid,
+                                                       1);
+
+               /* Not a commit -- keep it */
+               if (!commit)
+                       return 0;
+       }
+
+       /* Reachable from the current ref?  Don't prune. */
+       if (commit->object.flags & REACHABLE)
+               return 0;
+
+       if (cb->mark_list && cb->mark_limit) {
+               cb->mark_limit = 0; /* dig down to the root */
+               mark_reachable(cb);
+       }
+
+       return !(commit->object.flags & REACHABLE);
+}
+
+/*
+ * Return true iff the specified reflog entry should be expired.
+ */
+int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
+                                   const char *email, timestamp_t timestamp, int tz,
+                                   const char *message, void *cb_data)
+{
+       struct expire_reflog_policy_cb *cb = cb_data;
+       struct commit *old_commit, *new_commit;
+
+       if (timestamp < cb->cmd.expire_total)
+               return 1;
+
+       old_commit = new_commit = NULL;
+       if (cb->cmd.stalefix &&
+           (!keep_entry(&old_commit, ooid) || !keep_entry(&new_commit, noid)))
+               return 1;
+
+       if (timestamp < cb->cmd.expire_unreachable) {
+               switch (cb->unreachable_expire_kind) {
+               case UE_ALWAYS:
+                       return 1;
+               case UE_NORMAL:
+               case UE_HEAD:
+                       if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid))
+                               return 1;
+                       break;
+               }
+       }
+
+       if (cb->cmd.recno && --(cb->cmd.recno) == 0)
+               return 1;
+
+       return 0;
+}
+
+int should_expire_reflog_ent_verbose(struct object_id *ooid,
+                                           struct object_id *noid,
+                                           const char *email,
+                                           timestamp_t timestamp, int tz,
+                                           const char *message, void *cb_data)
+{
+       struct expire_reflog_policy_cb *cb = cb_data;
+       int expire;
+
+       expire = should_expire_reflog_ent(ooid, noid, email, timestamp, tz,
+                                         message, cb);
+
+       if (!expire)
+               printf("keep %s", message);
+       else if (cb->dry_run)
+               printf("would prune %s", message);
+       else
+               printf("prune %s", message);
+
+       return expire;
+}
+
+static int push_tip_to_list(const char *refname, const struct object_id *oid,
+                           int flags, void *cb_data)
+{
+       struct commit_list **list = cb_data;
+       struct commit *tip_commit;
+       if (flags & REF_ISSYMREF)
+               return 0;
+       tip_commit = lookup_commit_reference_gently(the_repository, oid, 1);
+       if (!tip_commit)
+               return 0;
+       commit_list_insert(tip_commit, list);
+       return 0;
+}
+
+static int is_head(const char *refname)
+{
+       switch (ref_type(refname)) {
+       case REF_TYPE_OTHER_PSEUDOREF:
+       case REF_TYPE_MAIN_PSEUDOREF:
+               if (parse_worktree_ref(refname, NULL, NULL, &refname))
+                       BUG("not a worktree ref: %s", refname);
+               break;
+       default:
+               break;
+       }
+       return !strcmp(refname, "HEAD");
+}
+
+void reflog_expiry_prepare(const char *refname,
+                                 const struct object_id *oid,
+                                 void *cb_data)
+{
+       struct expire_reflog_policy_cb *cb = cb_data;
+       struct commit_list *elem;
+       struct commit *commit = NULL;
+
+       if (!cb->cmd.expire_unreachable || is_head(refname)) {
+               cb->unreachable_expire_kind = UE_HEAD;
+       } else {
+               commit = lookup_commit(the_repository, oid);
+               if (commit && is_null_oid(&commit->object.oid))
+                       commit = NULL;
+               cb->unreachable_expire_kind = commit ? UE_NORMAL : UE_ALWAYS;
+       }
+
+       if (cb->cmd.expire_unreachable <= cb->cmd.expire_total)
+               cb->unreachable_expire_kind = UE_ALWAYS;
+
+       switch (cb->unreachable_expire_kind) {
+       case UE_ALWAYS:
+               return;
+       case UE_HEAD:
+               for_each_ref(push_tip_to_list, &cb->tips);
+               for (elem = cb->tips; elem; elem = elem->next)
+                       commit_list_insert(elem->item, &cb->mark_list);
+               break;
+       case UE_NORMAL:
+               commit_list_insert(commit, &cb->mark_list);
+               /* For reflog_expiry_cleanup() below */
+               cb->tip_commit = commit;
+       }
+       cb->mark_limit = cb->cmd.expire_total;
+       mark_reachable(cb);
+}
+
+void reflog_expiry_cleanup(void *cb_data)
+{
+       struct expire_reflog_policy_cb *cb = cb_data;
+       struct commit_list *elem;
+
+       switch (cb->unreachable_expire_kind) {
+       case UE_ALWAYS:
+               return;
+       case UE_HEAD:
+               for (elem = cb->tips; elem; elem = elem->next)
+                       clear_commit_marks(elem->item, REACHABLE);
+               free_commit_list(cb->tips);
+               break;
+       case UE_NORMAL:
+               clear_commit_marks(cb->tip_commit, REACHABLE);
+               break;
+       }
+}
+
+int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
+               const char *email, timestamp_t timestamp, int tz,
+               const char *message, void *cb_data)
+{
+       struct cmd_reflog_expire_cb *cb = cb_data;
+       if (!cb->expire_total || timestamp < cb->expire_total)
+               cb->recno++;
+       return 0;
+}
+
+int reflog_delete(const char *rev, enum expire_reflog_flags flags, int verbose)
+{
+       struct cmd_reflog_expire_cb cmd = { 0 };
+       int status = 0;
+       reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
+       const char *spec = strstr(rev, "@{");
+       char *ep, *ref;
+       int recno;
+       struct expire_reflog_policy_cb cb = {
+               .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
+       };
+
+       if (verbose)
+               should_prune_fn = should_expire_reflog_ent_verbose;
+
+       if (!spec)
+               return error(_("not a reflog: %s"), rev);
+
+       if (!dwim_log(rev, spec - rev, NULL, &ref)) {
+               status |= error(_("no reflog for '%s'"), rev);
+               goto cleanup;
+       }
+
+       recno = strtoul(spec + 2, &ep, 10);
+       if (*ep == '}') {
+               cmd.recno = -recno;
+               for_each_reflog_ent(ref, count_reflog_ent, &cmd);
+       } else {
+               cmd.expire_total = approxidate(spec + 2);
+               for_each_reflog_ent(ref, count_reflog_ent, &cmd);
+               cmd.expire_total = 0;
+       }
+
+       cb.cmd = cmd;
+       status |= reflog_expire(ref, flags,
+                               reflog_expiry_prepare,
+                               should_prune_fn,
+                               reflog_expiry_cleanup,
+                               &cb);
+
+ cleanup:
+       free(ref);
+       return status;
+}
diff --git a/reflog.h b/reflog.h
new file mode 100644 (file)
index 0000000..d2906fb
--- /dev/null
+++ b/reflog.h
@@ -0,0 +1,43 @@
+#ifndef REFLOG_H
+#define REFLOG_H
+#include "refs.h"
+
+struct cmd_reflog_expire_cb {
+       int stalefix;
+       int explicit_expiry;
+       timestamp_t expire_total;
+       timestamp_t expire_unreachable;
+       int recno;
+};
+
+struct expire_reflog_policy_cb {
+       enum {
+               UE_NORMAL,
+               UE_ALWAYS,
+               UE_HEAD
+       } unreachable_expire_kind;
+       struct commit_list *mark_list;
+       unsigned long mark_limit;
+       struct cmd_reflog_expire_cb cmd;
+       struct commit *tip_commit;
+       struct commit_list *tips;
+       unsigned int dry_run:1;
+};
+
+int reflog_delete(const char *rev, enum expire_reflog_flags flags,
+                 int verbose);
+void reflog_expiry_cleanup(void *cb_data);
+void reflog_expiry_prepare(const char *refname, const struct object_id *oid,
+                          void *cb_data);
+int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
+                            const char *email, timestamp_t timestamp, int tz,
+                            const char *message, void *cb_data);
+int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
+                    const char *email, timestamp_t timestamp, int tz,
+                    const char *message, void *cb_data);
+int should_expire_reflog_ent_verbose(struct object_id *ooid,
+                                    struct object_id *noid,
+                                    const char *email,
+                                    timestamp_t timestamp, int tz,
+                                    const char *message, void *cb_data);
+#endif /* REFLOG_H */
diff --git a/refs.c b/refs.c
index 183e9ce3a21176f08356f74deb00dbabeac42fc7..0b79bdd7c37d8a3f8da5f096b69a5e672cb5a60f 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1673,6 +1673,23 @@ int refs_read_raw_ref(struct ref_store *ref_store, const char *refname,
                                           type, failure_errno);
 }
 
+int refs_read_symbolic_ref(struct ref_store *ref_store, const char *refname,
+                          struct strbuf *referent)
+{
+       struct object_id oid;
+       int ret, failure_errno = 0;
+       unsigned int type = 0;
+
+       if (ref_store->be->read_symbolic_ref)
+               return ref_store->be->read_symbolic_ref(ref_store, refname, referent);
+
+       ret = refs_read_raw_ref(ref_store, refname, &oid, referent, &type, &failure_errno);
+       if (ret || !(type & REF_ISSYMREF))
+               return -1;
+
+       return 0;
+}
+
 const char *refs_resolve_ref_unsafe(struct ref_store *refs,
                                    const char *refname,
                                    int resolve_flags,
@@ -2418,6 +2435,22 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,
        return refs->be->initial_transaction_commit(refs, transaction, err);
 }
 
+void ref_transaction_for_each_queued_update(struct ref_transaction *transaction,
+                                           ref_transaction_for_each_queued_update_fn cb,
+                                           void *cb_data)
+{
+       int i;
+
+       for (i = 0; i < transaction->nr; i++) {
+               struct ref_update *update = transaction->updates[i];
+
+               cb(update->refname,
+                  (update->flags & REF_HAVE_OLD) ? &update->old_oid : NULL,
+                  (update->flags & REF_HAVE_NEW) ? &update->new_oid : NULL,
+                  cb_data);
+       }
+}
+
 int refs_delete_refs(struct ref_store *refs, const char *logmsg,
                     struct string_list *refnames, unsigned int flags)
 {
diff --git a/refs.h b/refs.h
index ff859d595136cfe605b3b3c78daebc512d39e6a6..23479c7ee09b9c5b73d8360a2107d71d03257bae 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -82,6 +82,9 @@ int read_ref_full(const char *refname, int resolve_flags,
                  struct object_id *oid, int *flags);
 int read_ref(const char *refname, struct object_id *oid);
 
+int refs_read_symbolic_ref(struct ref_store *ref_store, const char *refname,
+                          struct strbuf *referent);
+
 /*
  * Return 0 if a reference named refname could be created without
  * conflicting with the name of an existing reference. Otherwise,
@@ -776,6 +779,20 @@ int ref_transaction_abort(struct ref_transaction *transaction,
 int initial_ref_transaction_commit(struct ref_transaction *transaction,
                                   struct strbuf *err);
 
+/*
+ * Execute the given callback function for each of the reference updates which
+ * have been queued in the given transaction. `old_oid` and `new_oid` may be
+ * `NULL` pointers depending on whether the update has these object IDs set or
+ * not.
+ */
+typedef void ref_transaction_for_each_queued_update_fn(const char *refname,
+                                                      const struct object_id *old_oid,
+                                                      const struct object_id *new_oid,
+                                                      void *cb_data);
+void ref_transaction_for_each_queued_update(struct ref_transaction *transaction,
+                                           ref_transaction_for_each_queued_update_fn cb,
+                                           void *cb_data);
+
 /*
  * Free `*transaction` and all associated data.
  */
index 2b0771ca53b7854c46ab231b22ecf8be919ca19a..c590d377200c637687797f60e16b71f44e3d7a30 100644 (file)
@@ -435,6 +435,7 @@ struct ref_storage_be refs_be_debug = {
 
        debug_ref_iterator_begin,
        debug_read_raw_ref,
+       NULL,
 
        debug_reflog_iterator_begin,
        debug_for_each_reflog_ent,
index f59589d6cce4594e323ce12fdcde98f2dccd95ee..0457ecdb42dac658673519779f55f3d08ef50beb 100644 (file)
@@ -338,9 +338,9 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
        return refs->loose;
 }
 
-static int files_read_raw_ref(struct ref_store *ref_store, const char *refname,
-                             struct object_id *oid, struct strbuf *referent,
-                             unsigned int *type, int *failure_errno)
+static int read_ref_internal(struct ref_store *ref_store, const char *refname,
+                            struct object_id *oid, struct strbuf *referent,
+                            unsigned int *type, int *failure_errno, int skip_packed_refs)
 {
        struct files_ref_store *refs =
                files_downcast(ref_store, REF_STORE_READ, "read_raw_ref");
@@ -381,7 +381,7 @@ stat_ref:
        if (lstat(path, &st) < 0) {
                int ignore_errno;
                myerr = errno;
-               if (myerr != ENOENT)
+               if (myerr != ENOENT || skip_packed_refs)
                        goto out;
                if (refs_read_raw_ref(refs->packed_ref_store, refname, oid,
                                      referent, type, &ignore_errno)) {
@@ -425,7 +425,8 @@ stat_ref:
                 * ref is supposed to be, there could still be a
                 * packed ref:
                 */
-               if (refs_read_raw_ref(refs->packed_ref_store, refname, oid,
+               if (skip_packed_refs ||
+                   refs_read_raw_ref(refs->packed_ref_store, refname, oid,
                                      referent, type, &ignore_errno)) {
                        myerr = EISDIR;
                        goto out;
@@ -470,6 +471,27 @@ out:
        return ret;
 }
 
+static int files_read_raw_ref(struct ref_store *ref_store, const char *refname,
+                             struct object_id *oid, struct strbuf *referent,
+                             unsigned int *type, int *failure_errno)
+{
+       return read_ref_internal(ref_store, refname, oid, referent, type, failure_errno, 0);
+}
+
+static int files_read_symbolic_ref(struct ref_store *ref_store, const char *refname,
+                                  struct strbuf *referent)
+{
+       struct object_id oid;
+       int failure_errno, ret;
+       unsigned int type;
+
+       ret = read_ref_internal(ref_store, refname, &oid, referent, &type, &failure_errno, 1);
+       if (ret)
+               return ret;
+
+       return !(type & REF_ISSYMREF);
+}
+
 int parse_loose_ref_contents(const char *buf, struct object_id *oid,
                             struct strbuf *referent, unsigned int *type,
                             int *failure_errno)
@@ -3286,6 +3308,7 @@ struct ref_storage_be refs_be_files = {
 
        files_ref_iterator_begin,
        files_read_raw_ref,
+       files_read_symbolic_ref,
 
        files_reflog_iterator_begin,
        files_for_each_reflog_ent,
index 27dd8c3922b7039323bbfcfdf7f3b77c65ef1681..f56e2cc635b1824f12537d1a99dbf148909cd9ef 100644 (file)
@@ -1684,6 +1684,7 @@ struct ref_storage_be refs_be_packed = {
 
        packed_ref_iterator_begin,
        packed_read_raw_ref,
+       NULL,
 
        packed_reflog_iterator_begin,
        packed_for_each_reflog_ent,
index 6e15db3ca4ed4223400a42a9f5b4239180ef2b77..001ef1583540b002241dfd1d351f72791d39cc9c 100644 (file)
@@ -649,6 +649,21 @@ typedef int read_raw_ref_fn(struct ref_store *ref_store, const char *refname,
                            struct object_id *oid, struct strbuf *referent,
                            unsigned int *type, int *failure_errno);
 
+/*
+ * Read a symbolic reference from the specified reference store. This function
+ * is optional: if not implemented by a backend, then `read_raw_ref_fn` is used
+ * to read the symbolcic reference instead. It is intended to be implemented
+ * only in case the backend can optimize the reading of symbolic references.
+ *
+ * Return 0 on success, or -1 on failure. `referent` will be set to the target
+ * of the symbolic reference on success. This function explicitly does not
+ * distinguish between error cases and the reference not being a symbolic
+ * reference to allow backends to optimize this operation in case symbolic and
+ * non-symbolic references are treated differently.
+ */
+typedef int read_symbolic_ref_fn(struct ref_store *ref_store, const char *refname,
+                                struct strbuf *referent);
+
 struct ref_storage_be {
        struct ref_storage_be *next;
        const char *name;
@@ -668,6 +683,7 @@ struct ref_storage_be {
 
        ref_iterator_begin_fn *iterator_begin;
        read_raw_ref_fn *read_raw_ref;
+       read_symbolic_ref_fn *read_symbolic_ref;
 
        reflog_iterator_begin_fn *reflog_iterator_begin;
        for_each_reflog_ent_fn *for_each_reflog_ent;
index 2170748c5e9bb413f6a8ed2cef130686f47ecb58..34d4d073692f9e913d84715d987c8e1c35a7fe8a 100644 (file)
@@ -88,8 +88,9 @@ uint8_t block_writer_type(struct block_writer *bw)
        return bw->buf[bw->header_off];
 }
 
-/* adds the reftable_record to the block. Returns -1 if it does not fit, 0 on
-   success */
+/* Adds the reftable_record to the block. Returns -1 if it does not fit, 0 on
+   success. Returns REFTABLE_API_ERROR if attempting to write a record with
+   empty key. */
 int block_writer_add(struct block_writer *w, struct reftable_record *rec)
 {
        struct strbuf empty = STRBUF_INIT;
@@ -105,8 +106,14 @@ int block_writer_add(struct block_writer *w, struct reftable_record *rec)
        int is_restart = 0;
        struct strbuf key = STRBUF_INIT;
        int n = 0;
+       int err = -1;
 
        reftable_record_key(rec, &key);
+       if (!key.len) {
+               err = REFTABLE_API_ERROR;
+               goto done;
+       }
+
        n = reftable_encode_key(&is_restart, out, last, key,
                                reftable_record_val_type(rec));
        if (n < 0)
@@ -118,16 +125,11 @@ int block_writer_add(struct block_writer *w, struct reftable_record *rec)
                goto done;
        string_view_consume(&out, n);
 
-       if (block_writer_register_restart(w, start.len - out.len, is_restart,
-                                         &key) < 0)
-               goto done;
-
-       strbuf_release(&key);
-       return 0;
-
+       err = block_writer_register_restart(w, start.len - out.len, is_restart,
+                                           &key);
 done:
        strbuf_release(&key);
-       return -1;
+       return err;
 }
 
 int block_writer_finish(struct block_writer *w)
@@ -332,6 +334,9 @@ int block_iter_next(struct block_iter *it, struct reftable_record *rec)
        if (n < 0)
                return -1;
 
+       if (!key.len)
+               return REFTABLE_FORMAT_ERROR;
+
        string_view_consume(&in, n);
        n = reftable_record_decode(rec, key, extra, in, it->br->hash_size);
        if (n < 0)
@@ -358,6 +363,8 @@ int block_reader_first_key(struct block_reader *br, struct strbuf *key)
        int n = reftable_decode_key(key, &extra, empty, in);
        if (n < 0)
                return n;
+       if (!key->len)
+               return REFTABLE_FORMAT_ERROR;
 
        return 0;
 }
index fa2ee092ec0bf52546cfbf5f4d3158656e553629..cb88af4a5639258945f569ede758e31809b62ead 100644 (file)
@@ -42,6 +42,11 @@ static void test_block_read_write(void)
        block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
                          header_off, hash_size(GIT_SHA1_FORMAT_ID));
 
+       rec.u.ref.refname = "";
+       rec.u.ref.value_type = REFTABLE_REF_DELETION;
+       n = block_writer_add(&bw, &rec);
+       EXPECT(n == REFTABLE_API_ERROR);
+
        for (i = 0; i < N; i++) {
                char name[100];
                uint8_t hash[GIT_SHA1_RAWSZ];
index 00906e7a2de218085eaec3c8488365cb2da0a0a8..54b4025105cfd6dc53821d366a1dd20bc8d11004 100644 (file)
@@ -155,6 +155,11 @@ static int parse_footer(struct reftable_reader *r, uint8_t *footer,
        r->log_offsets.is_present = (first_block_typ == BLOCK_TYPE_LOG ||
                                     r->log_offsets.offset > 0);
        r->obj_offsets.is_present = r->obj_offsets.offset > 0;
+       if (r->obj_offsets.is_present && !r->object_id_len) {
+               err = REFTABLE_FORMAT_ERROR;
+               goto done;
+       }
+
        err = 0;
 done:
        return err;
index 605ba0f9fd4f57d0e801d3fc69503ccdc32b63ed..469ab79a5adf3dfaba0160e523bdcece5b9875e0 100644 (file)
@@ -100,7 +100,7 @@ static void write_table(char ***names, struct strbuf *buf, int N,
        n = reftable_writer_close(w);
        EXPECT(n == 0);
 
-       stats = writer_stats(w);
+       stats = reftable_writer_stats(w);
        for (i = 0; i < stats->ref_stats.blocks; i++) {
                int off = i * opts.block_size;
                if (off == 0) {
@@ -239,7 +239,7 @@ static void test_log_write_read(void)
        n = reftable_writer_close(w);
        EXPECT(n == 0);
 
-       stats = writer_stats(w);
+       stats = reftable_writer_stats(w);
        EXPECT(stats->log_stats.blocks > 0);
        reftable_writer_free(w);
        w = NULL;
@@ -330,7 +330,7 @@ static void test_log_zlib_corruption(void)
        n = reftable_writer_close(w);
        EXPECT(n == 0);
 
-       stats = writer_stats(w);
+       stats = reftable_writer_stats(w);
        EXPECT(stats->log_stats.blocks > 0);
        reftable_writer_free(w);
        w = NULL;
@@ -667,6 +667,102 @@ static void test_write_empty_table(void)
        strbuf_release(&buf);
 }
 
+static void test_write_object_id_min_length(void)
+{
+       struct reftable_write_options opts = {
+               .block_size = 75,
+       };
+       struct strbuf buf = STRBUF_INIT;
+       struct reftable_writer *w =
+               reftable_new_writer(&strbuf_add_void, &buf, &opts);
+       uint8_t hash[GIT_SHA1_RAWSZ] = {42};
+       struct reftable_ref_record ref = {
+               .update_index = 1,
+               .value_type = REFTABLE_REF_VAL1,
+               .value.val1 = hash,
+       };
+       int err;
+       int i;
+
+       reftable_writer_set_limits(w, 1, 1);
+
+       /* Write the same hash in many refs. If there is only 1 hash, the
+        * disambiguating prefix is length 0 */
+       for (i = 0; i < 256; i++) {
+               char name[256];
+               snprintf(name, sizeof(name), "ref%05d", i);
+               ref.refname = name;
+               err = reftable_writer_add_ref(w, &ref);
+               EXPECT_ERR(err);
+       }
+
+       err = reftable_writer_close(w);
+       EXPECT_ERR(err);
+       EXPECT(reftable_writer_stats(w)->object_id_len == 2);
+       reftable_writer_free(w);
+       strbuf_release(&buf);
+}
+
+static void test_write_object_id_length(void)
+{
+       struct reftable_write_options opts = {
+               .block_size = 75,
+       };
+       struct strbuf buf = STRBUF_INIT;
+       struct reftable_writer *w =
+               reftable_new_writer(&strbuf_add_void, &buf, &opts);
+       uint8_t hash[GIT_SHA1_RAWSZ] = {42};
+       struct reftable_ref_record ref = {
+               .update_index = 1,
+               .value_type = REFTABLE_REF_VAL1,
+               .value.val1 = hash,
+       };
+       int err;
+       int i;
+
+       reftable_writer_set_limits(w, 1, 1);
+
+       /* Write the same hash in many refs. If there is only 1 hash, the
+        * disambiguating prefix is length 0 */
+       for (i = 0; i < 256; i++) {
+               char name[256];
+               snprintf(name, sizeof(name), "ref%05d", i);
+               ref.refname = name;
+               ref.value.val1[15] = i;
+               err = reftable_writer_add_ref(w, &ref);
+               EXPECT_ERR(err);
+       }
+
+       err = reftable_writer_close(w);
+       EXPECT_ERR(err);
+       EXPECT(reftable_writer_stats(w)->object_id_len == 16);
+       reftable_writer_free(w);
+       strbuf_release(&buf);
+}
+
+static void test_write_empty_key(void)
+{
+       struct reftable_write_options opts = { 0 };
+       struct strbuf buf = STRBUF_INIT;
+       struct reftable_writer *w =
+               reftable_new_writer(&strbuf_add_void, &buf, &opts);
+       struct reftable_ref_record ref = {
+               .refname = "",
+               .update_index = 1,
+               .value_type = REFTABLE_REF_DELETION,
+       };
+       int err;
+
+       reftable_writer_set_limits(w, 1, 1);
+       err = reftable_writer_add_ref(w, &ref);
+       EXPECT(err == REFTABLE_API_ERROR);
+
+       err = reftable_writer_close(w);
+       EXPECT(err == REFTABLE_EMPTY_TABLE_ERROR);
+       reftable_writer_free(w);
+       strbuf_release(&buf);
+}
+
 static void test_write_key_order(void)
 {
        struct reftable_write_options opts = { 0 };
@@ -746,7 +842,10 @@ int readwrite_test_main(int argc, const char *argv[])
        RUN_TEST(test_table_read_write_seek_index);
        RUN_TEST(test_table_refs_for_no_index);
        RUN_TEST(test_table_refs_for_obj_index);
+       RUN_TEST(test_write_empty_key);
        RUN_TEST(test_write_empty_table);
        RUN_TEST(test_log_overflow);
+       RUN_TEST(test_write_object_id_length);
+       RUN_TEST(test_write_object_id_min_length);
        return 0;
 }
index a560dc1725596d9ae7e8ec775079ceeff6012e27..db8de197f6c42a0de203fc1fbca8174f7f4d2938 100644 (file)
@@ -143,7 +143,7 @@ int reftable_writer_close(struct reftable_writer *w);
 
    This struct becomes invalid when the writer is freed.
  */
-const struct reftable_stats *writer_stats(struct reftable_writer *w);
+const struct reftable_stats *reftable_writer_stats(struct reftable_writer *w);
 
 /* reftable_writer_free deallocates memory for the writer */
 void reftable_writer_free(struct reftable_writer *w);
index 944c2329ab568aa17cb1243507cc7a352faaf8ad..6d979e245ffbddd0bdd1ca43bf3725c5683633a7 100644 (file)
@@ -240,14 +240,13 @@ static int writer_add_record(struct reftable_writer *w,
 
        writer_reinit_block_writer(w, reftable_record_type(rec));
        err = block_writer_add(w->block_writer, rec);
-       if (err < 0) {
+       if (err == -1) {
                /* we are writing into memory, so an error can only mean it
                 * doesn't fit. */
                err = REFTABLE_ENTRY_TOO_BIG_ERROR;
                goto done;
        }
 
-       err = 0;
 done:
        strbuf_release(&key);
        return err;
@@ -516,7 +515,9 @@ static void object_record_free(void *void_arg, void *key)
 static int writer_dump_object_index(struct reftable_writer *w)
 {
        struct write_record_arg closure = { .w = w };
-       struct common_prefix_arg common = { NULL };
+       struct common_prefix_arg common = {
+               .max = 1,               /* obj_id_len should be >= 2. */
+       };
        if (w->obj_index_tree) {
                infix_walk(w->obj_index_tree, &update_common, &common);
        }
@@ -694,7 +695,7 @@ static int writer_flush_block(struct reftable_writer *w)
        return writer_flush_nonempty_block(w);
 }
 
-const struct reftable_stats *writer_stats(struct reftable_writer *w)
+const struct reftable_stats *reftable_writer_stats(struct reftable_writer *w)
 {
        return &w->stats;
 }
index 0dabef2dd7c565f3f2c2ecd74a0ca295bd874afb..ff44f41011e8a046cdd4eaa86d4b59c6a513a559 100644 (file)
@@ -1472,11 +1472,12 @@ int cmd_main(int argc, const char **argv)
 {
        struct strbuf buf = STRBUF_INIT;
        int nongit;
+       int ret = 1;
 
        setup_git_directory_gently(&nongit);
        if (argc < 2) {
                error(_("remote-curl: usage: git remote-curl <remote> [<url>]"));
-               return 1;
+               goto cleanup;
        }
 
        options.verbosity = 1;
@@ -1508,7 +1509,7 @@ int cmd_main(int argc, const char **argv)
                if (strbuf_getline_lf(&buf, stdin) == EOF) {
                        if (ferror(stdin))
                                error(_("remote-curl: error reading command stream from git"));
-                       return 1;
+                       goto cleanup;
                }
                if (buf.len == 0)
                        break;
@@ -1556,12 +1557,15 @@ int cmd_main(int argc, const char **argv)
                                break;
                } else {
                        error(_("remote-curl: unknown command '%s' from git"), buf.buf);
-                       return 1;
+                       goto cleanup;
                }
                strbuf_reset(&buf);
        } while (1);
 
        http_cleanup();
+       ret = 0;
+cleanup:
+       strbuf_release(&buf);
 
-       return 0;
+       return ret;
 }
index c97c626eaa83908408848733d2db32cbbe097931..42a4e7106e1255af5b79d32ff7d86170827582b1 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -1945,13 +1945,9 @@ const char *branch_get_push(struct branch *branch, struct strbuf *err)
        return branch->push_tracking_ref;
 }
 
-static int ignore_symref_update(const char *refname)
+static int ignore_symref_update(const char *refname, struct strbuf *scratch)
 {
-       int flag;
-
-       if (!resolve_ref_unsafe(refname, 0, NULL, &flag))
-               return 0; /* non-existing refs are OK */
-       return (flag & REF_ISSYMREF);
+       return !refs_read_symbolic_ref(get_main_ref_store(the_repository), refname, scratch);
 }
 
 /*
@@ -1964,6 +1960,7 @@ static int ignore_symref_update(const char *refname)
 static struct ref *get_expanded_map(const struct ref *remote_refs,
                                    const struct refspec_item *refspec)
 {
+       struct strbuf scratch = STRBUF_INIT;
        const struct ref *ref;
        struct ref *ret = NULL;
        struct ref **tail = &ret;
@@ -1971,11 +1968,13 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,
        for (ref = remote_refs; ref; ref = ref->next) {
                char *expn_name = NULL;
 
+               strbuf_reset(&scratch);
+
                if (strchr(ref->name, '^'))
                        continue; /* a dereference item */
                if (match_name_with_pattern(refspec->src, ref->name,
                                            refspec->dst, &expn_name) &&
-                   !ignore_symref_update(expn_name)) {
+                   !ignore_symref_update(expn_name, &scratch)) {
                        struct ref *cpy = copy_ref(ref);
 
                        cpy->peer_ref = alloc_ref(expn_name);
@@ -1987,6 +1986,7 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,
                free(expn_name);
        }
 
+       strbuf_release(&scratch);
        return ret;
 }
 
index 34610c5a33e013169de14189274fd4ed42512a96..5d166b692c8aa8ec24bbbde90cab1279ed2bdf53 100644 (file)
@@ -240,6 +240,20 @@ out:
        return ret;
 }
 
+static void repo_clear_path_cache(struct repo_path_cache *cache)
+{
+       FREE_AND_NULL(cache->squash_msg);
+       FREE_AND_NULL(cache->squash_msg);
+       FREE_AND_NULL(cache->merge_msg);
+       FREE_AND_NULL(cache->merge_rr);
+       FREE_AND_NULL(cache->merge_mode);
+       FREE_AND_NULL(cache->merge_head);
+       FREE_AND_NULL(cache->merge_autostash);
+       FREE_AND_NULL(cache->auto_merge);
+       FREE_AND_NULL(cache->fetch_head);
+       FREE_AND_NULL(cache->shallow);
+}
+
 void repo_clear(struct repository *repo)
 {
        FREE_AND_NULL(repo->gitdir);
@@ -280,6 +294,8 @@ void repo_clear(struct repository *repo)
                remote_state_clear(repo->remote_state);
                FREE_AND_NULL(repo->remote_state);
        }
+
+       repo_clear_path_cache(&repo->cached_paths);
 }
 
 int repo_read_index(struct repository *repo)
@@ -301,6 +317,13 @@ int repo_read_index(struct repository *repo)
        if (repo->settings.command_requires_full_index)
                ensure_full_index(repo->index);
 
+       /*
+        * If sparse checkouts are in use, check whether paths with the
+        * SKIP_WORKTREE attribute are missing from the worktree; if not,
+        * clear that attribute for that path.
+        */
+       clear_skip_worktree_from_present_files(repo->index);
+
        return res;
 }
 
index ca837cb9e914aaaf85c39f323ae671ab4eb8c8cb..e29f361703d2b37dd8b8121d88a26bebe88e070b 100644 (file)
@@ -44,6 +44,18 @@ struct repo_settings {
        int core_multi_pack_index;
 };
 
+struct repo_path_cache {
+       char *squash_msg;
+       char *merge_msg;
+       char *merge_rr;
+       char *merge_mode;
+       char *merge_head;
+       char *merge_autostash;
+       char *auto_merge;
+       char *fetch_head;
+       char *shallow;
+};
+
 struct repository {
        /* Environment */
        /*
@@ -82,7 +94,7 @@ struct repository {
        /*
         * Contains path to often used file names.
         */
-       struct path_cache cached_paths;
+       struct repo_path_cache cached_paths;
 
        /*
         * Path to the repository's graft file.
index 2bb913f2f3f9575e027e498f5026b4ddd89b546f..2646b78990ea4dc56dfdefef65f48b17ca4c894a 100644 (file)
@@ -32,6 +32,7 @@
 #include "utf8.h"
 #include "bloom.h"
 #include "json-writer.h"
+#include "list-objects-filter-options.h"
 
 volatile show_early_output_fn_t show_early_output;
 
@@ -2690,6 +2691,10 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
                revs->no_walk = 0;
        } else if (!strcmp(arg, "--single-worktree")) {
                revs->single_worktree = 1;
+       } else if (skip_prefix(arg, ("--" CL_ARG__FILTER "="), &arg)) {
+               parse_list_objects_filter(&revs->filter, arg);
+       } else if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) {
+               list_objects_filter_set_no_filter(&revs->filter);
        } else {
                return 0;
        }
@@ -2892,6 +2897,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                die("cannot combine --walk-reflogs with history-limiting options");
        if (revs->rewrite_parents && revs->children.name)
                die(_("options '%s' and '%s' cannot be used together"), "--parents", "--children");
+       if (revs->filter.choice && !revs->blob_objects)
+               die(_("object filtering requires --objects"));
 
        /*
         * Limitations on the graph functionality
index b9c2421687fef8e5e1d90cdcefa163165573d7ad..5bc59c7bfe1e35efe7fb24e93f9a093baa28b658 100644 (file)
@@ -8,6 +8,7 @@
 #include "pretty.h"
 #include "diff.h"
 #include "commit-slab-decl.h"
+#include "list-objects-filter-options.h"
 
 /**
  * The revision walking API offers functions to build a list of revisions
@@ -94,6 +95,12 @@ struct rev_info {
        /* The end-points specified by the end user */
        struct rev_cmdline_info cmdline;
 
+       /*
+        * Object filter options. No filtering is specified
+        * if and only if filter.choice is zero.
+        */
+       struct list_objects_filter_options filter;
+
        /* excluding from --branches, --refs, etc. expansion */
        struct string_list *ref_excludes;
 
index 35006c0cea6e30a8636b508b9e43cc7a896b52f6..a1bb39383db9ff3319c66faa357463e0c32c3257 100644 (file)
@@ -1220,7 +1220,7 @@ static int run_prepare_commit_msg_hook(struct repository *r,
        } else {
                arg1 = "message";
        }
-       if (run_commit_hook(0, r->index_file, "prepare-commit-msg", name,
+       if (run_commit_hook(0, r->index_file, NULL, "prepare-commit-msg", name,
                            arg1, arg2, NULL))
                ret = error(_("'prepare-commit-msg' hook failed"));
 
@@ -1552,7 +1552,7 @@ static int try_to_commit(struct repository *r,
                goto out;
        }
 
-       run_commit_hook(0, r->index_file, "post-commit", NULL);
+       run_commit_hook(0, r->index_file, NULL, "post-commit", NULL);
        if (flags & AMEND_MSG)
                commit_post_rewrite(r, current_head, oid);
 
@@ -3749,7 +3749,7 @@ static int do_merge(struct repository *r,
        int run_commit_flags = 0;
        struct strbuf ref_name = STRBUF_INIT;
        struct commit *head_commit, *merge_commit, *i;
-       struct commit_list *bases, *j, *reversed = NULL;
+       struct commit_list *bases, *j;
        struct commit_list *to_merge = NULL, **tail = &to_merge;
        const char *strategy = !opts->xopts_nr &&
                (!opts->strategy ||
@@ -3984,9 +3984,7 @@ static int do_merge(struct repository *r,
                      git_path_merge_head(r), 0);
        write_message("no-ff", 5, git_path_merge_mode(r), 0);
 
-       for (j = bases; j; j = j->next)
-               commit_list_insert(j->item, &reversed);
-       free_commit_list(bases);
+       bases = reverse_commit_list(bases);
 
        repo_read_index(r);
        init_merge_options(&o, r);
@@ -4002,10 +4000,10 @@ static int do_merge(struct repository *r,
                 * update the index and working copy immediately.
                 */
                ret = merge_ort_recursive(&o,
-                                         head_commit, merge_commit, reversed,
+                                         head_commit, merge_commit, bases,
                                          &i);
        } else {
-               ret = merge_recursive(&o, head_commit, merge_commit, reversed,
+               ret = merge_recursive(&o, head_commit, merge_commit, bases,
                                      &i);
        }
        if (ret <= 0)
diff --git a/shared.mak b/shared.mak
new file mode 100644 (file)
index 0000000..4e1b62e
--- /dev/null
@@ -0,0 +1,103 @@
+### Remove GNU make implicit rules
+
+## This speeds things up since we don't need to look for and stat() a
+## "foo.c,v" every time a rule referring to "foo.c" is in play. See
+## "make -p -f/dev/null | grep ^%::'".
+%:: %,v
+%:: RCS/%,v
+%:: RCS/%
+%:: s.%
+%:: SCCS/s.%
+
+## Likewise delete default $(SUFFIXES). See:
+##
+##     info make --index-search=.SUFFIXES
+.SUFFIXES:
+
+### Flags affecting all rules
+
+# A GNU make extension since gmake 3.72 (released in late 1994) to
+# remove the target of rules if commands in those rules fail. The
+# default is to only do that if make itself receives a signal. Affects
+# all targets, see:
+#
+#    info make --index-search=.DELETE_ON_ERROR
+.DELETE_ON_ERROR:
+
+### Global variables
+
+## comma, empty, space: handy variables as these tokens are either
+## special or can be hard to spot among other Makefile syntax.
+comma := ,
+empty :=
+space := $(empty) $(empty)
+
+### Quieting
+## common
+QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
+QUIET_SUBDIR1  =
+
+ifneq ($(findstring w,$(MAKEFLAGS)),w)
+PRINT_DIR = --no-print-directory
+else # "make -w"
+NO_SUBDIR = :
+endif
+
+ifneq ($(findstring s,$(MAKEFLAGS)),s)
+ifndef V
+## common
+       QUIET_SUBDIR0  = +@subdir=
+       QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
+                        $(MAKE) $(PRINT_DIR) -C $$subdir
+
+       QUIET          = @
+       QUIET_GEN      = @echo '   ' GEN $@;
+
+       QUIET_MKDIR_P_PARENT  = @echo $(wspfx_SQ) MKDIR -p $(@D);
+
+## Used in "Makefile"
+       QUIET_CC       = @echo '   ' CC $@;
+       QUIET_AR       = @echo '   ' AR $@;
+       QUIET_LINK     = @echo '   ' LINK $@;
+       QUIET_BUILT_IN = @echo '   ' BUILTIN $@;
+       QUIET_LNCP     = @echo '   ' LN/CP $@;
+       QUIET_XGETTEXT = @echo '   ' XGETTEXT $@;
+       QUIET_MSGFMT   = @echo '   ' MSGFMT $@;
+       QUIET_GCOV     = @echo '   ' GCOV $@;
+       QUIET_SP       = @echo '   ' SP $<;
+       QUIET_HDR      = @echo '   ' HDR $(<:hcc=h);
+       QUIET_RC       = @echo '   ' RC $@;
+       QUIET_SPATCH   = @echo '   ' SPATCH $<;
+
+## Used in "Documentation/Makefile"
+       QUIET_ASCIIDOC  = @echo '   ' ASCIIDOC $@;
+       QUIET_XMLTO     = @echo '   ' XMLTO $@;
+       QUIET_DB2TEXI   = @echo '   ' DB2TEXI $@;
+       QUIET_MAKEINFO  = @echo '   ' MAKEINFO $@;
+       QUIET_DBLATEX   = @echo '   ' DBLATEX $@;
+       QUIET_XSLTPROC  = @echo '   ' XSLTPROC $@;
+       QUIET_GEN       = @echo '   ' GEN $@;
+       QUIET_STDERR    = 2> /dev/null
+
+       QUIET_LINT_GITLINK      = @echo '   ' LINT GITLINK $<;
+       QUIET_LINT_MANSEC       = @echo '   ' LINT MAN SEC $<;
+       QUIET_LINT_MANEND       = @echo '   ' LINT MAN END $<;
+
+       export V
+endif
+endif
+
+### Templates
+
+## mkdir_p_parent: lazily "mkdir -p" the path needed for a $@
+## file. Uses $(wildcard) to avoid the "mkdir -p" if it's not
+## needed.
+##
+## Is racy, but in a good way; we might redundantly (and safely)
+## "mkdir -p" when running in parallel, but won't need to exhaustively create
+## individual rules for "a" -> "prefix" -> "dir" -> "file" if given a
+## "a/prefix/dir/file". This can instead be inserted at the start of
+## the "a/prefix/dir/file" rule.
+define mkdir_p_parent_template
+$(if $(wildcard $(@D)),,$(QUIET_MKDIR_P_PARENT)$(shell mkdir -p $(@D)))
+endef
index fdbe97b976900d604334e8ad0c9a811aa1ab2160..8636af72de59f38ff9de871c34bd44f8aab75286 100644 (file)
@@ -337,6 +337,80 @@ void ensure_correct_sparsity(struct index_state *istate)
                ensure_full_index(istate);
 }
 
+static int path_found(const char *path, const char **dirname, size_t *dir_len,
+                     int *dir_found)
+{
+       struct stat st;
+       char *newdir;
+       char *tmp;
+
+       /*
+        * If dirname corresponds to a directory that doesn't exist, and this
+        * path starts with dirname, then path can't exist.
+        */
+       if (!*dir_found && !memcmp(path, *dirname, *dir_len))
+               return 0;
+
+       /*
+        * If path itself exists, return 1.
+        */
+       if (!lstat(path, &st))
+               return 1;
+
+       /*
+        * Otherwise, path does not exist so we'll return 0...but we'll first
+        * determine some info about its parent directory so we can avoid
+        * lstat calls for future cache entries.
+        */
+       newdir = strrchr(path, '/');
+       if (!newdir)
+               return 0; /* Didn't find a parent dir; just return 0 now. */
+
+       /*
+        * If path starts with directory (which we already lstat'ed and found),
+        * then no need to lstat parent directory again.
+        */
+       if (*dir_found && *dirname && memcmp(path, *dirname, *dir_len))
+               return 0;
+
+       /* Free previous dirname, and cache path's dirname */
+       *dirname = path;
+       *dir_len = newdir - path + 1;
+
+       tmp = xstrndup(path, *dir_len);
+       *dir_found = !lstat(tmp, &st);
+       free(tmp);
+
+       return 0;
+}
+
+void clear_skip_worktree_from_present_files(struct index_state *istate)
+{
+       const char *last_dirname = NULL;
+       size_t dir_len = 0;
+       int dir_found = 1;
+
+       int i;
+
+       if (!core_apply_sparse_checkout ||
+           sparse_expect_files_outside_of_patterns)
+               return;
+
+restart:
+       for (i = 0; i < istate->cache_nr; i++) {
+               struct cache_entry *ce = istate->cache[i];
+
+               if (ce_skip_worktree(ce) &&
+                   path_found(ce->name, &last_dirname, &dir_len, &dir_found)) {
+                       if (S_ISSPARSEDIR(ce->ce_mode)) {
+                               ensure_full_index(istate);
+                               goto restart;
+                       }
+                       ce->ce_flags &= ~CE_SKIP_WORKTREE;
+               }
+       }
+}
+
 /*
  * This static global helps avoid infinite recursion between
  * expand_to_path() and index_file_exists().
index 656bd835b25e06c45ea382b174812e501be6e2be..633d4fb7e318afcd69257e5d85850138da9d7348 100644 (file)
@@ -5,6 +5,7 @@ struct index_state;
 #define SPARSE_INDEX_MEMORY_ONLY (1 << 0)
 int convert_to_sparse(struct index_state *istate, int flags);
 void ensure_correct_sparsity(struct index_state *istate);
+void clear_skip_worktree_from_present_files(struct index_state *istate);
 
 /*
  * Some places in the codebase expect to search for a specific path.
index 00abeb55afdd68089676d2007fbfd248b5a45e79..dd9eb85527ab5a2f5007907b2cb3ce76ed5ccc3f 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -875,9 +875,9 @@ static void strbuf_humanise(struct strbuf *buf, off_t bytes,
                strbuf_addf(buf,
                                humanise_rate == 0 ?
                                        /* TRANSLATORS: IEC 80000-13:2008 byte */
-                                       Q_("%u byte", "%u bytes", (unsigned)bytes) :
+                                       Q_("%u byte", "%u bytes", bytes) :
                                        /* TRANSLATORS: IEC 80000-13:2008 byte/second */
-                                       Q_("%u byte/s", "%u bytes/s", (unsigned)bytes),
+                                       Q_("%u byte/s", "%u bytes/s", bytes),
                                (unsigned)bytes);
        }
 }
index 267d6e5769d9bcea057d68bb46e685d59339d05d..d5a744e1438600080b9970e3f7cdedbe7f2a3708 100644 (file)
@@ -86,7 +86,8 @@ typedef int (*compare_strings_fn)(const char *, const char *);
  */
 struct string_list {
        struct string_list_item *items;
-       unsigned int nr, alloc;
+       size_t nr;
+       size_t alloc;
        unsigned int strdup_strings:1;
        compare_strings_fn cmp; /* NULL uses strcmp() */
 };
index 46cd5fc5273dc0bcb907ab49ce8c97c4783053be..056ce55dcc92c923ce560ef61a0b2888d799c450 100644 (file)
@@ -1,3 +1,6 @@
+# Import tree-wide shared Makefile behavior and libraries
+include ../shared.mak
+
 # Run tests
 #
 # Copyright (c) 2005 Junio C Hamano
index 75927b2c81d28de1b3f16de0ef4a9d55aad5e668..98b73bb8f25aabea7384cde0fb639cff16cf631e 100644 (file)
@@ -3,6 +3,7 @@
 #include "commit-graph.h"
 #include "repository.h"
 #include "object-store.h"
+#include "bloom.h"
 
 int cmd__read_graph(int argc, const char **argv)
 {
@@ -45,6 +46,18 @@ int cmd__read_graph(int argc, const char **argv)
                printf(" bloom_data");
        printf("\n");
 
+       printf("options:");
+       if (graph->bloom_filter_settings)
+               printf(" bloom(%"PRIu32",%"PRIu32",%"PRIu32")",
+                      graph->bloom_filter_settings->hash_version,
+                      graph->bloom_filter_settings->bits_per_entry,
+                      graph->bloom_filter_settings->num_hashes);
+       if (graph->read_generation_data)
+               printf(" read_generation_data");
+       if (graph->topo_levels)
+               printf(" topo_levels");
+       printf("\n");
+
        UNLEAK(graph);
 
        return 0;
index 8f370cd89f17609b1245ea50440cda971d6a7458..f3b90aa834a463e57485dc5bc1ec0b377d29626e 100644 (file)
@@ -19,7 +19,6 @@
 #include "thread-utils.h"
 #include "wildmatch.h"
 #include "gettext.h"
-#include "parse-options.h"
 
 static int number_callbacks;
 static int parallel_next(struct child_process *cp,
@@ -180,15 +179,16 @@ static int testsuite(int argc, const char **argv)
        if (max_jobs > suite.tests.nr)
                max_jobs = suite.tests.nr;
 
-       fprintf(stderr, "Running %d tests (%d at a time)\n",
-               suite.tests.nr, max_jobs);
+       fprintf(stderr, "Running %"PRIuMAX" tests (%d at a time)\n",
+               (uintmax_t)suite.tests.nr, max_jobs);
 
        ret = run_processes_parallel(max_jobs, next_test, test_failed,
                                     test_finished, &suite);
 
        if (suite.failed.nr > 0) {
                ret = 1;
-               fprintf(stderr, "%d tests failed:\n\n", suite.failed.nr);
+               fprintf(stderr, "%"PRIuMAX" tests failed:\n\n",
+                       (uintmax_t)suite.failed.nr);
                for (i = 0; i < suite.failed.nr; i++)
                        fprintf(stderr, "\t%s\n", suite.failed.items[i].string);
        }
index 31a4bbc716aa3ad97cdeed1344e852f1dfa04b46..6911c2915a74b16647c0075cabda39d415d2562a 100644 (file)
@@ -1,3 +1,6 @@
+# Import tree-wide shared Makefile behavior and libraries
+include ../../shared.mak
+
 -include ../../config.mak
 export GIT_TEST_OPTIONS
 
diff --git a/t/lib-commit-graph.sh b/t/lib-commit-graph.sh
new file mode 100755 (executable)
index 0000000..5d79e1a
--- /dev/null
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+# Helper functions for testing commit-graphs.
+
+# Initialize OID cache with oid_version
+test_oid_cache <<-EOF
+oid_version sha1:1
+oid_version sha256:2
+EOF
+
+graph_git_two_modes() {
+       git -c core.commitGraph=true $1 >output &&
+       git -c core.commitGraph=false $1 >expect &&
+       test_cmp expect output
+}
+
+graph_git_behavior() {
+       MSG=$1
+       DIR=$2
+       BRANCH=$3
+       COMPARE=$4
+       test_expect_success "check normal git operations: $MSG" '
+               cd "$TRASH_DIRECTORY/$DIR" &&
+               graph_git_two_modes "log --oneline $BRANCH" &&
+               graph_git_two_modes "log --topo-order $BRANCH" &&
+               graph_git_two_modes "log --graph $COMPARE..$BRANCH" &&
+               graph_git_two_modes "branch -vv" &&
+               graph_git_two_modes "merge-base -a $BRANCH $COMPARE"
+       '
+}
+
+graph_read_expect() {
+       OPTIONAL=""
+       NUM_CHUNKS=3
+       if test -n "$2"
+       then
+               OPTIONAL=" $2"
+               NUM_CHUNKS=$((3 + $(echo "$2" | wc -w)))
+       fi
+       GENERATION_VERSION=2
+       if test -n "$3"
+       then
+               GENERATION_VERSION=$3
+       fi
+       OPTIONS=
+       if test $GENERATION_VERSION -gt 1
+       then
+               OPTIONS=" read_generation_data"
+       fi
+       cat >expect <<- EOF
+       header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
+       num_commits: $1
+       chunks: oid_fanout oid_lookup commit_metadata$OPTIONAL
+       options:$OPTIONS
+       EOF
+       test-tool read-graph >output &&
+       test_cmp expect output
+}
index 3e7ee1386aa9fcb572652dc38768d099cdfc8368..114785586abde524fb13cb29e4b1f357ae9a3286 100644 (file)
@@ -40,7 +40,7 @@ test_lazy_prereq GPG '
                #               > lib-gpg/ownertrust
                mkdir "$GNUPGHOME" &&
                chmod 0700 "$GNUPGHOME" &&
-               (gpgconf --kill gpg-agent || : ) &&
+               (gpgconf --kill all || : ) &&
                gpg --homedir "${GNUPGHOME}" --import \
                        "$TEST_DIRECTORY"/lib-gpg/keyring.gpg &&
                gpg --homedir "${GNUPGHOME}" --import-ownertrust \
@@ -72,12 +72,11 @@ test_lazy_prereq GPGSM '
                --passphrase-fd 0 --pinentry-mode loopback \
                --import "$TEST_DIRECTORY"/lib-gpg/gpgsm_cert.p12 &&
 
-       gpgsm --homedir "${GNUPGHOME}" -K |
-       grep fingerprint: |
-       cut -d" " -f4 |
-       tr -d "\\n" >"${GNUPGHOME}/trustlist.txt" &&
+       gpgsm --homedir "${GNUPGHOME}" -K --with-colons |
+       awk -F ":" "/^fpr:/ {printf \"%s S relax\\n\", \$10}" \
+               >"${GNUPGHOME}/trustlist.txt" &&
+       (gpgconf --reload all || : ) &&
 
-       echo " S relax" >>"${GNUPGHOME}/trustlist.txt" &&
        echo hello | gpgsm --homedir "${GNUPGHOME}" >/dev/null \
               -u committer@example.com -o /dev/null --sign -
 '
index 2465770a7825b4f360d649df3684c5339c8ce657..e4808aebed08bfb87d0cc9ab6ccd2a70eee3c84e 100644 (file)
@@ -1,3 +1,6 @@
+# Import tree-wide shared Makefile behavior and libraries
+include ../../shared.mak
+
 -include ../../config.mak
 export GIT_TEST_OPTIONS
 
index 2a7106b9495ce1190dec2ff1a42bd0cb77ff3f9b..382716cfca909e82c0bc5bf04e3bb3726dc2f9f1 100755 (executable)
@@ -117,6 +117,7 @@ test_perf_on_all git diff
 test_perf_on_all git diff --cached
 test_perf_on_all git blame $SPARSE_CONE/a
 test_perf_on_all git blame $SPARSE_CONE/f3/a
+test_perf_on_all git read-tree -mu HEAD
 test_perf_on_all git checkout-index -f --all
 test_perf_on_all git update-index --add --remove $SPARSE_CONE/a
 
index b007f0efef26294d2aa0a0665d37d522eb60f103..9dcbf518a7828e25965bda8f16884867c768b62a 100755 (executable)
@@ -1089,7 +1089,8 @@ test_expect_success 'update-index D/F conflict' '
        mv path2 path0 &&
        mv tmp path2 &&
        git update-index --add --replace path2 path0/file2 &&
-       numpath0=$(git ls-files path0 | wc -l) &&
+       git ls-files path0 >tmp &&
+       numpath0=$(wc -l <tmp) &&
        test $numpath0 = 1
 '
 
@@ -1103,13 +1104,14 @@ test_expect_success 'very long name in the index handled sanely' '
 
        >path4 &&
        git update-index --add path4 &&
+       git ls-files -s path4 >tmp &&
        (
-               git ls-files -s path4 |
-               sed -e "s/      .*/     /" |
+               sed -e "s/      .*/     /" tmp |
                tr -d "\012" &&
                echo "$a"
        ) | git update-index --index-info &&
-       len=$(git ls-files "a*" | wc -c) &&
+       git ls-files "a*" >tmp &&
+       len=$(wc -c <tmp) &&
        test $len = 4098
 '
 
index 76052cb5620bebd4965370077a303d3f633154fc..f6356db183b98c5c29acdb4b03dcb453533a5728 100755 (executable)
@@ -65,9 +65,11 @@ test_expect_success 'check commit-tree' '
        test_path_is_file "$REAL/objects/$(objpath $SHA)"
 '
 
-test_expect_success 'check rev-list' '
+test_expect_success !SANITIZE_LEAK 'check rev-list' '
        git update-ref "HEAD" "$SHA" &&
-       test "$SHA" = "$(git rev-list HEAD)"
+       git rev-list HEAD >actual &&
+       echo $SHA >expected &&
+       test_cmp expected actual
 '
 
 test_expect_success 'setup_git_dir twice in subdir' '
index b9ed612af13262c0467aed2be927bd4704a221e4..143f1005179c8afb11aba90e4d279b6d288c8a48 100755 (executable)
@@ -205,15 +205,18 @@ test_expect_success 'attribute test: read paths from stdin' '
 test_expect_success 'attribute test: --all option' '
        grep -v unspecified <expect-all | sort >specified-all &&
        sed -e "s/:.*//" <expect-all | uniq >stdin-all &&
-       git check-attr --stdin --all <stdin-all | sort >actual &&
+       git check-attr --stdin --all <stdin-all >tmp &&
+       sort tmp >actual &&
        test_cmp specified-all actual
 '
 
 test_expect_success 'attribute test: --cached option' '
-       git check-attr --cached --stdin --all <stdin-all | sort >actual &&
+       git check-attr --cached --stdin --all <stdin-all >tmp &&
+       sort tmp >actual &&
        test_must_be_empty actual &&
        git add .gitattributes a/.gitattributes a/b/.gitattributes &&
-       git check-attr --cached --stdin --all <stdin-all | sort >actual &&
+       git check-attr --cached --stdin --all <stdin-all >tmp &&
+       sort tmp >actual &&
        test_cmp specified-all actual
 '
 
index cbd725ccac875a4ee387c0100c4b8e12d4194441..6c3e1f7159d4dcc6a2e61228fa7c4f3cec42ef3b 100755 (executable)
@@ -35,6 +35,9 @@ test_expect_success 'basic help commands' '
 '
 
 test_expect_success 'invalid usage' '
+       test_expect_code 129 git help -a add &&
+       test_expect_code 129 git help --all add &&
+
        test_expect_code 129 git help -g add &&
        test_expect_code 129 git help -a -c &&
 
@@ -46,6 +49,29 @@ test_expect_success 'invalid usage' '
        test_expect_code 129 git help --config-sections-for-completion add
 '
 
+for opt in '-a' '-g' '-c' '--config-for-completion' '--config-sections-for-completion'
+do
+       test_expect_success "invalid usage of '$opt' with [-i|-m|-w]" '
+               git help $opt &&
+               test_expect_code 129 git help $opt -i &&
+               test_expect_code 129 git help $opt -m &&
+               test_expect_code 129 git help $opt -w
+       '
+
+       if test "$opt" = "-a"
+       then
+               continue
+       fi
+
+       test_expect_success "invalid usage of '$opt' with --no-external-commands" '
+               test_expect_code 129 git help $opt --no-external-commands
+       '
+
+       test_expect_success "invalid usage of '$opt' with --no-aliases" '
+               test_expect_code 129 git help $opt --no-external-commands
+       '
+done
+
 test_expect_success "works for commands and guides by default" '
        configure_help &&
        git help status &&
@@ -138,6 +164,74 @@ test_expect_success 'git help --config-sections-for-completion' '
        test_cmp human.munged sections
 '
 
+test_section_spacing () {
+       cat >expect &&
+       "$@" >out &&
+       grep -E "(^[^ ]|^$)" out >actual
+}
+
+test_section_spacing_trailer () {
+       test_section_spacing "$@" &&
+       test_expect_code 1 git >out &&
+       sed -n '/list available subcommands/,$p' <out >>expect
+}
+
+
+for cmd in git "git help"
+do
+       test_expect_success "'$cmd' section spacing" '
+               test_section_spacing_trailer git help <<-\EOF &&
+               usage: git [--version] [--help] [-C <path>] [-c <name>=<value>]
+
+               These are common Git commands used in various situations:
+
+               start a working area (see also: git help tutorial)
+
+               work on the current change (see also: git help everyday)
+
+               examine the history and state (see also: git help revisions)
+
+               grow, mark and tweak your common history
+
+               collaborate (see also: git help workflows)
+
+               EOF
+               test_cmp expect actual
+       '
+done
+
+test_expect_success "'git help -a' section spacing" '
+       test_section_spacing \
+               git help -a --no-external-commands --no-aliases <<-\EOF &&
+       See '\''git help <command>'\'' to read about a specific subcommand
+
+       Main Porcelain Commands
+
+       Ancillary Commands / Manipulators
+
+       Ancillary Commands / Interrogators
+
+       Interacting with Others
+
+       Low-level Commands / Manipulators
+
+       Low-level Commands / Interrogators
+
+       Low-level Commands / Syncing Repositories
+
+       Low-level Commands / Internal Helpers
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success "'git help -g' section spacing" '
+       test_section_spacing_trailer git help -g <<-\EOF &&
+       The Git concept guides are:
+
+       EOF
+       test_cmp expect actual
+'
+
 test_expect_success 'generate builtin list' '
        mkdir -p sub &&
        git --list-cmds=builtins >builtins
index c1a331e9e939fa975ba038934e885bb2fe93b63c..9fe98912511d3b6cb050aa51e33906caf7fb51c5 100755 (executable)
@@ -24,8 +24,8 @@ test_expect_success setup '
 
 test_expect_success 'diff -M' '
 
-       git diff-tree -M -r --name-status HEAD^ HEAD |
-       sed -e "s/R[0-9]*/RNUM/" >actual &&
+       git diff-tree -M -r --name-status HEAD^ HEAD >tmp &&
+       sed -e "s/R[0-9]*/RNUM/" tmp >actual &&
        echo "RNUM      sample  elpmas" >expect &&
        test_cmp expect actual
 
index 81447978b7f66a7a2169341f0b6da649e106e8bd..f7202c192e7b63e07c904fe9dcf1b76252d8808d 100755 (executable)
@@ -22,8 +22,8 @@ test_expect_success 'renormalize CRLF in repo' '
        i/lf w/lf attr/text=auto LF.txt
        i/lf w/mixed attr/text=auto CRLF_mix_LF.txt
        EOF
-       git ls-files --eol |
-       sed -e "s/      / /g" -e "s/  */ /g" |
+       git ls-files --eol >tmp &&
+       sed -e "s/      / /g" -e "s/  */ /g" tmp |
        sort >actual &&
        test_cmp expect actual
 '
index c5f7ac63b0ab98d0ff3b4924e51f3c899b40eeb5..0feb41a23f2c27db155fc49b6fb3ff7f9106cc53 100755 (executable)
@@ -311,8 +311,8 @@ checkout_files () {
                i/-text w/$(stats_ascii $crlfnul) attr/$(attr_ascii $attr $aeol) crlf_false_attr__CRLF_nul.txt
                i/-text w/$(stats_ascii $crlfnul) attr/$(attr_ascii $attr $aeol) crlf_false_attr__LF_nul.txt
                EOF
-               git ls-files --eol crlf_false_attr__* |
-               sed -e "s/      / /g" -e "s/  */ /g" |
+               git ls-files --eol crlf_false_attr__* >tmp &&
+               sed -e "s/      / /g" -e "s/  */ /g" tmp |
                sort >actual &&
                test_cmp expect actual
        '
@@ -359,12 +359,12 @@ test_expect_success 'ls-files --eol -o Text/Binary' '
        i/ w/crlf TeBi_126_CL
        i/ w/-text TeBi_126_CLC
        EOF
-       git ls-files --eol -o |
+       git ls-files --eol -o >tmp &&
        sed -n -e "/TeBi_/{s!attr/[     ]*!!g
        s!      ! !g
        s!  *! !g
        p
-       }" | sort >actual &&
+       }" tmp | sort >actual &&
        test_cmp expect actual
 '
 
@@ -617,8 +617,8 @@ test_expect_success 'ls-files --eol -d -z' '
        i/lf w/ crlf_false_attr__LF.txt
        i/mixed w/ crlf_false_attr__CRLF_mix_LF.txt
        EOF
-       git ls-files --eol -d |
-       sed -e "s!attr/[^       ]*!!g" -e "s/   / /g" -e "s/  */ /g" |
+       git ls-files --eol -d >tmp &&
+       sed -e "s!attr/[^       ]*!!g" -e "s/   / /g" -e "s/  */ /g" tmp |
        sort >actual &&
        test_cmp expect actual
 '
index ae1ca380c1aa54709a557374d08a700337759b75..0a5713c5248280d88a241b8027b5c98e4f321718 100755 (executable)
@@ -13,6 +13,10 @@ s40='                                        '
 sss="$s40$s40$s40$s40$s40$s40$s40$s40$s40$s40" # 400
 ttt="$t40$t40$t40$t40$t40$t40$t40$t40$t40$t40" # 400
 
+printf_git_stripspace () {
+    printf "$1" | git stripspace
+}
+
 test_expect_success \
     'long lines without spaces should be unchanged' '
     echo "$ttt" >expect &&
@@ -225,32 +229,38 @@ test_expect_success \
 
 test_expect_success \
     'text without newline at end should end with newline' '
-    test $(printf "$ttt" | git stripspace | wc -l) -gt 0 &&
-    test $(printf "$ttt$ttt" | git stripspace | wc -l) -gt 0 &&
-    test $(printf "$ttt$ttt$ttt" | git stripspace | wc -l) -gt 0 &&
-    test $(printf "$ttt$ttt$ttt$ttt" | git stripspace | wc -l) -gt 0
+    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt" &&
+    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt" &&
+    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt" &&
+    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt$ttt"
 '
 
 # text plus spaces at the end:
 
 test_expect_success \
     'text plus spaces without newline at end should end with newline' '
-    test $(printf "$ttt$sss" | git stripspace | wc -l) -gt 0 &&
-    test $(printf "$ttt$ttt$sss" | git stripspace | wc -l) -gt 0 &&
-    test $(printf "$ttt$ttt$ttt$sss" | git stripspace | wc -l) -gt 0 &&
-    test $(printf "$ttt$sss$sss" | git stripspace | wc -l) -gt 0 &&
-    test $(printf "$ttt$ttt$sss$sss" | git stripspace | wc -l) -gt 0 &&
-    test $(printf "$ttt$sss$sss$sss" | git stripspace | wc -l) -gt 0
+    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss" &&
+    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$sss" &&
+    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$ttt$sss" &&
+    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss$sss" &&
+    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$ttt$sss$sss" &&
+    test_stdout_line_count -gt 0 printf_git_stripspace "$ttt$sss$sss$sss"
 '
 
 test_expect_success \
     'text plus spaces without newline at end should not show spaces' '
-    ! (printf "$ttt$sss" | git stripspace | grep "  " >/dev/null) &&
-    ! (printf "$ttt$ttt$sss" | git stripspace | grep "  " >/dev/null) &&
-    ! (printf "$ttt$ttt$ttt$sss" | git stripspace | grep "  " >/dev/null) &&
-    ! (printf "$ttt$sss$sss" | git stripspace | grep "  " >/dev/null) &&
-    ! (printf "$ttt$ttt$sss$sss" | git stripspace | grep "  " >/dev/null) &&
-    ! (printf "$ttt$sss$sss$sss" | git stripspace | grep "  " >/dev/null)
+    printf "$ttt$sss" | git stripspace >tmp &&
+    ! grep "  " tmp >/dev/null &&
+    printf "$ttt$ttt$sss" | git stripspace >tmp &&
+    ! grep "  " tmp >/dev/null &&
+    printf "$ttt$ttt$ttt$sss" | git stripspace >tmp &&
+    ! grep "  " tmp >/dev/null &&
+    printf "$ttt$sss$sss" | git stripspace >tmp &&
+    ! grep "  " tmp >/dev/null &&
+    printf "$ttt$ttt$sss$sss" | git stripspace >tmp &&
+    ! grep "  " tmp >/dev/null &&
+    printf "$ttt$sss$sss$sss" | git stripspace >tmp &&
+    ! grep "  " tmp >/dev/null
 '
 
 test_expect_success \
@@ -282,12 +292,18 @@ test_expect_success \
 
 test_expect_success \
     'text plus spaces at end should not show spaces' '
-    ! (echo "$ttt$sss" | git stripspace | grep "  " >/dev/null) &&
-    ! (echo "$ttt$ttt$sss" | git stripspace | grep "  " >/dev/null) &&
-    ! (echo "$ttt$ttt$ttt$sss" | git stripspace | grep "  " >/dev/null) &&
-    ! (echo "$ttt$sss$sss" | git stripspace | grep "  " >/dev/null) &&
-    ! (echo "$ttt$ttt$sss$sss" | git stripspace | grep "  " >/dev/null) &&
-    ! (echo "$ttt$sss$sss$sss" | git stripspace | grep "  " >/dev/null)
+    echo "$ttt$sss" | git stripspace >tmp &&
+    ! grep "  " tmp >/dev/null &&
+    echo "$ttt$ttt$sss" | git stripspace >tmp &&
+    ! grep "  " tmp >/dev/null &&
+    echo "$ttt$ttt$ttt$sss" | git stripspace >tmp &&
+    ! grep "  " tmp >/dev/null &&
+    echo "$ttt$sss$sss" | git stripspace >tmp &&
+    ! grep "  " tmp >/dev/null &&
+    echo "$ttt$ttt$sss$sss" | git stripspace >tmp &&
+    ! grep "  " tmp >/dev/null &&
+    echo "$ttt$sss$sss$sss" | git stripspace >tmp &&
+    ! grep "  " tmp >/dev/null
 '
 
 test_expect_success \
@@ -339,11 +355,16 @@ test_expect_success \
 
 test_expect_success \
     'spaces without newline at end should not show spaces' '
-    ! (printf "" | git stripspace | grep " " >/dev/null) &&
-    ! (printf "$sss" | git stripspace | grep " " >/dev/null) &&
-    ! (printf "$sss$sss" | git stripspace | grep " " >/dev/null) &&
-    ! (printf "$sss$sss$sss" | git stripspace | grep " " >/dev/null) &&
-    ! (printf "$sss$sss$sss$sss" | git stripspace | grep " " >/dev/null)
+    printf "" | git stripspace >tmp &&
+    ! grep " " tmp >/dev/null &&
+    printf "$sss" | git stripspace >tmp &&
+    ! grep " " tmp >/dev/null &&
+    printf "$sss$sss" | git stripspace >tmp &&
+    ! grep " " tmp >/dev/null &&
+    printf "$sss$sss$sss" | git stripspace >tmp &&
+    ! grep " " tmp >/dev/null &&
+    printf "$sss$sss$sss$sss" | git stripspace >tmp &&
+    ! grep " " tmp >/dev/null
 '
 
 test_expect_success \
index afc343cf9bb5e5e85d0a203a0eb5ea6f0bc90c2b..5c9dc90d0b096d9f104caedeb035b50b919b6811 100755 (executable)
@@ -104,7 +104,8 @@ test_expect_failure CASE_INSENSITIVE_FS 'add (with different case)' '
        rm camelcase &&
        echo 1 >CamelCase &&
        git add CamelCase &&
-       camel=$(git ls-files | grep -i camelcase) &&
+       git ls-files >tmp &&
+       camel=$(grep -i camelcase tmp) &&
        test $(echo "$camel" | wc -l) = 1 &&
        test "z$(git cat-file blob :$camel)" = z1
 '
index f17abd298c83467b3ead07ff87c08623ff640502..1e864cf3172bec45e949a840d026e507a117fec8 100755 (executable)
@@ -618,6 +618,25 @@ test_expect_success 'do not fetch when checking existence of tree we construct o
        git -C repo cherry-pick side1
 '
 
+test_expect_success 'exact rename does not need to fetch the blob lazily' '
+       rm -rf repo partial.git &&
+       test_create_repo repo &&
+       content="some dummy content" &&
+       test_commit -C repo create-a-file file.txt "$content" &&
+       git -C repo mv file.txt new-file.txt &&
+       git -C repo commit -m rename-the-file &&
+       FILE_HASH=$(git -C repo rev-parse HEAD:new-file.txt) &&
+       test_config -C repo uploadpack.allowfilter 1 &&
+       test_config -C repo uploadpack.allowanysha1inwant 1 &&
+
+       git clone --filter=blob:none --bare "file://$(pwd)/repo" partial.git &&
+       git -C partial.git rev-list --objects --missing=print HEAD >out &&
+       grep "[?]$FILE_HASH" out &&
+       git -C partial.git log --follow -- new-file.txt &&
+       git -C partial.git rev-list --objects --missing=print HEAD >out &&
+       grep "[?]$FILE_HASH" out
+'
+
 test_expect_success 'lazy-fetch when accessing object not in the_repository' '
        rm -rf full partial.git &&
        test_create_repo full &&
index d1115528cb9461495f3ad1e934beb1cd3682f97c..0710b1fb1e945b4d15d989f65f83050076e592cf 100755 (executable)
@@ -21,7 +21,6 @@ In the test, these paths are used:
        yomin   - not in H or M
 '
 
-TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
@@ -38,11 +37,12 @@ compare_change () {
 }
 
 check_cache_at () {
-       clean_if_empty=$(git diff-files -- "$1")
+       git diff-files -- "$1" >out &&
+       clean_if_empty=$(cat out) &&
        case "$clean_if_empty" in
        '')  echo "$1: clean" ;;
        ?*)  echo "$1: dirty" ;;
-       esac
+       esac &&
        case "$2,$clean_if_empty" in
        clean,)         :     ;;
        clean,?*)       false ;;
index ca5c5510c737cce1f7c224a7cd8ad0ab95d3010d..46cbd5514a68e135222b5dbec77a21e170ccaa2b 100755 (executable)
@@ -9,7 +9,6 @@ This is identical to t1001, but uses -u to update the work tree as well.
 
 '
 
-TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
@@ -23,11 +22,12 @@ compare_change () {
 }
 
 check_cache_at () {
-       clean_if_empty=$(git diff-files -- "$1")
+       git diff-files -- "$1" >out &&
+       clean_if_empty=$(cat out) &&
        case "$clean_if_empty" in
        '')  echo "$1: clean" ;;
        ?*)  echo "$1: dirty" ;;
-       esac
+       esac &&
        case "$2,$clean_if_empty" in
        clean,)         :     ;;
        clean,?*)       false ;;
index e0db2066f3194b7084f25ea249156c30711f3e2b..c860c08ecb46a37a6f20bf403963afd7576fed9f 100755 (executable)
@@ -25,4 +25,14 @@ test_expect_success 'read-tree --prefix' '
        cmp expect actual
 '
 
+test_expect_success 'read-tree --prefix with leading slash exits with error' '
+       git rm -rf . &&
+       test_must_fail git read-tree --prefix=/two/ $tree &&
+       git read-tree --prefix=two/ $tree &&
+
+       git rm -rf . &&
+       test_must_fail git read-tree --prefix=/ $tree &&
+       git read-tree --prefix= $tree
+'
+
 test_done
index 145eee11df97c9cf63542e49e42f80ecabab1b73..1b8520769446d217c757d8286e8c46c54dcafb97 100755 (executable)
@@ -105,13 +105,18 @@ strlen () {
 }
 
 maybe_remove_timestamp () {
-    if test -z "$2"; then
-        echo_without_newline "$1"
-    else
-       echo_without_newline "$(printf '%s\n' "$1" | sed -e 's/ [0-9][0-9]* [-+][0-9][0-9][0-9][0-9]$//')"
-    fi
+       if test -z "$2"; then
+               echo_without_newline "$1"
+       else
+               echo_without_newline "$(printf '%s\n' "$1" | remove_timestamp)"
+       fi
 }
 
+remove_timestamp () {
+       sed -e 's/ [0-9][0-9]* [-+][0-9][0-9][0-9][0-9]$//'
+}
+
+
 run_tests () {
     type=$1
     sha1=$2
@@ -177,12 +182,36 @@ $content"
        test_cmp expect actual
     '
 
+    for opt in --buffer --no-buffer
+    do
+       test -z "$content" ||
+               test_expect_success "--batch-command $opt output of $type content is correct" '
+               maybe_remove_timestamp "$batch_output" $no_ts >expect &&
+               maybe_remove_timestamp "$(test_write_lines "contents $sha1" |
+               git cat-file --batch-command $opt)" $no_ts >actual &&
+               test_cmp expect actual
+       '
+
+       test_expect_success "--batch-command $opt output of $type info is correct" '
+               echo "$sha1 $type $size" >expect &&
+               test_write_lines "info $sha1" |
+               git cat-file --batch-command $opt >actual &&
+               test_cmp expect actual
+       '
+    done
+
     test_expect_success "custom --batch-check format" '
        echo "$type $sha1" >expect &&
        echo $sha1 | git cat-file --batch-check="%(objecttype) %(objectname)" >actual &&
        test_cmp expect actual
     '
 
+    test_expect_success "custom --batch-command format" '
+       echo "$type $sha1" >expect &&
+       echo "info $sha1" | git cat-file --batch-command="%(objecttype) %(objectname)" >actual &&
+       test_cmp expect actual
+    '
+
     test_expect_success '--batch-check with %(rest)' '
        echo "$type this is some extra content" >expect &&
        echo "$sha1    this is some extra content" |
@@ -224,6 +253,22 @@ test_expect_success "setup" '
 
 run_tests 'blob' $hello_sha1 $hello_size "$hello_content" "$hello_content"
 
+test_expect_success '--batch-command --buffer with flush for blob info' '
+       echo "$hello_sha1 blob $hello_size" >expect &&
+       test_write_lines "info $hello_sha1" "flush" |
+       GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT=1 \
+       git cat-file --batch-command --buffer >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--batch-command --buffer without flush for blob info' '
+       touch output &&
+       test_write_lines "info $hello_sha1" |
+       GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT=1 \
+       git cat-file --batch-command --buffer >>output &&
+       test_must_be_empty output
+'
+
 test_expect_success '--batch-check without %(rest) considers whole line' '
        echo "$hello_sha1 blob $hello_size" >expect &&
        git update-index --add --cacheinfo 100644 $hello_sha1 "white space" &&
@@ -267,7 +312,7 @@ test_expect_success \
     "Reach a blob from a tag pointing to it" \
     "test '$hello_content' = \"\$(git cat-file blob $tag_sha1)\""
 
-for batch in batch batch-check
+for batch in batch batch-check batch-command
 do
     for opt in t s e p
     do
@@ -373,6 +418,49 @@ test_expect_success "--batch-check with multiple sha1s gives correct format" '
     "$(echo_without_newline "$batch_check_input" | git cat-file --batch-check)"
 '
 
+test_expect_success '--batch-command with multiple info calls gives correct format' '
+       cat >expect <<-EOF &&
+       $hello_sha1 blob $hello_size
+       $tree_sha1 tree $tree_size
+       $commit_sha1 commit $commit_size
+       $tag_sha1 tag $tag_size
+       deadbeef missing
+       EOF
+
+       git cat-file --batch-command --buffer >actual <<-EOF &&
+       info $hello_sha1
+       info $tree_sha1
+       info $commit_sha1
+       info $tag_sha1
+       info deadbeef
+       EOF
+
+       test_cmp expect actual
+'
+
+test_expect_success '--batch-command with multiple command calls gives correct format' '
+       remove_timestamp >expect <<-EOF &&
+       $hello_sha1 blob $hello_size
+       $hello_content
+       $commit_sha1 commit $commit_size
+       $commit_content
+       $tag_sha1 tag $tag_size
+       $tag_content
+       deadbeef missing
+       EOF
+
+       git cat-file --batch-command --buffer >actual_raw <<-EOF &&
+       contents $hello_sha1
+       contents $commit_sha1
+       contents $tag_sha1
+       contents deadbeef
+       flush
+       EOF
+
+       remove_timestamp <actual_raw >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'setup blobs which are likely to delta' '
        test-tool genrandom foo 10240 >foo &&
        { cat foo && echo plus; } >foo-plus &&
@@ -963,5 +1051,40 @@ test_expect_success 'cat-file --batch-all-objects --batch-check ignores replace'
        echo "$orig commit $orig_size" >expect &&
        test_cmp expect actual
 '
+test_expect_success 'batch-command empty command' '
+       echo "" >cmd &&
+       test_expect_code 128 git cat-file --batch-command <cmd 2>err &&
+       grep "^fatal:.*empty command in input.*" err
+'
+
+test_expect_success 'batch-command whitespace before command' '
+       echo " info deadbeef" >cmd &&
+       test_expect_code 128 git cat-file --batch-command <cmd 2>err &&
+       grep "^fatal:.*whitespace before command.*" err
+'
+
+test_expect_success 'batch-command unknown command' '
+       echo unknown_command >cmd &&
+       test_expect_code 128 git cat-file --batch-command <cmd 2>err &&
+       grep "^fatal:.*unknown command.*" err
+'
+
+test_expect_success 'batch-command missing arguments' '
+       echo "info" >cmd &&
+       test_expect_code 128 git cat-file --batch-command <cmd 2>err &&
+       grep "^fatal:.*info requires arguments.*" err
+'
+
+test_expect_success 'batch-command flush with arguments' '
+       echo "flush arg" >cmd &&
+       test_expect_code 128 git cat-file --batch-command --buffer <cmd 2>err &&
+       grep "^fatal:.*flush takes no arguments.*" err
+'
+
+test_expect_success 'batch-command flush without --buffer' '
+       echo "flush" >cmd &&
+       test_expect_code 128 git cat-file --batch-command <cmd 2>err &&
+       grep "^fatal:.*flush is only for --buffer mode.*" err
+'
 
 test_done
index 24092c09a95771df742337e8d6c1c9eac77221ba..dd957be1b78c5690fc79495f501eea52e4467600 100755 (executable)
@@ -187,11 +187,32 @@ test_expect_success 'read-tree updates worktree, absent case' '
        test ! -f init.t
 '
 
+test_expect_success 'read-tree will not throw away dirty changes, non-sparse' '
+       echo "/*" >.git/info/sparse-checkout &&
+       read_tree_u_must_succeed -m -u HEAD &&
+
+       echo dirty >init.t &&
+       read_tree_u_must_fail -m -u HEAD^ &&
+       test_path_is_file init.t &&
+       grep -q dirty init.t
+'
+
+test_expect_success 'read-tree will not throw away dirty changes, sparse' '
+       echo "/*" >.git/info/sparse-checkout &&
+       read_tree_u_must_succeed -m -u HEAD &&
+
+       echo dirty >init.t &&
+       echo sub/added >.git/info/sparse-checkout &&
+       read_tree_u_must_fail -m -u HEAD^ &&
+       test_path_is_file init.t &&
+       grep -q dirty init.t
+'
+
 test_expect_success 'read-tree updates worktree, dirty case' '
        echo sub/added >.git/info/sparse-checkout &&
        git checkout -f top &&
        echo dirty >init.t &&
-       read_tree_u_must_succeed -m -u HEAD^ &&
+       read_tree_u_must_fail -m -u HEAD^ &&
        grep -q dirty init.t &&
        rm init.t
 '
index 3deb490187415ec37ae5465ae980d10a775a58db..d1833c0f31b467e1dc5173a7332bfbb16c5de05c 100755 (executable)
@@ -52,6 +52,25 @@ test_expect_success 'return to full checkout of main' '
        test "$(cat b)" = "modified"
 '
 
+test_expect_success 'skip-worktree on files outside sparse patterns' '
+       git sparse-checkout disable &&
+       git sparse-checkout set --no-cone "a*" &&
+       git checkout-index --all --ignore-skip-worktree-bits &&
+
+       git ls-files -t >output &&
+       ! grep ^S output >actual &&
+       test_must_be_empty actual &&
+
+       test_config sparse.expectFilesOutsideOfPatterns true &&
+       cat <<-\EOF >expect &&
+       S b
+       S c
+       EOF
+       git ls-files -t >output &&
+       grep ^S output >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'in partial clone, sparse checkout only fetches needed blobs' '
        test_create_repo server &&
        git clone "file://$(pwd)/server" client &&
index f3a059e5af557aacf113909aa06e089d9fd43e8c..dcc0a30d4ad32e43a5aec5ceaf61008a05d1d11c 100755 (executable)
@@ -244,6 +244,24 @@ test_expect_success 'expanded in-memory index matches full index' '
        test_sparse_match git ls-files --stage
 '
 
+test_expect_success 'root directory cannot be sparse' '
+       init_repos &&
+
+       # Remove all in-cone files and directories from the index, collapse index
+       # with `git sparse-checkout reapply`
+       git -C sparse-index rm -r . &&
+       git -C sparse-index sparse-checkout reapply &&
+
+       # Verify sparse directories still present, root directory is not sparse
+       cat >expect <<-EOF &&
+       folder1/
+       folder2/
+       x/
+       EOF
+       git -C sparse-index ls-files --sparse >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'status with options' '
        init_repos &&
        test_sparse_match ls &&
@@ -260,6 +278,13 @@ test_expect_success 'status with options' '
        test_all_match git status --porcelain=v2 -uno
 '
 
+test_expect_success 'status with diff in unexpanded sparse directory' '
+       init_repos &&
+       test_all_match git checkout rename-base &&
+       test_all_match git reset --soft rename-out-to-out &&
+       test_all_match git status --porcelain=v2
+'
+
 test_expect_success 'status reports sparse-checkout' '
        init_repos &&
        git -C sparse-checkout status >full &&
@@ -367,7 +392,7 @@ test_expect_success 'status/add: outside sparse cone' '
        write_script edit-contents <<-\EOF &&
        echo text >>$1
        EOF
-       run_on_sparse ../edit-contents folder1/a &&
+       run_on_all ../edit-contents folder1/a &&
        run_on_all ../edit-contents folder1/new &&
 
        test_sparse_match git status --porcelain=v2 &&
@@ -376,8 +401,8 @@ test_expect_success 'status/add: outside sparse cone' '
        test_sparse_match test_must_fail git add folder1/a &&
        grep "Disable or modify the sparsity rules" sparse-checkout-err &&
        test_sparse_unstaged folder1/a &&
-       test_sparse_match test_must_fail git add --refresh folder1/a &&
-       grep "Disable or modify the sparsity rules" sparse-checkout-err &&
+       test_all_match git add --refresh folder1/a &&
+       test_must_be_empty sparse-checkout-err &&
        test_sparse_unstaged folder1/a &&
        test_sparse_match test_must_fail git add folder1/new &&
        grep "Disable or modify the sparsity rules" sparse-checkout-err &&
@@ -643,11 +668,11 @@ test_expect_success 'update-index modify outside sparse definition' '
        run_on_sparse cp ../initial-repo/folder1/a folder1/a &&
        run_on_all ../edit-contents folder1/a &&
 
-       # If file has skip-worktree enabled, update-index does not modify the
-       # index entry
-       test_sparse_match git update-index folder1/a &&
-       test_sparse_match git status --porcelain=v2 &&
-       test_must_be_empty sparse-checkout-out &&
+       # If file has skip-worktree enabled, but the file is present, it is
+       # treated the same as if skip-worktree is disabled
+       test_all_match git status --porcelain=v2 &&
+       test_all_match git update-index folder1/a &&
+       test_all_match git status --porcelain=v2 &&
 
        # When skip-worktree is disabled (even on files outside sparse cone), file
        # is updated in the index
@@ -794,6 +819,93 @@ test_expect_success 'update-index --cacheinfo' '
        test_cmp expect sparse-checkout-out
 '
 
+for MERGE_TREES in "base HEAD update-folder2" \
+                  "update-folder1 update-folder2" \
+                  "update-folder2"
+do
+       test_expect_success "'read-tree -mu $MERGE_TREES' with files outside sparse definition" '
+               init_repos &&
+
+               # Although the index matches, without --no-sparse-checkout, outside-of-
+               # definition files will not exist on disk for sparse checkouts
+               test_all_match git read-tree -mu $MERGE_TREES &&
+               test_all_match git status --porcelain=v2 &&
+               test_path_is_missing sparse-checkout/folder2 &&
+               test_path_is_missing sparse-index/folder2 &&
+
+               test_all_match git read-tree --reset -u HEAD &&
+               test_all_match git status --porcelain=v2 &&
+
+               test_all_match git read-tree -mu --no-sparse-checkout $MERGE_TREES &&
+               test_all_match git status --porcelain=v2 &&
+               test_cmp sparse-checkout/folder2/a sparse-index/folder2/a &&
+               test_cmp sparse-checkout/folder2/a full-checkout/folder2/a
+
+       '
+done
+
+test_expect_success 'read-tree --merge with edit/edit conflicts in sparse directories' '
+       init_repos &&
+
+       # Merge of multiple changes to same directory (but not same files) should
+       # succeed
+       test_all_match git read-tree -mu base rename-base update-folder1 &&
+       test_all_match git status --porcelain=v2 &&
+
+       test_all_match git reset --hard &&
+
+       test_all_match git read-tree -mu rename-base update-folder2 &&
+       test_all_match git status --porcelain=v2 &&
+
+       test_all_match git reset --hard &&
+
+       test_all_match test_must_fail git read-tree -mu base update-folder1 rename-out-to-in &&
+       test_all_match test_must_fail git read-tree -mu rename-out-to-in update-folder1
+'
+
+test_expect_success 'read-tree --prefix' '
+       init_repos &&
+
+       # If files differing between the index and target <commit-ish> exist
+       # inside the prefix, `read-tree --prefix` should fail
+       test_all_match test_must_fail git read-tree --prefix=deep/ deepest &&
+       test_all_match test_must_fail git read-tree --prefix=folder1/ update-folder1 &&
+
+       # If no differing index entries exist matching the prefix,
+       # `read-tree --prefix` updates the index successfully
+       test_all_match git rm -rf deep/deeper1/deepest/ &&
+       test_all_match git read-tree --prefix=deep/deeper1/deepest -u deepest &&
+       test_all_match git status --porcelain=v2 &&
+
+       test_all_match git rm -rf --sparse folder1/ &&
+       test_all_match git read-tree --prefix=folder1/ -u update-folder1 &&
+       test_all_match git status --porcelain=v2 &&
+
+       test_all_match git rm -rf --sparse folder2/0 &&
+       test_all_match git read-tree --prefix=folder2/0/ -u rename-out-to-out &&
+       test_all_match git status --porcelain=v2
+'
+
+test_expect_success 'read-tree --merge with directory-file conflicts' '
+       init_repos &&
+
+       test_all_match git checkout -b test-branch rename-base &&
+
+       # Although the index matches, without --no-sparse-checkout, outside-of-
+       # definition files will not exist on disk for sparse checkouts
+       test_sparse_match git read-tree -mu rename-out-to-out &&
+       test_sparse_match git status --porcelain=v2 &&
+       test_path_is_missing sparse-checkout/folder2 &&
+       test_path_is_missing sparse-index/folder2 &&
+
+       test_sparse_match git read-tree --reset -u HEAD &&
+       test_sparse_match git status --porcelain=v2 &&
+
+       test_sparse_match git read-tree -mu --no-sparse-checkout rename-out-to-out &&
+       test_sparse_match git status --porcelain=v2 &&
+       test_cmp sparse-checkout/folder2/0/1 sparse-index/folder2/0/1
+'
+
 test_expect_success 'merge, cherry-pick, and rebase' '
        init_repos &&
 
@@ -1297,6 +1409,27 @@ test_expect_success 'sparse index is not expanded: fetch/pull' '
        ensure_not_expanded pull full base
 '
 
+test_expect_success 'sparse index is not expanded: read-tree' '
+       init_repos &&
+
+       ensure_not_expanded checkout -b test-branch update-folder1 &&
+       for MERGE_TREES in "base HEAD update-folder2" \
+                          "base HEAD rename-base" \
+                          "base update-folder2" \
+                          "base rename-base" \
+                          "update-folder2"
+       do
+               ensure_not_expanded read-tree -mu $MERGE_TREES &&
+               ensure_not_expanded reset --hard || return 1
+       done &&
+
+       rm -rf sparse-index/deep/deeper2 &&
+       ensure_not_expanded add . &&
+       ensure_not_expanded commit -m "test" &&
+
+       ensure_not_expanded read-tree --prefix=deep/deeper2 -u deepest
+'
+
 test_expect_success 'ls-files' '
        init_repos &&
 
@@ -1331,30 +1464,27 @@ test_expect_success 'ls-files' '
        test_cmp dense sparse &&
 
        # Set up a strange condition of having a file edit
-       # outside of the sparse-checkout cone. This is just
-       # to verify that sparse-checkout and sparse-index
-       # behave the same in this case.
+       # outside of the sparse-checkout cone. We want to verify
+       # that all modes handle this the same, and detect the
+       # modification.
        write_script edit-content <<-\EOF &&
-       mkdir folder1 &&
+       mkdir -p folder1 &&
        echo content >>folder1/a
        EOF
-       run_on_sparse ../edit-content &&
+       run_on_all ../edit-content &&
 
-       # ls-files does not currently notice modified files whose
-       # cache entries are marked SKIP_WORKTREE. This may change
-       # in the future, but here we test that sparse index does
-       # not accidentally create a change of behavior.
-       test_sparse_match git ls-files --modified &&
-       test_must_be_empty sparse-checkout-out &&
-       test_must_be_empty sparse-index-out &&
+       test_all_match git ls-files --modified &&
 
        git -C sparse-index ls-files --sparse --modified >sparse-index-out &&
-       test_must_be_empty sparse-index-out &&
+       cat >expect <<-\EOF &&
+       folder1/a
+       EOF
+       test_cmp expect sparse-index-out &&
 
        # Add folder1 to the sparse-checkout cone and
        # check that ls-files shows the expanded files.
        test_sparse_match git sparse-checkout add folder1 &&
-       test_sparse_match git ls-files --modified &&
+       test_all_match git ls-files --modified &&
 
        test_all_match git ls-files &&
        git -C sparse-index ls-files --sparse >actual &&
index 68f69bb5431d3248020ac6990189e4b1972dc93d..ea8e6ac2a024351643a57a927cf4778d13410637 100755 (executable)
@@ -423,4 +423,13 @@ test_expect_success 'expire with multiple worktrees' '
        )
 '
 
+test_expect_success REFFILES 'empty reflog' '
+       test_when_finished "rm -rf empty" &&
+       git init empty &&
+       test_commit -C empty A &&
+       >empty/.git/logs/refs/heads/foo &&
+       git -C empty reflog expire --all 2>err &&
+       test_must_be_empty err
+'
+
 test_done
index 94fe413ee3771d1d30b0d70a4354811a6e93d163..ba43168d1237ad5216e3ba5ed22069323fbf93b0 100755 (executable)
@@ -132,8 +132,9 @@ test_expect_success 'use --default' '
        test_must_fail git rev-parse --verify --default bar
 '
 
-test_expect_success 'main@{n} for various n' '
-       N=$(git reflog | wc -l) &&
+test_expect_success !SANITIZE_LEAK 'main@{n} for various n' '
+       git reflog >out &&
+       N=$(wc -l <out) &&
        Nm1=$(($N-1)) &&
        Np1=$(($N+1)) &&
        git rev-parse --verify main@{0} &&
index 42601d5a310de33185c40f928b566a45c9985f1c..1f6c4ed0428a5c966c76359c1f4fa2e536f37fcf 100755 (executable)
@@ -21,14 +21,20 @@ test_expect_success 'first branch switch' '
        git checkout other
 '
 
+test_cmp_symbolic_HEAD_ref () {
+       echo refs/heads/"$1" >expect &&
+       git symbolic-ref HEAD >actual &&
+       test_cmp expect actual
+}
+
 test_expect_success '"checkout -" switches back' '
        git checkout - &&
-       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main"
+       test_cmp_symbolic_HEAD_ref main
 '
 
 test_expect_success '"checkout -" switches forth' '
        git checkout - &&
-       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other"
+       test_cmp_symbolic_HEAD_ref other
 '
 
 test_expect_success 'detach HEAD' '
@@ -37,12 +43,16 @@ test_expect_success 'detach HEAD' '
 
 test_expect_success '"checkout -" attaches again' '
        git checkout - &&
-       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other"
+       test_cmp_symbolic_HEAD_ref other
 '
 
 test_expect_success '"checkout -" detaches again' '
        git checkout - &&
-       test "z$(git rev-parse HEAD)" = "z$(git rev-parse other)" &&
+
+       git rev-parse other >expect &&
+       git rev-parse HEAD >actual &&
+       test_cmp expect actual &&
+
        test_must_fail git symbolic-ref HEAD
 '
 
@@ -63,31 +73,31 @@ more_switches () {
 test_expect_success 'switch to the last' '
        more_switches &&
        git checkout @{-1} &&
-       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch2"
+       test_cmp_symbolic_HEAD_ref branch2
 '
 
 test_expect_success 'switch to second from the last' '
        more_switches &&
        git checkout @{-2} &&
-       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch3"
+       test_cmp_symbolic_HEAD_ref branch3
 '
 
 test_expect_success 'switch to third from the last' '
        more_switches &&
        git checkout @{-3} &&
-       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch4"
+       test_cmp_symbolic_HEAD_ref branch4
 '
 
 test_expect_success 'switch to fourth from the last' '
        more_switches &&
        git checkout @{-4} &&
-       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch5"
+       test_cmp_symbolic_HEAD_ref branch5
 '
 
 test_expect_success 'switch to twelfth from the last' '
        more_switches &&
        git checkout @{-12} &&
-       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch13"
+       test_cmp_symbolic_HEAD_ref branch13
 '
 
 test_expect_success 'merge base test setup' '
@@ -98,19 +108,28 @@ test_expect_success 'merge base test setup' '
 test_expect_success 'another...main' '
        git checkout another &&
        git checkout another...main &&
-       test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify main^)"
+
+       git rev-parse --verify main^ >expect &&
+       git rev-parse --verify HEAD >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success '...main' '
        git checkout another &&
        git checkout ...main &&
-       test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify main^)"
+
+       git rev-parse --verify main^ >expect &&
+       git rev-parse --verify HEAD >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'main...' '
        git checkout another &&
        git checkout main... &&
-       test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify main^)"
+
+       git rev-parse --verify main^ >expect &&
+       git rev-parse --verify HEAD >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success '"checkout -" works after a rebase A' '
@@ -118,7 +137,7 @@ test_expect_success '"checkout -" works after a rebase A' '
        git checkout other &&
        git rebase main &&
        git checkout - &&
-       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main"
+       test_cmp_symbolic_HEAD_ref main
 '
 
 test_expect_success '"checkout -" works after a rebase A B' '
@@ -127,7 +146,7 @@ test_expect_success '"checkout -" works after a rebase A B' '
        git checkout other &&
        git rebase main moodle &&
        git checkout - &&
-       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main"
+       test_cmp_symbolic_HEAD_ref main
 '
 
 test_expect_success '"checkout -" works after a rebase -i A' '
@@ -135,7 +154,7 @@ test_expect_success '"checkout -" works after a rebase -i A' '
        git checkout other &&
        git rebase -i main &&
        git checkout - &&
-       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main"
+       test_cmp_symbolic_HEAD_ref main
 '
 
 test_expect_success '"checkout -" works after a rebase -i A B' '
@@ -144,7 +163,7 @@ test_expect_success '"checkout -" works after a rebase -i A B' '
        git checkout other &&
        git rebase main foodle &&
        git checkout - &&
-       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/main"
+       test_cmp_symbolic_HEAD_ref main
 '
 
 test_done
index acd3650d3c08cc2ef8227c95d6c89dfd0f063b2f..0c38f8e35695745c8eb02fe090c0d15b6d1666c8 100755 (executable)
@@ -14,7 +14,6 @@ only the updates to dir/sub.
 Also tested are "git add -u" without limiting, and "git add -u"
 without contents changes, and other conditions'
 
-TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
@@ -41,20 +40,28 @@ test_expect_success update '
 '
 
 test_expect_success 'update noticed a removal' '
-       test "$(git ls-files dir1/sub1)" = ""
+       git ls-files dir1/sub1 >out &&
+       test_must_be_empty out
 '
 
 test_expect_success 'update touched correct path' '
-       test "$(git diff-files --name-status dir2/sub3)" = ""
+       git diff-files --name-status dir2/sub3 >out &&
+       test_must_be_empty out
 '
 
 test_expect_success 'update did not touch other tracked files' '
-       test "$(git diff-files --name-status check)" = "M       check" &&
-       test "$(git diff-files --name-status top)" = "M top"
+       echo "M check" >expect &&
+       git diff-files --name-status check >actual &&
+       test_cmp expect actual &&
+
+       echo "M top" >expect &&
+       git diff-files --name-status top >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'update did not touch untracked files' '
-       test "$(git ls-files dir2/other)" = ""
+       git ls-files dir2/other >out &&
+       test_must_be_empty out
 '
 
 test_expect_success 'cache tree has not been corrupted' '
@@ -76,9 +83,8 @@ test_expect_success 'update from a subdirectory' '
 '
 
 test_expect_success 'change gets noticed' '
-
-       test "$(git diff-files --name-status dir1)" = ""
-
+       git diff-files --name-status dir1 >out &&
+       test_must_be_empty out
 '
 
 test_expect_success 'non-qualified update in subdir updates from the root' '
@@ -103,7 +109,8 @@ test_expect_success 'replace a file with a symlink' '
 test_expect_success 'add everything changed' '
 
        git add -u &&
-       test -z "$(git diff-files)"
+       git diff-files >out &&
+       test_must_be_empty out
 
 '
 
@@ -111,7 +118,8 @@ test_expect_success 'touch and then add -u' '
 
        touch check &&
        git add -u &&
-       test -z "$(git diff-files)"
+       git diff-files >out &&
+       test_must_be_empty out
 
 '
 
@@ -119,7 +127,8 @@ test_expect_success 'touch and then add explicitly' '
 
        touch check &&
        git add check &&
-       test -z "$(git diff-files)"
+       git diff-files >out &&
+       test_must_be_empty out
 
 '
 
index bc9d8ee1e6a8b223f084cdc405a52048630ba5b1..bb5fea02a03a563a23e806d80dbb88a673c40802 100755 (executable)
@@ -8,7 +8,6 @@ test_description='Test commit notes index (expensive!)'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
-TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 create_repo () {
@@ -65,7 +64,8 @@ create_repo () {
 test_notes () {
        count=$1 &&
        git config core.notesRef refs/notes/commits &&
-       git log | grep "^    " >output &&
+       git log >tmp &&
+       grep "^    " tmp >output &&
        i=$count &&
        while test $i -gt 0
        do
@@ -90,7 +90,7 @@ write_script time_notes <<\EOF
                        unset GIT_NOTES_REF
                        ;;
                esac
-               git log
+               git log || exit $?
                i=$(($i+1))
        done >/dev/null
 EOF
index 7e0a8960af886fb83d555766919e34fcd532bf7c..eac193757bf1a5cd27ed4d54249a41de7b302bde 100755 (executable)
@@ -5,7 +5,6 @@ test_description='Test commit notes organized in subtrees'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
-TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 number_of_commits=100
@@ -79,7 +78,7 @@ test_sha1_based () {
        (
                start_note_commit &&
                nr=$number_of_commits &&
-               git rev-list refs/heads/main |
+               git rev-list refs/heads/main >out &&
                while read sha1; do
                        note_path=$(echo "$sha1" | sed "$1")
                        cat <<INPUT_END &&
@@ -91,9 +90,9 @@ EOF
 INPUT_END
 
                        nr=$(($nr-1))
-               done
-       ) |
-       git fast-import --quiet
+               done <out
+       ) >gfi &&
+       git fast-import --quiet <gfi
 }
 
 test_expect_success 'test notes in 2/38-fanout' 'test_sha1_based "s|^..|&/|"'
index 1f5964865ae173e05fab2070c179ff305886bf82..9976d787f4721c898d52eb0211399b145c9e1872 100755 (executable)
@@ -2,7 +2,6 @@
 
 test_description='Test that adding/removing many notes triggers automatic fanout restructuring'
 
-TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 path_has_fanout() {
@@ -24,7 +23,7 @@ touched_one_note_with_fanout() {
 all_notes_have_fanout() {
        notes_commit=$1 &&
        fanout=$2 &&
-       git ls-tree -r --name-only $notes_commit 2>/dev/null |
+       git ls-tree -r --name-only $notes_commit |
        while read path
        do
                path_has_fanout $path $fanout || return 1
@@ -51,8 +50,9 @@ test_expect_success 'creating many notes with git-notes' '
        done
 '
 
-test_expect_success 'many notes created correctly with git-notes' '
-       git log | grep "^    " > output &&
+test_expect_success !SANITIZE_LEAK 'many notes created correctly with git-notes' '
+       git log >output.raw &&
+       grep "^    " output.raw >output &&
        i=$num_notes &&
        while test $i -gt 0
        do
@@ -91,13 +91,13 @@ test_expect_success 'stable fanout 0 is followed by stable fanout 1' '
 test_expect_success 'deleting most notes with git-notes' '
        remove_notes=285 &&
        i=0 &&
-       git rev-list HEAD |
+       git rev-list HEAD >revs &&
        while test $i -lt $remove_notes && read sha1
        do
                i=$(($i + 1)) &&
                test_tick &&
-               git notes remove "$sha1" 2>/dev/null || return 1
-       done
+               git notes remove "$sha1" || return 1
+       done <revs
 '
 
 test_expect_success 'most notes deleted correctly with git-notes' '
index 81f3384eeed4bba508f2e87aeb72a1a6f9a37394..95609046c61d3711244524d48061df4dae8cc343 100755 (executable)
@@ -19,6 +19,7 @@ setup_sparse_entry () {
        fi &&
        git add sparse_entry &&
        git update-index --skip-worktree sparse_entry &&
+       git config core.sparseCheckout false &&
        git commit --allow-empty -m "ensure sparse_entry exists at HEAD" &&
        SPARSE_ENTRY_BLOB=$(git rev-parse :sparse_entry)
 }
@@ -126,6 +127,7 @@ test_expect_success 'git add --chmod does not update sparse entries' '
 '
 
 test_expect_success 'git add --renormalize does not update sparse entries' '
+       test_when_finished rm .gitattributes &&
        test_config core.autocrlf false &&
        setup_sparse_entry "LINEONE\r\nLINETWO\r\n" &&
        echo "sparse_entry text=auto" >.gitattributes &&
index f36e121210e18e655b98c7e05505e5a5917ac2b7..42638b11d865278476667fce03b744cfdcbe461b 100755 (executable)
@@ -41,7 +41,7 @@ diff_cmp () {
        rm -f "$1.compare" "$2.compare"
 }
 
-test_expect_success 'stash some dirty working directory' '
+setup_stash() {
        echo 1 >file &&
        git add file &&
        echo unrelated >other-file &&
@@ -55,6 +55,10 @@ test_expect_success 'stash some dirty working directory' '
        git stash &&
        git diff-files --quiet &&
        git diff-index --cached --quiet HEAD
+}
+
+test_expect_success 'stash some dirty working directory' '
+       setup_stash
 '
 
 cat >expect <<EOF
@@ -185,6 +189,43 @@ test_expect_success 'drop middle stash by index' '
        test 1 = $(git show HEAD:file)
 '
 
+test_expect_success 'drop stash reflog updates refs/stash' '
+       git reset --hard &&
+       git rev-parse refs/stash >expect &&
+       echo 9 >file &&
+       git stash &&
+       git stash drop stash@{0} &&
+       git rev-parse refs/stash >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite' '
+       git init repo &&
+       (
+               cd repo &&
+               setup_stash
+       ) &&
+       echo 9 >repo/file &&
+
+       old_oid="$(git -C repo rev-parse stash@{0})" &&
+       git -C repo stash &&
+       new_oid="$(git -C repo rev-parse stash@{0})" &&
+
+       cat >expect <<-EOF &&
+       $(test_oid zero) $old_oid
+       $old_oid $new_oid
+       EOF
+       cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+       test_cmp expect actual &&
+
+       git -C repo stash drop stash@{1} &&
+       cut -d" " -f1-2 repo/.git/logs/refs/stash >actual &&
+       cat >expect <<-EOF &&
+       $(test_oid zero) $new_oid
+       EOF
+       test_cmp expect actual
+'
+
 test_expect_success 'stash pop' '
        git reset --hard &&
        git stash pop &&
diff --git a/t/t4018/kotlin-class b/t/t4018/kotlin-class
new file mode 100644 (file)
index 0000000..bb864f2
--- /dev/null
@@ -0,0 +1,5 @@
+class RIGHT {
+       //comment
+       //comment
+       return ChangeMe
+}
diff --git a/t/t4018/kotlin-enum-class b/t/t4018/kotlin-enum-class
new file mode 100644 (file)
index 0000000..8885f90
--- /dev/null
@@ -0,0 +1,5 @@
+enum class RIGHT{
+       // Left
+       // a comment
+       ChangeMe
+}
diff --git a/t/t4018/kotlin-fun b/t/t4018/kotlin-fun
new file mode 100644 (file)
index 0000000..2a60280
--- /dev/null
@@ -0,0 +1,5 @@
+fun RIGHT(){
+       //a comment
+       //b comment
+    return ChangeMe()
+}
diff --git a/t/t4018/kotlin-inheritace-class b/t/t4018/kotlin-inheritace-class
new file mode 100644 (file)
index 0000000..77376c1
--- /dev/null
@@ -0,0 +1,5 @@
+open class RIGHT{
+       // a comment
+       // b comment
+       // ChangeMe
+}
diff --git a/t/t4018/kotlin-inline-class b/t/t4018/kotlin-inline-class
new file mode 100644 (file)
index 0000000..7bf46dd
--- /dev/null
@@ -0,0 +1,5 @@
+value class RIGHT(Args){
+       // a comment
+       // b comment
+       ChangeMe
+}
diff --git a/t/t4018/kotlin-interface b/t/t4018/kotlin-interface
new file mode 100644 (file)
index 0000000..f686ba7
--- /dev/null
@@ -0,0 +1,5 @@
+interface RIGHT{
+       //another comment
+       //another comment
+       //ChangeMe
+}
diff --git a/t/t4018/kotlin-nested-fun b/t/t4018/kotlin-nested-fun
new file mode 100644 (file)
index 0000000..1218685
--- /dev/null
@@ -0,0 +1,9 @@
+class LEFT{
+       class CENTER{
+               fun RIGHT(  a:Int){
+                       //comment
+                       //comment
+                       ChangeMe
+               }
+       }
+}
diff --git a/t/t4018/kotlin-public-class b/t/t4018/kotlin-public-class
new file mode 100644 (file)
index 0000000..9433fcc
--- /dev/null
@@ -0,0 +1,5 @@
+public class RIGHT{
+       //comment1
+       //comment2
+       ChangeMe
+}
diff --git a/t/t4018/kotlin-sealed-class b/t/t4018/kotlin-sealed-class
new file mode 100644 (file)
index 0000000..0efa4a4
--- /dev/null
@@ -0,0 +1,5 @@
+sealed class RIGHT {
+       // a comment
+       // b comment
+       ChangeMe
+}
index 54bb8ef27e7f0089a46ca48ecccf3998deb84a69..1219f8bd4c05f69ca159717c9b2e92f9510e76da 100755 (executable)
@@ -24,45 +24,38 @@ test_expect_success setup '
 '
 
 test_expect_success 'GIT_EXTERNAL_DIFF environment' '
-
-       GIT_EXTERNAL_DIFF=echo git diff | {
-               read path oldfile oldhex oldmode newfile newhex newmode &&
-               test "z$path" = zfile &&
-               test "z$oldmode" = z100644 &&
-               test "z$newhex" = "z$ZERO_OID" &&
-               test "z$newmode" = z100644 &&
-               oh=$(git rev-parse --verify HEAD:file) &&
-               test "z$oh" = "z$oldhex"
-       }
+       cat >expect <<-EOF &&
+       file $(git rev-parse --verify HEAD:file) 100644 file $(test_oid zero) 100644
+       EOF
+       GIT_EXTERNAL_DIFF=echo git diff >out &&
+       cut -d" " -f1,3- <out >actual &&
+       test_cmp expect actual
 
 '
 
-test_expect_success 'GIT_EXTERNAL_DIFF environment should apply only to diff' '
-
-       GIT_EXTERNAL_DIFF=echo git log -p -1 HEAD |
-       grep "^diff --git a/file b/file"
+test_expect_success !SANITIZE_LEAK 'GIT_EXTERNAL_DIFF environment should apply only to diff' '
+       GIT_EXTERNAL_DIFF=echo git log -p -1 HEAD >out &&
+       grep "^diff --git a/file b/file" out
 
 '
 
 test_expect_success 'GIT_EXTERNAL_DIFF environment and --no-ext-diff' '
-
-       GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff |
-       grep "^diff --git a/file b/file"
+       GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff >out &&
+       grep "^diff --git a/file b/file" out
 
 '
 
 test_expect_success SYMLINKS 'typechange diff' '
        rm -f file &&
        ln -s elif file &&
-       GIT_EXTERNAL_DIFF=echo git diff  | {
-               read path oldfile oldhex oldmode newfile newhex newmode &&
-               test "z$path" = zfile &&
-               test "z$oldmode" = z100644 &&
-               test "z$newhex" = "z$ZERO_OID" &&
-               test "z$newmode" = z120000 &&
-               oh=$(git rev-parse --verify HEAD:file) &&
-               test "z$oh" = "z$oldhex"
-       } &&
+
+       cat >expect <<-EOF &&
+       file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 120000
+       EOF
+       GIT_EXTERNAL_DIFF=echo git diff >out &&
+       cut -d" " -f1,3-4,6- <out >actual &&
+       test_cmp expect actual &&
+
        GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff >actual &&
        git diff >expect &&
        test_cmp expect actual
@@ -72,27 +65,25 @@ test_expect_success 'diff.external' '
        git reset --hard &&
        echo third >file &&
        test_config diff.external echo &&
-       git diff | {
-               read path oldfile oldhex oldmode newfile newhex newmode &&
-               test "z$path" = zfile &&
-               test "z$oldmode" = z100644 &&
-               test "z$newhex" = "z$ZERO_OID" &&
-               test "z$newmode" = z100644 &&
-               oh=$(git rev-parse --verify HEAD:file) &&
-               test "z$oh" = "z$oldhex"
-       }
+
+       cat >expect <<-EOF &&
+       file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 100644
+       EOF
+       git diff >out &&
+       cut -d" " -f1,3-4,6- <out >actual &&
+       test_cmp expect actual
 '
 
-test_expect_success 'diff.external should apply only to diff' '
+test_expect_success !SANITIZE_LEAK 'diff.external should apply only to diff' '
        test_config diff.external echo &&
-       git log -p -1 HEAD |
-       grep "^diff --git a/file b/file"
+       git log -p -1 HEAD >out &&
+       grep "^diff --git a/file b/file" out
 '
 
 test_expect_success 'diff.external and --no-ext-diff' '
        test_config diff.external echo &&
-       git diff --no-ext-diff |
-       grep "^diff --git a/file b/file"
+       git diff --no-ext-diff >out &&
+       grep "^diff --git a/file b/file" out
 '
 
 test_expect_success 'diff attribute' '
@@ -103,29 +94,23 @@ test_expect_success 'diff attribute' '
 
        echo >.gitattributes "file diff=parrot" &&
 
-       git diff | {
-               read path oldfile oldhex oldmode newfile newhex newmode &&
-               test "z$path" = zfile &&
-               test "z$oldmode" = z100644 &&
-               test "z$newhex" = "z$ZERO_OID" &&
-               test "z$newmode" = z100644 &&
-               oh=$(git rev-parse --verify HEAD:file) &&
-               test "z$oh" = "z$oldhex"
-       }
-
+       cat >expect <<-EOF &&
+       file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 100644
+       EOF
+       git diff >out &&
+       cut -d" " -f1,3-4,6- <out >actual &&
+       test_cmp expect actual
 '
 
-test_expect_success 'diff attribute should apply only to diff' '
-
-       git log -p -1 HEAD |
-       grep "^diff --git a/file b/file"
+test_expect_success !SANITIZE_LEAK 'diff attribute should apply only to diff' '
+       git log -p -1 HEAD >out &&
+       grep "^diff --git a/file b/file" out
 
 '
 
 test_expect_success 'diff attribute and --no-ext-diff' '
-
-       git diff --no-ext-diff |
-       grep "^diff --git a/file b/file"
+       git diff --no-ext-diff >out &&
+       grep "^diff --git a/file b/file" out
 
 '
 
@@ -136,48 +121,55 @@ test_expect_success 'diff attribute' '
 
        echo >.gitattributes "file diff=color" &&
 
-       git diff | {
-               read path oldfile oldhex oldmode newfile newhex newmode &&
-               test "z$path" = zfile &&
-               test "z$oldmode" = z100644 &&
-               test "z$newhex" = "z$ZERO_OID" &&
-               test "z$newmode" = z100644 &&
-               oh=$(git rev-parse --verify HEAD:file) &&
-               test "z$oh" = "z$oldhex"
-       }
-
+       cat >expect <<-EOF &&
+       file $(git rev-parse --verify HEAD:file) 100644 $(test_oid zero) 100644
+       EOF
+       git diff >out &&
+       cut -d" " -f1,3-4,6- <out >actual &&
+       test_cmp expect actual
 '
 
-test_expect_success 'diff attribute should apply only to diff' '
-
-       git log -p -1 HEAD |
-       grep "^diff --git a/file b/file"
+test_expect_success !SANITIZE_LEAK 'diff attribute should apply only to diff' '
+       git log -p -1 HEAD >out &&
+       grep "^diff --git a/file b/file" out
 
 '
 
 test_expect_success 'diff attribute and --no-ext-diff' '
-
-       git diff --no-ext-diff |
-       grep "^diff --git a/file b/file"
+       git diff --no-ext-diff >out &&
+       grep "^diff --git a/file b/file" out
 
 '
 
 test_expect_success 'GIT_EXTERNAL_DIFF trumps diff.external' '
        >.gitattributes &&
        test_config diff.external "echo ext-global" &&
-       GIT_EXTERNAL_DIFF="echo ext-env" git diff | grep ext-env
+
+       cat >expect <<-EOF &&
+       ext-env file $(git rev-parse --verify HEAD:file) 100644 file $(test_oid zero) 100644
+       EOF
+       GIT_EXTERNAL_DIFF="echo ext-env" git diff >out &&
+       cut -d" " -f1-2,4- <out >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'attributes trump GIT_EXTERNAL_DIFF and diff.external' '
        test_config diff.foo.command "echo ext-attribute" &&
        test_config diff.external "echo ext-global" &&
        echo "file diff=foo" >.gitattributes &&
-       GIT_EXTERNAL_DIFF="echo ext-env" git diff | grep ext-attribute
+
+       cat >expect <<-EOF &&
+       ext-attribute file $(git rev-parse --verify HEAD:file) 100644 file $(test_oid zero) 100644
+       EOF
+       GIT_EXTERNAL_DIFF="echo ext-env" git diff >out &&
+       cut -d" " -f1-2,4- <out >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'no diff with -diff' '
        echo >.gitattributes "file -diff" &&
-       git diff | grep Binary
+       git diff >out &&
+       grep Binary out
 '
 
 echo NULZbetweenZwords | perl -pe 'y/Z/\000/' > file
@@ -217,7 +209,12 @@ test_expect_success 'GIT_EXTERNAL_DIFF generates pretty paths' '
        touch file.ext &&
        git add file.ext &&
        echo with extension > file.ext &&
-       GIT_EXTERNAL_DIFF=echo git diff file.ext | grep ......_file\.ext &&
+
+       cat >expect <<-EOF &&
+       file.ext file $(git rev-parse --verify HEAD:file) 100644 file.ext $(test_oid zero) 100644
+       EOF
+       GIT_EXTERNAL_DIFF=echo git diff file.ext >out &&
+       cut -d" " -f1,3- <out >actual &&
        git update-index --force-remove file.ext &&
        rm file.ext
 '
index 6cef0da982faa1fbb86696ca0275eb49c8224090..295da987cce5c8141b96d35944b73fb3f86f7e64 100755 (executable)
@@ -2,7 +2,6 @@
 
 test_description='difference in submodules'
 
-TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
@@ -28,10 +27,8 @@ test_expect_success setup '
                git commit -m "submodule #2"
        ) &&
 
-       set x $(
-               cd sub &&
-               git rev-list HEAD
-       ) &&
+       git -C sub rev-list HEAD >revs &&
+       set x $(cat revs) &&
        echo ":160000 160000 $3 $ZERO_OID M     sub" >expect &&
        subtip=$3 subprev=$2
 '
index d5abcf4b4c6fa9f828b635dccbea37eae3ed4eb7..15764ee9ac8ab7984e57473a121be595c2e9b0cc 100755 (executable)
@@ -324,6 +324,7 @@ test_language_driver dts
 test_language_driver fortran
 test_language_driver html
 test_language_driver java
+test_language_driver kotlin
 test_language_driver matlab
 test_language_driver objc
 test_language_driver pascal
diff --git a/t/t4034/kotlin/expect b/t/t4034/kotlin/expect
new file mode 100644 (file)
index 0000000..7f76f75
--- /dev/null
@@ -0,0 +1,43 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 11ea3de..2e1df4c 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,30 +1,30 @@<RESET>
+println("Hello World<RED>!\n<RESET><GREEN>?<RESET>")
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a a<RESET><GREEN>x x<RESET>.inv() <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET><GREEN>y<RESET>
+a <RED>shr<RESET><GREEN>shl<RESET> b
+<RED>a<RESET><GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b a<RESET><GREEN>y x<RESET>===<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET> and <RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET> or <RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b a<RESET><GREEN>y x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET><GREEN>y<RESET>
+a<RED>=<RESET><GREEN>+=<RESET>b c<RED>+=<RESET><GREEN>=<RESET>d e<RED>-=<RESET><GREEN><=<RESET>f g<RED>*=<RESET><GREEN>>=<RESET>h i<RED>/=<RESET><GREEN>/<RESET>j k<RED>%=<RESET><GREEN>%<RESET>l m<RED><<=<RESET><GREEN><<<RESET>n o<RED>>>=<RESET><GREEN>>><RESET>p q<RED>&=<RESET><GREEN>&<RESET>r s<RED>^=<RESET><GREEN>^<RESET>t u<RED>|=<RESET><GREEN>|<RESET>v
+a<RED><<=<RESET><GREEN><=<RESET>b
+a<RED>||<RESET><GREEN>|<RESET>b a<RED>&&<RESET><GREEN>&<RESET>b
+<RED>a<RESET><GREEN>x<RESET>,y
+--a<RED>==<RESET><GREEN>!=<RESET>--b
+a++<RED>==<RESET><GREEN>!=<RESET>++b
+<RED>0xFF_EC_DE_5E 0b100_000 100_000<RESET><GREEN>0xFF_E1_DE_5E 0b100_100 200_000<RESET>
+a<RED>==<RESET><GREEN>===<RESET>b
+a<RED>!!<RESET><GREEN>!=<RESET>b
+<RED>_32<RESET><GREEN>_33<RESET>.find(arr)
+X.<RED>fill<RESET><GREEN>find<RESET>()
+X.<RED>u<RESET><GREEN>f<RESET>+1
+X.u<RED>-<RESET><GREEN>+<RESET>2
+a<RED>.<RESET><GREEN>..<RESET>b
+a<RED>?.<RESET><GREEN>?:<RESET>b
+<RED>.32_00_456<RESET><GREEN>.32_00_446<RESET>
diff --git a/t/t4034/kotlin/post b/t/t4034/kotlin/post
new file mode 100644 (file)
index 0000000..2e1df4c
--- /dev/null
@@ -0,0 +1,30 @@
+println("Hello World?")
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x x.inv() x*y x&y
+x*y x/y x%y
+x+y x-y
+a shl b
+x<y x<=y x>y x>=y
+x==y x!=y x===y
+x and y
+x^y
+x or y
+x&&y x||y
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+a+=b c=d e<=f g>=h i/j k%l m<<n o>>p q&r s^t u|v
+a<=b
+a|b a&b
+x,y
+--a!=--b
+a++!=++b
+0xFF_E1_DE_5E 0b100_100 200_000
+a===b
+a!=b
+_33.find(arr)
+X.find()
+X.f+1
+X.u+2
+a..b
+a?:b
+.32_00_446
diff --git a/t/t4034/kotlin/pre b/t/t4034/kotlin/pre
new file mode 100644 (file)
index 0000000..11ea3de
--- /dev/null
@@ -0,0 +1,30 @@
+println("Hello World!\n")
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a a.inv() a*b a&b
+a*b a/b a%b
+a+b a-b
+a shr b
+a<b a<=b a>b a>=b
+a==b a!=b a===b
+a and b
+a^b
+a or b
+a&&b a||b
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a=b c+=d e-=f g*=h i/=j k%=l m<<=n o>>=p q&=r s^=t u|=v
+a<<=b
+a||b a&&b
+a,y
+--a==--b
+a++==++b
+0xFF_EC_DE_5E 0b100_000 100_000
+a==b
+a!!b
+_32.find(arr)
+X.fill()
+X.u+1
+X.u-2
+a.b
+a?.b
+.32_00_456
index dfa053ff28e8cbeb9ae311732871811777a8a18c..3ef84619f53e27ddc164d74c07c0a38721dedfdf 100755 (executable)
@@ -2,8 +2,6 @@
 
 test_description='apply a patch that is larger than the preimage'
 
-
-TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 cat >F  <<\EOF
@@ -41,20 +39,8 @@ test_expect_success setup '
 '
 
 test_expect_success 'apply should fail gracefully' '
-
-       if git apply --index patch
-       then
-               echo Oops, should not have succeeded
-               false
-       else
-               status=$? &&
-               echo "Status was $status" &&
-               if test -f .git/index.lock
-               then
-                       echo Oops, should not have crashed
-                       false
-               fi
-       fi
+       test_must_fail git apply --index patch &&
+       test_path_is_missing .git/index.lock
 '
 
 test_done
index cb3181e8b71a8e25586467c20086063b33ec36d0..f6db5a79dd9d356c93a935374c4c27520ecf6593 100755 (executable)
@@ -2,8 +2,6 @@
 
 test_description='apply same filename'
 
-
-TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -26,10 +24,11 @@ diff a/bla/blub/dir/file b/bla/blub/dir/file
 EOF
 
 test_expect_success 'apply --directory -p (1)' '
-
        git apply --directory=some/sub -p3 --index patch &&
-       test Bello = $(git show :some/sub/dir/file) &&
-       test Bello = $(cat some/sub/dir/file)
+       echo Bello >expect &&
+       git show :some/sub/dir/file >actual &&
+       test_cmp expect actual &&
+       test_cmp expect some/sub/dir/file
 
 '
 
@@ -37,8 +36,10 @@ test_expect_success 'apply --directory -p (2) ' '
 
        git reset --hard initial &&
        git apply --directory=some/sub/ -p3 --index patch &&
-       test Bello = $(git show :some/sub/dir/file) &&
-       test Bello = $(cat some/sub/dir/file)
+       echo Bello >expect &&
+       git show :some/sub/dir/file >actual &&
+       test_cmp expect actual &&
+       test_cmp expect some/sub/dir/file
 
 '
 
@@ -55,8 +56,10 @@ EOF
 test_expect_success 'apply --directory (new file)' '
        git reset --hard initial &&
        git apply --directory=some/sub/dir/ --index patch &&
-       test content = $(git show :some/sub/dir/newfile) &&
-       test content = $(cat some/sub/dir/newfile)
+       echo content >expect &&
+       git show :some/sub/dir/newfile >actual &&
+       test_cmp expect actual &&
+       test_cmp expect some/sub/dir/newfile
 '
 
 cat > patch << EOF
@@ -72,8 +75,10 @@ EOF
 test_expect_success 'apply --directory -p (new file)' '
        git reset --hard initial &&
        git apply -p2 --directory=some/sub/dir/ --index patch &&
-       test content = $(git show :some/sub/dir/newfile2) &&
-       test content = $(cat some/sub/dir/newfile2)
+       echo content >expect &&
+       git show :some/sub/dir/newfile2 >actual &&
+       test_cmp expect actual &&
+       test_cmp expect some/sub/dir/newfile2
 '
 
 cat > patch << EOF
@@ -91,7 +96,8 @@ test_expect_success 'apply --directory (delete file)' '
        echo content >some/sub/dir/delfile &&
        git add some/sub/dir/delfile &&
        git apply --directory=some/sub/dir/ --index patch &&
-       ! (git ls-files | grep delfile)
+       git ls-files >out &&
+       ! grep delfile out
 '
 
 cat > patch << 'EOF'
@@ -107,8 +113,10 @@ EOF
 test_expect_success 'apply --directory (quoted filename)' '
        git reset --hard initial &&
        git apply --directory=some/sub/dir/ --index patch &&
-       test content = $(git show :some/sub/dir/quotefile) &&
-       test content = $(cat some/sub/dir/quotefile)
+       echo content >expect &&
+       git show :some/sub/dir/quotefile >actual &&
+       test_cmp expect actual &&
+       test_cmp expect some/sub/dir/quotefile
 '
 
 test_done
index 55fac644464f39b0534d8c0a64aaabb9741f696d..be07407f855597ca276deccfbc17c3b692aa0642 100755 (executable)
@@ -484,7 +484,6 @@ do
                )
        '
 done
-test_done
 
 test_expect_success 'log --author' '
        cat >expect <<-\EOF &&
@@ -2037,7 +2036,8 @@ test_expect_success GPGSM 'log --graph --show-signature for merged tag x509 miss
        git merge --no-ff -m msg signed_tag_x509_nokey &&
        GNUPGHOME=. git log --graph --show-signature -n1 plain-x509-nokey >actual &&
        grep "^|\\\  merged tag" actual &&
-       grep "^| | gpgsm: certificate not found" actual
+       grep -e "^| | gpgsm: certificate not found" \
+            -e "^| | gpgsm: failed to find the certificate: Not found" actual
 '
 
 test_expect_success GPGSM 'log --graph --show-signature for merged tag x509 bad signature' '
index cc3cebf672242a622e86b388ada63c9e0c1a9c04..fa9d32facfb0ddca6c001d78c1011cf0b3568f19 100755 (executable)
@@ -48,6 +48,7 @@ graph_read_expect () {
        header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
        num_commits: $1
        chunks: oid_fanout oid_lookup commit_metadata generation_data bloom_indexes bloom_data
+       options: bloom(1,10,7) read_generation_data
        EOF
        test-tool read-graph >actual &&
        test_cmp expect actual
index 2fd845187e76bd2ca7269ae45441c1495eae5839..a11d61206addc4deaf4e8887bb23a7e9fc83cdd7 100755 (executable)
@@ -315,8 +315,10 @@ test_expect_success \
      git index-pack -o tmp.idx test-3.pack &&
      cmp tmp.idx test-1-${packname_1}.idx &&
 
-     git index-pack test-3.pack &&
+     git index-pack --promisor=message test-3.pack &&
      cmp test-3.idx test-1-${packname_1}.idx &&
+     echo message >expect &&
+     test_cmp expect test-3.promisor &&
 
      cat test-2-${packname_2}.pack >test-3.pack &&
      git index-pack -o tmp.idx test-2-${packname_2}.pack &&
index edb728f77c3583cecde2d87555a4547464db7b96..fbf0d64578cde0544251020cf0df5ea6b90c4a2e 100755 (executable)
@@ -29,12 +29,7 @@ test_expect_success 'setup full repo' '
        cd "$TRASH_DIRECTORY/full" &&
        git init &&
        git config core.commitGraph true &&
-       objdir=".git/objects" &&
-
-       test_oid_cache <<-EOF
-       oid_version sha1:1
-       oid_version sha256:2
-       EOF
+       objdir=".git/objects"
 '
 
 test_expect_success POSIXPERM 'tweak umask for modebit tests' '
@@ -69,46 +64,10 @@ test_expect_success 'create commits and repack' '
        git repack
 '
 
-graph_git_two_modes() {
-       git -c core.commitGraph=true $1 >output &&
-       git -c core.commitGraph=false $1 >expect &&
-       test_cmp expect output
-}
-
-graph_git_behavior() {
-       MSG=$1
-       DIR=$2
-       BRANCH=$3
-       COMPARE=$4
-       test_expect_success "check normal git operations: $MSG" '
-               cd "$TRASH_DIRECTORY/$DIR" &&
-               graph_git_two_modes "log --oneline $BRANCH" &&
-               graph_git_two_modes "log --topo-order $BRANCH" &&
-               graph_git_two_modes "log --graph $COMPARE..$BRANCH" &&
-               graph_git_two_modes "branch -vv" &&
-               graph_git_two_modes "merge-base -a $BRANCH $COMPARE"
-       '
-}
+. "$TEST_DIRECTORY"/lib-commit-graph.sh
 
 graph_git_behavior 'no graph' full commits/3 commits/1
 
-graph_read_expect() {
-       OPTIONAL=""
-       NUM_CHUNKS=3
-       if test ! -z "$2"
-       then
-               OPTIONAL=" $2"
-               NUM_CHUNKS=$((3 + $(echo "$2" | wc -w)))
-       fi
-       cat >expect <<- EOF
-       header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
-       num_commits: $1
-       chunks: oid_fanout oid_lookup commit_metadata$OPTIONAL
-       EOF
-       test-tool read-graph >output &&
-       test_cmp expect output
-}
-
 test_expect_success 'exit with correct error on bad input to --stdin-commits' '
        cd "$TRASH_DIRECTORY/full" &&
        # invalid, non-hex OID
@@ -466,10 +425,10 @@ test_expect_success 'warn on improper hash version' '
        )
 '
 
-test_expect_success 'lower layers have overflow chunk' '
+test_expect_success TIME_IS_64BIT,TIME_T_IS_64BIT 'lower layers have overflow chunk' '
        cd "$TRASH_DIRECTORY/full" &&
        UNIX_EPOCH_ZERO="@0 +0000" &&
-       FUTURE_DATE="@2147483646 +0000" &&
+       FUTURE_DATE="@4147483646 +0000" &&
        rm -f .git/objects/info/commit-graph &&
        test_commit --date "$FUTURE_DATE" future-1 &&
        test_commit --date "$UNIX_EPOCH_ZERO" old-1 &&
@@ -497,7 +456,7 @@ test_expect_success 'git commit-graph verify' '
        cd "$TRASH_DIRECTORY/full" &&
        git rev-parse commits/8 | git -c commitGraph.generationVersion=1 commit-graph write --stdin-commits &&
        git commit-graph verify >output &&
-       graph_read_expect 9 extra_edges
+       graph_read_expect 9 extra_edges 1
 '
 
 NUM_COMMITS=9
@@ -825,10 +784,6 @@ test_expect_success 'set up and verify repo with generation data overflow chunk'
        objdir=".git/objects" &&
        UNIX_EPOCH_ZERO="@0 +0000" &&
        FUTURE_DATE="@2147483646 +0000" &&
-       test_oid_cache <<-EOF &&
-       oid_version sha1:1
-       oid_version sha256:2
-       EOF
        cd "$TRASH_DIRECTORY" &&
        mkdir repo &&
        cd repo &&
index 847b8097109cef7102c1e0709bf1c97a0457988c..669ddc645faccfc402b04044ed75676592e64356 100755 (executable)
@@ -30,10 +30,16 @@ graph_read_expect() {
        then
                NUM_BASE=$2
        fi
+       OPTIONS=
+       if test -z "$3"
+       then
+               OPTIONS=" read_generation_data"
+       fi
        cat >expect <<- EOF
        header: 43475048 1 $(test_oid oid_version) 4 $NUM_BASE
        num_commits: $1
        chunks: oid_fanout oid_lookup commit_metadata generation_data
+       options:$OPTIONS
        EOF
        test-tool read-graph >output &&
        test_cmp expect output
@@ -508,6 +514,7 @@ test_expect_success 'setup repo for mixed generation commit-graph-chain' '
                header: 43475048 1 $(test_oid oid_version) 4 1
                num_commits: $NUM_SECOND_LAYER_COMMITS
                chunks: oid_fanout oid_lookup commit_metadata
+               options:
                EOF
                test_cmp expect output &&
                git commit-graph verify &&
@@ -540,6 +547,7 @@ test_expect_success 'do not write generation data chunk if not present on existi
                header: 43475048 1 $(test_oid oid_version) 4 2
                num_commits: $NUM_THIRD_LAYER_COMMITS
                chunks: oid_fanout oid_lookup commit_metadata
+               options:
                EOF
                test_cmp expect output &&
                git commit-graph verify
@@ -581,6 +589,7 @@ test_expect_success 'do not write generation data chunk if the topmost remaining
                header: 43475048 1 $(test_oid oid_version) 4 2
                num_commits: $(($NUM_THIRD_LAYER_COMMITS + $NUM_FOURTH_LAYER_COMMITS))
                chunks: oid_fanout oid_lookup commit_metadata
+               options:
                EOF
                test_cmp expect output &&
                git commit-graph verify
@@ -620,6 +629,7 @@ test_expect_success 'write generation data chunk if topmost remaining layer has
                header: 43475048 1 $(test_oid oid_version) 5 1
                num_commits: $(($NUM_SECOND_LAYER_COMMITS + $NUM_THIRD_LAYER_COMMITS + $NUM_FOURTH_LAYER_COMMITS + $NUM_FIFTH_LAYER_COMMITS))
                chunks: oid_fanout oid_lookup commit_metadata generation_data
+               options: read_generation_data
                EOF
                test_cmp expect output
        )
diff --git a/t/t5328-commit-graph-64bit-time.sh b/t/t5328-commit-graph-64bit-time.sh
new file mode 100755 (executable)
index 0000000..093f0c0
--- /dev/null
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+test_description='commit graph with 64-bit timestamps'
+. ./test-lib.sh
+
+if ! test_have_prereq TIME_IS_64BIT || ! test_have_prereq TIME_T_IS_64BIT
+then
+       skip_all='skipping 64-bit timestamp tests'
+       test_done
+fi
+
+. "$TEST_DIRECTORY"/lib-commit-graph.sh
+
+UNIX_EPOCH_ZERO="@0 +0000"
+FUTURE_DATE="@4147483646 +0000"
+
+GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=0
+
+test_expect_success 'lower layers have overflow chunk' '
+       rm -f .git/objects/info/commit-graph &&
+       test_commit --date "$FUTURE_DATE" future-1 &&
+       test_commit --date "$UNIX_EPOCH_ZERO" old-1 &&
+       git commit-graph write --reachable &&
+       test_commit --date "$FUTURE_DATE" future-2 &&
+       test_commit --date "$UNIX_EPOCH_ZERO" old-2 &&
+       git commit-graph write --reachable --split=no-merge &&
+       test_commit extra &&
+       git commit-graph write --reachable --split=no-merge &&
+       git commit-graph write --reachable &&
+       graph_read_expect 5 "generation_data generation_data_overflow" &&
+       mv .git/objects/info/commit-graph commit-graph-upgraded &&
+       git commit-graph write --reachable &&
+       graph_read_expect 5 "generation_data generation_data_overflow" &&
+       test_cmp .git/objects/info/commit-graph commit-graph-upgraded
+'
+
+graph_git_behavior 'overflow' '' HEAD~2 HEAD
+
+test_expect_success 'set up and verify repo with generation data overflow chunk' '
+       mkdir repo &&
+       cd repo &&
+       git init &&
+       test_commit --date "$UNIX_EPOCH_ZERO" 1 &&
+       test_commit 2 &&
+       test_commit --date "$UNIX_EPOCH_ZERO" 3 &&
+       git commit-graph write --reachable &&
+       graph_read_expect 3 generation_data &&
+       test_commit --date "$FUTURE_DATE" 4 &&
+       test_commit 5 &&
+       test_commit --date "$UNIX_EPOCH_ZERO" 6 &&
+       git branch left &&
+       git reset --hard 3 &&
+       test_commit 7 &&
+       test_commit --date "$FUTURE_DATE" 8 &&
+       test_commit 9 &&
+       git branch right &&
+       git reset --hard 3 &&
+       test_merge M left right &&
+       git commit-graph write --reachable &&
+       graph_read_expect 10 "generation_data generation_data_overflow" &&
+       git commit-graph verify
+'
+
+graph_git_behavior 'overflow 2' repo left right
+
+test_done
index 195fc64dd44ae74c1546698e111f1a19c07dbb04..a3c01014b7ee445d7e51c6735faea36f46833bfb 100755 (executable)
@@ -160,4 +160,68 @@ test_expect_success 'new clone fetch main and tags' '
        test_cmp expect actual
 '
 
+test_expect_success 'atomic fetch with failing backfill' '
+       git init clone3 &&
+
+       # We want to test whether a failure when backfilling tags correctly
+       # aborts the complete transaction when `--atomic` is passed: we should
+       # neither create the branch nor should we create the tag when either
+       # one of both fails to update correctly.
+       #
+       # To trigger failure we simply abort when backfilling a tag.
+       write_script clone3/.git/hooks/reference-transaction <<-\EOF &&
+               while read oldrev newrev reference
+               do
+                       if test "$reference" = refs/tags/tag1
+                       then
+                               exit 1
+                       fi
+               done
+       EOF
+
+       test_must_fail git -C clone3 fetch --atomic .. $B:refs/heads/something &&
+       test_must_fail git -C clone3 rev-parse --verify refs/heads/something &&
+       test_must_fail git -C clone3 rev-parse --verify refs/tags/tag2
+'
+
+test_expect_success 'atomic fetch with backfill should use single transaction' '
+       git init clone4 &&
+
+       # Fetching with the `--atomic` flag should update all references in a
+       # single transaction, including backfilled tags. We thus expect to see
+       # a single reference transaction for the created branch and tags.
+       cat >expected <<-EOF &&
+               prepared
+               $ZERO_OID $B refs/heads/something
+               $ZERO_OID $S refs/tags/tag2
+               $ZERO_OID $T refs/tags/tag1
+               committed
+               $ZERO_OID $B refs/heads/something
+               $ZERO_OID $S refs/tags/tag2
+               $ZERO_OID $T refs/tags/tag1
+       EOF
+
+       write_script clone4/.git/hooks/reference-transaction <<-\EOF &&
+               ( echo "$*" && cat ) >>actual
+       EOF
+
+       git -C clone4 fetch --atomic .. $B:refs/heads/something &&
+       test_cmp expected clone4/actual
+'
+
+test_expect_success 'backfill failure causes command to fail' '
+       git init clone5 &&
+
+       # Create a tag that is nested below the tag we are about to fetch via
+       # the backfill mechanism. This causes a D/F conflict when backfilling
+       # and should thus cause the command to fail.
+       empty_blob=$(git -C clone5 hash-object -w --stdin </dev/null) &&
+       git -C clone5 update-ref refs/tags/tag1/nested $empty_blob &&
+
+       test_must_fail git -C clone5 fetch .. $B:refs/heads/something &&
+       test $B = $(git -C clone5 rev-parse --verify refs/heads/something) &&
+       test $S = $(git -C clone5 rev-parse --verify tag2) &&
+       test_must_fail git -C clone5 rev-parse --verify tag1
+'
+
 test_done
index 9ab315424c4b71e1a431c511bddc02b9c0b4e49b..c90cf47acdb23489dbc06e76437372cb407bfa75 100755 (executable)
@@ -753,7 +753,9 @@ test_expect_success 'rename a remote' '
        (
                cd four &&
                git config branch.main.pushRemote origin &&
-               git remote rename origin upstream &&
+               GIT_TRACE2_EVENT=$(pwd)/trace \
+                       git remote rename --progress origin upstream &&
+               test_region progress "Renaming remote references" trace &&
                grep "pushRemote" .git/config &&
                test -z "$(git for-each-ref refs/remotes/origin)" &&
                test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/main" &&
index ef0da0a63b5c2a970d63aece5e95e3efd95d2023..48e14e2dab1dc38055370655237ade6ff2fc1bd9 100755 (executable)
@@ -343,6 +343,35 @@ test_expect_success 'fetch --atomic --append appends to FETCH_HEAD' '
        test_cmp expected atomic/.git/FETCH_HEAD
 '
 
+test_expect_success 'fetch --atomic --prune executes a single reference transaction only' '
+       test_when_finished "rm -rf \"$D\"/atomic" &&
+
+       cd "$D" &&
+       git branch scheduled-for-deletion &&
+       git clone . atomic &&
+       git branch -D scheduled-for-deletion &&
+       git branch new-branch &&
+       head_oid=$(git rev-parse HEAD) &&
+
+       # Fetching with the `--atomic` flag should update all references in a
+       # single transaction.
+       cat >expected <<-EOF &&
+               prepared
+               $ZERO_OID $ZERO_OID refs/remotes/origin/scheduled-for-deletion
+               $ZERO_OID $head_oid refs/remotes/origin/new-branch
+               committed
+               $ZERO_OID $ZERO_OID refs/remotes/origin/scheduled-for-deletion
+               $ZERO_OID $head_oid refs/remotes/origin/new-branch
+       EOF
+
+       write_script atomic/.git/hooks/reference-transaction <<-\EOF &&
+               ( echo "$*" && cat ) >>actual
+       EOF
+
+       git -C atomic fetch --atomic --prune origin &&
+       test_cmp expected atomic/actual
+'
+
 test_expect_success '--refmap="" ignores configured refspec' '
        cd "$TRASH_DIRECTORY" &&
        git clone "$D" remote-refs &&
index 86542c650e241976943ca58c5496cf03965c630e..e960049f647cfca4a18defaba7dde9d43c8f94ef 100755 (executable)
@@ -2,7 +2,6 @@
 
 test_description='git rev-list --max-count and --skip test'
 
-TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
@@ -14,39 +13,39 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'no options' '
-    test $(git rev-list HEAD | wc -l) = 5
+       test_stdout_line_count = 5 git rev-list HEAD
 '
 
 test_expect_success '--max-count' '
-    test $(git rev-list HEAD --max-count=0 | wc -l) = 0 &&
-    test $(git rev-list HEAD --max-count=3 | wc -l) = 3 &&
-    test $(git rev-list HEAD --max-count=5 | wc -l) = 5 &&
-    test $(git rev-list HEAD --max-count=10 | wc -l) = 5
+       test_stdout_line_count = 0 git rev-list HEAD --max-count=0 &&
+       test_stdout_line_count = 3 git rev-list HEAD --max-count=3 &&
+       test_stdout_line_count = 5 git rev-list HEAD --max-count=5 &&
+       test_stdout_line_count = 5 git rev-list HEAD --max-count=10
 '
 
 test_expect_success '--max-count all forms' '
-    test $(git rev-list HEAD --max-count=1 | wc -l) = 1 &&
-    test $(git rev-list HEAD -1 | wc -l) = 1 &&
-    test $(git rev-list HEAD -n1 | wc -l) = 1 &&
-    test $(git rev-list HEAD -n 1 | wc -l) = 1
+       test_stdout_line_count = 1 git rev-list HEAD --max-count=1 &&
+       test_stdout_line_count = 1 git rev-list HEAD -1 &&
+       test_stdout_line_count = 1 git rev-list HEAD -n1 &&
+       test_stdout_line_count = 1 git rev-list HEAD -n 1
 '
 
 test_expect_success '--skip' '
-    test $(git rev-list HEAD --skip=0 | wc -l) = 5 &&
-    test $(git rev-list HEAD --skip=3 | wc -l) = 2 &&
-    test $(git rev-list HEAD --skip=5 | wc -l) = 0 &&
-    test $(git rev-list HEAD --skip=10 | wc -l) = 0
+       test_stdout_line_count = 5 git rev-list HEAD --skip=0 &&
+       test_stdout_line_count = 2 git rev-list HEAD --skip=3 &&
+       test_stdout_line_count = 0 git rev-list HEAD --skip=5 &&
+       test_stdout_line_count = 0 git rev-list HEAD --skip=10
 '
 
 test_expect_success '--skip --max-count' '
-    test $(git rev-list HEAD --skip=0 --max-count=0 | wc -l) = 0 &&
-    test $(git rev-list HEAD --skip=0 --max-count=10 | wc -l) = 5 &&
-    test $(git rev-list HEAD --skip=3 --max-count=0 | wc -l) = 0 &&
-    test $(git rev-list HEAD --skip=3 --max-count=1 | wc -l) = 1 &&
-    test $(git rev-list HEAD --skip=3 --max-count=2 | wc -l) = 2 &&
-    test $(git rev-list HEAD --skip=3 --max-count=10 | wc -l) = 2 &&
-    test $(git rev-list HEAD --skip=5 --max-count=10 | wc -l) = 0 &&
-    test $(git rev-list HEAD --skip=10 --max-count=10 | wc -l) = 0
+       test_stdout_line_count = 0 git rev-list HEAD --skip=0 --max-count=0 &&
+       test_stdout_line_count = 5 git rev-list HEAD --skip=0 --max-count=10 &&
+       test_stdout_line_count = 0 git rev-list HEAD --skip=3 --max-count=0 &&
+       test_stdout_line_count = 1 git rev-list HEAD --skip=3 --max-count=1 &&
+       test_stdout_line_count = 2 git rev-list HEAD --skip=3 --max-count=2 &&
+       test_stdout_line_count = 2 git rev-list HEAD --skip=3 --max-count=10 &&
+       test_stdout_line_count = 0 git rev-list HEAD --skip=5 --max-count=10 &&
+       test_stdout_line_count = 0 git rev-list HEAD --skip=10 --max-count=10
 '
 
 test_done
index 63fcccec32e2467f7ece263195290a95232d0d50..de1e87f1621d5db8c3730662731fdd67e1dffba7 100755 (executable)
@@ -12,7 +12,9 @@ note () {
 }
 
 unnote () {
-       git name-rev --tags --annotate-stdin | sed -e "s|$OID_REGEX (tags/\([^)]*\)) |\1 |g"
+       test_when_finished "rm -f tmp" &&
+       git name-rev --tags --annotate-stdin >tmp &&
+       sed -e "s|$OID_REGEX (tags/\([^)]*\)) |\1 |g" <tmp
 }
 
 #
@@ -111,8 +113,8 @@ check_outcome () {
        shift &&
        param="$*" &&
        test_expect_$outcome "log $param" '
-               git log --pretty="$FMT" --parents $param |
-               unnote >actual &&
+               git log --pretty="$FMT" --parents $param >out &&
+               unnote >actual <out &&
                sed -e "s/^.*   \([^ ]*\) .*/\1/" >check <actual &&
                test_cmp expect check
        '
@@ -151,8 +153,8 @@ check_result 'L K I H G B' --exclude-first-parent-only --first-parent L ^F
 check_result 'E C B A' --full-history E -- lost
 test_expect_success 'full history simplification without parent' '
        printf "%s\n" E C B A >expect &&
-       git log --pretty="$FMT" --full-history E -- lost |
-       unnote >actual &&
+       git log --pretty="$FMT" --full-history E -- lost >out &&
+       unnote >actual <out &&
        sed -e "s/^.*   \([^ ]*\) .*/\1/" >check <actual &&
        test_cmp expect check
 '
index b13e8a52a93a4e96cde54c5076c19960a4d41a2c..ed95d195427114f61759ce27b677ee2dfbf15e70 100755 (executable)
@@ -475,4 +475,78 @@ test_expect_success 'clone from bundle' '
        test_cmp expect actual
 '
 
+test_expect_success 'unfiltered bundle with --objects' '
+       git bundle create all-objects.bdl \
+               --all --objects &&
+       git bundle create all.bdl \
+               --all &&
+
+       # Compare the headers of these files.
+       sed -n -e "/^$/q" -e "p" all.bdl >expect &&
+       sed -n -e "/^$/q" -e "p" all-objects.bdl >actual &&
+       test_cmp expect actual
+'
+
+for filter in "blob:none" "tree:0" "tree:1" "blob:limit=100"
+do
+       test_expect_success "filtered bundle: $filter" '
+               test_when_finished rm -rf .git/objects/pack cloned unbundled &&
+               git bundle create partial.bdl \
+                       --all \
+                       --filter=$filter &&
+
+               git bundle verify partial.bdl >unfiltered &&
+               make_user_friendly_and_stable_output <unfiltered >actual &&
+
+               cat >expect <<-EOF &&
+               The bundle contains these 10 refs:
+               <COMMIT-P> refs/heads/main
+               <COMMIT-N> refs/heads/release
+               <COMMIT-D> refs/heads/topic/1
+               <COMMIT-H> refs/heads/topic/2
+               <COMMIT-D> refs/pull/1/head
+               <COMMIT-G> refs/pull/2/head
+               <TAG-1> refs/tags/v1
+               <TAG-2> refs/tags/v2
+               <TAG-3> refs/tags/v3
+               <COMMIT-P> HEAD
+               The bundle uses this filter: $filter
+               The bundle records a complete history.
+               EOF
+               test_cmp expect actual &&
+
+               test_config uploadpack.allowfilter 1 &&
+               test_config uploadpack.allowanysha1inwant 1 &&
+               git clone --no-local --filter=$filter --bare "file://$(pwd)" cloned &&
+
+               git init unbundled &&
+               git -C unbundled bundle unbundle ../partial.bdl >ref-list.txt &&
+               ls unbundled/.git/objects/pack/pack-*.promisor >promisor &&
+               test_line_count = 1 promisor &&
+
+               # Count the same number of reachable objects.
+               reflist=$(git for-each-ref --format="%(objectname)") &&
+               git rev-list --objects --filter=$filter --missing=allow-any \
+                       $reflist >expect &&
+               for repo in cloned unbundled
+               do
+                       git -C $repo rev-list --objects --missing=allow-any \
+                               $reflist >actual &&
+                       test_cmp expect actual || return 1
+               done
+       '
+done
+
+# NEEDSWORK: 'git clone --bare' should be able to clone from a filtered
+# bundle, but that requires a change to promisor/filter config options.
+# For now, we fail gracefully with a helpful error. This behavior can be
+# changed in the future to succeed as much as possible.
+test_expect_success 'cloning from filtered bundle has useful error' '
+       git bundle create partial.bdl \
+               --all \
+               --filter=blob:none &&
+       test_must_fail git clone --bare partial.bdl partial 2>err &&
+       grep "cannot clone from filtered bundle" err
+'
+
 test_done
index 6f0902b86383191511116ee4fc5406e9dac6fb2e..cf0195e8263c6fdc25977f421252c41238f03419 100755 (executable)
@@ -17,8 +17,13 @@ test_expect_success 'setup unexpected non-blob entry' '
        broken_tree="$(git hash-object -w --literally -t tree broken-tree)"
 '
 
-test_expect_failure 'traverse unexpected non-blob entry (lone)' '
-       test_must_fail git rev-list --objects $broken_tree
+test_expect_success !SANITIZE_LEAK 'TODO (should fail!): traverse unexpected non-blob entry (lone)' '
+       sed "s/Z$//" >expect <<-EOF &&
+       $broken_tree Z
+       $tree foo
+       EOF
+       git rev-list --objects $broken_tree >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'traverse unexpected non-blob entry (seen)' '
@@ -116,8 +121,8 @@ test_expect_success 'setup unexpected non-blob tag' '
        tag=$(git hash-object -w --literally -t tag broken-tag)
 '
 
-test_expect_failure 'traverse unexpected non-blob tag (lone)' '
-       test_must_fail git rev-list --objects $tag
+test_expect_success !SANITIZE_LEAK 'TODO (should fail!): traverse unexpected non-blob tag (lone)' '
+       git rev-list --objects $tag
 '
 
 test_expect_success 'traverse unexpected non-blob tag (seen)' '
index 9781b92aeddfee9fbfbfba921a15edea51de50cd..9a35e783a757b6fe9800ce475e65e73b880708c7 100755 (executable)
@@ -488,6 +488,124 @@ test_expect_success 'name-rev covers all conditions while looking at parents' '
        )
 '
 
+# A-B-C-D-E-main
+#
+# Where C has a non-monotonically increasing commit timestamp w.r.t. other
+# commits
+test_expect_success 'non-monotonic commit dates setup' '
+       UNIX_EPOCH_ZERO="@0 +0000" &&
+       git init non-monotonic &&
+       test_commit -C non-monotonic A &&
+       test_commit -C non-monotonic --no-tag B &&
+       test_commit -C non-monotonic --no-tag --date "$UNIX_EPOCH_ZERO" C &&
+       test_commit -C non-monotonic D &&
+       test_commit -C non-monotonic E
+'
+
+test_expect_success 'name-rev with commitGraph handles non-monotonic timestamps' '
+       test_config -C non-monotonic core.commitGraph true &&
+       (
+               cd non-monotonic &&
+
+               git commit-graph write --reachable &&
+
+               echo "main~3 tags/D~2" >expect &&
+               git name-rev --tags main~3 >actual &&
+
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'name-rev --all works with non-monotonic timestamps' '
+       test_config -C non-monotonic core.commitGraph false &&
+       (
+               cd non-monotonic &&
+
+               rm -rf .git/info/commit-graph* &&
+
+               cat >tags <<-\EOF &&
+               tags/E
+               tags/D
+               tags/D~1
+               tags/D~2
+               tags/A
+               EOF
+
+               git log --pretty=%H >revs &&
+
+               paste -d" " revs tags | sort >expect &&
+
+               git name-rev --tags --all | sort >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'name-rev --annotate-stdin works with non-monotonic timestamps' '
+       test_config -C non-monotonic core.commitGraph false &&
+       (
+               cd non-monotonic &&
+
+               rm -rf .git/info/commit-graph* &&
+
+               cat >expect <<-\EOF &&
+               E
+               D
+               D~1
+               D~2
+               A
+               EOF
+
+               git log --pretty=%H >revs &&
+               git name-rev --tags --annotate-stdin --name-only <revs >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'name-rev --all works with commitGraph' '
+       test_config -C non-monotonic core.commitGraph true &&
+       (
+               cd non-monotonic &&
+
+               git commit-graph write --reachable &&
+
+               cat >tags <<-\EOF &&
+               tags/E
+               tags/D
+               tags/D~1
+               tags/D~2
+               tags/A
+               EOF
+
+               git log --pretty=%H >revs &&
+
+               paste -d" " revs tags | sort >expect &&
+
+               git name-rev --tags --all | sort >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'name-rev --annotate-stdin works with commitGraph' '
+       test_config -C non-monotonic core.commitGraph true &&
+       (
+               cd non-monotonic &&
+
+               git commit-graph write --reachable &&
+
+               cat >expect <<-\EOF &&
+               E
+               D
+               D~1
+               D~2
+               A
+               EOF
+
+               git log --pretty=%H >revs &&
+               git name-rev --tags --annotate-stdin --name-only <revs >actual &&
+               test_cmp expect actual
+       )
+'
+
 #               B
 #               o
 #                \
index 8e6241f92e6a6c10b8686900bf1ed61c2c96d4d3..0753fc95f45efb642543f9f23191d3430d4d6cde 100755 (executable)
@@ -43,14 +43,9 @@ test_expect_success resolve '
        rm -f a* m* &&
        git reset --hard anchor &&
 
-       if git merge -s resolve main
-       then
-               echo Oops, should not have succeeded
-               false
-       else
-               git ls-files -s >current &&
-               test_cmp expect current
-       fi
+       test_must_fail git merge -s resolve main &&
+       git ls-files -s >current &&
+       test_cmp expect current
 '
 
 test_expect_success recursive '
@@ -58,14 +53,9 @@ test_expect_success recursive '
        rm -f a* m* &&
        git reset --hard anchor &&
 
-       if git merge -s recursive main
-       then
-               echo Oops, should not have succeeded
-               false
-       else
-               git ls-files -s >current &&
-               test_cmp expect current
-       fi
+       test_must_fail git merge -s recursive main &&
+       git ls-files -s >current &&
+       test_cmp expect current
 '
 
 test_done
index 5b81a130e9c45086a8bb618d8b8eeed16324ad20..479db32cd629c1b118eb892ee541700fb9100afd 100755 (executable)
@@ -4421,14 +4421,14 @@ test_setup_12c1 () {
 
                git checkout A &&
                git mv node2/ node1/ &&
-               for i in `git ls-files`; do echo side A >>$i; done &&
+               for i in $(git ls-files); do echo side A >>$i; done &&
                git add -u &&
                test_tick &&
                git commit -m "A" &&
 
                git checkout B &&
                git mv node1/ node2/ &&
-               for i in `git ls-files`; do echo side B >>$i; done &&
+               for i in $(git ls-files); do echo side B >>$i; done &&
                git add -u &&
                test_tick &&
                git commit -m "B"
@@ -4511,7 +4511,7 @@ test_setup_12c2 () {
 
                git checkout A &&
                git mv node2/ node1/ &&
-               for i in `git ls-files`; do echo side A >>$i; done &&
+               for i in $(git ls-files); do echo side A >>$i; done &&
                git add -u &&
                echo leaf5 >node1/leaf5 &&
                git add node1/leaf5 &&
@@ -4520,7 +4520,7 @@ test_setup_12c2 () {
 
                git checkout B &&
                git mv node1/ node2/ &&
-               for i in `git ls-files`; do echo side B >>$i; done &&
+               for i in $(git ls-files); do echo side B >>$i; done &&
                git add -u &&
                echo leaf6 >node2/leaf6 &&
                git add node2/leaf6 &&
@@ -4759,7 +4759,7 @@ test_setup_12f () {
                echo g >dir/subdir/tweaked/g &&
                echo h >dir/subdir/tweaked/h &&
                test_seq 20 30 >dir/subdir/tweaked/Makefile &&
-               for i in `test_seq 1 88`; do
+               for i in $(test_seq 1 88); do
                        echo content $i >dir/unchanged/file_$i
                done &&
                git add . &&
index 7e8bf497f821da35e795fcc2d7b23ac0291c7106..142c9aaabc5309078e38cc01607f821520f965fc 100755 (executable)
@@ -112,7 +112,7 @@ test_expect_success 'conflicting entries written to worktree even if sparse' '
        )
 '
 
-test_expect_merge_algorithm failure success 'present-despite-SKIP_WORKTREE handled reasonably' '
+test_expect_success 'present-despite-SKIP_WORKTREE handled reasonably' '
        test_setup_numerals in_the_way &&
        (
                cd numerals_in_the_way &&
@@ -132,26 +132,13 @@ test_expect_merge_algorithm failure success 'present-despite-SKIP_WORKTREE handl
 
                test_must_fail git merge -s recursive B^0 &&
 
-               git ls-files -t >index_files &&
-               test_cmp expected-index index_files &&
+               test_path_is_missing .git/MERGE_HEAD &&
 
-               test_path_is_file README &&
                test_path_is_file numerals &&
 
-               test_cmp expected-merge numerals &&
-
-               # There should still be a file with "foobar" in it
-               grep foobar * &&
-
-               # 5 other files:
-               #   * expected-merge
-               #   * expected-index
-               #   * index_files
-               #   * others
-               #   * whatever name was given to the numerals file that had
-               #     "foobar" in it
-               git ls-files -o >others &&
-               test_line_count = 5 others
+               # numerals should still have "foobar" in it
+               echo foobar >expect &&
+               test_cmp expect numerals
        )
 '
 
index a1080b94e388aef309c958e9ca7d6150e2577b06..cb9f1a6981eb5f05dd662eac436a8ee6f7a17dee 100755 (executable)
@@ -171,50 +171,20 @@ test_expect_success 'stash restore in sparse checkout' '
 
                # Put a file in the working directory in the way
                echo in the way >modified &&
-               git stash apply &&
+               test_must_fail git stash apply 2>error&&
 
-               # Ensure stash vivifies modifies paths...
-               cat >expect <<-EOF &&
-               H addme
-               H modified
-               H removeme
-               H subdir/A
-               S untouched
-               EOF
-               git ls-files -t >actual &&
-               test_cmp expect actual &&
+               grep "changes.*would be overwritten by merge" error &&
 
-               # ...and that the paths show up in status as changed...
-               cat >expect <<-EOF &&
-               A  addme
-                M modified
-                D removeme
-                M subdir/A
-               ?? actual
-               ?? expect
-               ?? modified.stash.XXXXXX
-               EOF
-               git status --porcelain | \
-                       sed -e s/stash......./stash.XXXXXX/ >actual &&
-               test_cmp expect actual &&
+               echo in the way >expect &&
+               test_cmp expect modified &&
+               git diff --quiet HEAD ":!modified" &&
 
                # ...and that working directory reflects the files correctly
-               test_path_is_file    addme &&
+               test_path_is_missing addme &&
                test_path_is_file    modified &&
                test_path_is_missing removeme &&
                test_path_is_file    subdir/A &&
-               test_path_is_missing untouched &&
-
-               # ...including that we have the expected "modified" file...
-               cat >expect <<-EOF &&
-               modified
-               tweaked
-               EOF
-               test_cmp expect modified &&
-
-               # ...and that the other "modified" file is still present...
-               echo in the way >expect &&
-               test_cmp expect modified.stash.*
+               test_path_is_missing untouched
        )
 '
 
index a0c123b0a77a6f99877d4e5cd2fd74cb78e0b96c..ca90ee805e7b3411c6e7c8c5868c3aa2063dba05 100755 (executable)
@@ -90,6 +90,9 @@ test_expect_success 'setup' '
        cd worktree &&
        mkdir done dtwo dthree &&
        touch one two three done/one dtwo/two dthree/three &&
+       test-tool chmtime =-300 one two three done/one dtwo/two dthree/three &&
+       test-tool chmtime =-300 done dtwo dthree &&
+       test-tool chmtime =-300 . &&
        git add one two done/one &&
        : >.git/info/exclude &&
        git update-index --untracked-cache &&
@@ -142,7 +145,6 @@ two
 EOF
 
 test_expect_success 'status first time (empty cache)' '
-       avoid_racy &&
        : >../trace.output &&
        GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
        git status --porcelain >../actual &&
@@ -166,7 +168,6 @@ test_expect_success 'untracked cache after first status' '
 '
 
 test_expect_success 'status second time (fully populated cache)' '
-       avoid_racy &&
        : >../trace.output &&
        GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
        git status --porcelain >../actual &&
@@ -190,8 +191,8 @@ test_expect_success 'untracked cache after second status' '
 '
 
 test_expect_success 'modify in root directory, one dir invalidation' '
-       avoid_racy &&
        : >four &&
+       test-tool chmtime =-240 four &&
        : >../trace.output &&
        GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
        git status --porcelain >../actual &&
@@ -241,7 +242,6 @@ EOF
 '
 
 test_expect_success 'new .gitignore invalidates recursively' '
-       avoid_racy &&
        echo four >.gitignore &&
        : >../trace.output &&
        GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
@@ -292,7 +292,6 @@ EOF
 '
 
 test_expect_success 'new info/exclude invalidates everything' '
-       avoid_racy &&
        echo three >>.git/info/exclude &&
        : >../trace.output &&
        GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
@@ -520,14 +519,14 @@ test_expect_success 'create/modify files, some of which are gitignored' '
        echo three >done/three && # three is gitignored
        echo four >done/four && # four is gitignored at a higher level
        echo five >done/five && # five is not gitignored
-       echo test >base && #we need to ensure that the root dir is touched
-       rm base &&
+       test-tool chmtime =-180 done/two done/three done/four done/five done &&
+       # we need to ensure that the root dir is touched (in the past);
+       test-tool chmtime =-180 . &&
        sync_mtime
 '
 
 test_expect_success 'test sparse status with untracked cache' '
        : >../trace.output &&
-       avoid_racy &&
        GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
        git status --porcelain >../status.actual &&
        iuc status --porcelain >../status.iuc &&
@@ -570,7 +569,6 @@ EOF
 '
 
 test_expect_success 'test sparse status again with untracked cache' '
-       avoid_racy &&
        : >../trace.output &&
        GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
        git status --porcelain >../status.actual &&
@@ -597,11 +595,11 @@ EOF
 test_expect_success 'set up for test of subdir and sparse checkouts' '
        mkdir done/sub &&
        mkdir done/sub/sub &&
-       echo "sub" > done/sub/sub/file
+       echo "sub" > done/sub/sub/file &&
+       test-tool chmtime =-120 done/sub/sub/file done/sub/sub done/sub done
 '
 
 test_expect_success 'test sparse status with untracked cache and subdir' '
-       avoid_racy &&
        : >../trace.output &&
        GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
        git status --porcelain >../status.actual &&
@@ -651,7 +649,6 @@ EOF
 '
 
 test_expect_success 'test sparse status again with untracked cache and subdir' '
-       avoid_racy &&
        : >../trace.output &&
        GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.output" \
        git status --porcelain >../status.actual &&
index 0de83e36199ec44eec0ea1ca568c730da759d5d3..a60153f9f32318c5ee31d2b16ec506a7a59ddd6d 100755 (executable)
@@ -63,9 +63,12 @@ test_expect_success '"mixed" reset is not allowed in bare' '
        test_must_fail git reset --mixed HEAD^
 '
 
-test_expect_success '"soft" reset is allowed in bare' '
+test_expect_success !SANITIZE_LEAK '"soft" reset is allowed in bare' '
        git reset --soft HEAD^ &&
-       test "$(git show --pretty=format:%s | head -n 1)" = "one"
+       git show --pretty=format:%s >out &&
+       echo one >expect &&
+       head -n 1 out >actual &&
+       test_cmp expect actual
 '
 
 test_done
index 11cccbb333b127436aebea313e0270acea812e87..000e055811c67f2d7cfe89b50ce8fa0aee1af61d 100755 (executable)
@@ -205,8 +205,18 @@ test_expect_success 'submodule update should fail due to local changes' '
         (cd submodule &&
          compare_head
         ) &&
-        test_must_fail git submodule update submodule
-       )
+        test_must_fail git submodule update submodule 2>../actual.raw
+       ) &&
+       sed "s/^> //" >expect <<-\EOF &&
+       > error: Your local changes to the following files would be overwritten by checkout:
+       >       file
+       > Please commit your changes or stash them before you switch branches.
+       > Aborting
+       > fatal: Unable to checkout OID in submodule path '\''submodule'\''
+       EOF
+       sed -e "s/checkout $SQ[^$SQ]*$SQ/checkout OID/" <actual.raw >actual &&
+       test_cmp expect actual
+
 '
 test_expect_success 'submodule update should throw away changes with --force ' '
        (cd super &&
@@ -1061,4 +1071,16 @@ test_expect_success 'submodule update --quiet passes quietness to fetch with a s
        )
 '
 
+test_expect_success 'submodule update --filter requires --init' '
+       test_expect_code 129 git -C super submodule update --filter blob:none
+'
+
+test_expect_success 'submodule update --filter sets partial clone settings' '
+       test_when_finished "rm -rf super-filter" &&
+       git clone cloned super-filter &&
+       git -C super-filter submodule update --init --filter blob:none &&
+       test_cmp_config -C super-filter/submodule true remote.origin.promisor &&
+       test_cmp_config -C super-filter/submodule blob:none remote.origin.partialclonefilter
+'
+
 test_done
index a3892f494b68c866b150fdead376942bff47541b..c3a45455106f0961d7d80813cff55988caedd9bc 100755 (executable)
@@ -193,7 +193,19 @@ test_expect_success 'missing nested submodule alternate fails clone and submodul
                cd supersuper-clone &&
                check_that_two_of_three_alternates_are_used &&
                # update of the submodule fails
-               test_must_fail git submodule update --init --recursive
+               cat >expect <<-\EOF &&
+               fatal: submodule '\''sub'\'' cannot add alternate: path ... does not exist
+               Failed to clone '\''sub'\''. Retry scheduled
+               fatal: submodule '\''sub-dissociate'\'' cannot add alternate: path ... does not exist
+               Failed to clone '\''sub-dissociate'\''. Retry scheduled
+               fatal: submodule '\''sub'\'' cannot add alternate: path ... does not exist
+               Failed to clone '\''sub'\'' a second time, aborting
+               fatal: Failed to recurse into submodule path ...
+               EOF
+               test_must_fail git submodule update --init --recursive 2>err &&
+               grep -e fatal: -e ^Failed err >actual.raw &&
+               sed -e "s/path $SQ[^$SQ]*$SQ/path .../" <actual.raw >actual &&
+               test_cmp expect actual
        )
 '
 
index a6308acf006c9e4d35e47578465e348bb213356f..fffc57120d651fa530bd4c8d1ba54a85fdedf396 100755 (executable)
@@ -324,17 +324,24 @@ test_expect_success UNTRACKED_CACHE 'ignore .git changes when invalidating UNTR'
                cd dot-git &&
                mkdir -p .git/hooks &&
                : >tracked &&
+               test-tool chmtime =-60 tracked &&
                : >modified &&
+               test-tool chmtime =-60 modified &&
                mkdir dir1 &&
                : >dir1/tracked &&
+               test-tool chmtime =-60 dir1/tracked &&
                : >dir1/modified &&
+               test-tool chmtime =-60 dir1/modified &&
                mkdir dir2 &&
                : >dir2/tracked &&
+               test-tool chmtime =-60 dir2/tracked &&
                : >dir2/modified &&
+               test-tool chmtime =-60 dir2/modified &&
                write_integration_script &&
                git config core.fsmonitor .git/hooks/fsmonitor-test &&
                git update-index --untracked-cache &&
                git update-index --fsmonitor &&
+               git status &&
                GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace-before" \
                git status &&
                test-tool dump-untracked-cache >../before
index 5922fb5bdd6459b2bb2f8e1e880d6cd55ffee82b..770d1432046b1fd254b7311fb128774b595e9b36 100755 (executable)
@@ -381,4 +381,54 @@ test_expect_success TTY '--quiet disables progress' '
        test_must_be_empty stderr
 '
 
+test_expect_success 'setup for update-server-info' '
+       git init update-server-info &&
+       test_commit -C update-server-info message
+'
+
+test_server_info_present () {
+       test_path_is_file update-server-info/.git/objects/info/packs &&
+       test_path_is_file update-server-info/.git/info/refs
+}
+
+test_server_info_missing () {
+       test_path_is_missing update-server-info/.git/objects/info/packs &&
+       test_path_is_missing update-server-info/.git/info/refs
+}
+
+test_server_info_cleanup () {
+       rm -f update-server-info/.git/objects/info/packs update-server-info/.git/info/refs &&
+       test_server_info_missing
+}
+
+test_expect_success 'updates server info by default' '
+       test_server_info_cleanup &&
+       git -C update-server-info repack &&
+       test_server_info_present
+'
+
+test_expect_success '-n skips updating server info' '
+       test_server_info_cleanup &&
+       git -C update-server-info repack -n &&
+       test_server_info_missing
+'
+
+test_expect_success 'repack.updateServerInfo=true updates server info' '
+       test_server_info_cleanup &&
+       git -C update-server-info -c repack.updateServerInfo=true repack &&
+       test_server_info_present
+'
+
+test_expect_success 'repack.updateServerInfo=false skips updating server info' '
+       test_server_info_cleanup &&
+       git -C update-server-info -c repack.updateServerInfo=false repack &&
+       test_server_info_missing
+'
+
+test_expect_success '-n overrides repack.updateServerInfo=true' '
+       test_server_info_cleanup &&
+       git -C update-server-info -c repack.updateServerInfo=true repack -n &&
+       test_server_info_missing
+'
+
 test_done
index ca3f24f8079b7acf491f8e8b0cb20c14e43272d3..9047d665a1049195ec03b64504c973559a677c36 100755 (executable)
@@ -11,9 +11,19 @@ test_expect_success GETTEXT_LOCALE 'setup' '
        export LC_ALL
 '
 
-test_have_prereq GETTEXT_LOCALE &&
-test-tool regex "HALLÓ" "Halló" ICASE &&
-test_set_prereq REGEX_LOCALE
+test_expect_success GETTEXT_LOCALE 'setup REGEX_LOCALE prerequisite' '
+       # This "test-tool" invocation is identical...
+       if test-tool regex "HALLÓ" "Halló" ICASE
+       then
+               test_set_prereq REGEX_LOCALE
+       else
+
+               # ... to this one, but this way "test_must_fail" will
+               # tell a segfault or abort() from the regexec() test
+               # itself
+               test_must_fail test-tool regex "HALLÓ" "Halló" ICASE
+       fi
+'
 
 test_expect_success REGEX_LOCALE 'grep literal string, no -F' '
        git grep -i "TILRAUN: Halló Heimur!" &&
index 590b99bbb6f7bbc2182acf528a4cef566075cc68..eb595645657fad7f1dce4cf5fca6ac57eeb6bfce 100755 (executable)
@@ -83,10 +83,13 @@ test_expect_success 'setup' '
 
 # The test below covers a special case: the sparsity patterns exclude '/b' and
 # sparse checkout is enabled, but the path exists in the working tree (e.g.
-# manually created after `git sparse-checkout init`). git grep should skip it.
+# manually created after `git sparse-checkout init`).  Although b is marked
+# as SKIP_WORKTREE, git grep should notice it IS present in the worktree and
+# report it.
 test_expect_success 'working tree grep honors sparse checkout' '
        cat >expect <<-EOF &&
        a:text
+       b:new-text
        EOF
        test_when_finished "rm -f b" &&
        echo "new-text" >b &&
@@ -126,12 +129,16 @@ test_expect_success 'grep --cached searches entries with the SKIP_WORKTREE bit'
 '
 
 # Note that sub2/ is present in the worktree but it is excluded by the sparsity
-# patterns, so grep should not recurse into it.
+# patterns.  We also explicitly mark it as SKIP_WORKTREE in case it got cleared
+# by previous git commands.  Thus sub2 starts as SKIP_WORKTREE but since it is
+# present in the working tree, grep should recurse into it.
 test_expect_success 'grep --recurse-submodules honors sparse checkout in submodule' '
        cat >expect <<-EOF &&
        a:text
        sub/B/b:text
+       sub2/a:text
        EOF
+       git update-index --skip-worktree sub2 &&
        git grep --recurse-submodules "text" >actual &&
        test_cmp expect actual
 '
index 3167473b3031c66555883745d296a881593b8272..8cb582f0e610d407ec5ba560740606629e5bc9da 100755 (executable)
@@ -34,7 +34,7 @@ EOF
 #
 # This will check that gitweb HTTP header contains proposed filename
 # as <basename> with '.tar' suffix added, and that generated tarfile
-# (gitweb message body) has <prefix> as prefix for al files in tarfile
+# (gitweb message body) has <prefix> as prefix for all files in tarfile
 #
 # <prefix> default to <basename>
 check_snapshot () {
@@ -207,4 +207,17 @@ test_expect_success 'xss checks' '
        xss "" "$TAG+"
 '
 
+no_http_equiv_content_type() {
+       gitweb_run "$@" &&
+       ! grep -E "http-equiv=['\"]?content-type" gitweb.body
+}
+
+# See: <https://html.spec.whatwg.org/dev/semantics.html#attr-meta-http-equiv-content-type>
+test_expect_success 'no http-equiv="content-type" in XHTML' '
+       no_http_equiv_content_type &&
+       no_http_equiv_content_type "p=.git" &&
+       no_http_equiv_content_type "p=.git;a=log" &&
+       no_http_equiv_content_type "p=.git;a=tree"
+'
+
 test_done
index 9af5fb7674d7a4a46a269e102f87b8ebcd08cd44..515b1af7ed4bf03d8e49c1099b8acb481268b35f 100644 (file)
@@ -548,11 +548,29 @@ then
        }
 else
        setup_malloc_check () {
+               local g
+               local t
                MALLOC_CHECK_=3 MALLOC_PERTURB_=165
                export MALLOC_CHECK_ MALLOC_PERTURB_
+               if _GLIBC_VERSION=$(getconf GNU_LIBC_VERSION 2>/dev/null) &&
+                  _GLIBC_VERSION=${_GLIBC_VERSION#"glibc "} &&
+                  expr 2.34 \<= "$_GLIBC_VERSION" >/dev/null
+               then
+                       g=
+                       LD_PRELOAD="libc_malloc_debug.so.0"
+                       for t in \
+                               glibc.malloc.check=1 \
+                               glibc.malloc.perturb=165
+                       do
+                               g="${g#:}:$t"
+                       done
+                       GLIBC_TUNABLES=$g
+                       export LD_PRELOAD GLIBC_TUNABLES
+               fi
        }
        teardown_malloc_check () {
                unset MALLOC_CHECK_ MALLOC_PERTURB_
+               unset LD_PRELOAD GLIBC_TUNABLES
        }
 fi
 
index d22a71a3999b3dc0e99f5e36053447b33f967bd7..367ad00c24cb34c0668eb5236dc3b08a477a65b7 100644 (file)
@@ -1,9 +1,7 @@
-# make and install sample templates
-
-ifndef V
-       QUIET = @
-endif
+# Import tree-wide shared Makefile behavior and libraries
+include ../shared.mak
 
+# make and install sample templates
 INSTALL ?= install
 TAR ?= tar
 RM ?= rm -f
diff --git a/trace.c b/trace.c
index f726686fd92f0b9f388b7dddeca55edc2ea8d8a8..794a087c21e100e147665181a592bc811b3c1265 100644 (file)
--- a/trace.c
+++ b/trace.c
@@ -108,16 +108,11 @@ static int prepare_trace_line(const char *file, int line,
        gettimeofday(&tv, NULL);
        secs = tv.tv_sec;
        localtime_r(&secs, &tm);
-       strbuf_addf(buf, "%02d:%02d:%02d.%06ld ", tm.tm_hour, tm.tm_min,
-                   tm.tm_sec, (long) tv.tv_usec);
-
-#ifdef HAVE_VARIADIC_MACROS
-       /* print file:line */
-       strbuf_addf(buf, "%s:%d ", file, line);
+       strbuf_addf(buf, "%02d:%02d:%02d.%06ld %s:%d", tm.tm_hour, tm.tm_min,
+                   tm.tm_sec, (long) tv.tv_usec, file, line);
        /* align trace output (column 40 catches most files names in git) */
        while (buf->len < 40)
                strbuf_addch(buf, ' ');
-#endif
 
        return 1;
 }
@@ -229,74 +224,6 @@ static void trace_performance_vprintf_fl(const char *file, int line,
        strbuf_release(&buf);
 }
 
-#ifndef HAVE_VARIADIC_MACROS
-
-void trace_printf(const char *format, ...)
-{
-       va_list ap;
-       va_start(ap, format);
-       trace_vprintf_fl(NULL, 0, &trace_default_key, format, ap);
-       va_end(ap);
-}
-
-void trace_printf_key(struct trace_key *key, const char *format, ...)
-{
-       va_list ap;
-       va_start(ap, format);
-       trace_vprintf_fl(NULL, 0, key, format, ap);
-       va_end(ap);
-}
-
-void trace_argv_printf(const char **argv, const char *format, ...)
-{
-       va_list ap;
-       va_start(ap, format);
-       trace_argv_vprintf_fl(NULL, 0, argv, format, ap);
-       va_end(ap);
-}
-
-void trace_strbuf(struct trace_key *key, const struct strbuf *data)
-{
-       trace_strbuf_fl(NULL, 0, key, data);
-}
-
-void trace_performance(uint64_t nanos, const char *format, ...)
-{
-       va_list ap;
-       va_start(ap, format);
-       trace_performance_vprintf_fl(NULL, 0, nanos, format, ap);
-       va_end(ap);
-}
-
-void trace_performance_since(uint64_t start, const char *format, ...)
-{
-       va_list ap;
-       va_start(ap, format);
-       trace_performance_vprintf_fl(NULL, 0, getnanotime() - start,
-                                    format, ap);
-       va_end(ap);
-}
-
-void trace_performance_leave(const char *format, ...)
-{
-       va_list ap;
-       uint64_t since;
-
-       if (perf_indent)
-               perf_indent--;
-
-       if (!format) /* Allow callers to leave without tracing anything */
-               return;
-
-       since = perf_start_times[perf_indent];
-       va_start(ap, format);
-       trace_performance_vprintf_fl(NULL, 0, getnanotime() - since,
-                                    format, ap);
-       va_end(ap);
-}
-
-#else
-
 void trace_printf_key_fl(const char *file, int line, struct trace_key *key,
                         const char *format, ...)
 {
@@ -342,9 +269,6 @@ void trace_performance_leave_fl(const char *file, int line,
        va_end(ap);
 }
 
-#endif /* HAVE_VARIADIC_MACROS */
-
-
 static const char *quote_crnl(const char *path)
 {
        static struct strbuf new_path = STRBUF_INIT;
diff --git a/trace.h b/trace.h
index e25984051aa041eb7557766a29a8c8b4743c62fd..4e771f86ac289ada2f5b97eb4013ef313fbb3cbf 100644 (file)
--- a/trace.h
+++ b/trace.h
@@ -126,71 +126,6 @@ void trace_command_performance(const char **argv);
 void trace_verbatim(struct trace_key *key, const void *buf, unsigned len);
 uint64_t trace_performance_enter(void);
 
-#ifndef HAVE_VARIADIC_MACROS
-
-/**
- * Prints a formatted message, similar to printf.
- */
-__attribute__((format (printf, 1, 2)))
-void trace_printf(const char *format, ...);
-
-__attribute__((format (printf, 2, 3)))
-void trace_printf_key(struct trace_key *key, const char *format, ...);
-
-/**
- * Prints a formatted message, followed by a quoted list of arguments.
- */
-__attribute__((format (printf, 2, 3)))
-void trace_argv_printf(const char **argv, const char *format, ...);
-
-/**
- * Prints the strbuf, without additional formatting (i.e. doesn't
- * choke on `%` or even `\0`).
- */
-void trace_strbuf(struct trace_key *key, const struct strbuf *data);
-
-/**
- * Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled.
- *
- * Example:
- * ------------
- * uint64_t t = 0;
- * for (;;) {
- *     // ignore
- * t -= getnanotime();
- * // code section to measure
- * t += getnanotime();
- * // ignore
- * }
- * trace_performance(t, "frotz");
- * ------------
- */
-__attribute__((format (printf, 2, 3)))
-void trace_performance(uint64_t nanos, const char *format, ...);
-
-/**
- * Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled.
- *
- * Example:
- * ------------
- * uint64_t start = getnanotime();
- * // code section to measure
- * trace_performance_since(start, "foobar");
- * ------------
- */
-__attribute__((format (printf, 2, 3)))
-void trace_performance_since(uint64_t start, const char *format, ...);
-
-__attribute__((format (printf, 1, 2)))
-void trace_performance_leave(const char *format, ...);
-
-#else
-
-/*
- * Macros to add file:line - see above for C-style declarations of how these
- * should be used.
- */
-
 /*
  * TRACE_CONTEXT may be set to __FUNCTION__ if the compiler supports it. The
  * default is __FILE__, as it is consistent with assert(), and static function
@@ -204,7 +139,10 @@ void trace_performance_leave(const char *format, ...);
 # define TRACE_CONTEXT __FILE__
 #endif
 
-/*
+/**
+ * Macros to add the file:line of the calling code, instead of that of
+ * the trace function itself.
+ *
  * Note: with C99 variadic macros, __VA_ARGS__ must include the last fixed
  * parameter ('format' in this case). Otherwise, a call without variable
  * arguments will have a surplus ','. E.g.:
@@ -220,6 +158,16 @@ void trace_performance_leave(const char *format, ...);
  * comma, but this is non-standard.
  */
 
+/**
+ * trace_printf(), accepts "const char *format, ...".
+ *
+ * Prints a formatted message, similar to printf.
+ */
+#define trace_printf(...) trace_printf_key(&trace_default_key, __VA_ARGS__)
+
+/**
+ * trace_printf_key(), accepts "struct trace_key *key, const char *format, ...".
+ */
 #define trace_printf_key(key, ...)                                         \
        do {                                                                \
                if (trace_pass_fl(key))                                     \
@@ -227,8 +175,11 @@ void trace_performance_leave(const char *format, ...);
                                            __VA_ARGS__);                   \
        } while (0)
 
-#define trace_printf(...) trace_printf_key(&trace_default_key, __VA_ARGS__)
-
+/**
+ * trace_argv_printf(), accepts "struct trace_key *key, const char *format, ...)".
+ *
+ * Prints a formatted message, followed by a quoted list of arguments.
+ */
 #define trace_argv_printf(argv, ...)                                       \
        do {                                                                \
                if (trace_pass_fl(&trace_default_key))                      \
@@ -236,12 +187,36 @@ void trace_performance_leave(const char *format, ...);
                                            argv, __VA_ARGS__);             \
        } while (0)
 
+/**
+ * trace_strbuf(), accepts "struct trace_key *key, const struct strbuf *data".
+ *
+ * Prints the strbuf, without additional formatting (i.e. doesn't
+ * choke on `%` or even `\0`).
+ */
 #define trace_strbuf(key, data)                                                    \
        do {                                                                \
                if (trace_pass_fl(key))                                     \
                        trace_strbuf_fl(TRACE_CONTEXT, __LINE__, key, data);\
        } while (0)
 
+/**
+ * trace_performance(), accepts "uint64_t nanos, const char *format, ...".
+ *
+ * Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled.
+ *
+ * Example:
+ * ------------
+ * uint64_t t = 0;
+ * for (;;) {
+ *     // ignore
+ * t -= getnanotime();
+ * // code section to measure
+ * t += getnanotime();
+ * // ignore
+ * }
+ * trace_performance(t, "frotz");
+ * ------------
+ */
 #define trace_performance(nanos, ...)                                      \
        do {                                                                \
                if (trace_pass_fl(&trace_perf_key))                         \
@@ -249,6 +224,18 @@ void trace_performance_leave(const char *format, ...);
                                             __VA_ARGS__);                  \
        } while (0)
 
+/**
+ * trace_performance_since(), accepts "uint64_t start, const char *format, ...".
+ *
+ * Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled.
+ *
+ * Example:
+ * ------------
+ * uint64_t start = getnanotime();
+ * // code section to measure
+ * trace_performance_since(start, "foobar");
+ * ------------
+ */
 #define trace_performance_since(start, ...)                                \
        do {                                                                \
                if (trace_pass_fl(&trace_perf_key))                         \
@@ -257,6 +244,9 @@ void trace_performance_leave(const char *format, ...);
                                             __VA_ARGS__);                  \
        } while (0)
 
+/**
+ * trace_performance_leave(), accepts "const char *format, ...".
+ */
 #define trace_performance_leave(...)                                       \
        do {                                                                \
                if (trace_pass_fl(&trace_perf_key))                         \
@@ -285,6 +275,4 @@ static inline int trace_pass_fl(struct trace_key *key)
        return key->fd || !key->initialized;
 }
 
-#endif /* HAVE_VARIADIC_MACROS */
-
 #endif /* TRACE_H */
index b2d471526fd64cf323ae7ef6facc730cbd133107..179caa72cfee3035fee351bbebcf42097207e635 100644 (file)
--- a/trace2.c
+++ b/trace2.c
@@ -641,20 +641,6 @@ void trace2_region_enter_printf_fl(const char *file, int line,
        va_end(ap);
 }
 
-#ifndef HAVE_VARIADIC_MACROS
-void trace2_region_enter_printf(const char *category, const char *label,
-                               const struct repository *repo, const char *fmt,
-                               ...)
-{
-       va_list ap;
-
-       va_start(ap, fmt);
-       trace2_region_enter_printf_va_fl(NULL, 0, category, label, repo, fmt,
-                                        ap);
-       va_end(ap);
-}
-#endif
-
 void trace2_region_leave_printf_va_fl(const char *file, int line,
                                      const char *category, const char *label,
                                      const struct repository *repo,
@@ -717,20 +703,6 @@ void trace2_region_leave_printf_fl(const char *file, int line,
        va_end(ap);
 }
 
-#ifndef HAVE_VARIADIC_MACROS
-void trace2_region_leave_printf(const char *category, const char *label,
-                               const struct repository *repo, const char *fmt,
-                               ...)
-{
-       va_list ap;
-
-       va_start(ap, fmt);
-       trace2_region_leave_printf_va_fl(NULL, 0, category, label, repo, fmt,
-                                        ap);
-       va_end(ap);
-}
-#endif
-
 void trace2_data_string_fl(const char *file, int line, const char *category,
                           const struct repository *repo, const char *key,
                           const char *value)
@@ -826,17 +798,6 @@ void trace2_printf_fl(const char *file, int line, const char *fmt, ...)
        va_end(ap);
 }
 
-#ifndef HAVE_VARIADIC_MACROS
-void trace2_printf(const char *fmt, ...)
-{
-       va_list ap;
-
-       va_start(ap, fmt);
-       trace2_printf_va_fl(NULL, 0, fmt, ap);
-       va_end(ap);
-}
-#endif
-
 const char *trace2_session_id(void)
 {
        return tr2_sid_get();
index 0cc7b5f53127e25c50021776bc5b505454ff5b5d..1b109f57d0a94a6eef1649b2ccc1a286b0e92381 100644 (file)
--- a/trace2.h
+++ b/trace2.h
@@ -397,18 +397,9 @@ void trace2_region_enter_printf_fl(const char *file, int line,
                                   const struct repository *repo,
                                   const char *fmt, ...);
 
-#ifdef HAVE_VARIADIC_MACROS
 #define trace2_region_enter_printf(category, label, repo, ...)                 \
        trace2_region_enter_printf_fl(__FILE__, __LINE__, (category), (label), \
                                      (repo), __VA_ARGS__)
-#else
-/* clang-format off */
-__attribute__((format (region_enter_printf, 4, 5)))
-void trace2_region_enter_printf(const char *category, const char *label,
-                               const struct repository *repo, const char *fmt,
-                               ...);
-/* clang-format on */
-#endif
 
 /**
  * Emit a 'region_leave' event for <category>.<label> with optional
@@ -442,18 +433,9 @@ void trace2_region_leave_printf_fl(const char *file, int line,
                                   const struct repository *repo,
                                   const char *fmt, ...);
 
-#ifdef HAVE_VARIADIC_MACROS
 #define trace2_region_leave_printf(category, label, repo, ...)                 \
        trace2_region_leave_printf_fl(__FILE__, __LINE__, (category), (label), \
                                      (repo), __VA_ARGS__)
-#else
-/* clang-format off */
-__attribute__((format (region_leave_printf, 4, 5)))
-void trace2_region_leave_printf(const char *category, const char *label,
-                               const struct repository *repo, const char *fmt,
-                               ...);
-/* clang-format on */
-#endif
 
 /**
  * Emit a key-value pair 'data' event of the form <category>.<key> = <value>.
@@ -506,14 +488,7 @@ void trace2_printf_va_fl(const char *file, int line, const char *fmt,
 
 void trace2_printf_fl(const char *file, int line, const char *fmt, ...);
 
-#ifdef HAVE_VARIADIC_MACROS
 #define trace2_printf(...) trace2_printf_fl(__FILE__, __LINE__, __VA_ARGS__)
-#else
-/* clang-format off */
-__attribute__((format (printf, 1, 2)))
-void trace2_printf(const char *fmt, ...);
-/* clang-format on */
-#endif
 
 /*
  * Optional platform-specific code to dump information about the
index 253d6671b1f1edb889525b0ed8a2bb2769540b33..70e9840a90e4cce41b4921fca38a472281bf3bec 100644 (file)
@@ -125,16 +125,9 @@ struct bundle_transport_data {
        unsigned get_refs_from_bundle_called : 1;
 };
 
-static struct ref *get_refs_from_bundle(struct transport *transport,
-                                       int for_push,
-                                       struct transport_ls_refs_options *transport_options)
+static void get_refs_from_bundle_inner(struct transport *transport)
 {
        struct bundle_transport_data *data = transport->data;
-       struct ref *result = NULL;
-       int i;
-
-       if (for_push)
-               return NULL;
 
        data->get_refs_from_bundle_called = 1;
 
@@ -145,6 +138,20 @@ static struct ref *get_refs_from_bundle(struct transport *transport,
                die(_("could not read bundle '%s'"), transport->url);
 
        transport->hash_algo = data->header.hash_algo;
+}
+
+static struct ref *get_refs_from_bundle(struct transport *transport,
+                                       int for_push,
+                                       struct transport_ls_refs_options *transport_options)
+{
+       struct bundle_transport_data *data = transport->data;
+       struct ref *result = NULL;
+       int i;
+
+       if (for_push)
+               return NULL;
+
+       get_refs_from_bundle_inner(transport);
 
        for (i = 0; i < data->header.references.nr; i++) {
                struct string_list_item *e = data->header.references.items + i;
@@ -169,7 +176,7 @@ static int fetch_refs_from_bundle(struct transport *transport,
                strvec_push(&extra_index_pack_args, "-v");
 
        if (!data->get_refs_from_bundle_called)
-               get_refs_from_bundle(transport, 0, NULL);
+               get_refs_from_bundle_inner(transport);
        ret = unbundle(the_repository, &data->header, data->fd,
                       &extra_index_pack_args);
        transport->hash_algo = data->header.hash_algo;
index 3a94959d64a3a63b784bcadfd47cd12213f3fb2d..506234b4b8138894d6516fc80e9fc42753f78f00 100644 (file)
@@ -89,7 +89,7 @@ void *fill_tree_descriptor(struct repository *r,
        void *buf = NULL;
 
        if (oid) {
-               buf = read_object_with_reference(r, oid, tree_type, &size, NULL);
+               buf = read_object_with_reference(r, oid, OBJ_TREE, &size, NULL);
                if (!buf)
                        die("unable to read tree %s", oid_to_hex(oid));
        }
@@ -605,7 +605,7 @@ int get_tree_entry(struct repository *r,
        unsigned long size;
        struct object_id root;
 
-       tree = read_object_with_reference(r, tree_oid, tree_type, &size, &root);
+       tree = read_object_with_reference(r, tree_oid, OBJ_TREE, &size, &root);
        if (!tree)
                return -1;
 
@@ -677,7 +677,7 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r,
                        unsigned long size;
                        tree = read_object_with_reference(r,
                                                          &current_tree_oid,
-                                                         tree_type, &size,
+                                                         OBJ_TREE, &size,
                                                          &root);
                        if (!tree)
                                goto done;
index 360844bda3ab976c73e9443b318b58dfc400f475..2763a029a1756f33b351cfc6c800eea588058bb2 100644 (file)
@@ -1360,6 +1360,42 @@ static int is_sparse_directory_entry(struct cache_entry *ce,
        return sparse_dir_matches_path(ce, info, name);
 }
 
+static int unpack_sparse_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *names, struct traverse_info *info)
+{
+       struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
+       struct unpack_trees_options *o = info->data;
+       int ret;
+
+       assert(o->merge);
+
+       /*
+        * Unlike in 'unpack_callback', where src[0] is derived from the index when
+        * merging, src[0] is a transient cache entry derived from the first tree
+        * provided. Create the temporary entry as if it came from a non-sparse index.
+        */
+       if (!is_null_oid(&names[0].oid)) {
+               src[0] = create_ce_entry(info, &names[0], 0,
+                                       &o->result, 1,
+                                       dirmask & (1ul << 0));
+               src[0]->ce_flags |= (CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
+       }
+
+       /*
+        * 'unpack_single_entry' assumes that src[0] is derived directly from
+        * the index, rather than from an entry in 'names'. This is *not* true when
+        * merging a sparse directory, in which case names[0] is the "index" source
+        * entry. To match the expectations of 'unpack_single_entry', shift past the
+        * "index" tree (i.e., names[0]) and adjust 'names', 'n', 'mask', and
+        * 'dirmask' accordingly.
+        */
+       ret = unpack_single_entry(n - 1, mask >> 1, dirmask >> 1, src, names + 1, info);
+
+       if (src[0])
+               discard_cache_entry(src[0]);
+
+       return ret >= 0 ? mask : -1;
+}
+
 /*
  * Note that traverse_by_cache_tree() duplicates some logic in this function
  * without actually calling it. If you change the logic here you may need to
@@ -1693,6 +1729,41 @@ static void populate_from_existing_patterns(struct unpack_trees_options *o,
                o->pl = pl;
 }
 
+static void update_sparsity_for_prefix(const char *prefix,
+                                      struct index_state *istate)
+{
+       int prefix_len = strlen(prefix);
+       struct strbuf ce_prefix = STRBUF_INIT;
+
+       if (!istate->sparse_index)
+               return;
+
+       while (prefix_len > 0 && prefix[prefix_len - 1] == '/')
+               prefix_len--;
+
+       if (prefix_len <= 0)
+               BUG("Invalid prefix passed to update_sparsity_for_prefix");
+
+       strbuf_grow(&ce_prefix, prefix_len + 1);
+       strbuf_add(&ce_prefix, prefix, prefix_len);
+       strbuf_addch(&ce_prefix, '/');
+
+       /*
+        * If the prefix points to a sparse directory or a path inside a sparse
+        * directory, the index should be expanded. This is accomplished in one
+        * of two ways:
+        * - if the prefix is inside a sparse directory, it will be expanded by
+        *   the 'ensure_full_index(...)' call in 'index_name_pos(...)'.
+        * - if the prefix matches an existing sparse directory entry,
+        *   'index_name_pos(...)' will return its index position, triggering
+        *   the 'ensure_full_index(...)' below.
+        */
+       if (!path_in_cone_mode_sparse_checkout(ce_prefix.buf, istate) &&
+           index_name_pos(istate, ce_prefix.buf, ce_prefix.len) >= 0)
+               ensure_full_index(istate);
+
+       strbuf_release(&ce_prefix);
+}
 
 static int verify_absent(const struct cache_entry *,
                         enum unpack_trees_error_types,
@@ -1739,6 +1810,9 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                setup_standard_excludes(o->dir);
        }
 
+       if (o->prefix)
+               update_sparsity_for_prefix(o->prefix, o->src_index);
+
        if (!core_apply_sparse_checkout || !o->update)
                o->skip_sparse_checkout = 1;
        if (!o->skip_sparse_checkout && !o->pl) {
@@ -2065,7 +2139,9 @@ static int verify_uptodate_1(const struct cache_entry *ce,
 int verify_uptodate(const struct cache_entry *ce,
                    struct unpack_trees_options *o)
 {
-       if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE))
+       if (!o->skip_sparse_checkout &&
+           (ce->ce_flags & CE_SKIP_WORKTREE) &&
+           (ce->ce_flags & CE_NEW_SKIP_WORKTREE))
                return 0;
        return verify_uptodate_1(ce, o, ERROR_NOT_UPTODATE_FILE);
 }
@@ -2434,6 +2510,37 @@ static int merged_entry(const struct cache_entry *ce,
        return 1;
 }
 
+static int merged_sparse_dir(const struct cache_entry * const *src, int n,
+                            struct unpack_trees_options *o)
+{
+       struct tree_desc t[MAX_UNPACK_TREES + 1];
+       void * tree_bufs[MAX_UNPACK_TREES + 1];
+       struct traverse_info info;
+       int i, ret;
+
+       /*
+        * Create the tree traversal information for traversing into *only* the
+        * sparse directory.
+        */
+       setup_traverse_info(&info, src[0]->name);
+       info.fn = unpack_sparse_callback;
+       info.data = o;
+       info.show_all_errors = o->show_all_errors;
+       info.pathspec = o->pathspec;
+
+       /* Get the tree descriptors of the sparse directory in each of the merging trees */
+       for (i = 0; i < n; i++)
+               tree_bufs[i] = fill_tree_descriptor(o->src_index->repo, &t[i],
+                                                   src[i] && !is_null_oid(&src[i]->oid) ? &src[i]->oid : NULL);
+
+       ret = traverse_trees(o->src_index, n, t, &info);
+
+       for (i = 0; i < n; i++)
+               free(tree_bufs[i]);
+
+       return ret;
+}
+
 static int deleted_entry(const struct cache_entry *ce,
                         const struct cache_entry *old,
                         struct unpack_trees_options *o)
@@ -2538,16 +2645,24 @@ int threeway_merge(const struct cache_entry * const *stages,
         */
        /* #14, #14ALT, #2ALT */
        if (remote && !df_conflict_head && head_match && !remote_match) {
-               if (index && !same(index, remote) && !same(index, head))
-                       return reject_merge(index, o);
+               if (index && !same(index, remote) && !same(index, head)) {
+                       if (S_ISSPARSEDIR(index->ce_mode))
+                               return merged_sparse_dir(stages, 4, o);
+                       else
+                               return reject_merge(index, o);
+               }
                return merged_entry(remote, index, o);
        }
        /*
         * If we have an entry in the index cache, then we want to
         * make sure that it matches head.
         */
-       if (index && !same(index, head))
-               return reject_merge(index, o);
+       if (index && !same(index, head)) {
+               if (S_ISSPARSEDIR(index->ce_mode))
+                       return merged_sparse_dir(stages, 4, o);
+               else
+                       return reject_merge(index, o);
+       }
 
        if (head) {
                /* #5ALT, #15 */
@@ -2609,11 +2724,21 @@ int threeway_merge(const struct cache_entry * const *stages,
 
        }
 
-       /* Below are "no merge" cases, which require that the index be
-        * up-to-date to avoid the files getting overwritten with
-        * conflict resolution files.
-        */
+       /* Handle "no merge" cases (see t/t1000-read-tree-m-3way.sh) */
        if (index) {
+               /*
+                * If we've reached the "no merge" cases and we're merging
+                * a sparse directory, we may have an "edit/edit" conflict that
+                * can be resolved by individually merging directory contents.
+                */
+               if (S_ISSPARSEDIR(index->ce_mode))
+                       return merged_sparse_dir(stages, 4, o);
+
+               /*
+                * If we're not merging a sparse directory, ensure the index is
+                * up-to-date to avoid files getting overwritten with conflict
+                * resolution files
+                */
                if (verify_uptodate(index, o))
                        return -1;
        }
@@ -2704,6 +2829,14 @@ int twoway_merge(const struct cache_entry * const *src,
                         * reject the merge instead.
                         */
                        return merged_entry(newtree, current, o);
+               } else if (S_ISSPARSEDIR(current->ce_mode)) {
+                       /*
+                        * The sparse directories differ, but we don't know whether that's
+                        * because of two different files in the directory being modified
+                        * (can be trivially merged) or if there is a real file conflict.
+                        * Merge the sparse directory by OID to compare file-by-file.
+                        */
+                       return merged_sparse_dir(src, 3, o);
                } else
                        return reject_merge(current, o);
        }
index 8acc98741bbb83db90dc6b0901d5be817cacd0a3..3a851b360663a56bc2ad0d7beed0cc566d581546 100644 (file)
@@ -1400,13 +1400,19 @@ static int parse_want(struct packet_writer *writer, const char *line,
        const char *arg;
        if (skip_prefix(line, "want ", &arg)) {
                struct object_id oid;
+               struct commit *commit;
                struct object *o;
 
                if (get_oid_hex(arg, &oid))
                        die("git upload-pack: protocol error, "
                            "expected to get oid, not '%s'", line);
 
-               o = parse_object(the_repository, &oid);
+               commit = lookup_commit_in_graph(the_repository, &oid);
+               if (commit)
+                       o = &commit->object;
+               else
+                       o = parse_object(the_repository, &oid);
+
                if (!o) {
                        packet_writer_error(writer,
                                            "upload-pack: not our ref %s",
@@ -1434,7 +1440,7 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,
        if (skip_prefix(line, "want-ref ", &refname_nons)) {
                struct object_id oid;
                struct string_list_item *item;
-               struct object *o;
+               struct object *o = NULL;
                struct strbuf refname = STRBUF_INIT;
 
                strbuf_addf(&refname, "%s%s", get_git_namespace(), refname_nons);
@@ -1448,7 +1454,15 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,
                item = string_list_append(wanted_refs, refname_nons);
                item->util = oiddup(&oid);
 
-               o = parse_object_or_die(&oid, refname_nons);
+               if (!starts_with(refname_nons, "refs/tags/")) {
+                       struct commit *commit = lookup_commit_in_graph(the_repository, &oid);
+                       if (commit)
+                               o = &commit->object;
+               }
+
+               if (!o)
+                       o = parse_object_or_die(&oid, refname_nons);
+
                if (!(o->flags & WANTED)) {
                        o->flags |= WANTED;
                        add_object_array(o, NULL, want_obj);
index 03ad3f30a9c09f793aa94cac1fa2730cf4180b45..b615adc923ae019b756e25a1cdcfb251333e69ff 100644 (file)
@@ -611,3 +611,8 @@ int urlmatch_config_entry(const char *var, const char *value, void *cb)
        strbuf_release(&synthkey);
        return retval;
 }
+
+void urlmatch_config_release(struct urlmatch_config *config)
+{
+       string_list_clear(&config->vars, 1);
+}
index 34a3ba6d1973b4aa8d82a2af7fab8b6672433cc5..9f40b00bfb82b1f32bf75e60538be145bbe9d116 100644 (file)
@@ -71,5 +71,6 @@ struct urlmatch_config {
 }
 
 int urlmatch_config_entry(const char *var, const char *value, void *cb);
+void urlmatch_config_release(struct urlmatch_config *config);
 
 #endif /* URL_MATCH_H */
diff --git a/usage.c b/usage.c
index 9943dd8742e8adb2d283be453d7cb9da478a0a7e..b738dd178b3c9983cbe381248f188caefec79336 100644 (file)
--- a/usage.c
+++ b/usage.c
@@ -299,10 +299,7 @@ static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_lis
        va_copy(params_copy, params);
 
        /* truncation via snprintf is OK here */
-       if (file)
-               snprintf(prefix, sizeof(prefix), "BUG: %s:%d: ", file, line);
-       else
-               snprintf(prefix, sizeof(prefix), "BUG: ");
+       snprintf(prefix, sizeof(prefix), "BUG: %s:%d: ", file, line);
 
        vreportf(prefix, fmt, params);
 
@@ -317,7 +314,6 @@ static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_lis
        abort();
 }
 
-#ifdef HAVE_VARIADIC_MACROS
 NORETURN void BUG_fl(const char *file, int line, const char *fmt, ...)
 {
        va_list ap;
@@ -325,15 +321,6 @@ NORETURN void BUG_fl(const char *file, int line, const char *fmt, ...)
        BUG_vfl(file, line, fmt, ap);
        va_end(ap);
 }
-#else
-NORETURN void BUG(const char *fmt, ...)
-{
-       va_list ap;
-       va_start(ap, fmt);
-       BUG_vfl(NULL, 0, fmt, ap);
-       va_end(ap);
-}
-#endif
 
 #ifdef SUPPRESS_ANNOTATED_LEAKS
 void unleak_memory(const void *ptr, size_t len)
index 2d9eb99bf287abcb5ed39fde334877c6c8945158..151d9a52784b1f4a149913d38739c91e389d95d6 100644 (file)
@@ -180,6 +180,18 @@ PATTERNS("java",
         "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
         "|[-+*/<>%&^|=!]="
         "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"),
+PATTERNS("kotlin",
+        "^[ \t]*(([a-z]+[ \t]+)*(fun|class|interface)[ \t]+.*)$",
+        /* -- */
+        "[a-zA-Z_][a-zA-Z0-9_]*"
+        /* hexadecimal and binary numbers */
+        "|0[xXbB][0-9a-fA-F_]+[lLuU]*"
+        /* integers and floats */
+        "|[0-9][0-9_]*([.][0-9_]*)?([Ee][-+]?[0-9]+)?[fFlLuU]*"
+        /* floating point numbers beginning with decimal point */
+        "|[.][0-9][0-9_]*([Ee][-+]?[0-9]+)?[fFlLuU]?"
+        /* unary and binary operators */
+        "|[-+*/<>%&^|=!]==?|--|\\+\\+|<<=|>>=|&&|\\|\\||->|\\.\\*|!!|[?:.][.:]"),
 PATTERNS("markdown",
         "^ {0,3}#{1,6}[ \t].*",
         /* -- */
index 335e723a71e2537095359d3d4ee6cb77daea1e16..d33f9272b724b562f5b854d306500e8c0b445c4c 100644 (file)
@@ -651,6 +651,15 @@ static void wt_status_collect_changes_index(struct wt_status *s)
        rev.diffopt.detect_rename = s->detect_rename >= 0 ? s->detect_rename : rev.diffopt.detect_rename;
        rev.diffopt.rename_limit = s->rename_limit >= 0 ? s->rename_limit : rev.diffopt.rename_limit;
        rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
+
+       /*
+        * The `recursive` option must be enabled to allow the diff to recurse
+        * into subdirectories of sparse directory index entries. If it is not
+        * enabled, a subdirectory containing file(s) with changes is reported
+        * as "modified", rather than the modified files themselves.
+        */
+       rev.diffopt.flags.recursive = 1;
+
        copy_pathspec(&rev.prune_data, &s->pathspec);
        run_diff_index(&rev, 1);
        object_array_clear(&rev.pending);
@@ -1374,10 +1383,10 @@ static void show_rebase_information(struct wt_status *s,
                        status_printf_ln(s, color, _("No commands done."));
                else {
                        status_printf_ln(s, color,
-                               Q_("Last command done (%d command done):",
-                                       "Last commands done (%d commands done):",
+                               Q_("Last command done (%"PRIuMAX" command done):",
+                                       "Last commands done (%"PRIuMAX" commands done):",
                                        have_done.nr),
-                               have_done.nr);
+                               (uintmax_t)have_done.nr);
                        for (i = (have_done.nr > nr_lines_to_show)
                                ? have_done.nr - nr_lines_to_show : 0;
                                i < have_done.nr;
@@ -1393,10 +1402,10 @@ static void show_rebase_information(struct wt_status *s,
                                         _("No commands remaining."));
                else {
                        status_printf_ln(s, color,
-                               Q_("Next command to do (%d remaining command):",
-                                       "Next commands to do (%d remaining commands):",
+                               Q_("Next command to do (%"PRIuMAX" remaining command):",
+                                       "Next commands to do (%"PRIuMAX" remaining commands):",
                                        yet_to_do.nr),
-                               yet_to_do.nr);
+                               (uintmax_t)yet_to_do.nr);
                        for (i = 0; i < nr_lines_to_show && i < yet_to_do.nr; i++)
                                status_printf_ln(s, color, "   %s", yet_to_do.items[i].string);
                        if (s->hints)
index 69689fab2478c8fed40b63e9a7cefb43c13e7ae7..758410c11ac286adc77c6f992e51822def202a40 100644 (file)
@@ -315,16 +315,19 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
        long *kvd, *kvdf, *kvdb;
        xdalgoenv_t xenv;
        diffdata_t dd1, dd2;
+       int res;
 
-       if (XDF_DIFF_ALG(xpp->flags) == XDF_PATIENCE_DIFF)
-               return xdl_do_patience_diff(mf1, mf2, xpp, xe);
-
-       if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF)
-               return xdl_do_histogram_diff(mf1, mf2, xpp, xe);
+       if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0)
+               return -1;
 
-       if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) {
+       if (XDF_DIFF_ALG(xpp->flags) == XDF_PATIENCE_DIFF) {
+               res = xdl_do_patience_diff(mf1, mf2, xpp, xe);
+               goto out;
+       }
 
-               return -1;
+       if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF) {
+               res = xdl_do_histogram_diff(mf1, mf2, xpp, xe);
+               goto out;
        }
 
        /*
@@ -359,17 +362,15 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
        dd2.rchg = xe->xdf2.rchg;
        dd2.rindex = xe->xdf2.rindex;
 
-       if (xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec,
-                        kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0, &xenv) < 0) {
-
-               xdl_free(kvd);
-               xdl_free_env(xe);
-               return -1;
-       }
-
+       res = xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec,
+                          kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0,
+                          &xenv);
        xdl_free(kvd);
+ out:
+       if (res < 0)
+               xdl_free_env(xe);
 
-       return 0;
+       return res;
 }
 
 
index 80794748b0de6bb9176ce088c472d44c62b91e6d..01decffc332629dd9dcfd79c904187b7cc6d0943 100644 (file)
@@ -372,9 +372,6 @@ out:
 int xdl_do_histogram_diff(mmfile_t *file1, mmfile_t *file2,
        xpparam_t const *xpp, xdfenv_t *env)
 {
-       if (xdl_prepare_env(file1, file2, xpp, env) < 0)
-               return -1;
-
        return histogram_diff(xpp, env,
                env->xdf1.dstart + 1, env->xdf1.dend - env->xdf1.dstart + 1,
                env->xdf2.dstart + 1, env->xdf2.dend - env->xdf2.dstart + 1);
index fff0b594f9a851a8e1a6fca961692614ff0c822a..af40c88a5b36fa86386a0cd5dfbe8d6dbe8f7fb7 100644 (file)
@@ -684,42 +684,42 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,
 int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
                xmparam_t const *xmp, mmbuffer_t *result)
 {
-       xdchange_t *xscr1, *xscr2;
+       xdchange_t *xscr1 = NULL, *xscr2 = NULL;
        xdfenv_t xe1, xe2;
-       int status;
+       int status = -1;
        xpparam_t const *xpp = &xmp->xpp;
 
        result->ptr = NULL;
        result->size = 0;
 
-       if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0) {
+       if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0)
                return -1;
-       }
-       if (xdl_do_diff(orig, mf2, xpp, &xe2) < 0) {
-               xdl_free_env(&xe1);
-               return -1;
-       }
+
+       if (xdl_do_diff(orig, mf2, xpp, &xe2) < 0)
+               goto free_xe1; /* avoid double free of xe2 */
+
        if (xdl_change_compact(&xe1.xdf1, &xe1.xdf2, xpp->flags) < 0 ||
            xdl_change_compact(&xe1.xdf2, &xe1.xdf1, xpp->flags) < 0 ||
-           xdl_build_script(&xe1, &xscr1) < 0) {
-               xdl_free_env(&xe1);
-               return -1;
-       }
+           xdl_build_script(&xe1, &xscr1) < 0)
+               goto out;
+
        if (xdl_change_compact(&xe2.xdf1, &xe2.xdf2, xpp->flags) < 0 ||
            xdl_change_compact(&xe2.xdf2, &xe2.xdf1, xpp->flags) < 0 ||
-           xdl_build_script(&xe2, &xscr2) < 0) {
-               xdl_free_script(xscr1);
-               xdl_free_env(&xe1);
-               xdl_free_env(&xe2);
-               return -1;
-       }
-       status = 0;
+           xdl_build_script(&xe2, &xscr2) < 0)
+               goto out;
+
        if (!xscr1) {
                result->ptr = xdl_malloc(mf2->size);
+               if (!result->ptr)
+                       goto out;
+               status = 0;
                memcpy(result->ptr, mf2->ptr, mf2->size);
                result->size = mf2->size;
        } else if (!xscr2) {
                result->ptr = xdl_malloc(mf1->size);
+               if (!result->ptr)
+                       goto out;
+               status = 0;
                memcpy(result->ptr, mf1->ptr, mf1->size);
                result->size = mf1->size;
        } else {
@@ -727,11 +727,13 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
                                      &xe2, xscr2,
                                      xmp, result);
        }
+ out:
        xdl_free_script(xscr1);
        xdl_free_script(xscr2);
 
-       xdl_free_env(&xe1);
        xdl_free_env(&xe2);
+ free_xe1:
+       xdl_free_env(&xe1);
 
        return status;
 }
index c5d48e80aefb33eddb4dbe4e359f2c598a5483d7..1a21c6a74b368cb094e20c708a43071c72558d7e 100644 (file)
@@ -198,7 +198,7 @@ static int binary_search(struct entry **sequence, int longest,
  * item per sequence length: the sequence with the smallest last
  * element (in terms of line2).
  */
-static struct entry *find_longest_common_sequence(struct hashmap *map)
+static int find_longest_common_sequence(struct hashmap *map, struct entry **res)
 {
        struct entry **sequence = xdl_malloc(map->nr * sizeof(struct entry *));
        int longest = 0, i;
@@ -211,6 +211,9 @@ static struct entry *find_longest_common_sequence(struct hashmap *map)
         */
        int anchor_i = -1;
 
+       if (!sequence)
+               return -1;
+
        for (entry = map->first; entry; entry = entry->next) {
                if (!entry->line2 || entry->line2 == NON_UNIQUE)
                        continue;
@@ -230,8 +233,9 @@ static struct entry *find_longest_common_sequence(struct hashmap *map)
 
        /* No common unique lines were found */
        if (!longest) {
+               *res = NULL;
                xdl_free(sequence);
-               return NULL;
+               return 0;
        }
 
        /* Iterate starting at the last element, adjusting the "next" members */
@@ -241,8 +245,9 @@ static struct entry *find_longest_common_sequence(struct hashmap *map)
                entry->previous->next = entry;
                entry = entry->previous;
        }
+       *res = entry;
        xdl_free(sequence);
-       return entry;
+       return 0;
 }
 
 static int match(struct hashmap *map, int line1, int line2)
@@ -358,14 +363,16 @@ static int patience_diff(mmfile_t *file1, mmfile_t *file2,
                return 0;
        }
 
-       first = find_longest_common_sequence(&map);
+       result = find_longest_common_sequence(&map, &first);
+       if (result)
+               goto out;
        if (first)
                result = walk_common_sequence(&map, first,
                        line1, count1, line2, count2);
        else
                result = fall_back_to_classic_diff(&map,
                        line1, count1, line2, count2);
-
+ out:
        xdl_free(map.entries);
        return result;
 }
@@ -373,10 +380,6 @@ static int patience_diff(mmfile_t *file1, mmfile_t *file2,
 int xdl_do_patience_diff(mmfile_t *file1, mmfile_t *file2,
                xpparam_t const *xpp, xdfenv_t *env)
 {
-       if (xdl_prepare_env(file1, file2, xpp, env) < 0)
-               return -1;
-
-       /* environment is cleaned up in xdl_diff() */
        return patience_diff(file1, file2, xpp, env,
                        1, env->xdf1.nrec, 1, env->xdf2.nrec);
 }