Merge branch 'en/merge-recursive-fixes'
authorJunio C Hamano <gitster@pobox.com>
Thu, 15 Feb 2018 22:55:40 +0000 (14:55 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 15 Feb 2018 22:55:40 +0000 (14:55 -0800)
* en/merge-recursive-fixes:
  merge-recursive: add explanation for src_entry and dst_entry
  merge-recursive: fix logic ordering issue
  Tighten and correct a few testcases for merging and cherry-picking

173 files changed:
.clang-format
Documentation/Makefile
Documentation/RelNotes/2.16.1.txt [new file with mode: 0644]
Documentation/RelNotes/2.17.0.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-cat-file.txt
Documentation/git-commit.txt
Documentation/git-pack-objects.txt
Documentation/git-read-tree.txt
Documentation/git-show.txt
Documentation/git-status.txt
Documentation/git-submodule.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitremote-helpers.txt
Documentation/gitsubmodules.txt
Documentation/rev-list-options.txt
Documentation/technical/long-running-process-protocol.txt [new file with mode: 0644]
Documentation/technical/pack-protocol.txt
Documentation/technical/protocol-capabilities.txt
Documentation/technical/repository-version.txt
GIT-VERSION-GEN
INSTALL
Makefile
RelNotes
apply.c
bisect.c
builtin/am.c
builtin/cat-file.c
builtin/checkout.c
builtin/clone.c
builtin/commit.c
builtin/describe.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/fsck.c
builtin/gc.c
builtin/index-pack.c
builtin/log.c
builtin/pack-objects.c
builtin/prune.c
builtin/rebase--helper.c
builtin/repack.c
builtin/rev-list.c
builtin/rev-parse.c
builtin/revert.c
builtin/submodule--helper.c
builtin/worktree.c
bundle.c
cache-tree.c
cache.h
ci/lib-travisci.sh
ci/run-linux32-build.sh
ci/run-linux32-docker.sh
ci/run-tests.sh
combine-diff.c
commit.c
commit.h
config.c
connected.c
contrib/coccinelle/strbuf.cocci
contrib/completion/git-completion.bash
contrib/examples/git-difftool.perl
convert.c
convert.h
daemon.c
diff.c
diff.h
diffcore-pickaxe.c
diffcore-rename.c
dir.c
environment.c
fetch-object.c [new file with mode: 0644]
fetch-object.h [new file with mode: 0644]
fetch-pack.c
fetch-pack.h
git-add--interactive.perl
git-send-email.perl
git-stash.sh
git-submodule.sh
git-svn.perl
hashmap.h
http-push.c
http-walker.c
http.c
list-objects-filter-options.c
list-objects-filter-options.h
list-objects.c
mailinfo.c
merge-recursive.c
mru.c [deleted file]
mru.h [deleted file]
object.c
object.h
oidmap.c
pack-revindex.c
packfile.c
packfile.h
parse-options.c
perl/.gitignore
perl/Git.pm
perl/Git/Error.pm [new file with mode: 0644]
perl/Git/FromCPAN/Error.pm [moved from perl/private-Error.pm with 100% similarity]
perl/Git/FromCPAN/Mail/Address.pm [new file with mode: 0644]
perl/Git/I18N.pm
perl/Git/Mail/Address.pm [new file with mode: 0755]
perl/Makefile [deleted file]
perl/Makefile.PL [deleted file]
quote.c
quote.h
read-cache.c
ref-filter.c
refs/files-backend.c
refs/ref-cache.c
remote-curl.c
replace_object.c
repository.c
rerere.c
revision.c
revision.h
run-command.c
sequencer.c
sequencer.h
setup.c
sha1_file.c
sha1dc_git.h
split-index.c
sub-process.h
submodule.c
t/helper/test-run-command.c
t/lib-git-daemon.sh
t/lib-submodule-update.sh
t/perf/aggregate.perl
t/perf/p7820-grep-engines.sh
t/perf/p7821-grep-engines-fixed.sh
t/perf/run
t/t0061-run-command.sh
t/t0410-partial-clone.sh [new file with mode: 0755]
t/t1700-split-index.sh
t/t2203-add-intent.sh
t/t3404-rebase-interactive.sh
t/t3512-cherry-pick-submodule.sh
t/t3513-revert-submodule.sh
t/t3701-add-interactive.sh
t/t3903-stash.sh
t/t4064-diff-oidfind.sh [new file with mode: 0755]
t/t5500-fetch-pack.sh
t/t5551-http-fetch-smart.sh
t/t5570-git-daemon.sh
t/t5600-clone-fail-cleanup.sh
t/t5601-clone.sh
t/t5616-partial-clone.sh [new file with mode: 0755]
t/t6037-merge-ours-theirs.sh
t/t6120-describe.sh
t/t7500-commit.sh
t/t7505-prepare-commit-msg-hook.sh
t/t7505/expected-rebase-i [new file with mode: 0644]
t/t7505/expected-rebase-p [new file with mode: 0644]
t/t9000-addresses.sh [deleted file]
t/t9000/test.pl [deleted file]
t/t9001-send-email.sh
t/test-lib-functions.sh
t/test-lib.sh
trace.c
transport-helper.c
transport.c
transport.h
unpack-trees.c
upload-pack.c
wrap-for-bin.sh
wt-status.c
wt-status.h

index 611ab47..12a89f9 100644 (file)
@@ -163,7 +163,7 @@ PenaltyBreakComment: 10
 PenaltyBreakFirstLessLess: 0
 PenaltyBreakString: 10
 PenaltyExcessCharacter: 100
-PenaltyReturnTypeOnItsOwnLine: 5
+PenaltyReturnTypeOnItsOwnLine: 60
 
 # Don't sort #include's
 SortIncludes: false
index 4ae9ba5..6232143 100644 (file)
@@ -72,6 +72,7 @@ TECH_DOCS += SubmittingPatches
 TECH_DOCS += technical/hash-function-transition
 TECH_DOCS += technical/http-protocol
 TECH_DOCS += technical/index-format
+TECH_DOCS += technical/long-running-process-protocol
 TECH_DOCS += technical/pack-format
 TECH_DOCS += technical/pack-heuristics
 TECH_DOCS += technical/pack-protocol
diff --git a/Documentation/RelNotes/2.16.1.txt b/Documentation/RelNotes/2.16.1.txt
new file mode 100644 (file)
index 0000000..66e6436
--- /dev/null
@@ -0,0 +1,11 @@
+Git v2.16.1 Release Notes
+=========================
+
+Fixes since v2.16
+-----------------
+
+ * "git clone" segfaulted when cloning a project that happens to
+   track two paths that differ only in case on a case insensitive
+   filesystem.
+
+Does not contain any other documentation updates or code clean-ups.
diff --git a/Documentation/RelNotes/2.17.0.txt b/Documentation/RelNotes/2.17.0.txt
new file mode 100644 (file)
index 0000000..e1e509b
--- /dev/null
@@ -0,0 +1,161 @@
+Git 2.17 Release Notes
+======================
+
+Updates since v2.16
+-------------------
+
+UI, Workflows & Features
+
+ * "diff" family of commands learned "--find-object=<object-id>" option
+   to limit the findings to changes that involve the named object.
+   (merge 4d8c51aa19 sb/diff-blobfind-pickaxe later to maint).
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * More perf tests for threaded grep
+   (merge 7b31b55db1 ab/perf-grep-threads later to maint).
+
+ * "perf" test output can be sent to codespeed server.
+   (merge 19cf57a92e cc/codespeed later to maint).
+
+ * The build procedure for perl/ part has been greatly simplified by
+   weaning ourselves off of MakeMaker.
+
+ * In preparation for implementing narrow/partial clone, the machinery
+   for checking object connectivity used by gc and fsck has been
+   taught that a missing object is OK when it is referenced by a
+   packfile specially marked as coming from trusted repository that
+   promises to make them available on-demand and lazily.
+
+ * The machinery to clone & fetch, which in turn involves packing and
+   unpacking objects, has been told how to omit certain objects using
+   the filtering mechanism introduced by another topic.  It now knows
+   to mark the resulting pack as a promisor pack to tolerate missing
+   objects, laying foundation for "narrow" clones.
+
+ * The first step to getting rid of mru API and using the
+   doubly-linked list API directly instead.
+
+ * Retire mru API as it does not give enough abstraction over
+   underlying list API to be worth it.
+
+ * Rewrite two more "git submodule" subcommands in C.
+
+ * The tracing machinery learned to report tweaking of environment
+   variables as well.
+   (merge 090a09272a nd/trace-with-env later to maint).
+
+ * Update Coccinelle rules to catch and optimize strbuf_addf(&buf, "%s", str)
+   (merge cd9a4b6d93 rs/strbuf-cocci-workaround later to maint).
+
+ * Prevent "clang-format" from breaking line after function return type.
+   (merge a3715d43e8 po/clang-format-functype-weight later to maint).
+
+ * The sequencer infrastructure is shared across "git cherry-pick",
+   "git rebase -i", etc., and has always spawned "git commit" when it
+   needs to create a commit.  It has been taught to do so internally,
+   when able, by reusing the codepath "git commit" itself uses, which
+   gives performance boost for a few tens of percents in some sample
+   scenarios.
+
+
+Also contains various documentation updates and code clean-ups.
+
+
+Fixes since v2.16
+-----------------
+
+ * An old regression in "git describe --all $annotated_tag^0" has been
+   fixed.
+   (merge 1bba00130a dk/describe-all-output-fix later to maint).
+
+ * "git status" after moving a path in the working tree (hence making
+   it appear "removed") and then adding with the -N option (hence
+   making that appear "added") detected it as a rename, but did not
+   report the  old and new pathnames correctly.
+   (merge 176ea74793 nd/ita-wt-renames-in-status later to maint).
+
+ * "git svn dcommit" did not take into account the fact that a
+   svn+ssh:// URL with a username@ (typically used for pushing) refers
+   to the same SVN repository without the username@ and failed when
+   svn.pushmergeinfo option is set.
+   (merge 8aaed892fd jm/svn-pushmergeinfo-fix later to maint).
+
+ * API clean-up around revision traversal.
+   (merge 6fcec2f9ae rs/lose-leak-pending later to maint).
+
+ * "git merge -Xours/-Xtheirs" learned to use our/their version when
+   resolving a conflicting updates to a symbolic link.
+   (merge fd48b46474 jc/merge-symlink-ours-theirs later to maint).
+
+ * "git clone $there $here" is allowed even when here directory exists
+   as long as it is an empty directory, but the command incorrectly
+   removed it upon a failure of the operation.
+   (merge d45420c1c8 jk/abort-clone-with-existing-dest later to maint).
+
+ * "git commit --fixup" did not allow "-m<message>" option to be used
+   at the same time; allow it to annotate resulting commit with more
+   text.
+   (merge 30884c9afc ab/commit-m-with-fixup later to maint).
+
+ * When resetting the working tree files recursively, the working tree
+   of submodules are now also reset to match.
+   (merge 7dcc1f4df8 sb/submodule-update-reset-fix later to maint).
+
+ * "git stash -- <pathspec>" incorrectly blew away untracked files in
+   the directory that matched the pathspec, which has been corrected.
+   (merge bba067d2fa tg/stash-with-pathspec-fix later to maint).
+
+ * Instead of maintaining home-grown email address parsing code, ship
+   a copy of reasonably recent Mail::Address to be used as a fallback
+   in 'git send-email' when the platform lacks it.
+   (merge d60be8acab mm/send-email-fallback-to-local-mail-address later to maint).
+
+ * "git add -p" was taught to ignore local changes to submodules as
+   they do not interfere with the partial addition of regular changes
+   anyway.
+   (merge 12434efc1d nd/add-i-ignore-submodules later to maint).
+
+ * Avoid showing a warning message in the middle of a line of "git
+   diff" output.
+   (merge 4e056c989f nd/diff-flush-before-warning later to maint).
+
+ * The http tracing code, often used to debug connection issues,
+   learned to redact potentially sensitive information from its output
+   so that it can be more safely sharable.
+   (merge 8ba18e6fa4 jt/http-redact-cookies later to maint).
+
+ * Crash fix for a corner case where an error codepath tried to unlock
+   what it did not acquire lock on.
+   (merge 81fcb698e0 mr/packed-ref-store-fix later to maint).
+
+ * The split-index mode had a few corner case bugs fixed.
+   (merge ae59a4e44f tg/split-index-fixes later to maint).
+
+ * Assorted fixes to "git daemon".
+   (merge ed15e58efe jk/daemon-fixes later to maint).
+
+ * Completion of "git merge -s<strategy>" (in contrib/) did not work
+   well in non-C locale.
+   (merge 7cc763aaa3 nd/list-merge-strategy later to maint).
+
+ * Workaround for segfault with more recent versions of SVN.
+   (merge 7f6f75e97a ew/svn-branch-segfault-fix later to maint).
+
+ * Other minor doc, test and build updates and code cleanups.
+   (merge e2a5a028c7 bw/oidmap-autoinit later to maint).
+   (merge f0a6068a9f ys/bisect-object-id-missing-conversion-fix later to maint).
+   (merge 30221a3389 as/read-tree-prefix-doc-fix later to maint).
+   (merge 9bd2ce5432 ab/doc-cat-file-e-still-shows-errors later to maint).
+   (merge ec3b4b06f8 cl/t9001-cleanup later to maint).
+   (merge e1b3f3dd38 ks/submodule-doc-updates later to maint).
+   (merge fbac558a9b rs/describe-unique-abbrev later to maint).
+   (merge 8462ff43e4 tb/crlf-conv-flags later to maint).
+   (merge 7d68bb0766 rb/hashmap-h-compilation-fix later to maint).
+   (merge 3449847168 cc/sha1-file-name later to maint).
+   (merge ad622a256f ds/use-get-be64 later to maint).
+   (merge f919ffebed sg/cocci-move-array later to maint).
+   (merge 4e801463c7 jc/mailinfo-cleanup-fix later to maint).
+   (merge ef5b3a6c5e nd/shared-index-fix later to maint).
+   (merge 9f5258cbb8 tz/doc-show-defaults-to-head later to maint).
index 0e25b2c..f57e9cf 100644 (file)
@@ -3343,6 +3343,10 @@ uploadpack.packObjectsHook::
        was run. I.e., `upload-pack` will feed input intended for
        `pack-objects` to the hook, and expects a completed packfile on
        stdout.
+
+uploadpack.allowFilter::
+       If this option is set, `upload-pack` will advertise partial
+       clone and partial fetch object filtering.
 +
 Note that this configuration variable is ignored if it is seen in the
 repository-level config (this is a safety measure against fetching from
index 743af97..c330c01 100644 (file)
@@ -508,6 +508,15 @@ occurrences of that string did not change).
 See the 'pickaxe' entry in linkgit:gitdiffcore[7] for more
 information.
 
+--find-object=<object-id>::
+       Look for differences that change the number of occurrences of
+       the specified object. Similar to `-S`, just the argument is different
+       in that it doesn't search for a specific string but for a specific
+       object id.
++
+The object can be a blob or a submodule commit. It implies the `-t` option in
+`git-log` to also find trees.
+
 --pickaxe-all::
        When `-S` or `-G` finds a change, show all the changes in that
        changeset, not just the files that contain the change
@@ -516,6 +525,7 @@ information.
 --pickaxe-regex::
        Treat the <string> given to `-S` as an extended POSIX regular
        expression to match.
+
 endif::git-format-patch[]
 
 -O<orderfile>::
index fb09cd6..f90f09b 100644 (file)
@@ -42,8 +42,9 @@ OPTIONS
        <object>.
 
 -e::
-       Suppress all output; instead exit with zero status if <object>
-       exists and is a valid object.
+       Exit with zero status if <object> exists and is a valid
+       object. If <object> is of an invalid format exit with non-zero and
+       emits an error on stderr.
 
 -p::
        Pretty-print the contents of <object> based on its type.
@@ -168,7 +169,7 @@ If `-t` is specified, one of the <type>.
 
 If `-s` is specified, the size of the <object> in bytes.
 
-If `-e` is specified, no output.
+If `-e` is specified, no output, unless the <object> is malformed.
 
 If `-p` is specified, the contents of <object> are pretty-printed.
 
index 8c74a2c..f970a43 100644 (file)
@@ -144,6 +144,8 @@ OPTIONS
        Use the given <msg> as the commit message.
        If multiple `-m` options are given, their values are
        concatenated as separate paragraphs.
++
+The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
 
 -t <file>::
 --template=<file>::
index aa403d0..81bc490 100644 (file)
@@ -255,6 +255,17 @@ a missing object is encountered.  This is the default action.
 The form '--missing=allow-any' will allow object traversal to continue
 if a missing object is encountered.  Missing objects will silently be
 omitted from the results.
++
+The form '--missing=allow-promisor' is like 'allow-any', but will only
+allow object traversal to continue for EXPECTED promisor missing objects.
+Unexpected missing object will raise an error.
+
+--exclude-promisor-objects::
+       Omit objects that are known to be in the promisor remote.  (This
+       option has the purpose of operating only on locally created objects,
+       so that when we repack, we still maintain a distinction between
+       locally created objects [without .promisor] and objects from the
+       promisor remote [with .promisor].)  This is used with partial clone.
 
 SEE ALSO
 --------
index 72bd809..f2a07d5 100644 (file)
@@ -81,12 +81,11 @@ OPTIONS
 * when both sides add a path identically.  The resolution
   is to add that path.
 
---prefix=<prefix>/::
+--prefix=<prefix>::
        Keep the current index contents, and read the contents
        of the named tree-ish under the directory at `<prefix>`.
        The command will refuse to overwrite entries that already
-       existed in the original index file. Note that the `<prefix>/`
-       value must end with a slash.
+       existed in the original index file.
 
 --exclude-per-directory=<gitignore>::
        When running the command with `-u` and `-m` options, the
index 82a4125..e73ef54 100644 (file)
@@ -9,7 +9,7 @@ git-show - Show various types of objects
 SYNOPSIS
 --------
 [verse]
-'git show' [options] <object>...
+'git show' [options] [<object>...]
 
 DESCRIPTION
 -----------
@@ -35,7 +35,7 @@ This manual page describes only the most frequently used options.
 OPTIONS
 -------
 <object>...::
-       The names of objects to show.
+       The names of objects to show (defaults to 'HEAD').
        For a more complete list of ways to spell object names, see
        "SPECIFYING REVISIONS" section in linkgit:gitrevisions[7].
 
index 81cab9a..72bfb87 100644 (file)
@@ -149,14 +149,15 @@ the status.relativePaths config option below.
 Short Format
 ~~~~~~~~~~~~
 
-In the short-format, the status of each path is shown as
+In the short-format, the status of each path is shown as one of these
+forms
 
-       XY PATH1 -> PATH2
+       XY PATH
+       XY ORIG_PATH -> PATH
 
-where `PATH1` is the path in the `HEAD`, and the " `-> PATH2`" part is
-shown only when `PATH1` corresponds to a different path in the
-index/worktree (i.e. the file is renamed). The `XY` is a two-letter
-status code.
+where `ORIG_PATH` is where the renamed/copied contents came
+from. `ORIG_PATH` is only shown when the entry is renamed or
+copied. The `XY` is a two-letter status code.
 
 The fields (including the `->`) are separated from each other by a
 single space. If a filename contains whitespace or other nonprintable
@@ -192,6 +193,8 @@ in which case `XY` are `!!`.
     [MARC]           index and work tree matches
     [ MARC]     M    work tree changed since index
     [ MARC]     D    deleted in work tree
+    [ D]        R    renamed in work tree
+    [ D]        C    copied in work tree
     -------------------------------------------------
     D           D    unmerged, both deleted
     A           U    unmerged, added by us
@@ -309,13 +312,13 @@ Renamed or copied entries have the following format:
                of similarity between the source and target of the
                move or copy). For example "R100" or "C75".
     <path>      The pathname.  In a renamed/copied entry, this
-               is the path in the index and in the working tree.
+               is the target path.
     <sep>       When the `-z` option is used, the 2 pathnames are separated
                with a NUL (ASCII 0x00) byte; otherwise, a tab (ASCII 0x09)
                byte separates them.
-    <origPath>  The pathname in the commit at HEAD.  This is only
-               present in a renamed/copied entry, and tells
-               where the renamed/copied contents came from.
+    <origPath>  The pathname in the commit at HEAD or in the index.
+               This is only present in a renamed/copied entry, and
+               tells where the renamed/copied contents came from.
     --------------------------------------------------------
 
 Unmerged entries have the following format; the first character is
index ff61200..71c5618 100644 (file)
@@ -70,8 +70,8 @@ status [--cached] [--recursive] [--] [<path>...]::
        Show the status of the submodules. This will print the SHA-1 of the
        currently checked out commit for each submodule, along with the
        submodule path and the output of 'git describe' for the
-       SHA-1. Each SHA-1 will be prefixed with `-` if the submodule is not
-       initialized, `+` if the currently checked out submodule commit
+       SHA-1. Each SHA-1 will possibly be prefixed with `-` if the submodule is
+       not initialized, `+` if the currently checked out submodule commit
        does not match the SHA-1 found in the index of the containing
        repository and `U` if the submodule has merge conflicts.
 +
@@ -132,15 +132,15 @@ expects by cloning missing submodules and updating the working tree of
 the submodules. The "updating" can be done in several ways depending
 on command line options and the value of `submodule.<name>.update`
 configuration variable. The command line option takes precedence over
-the configuration variable. if neither is given, a checkout is performed.
-update procedures supported both from the command line as well as setting
-`submodule.<name>.update`:
+the configuration variable. If neither is given, a 'checkout' is performed.
+The 'update' procedures supported both from the command line as well as
+through the `submodule.<name>.update` configuration are:
 
        checkout;; the commit recorded in the superproject will be
            checked out in the submodule on a detached HEAD.
 +
 If `--force` is specified, the submodule will be checked out (using
-`git checkout --force` if appropriate), even if the commit specified
+`git checkout --force`), even if the commit specified
 in the index of the containing repository already matches the commit
 checked out in the submodule.
 
@@ -150,8 +150,8 @@ checked out in the submodule.
        merge;; the commit recorded in the superproject will be merged
            into the current branch in the submodule.
 
-The following procedures are only available via the `submodule.<name>.update`
-configuration variable:
+The following 'update' procedures are only available via the
+`submodule.<name>.update` configuration variable:
 
        custom command;; arbitrary shell command that takes a single
            argument (the sha1 of the commit recorded in the
index 3f4161a..8163b57 100644 (file)
@@ -646,6 +646,16 @@ of clones and fetches.
        variable.
        See `GIT_TRACE` for available trace output options.
 
+`GIT_TRACE_CURL_NO_DATA`::
+       When a curl trace is enabled (see `GIT_TRACE_CURL` above), do not dump
+       data (that is, only dump info lines and headers).
+
+`GIT_REDACT_COOKIES`::
+       This can be set to a comma-separated list of strings. When a curl trace
+       is enabled (see `GIT_TRACE_CURL` above), whenever a "Cookies:" header
+       sent by the client is dumped, values of cookies whose key is in that
+       list (case-sensitive) are redacted.
+
 `GIT_LITERAL_PATHSPECS`::
        Setting this variable to `1` will cause Git to treat all
        pathspecs literally, rather than as glob patterns. For example,
index 30687de..c21f5ca 100644 (file)
@@ -392,46 +392,14 @@ Long Running Filter Process
 If the filter command (a string value) is defined via
 `filter.<driver>.process` then Git can process all blobs with a
 single filter invocation for the entire life of a single Git
-command. This is achieved by using a packet format (pkt-line,
-see technical/protocol-common.txt) based protocol over standard
-input and standard output as follows. All packets, except for the
-"*CONTENT" packets and the "0000" flush packet, are considered
-text and therefore are terminated by a LF.
-
-Git starts the filter when it encounters the first file
-that needs to be cleaned or smudged. After the filter started
-Git sends a welcome message ("git-filter-client"), a list of supported
-protocol version numbers, and a flush packet. Git expects to read a welcome
-response message ("git-filter-server"), exactly one protocol version number
-from the previously sent list, and a flush packet. All further
-communication will be based on the selected version. The remaining
-protocol description below documents "version=2". Please note that
-"version=42" in the example below does not exist and is only there
-to illustrate how the protocol would look like with more than one
-version.
-
-After the version negotiation Git sends a list of all capabilities that
-it supports and a flush packet. Git expects to read a list of desired
-capabilities, which must be a subset of the supported capabilities list,
-and a flush packet as response:
-------------------------
-packet:          git> git-filter-client
-packet:          git> version=2
-packet:          git> version=42
-packet:          git> 0000
-packet:          git< git-filter-server
-packet:          git< version=2
-packet:          git< 0000
-packet:          git> capability=clean
-packet:          git> capability=smudge
-packet:          git> capability=not-yet-invented
-packet:          git> 0000
-packet:          git< capability=clean
-packet:          git< capability=smudge
-packet:          git< 0000
-------------------------
-Supported filter capabilities in version 2 are "clean", "smudge",
-and "delay".
+command. This is achieved by using the long-running process protocol
+(described in technical/long-running-process-protocol.txt).
+
+When Git encounters the first file that needs to be cleaned or smudged,
+it starts the filter and performs the handshake. In the handshake, the
+welcome message sent by Git is "git-filter-client", only version 2 is
+suppported, and the supported capabilities are "clean", "smudge", and
+"delay".
 
 Afterwards Git sends a list of "key=value" pairs terminated with
 a flush packet. The list will contain at least the filter command
@@ -517,12 +485,6 @@ the protocol then Git will stop the filter process and restart it
 with the next file that needs to be processed. Depending on the
 `filter.<driver>.required` flag Git will interpret that as error.
 
-After the filter has processed a command it is expected to wait for
-a "key=value" list containing the next command. Git will close
-the command pipe on exit. The filter is expected to detect EOF
-and exit gracefully on its own. Git will wait until the filter
-process has stopped.
-
 Delay
 ^^^^^
 
index 4a584f3..4b8c93e 100644 (file)
@@ -466,6 +466,13 @@ set by Git if the remote helper has the 'option' capability.
        Transmit <string> as a push option. As the push option
        must not contain LF or NUL characters, the string is not encoded.
 
+'option from-promisor' {'true'|'false'}::
+       Indicate that these objects are being fetched from a promisor.
+
+'option no-dependents' {'true'|'false'}::
+       Indicate that only the objects wanted need to be fetched, not
+       their dependents.
+
 SEE ALSO
 --------
 linkgit:git-remote[1]
index 46cf120..4d6c177 100644 (file)
@@ -36,8 +36,8 @@ The `gitlink` entry contains the object name of the commit that the
 superproject expects the submodule’s working directory to be at.
 
 The section `submodule.foo.*` in the `.gitmodules` file gives additional
-hints to Gits porcelain layer such as where to obtain the submodule via
-the `submodule.foo.url` setting.
+hints to Git's porcelain layer. For example, the `submodule.foo.url`
+setting specifies where to obtain the submodule.
 
 Submodules can be used for at least two different use cases:
 
@@ -51,18 +51,21 @@ Submodules can be used for at least two different use cases:
 
 2. Splitting a (logically single) project into multiple
    repositories and tying them back together. This can be used to
-   overcome current limitations of Gits implementation to have
+   overcome current limitations of Git's implementation to have
    finer grained access:
 
-    * Size of the git repository:
+    * Size of the Git repository:
       In its current form Git scales up poorly for large repositories containing
       content that is not compressed by delta computation between trees.
-      However you can also use submodules to e.g. hold large binary assets
-      and these repositories are then shallowly cloned such that you do not
+      For example, you can use submodules to hold large binary assets
+      and these repositories can be shallowly cloned such that you do not
       have a large history locally.
     * Transfer size:
       In its current form Git requires the whole working tree present. It
       does not allow partial trees to be transferred in fetch or clone.
+      If the project you work on consists of multiple repositories tied
+      together as submodules in a superproject, you can avoid fetching the
+      working trees of the repositories you are not interested in.
     * Access control:
       By restricting user access to submodules, this can be used to implement
       read/write policies for different users.
@@ -73,9 +76,10 @@ The configuration of submodules
 Submodule operations can be configured using the following mechanisms
 (from highest to lowest precedence):
 
- * The command line for those commands that support taking submodule specs.
-   Most commands have a boolean flag '--recurse-submodules' whether to
-   recurse into submodules. Examples are `ls-files` or `checkout`.
+ * The command line for those commands that support taking submodules
+   as part of their pathspecs. Most commands have a boolean flag
+   `--recurse-submodules` which specify whether to recurse into submodules.
+   Examples are `grep` and `checkout`.
    Some commands take enums, such as `fetch` and `push`, where you can
    specify how submodules are affected.
 
@@ -87,8 +91,8 @@ Submodule operations can be configured using the following mechanisms
 For example an effect from the submodule's `.gitignore` file
 would be observed when you run `git status --ignore-submodules=none` in
 the superproject. This collects information from the submodule's working
-directory by running `status` in the submodule, which does pay attention
-to its `.gitignore` file.
+directory by running `status` in the submodule while paying attention
+to the `.gitignore` file of the submodule.
 +
 The submodule's `$GIT_DIR/config` file would come into play when running
 `git push --recurse-submodules=check` in the superproject, as this would
@@ -97,20 +101,20 @@ remotes are configured in the submodule as usual in the `$GIT_DIR/config`
 file.
 
  * The configuration file `$GIT_DIR/config` in the superproject.
-   Typical configuration at this place is controlling if a submodule
-   is recursed into at all via the `active` flag for example.
+   Git only recurses into active submodules (see "ACTIVE SUBMODULES"
+   section below).
 +
 If the submodule is not yet initialized, then the configuration
-inside the submodule does not exist yet, so configuration where to
+inside the submodule does not exist yet, so where to
 obtain the submodule from is configured here for example.
 
- * the `.gitmodules` file inside the superproject. Additionally to the
-   required mapping between submodule's name and path, a project usually
+ * The `.gitmodules` file inside the superproject. A project usually
    uses this file to suggest defaults for the upstream collection
-   of repositories.
+   of repositories for the mapping that is required between a
+   submodule's name and its path.
 +
-This file mainly serves as the mapping between name and path in
-the superproject, such that the submodule's git directory can be
+This file mainly serves as the mapping between the name and path of submodules
+in the superproject, such that the submodule's Git directory can be
 located.
 +
 If the submodule has never been initialized, this is the only place
@@ -137,8 +141,8 @@ directory is automatically moved to `$GIT_DIR/modules/<name>/`
 of the superproject.
 
  * Deinitialized submodule: A `gitlink`, and a `.gitmodules` entry,
-but no submodule working directory. The submodule’s git directory
-may be there as after deinitializing the git directory is kept around.
+but no submodule working directory. The submodule’s Git directory
+may be there as after deinitializing the Git directory is kept around.
 The directory which is supposed to be the working directory is empty instead.
 +
 A submodule can be deinitialized by running `git submodule deinit`.
@@ -160,6 +164,60 @@ from another repository.
 To completely remove a submodule, manually delete
 `$GIT_DIR/modules/<name>/`.
 
+ACTIVE SUBMODULES
+-----------------
+
+A submodule is considered active,
+
+  (a) if `submodule.<name>.active` is set to `true`
+     or
+  (b) if the submodule's path matches the pathspec in `submodule.active`
+     or
+  (c) if `submodule.<name>.url` is set.
+
+and these are evaluated in this order.
+
+For example:
+
+  [submodule "foo"]
+    active = false
+    url = https://example.org/foo
+  [submodule "bar"]
+    active = true
+    url = https://example.org/bar
+  [submodule "baz"]
+    url = https://example.org/baz
+
+In the above config only the submodule 'bar' and 'baz' are active,
+'bar' due to (a) and 'baz' due to (c). 'foo' is inactive because
+(a) takes precedence over (c)
+
+Note that (c) is a historical artefact and will be ignored if the
+(a) and (b) specify that the submodule is not active. In other words,
+if we have an `submodule.<name>.active` set to `false` or if the
+submodule's path is excluded in the pathspec in `submodule.active`, the
+url doesn't matter whether it is present or not. This is illustrated in
+the example that follows.
+
+  [submodule "foo"]
+    active = true
+    url = https://example.org/foo
+  [submodule "bar"]
+    url = https://example.org/bar
+  [submodule "baz"]
+    url = https://example.org/baz
+  [submodule "bob"]
+    ignore = true
+  [submodule]
+    active = b*
+    active = :(exclude) baz
+
+In here all submodules except 'baz' (foo, bar, bob) are active.
+'foo' due to its own active flag and all the others due to the
+submodule active pathspec, which specifies that any submodule
+starting with 'b' except 'baz' are also active, regardless of the
+presence of the .url field.
+
 Workflow for a third party library
 ----------------------------------
 
index 22f5c9b..7b27363 100644 (file)
@@ -750,10 +750,21 @@ The form '--missing=allow-any' will allow object traversal to continue
 if a missing object is encountered.  Missing objects will silently be
 omitted from the results.
 +
+The form '--missing=allow-promisor' is like 'allow-any', but will only
+allow object traversal to continue for EXPECTED promisor missing objects.
+Unexpected missing objects will raise an error.
++
 The form '--missing=print' is like 'allow-any', but will also print a
 list of the missing objects.  Object IDs are prefixed with a ``?'' character.
 endif::git-rev-list[]
 
+--exclude-promisor-objects::
+       (For internal use only.)  Prefilter object traversal at
+       promisor boundary.  This is used with partial clone.  This is
+       stronger than `--missing=allow-promisor` because it limits the
+       traversal, rather than just silencing errors about missing
+       objects.
+
 --no-walk[=(sorted|unsorted)]::
        Only show the given commits, but do not traverse their ancestors.
        This has no effect if a range is specified. If the argument
diff --git a/Documentation/technical/long-running-process-protocol.txt b/Documentation/technical/long-running-process-protocol.txt
new file mode 100644 (file)
index 0000000..aa0aa9a
--- /dev/null
@@ -0,0 +1,50 @@
+Long-running process protocol
+=============================
+
+This protocol is used when Git needs to communicate with an external
+process throughout the entire life of a single Git command. All
+communication is in pkt-line format (see technical/protocol-common.txt)
+over standard input and standard output.
+
+Handshake
+---------
+
+Git starts by sending a welcome message (for example,
+"git-filter-client"), a list of supported protocol version numbers, and
+a flush packet. Git expects to read the welcome message with "server"
+instead of "client" (for example, "git-filter-server"), exactly one
+protocol version number from the previously sent list, and a flush
+packet. All further communication will be based on the selected version.
+The remaining protocol description below documents "version=2". Please
+note that "version=42" in the example below does not exist and is only
+there to illustrate how the protocol would look like with more than one
+version.
+
+After the version negotiation Git sends a list of all capabilities that
+it supports and a flush packet. Git expects to read a list of desired
+capabilities, which must be a subset of the supported capabilities list,
+and a flush packet as response:
+------------------------
+packet:          git> git-filter-client
+packet:          git> version=2
+packet:          git> version=42
+packet:          git> 0000
+packet:          git< git-filter-server
+packet:          git< version=2
+packet:          git< 0000
+packet:          git> capability=clean
+packet:          git> capability=smudge
+packet:          git> capability=not-yet-invented
+packet:          git> 0000
+packet:          git< capability=clean
+packet:          git< capability=smudge
+packet:          git< 0000
+------------------------
+
+Shutdown
+--------
+
+Git will close
+the command pipe on exit. The filter is expected to detect EOF
+and exit gracefully on its own. Git will wait until the filter
+process has stopped.
index cd31edc..7fee6b7 100644 (file)
@@ -241,6 +241,7 @@ out of what the server said it could do with the first 'want' line.
   upload-request    =  want-list
                       *shallow-line
                       *1depth-request
+                      [filter-request]
                       flush-pkt
 
   want-list         =  first-want
@@ -256,6 +257,8 @@ out of what the server said it could do with the first 'want' line.
   additional-want   =  PKT-LINE("want" SP obj-id)
 
   depth             =  1*DIGIT
+
+  filter-request    =  PKT-LINE("filter" SP filter-spec)
 ----
 
 Clients MUST send all the obj-ids it wants from the reference
@@ -278,6 +281,11 @@ complete those commits. Commits whose parents are not received as a
 result are defined as shallow and marked as such in the server. This
 information is sent back to the client in the next step.
 
+The client can optionally request that pack-objects omit various
+objects from the packfile using one of several filtering techniques.
+These are intended for use with partial clone and partial fetch
+operations.  See `rev-list` for possible "filter-spec" values.
+
 Once all the 'want's and 'shallow's (and optional 'deepen') are
 transferred, clients MUST send a flush-pkt, to tell the server side
 that it is done sending the list.
index 26dcc6f..332d209 100644 (file)
@@ -309,3 +309,11 @@ to accept a signed push certificate, and asks the <nonce> to be
 included in the push certificate.  A send-pack client MUST NOT
 send a push-cert packet unless the receive-pack server advertises
 this capability.
+
+filter
+------
+
+If the upload-pack server advertises the 'filter' capability,
+fetch-pack may send "filter" commands to request a partial clone
+or partial fetch and request that the server omit various objects
+from the packfile.
index 00ad379..e03eacc 100644 (file)
@@ -86,3 +86,15 @@ for testing format-1 compatibility.
 When the config key `extensions.preciousObjects` is set to `true`,
 objects in the repository MUST NOT be deleted (e.g., by `git-prune` or
 `git repack -d`).
+
+`partialclone`
+~~~~~~~~~~~~~~
+
+When the config key `extensions.partialclone` is set, it indicates
+that the repo was created with a partial clone (or later performed
+a partial fetch) and that the remote may have omitted sending
+certain unwanted objects.  Such a remote is called a "promisor remote"
+and it promises that all such omitted objects can be fetched from it
+in the future.
+
+The value of this key is the name of the promisor remote.
index e4c858d..0c86f1c 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.16.0
+DEF_VER=v2.16.GIT
 
 LF='
 '
diff --git a/INSTALL b/INSTALL
index ffb071e..808e07b 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -84,9 +84,24 @@ Issues of note:
 
        GIT_EXEC_PATH=`pwd`
        PATH=`pwd`:$PATH
-       GITPERLLIB=`pwd`/perl/blib/lib
+       GITPERLLIB=`pwd`/perl/build/lib
        export GIT_EXEC_PATH PATH GITPERLLIB
 
+ - By default (unless NO_PERL is provided) Git will ship various perl
+   scripts & libraries it needs. However, for simplicity it doesn't
+   use the ExtUtils::MakeMaker toolchain to decide where to place the
+   perl libraries. Depending on the system this can result in the perl
+   libraries not being where you'd like them if they're expected to be
+   used by things other than Git itself.
+
+   Manually supplying a perllibdir prefix should fix this, if this is
+   a problem you care about, e.g.:
+
+       prefix=/usr perllibdir=/usr/$(/usr/bin/perl -MConfig -wle 'print substr $Config{installsitelib}, 1 + length $Config{siteprefixexp}')
+
+   Will result in e.g. perllibdir=/usr/share/perl/5.26.1 on Debian,
+   perllibdir=/usr/share/perl5 (which we'd use by default) on CentOS.
+
  - Git is reasonably self-sufficient, but does depend on a few external
    programs and libraries.  Git can be used without most of them by adding
    the approriate "NO_<LIBRARY>=YesPlease" to the make command line or
index 1a9b23b..c56fdc1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -294,9 +294,6 @@ all::
 #
 # Define PERL_PATH to the path of your Perl binary (usually /usr/bin/perl).
 #
-# Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
-# MakeMaker (e.g. using ActiveState under Cygwin).
-#
 # Define NO_PERL if you do not want Perl scripts or libraries at all.
 #
 # Define PYTHON_PATH to the path of your Python binary (often /usr/bin/python
@@ -479,6 +476,7 @@ gitexecdir = libexec/git-core
 mergetoolsdir = $(gitexecdir)/mergetools
 sharedir = $(prefix)/share
 gitwebdir = $(sharedir)/gitweb
+perllibdir = $(sharedir)/perl5
 localedir = $(sharedir)/locale
 template_dir = share/git-core/templates
 htmldir = $(prefix)/share/doc/git-doc
@@ -492,7 +490,7 @@ mandir_relative = $(patsubst $(prefix)/%,%,$(mandir))
 infodir_relative = $(patsubst $(prefix)/%,%,$(infodir))
 htmldir_relative = $(patsubst $(prefix)/%,%,$(htmldir))
 
-export prefix bindir sharedir sysconfdir gitwebdir localedir
+export prefix bindir sharedir sysconfdir gitwebdir perllibdir localedir
 
 CC = cc
 AR = ar
@@ -804,6 +802,7 @@ LIB_OBJS += ewah/ewah_bitmap.o
 LIB_OBJS += ewah/ewah_io.o
 LIB_OBJS += ewah/ewah_rlw.o
 LIB_OBJS += exec_cmd.o
+LIB_OBJS += fetch-object.o
 LIB_OBJS += fetch-pack.o
 LIB_OBJS += fsck.o
 LIB_OBJS += fsmonitor.o
@@ -832,7 +831,6 @@ LIB_OBJS += merge.o
 LIB_OBJS += merge-blobs.o
 LIB_OBJS += merge-recursive.o
 LIB_OBJS += mergesort.o
-LIB_OBJS += mru.o
 LIB_OBJS += name-hash.o
 LIB_OBJS += notes.o
 LIB_OBJS += notes-cache.o
@@ -1515,7 +1513,9 @@ else
        LIB_OBJS += sha1dc_git.o
 ifdef DC_SHA1_EXTERNAL
        ifdef DC_SHA1_SUBMODULE
+               ifneq ($(DC_SHA1_SUBMODULE),auto)
 $(error Only set DC_SHA1_EXTERNAL or DC_SHA1_SUBMODULE, not both)
+               endif
        endif
        BASIC_CFLAGS += -DDC_SHA1_EXTERNAL
        EXTLIBS += -lsha1detectcoll
@@ -1543,9 +1543,6 @@ ifdef SHA1_MAX_BLOCK_SIZE
        LIB_OBJS += compat/sha1-chunked.o
        BASIC_CFLAGS += -DSHA1_MAX_BLOCK_SIZE="$(SHA1_MAX_BLOCK_SIZE)"
 endif
-ifdef NO_PERL_MAKEMAKER
-       export NO_PERL_MAKEMAKER
-endif
 ifdef NO_HSTRERROR
        COMPAT_CFLAGS += -DNO_HSTRERROR
        COMPAT_OBJS += compat/hstrerror.o
@@ -1732,8 +1729,10 @@ ETC_GITATTRIBUTES_SQ = $(subst ','\'',$(ETC_GITATTRIBUTES))
 DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
 bindir_SQ = $(subst ','\'',$(bindir))
 bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
+mandir_SQ = $(subst ','\'',$(mandir))
 mandir_relative_SQ = $(subst ','\'',$(mandir_relative))
 infodir_relative_SQ = $(subst ','\'',$(infodir_relative))
+perllibdir_SQ = $(subst ','\'',$(perllibdir))
 localedir_SQ = $(subst ','\'',$(localedir))
 gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
 template_dir_SQ = $(subst ','\'',$(template_dir))
@@ -1843,9 +1842,6 @@ all::
 ifndef NO_TCLTK
        $(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) gitexecdir='$(gitexec_instdir_SQ)' all
        $(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all
-endif
-ifndef NO_PERL
-       $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' localedir='$(localedir_SQ)' all
 endif
        $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)'
 
@@ -1928,7 +1924,8 @@ common-cmds.h: $(wildcard Documentation/git-*.txt)
 
 SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
        $(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
-       $(gitwebdir_SQ):$(PERL_PATH_SQ):$(SANE_TEXT_GREP):$(PAGER_ENV)
+       $(gitwebdir_SQ):$(PERL_PATH_SQ):$(SANE_TEXT_GREP):$(PAGER_ENV):\
+       $(perllibdir_SQ)
 define cmd_munge_script
 $(RM) $@ $@+ && \
 sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
@@ -1972,23 +1969,12 @@ git.res: git.rc GIT-VERSION-FILE
 $(SCRIPT_PERL_GEN): GIT-BUILD-OPTIONS
 
 ifndef NO_PERL
-$(SCRIPT_PERL_GEN): perl/perl.mak
+$(SCRIPT_PERL_GEN):
 
-perl/perl.mak: perl/PM.stamp
-
-perl/PM.stamp: FORCE
-       @$(FIND) perl -type f -name '*.pm' | sort >$@+ && \
-       $(PERL_PATH) -V >>$@+ && \
-       { cmp $@+ $@ >/dev/null 2>/dev/null || mv $@+ $@; } && \
-       $(RM) $@+
-
-perl/perl.mak: GIT-CFLAGS GIT-PREFIX perl/Makefile perl/Makefile.PL
-       $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
-
-PERL_DEFINES = $(PERL_PATH_SQ):$(PERLLIB_EXTRA_SQ)
-$(SCRIPT_PERL_GEN): % : %.perl perl/perl.mak GIT-PERL-DEFINES GIT-VERSION-FILE
+PERL_DEFINES = $(PERL_PATH_SQ):$(PERLLIB_EXTRA_SQ):$(perllibdir_SQ)
+$(SCRIPT_PERL_GEN): % : %.perl GIT-PERL-DEFINES GIT-VERSION-FILE
        $(QUIET_GEN)$(RM) $@ $@+ && \
-       INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \
+       INSTLIBDIR='$(perllibdir_SQ)' && \
        INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \
        INSTLIBDIR="$$INSTLIBDIR$${INSTLIBDIR_EXTRA:+:$$INSTLIBDIR_EXTRA}" && \
        sed -e '1{' \
@@ -2312,6 +2298,21 @@ endif
 po/build/locale/%/LC_MESSAGES/git.mo: po/%.po
        $(QUIET_MSGFMT)mkdir -p $(dir $@) && $(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))
+
+ifndef NO_PERL
+all:: $(LIB_PERL_GEN)
+endif
+
+perl/build/lib/%.pm: perl/%.pm
+       $(QUIET_GEN)mkdir -p $(dir $@) && \
+       sed -e 's|@@LOCALEDIR@@|$(localedir_SQ)|g' < $< > $@
+
+perl/build/man/man3/Git.3pm: perl/Git.pm
+       $(QUIET_GEN)mkdir -p $(dir $@) && \
+       pod2man $< $@
+
 FIND_SOURCE_FILES = ( \
        git ls-files \
                '*.[hcS]' \
@@ -2572,7 +2573,9 @@ ifndef NO_GETTEXT
        (cd '$(DESTDIR_SQ)$(localedir_SQ)' && umask 022 && $(TAR) xof -)
 endif
 ifndef NO_PERL
-       $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
+       $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perllibdir_SQ)'
+       (cd perl/build/lib && $(TAR) cf - .) | \
+       (cd '$(DESTDIR_SQ)$(perllibdir_SQ)' && umask 022 && $(TAR) xof -)
        $(MAKE) -C gitweb install
 endif
 ifndef NO_TCLTK
@@ -2622,12 +2625,17 @@ endif
 install-gitweb:
        $(MAKE) -C gitweb install
 
-install-doc:
+install-doc: install-man-perl
        $(MAKE) -C Documentation install
 
-install-man:
+install-man: install-man-perl
        $(MAKE) -C Documentation install-man
 
+install-man-perl: perl/build/man/man3/Git.3pm
+       $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(mandir_SQ)/man3'
+       (cd perl/build/man/man3 && $(TAR) cf - .) | \
+       (cd '$(DESTDIR_SQ)$(mandir_SQ)/man3' && umask 022 && $(TAR) xof -)
+
 install-html:
        $(MAKE) -C Documentation install-html
 
@@ -2662,6 +2670,21 @@ dist: git-archive$(X) configure
                $(GIT_TARNAME)/configure \
                $(GIT_TARNAME)/version \
                $(GIT_TARNAME)/git-gui/version
+ifdef DC_SHA1_SUBMODULE
+       @mkdir -p $(GIT_TARNAME)/sha1collisiondetection/lib
+       @cp sha1collisiondetection/LICENSE.txt \
+               $(GIT_TARNAME)/sha1collisiondetection/
+       @cp sha1collisiondetection/LICENSE.txt \
+               $(GIT_TARNAME)/sha1collisiondetection/
+       @cp sha1collisiondetection/lib/sha1.[ch] \
+               $(GIT_TARNAME)/sha1collisiondetection/lib/
+       @cp sha1collisiondetection/lib/ubc_check.[ch] \
+               $(GIT_TARNAME)/sha1collisiondetection/lib/
+       $(TAR) rf $(GIT_TARNAME).tar \
+               $(GIT_TARNAME)/sha1collisiondetection/LICENSE.txt \
+               $(GIT_TARNAME)/sha1collisiondetection/lib/sha1.[ch] \
+               $(GIT_TARNAME)/sha1collisiondetection/lib/ubc_check.[ch]
+endif
        @$(RM) -r $(GIT_TARNAME)
        gzip -f -9 $(GIT_TARNAME).tar
 
@@ -2719,7 +2742,7 @@ clean: profile-clean coverage-clean
        $(MAKE) -C Documentation/ clean
 ifndef NO_PERL
        $(MAKE) -C gitweb clean
-       $(MAKE) -C perl clean
+       $(RM) -r perl/build/
 endif
        $(MAKE) -C templates/ clean
        $(MAKE) -C t/ clean
index bec73d6..7a6dc06 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.16.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.17.0.txt
\ No newline at end of file
diff --git a/apply.c b/apply.c
index 321a9fa..f8b67bf 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -2263,8 +2263,8 @@ static void show_stats(struct apply_state *state, struct patch *patch)
 static int read_old_data(struct stat *st, struct patch *patch,
                         const char *path, struct strbuf *buf)
 {
-       enum safe_crlf safe_crlf = patch->crlf_in_old ?
-               SAFE_CRLF_KEEP_CRLF : SAFE_CRLF_RENORMALIZE;
+       int conv_flags = patch->crlf_in_old ?
+               CONV_EOL_KEEP_CRLF : CONV_EOL_RENORMALIZE;
        switch (st->st_mode & S_IFMT) {
        case S_IFLNK:
                if (strbuf_readlink(buf, path, st->st_size) < 0)
@@ -2281,7 +2281,7 @@ static int read_old_data(struct stat *st, struct patch *patch,
                 * should never look at the index when explicit crlf option
                 * is given.
                 */
-               convert_to_git(NULL, path, buf->buf, buf->len, buf, safe_crlf);
+               convert_to_git(NULL, path, buf->buf, buf->len, buf, conv_flags);
                return 0;
        default:
                return -1;
index 2f3008b..f6d05bd 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -132,7 +132,7 @@ static void show_list(const char *debug, int counted, int nr,
                unsigned flags = commit->object.flags;
                enum object_type type;
                unsigned long size;
-               char *buf = read_sha1_file(commit->object.sha1, &type, &size);
+               char *buf = read_sha1_file(commit->object.oid.hash, &type, &size);
                const char *subject_start;
                int subject_len;
 
@@ -144,10 +144,10 @@ static void show_list(const char *debug, int counted, int nr,
                        fprintf(stderr, "%3d", weight(p));
                else
                        fprintf(stderr, "---");
-               fprintf(stderr, " %.*s", 8, sha1_to_hex(commit->object.sha1));
+               fprintf(stderr, " %.*s", 8, sha1_to_hex(commit->object.oid.hash));
                for (pp = commit->parents; pp; pp = pp->next)
                        fprintf(stderr, " %.*s", 8,
-                               sha1_to_hex(pp->item->object.sha1));
+                               sha1_to_hex(pp->item->object.oid.hash));
 
                subject_len = find_commit_subject(buf, &subject_start);
                if (subject_len)
@@ -792,11 +792,9 @@ static void handle_skipped_merge_base(const struct object_id *mb)
  * - If one is "skipped", we can't know but we should warn.
  * - If we don't know, we should check it out and ask the user to test.
  */
-static void check_merge_bases(int no_checkout)
+static void check_merge_bases(int rev_nr, struct commit **rev, int no_checkout)
 {
        struct commit_list *result;
-       int rev_nr;
-       struct commit **rev = get_bad_and_good_commits(&rev_nr);
 
        result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1);
 
@@ -814,34 +812,21 @@ static void check_merge_bases(int no_checkout)
                }
        }
 
-       free(rev);
        free_commit_list(result);
 }
 
-static int check_ancestors(const char *prefix)
+static int check_ancestors(int rev_nr, struct commit **rev, const char *prefix)
 {
        struct rev_info revs;
-       struct object_array pending_copy;
        int res;
 
        bisect_rev_setup(&revs, prefix, "^%s", "%s", 0);
 
-       /* Save pending objects, so they can be cleaned up later. */
-       pending_copy = revs.pending;
-       revs.leak_pending = 1;
-
-       /*
-        * bisect_common calls prepare_revision_walk right away, which
-        * (together with .leak_pending = 1) makes us the sole owner of
-        * the list of pending objects.
-        */
        bisect_common(&revs);
        res = (revs.commits != NULL);
 
        /* Clean up objects used, as they will be reused. */
-       clear_commit_marks_for_object_array(&pending_copy, ALL_REV_FLAGS);
-
-       object_array_clear(&pending_copy);
+       clear_commit_marks_many(rev_nr, rev, ALL_REV_FLAGS);
 
        return res;
 }
@@ -858,7 +843,8 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
 {
        char *filename = git_pathdup("BISECT_ANCESTORS_OK");
        struct stat st;
-       int fd;
+       int fd, rev_nr;
+       struct commit **rev;
 
        if (!current_bad_oid)
                die(_("a %s revision is needed"), term_bad);
@@ -872,8 +858,10 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
                goto done;
 
        /* Check if all good revs are ancestor of the bad rev. */
-       if (check_ancestors(prefix))
-               check_merge_bases(no_checkout);
+       rev = get_bad_and_good_commits(&rev_nr);
+       if (check_ancestors(rev_nr, rev, prefix))
+               check_merge_bases(rev_nr, rev, no_checkout);
+       free(rev);
 
        /* Create file BISECT_ANCESTORS_OK. */
        fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
index acfe9d3..5bdd2d7 100644 (file)
@@ -1061,7 +1061,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
        }
        write_state_text(state, "scissors", str);
 
-       sq_quote_argv(&sb, state->git_apply_opts.argv, 0);
+       sq_quote_argv(&sb, state->git_apply_opts.argv);
        write_state_text(state, "apply-opt", sb.buf);
 
        if (state->rebasing)
index f5fa4fd..cf9ea5c 100644 (file)
@@ -475,6 +475,8 @@ static int batch_objects(struct batch_options *opt)
 
                for_each_loose_object(batch_loose_object, &sa, 0);
                for_each_packed_object(batch_packed_object, &sa, 0);
+               if (repository_format_partial_clone)
+                       warning("This repository has extensions.partialClone set. Some objects may not be loaded.");
 
                cb.opt = opt;
                cb.expand = &data;
index 8bdc927..c54c78d 100644 (file)
@@ -791,7 +791,6 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new)
 {
        struct rev_info revs;
        struct object *object = &old->object;
-       struct object_array refs;
 
        init_revisions(&revs, NULL);
        setup_revisions(0, NULL, &revs, NULL);
@@ -802,14 +801,6 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new)
        for_each_ref(add_pending_uninteresting_ref, &revs);
        add_pending_oid(&revs, "HEAD", &new->object.oid, UNINTERESTING);
 
-       /* Save pending objects, so they can be cleaned up later. */
-       refs = revs.pending;
-       revs.leak_pending = 1;
-
-       /*
-        * prepare_revision_walk (together with .leak_pending = 1) makes us
-        * the sole owner of the list of pending objects.
-        */
        if (prepare_revision_walk(&revs))
                die(_("internal error in revision walk"));
        if (!(old->object.flags & UNINTERESTING))
@@ -818,9 +809,7 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new)
                describe_detached_head(_("Previous HEAD position was"), old);
 
        /* Clean up objects used, as they will be reused. */
-       clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
-
-       object_array_clear(&refs);
+       clear_commit_marks_all(ALL_REV_FLAGS);
 }
 
 static int switch_branches(const struct checkout_opts *opts,
index 2da71db..101c27a 100644 (file)
@@ -26,6 +26,7 @@
 #include "run-command.h"
 #include "connected.h"
 #include "packfile.h"
+#include "list-objects-filter-options.h"
 
 /*
  * Overall FIXMEs:
@@ -60,6 +61,7 @@ static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP;
 static int option_dissociate;
 static int max_jobs = -1;
 static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP;
+static struct list_objects_filter_options filter_options;
 
 static int recurse_submodules_cb(const struct option *opt,
                                 const char *arg, int unset)
@@ -135,6 +137,7 @@ static struct option builtin_clone_options[] = {
                        TRANSPORT_FAMILY_IPV4),
        OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
                        TRANSPORT_FAMILY_IPV6),
+       OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
        OPT_END()
 };
 
@@ -473,7 +476,9 @@ static void clone_local(const char *src_repo, const char *dest_repo)
 }
 
 static const char *junk_work_tree;
+static int junk_work_tree_flags;
 static const char *junk_git_dir;
+static int junk_git_dir_flags;
 static enum {
        JUNK_LEAVE_NONE,
        JUNK_LEAVE_REPO,
@@ -502,12 +507,12 @@ static void remove_junk(void)
 
        if (junk_git_dir) {
                strbuf_addstr(&sb, junk_git_dir);
-               remove_dir_recursively(&sb, 0);
+               remove_dir_recursively(&sb, junk_git_dir_flags);
                strbuf_reset(&sb);
        }
        if (junk_work_tree) {
                strbuf_addstr(&sb, junk_work_tree);
-               remove_dir_recursively(&sb, 0);
+               remove_dir_recursively(&sb, junk_work_tree_flags);
        }
        strbuf_release(&sb);
 }
@@ -863,10 +868,15 @@ static void dissociate_from_references(void)
        free(alternates);
 }
 
+static int dir_exists(const char *path)
+{
+       struct stat sb;
+       return !stat(path, &sb);
+}
+
 int cmd_clone(int argc, const char **argv, const char *prefix)
 {
        int is_bundle = 0, is_local;
-       struct stat buf;
        const char *repo_name, *repo, *work_tree, *git_dir;
        char *path, *dir;
        int dest_exists;
@@ -886,6 +896,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        struct refspec *refspec;
        const char *fetch_pattern;
 
+       fetch_if_missing = 0;
+
        packet_trace_identity("clone");
        argc = parse_options(argc, argv, prefix, builtin_clone_options,
                             builtin_clone_usage, 0);
@@ -938,7 +950,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                dir = guess_dir_name(repo_name, is_bundle, option_bare);
        strip_trailing_slashes(dir);
 
-       dest_exists = !stat(dir, &buf);
+       dest_exists = dir_exists(dir);
        if (dest_exists && !is_empty_dir(dir))
                die(_("destination path '%s' already exists and is not "
                        "an empty directory."), dir);
@@ -949,7 +961,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                work_tree = NULL;
        else {
                work_tree = getenv("GIT_WORK_TREE");
-               if (work_tree && !stat(work_tree, &buf))
+               if (work_tree && dir_exists(work_tree))
                        die(_("working tree '%s' already exists."), work_tree);
        }
 
@@ -967,14 +979,24 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                if (safe_create_leading_directories_const(work_tree) < 0)
                        die_errno(_("could not create leading directories of '%s'"),
                                  work_tree);
-               if (!dest_exists && mkdir(work_tree, 0777))
+               if (dest_exists)
+                       junk_work_tree_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
+               else if (mkdir(work_tree, 0777))
                        die_errno(_("could not create work tree dir '%s'"),
                                  work_tree);
                junk_work_tree = work_tree;
                set_git_work_tree(work_tree);
        }
 
-       junk_git_dir = real_git_dir ? real_git_dir : git_dir;
+       if (real_git_dir) {
+               if (dir_exists(real_git_dir))
+                       junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
+               junk_git_dir = real_git_dir;
+       } else {
+               if (dest_exists)
+                       junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
+               junk_git_dir = git_dir;
+       }
        if (safe_create_leading_directories_const(git_dir) < 0)
                die(_("could not create leading directories of '%s'"), git_dir);
 
@@ -1073,6 +1095,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                        warning(_("--shallow-since is ignored in local clones; use file:// instead."));
                if (option_not.nr)
                        warning(_("--shallow-exclude is ignored in local clones; use file:// instead."));
+               if (filter_options.choice)
+                       warning(_("--filter is ignored in local clones; use file:// instead."));
                if (!access(mkpath("%s/shallow", path), F_OK)) {
                        if (option_local > 0)
                                warning(_("source repository is shallow, ignoring --local"));
@@ -1101,7 +1125,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                transport_set_option(transport, TRANS_OPT_UPLOADPACK,
                                     option_upload_pack);
 
-       if (transport->smart_options && !deepen)
+       if (filter_options.choice) {
+               transport_set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
+                                    filter_options.filter_spec);
+               transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
+       }
+
+       if (transport->smart_options && !deepen && !filter_options.choice)
                transport->smart_options->check_self_contained_and_connected = 1;
 
        refs = transport_get_remote_refs(transport);
@@ -1161,13 +1191,17 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        write_refspec_config(src_ref_prefix, our_head_points_at,
                        remote_head_points_at, &branch_top);
 
+       if (filter_options.choice)
+               partial_clone_register("origin", &filter_options);
+
        if (is_local)
                clone_local(path, git_dir);
        else if (refs && complete_refs_before_fetch)
                transport_fetch_refs(transport, mapped_refs);
 
        update_remote_refs(refs, mapped_refs, remote_head_points_at,
-                          branch_top.buf, reflog_msg.buf, transport, !is_local);
+                          branch_top.buf, reflog_msg.buf, transport,
+                          !is_local && !filter_options.choice);
 
        update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
@@ -1188,6 +1222,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        }
 
        junk_mode = JUNK_LEAVE_REPO;
+       fetch_if_missing = 1;
        err = checkout(submodule_progress);
 
        strbuf_release(&reflog_msg);
index 8a87701..5dd766a 100644 (file)
@@ -31,9 +31,7 @@
 #include "gpg-interface.h"
 #include "column.h"
 #include "sequencer.h"
-#include "notes-utils.h"
 #include "mailmap.h"
-#include "sigchain.h"
 
 static const char * const builtin_commit_usage[] = {
        N_("git commit [<options>] [--] <pathspec>..."),
@@ -45,31 +43,6 @@ static const char * const builtin_status_usage[] = {
        NULL
 };
 
-static const char implicit_ident_advice_noconfig[] =
-N_("Your name and email address were configured automatically based\n"
-"on your username and hostname. Please check that they are accurate.\n"
-"You can suppress this message by setting them explicitly. Run the\n"
-"following command and follow the instructions in your editor to edit\n"
-"your configuration file:\n"
-"\n"
-"    git config --global --edit\n"
-"\n"
-"After doing this, you may fix the identity used for this commit with:\n"
-"\n"
-"    git commit --amend --reset-author\n");
-
-static const char implicit_ident_advice_config[] =
-N_("Your name and email address were configured automatically based\n"
-"on your username and hostname. Please check that they are accurate.\n"
-"You can suppress this message by setting them explicitly:\n"
-"\n"
-"    git config --global user.name \"Your Name\"\n"
-"    git config --global user.email you@example.com\n"
-"\n"
-"After doing this, you may fix the identity used for this commit with:\n"
-"\n"
-"    git commit --amend --reset-author\n");
-
 static const char empty_amend_advice[] =
 N_("You asked to amend the most recent commit, but doing so would make\n"
 "it empty. You can repeat your command with --allow-empty, or you can\n"
@@ -93,8 +66,6 @@ N_("If you wish to skip this commit, use:\n"
 "Then \"git cherry-pick --continue\" will resume cherry-picking\n"
 "the remaining commits.\n");
 
-static GIT_PATH_FUNC(git_path_commit_editmsg, "COMMIT_EDITMSG")
-
 static const char *use_message_buffer;
 static struct lock_file index_lock; /* real index */
 static struct lock_file false_lock; /* used only for partial commits */
@@ -128,12 +99,7 @@ static char *sign_commit;
  * if editor is used, and only the whitespaces if the message
  * is specified explicitly.
  */
-static enum {
-       CLEANUP_SPACE,
-       CLEANUP_NONE,
-       CLEANUP_SCISSORS,
-       CLEANUP_ALL
-} cleanup_mode;
+static enum commit_msg_cleanup_mode cleanup_mode;
 static const char *cleanup_arg;
 
 static enum commit_whence whence;
@@ -673,7 +639,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        struct strbuf sb = STRBUF_INIT;
        const char *hook_arg1 = NULL;
        const char *hook_arg2 = NULL;
-       int clean_message_contents = (cleanup_mode != CLEANUP_NONE);
+       int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE);
        int old_display_comment_prefix;
 
        /* This checks and barfs if author is badly specified */
@@ -701,7 +667,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                }
        }
 
-       if (have_option_m) {
+       if (have_option_m && !fixup_message) {
                strbuf_addbuf(&sb, &message);
                hook_arg1 = "message";
        } else if (logfile && !strcmp(logfile, "-")) {
@@ -731,6 +697,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                ctx.output_encoding = get_commit_output_encoding();
                format_commit_message(commit, "fixup! %s\n\n",
                                      &sb, &ctx);
+               if (have_option_m)
+                       strbuf_addbuf(&sb, &message);
                hook_arg1 = "message";
        } else if (!stat(git_path_merge_msg(), &statbuf)) {
                /*
@@ -812,7 +780,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                struct ident_split ci, ai;
 
                if (whence != FROM_COMMIT) {
-                       if (cleanup_mode == CLEANUP_SCISSORS)
+                       if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
                                wt_status_add_cut_line(s->fp);
                        status_printf_ln(s, GIT_COLOR_NORMAL,
                            whence == FROM_MERGE
@@ -832,14 +800,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                }
 
                fprintf(s->fp, "\n");
-               if (cleanup_mode == CLEANUP_ALL)
+               if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL)
                        status_printf(s, GIT_COLOR_NORMAL,
                                _("Please enter the commit message for your changes."
                                  " Lines starting\nwith '%c' will be ignored, and an empty"
                                  " message aborts the commit.\n"), comment_line_char);
-               else if (cleanup_mode == CLEANUP_SCISSORS && whence == FROM_COMMIT)
+               else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
+                        whence == FROM_COMMIT)
                        wt_status_add_cut_line(s->fp);
-               else /* CLEANUP_SPACE, that is. */
+               else /* COMMIT_MSG_CLEANUP_SPACE, that is. */
                        status_printf(s, GIT_COLOR_NORMAL,
                                _("Please enter the commit message for your changes."
                                  " Lines starting\n"
@@ -984,65 +953,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        return 1;
 }
 
-static int rest_is_empty(struct strbuf *sb, int start)
-{
-       int i, eol;
-       const char *nl;
-
-       /* Check if the rest is just whitespace and Signed-off-by's. */
-       for (i = start; i < sb->len; i++) {
-               nl = memchr(sb->buf + i, '\n', sb->len - i);
-               if (nl)
-                       eol = nl - sb->buf;
-               else
-                       eol = sb->len;
-
-               if (strlen(sign_off_header) <= eol - i &&
-                   starts_with(sb->buf + i, sign_off_header)) {
-                       i = eol;
-                       continue;
-               }
-               while (i < eol)
-                       if (!isspace(sb->buf[i++]))
-                               return 0;
-       }
-
-       return 1;
-}
-
-/*
- * Find out if the message in the strbuf contains only whitespace and
- * Signed-off-by lines.
- */
-static int message_is_empty(struct strbuf *sb)
-{
-       if (cleanup_mode == CLEANUP_NONE && sb->len)
-               return 0;
-       return rest_is_empty(sb, 0);
-}
-
-/*
- * See if the user edited the message in the editor or left what
- * was in the template intact
- */
-static int template_untouched(struct strbuf *sb)
-{
-       struct strbuf tmpl = STRBUF_INIT;
-       const char *start;
-
-       if (cleanup_mode == CLEANUP_NONE && sb->len)
-               return 0;
-
-       if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
-               return 0;
-
-       strbuf_stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
-       if (!skip_prefix(sb->buf, tmpl.buf, &start))
-               start = sb->buf;
-       strbuf_release(&tmpl);
-       return rest_is_empty(sb, start - sb->buf);
-}
-
 static const char *find_author_by_nickname(const char *name)
 {
        struct rev_info revs;
@@ -1197,8 +1107,8 @@ static int parse_and_validate_options(int argc, const char *argv[],
                f++;
        if (f > 1)
                die(_("Only one of -c/-C/-F/--fixup can be used."));
-       if (have_option_m && f > 0)
-               die((_("Option -m cannot be combined with -c/-C/-F/--fixup.")));
+       if (have_option_m && (edit_message || use_message || logfile))
+               die((_("Option -m cannot be combined with -c/-C/-F.")));
        if (f || have_option_m)
                template_file = NULL;
        if (edit_message)
@@ -1227,15 +1137,17 @@ static int parse_and_validate_options(int argc, const char *argv[],
        if (argc == 0 && (also || (only && !amend && !allow_empty)))
                die(_("No paths with --include/--only does not make sense."));
        if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
-               cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE;
+               cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_ALL :
+                                           COMMIT_MSG_CLEANUP_SPACE;
        else if (!strcmp(cleanup_arg, "verbatim"))
-               cleanup_mode = CLEANUP_NONE;
+               cleanup_mode = COMMIT_MSG_CLEANUP_NONE;
        else if (!strcmp(cleanup_arg, "whitespace"))
-               cleanup_mode = CLEANUP_SPACE;
+               cleanup_mode = COMMIT_MSG_CLEANUP_SPACE;
        else if (!strcmp(cleanup_arg, "strip"))
-               cleanup_mode = CLEANUP_ALL;
+               cleanup_mode = COMMIT_MSG_CLEANUP_ALL;
        else if (!strcmp(cleanup_arg, "scissors"))
-               cleanup_mode = use_editor ? CLEANUP_SCISSORS : CLEANUP_SPACE;
+               cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :
+                                           COMMIT_MSG_CLEANUP_SPACE;
        else
                die(_("Invalid cleanup mode %s"), cleanup_arg);
 
@@ -1437,98 +1349,6 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        return 0;
 }
 
-static const char *implicit_ident_advice(void)
-{
-       char *user_config = expand_user_path("~/.gitconfig", 0);
-       char *xdg_config = xdg_config_home("config");
-       int config_exists = file_exists(user_config) || file_exists(xdg_config);
-
-       free(user_config);
-       free(xdg_config);
-
-       if (config_exists)
-               return _(implicit_ident_advice_config);
-       else
-               return _(implicit_ident_advice_noconfig);
-
-}
-
-static void print_summary(const char *prefix, const struct object_id *oid,
-                         int initial_commit)
-{
-       struct rev_info rev;
-       struct commit *commit;
-       struct strbuf format = STRBUF_INIT;
-       const char *head;
-       struct pretty_print_context pctx = {0};
-       struct strbuf author_ident = STRBUF_INIT;
-       struct strbuf committer_ident = STRBUF_INIT;
-
-       commit = lookup_commit(oid);
-       if (!commit)
-               die(_("couldn't look up newly created commit"));
-       if (parse_commit(commit))
-               die(_("could not parse newly created commit"));
-
-       strbuf_addstr(&format, "format:%h] %s");
-
-       format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
-       format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
-       if (strbuf_cmp(&author_ident, &committer_ident)) {
-               strbuf_addstr(&format, "\n Author: ");
-               strbuf_addbuf_percentquote(&format, &author_ident);
-       }
-       if (author_date_is_interesting()) {
-               struct strbuf date = STRBUF_INIT;
-               format_commit_message(commit, "%ad", &date, &pctx);
-               strbuf_addstr(&format, "\n Date: ");
-               strbuf_addbuf_percentquote(&format, &date);
-               strbuf_release(&date);
-       }
-       if (!committer_ident_sufficiently_given()) {
-               strbuf_addstr(&format, "\n Committer: ");
-               strbuf_addbuf_percentquote(&format, &committer_ident);
-               if (advice_implicit_identity) {
-                       strbuf_addch(&format, '\n');
-                       strbuf_addstr(&format, implicit_ident_advice());
-               }
-       }
-       strbuf_release(&author_ident);
-       strbuf_release(&committer_ident);
-
-       init_revisions(&rev, prefix);
-       setup_revisions(0, NULL, &rev, NULL);
-
-       rev.diff = 1;
-       rev.diffopt.output_format =
-               DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
-
-       rev.verbose_header = 1;
-       rev.show_root_diff = 1;
-       get_commit_format(format.buf, &rev);
-       rev.always_show_header = 0;
-       rev.diffopt.detect_rename = 1;
-       rev.diffopt.break_opt = 0;
-       diff_setup_done(&rev.diffopt);
-
-       head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
-       if (!head)
-               die_errno(_("unable to resolve HEAD after creating commit"));
-       if (!strcmp(head, "HEAD"))
-               head = _("detached HEAD");
-       else
-               skip_prefix(head, "refs/heads/", &head);
-       printf("[%s%s ", head, initial_commit ? _(" (root-commit)") : "");
-
-       if (!log_tree_commit(&rev, commit)) {
-               rev.always_show_header = 1;
-               rev.use_terminator = 1;
-               log_tree_commit(&rev, commit);
-       }
-
-       strbuf_release(&format);
-}
-
 static int git_commit_config(const char *k, const char *v, void *cb)
 {
        struct wt_status *s = cb;
@@ -1558,37 +1378,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)
        return git_status_config(k, v, s);
 }
 
-static int run_rewrite_hook(const struct object_id *oldoid,
-                           const struct object_id *newoid)
-{
-       struct child_process proc = CHILD_PROCESS_INIT;
-       const char *argv[3];
-       int code;
-       struct strbuf sb = STRBUF_INIT;
-
-       argv[0] = find_hook("post-rewrite");
-       if (!argv[0])
-               return 0;
-
-       argv[1] = "amend";
-       argv[2] = NULL;
-
-       proc.argv = argv;
-       proc.in = -1;
-       proc.stdout_to_stderr = 1;
-
-       code = start_command(&proc);
-       if (code)
-               return code;
-       strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
-       sigchain_push(SIGPIPE, SIG_IGN);
-       write_in_full(proc.in, sb.buf, sb.len);
-       close(proc.in);
-       strbuf_release(&sb);
-       sigchain_pop(SIGPIPE);
-       return finish_command(&proc);
-}
-
 int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)
 {
        struct argv_array hook_env = ARGV_ARRAY_INIT;
@@ -1671,13 +1460,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        struct strbuf sb = STRBUF_INIT;
        struct strbuf author_ident = STRBUF_INIT;
        const char *index_file, *reflog_msg;
-       char *nl;
        struct object_id oid;
        struct commit_list *parents = NULL;
        struct stat statbuf;
        struct commit *current_head = NULL;
        struct commit_extra_header *extra = NULL;
-       struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
 
        if (argc == 2 && !strcmp(argv[1], "-h"))
@@ -1768,17 +1555,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        }
 
        if (verbose || /* Truncate the message just before the diff, if any. */
-           cleanup_mode == CLEANUP_SCISSORS)
+           cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
                strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));
-       if (cleanup_mode != CLEANUP_NONE)
-               strbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);
+       if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
+               strbuf_stripspace(&sb, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
 
-       if (message_is_empty(&sb) && !allow_empty_message) {
+       if (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) {
                rollback_index_files();
                fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
                exit(1);
        }
-       if (template_untouched(&sb) && !allow_empty_message) {
+       if (template_untouched(&sb, template_file, cleanup_mode) && !allow_empty_message) {
                rollback_index_files();
                fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
                exit(1);
@@ -1800,25 +1587,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        strbuf_release(&author_ident);
        free_commit_extra_headers(extra);
 
-       nl = strchr(sb.buf, '\n');
-       if (nl)
-               strbuf_setlen(&sb, nl + 1 - sb.buf);
-       else
-               strbuf_addch(&sb, '\n');
-       strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
-       strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
-
-       transaction = ref_transaction_begin(&err);
-       if (!transaction ||
-           ref_transaction_update(transaction, "HEAD", &oid,
-                                  current_head
-                                  ? &current_head->object.oid : &null_oid,
-                                  0, sb.buf, &err) ||
-           ref_transaction_commit(transaction, &err)) {
+       if (update_head_with_reflog(current_head, &oid, reflog_msg, &sb,
+                                   &err)) {
                rollback_index_files();
                die("%s", err.buf);
        }
-       ref_transaction_free(transaction);
 
        unlink(git_path_cherry_pick_head());
        unlink(git_path_revert_head());
@@ -1835,17 +1608,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        rerere(0);
        run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
        if (amend && !no_post_rewrite) {
-               struct notes_rewrite_cfg *cfg;
-               cfg = init_copy_notes_for_rewrite("amend");
-               if (cfg) {
-                       /* we are amending, so current_head is not NULL */
-                       copy_note_for_rewrite(cfg, &current_head->object.oid, &oid);
-                       finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
-               }
-               run_rewrite_hook(&current_head->object.oid, &oid);
+               commit_post_rewrite(current_head, &oid);
+       }
+       if (!quiet) {
+               unsigned int flags = 0;
+
+               if (!current_head)
+                       flags |= SUMMARY_INITIAL_COMMIT;
+               if (author_date_is_interesting())
+                       flags |= SUMMARY_SHOW_AUTHOR_DATE;
+               print_commit_summary(prefix, &oid, flags);
        }
-       if (!quiet)
-               print_summary(prefix, &oid, !current_head);
 
        UNLEAK(err);
        UNLEAK(sb);
index 3b0b204..c428984 100644 (file)
@@ -274,10 +274,13 @@ static void append_name(struct commit_name *n, struct strbuf *dst)
                n->name_checked = 1;
        }
 
-       if (n->tag)
+       if (n->tag) {
+               if (all)
+                       strbuf_addstr(dst, "tags/");
                strbuf_addstr(dst, n->tag->tag);
-       else
+       } else {
                strbuf_addstr(dst, n->path);
+       }
 }
 
 static void append_suffix(int depth, const struct object_id *oid, struct strbuf *dst)
@@ -380,7 +383,7 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst)
        if (!match_cnt) {
                struct object_id *cmit_oid = &cmit->object.oid;
                if (always) {
-                       strbuf_addstr(dst, find_unique_abbrev(cmit_oid->hash, abbrev));
+                       strbuf_add_unique_abbrev(dst, cmit_oid->hash, abbrev);
                        if (suffix)
                                strbuf_addstr(dst, suffix);
                        return;
index 366b9d1..a7bc136 100644 (file)
@@ -53,6 +53,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        struct oid_array shallow = OID_ARRAY_INIT;
        struct string_list deepen_not = STRING_LIST_INIT_DUP;
 
+       fetch_if_missing = 0;
+
        packet_trace_identity("fetch-pack");
 
        memset(&args, 0, sizeof(args));
@@ -143,6 +145,22 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                        args.update_shallow = 1;
                        continue;
                }
+               if (!strcmp("--from-promisor", arg)) {
+                       args.from_promisor = 1;
+                       continue;
+               }
+               if (!strcmp("--no-dependents", arg)) {
+                       args.no_dependents = 1;
+                       continue;
+               }
+               if (skip_prefix(arg, ("--" CL_ARG__FILTER "="), &arg)) {
+                       parse_list_objects_filter(&args.filter_options, arg);
+                       continue;
+               }
+               if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) {
+                       list_objects_filter_set_no_filter(&args.filter_options);
+                       continue;
+               }
                usage(fetch_pack_usage);
        }
        if (deepen_not.nr)
index 7bbcd26..8ee998e 100644 (file)
@@ -19,6 +19,7 @@
 #include "argv-array.h"
 #include "utf8.h"
 #include "packfile.h"
+#include "list-objects-filter-options.h"
 
 static const char * const builtin_fetch_usage[] = {
        N_("git fetch [<options>] [<repository> [<refspec>...]]"),
@@ -56,6 +57,7 @@ static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
 static int shown_url = 0;
 static int refmap_alloc, refmap_nr;
 static const char **refmap_array;
+static struct list_objects_filter_options filter_options;
 
 static int git_fetch_config(const char *k, const char *v, void *cb)
 {
@@ -161,6 +163,7 @@ static struct option builtin_fetch_options[] = {
                        TRANSPORT_FAMILY_IPV4),
        OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
                        TRANSPORT_FAMILY_IPV6),
+       OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
        OPT_END()
 };
 
@@ -1045,6 +1048,11 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
                set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, "yes");
        if (update_shallow)
                set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
+       if (filter_options.choice) {
+               set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
+                          filter_options.filter_spec);
+               set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
+       }
        return transport;
 }
 
@@ -1265,6 +1273,56 @@ static int fetch_multiple(struct string_list *list)
        return result;
 }
 
+/*
+ * Fetching from the promisor remote should use the given filter-spec
+ * or inherit the default filter-spec from the config.
+ */
+static inline void fetch_one_setup_partial(struct remote *remote)
+{
+       /*
+        * Explicit --no-filter argument overrides everything, regardless
+        * of any prior partial clones and fetches.
+        */
+       if (filter_options.no_filter)
+               return;
+
+       /*
+        * If no prior partial clone/fetch and the current fetch DID NOT
+        * request a partial-fetch, do a normal fetch.
+        */
+       if (!repository_format_partial_clone && !filter_options.choice)
+               return;
+
+       /*
+        * If this is the FIRST partial-fetch request, we enable partial
+        * on this repo and remember the given filter-spec as the default
+        * for subsequent fetches to this remote.
+        */
+       if (!repository_format_partial_clone && filter_options.choice) {
+               partial_clone_register(remote->name, &filter_options);
+               return;
+       }
+
+       /*
+        * We are currently limited to only ONE promisor remote and only
+        * allow partial-fetches from the promisor remote.
+        */
+       if (strcmp(remote->name, repository_format_partial_clone)) {
+               if (filter_options.choice)
+                       die(_("--filter can only be used with the remote configured in core.partialClone"));
+               return;
+       }
+
+       /*
+        * Do a partial-fetch from the promisor remote using either the
+        * explicitly given filter-spec or inherit the filter-spec from
+        * the config.
+        */
+       if (!filter_options.choice)
+               partial_clone_get_default_filter_spec(&filter_options);
+       return;
+}
+
 static int fetch_one(struct remote *remote, int argc, const char **argv)
 {
        static const char **refs = NULL;
@@ -1320,12 +1378,14 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
 {
        int i;
        struct string_list list = STRING_LIST_INIT_DUP;
-       struct remote *remote;
+       struct remote *remote = NULL;
        int result = 0;
        struct argv_array argv_gc_auto = ARGV_ARRAY_INIT;
 
        packet_trace_identity("fetch");
 
+       fetch_if_missing = 0;
+
        /* Record the command line for the reflog */
        strbuf_addstr(&default_rla, "fetch");
        for (i = 1; i < argc; i++)
@@ -1359,23 +1419,23 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        if (depth || deepen_since || deepen_not.nr)
                deepen = 1;
 
+       if (filter_options.choice && !repository_format_partial_clone)
+               die("--filter can only be used when extensions.partialClone is set");
+
        if (all) {
                if (argc == 1)
                        die(_("fetch --all does not take a repository argument"));
                else if (argc > 1)
                        die(_("fetch --all does not make sense with refspecs"));
                (void) for_each_remote(get_one_remote_for_fetch, &list);
-               result = fetch_multiple(&list);
        } else if (argc == 0) {
                /* No arguments -- use default remote */
                remote = remote_get(NULL);
-               result = fetch_one(remote, argc, argv);
        } else if (multiple) {
                /* All arguments are assumed to be remotes or groups */
                for (i = 0; i < argc; i++)
                        if (!add_remote_or_group(argv[i], &list))
                                die(_("No such remote or remote group: %s"), argv[i]);
-               result = fetch_multiple(&list);
        } else {
                /* Single remote or group */
                (void) add_remote_or_group(argv[0], &list);
@@ -1383,14 +1443,25 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                        /* More than one remote */
                        if (argc > 1)
                                die(_("Fetching a group and specifying refspecs does not make sense"));
-                       result = fetch_multiple(&list);
                } else {
                        /* Zero or one remotes */
                        remote = remote_get(argv[0]);
-                       result = fetch_one(remote, argc-1, argv+1);
+                       argc--;
+                       argv++;
                }
        }
 
+       if (remote) {
+               if (filter_options.choice || repository_format_partial_clone)
+                       fetch_one_setup_partial(remote);
+               result = fetch_one(remote, argc, argv);
+       } else {
+               if (filter_options.choice)
+                       die(_("--filter can only be used with the remote configured in core.partialClone"));
+               /* TODO should this also die if we have a previous partial-clone? */
+               result = fetch_multiple(&list);
+       }
+
        if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
                struct argv_array options = ARGV_ARRAY_INIT;
 
index 04846d4..7a8a679 100644 (file)
@@ -149,6 +149,15 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
        if (obj->flags & REACHABLE)
                return 0;
        obj->flags |= REACHABLE;
+
+       if (is_promisor_object(&obj->oid))
+               /*
+                * Further recursion does not need to be performed on this
+                * object since it is a promisor object (so it does not need to
+                * be added to "pending").
+                */
+               return 0;
+
        if (!(obj->flags & HAS_OBJ)) {
                if (parent && !has_object_file(&obj->oid)) {
                        printf("broken link from %7s %s\n",
@@ -208,6 +217,8 @@ static void check_reachable_object(struct object *obj)
         * do a full fsck
         */
        if (!(obj->flags & HAS_OBJ)) {
+               if (is_promisor_object(&obj->oid))
+                       return;
                if (has_sha1_pack(obj->oid.hash))
                        return; /* it is in pack - forget about it */
                printf("missing %s %s\n", printable_type(obj),
@@ -398,7 +409,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
                                        xstrfmt("%s@{%"PRItime"}", refname, timestamp));
                        obj->flags |= USED;
                        mark_object_reachable(obj);
-               } else {
+               } else if (!is_promisor_object(oid)) {
                        error("%s: invalid reflog entry %s", refname, oid_to_hex(oid));
                        errors_found |= ERROR_REACHABLE;
                }
@@ -434,6 +445,14 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,
 
        obj = parse_object(oid);
        if (!obj) {
+               if (is_promisor_object(oid)) {
+                       /*
+                        * Increment default_refs anyway, because this is a
+                        * valid ref.
+                        */
+                        default_refs++;
+                        return 0;
+               }
                error("%s: invalid sha1 pointer %s", refname, oid_to_hex(oid));
                errors_found |= ERROR_REACHABLE;
                /* We'll continue with the rest despite the error.. */
@@ -659,6 +678,9 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
        int i;
        struct alternate_object_database *alt;
 
+       /* fsck knows how to handle missing promisor objects */
+       fetch_if_missing = 0;
+
        errors_found = 0;
        check_replace_refs = 0;
 
@@ -731,6 +753,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                        struct object *obj = lookup_object(oid.hash);
 
                        if (!obj || !(obj->flags & HAS_OBJ)) {
+                               if (is_promisor_object(&oid))
+                                       continue;
                                error("%s: object missing", oid_to_hex(&oid));
                                errors_found |= ERROR_OBJECT;
                                continue;
index 3c5eae0..77fa720 100644 (file)
@@ -458,6 +458,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                        argv_array_push(&prune, prune_expire);
                        if (quiet)
                                argv_array_push(&prune, "--no-progress");
+                       if (repository_format_partial_clone)
+                               argv_array_push(&prune,
+                                               "--exclude-promisor-objects");
                        if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
                                return error(FAILED_RUN, prune.argv[0]);
                }
index 4c51aec..5ebd370 100644 (file)
@@ -1389,15 +1389,60 @@ static void fix_unresolved_deltas(struct sha1file *f)
        free(sorted_by_pos);
 }
 
+static const char *derive_filename(const char *pack_name, const char *suffix,
+                                  struct strbuf *buf)
+{
+       size_t len;
+       if (!strip_suffix(pack_name, ".pack", &len))
+               die(_("packfile name '%s' does not end with '.pack'"),
+                   pack_name);
+       strbuf_add(buf, pack_name, len);
+       strbuf_addch(buf, '.');
+       strbuf_addstr(buf, suffix);
+       return buf->buf;
+}
+
+static void write_special_file(const char *suffix, const char *msg,
+                              const char *pack_name, const unsigned char *sha1,
+                              const char **report)
+{
+       struct strbuf name_buf = STRBUF_INIT;
+       const char *filename;
+       int fd;
+       int msg_len = strlen(msg);
+
+       if (pack_name)
+               filename = derive_filename(pack_name, suffix, &name_buf);
+       else
+               filename = odb_pack_name(&name_buf, sha1, suffix);
+
+       fd = odb_pack_keep(filename);
+       if (fd < 0) {
+               if (errno != EEXIST)
+                       die_errno(_("cannot write %s file '%s'"),
+                                 suffix, filename);
+       } else {
+               if (msg_len > 0) {
+                       write_or_die(fd, msg, msg_len);
+                       write_or_die(fd, "\n", 1);
+               }
+               if (close(fd) != 0)
+                       die_errno(_("cannot close written %s file '%s'"),
+                                 suffix, filename);
+               if (report)
+                       *report = suffix;
+       }
+       strbuf_release(&name_buf);
+}
+
 static void final(const char *final_pack_name, const char *curr_pack_name,
                  const char *final_index_name, const char *curr_index_name,
-                 const char *keep_name, const char *keep_msg,
+                 const char *keep_msg, const char *promisor_msg,
                  unsigned char *sha1)
 {
        const char *report = "pack";
        struct strbuf pack_name = STRBUF_INIT;
        struct strbuf index_name = STRBUF_INIT;
-       struct strbuf keep_name_buf = STRBUF_INIT;
        int err;
 
        if (!from_stdin) {
@@ -1409,28 +1454,12 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                        die_errno(_("error while closing pack file"));
        }
 
-       if (keep_msg) {
-               int keep_fd, keep_msg_len = strlen(keep_msg);
-
-               if (!keep_name)
-                       keep_name = odb_pack_name(&keep_name_buf, sha1, "keep");
-
-               keep_fd = odb_pack_keep(keep_name);
-               if (keep_fd < 0) {
-                       if (errno != EEXIST)
-                               die_errno(_("cannot write keep file '%s'"),
-                                         keep_name);
-               } else {
-                       if (keep_msg_len > 0) {
-                               write_or_die(keep_fd, keep_msg, keep_msg_len);
-                               write_or_die(keep_fd, "\n", 1);
-                       }
-                       if (close(keep_fd) != 0)
-                               die_errno(_("cannot close written keep file '%s'"),
-                                         keep_name);
-                       report = "keep";
-               }
-       }
+       if (keep_msg)
+               write_special_file("keep", keep_msg, final_pack_name, sha1,
+                                  &report);
+       if (promisor_msg)
+               write_special_file("promisor", promisor_msg, final_pack_name,
+                                  sha1, NULL);
 
        if (final_pack_name != curr_pack_name) {
                if (!final_pack_name)
@@ -1472,7 +1501,6 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
 
        strbuf_release(&index_name);
        strbuf_release(&pack_name);
-       strbuf_release(&keep_name_buf);
 }
 
 static int git_index_pack_config(const char *k, const char *v, void *cb)
@@ -1615,32 +1643,26 @@ static void show_pack_info(int stat_only)
        }
 }
 
-static const char *derive_filename(const char *pack_name, const char *suffix,
-                                  struct strbuf *buf)
-{
-       size_t len;
-       if (!strip_suffix(pack_name, ".pack", &len))
-               die(_("packfile name '%s' does not end with '.pack'"),
-                   pack_name);
-       strbuf_add(buf, pack_name, len);
-       strbuf_addstr(buf, suffix);
-       return buf->buf;
-}
-
 int cmd_index_pack(int argc, const char **argv, const char *prefix)
 {
        int i, fix_thin_pack = 0, verify = 0, stat_only = 0;
        const char *curr_index;
        const char *index_name = NULL, *pack_name = NULL;
-       const char *keep_name = NULL, *keep_msg = NULL;
-       struct strbuf index_name_buf = STRBUF_INIT,
-                     keep_name_buf = STRBUF_INIT;
+       const char *keep_msg = NULL;
+       const char *promisor_msg = NULL;
+       struct strbuf index_name_buf = STRBUF_INIT;
        struct pack_idx_entry **idx_objects;
        struct pack_idx_option opts;
        unsigned char pack_sha1[20];
        unsigned foreign_nr = 1;        /* zero is a "good" value, assume bad */
        int report_end_of_input = 0;
 
+       /*
+        * index-pack never needs to fetch missing objects, since it only
+        * accesses the repo to do hash collision checks
+        */
+       fetch_if_missing = 0;
+
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage(index_pack_usage);
 
@@ -1678,6 +1700,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                                stat_only = 1;
                        } else if (skip_to_optional_arg(arg, "--keep", &keep_msg)) {
                                ; /* nothing to do */
+                       } else if (skip_to_optional_arg(arg, "--promisor", &promisor_msg)) {
+                               ; /* already parsed */
                        } else if (starts_with(arg, "--threads=")) {
                                char *end;
                                nr_threads = strtoul(arg+10, &end, 0);
@@ -1740,9 +1764,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        if (from_stdin && !startup_info->have_repository)
                die(_("--stdin requires a git repository"));
        if (!index_name && pack_name)
-               index_name = derive_filename(pack_name, ".idx", &index_name_buf);
-       if (keep_msg && !keep_name && pack_name)
-               keep_name = derive_filename(pack_name, ".keep", &keep_name_buf);
+               index_name = derive_filename(pack_name, "idx", &index_name_buf);
 
        if (verify) {
                if (!index_name)
@@ -1790,13 +1812,12 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        if (!verify)
                final(pack_name, curr_pack,
                      index_name, curr_index,
-                     keep_name, keep_msg,
+                     keep_msg, promisor_msg,
                      pack_sha1);
        else
                close(input_fd);
        free(objects);
        strbuf_release(&index_name_buf);
-       strbuf_release(&keep_name_buf);
        if (pack_name == NULL)
                free((void *) curr_pack);
        if (index_name == NULL)
index 14fdf39..46b4ca1 100644 (file)
@@ -188,8 +188,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
        if (rev->show_notes)
                init_display_notes(&rev->notes_opt);
 
-       if (rev->diffopt.pickaxe || rev->diffopt.filter ||
-           rev->diffopt.flags.follow_renames)
+       if ((rev->diffopt.pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) ||
+           rev->diffopt.filter || rev->diffopt.flags.follow_renames)
                rev->always_show_header = 0;
 
        if (source)
index 6b9cfc2..83dcbc9 100644 (file)
@@ -26,7 +26,7 @@
 #include "reachable.h"
 #include "sha1-array.h"
 #include "argv-array.h"
-#include "mru.h"
+#include "list.h"
 #include "packfile.h"
 
 static const char *pack_usage[] = {
@@ -75,6 +75,8 @@ static int use_bitmap_index = -1;
 static int write_bitmap_index;
 static uint16_t write_bitmap_options;
 
+static int exclude_promisor_objects;
+
 static unsigned long delta_cache_size = 0;
 static unsigned long max_delta_cache_size = 256 * 1024 * 1024;
 static unsigned long cache_max_small_delta_size = 1000;
@@ -84,8 +86,9 @@ static unsigned long window_memory_limit = 0;
 static struct list_objects_filter_options filter_options;
 
 enum missing_action {
-       MA_ERROR = 0,    /* fail if any missing objects are encountered */
-       MA_ALLOW_ANY,    /* silently allow ALL missing objects */
+       MA_ERROR = 0,      /* fail if any missing objects are encountered */
+       MA_ALLOW_ANY,      /* silently allow ALL missing objects */
+       MA_ALLOW_PROMISOR, /* silently allow all missing PROMISOR objects */
 };
 static enum missing_action arg_missing_action;
 static show_object_fn fn_show_object;
@@ -1006,8 +1009,8 @@ static int want_object_in_pack(const struct object_id *oid,
                               struct packed_git **found_pack,
                               off_t *found_offset)
 {
-       struct mru_entry *entry;
        int want;
+       struct list_head *pos;
 
        if (!exclude && local && has_loose_object_nonlocal(oid->hash))
                return 0;
@@ -1023,8 +1026,8 @@ static int want_object_in_pack(const struct object_id *oid,
                        return want;
        }
 
-       for (entry = packed_git_mru.head; entry; entry = entry->next) {
-               struct packed_git *p = entry->item;
+       list_for_each(pos, &packed_git_mru) {
+               struct packed_git *p = list_entry(pos, struct packed_git, mru);
                off_t offset;
 
                if (p == *found_pack)
@@ -1041,7 +1044,7 @@ static int want_object_in_pack(const struct object_id *oid,
                        }
                        want = want_found_object(exclude, p);
                        if (!exclude && want > 0)
-                               mru_mark(&packed_git_mru, entry);
+                               list_move(&p->mru, &packed_git_mru);
                        if (want != -1)
                                return want;
                }
@@ -2578,6 +2581,20 @@ static void show_object__ma_allow_any(struct object *obj, const char *name, void
        show_object(obj, name, data);
 }
 
+static void show_object__ma_allow_promisor(struct object *obj, const char *name, void *data)
+{
+       assert(arg_missing_action == MA_ALLOW_PROMISOR);
+
+       /*
+        * Quietly ignore EXPECTED missing objects.  This avoids problems with
+        * staging them now and getting an odd error later.
+        */
+       if (!has_object_file(&obj->oid) && is_promisor_object(&obj->oid))
+               return;
+
+       show_object(obj, name, data);
+}
+
 static int option_parse_missing_action(const struct option *opt,
                                       const char *arg, int unset)
 {
@@ -2592,10 +2609,18 @@ static int option_parse_missing_action(const struct option *opt,
 
        if (!strcmp(arg, "allow-any")) {
                arg_missing_action = MA_ALLOW_ANY;
+               fetch_if_missing = 0;
                fn_show_object = show_object__ma_allow_any;
                return 0;
        }
 
+       if (!strcmp(arg, "allow-promisor")) {
+               arg_missing_action = MA_ALLOW_PROMISOR;
+               fetch_if_missing = 0;
+               fn_show_object = show_object__ma_allow_promisor;
+               return 0;
+       }
+
        die(_("invalid value for --missing"));
        return 0;
 }
@@ -3009,6 +3034,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                { OPTION_CALLBACK, 0, "missing", NULL, N_("action"),
                  N_("handling for missing objects"), PARSE_OPT_NONEG,
                  option_parse_missing_action },
+               OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects,
+                        N_("do not pack objects in promisor packfiles")),
                OPT_END(),
        };
 
@@ -3054,6 +3081,12 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                argv_array_push(&rp, "--unpacked");
        }
 
+       if (exclude_promisor_objects) {
+               use_internal_rev_list = 1;
+               fetch_if_missing = 0;
+               argv_array_push(&rp, "--exclude-promisor-objects");
+       }
+
        if (!reuse_object)
                reuse_delta = 0;
        if (pack_compression_level == -1)
index d2fdae6..4cfec82 100644 (file)
@@ -101,12 +101,15 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
        struct progress *progress = NULL;
+       int exclude_promisor_objects = 0;
        const struct option options[] = {
                OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
                OPT__VERBOSE(&verbose, N_("report pruned objects")),
                OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
                OPT_EXPIRY_DATE(0, "expire", &expire,
                                N_("expire objects older than <time>")),
+               OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects,
+                        N_("limit traversal to objects outside promisor packfiles")),
                OPT_END()
        };
        char *s;
@@ -139,6 +142,10 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
                show_progress = isatty(2);
        if (show_progress)
                progress = start_delayed_progress(_("Checking connectivity"), 0);
+       if (exclude_promisor_objects) {
+               fetch_if_missing = 0;
+               revs.exclude_promisor_objects = 1;
+       }
 
        mark_reachable_objects(&revs, 1, expire, progress);
        stop_progress(&progress);
index 7daee54..00faf14 100644 (file)
@@ -43,7 +43,7 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
-       git_config(git_default_config, NULL);
+       sequencer_init_config(&opts);
        git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
 
        opts.action = REPLAY_INTERACTIVE_REBASE;
index f17a68a..7bdb401 100644 (file)
@@ -83,7 +83,8 @@ static void remove_pack_on_signal(int signo)
 
 /*
  * Adds all packs hex strings to the fname list, which do not
- * have a corresponding .keep file.
+ * have a corresponding .keep or .promisor file. These packs are not to
+ * be kept if we are going to pack everything into one file.
  */
 static void get_non_kept_pack_filenames(struct string_list *fname_list)
 {
@@ -101,7 +102,8 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list)
 
                fname = xmemdupz(e->d_name, len);
 
-               if (!file_exists(mkpath("%s/%s.keep", packdir, fname)))
+               if (!file_exists(mkpath("%s/%s.keep", packdir, fname)) &&
+                   !file_exists(mkpath("%s/%s.promisor", packdir, fname)))
                        string_list_append_nodup(fname_list, fname);
                else
                        free(fname);
@@ -232,6 +234,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        argv_array_push(&cmd.args, "--all");
        argv_array_push(&cmd.args, "--reflog");
        argv_array_push(&cmd.args, "--indexed-objects");
+       if (repository_format_partial_clone)
+               argv_array_push(&cmd.args, "--exclude-promisor-objects");
        if (window)
                argv_array_pushf(&cmd.args, "--window=%s", window);
        if (window_memory)
index d5345b6..48300d9 100644 (file)
@@ -15,6 +15,7 @@
 #include "progress.h"
 #include "reflog-walk.h"
 #include "oidset.h"
+#include "packfile.h"
 
 static const char rev_list_usage[] =
 "git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
@@ -67,6 +68,7 @@ enum missing_action {
        MA_ERROR = 0,    /* fail if any missing objects are encountered */
        MA_ALLOW_ANY,    /* silently allow ALL missing objects */
        MA_PRINT,        /* print ALL missing objects in special section */
+       MA_ALLOW_PROMISOR, /* silently allow all missing PROMISOR objects */
 };
 static enum missing_action arg_missing_action;
 
@@ -197,6 +199,12 @@ static void finish_commit(struct commit *commit, void *data)
 
 static inline void finish_object__ma(struct object *obj)
 {
+       /*
+        * Whether or not we try to dynamically fetch missing objects
+        * from the server, we currently DO NOT have the object.  We
+        * can either print, allow (ignore), or conditionally allow
+        * (ignore) them.
+        */
        switch (arg_missing_action) {
        case MA_ERROR:
                die("missing blob object '%s'", oid_to_hex(&obj->oid));
@@ -209,25 +217,36 @@ static inline void finish_object__ma(struct object *obj)
                oidset_insert(&missing_objects, &obj->oid);
                return;
 
+       case MA_ALLOW_PROMISOR:
+               if (is_promisor_object(&obj->oid))
+                       return;
+               die("unexpected missing blob object '%s'",
+                   oid_to_hex(&obj->oid));
+               return;
+
        default:
                BUG("unhandled missing_action");
                return;
        }
 }
 
-static void finish_object(struct object *obj, const char *name, void *cb_data)
+static int finish_object(struct object *obj, const char *name, void *cb_data)
 {
        struct rev_list_info *info = cb_data;
-       if (obj->type == OBJ_BLOB && !has_object_file(&obj->oid))
+       if (obj->type == OBJ_BLOB && !has_object_file(&obj->oid)) {
                finish_object__ma(obj);
+               return 1;
+       }
        if (info->revs->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT)
                parse_object(&obj->oid);
+       return 0;
 }
 
 static void show_object(struct object *obj, const char *name, void *cb_data)
 {
        struct rev_list_info *info = cb_data;
-       finish_object(obj, name, cb_data);
+       if (finish_object(obj, name, cb_data))
+               return;
        display_progress(progress, ++progress_counter);
        if (info->flags & REV_LIST_QUIET)
                return;
@@ -315,11 +334,19 @@ static inline int parse_missing_action_value(const char *value)
 
        if (!strcmp(value, "allow-any")) {
                arg_missing_action = MA_ALLOW_ANY;
+               fetch_if_missing = 0;
                return 1;
        }
 
        if (!strcmp(value, "print")) {
                arg_missing_action = MA_PRINT;
+               fetch_if_missing = 0;
+               return 1;
+       }
+
+       if (!strcmp(value, "allow-promisor")) {
+               arg_missing_action = MA_ALLOW_PROMISOR;
+               fetch_if_missing = 0;
                return 1;
        }
 
@@ -344,6 +371,35 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        init_revisions(&revs, prefix);
        revs.abbrev = DEFAULT_ABBREV;
        revs.commit_format = CMIT_FMT_UNSPECIFIED;
+
+       /*
+        * Scan the argument list before invoking setup_revisions(), so that we
+        * know if fetch_if_missing needs to be set to 0.
+        *
+        * "--exclude-promisor-objects" acts as a pre-filter on missing objects
+        * by not crossing the boundary from realized objects to promisor
+        * objects.
+        *
+        * Let "--missing" to conditionally set fetch_if_missing.
+        */
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               if (!strcmp(arg, "--exclude-promisor-objects")) {
+                       fetch_if_missing = 0;
+                       revs.exclude_promisor_objects = 1;
+                       break;
+               }
+       }
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               if (skip_prefix(arg, "--missing=", &arg)) {
+                       if (revs.exclude_promisor_objects)
+                               die(_("cannot combine --exclude-promisor-objects and --missing"));
+                       if (parse_missing_action_value(arg))
+                               break;
+               }
+       }
+
        argc = setup_revisions(argc, argv, &revs, NULL);
 
        memset(&info, 0, sizeof(info));
@@ -404,7 +460,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                        continue;
                }
                if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) {
-                       list_objects_filter_release(&filter_options);
+                       list_objects_filter_set_no_filter(&filter_options);
                        continue;
                }
                if (!strcmp(arg, "--filter-print-omitted")) {
@@ -412,9 +468,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                        continue;
                }
 
-               if (skip_prefix(arg, "--missing=", &arg) &&
-                   parse_missing_action_value(arg))
-                       continue;
+               if (!strcmp(arg, "--exclude-promisor-objects"))
+                       continue; /* already handled above */
+               if (skip_prefix(arg, "--missing=", &arg))
+                       continue; /* already handled above */
 
                usage(rev_list_usage);
 
index 74aa644..96d06a5 100644 (file)
@@ -516,7 +516,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
                        PARSE_OPT_SHELL_EVAL);
 
        strbuf_addstr(&parsed, " --");
-       sq_quote_argv(&parsed, argv, 0);
+       sq_quote_argv(&parsed, argv);
        puts(parsed.buf);
        return 0;
 }
@@ -526,7 +526,7 @@ static int cmd_sq_quote(int argc, const char **argv)
        struct strbuf buf = STRBUF_INIT;
 
        if (argc)
-               sq_quote_argv(&buf, argv, 0);
+               sq_quote_argv(&buf, argv);
        printf("%s\n", buf.buf);
        strbuf_release(&buf);
 
index b9d927e..76f0a35 100644 (file)
@@ -208,7 +208,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
        if (isatty(0))
                opts.edit = 1;
        opts.action = REPLAY_REVERT;
-       git_config(git_default_config, NULL);
+       sequencer_init_config(&opts);
        res = run_sequencer(argc, argv, &opts);
        if (res < 0)
                die(_("revert failed"));
@@ -221,7 +221,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
        int res;
 
        opts.action = REPLAY_PICK;
-       git_config(git_default_config, NULL);
+       sequencer_init_config(&opts);
        res = run_sequencer(argc, argv, &opts);
        if (res < 0)
                die(_("cherry-pick failed"));
index a5c4a8a..b1daca9 100644 (file)
@@ -20,6 +20,7 @@
 #define OPT_QUIET (1 << 0)
 #define OPT_CACHED (1 << 1)
 #define OPT_RECURSIVE (1 << 2)
+#define OPT_FORCE (1 << 3)
 
 typedef void (*each_submodule_fn)(const struct cache_entry *list_item,
                                  void *cb_data);
@@ -50,6 +51,20 @@ static char *get_default_remote(void)
        return ret;
 }
 
+static int print_default_remote(int argc, const char **argv, const char *prefix)
+{
+       const char *remote;
+
+       if (argc != 1)
+               die(_("submodule--helper print-default-remote takes no arguments"));
+
+       remote = get_default_remote();
+       if (remote)
+               printf("%s\n", remote);
+
+       return 0;
+}
+
 static int starts_with_dot_slash(const char *str)
 {
        return str[0] == '.' && is_dir_sep(str[1]);
@@ -358,6 +373,25 @@ static void module_list_active(struct module_list *list)
        *list = active_modules;
 }
 
+static char *get_up_path(const char *path)
+{
+       int i;
+       struct strbuf sb = STRBUF_INIT;
+
+       for (i = count_slashes(path); i; i--)
+               strbuf_addstr(&sb, "../");
+
+       /*
+        * Check if 'path' ends with slash or not
+        * for having the same output for dir/sub_dir
+        * and dir/sub_dir/
+        */
+       if (!is_dir_sep(path[strlen(path) - 1]))
+               strbuf_addstr(&sb, "../");
+
+       return strbuf_detach(&sb, NULL);
+}
+
 static int module_list(int argc, const char **argv, const char *prefix)
 {
        int i;
@@ -718,6 +752,309 @@ static int module_name(int argc, const char **argv, const char *prefix)
        return 0;
 }
 
+struct sync_cb {
+       const char *prefix;
+       unsigned int flags;
+};
+
+#define SYNC_CB_INIT { NULL, 0 }
+
+static void sync_submodule(const char *path, const char *prefix,
+                          unsigned int flags)
+{
+       const struct submodule *sub;
+       char *remote_key = NULL;
+       char *sub_origin_url, *super_config_url, *displaypath;
+       struct strbuf sb = STRBUF_INIT;
+       struct child_process cp = CHILD_PROCESS_INIT;
+       char *sub_config_path = NULL;
+
+       if (!is_submodule_active(the_repository, path))
+               return;
+
+       sub = submodule_from_path(&null_oid, path);
+
+       if (sub && sub->url) {
+               if (starts_with_dot_dot_slash(sub->url) ||
+                   starts_with_dot_slash(sub->url)) {
+                       char *remote_url, *up_path;
+                       char *remote = get_default_remote();
+                       strbuf_addf(&sb, "remote.%s.url", remote);
+
+                       if (git_config_get_string(sb.buf, &remote_url))
+                               remote_url = xgetcwd();
+
+                       up_path = get_up_path(path);
+                       sub_origin_url = relative_url(remote_url, sub->url, up_path);
+                       super_config_url = relative_url(remote_url, sub->url, NULL);
+
+                       free(remote);
+                       free(up_path);
+                       free(remote_url);
+               } else {
+                       sub_origin_url = xstrdup(sub->url);
+                       super_config_url = xstrdup(sub->url);
+               }
+       } else {
+               sub_origin_url = xstrdup("");
+               super_config_url = xstrdup("");
+       }
+
+       displaypath = get_submodule_displaypath(path, prefix);
+
+       if (!(flags & OPT_QUIET))
+               printf(_("Synchronizing submodule url for '%s'\n"),
+                        displaypath);
+
+       strbuf_reset(&sb);
+       strbuf_addf(&sb, "submodule.%s.url", sub->name);
+       if (git_config_set_gently(sb.buf, super_config_url))
+               die(_("failed to register url for submodule path '%s'"),
+                     displaypath);
+
+       if (!is_submodule_populated_gently(path, NULL))
+               goto cleanup;
+
+       prepare_submodule_repo_env(&cp.env_array);
+       cp.git_cmd = 1;
+       cp.dir = path;
+       argv_array_pushl(&cp.args, "submodule--helper",
+                        "print-default-remote", NULL);
+
+       strbuf_reset(&sb);
+       if (capture_command(&cp, &sb, 0))
+               die(_("failed to get the default remote for submodule '%s'"),
+                     path);
+
+       strbuf_strip_suffix(&sb, "\n");
+       remote_key = xstrfmt("remote.%s.url", sb.buf);
+
+       strbuf_reset(&sb);
+       submodule_to_gitdir(&sb, path);
+       strbuf_addstr(&sb, "/config");
+
+       if (git_config_set_in_file_gently(sb.buf, remote_key, sub_origin_url))
+               die(_("failed to update remote for submodule '%s'"),
+                     path);
+
+       if (flags & OPT_RECURSIVE) {
+               struct child_process cpr = CHILD_PROCESS_INIT;
+
+               cpr.git_cmd = 1;
+               cpr.dir = path;
+               prepare_submodule_repo_env(&cpr.env_array);
+
+               argv_array_push(&cpr.args, "--super-prefix");
+               argv_array_pushf(&cpr.args, "%s/", displaypath);
+               argv_array_pushl(&cpr.args, "submodule--helper", "sync",
+                                "--recursive", NULL);
+
+               if (flags & OPT_QUIET)
+                       argv_array_push(&cpr.args, "--quiet");
+
+               if (run_command(&cpr))
+                       die(_("failed to recurse into submodule '%s'"),
+                             path);
+       }
+
+cleanup:
+       free(super_config_url);
+       free(sub_origin_url);
+       strbuf_release(&sb);
+       free(remote_key);
+       free(displaypath);
+       free(sub_config_path);
+}
+
+static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data)
+{
+       struct sync_cb *info = cb_data;
+       sync_submodule(list_item->name, info->prefix, info->flags);
+
+}
+
+static int module_sync(int argc, const char **argv, const char *prefix)
+{
+       struct sync_cb info = SYNC_CB_INIT;
+       struct pathspec pathspec;
+       struct module_list list = MODULE_LIST_INIT;
+       int quiet = 0;
+       int recursive = 0;
+
+       struct option module_sync_options[] = {
+               OPT__QUIET(&quiet, N_("Suppress output of synchronizing submodule url")),
+               OPT_BOOL(0, "recursive", &recursive,
+                       N_("Recurse into nested submodules")),
+               OPT_END()
+       };
+
+       const char *const git_submodule_helper_usage[] = {
+               N_("git submodule--helper sync [--quiet] [--recursive] [<path>]"),
+               NULL
+       };
+
+       argc = parse_options(argc, argv, prefix, module_sync_options,
+                            git_submodule_helper_usage, 0);
+
+       if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+               return 1;
+
+       info.prefix = prefix;
+       if (quiet)
+               info.flags |= OPT_QUIET;
+       if (recursive)
+               info.flags |= OPT_RECURSIVE;
+
+       for_each_listed_submodule(&list, sync_submodule_cb, &info);
+
+       return 0;
+}
+
+struct deinit_cb {
+       const char *prefix;
+       unsigned int flags;
+};
+#define DEINIT_CB_INIT { NULL, 0 }
+
+static void deinit_submodule(const char *path, const char *prefix,
+                            unsigned int flags)
+{
+       const struct submodule *sub;
+       char *displaypath = NULL;
+       struct child_process cp_config = CHILD_PROCESS_INIT;
+       struct strbuf sb_config = STRBUF_INIT;
+       char *sub_git_dir = xstrfmt("%s/.git", path);
+
+       sub = submodule_from_path(&null_oid, path);
+
+       if (!sub || !sub->name)
+               goto cleanup;
+
+       displaypath = get_submodule_displaypath(path, prefix);
+
+       /* remove the submodule work tree (unless the user already did it) */
+       if (is_directory(path)) {
+               struct strbuf sb_rm = STRBUF_INIT;
+               const char *format;
+
+               /*
+                * protect submodules containing a .git directory
+                * NEEDSWORK: instead of dying, automatically call
+                * absorbgitdirs and (possibly) warn.
+                */
+               if (is_directory(sub_git_dir))
+                       die(_("Submodule work tree '%s' contains a .git "
+                             "directory (use 'rm -rf' if you really want "
+                             "to remove it including all of its history)"),
+                           displaypath);
+
+               if (!(flags & OPT_FORCE)) {
+                       struct child_process cp_rm = CHILD_PROCESS_INIT;
+                       cp_rm.git_cmd = 1;
+                       argv_array_pushl(&cp_rm.args, "rm", "-qn",
+                                        path, NULL);
+
+                       if (run_command(&cp_rm))
+                               die(_("Submodule work tree '%s' contains local "
+                                     "modifications; use '-f' to discard them"),
+                                     displaypath);
+               }
+
+               strbuf_addstr(&sb_rm, path);
+
+               if (!remove_dir_recursively(&sb_rm, 0))
+                       format = _("Cleared directory '%s'\n");
+               else
+                       format = _("Could not remove submodule work tree '%s'\n");
+
+               if (!(flags & OPT_QUIET))
+                       printf(format, displaypath);
+
+               strbuf_release(&sb_rm);
+       }
+
+       if (mkdir(path, 0777))
+               printf(_("could not create empty submodule directory %s"),
+                     displaypath);
+
+       cp_config.git_cmd = 1;
+       argv_array_pushl(&cp_config.args, "config", "--get-regexp", NULL);
+       argv_array_pushf(&cp_config.args, "submodule.%s\\.", sub->name);
+
+       /* remove the .git/config entries (unless the user already did it) */
+       if (!capture_command(&cp_config, &sb_config, 0) && sb_config.len) {
+               char *sub_key = xstrfmt("submodule.%s", sub->name);
+               /*
+                * remove the whole section so we have a clean state when
+                * the user later decides to init this submodule again
+                */
+               git_config_rename_section_in_file(NULL, sub_key, NULL);
+               if (!(flags & OPT_QUIET))
+                       printf(_("Submodule '%s' (%s) unregistered for path '%s'\n"),
+                                sub->name, sub->url, displaypath);
+               free(sub_key);
+       }
+
+cleanup:
+       free(displaypath);
+       free(sub_git_dir);
+       strbuf_release(&sb_config);
+}
+
+static void deinit_submodule_cb(const struct cache_entry *list_item,
+                               void *cb_data)
+{
+       struct deinit_cb *info = cb_data;
+       deinit_submodule(list_item->name, info->prefix, info->flags);
+}
+
+static int module_deinit(int argc, const char **argv, const char *prefix)
+{
+       struct deinit_cb info = DEINIT_CB_INIT;
+       struct pathspec pathspec;
+       struct module_list list = MODULE_LIST_INIT;
+       int quiet = 0;
+       int force = 0;
+       int all = 0;
+
+       struct option module_deinit_options[] = {
+               OPT__QUIET(&quiet, N_("Suppress submodule status output")),
+               OPT__FORCE(&force, N_("Remove submodule working trees even if they contain local changes")),
+               OPT_BOOL(0, "all", &all, N_("Unregister all submodules")),
+               OPT_END()
+       };
+
+       const char *const git_submodule_helper_usage[] = {
+               N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"),
+               NULL
+       };
+
+       argc = parse_options(argc, argv, prefix, module_deinit_options,
+                            git_submodule_helper_usage, 0);
+
+       if (all && argc) {
+               error("pathspec and --all are incompatible");
+               usage_with_options(git_submodule_helper_usage,
+                                  module_deinit_options);
+       }
+
+       if (!argc && !all)
+               die(_("Use '--all' if you really want to deinitialize all submodules"));
+
+       if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+               BUG("module_list_compute should not choke on empty pathspec");
+
+       info.prefix = prefix;
+       if (quiet)
+               info.flags |= OPT_QUIET;
+       if (force)
+               info.flags |= OPT_FORCE;
+
+       for_each_listed_submodule(&list, deinit_submodule_cb, &info);
+
+       return 0;
+}
+
 static int clone_submodule(const char *path, const char *gitdir, const char *url,
                           const char *depth, struct string_list *reference,
                           int quiet, int progress)
@@ -1498,6 +1835,9 @@ static struct cmd_struct commands[] = {
        {"resolve-relative-url-test", resolve_relative_url_test, 0},
        {"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},
        {"remote-branch", resolve_remote_submodule_branch, 0},
        {"push-check", push_check, 0},
        {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
index 7cef5b1..9efdc22 100644 (file)
@@ -14,7 +14,7 @@
 #include "worktree.h"
 
 static const char * const worktree_usage[] = {
-       N_("git worktree add [<options>] <path> [<branch>]"),
+       N_("git worktree add [<options>] <path> [<commit-ish>]"),
        N_("git worktree list [<options>]"),
        N_("git worktree lock [<options>] <path>"),
        N_("git worktree prune [<options>]"),
index 9329096..efe547e 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -134,7 +134,6 @@ int verify_bundle(struct bundle_header *header, int verbose)
        struct ref_list *p = &header->prerequisites;
        struct rev_info revs;
        const char *argv[] = {NULL, "--all", NULL};
-       struct object_array refs;
        struct commit *commit;
        int i, ret = 0, req_nr;
        const char *message = _("Repository lacks these prerequisite commits:");
@@ -157,14 +156,6 @@ int verify_bundle(struct bundle_header *header, int verbose)
        req_nr = revs.pending.nr;
        setup_revisions(2, argv, &revs, NULL);
 
-       /* Save pending objects, so they can be cleaned up later. */
-       refs = revs.pending;
-       revs.leak_pending = 1;
-
-       /*
-        * prepare_revision_walk (together with .leak_pending = 1) makes us
-        * the sole owner of the list of pending objects.
-        */
        if (prepare_revision_walk(&revs))
                die(_("revision walk setup failed"));
 
@@ -173,18 +164,24 @@ int verify_bundle(struct bundle_header *header, int verbose)
                if (commit->object.flags & PREREQ_MARK)
                        i--;
 
-       for (i = 0; i < req_nr; i++)
-               if (!(refs.objects[i].item->flags & SHOWN)) {
-                       if (++ret == 1)
-                               error("%s", message);
-                       error("%s %s", oid_to_hex(&refs.objects[i].item->oid),
-                               refs.objects[i].name);
-               }
+       for (i = 0; i < p->nr; i++) {
+               struct ref_list_entry *e = p->list + i;
+               struct object *o = parse_object(&e->oid);
+               assert(o); /* otherwise we'd have returned early */
+               if (o->flags & SHOWN)
+                       continue;
+               if (++ret == 1)
+                       error("%s", message);
+               error("%s %s", oid_to_hex(&e->oid), e->name);
+       }
 
        /* Clean up objects used, as they will be reused. */
-       clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
-
-       object_array_clear(&refs);
+       for (i = 0; i < p->nr; i++) {
+               struct ref_list_entry *e = p->list + i;
+               commit = lookup_commit_reference_gently(&e->oid, 1);
+               if (commit)
+                       clear_commit_marks(commit, ALL_REV_FLAGS);
+       }
 
        if (verbose) {
                struct ref_list *r;
index e03e72c..3841cef 100644 (file)
@@ -84,9 +84,8 @@ static struct cache_tree_sub *find_subtree(struct cache_tree *it,
        down->namelen = pathlen;
 
        if (pos < it->subtree_nr)
-               memmove(it->down + pos + 1,
-                       it->down + pos,
-                       sizeof(down) * (it->subtree_nr - pos - 1));
+               MOVE_ARRAY(it->down + pos + 1, it->down + pos,
+                          it->subtree_nr - pos - 1);
        it->down[pos] = down;
        return down;
 }
@@ -608,7 +607,7 @@ int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, co
 
        hold_lock_file_for_update(&lock_file, index_path, LOCK_DIE_ON_ERROR);
 
-       entries = read_index_from(index_state, index_path);
+       entries = read_index_from(index_state, index_path, get_git_dir());
        if (entries < 0) {
                ret = WRITE_TREE_UNREADABLE_INDEX;
                goto out;
diff --git a/cache.h b/cache.h
index d8b975a..9cac7bb 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -4,7 +4,7 @@
 #include "git-compat-util.h"
 #include "strbuf.h"
 #include "hashmap.h"
-#include "mru.h"
+#include "list.h"
 #include "advice.h"
 #include "gettext.h"
 #include "convert.h"
@@ -345,7 +345,8 @@ struct index_state {
        struct split_index *split_index;
        struct cache_time timestamp;
        unsigned name_hash_initialized : 1,
-                initialized : 1;
+                initialized : 1,
+                drop_cache_tree : 1;
        struct hashmap name_hash;
        struct hashmap dir_hash;
        unsigned char sha1[20];
@@ -371,7 +372,7 @@ extern void free_name_hash(struct index_state *istate);
 #define active_cache_tree (the_index.cache_tree)
 
 #define read_cache() read_index(&the_index)
-#define read_cache_from(path) read_index_from(&the_index, (path))
+#define read_cache_from(path) read_index_from(&the_index, (path), (get_git_dir()))
 #define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec))
 #define is_cache_unborn() is_index_unborn(&the_index)
 #define read_cache_unmerged() read_index_unmerged(&the_index)
@@ -616,7 +617,8 @@ extern int read_index(struct index_state *);
 extern int read_index_preload(struct index_state *, const struct pathspec *pathspec);
 extern int do_read_index(struct index_state *istate, const char *path,
                         int must_exist); /* for testting only! */
-extern int read_index_from(struct index_state *, const char *path);
+extern int read_index_from(struct index_state *, const char *path,
+                          const char *gitdir);
 extern int is_index_unborn(struct index_state *);
 extern int read_index_unmerged(struct index_state *);
 
@@ -914,10 +916,13 @@ extern int grafts_replace_parents;
 #define GIT_REPO_VERSION 0
 #define GIT_REPO_VERSION_READ 1
 extern int repository_format_precious_objects;
+extern char *repository_format_partial_clone;
+extern const char *core_partial_clone_filter_default;
 
 struct repository_format {
        int version;
        int precious_objects;
+       char *partial_clone; /* value of extensions.partialclone */
        int is_bare;
        int hash_algo;
        char *work_tree;
@@ -957,12 +962,10 @@ extern void check_repository_format(void);
 #define TYPE_CHANGED    0x0040
 
 /*
- * Return the name of the file in the local object database that would
- * be used to store a loose object with the specified sha1.  The
- * return value is a pointer to a statically allocated buffer that is
- * overwritten each time the function is called.
+ * Put in `buf` the name of the file in the local object database that
+ * would be used to store a loose object with the specified sha1.
  */
-extern const char *sha1_file_name(const unsigned char *sha1);
+extern void sha1_file_name(struct strbuf *buf, const unsigned char *sha1);
 
 /*
  * Return an abbreviated sha1 unique within this repository's object database.
@@ -1635,6 +1638,7 @@ struct pack_window {
 
 extern struct packed_git {
        struct packed_git *next;
+       struct list_head mru;
        struct pack_window *windows;
        off_t pack_size;
        const void *index_data;
@@ -1648,7 +1652,8 @@ extern struct packed_git {
        unsigned pack_local:1,
                 pack_keep:1,
                 freshened:1,
-                do_not_close:1;
+                do_not_close:1,
+                pack_promisor:1;
        unsigned char sha1[20];
        struct revindex_entry *revindex;
        /* something like ".git/objects/pack/xxxxx.pack" */
@@ -1656,10 +1661,9 @@ extern struct packed_git {
 } *packed_git;
 
 /*
- * A most-recently-used ordered version of the packed_git list, which can
- * be iterated instead of packed_git (and marked via mru_mark).
+ * A most-recently-used ordered version of the packed_git list.
  */
-extern struct mru packed_git_mru;
+extern struct list_head packed_git_mru;
 
 struct pack_entry {
        off_t offset;
@@ -1787,6 +1791,14 @@ struct object_info {
 #define OBJECT_INFO_QUICK 8
 extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
 
+/*
+ * Set this to 0 to prevent sha1_object_info_extended() from fetching missing
+ * blobs. This has a difference only if extensions.partialClone is set.
+ *
+ * Its default value is 1.
+ */
+extern int fetch_if_missing;
+
 /* Dumb servers support */
 extern int update_server_info(int);
 
index 07f27c7..1efee55 100755 (executable)
@@ -21,8 +21,6 @@ skip_branch_tip_with_tag () {
        fi
 }
 
-good_trees_file="$HOME/travis-cache/good-trees"
-
 # Save some info about the current commit's tree, so we can skip the build
 # job if we encounter the same tree again and can provide a useful info
 # message.
@@ -83,7 +81,10 @@ check_unignored_build_artifacts ()
 # and installing dependencies.
 set -ex
 
-mkdir -p "$HOME/travis-cache"
+cache_dir="$HOME/travis-cache"
+good_trees_file="$cache_dir/good-trees"
+
+mkdir -p "$cache_dir"
 
 skip_branch_tip_with_tag
 skip_good_tree
index c19c50c..2c60d2e 100755 (executable)
@@ -3,31 +3,58 @@
 # Build and test Git in a 32-bit environment
 #
 # Usage:
-#   run-linux32-build.sh [host-user-id]
+#   run-linux32-build.sh <host-user-id>
 #
 
-set -x
+set -ex
+
+if test $# -ne 1 || test -z "$1"
+then
+       echo >&2 "usage: run-linux32-build.sh <host-user-id>"
+       exit 1
+fi
 
 # Update packages to the latest available versions
 linux32 --32bit i386 sh -c '
     apt update >/dev/null &&
     apt install -y build-essential libcurl4-openssl-dev libssl-dev \
        libexpat-dev gettext python >/dev/null
-' &&
+'
 
 # If this script runs inside a docker container, then all commands are
 # usually executed as root. Consequently, the host user might not be
 # able to access the test output files.
-# If a host user id is given, then create a user "ci" with the host user
-# id to make everything accessible to the host user.
-HOST_UID=$1 &&
-CI_USER=$USER &&
-test -z $HOST_UID || (CI_USER="ci" && useradd -u $HOST_UID $CI_USER) &&
+# If a non 0 host user id is given, then create a user "ci" with that
+# user id to make everything accessible to the host user.
+HOST_UID=$1
+if test $HOST_UID -eq 0
+then
+       # Just in case someone does want to run the test suite as root.
+       CI_USER=root
+else
+       CI_USER=ci
+       if test "$(id -u $CI_USER 2>/dev/null)" = $HOST_UID
+       then
+               echo "user '$CI_USER' already exists with the requested ID $HOST_UID"
+       else
+               useradd -u $HOST_UID $CI_USER
+       fi
+
+       # Due to a bug the test suite was run as root in the past, so
+       # a prove state file created back then is only accessible by
+       # root.  Now that bug is fixed, the test suite is run as a
+       # regular user, but the prove state file coming from Travis
+       # CI's cache might still be owned by root.
+       # Make sure that this user has rights to any cached files,
+       # including an existing prove state file.
+       test -n "$cache_dir" && chown -R $HOST_UID:$HOST_UID "$cache_dir"
+fi
 
 # Build and test
 linux32 --32bit i386 su -m -l $CI_USER -c '
-    cd /usr/src/git &&
-    ln -s /tmp/travis-cache/.prove t/.prove &&
-    make --jobs=2 &&
-    make --quiet test
+       set -ex
+       cd /usr/src/git
+       test -n "$cache_dir" && ln -s "$cache_dir/.prove" t/.prove
+       make --jobs=2
+       make --quiet test
 '
index 4f191c5..2163790 100755 (executable)
@@ -9,7 +9,9 @@ docker pull daald/ubuntu32:xenial
 
 # Use the following command to debug the docker build locally:
 # $ docker run -itv "${PWD}:/usr/src/git" --entrypoint /bin/bash daald/ubuntu32:xenial
-# root@container:/# /usr/src/git/ci/run-linux32-build.sh
+# root@container:/# /usr/src/git/ci/run-linux32-build.sh <host-user-id>
+
+container_cache_dir=/tmp/travis-cache
 
 docker run \
        --interactive \
@@ -18,8 +20,9 @@ docker run \
        --env GIT_PROVE_OPTS \
        --env GIT_TEST_OPTS \
        --env GIT_TEST_CLONE_2GB \
+       --env cache_dir="$container_cache_dir" \
        --volume "${PWD}:/usr/src/git" \
-       --volume "${HOME}/travis-cache:/tmp/travis-cache" \
+       --volume "$cache_dir:$container_cache_dir" \
        daald/ubuntu32:xenial \
        /usr/src/git/ci/run-linux32-build.sh $(id -u $USER)
 
index 22355f0..73e273f 100755 (executable)
@@ -5,8 +5,13 @@
 
 . ${0%/*}/lib-travisci.sh
 
-ln -s $HOME/travis-cache/.prove t/.prove
+ln -s "$cache_dir/.prove" t/.prove
+
 make --quiet test
+if test "$jobname" = "linux-gcc"
+then
+       GIT_TEST_SPLIT_INDEX=YesPlease make --quiet test
+fi
 
 check_unignored_build_artifacts
 
index 2505de1..18c74da 100644 (file)
@@ -1053,7 +1053,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                        if (is_file) {
                                struct strbuf buf = STRBUF_INIT;
 
-                               if (convert_to_git(&the_index, elem->path, result, len, &buf, safe_crlf)) {
+                               if (convert_to_git(&the_index, elem->path, result, len, &buf, global_conv_flags_eol)) {
                                        free(result);
                                        result = strbuf_detach(&buf, &len);
                                        result_size = len;
@@ -1438,7 +1438,7 @@ void diff_tree_combined(const struct object_id *oid,
                        opt->flags.follow_renames       ||
                        opt->break_opt != -1    ||
                        opt->detect_rename      ||
-                       opt->pickaxe            ||
+                       (opt->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)   ||
                        opt->filter;
 
 
index cab8d44..c948c8b 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -126,10 +126,8 @@ int register_commit_graft(struct commit_graft *graft, int ignore_dups)
        ALLOC_GROW(commit_graft, commit_graft_nr + 1, commit_graft_alloc);
        commit_graft_nr++;
        if (pos < commit_graft_nr)
-               memmove(commit_graft + pos + 1,
-                       commit_graft + pos,
-                       (commit_graft_nr - pos - 1) *
-                       sizeof(*commit_graft));
+               MOVE_ARRAY(commit_graft + pos + 1, commit_graft + pos,
+                          commit_graft_nr - pos - 1);
        commit_graft[pos] = graft;
        return 0;
 }
@@ -547,7 +545,7 @@ void clear_commit_marks_many(int nr, struct commit **commit, unsigned int mark)
        struct commit_list *list = NULL;
 
        while (nr--) {
-               commit_list_insert(*commit, &list);
+               clear_commit_marks_1(&list, *commit, mark);
                commit++;
        }
        while (list)
@@ -559,20 +557,6 @@ void clear_commit_marks(struct commit *commit, unsigned int mark)
        clear_commit_marks_many(1, &commit, mark);
 }
 
-void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark)
-{
-       struct object *object;
-       struct commit *commit;
-       unsigned int i;
-
-       for (i = 0; i < a->nr; i++) {
-               object = a->objects[i].item;
-               commit = lookup_commit_reference_gently(&object->oid, 1);
-               if (commit)
-                       clear_commit_marks(commit, mark);
-       }
-}
-
 struct commit *pop_commit(struct commit_list **stack)
 {
        struct commit_list *top = *stack;
@@ -929,8 +913,7 @@ static int remove_redundant(struct commit **array, int cnt)
                        if (work[j]->object.flags & PARENT1)
                                redundant[filled_index[j]] = 1;
                clear_commit_marks(array[i], all_flags);
-               for (j = 0; j < filled; j++)
-                       clear_commit_marks(work[j], all_flags);
+               clear_commit_marks_many(filled, work, all_flags);
                free_commit_list(common);
        }
 
index 8c68ca1..425f402 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -140,7 +140,6 @@ struct commit *pop_commit(struct commit_list **stack);
 
 void clear_commit_marks(struct commit *commit, unsigned int mark);
 void clear_commit_marks_many(int nr, struct commit **commit, unsigned int mark);
-void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark);
 
 
 enum rev_sort_order {
index e617c20..b0c20e6 100644 (file)
--- a/config.c
+++ b/config.c
@@ -1149,11 +1149,14 @@ static int git_default_core_config(const char *var, const char *value)
        }
 
        if (!strcmp(var, "core.safecrlf")) {
+               int eol_rndtrp_die;
                if (value && !strcasecmp(value, "warn")) {
-                       safe_crlf = SAFE_CRLF_WARN;
+                       global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
                        return 0;
                }
-               safe_crlf = git_config_bool(var, value);
+               eol_rndtrp_die = git_config_bool(var, value);
+               global_conv_flags_eol = eol_rndtrp_die ?
+                       CONV_EOL_RNDTRP_DIE : CONV_EOL_RNDTRP_WARN;
                return 0;
        }
 
@@ -1251,6 +1254,11 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.partialclonefilter")) {
+               return git_config_string(&core_partial_clone_filter_default,
+                                        var, value);
+       }
+
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
 }
index 4a47f33..91feb78 100644 (file)
@@ -56,6 +56,8 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
        argv_array_push(&rev_list.args,"rev-list");
        argv_array_push(&rev_list.args, "--objects");
        argv_array_push(&rev_list.args, "--stdin");
+       if (repository_format_partial_clone)
+               argv_array_push(&rev_list.args, "--exclude-promisor-objects");
        argv_array_push(&rev_list.args, "--not");
        argv_array_push(&rev_list.args, "--all");
        argv_array_push(&rev_list.args, "--quiet");
index 1d580e4..6fe8727 100644 (file)
@@ -29,8 +29,9 @@ cocci.include_match("%" not in fmt)
 
 @@
 expression E1, E2;
+format F =~ "s";
 @@
-- strbuf_addf(E1, "%s", E2);
+- strbuf_addf(E1, "%@F@", E2);
 + strbuf_addstr(E1, E2);
 
 @@
index 3683c77..88813e9 100644 (file)
@@ -594,7 +594,7 @@ __git_is_configured_remote ()
 
 __git_list_merge_strategies ()
 {
-       git merge -s help 2>&1 |
+       LANG=C LC_ALL=C git merge -s help 2>&1 |
        sed -n -e '/[Aa]vailable strategies are: /,/^$/{
                s/\.$//
                s/.*://
index df59bdf..fb0fd0b 100755 (executable)
@@ -13,7 +13,7 @@
 use 5.008;
 use strict;
 use warnings;
-use Error qw(:try);
+use Git::Error qw(:try);
 use File::Basename qw(dirname);
 use File::Copy;
 use File::Find;
index 1a41a48..b976eb9 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -193,30 +193,30 @@ static enum eol output_eol(enum crlf_action crlf_action)
        return core_eol;
 }
 
-static void check_safe_crlf(const char *path, enum crlf_action crlf_action,
+static void check_global_conv_flags_eol(const char *path, enum crlf_action crlf_action,
                            struct text_stat *old_stats, struct text_stat *new_stats,
-                           enum safe_crlf checksafe)
+                           int conv_flags)
 {
        if (old_stats->crlf && !new_stats->crlf ) {
                /*
                 * CRLFs would not be restored by checkout
                 */
-               if (checksafe == SAFE_CRLF_WARN)
+               if (conv_flags & CONV_EOL_RNDTRP_DIE)
+                       die(_("CRLF would be replaced by LF in %s."), path);
+               else if (conv_flags & CONV_EOL_RNDTRP_WARN)
                        warning(_("CRLF will be replaced by LF in %s.\n"
                                  "The file will have its original line"
                                  " endings in your working directory."), path);
-               else /* i.e. SAFE_CRLF_FAIL */
-                       die(_("CRLF would be replaced by LF in %s."), path);
        } else if (old_stats->lonelf && !new_stats->lonelf ) {
                /*
                 * CRLFs would be added by checkout
                 */
-               if (checksafe == SAFE_CRLF_WARN)
+               if (conv_flags & CONV_EOL_RNDTRP_DIE)
+                       die(_("LF would be replaced by CRLF in %s"), path);
+               else if (conv_flags & CONV_EOL_RNDTRP_WARN)
                        warning(_("LF will be replaced by CRLF in %s.\n"
                                  "The file will have its original line"
                                  " endings in your working directory."), path);
-               else /* i.e. SAFE_CRLF_FAIL */
-                       die(_("LF would be replaced by CRLF in %s"), path);
        }
 }
 
@@ -268,7 +268,7 @@ static int will_convert_lf_to_crlf(size_t len, struct text_stat *stats,
 static int crlf_to_git(const struct index_state *istate,
                       const char *path, const char *src, size_t len,
                       struct strbuf *buf,
-                      enum crlf_action crlf_action, enum safe_crlf checksafe)
+                      enum crlf_action crlf_action, int conv_flags)
 {
        struct text_stat stats;
        char *dst;
@@ -298,12 +298,12 @@ static int crlf_to_git(const struct index_state *istate,
                 * unless we want to renormalize in a merge or
                 * cherry-pick.
                 */
-               if ((checksafe != SAFE_CRLF_RENORMALIZE) &&
+               if ((!(conv_flags & CONV_EOL_RENORMALIZE)) &&
                    has_crlf_in_index(istate, path))
                        convert_crlf_into_lf = 0;
        }
-       if ((checksafe == SAFE_CRLF_WARN ||
-           (checksafe == SAFE_CRLF_FAIL)) && len) {
+       if (((conv_flags & CONV_EOL_RNDTRP_WARN) ||
+            ((conv_flags & CONV_EOL_RNDTRP_DIE) && len))) {
                struct text_stat new_stats;
                memcpy(&new_stats, &stats, sizeof(new_stats));
                /* simulate "git add" */
@@ -316,7 +316,7 @@ static int crlf_to_git(const struct index_state *istate,
                        new_stats.crlf += new_stats.lonelf;
                        new_stats.lonelf = 0;
                }
-               check_safe_crlf(path, crlf_action, &stats, &new_stats, checksafe);
+               check_global_conv_flags_eol(path, crlf_action, &stats, &new_stats, conv_flags);
        }
        if (!convert_crlf_into_lf)
                return 0;
@@ -1129,7 +1129,7 @@ const char *get_convert_attr_ascii(const char *path)
 
 int convert_to_git(const struct index_state *istate,
                   const char *path, const char *src, size_t len,
-                   struct strbuf *dst, enum safe_crlf checksafe)
+                  struct strbuf *dst, int conv_flags)
 {
        int ret = 0;
        struct conv_attrs ca;
@@ -1144,8 +1144,8 @@ int convert_to_git(const struct index_state *istate,
                src = dst->buf;
                len = dst->len;
        }
-       if (checksafe != SAFE_CRLF_KEEP_CRLF) {
-               ret |= crlf_to_git(istate, path, src, len, dst, ca.crlf_action, checksafe);
+       if (!(conv_flags & CONV_EOL_KEEP_CRLF)) {
+               ret |= crlf_to_git(istate, path, src, len, dst, ca.crlf_action, conv_flags);
                if (ret && dst) {
                        src = dst->buf;
                        len = dst->len;
@@ -1156,7 +1156,7 @@ int convert_to_git(const struct index_state *istate,
 
 void convert_to_git_filter_fd(const struct index_state *istate,
                              const char *path, int fd, struct strbuf *dst,
-                             enum safe_crlf checksafe)
+                             int conv_flags)
 {
        struct conv_attrs ca;
        convert_attrs(&ca, path);
@@ -1167,7 +1167,7 @@ void convert_to_git_filter_fd(const struct index_state *istate,
        if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN, NULL))
                die("%s: clean filter '%s' failed", path, ca.drv->name);
 
-       crlf_to_git(istate, path, dst->buf, dst->len, dst, ca.crlf_action, checksafe);
+       crlf_to_git(istate, path, dst->buf, dst->len, dst, ca.crlf_action, conv_flags);
        ident_to_git(path, dst->buf, dst->len, dst, ca.ident);
 }
 
@@ -1226,7 +1226,7 @@ int renormalize_buffer(const struct index_state *istate, const char *path,
                src = dst->buf;
                len = dst->len;
        }
-       return ret | convert_to_git(istate, path, src, len, dst, SAFE_CRLF_RENORMALIZE);
+       return ret | convert_to_git(istate, path, src, len, dst, CONV_EOL_RENORMALIZE);
 }
 
 /*****************************************************************
index 4f2da22..65ab3e5 100644 (file)
--- a/convert.h
+++ b/convert.h
@@ -8,15 +8,12 @@
 
 struct index_state;
 
-enum safe_crlf {
-       SAFE_CRLF_FALSE = 0,
-       SAFE_CRLF_FAIL = 1,
-       SAFE_CRLF_WARN = 2,
-       SAFE_CRLF_RENORMALIZE = 3,
-       SAFE_CRLF_KEEP_CRLF = 4
-};
+#define CONV_EOL_RNDTRP_DIE   (1<<0) /* Die if CRLF to LF to CRLF is different */
+#define CONV_EOL_RNDTRP_WARN  (1<<1) /* Warn if CRLF to LF to CRLF is different */
+#define CONV_EOL_RENORMALIZE  (1<<2) /* Convert CRLF to LF */
+#define CONV_EOL_KEEP_CRLF    (1<<3) /* Keep CRLF line endings as is */
 
-extern enum safe_crlf safe_crlf;
+extern int global_conv_flags_eol;
 
 enum auto_crlf {
        AUTO_CRLF_FALSE = 0,
@@ -66,7 +63,7 @@ extern const char *get_convert_attr_ascii(const char *path);
 /* returns 1 if *dst was used */
 extern int convert_to_git(const struct index_state *istate,
                          const char *path, const char *src, size_t len,
-                         struct strbuf *dst, enum safe_crlf checksafe);
+                         struct strbuf *dst, int conv_flags);
 extern int convert_to_working_tree(const char *path, const char *src,
                                   size_t len, struct strbuf *dst);
 extern int async_convert_to_working_tree(const char *path, const char *src,
@@ -85,7 +82,7 @@ static inline int would_convert_to_git(const struct index_state *istate,
 extern void convert_to_git_filter_fd(const struct index_state *istate,
                                     const char *path, int fd,
                                     struct strbuf *dst,
-                                    enum safe_crlf checksafe);
+                                    int conv_flags);
 extern int would_convert_to_git_filter_fd(const char *path);
 
 /*****************************************************************
index e37e343..72dfeaf 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -597,6 +597,7 @@ static char *parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen)
                if (strncasecmp("host=", extra_args, 5) == 0) {
                        val = extra_args + 5;
                        vallen = strlen(val) + 1;
+                       loginfo("Extended attribute \"host\": %s", val);
                        if (*val) {
                                /* Split <host>:<port> at colon. */
                                char *host;
@@ -647,9 +648,11 @@ static void parse_extra_args(struct hostinfo *hi, struct argv_array *env,
                }
        }
 
-       if (git_protocol.len > 0)
+       if (git_protocol.len > 0) {
+               loginfo("Extended attribute \"protocol\": %s", git_protocol.buf);
                argv_array_pushf(env, GIT_PROTOCOL_ENVIRONMENT "=%s",
                                 git_protocol.buf);
+       }
        strbuf_release(&git_protocol);
 }
 
@@ -757,14 +760,8 @@ static int execute(void)
        alarm(0);
 
        len = strlen(line);
-       if (pktlen != len)
-               loginfo("Extended attributes (%d bytes) exist <%.*s>",
-                       (int) pktlen - len,
-                       (int) pktlen - len, line + len + 1);
-       if (len && line[len-1] == '\n') {
-               line[--len] = 0;
-               pktlen--;
-       }
+       if (len && line[len-1] == '\n')
+               line[len-1] = 0;
 
        /* parse additional args hidden behind a NUL byte */
        if (len != pktlen)
diff --git a/diff.c b/diff.c
index fb22b19..21c3838 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -246,7 +246,7 @@ static int parse_ws_error_highlight(const char *arg)
  */
 void init_diff_ui_defaults(void)
 {
-       diff_detect_rename_default = 1;
+       diff_detect_rename_default = DIFF_DETECT_RENAME;
 }
 
 int git_diff_heuristic_config(const char *var, const char *value, void *cb)
@@ -3520,13 +3520,13 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
 {
        int size_only = flags & CHECK_SIZE_ONLY;
        int err = 0;
+       int conv_flags = global_conv_flags_eol;
        /*
         * demote FAIL to WARN to allow inspecting the situation
         * instead of refusing.
         */
-       enum safe_crlf crlf_warn = (safe_crlf == SAFE_CRLF_FAIL
-                                   ? SAFE_CRLF_WARN
-                                   : safe_crlf);
+       if (conv_flags & CONV_EOL_RNDTRP_DIE)
+               conv_flags = CONV_EOL_RNDTRP_WARN;
 
        if (!DIFF_FILE_VALID(s))
                die("internal error: asking to populate invalid file.");
@@ -3603,7 +3603,7 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
                /*
                 * Convert from working tree format to canonical git format
                 */
-               if (convert_to_git(&the_index, s->path, s->data, s->size, &buf, crlf_warn)) {
+               if (convert_to_git(&the_index, s->path, s->data, s->size, &buf, conv_flags)) {
                        size_t size = 0;
                        munmap(s->data, s->size);
                        s->should_munmap = 0;
@@ -4086,6 +4086,7 @@ void diff_setup(struct diff_options *options)
        options->interhunkcontext = diff_interhunk_context_default;
        options->ws_error_highlight = ws_error_highlight_default;
        options->flags.rename_empty = 1;
+       options->objfind = NULL;
 
        /* pathchange left =NULL by default */
        options->change = diff_change;
@@ -4110,22 +4111,20 @@ void diff_setup(struct diff_options *options)
 
 void diff_setup_done(struct diff_options *options)
 {
-       int count = 0;
+       unsigned check_mask = DIFF_FORMAT_NAME |
+                             DIFF_FORMAT_NAME_STATUS |
+                             DIFF_FORMAT_CHECKDIFF |
+                             DIFF_FORMAT_NO_OUTPUT;
 
        if (options->set_default)
                options->set_default(options);
 
-       if (options->output_format & DIFF_FORMAT_NAME)
-               count++;
-       if (options->output_format & DIFF_FORMAT_NAME_STATUS)
-               count++;
-       if (options->output_format & DIFF_FORMAT_CHECKDIFF)
-               count++;
-       if (options->output_format & DIFF_FORMAT_NO_OUTPUT)
-               count++;
-       if (count > 1)
+       if (HAS_MULTI_BITS(options->output_format & check_mask))
                die(_("--name-only, --name-status, --check and -s are mutually exclusive"));
 
+       if (HAS_MULTI_BITS(options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK))
+               die(_("-G, -S and --find-object are mutually exclusive"));
+
        /*
         * Most of the time we can say "there are changes"
         * only by checking if there are changed paths, but
@@ -4175,7 +4174,7 @@ void diff_setup_done(struct diff_options *options)
        /*
         * Also pickaxe would not work very well if you do not say recursive
         */
-       if (options->pickaxe)
+       if (options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)
                options->flags.recursive = 1;
        /*
         * When patches are generated, submodules diffed against the work tree
@@ -4489,6 +4488,23 @@ static int parse_ws_error_highlight_opt(struct diff_options *opt, const char *ar
        return 1;
 }
 
+static int parse_objfind_opt(struct diff_options *opt, const char *arg)
+{
+       struct object_id oid;
+
+       if (get_oid(arg, &oid))
+               return error("unable to resolve '%s'", arg);
+
+       if (!opt->objfind)
+               opt->objfind = xcalloc(1, sizeof(*opt->objfind));
+
+       opt->pickaxe_opts |= DIFF_PICKAXE_KIND_OBJFIND;
+       opt->flags.recursive = 1;
+       opt->flags.tree_in_recursive = 1;
+       oidset_insert(opt->objfind, &oid);
+       return 1;
+}
+
 int diff_opt_parse(struct diff_options *options,
                   const char **av, int ac, const char *prefix)
 {
@@ -4736,7 +4752,8 @@ int diff_opt_parse(struct diff_options *options,
        else if ((argcount = short_opt('O', av, &optarg))) {
                options->orderfile = prefix_filename(prefix, optarg);
                return argcount;
-       }
+       } else if (skip_prefix(arg, "--find-object=", &arg))
+               return parse_objfind_opt(options, arg);
        else if ((argcount = parse_long_opt("diff-filter", av, &optarg))) {
                int offending = parse_diff_filter_opt(optarg, options);
                if (offending)
@@ -5454,6 +5471,7 @@ N_("you may want to set your %s variable to at least "
 
 void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc)
 {
+       fflush(stdout);
        if (degraded_cc)
                warning(_(degrade_cc_to_c_warning));
        else if (needed)
@@ -5783,7 +5801,7 @@ void diffcore_std(struct diff_options *options)
                if (options->break_opt != -1)
                        diffcore_merge_broken();
        }
-       if (options->pickaxe)
+       if (options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)
                diffcore_pickaxe(options);
        if (options->orderfile)
                diffcore_order(options->orderfile);
diff --git a/diff.h b/diff.h
index 7cf276f..6bd278a 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -7,6 +7,7 @@
 #include "tree-walk.h"
 #include "pathspec.h"
 #include "object.h"
+#include "oidset.h"
 
 struct rev_info;
 struct diff_options;
@@ -91,7 +92,6 @@ struct diff_flags {
        unsigned override_submodule_config:1;
        unsigned dirstat_by_line:1;
        unsigned funccontext:1;
-       unsigned pickaxe_ignore_case:1;
        unsigned default_follow_renames:1;
 };
 
@@ -146,7 +146,7 @@ struct diff_options {
        int skip_stat_unmatch;
        int line_termination;
        int output_format;
-       int pickaxe_opts;
+       unsigned pickaxe_opts;
        int rename_score;
        int rename_limit;
        int needed_rename_limit;
@@ -178,6 +178,8 @@ struct diff_options {
        enum diff_words_type word_diff;
        enum diff_submodule_format submodule_format;
 
+       struct oidset *objfind;
+
        /* this is set by diffcore for DIFF_FORMAT_PATCH */
        int found_changes;
 
@@ -330,6 +332,13 @@ extern void diff_setup_done(struct diff_options *);
 
 #define DIFF_PICKAXE_KIND_S    4 /* traditional plumbing counter */
 #define DIFF_PICKAXE_KIND_G    8 /* grep in the patch */
+#define DIFF_PICKAXE_KIND_OBJFIND      16 /* specific object IDs */
+
+#define DIFF_PICKAXE_KINDS_MASK (DIFF_PICKAXE_KIND_S | \
+                                DIFF_PICKAXE_KIND_G | \
+                                DIFF_PICKAXE_KIND_OBJFIND)
+
+#define DIFF_PICKAXE_IGNORE_CASE       32
 
 extern void diffcore_std(struct diff_options *);
 extern void diffcore_fix_diff_index(struct diff_options *);
index 9476bd2..239ce51 100644 (file)
@@ -124,13 +124,20 @@ static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
        mmfile_t mf1, mf2;
        int ret;
 
-       if (!o->pickaxe[0])
-               return 0;
-
        /* ignore unmerged */
        if (!DIFF_FILE_VALID(p->one) && !DIFF_FILE_VALID(p->two))
                return 0;
 
+       if (o->objfind) {
+               return  (DIFF_FILE_VALID(p->one) &&
+                        oidset_contains(o->objfind, &p->one->oid)) ||
+                       (DIFF_FILE_VALID(p->two) &&
+                        oidset_contains(o->objfind, &p->two->oid));
+       }
+
+       if (!o->pickaxe[0])
+               return 0;
+
        if (o->flags.allow_textconv) {
                textconv_one = get_textconv(p->one);
                textconv_two = get_textconv(p->two);
@@ -222,33 +229,34 @@ void diffcore_pickaxe(struct diff_options *o)
 
        if (opts & (DIFF_PICKAXE_REGEX | DIFF_PICKAXE_KIND_G)) {
                int cflags = REG_EXTENDED | REG_NEWLINE;
-               if (o->flags.pickaxe_ignore_case)
+               if (o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE)
                        cflags |= REG_ICASE;
                regcomp_or_die(&regex, needle, cflags);
                regexp = &regex;
-       } else if (o->flags.pickaxe_ignore_case &&
-                  has_non_ascii(needle)) {
-               struct strbuf sb = STRBUF_INIT;
-               int cflags = REG_NEWLINE | REG_ICASE;
-
-               basic_regex_quote_buf(&sb, needle);
-               regcomp_or_die(&regex, sb.buf, cflags);
-               strbuf_release(&sb);
-               regexp = &regex;
-       } else {
-               kws = kwsalloc(o->flags.pickaxe_ignore_case
-                              ? tolower_trans_tbl : NULL);
-               kwsincr(kws, needle, strlen(needle));
-               kwsprep(kws);
+       } else if (opts & DIFF_PICKAXE_KIND_S) {
+               if (o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE &&
+                   has_non_ascii(needle)) {
+                       struct strbuf sb = STRBUF_INIT;
+                       int cflags = REG_NEWLINE | REG_ICASE;
+
+                       basic_regex_quote_buf(&sb, needle);
+                       regcomp_or_die(&regex, sb.buf, cflags);
+                       strbuf_release(&sb);
+                       regexp = &regex;
+               } else {
+                       kws = kwsalloc(o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE
+                                      ? tolower_trans_tbl : NULL);
+                       kwsincr(kws, needle, strlen(needle));
+                       kwsprep(kws);
+               }
        }
 
-       /* Might want to warn when both S and G are on; I don't care... */
        pickaxe(&diff_queued_diff, o, regexp, kws,
                (opts & DIFF_PICKAXE_KIND_G) ? diff_grep : has_changes);
 
        if (regexp)
                regfree(regexp);
-       else
+       if (kws)
                kwsfree(kws);
        return;
 }
index 245e999..888a4b0 100644 (file)
@@ -57,8 +57,8 @@ static int add_rename_dst(struct diff_filespec *two)
        ALLOC_GROW(rename_dst, rename_dst_nr + 1, rename_dst_alloc);
        rename_dst_nr++;
        if (first < rename_dst_nr)
-               memmove(rename_dst + first + 1, rename_dst + first,
-                       (rename_dst_nr - first - 1) * sizeof(*rename_dst));
+               MOVE_ARRAY(rename_dst + first + 1, rename_dst + first,
+                          rename_dst_nr - first - 1);
        rename_dst[first].two = alloc_filespec(two->path);
        fill_filespec(rename_dst[first].two, &two->oid, two->oid_valid,
                      two->mode);
@@ -98,8 +98,8 @@ static struct diff_rename_src *register_rename_src(struct diff_filepair *p)
        ALLOC_GROW(rename_src, rename_src_nr + 1, rename_src_alloc);
        rename_src_nr++;
        if (first < rename_src_nr)
-               memmove(rename_src + first + 1, rename_src + first,
-                       (rename_src_nr - first - 1) * sizeof(*rename_src));
+               MOVE_ARRAY(rename_src + first + 1, rename_src + first,
+                          rename_src_nr - first - 1);
        rename_src[first].p = p;
        rename_src[first].score = score;
        return &(rename_src[first]);
diff --git a/dir.c b/dir.c
index 7c4b45e..ce6e50d 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -747,8 +747,8 @@ static struct untracked_cache_dir *lookup_untracked(struct untracked_cache *uc,
        FLEX_ALLOC_MEM(d, name, name, len);
 
        ALLOC_GROW(dir->dirs, dir->dirs_nr + 1, dir->dirs_alloc);
-       memmove(dir->dirs + first + 1, dir->dirs + first,
-               (dir->dirs_nr - first) * sizeof(*dir->dirs));
+       MOVE_ARRAY(dir->dirs + first + 1, dir->dirs + first,
+                  dir->dirs_nr - first);
        dir->dirs_nr++;
        dir->dirs[first] = d;
        return d;
index 63ac38a..de8431e 100644 (file)
@@ -27,6 +27,8 @@ int warn_ambiguous_refs = 1;
 int warn_on_object_refname_ambiguity = 1;
 int ref_paranoia = -1;
 int repository_format_precious_objects;
+char *repository_format_partial_clone;
+const char *core_partial_clone_filter_default;
 const char *git_commit_encoding;
 const char *git_log_output_encoding;
 const char *apply_default_whitespace;
@@ -49,7 +51,7 @@ enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
 int check_replace_refs = 1;
 char *git_replace_ref_base;
 enum eol core_eol = EOL_UNSET;
-enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
+int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
 unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
 enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
diff --git a/fetch-object.c b/fetch-object.c
new file mode 100644 (file)
index 0000000..853624f
--- /dev/null
@@ -0,0 +1,45 @@
+#include "cache.h"
+#include "packfile.h"
+#include "pkt-line.h"
+#include "strbuf.h"
+#include "transport.h"
+#include "fetch-object.h"
+
+static void fetch_refs(const char *remote_name, struct ref *ref)
+{
+       struct remote *remote;
+       struct transport *transport;
+       int original_fetch_if_missing = fetch_if_missing;
+
+       fetch_if_missing = 0;
+       remote = remote_get(remote_name);
+       if (!remote->url[0])
+               die(_("Remote with no URL"));
+       transport = transport_get(remote, remote->url[0]);
+
+       transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
+       transport_set_option(transport, TRANS_OPT_NO_DEPENDENTS, "1");
+       transport_fetch_refs(transport, ref);
+       fetch_if_missing = original_fetch_if_missing;
+}
+
+void fetch_object(const char *remote_name, const unsigned char *sha1)
+{
+       struct ref *ref = alloc_ref(sha1_to_hex(sha1));
+       hashcpy(ref->old_oid.hash, sha1);
+       fetch_refs(remote_name, ref);
+}
+
+void fetch_objects(const char *remote_name, const struct oid_array *to_fetch)
+{
+       struct ref *ref = NULL;
+       int i;
+
+       for (i = 0; i < to_fetch->nr; i++) {
+               struct ref *new_ref = alloc_ref(oid_to_hex(&to_fetch->oid[i]));
+               oidcpy(&new_ref->old_oid, &to_fetch->oid[i]);
+               new_ref->next = ref;
+               ref = new_ref;
+       }
+       fetch_refs(remote_name, ref);
+}
diff --git a/fetch-object.h b/fetch-object.h
new file mode 100644 (file)
index 0000000..4b269d0
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef FETCH_OBJECT_H
+#define FETCH_OBJECT_H
+
+#include "sha1-array.h"
+
+extern void fetch_object(const char *remote_name, const unsigned char *sha1);
+
+extern void fetch_objects(const char *remote_name,
+                         const struct oid_array *to_fetch);
+
+#endif
index 9f6b07a..8253d74 100644 (file)
@@ -29,6 +29,7 @@ static int deepen_not_ok;
 static int fetch_fsck_objects = -1;
 static int transfer_fsck_objects = -1;
 static int agent_supported;
+static int server_supports_filtering;
 static struct lock_file shallow_lock;
 static const char *alternate_shallow_file;
 
@@ -379,6 +380,8 @@ static int find_common(struct fetch_pack_args *args,
                        if (deepen_not_ok)      strbuf_addstr(&c, " deepen-not");
                        if (agent_supported)    strbuf_addf(&c, " agent=%s",
                                                            git_user_agent_sanitized());
+                       if (args->filter_options.choice)
+                               strbuf_addstr(&c, " filter");
                        packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf);
                        strbuf_release(&c);
                } else
@@ -407,6 +410,9 @@ static int find_common(struct fetch_pack_args *args,
                        packet_buf_write(&req_buf, "deepen-not %s", s->string);
                }
        }
+       if (server_supports_filtering && args->filter_options.choice)
+               packet_buf_write(&req_buf, "filter %s",
+                                args->filter_options.filter_spec);
        packet_buf_flush(&req_buf);
        state_len = req_buf.len;
 
@@ -450,6 +456,8 @@ static int find_common(struct fetch_pack_args *args,
 
        flushes = 0;
        retval = -1;
+       if (args->no_dependents)
+               goto done;
        while ((oid = get_rev())) {
                packet_buf_write(&req_buf, "have %s\n", oid_to_hex(oid));
                print_verbose(args, "have %s", oid_to_hex(oid));
@@ -709,6 +717,7 @@ static int everything_local(struct fetch_pack_args *args,
 {
        struct ref *ref;
        int retval;
+       int old_save_commit_buffer = save_commit_buffer;
        timestamp_t cutoff = 0;
 
        save_commit_buffer = 0;
@@ -735,29 +744,31 @@ static int everything_local(struct fetch_pack_args *args,
                }
        }
 
-       if (!args->deepen) {
-               for_each_ref(mark_complete_oid, NULL);
-               for_each_cached_alternate(mark_alternate_complete);
-               commit_list_sort_by_date(&complete);
-               if (cutoff)
-                       mark_recent_complete_commits(args, cutoff);
-       }
+       if (!args->no_dependents) {
+               if (!args->deepen) {
+                       for_each_ref(mark_complete_oid, NULL);
+                       for_each_cached_alternate(mark_alternate_complete);
+                       commit_list_sort_by_date(&complete);
+                       if (cutoff)
+                               mark_recent_complete_commits(args, cutoff);
+               }
 
-       /*
-        * Mark all complete remote refs as common refs.
-        * Don't mark them common yet; the server has to be told so first.
-        */
-       for (ref = *refs; ref; ref = ref->next) {
-               struct object *o = deref_tag(lookup_object(ref->old_oid.hash),
-                                            NULL, 0);
+               /*
+                * Mark all complete remote refs as common refs.
+                * Don't mark them common yet; the server has to be told so first.
+                */
+               for (ref = *refs; ref; ref = ref->next) {
+                       struct object *o = deref_tag(lookup_object(ref->old_oid.hash),
+                                                    NULL, 0);
 
-               if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
-                       continue;
+                       if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
+                               continue;
 
-               if (!(o->flags & SEEN)) {
-                       rev_list_push((struct commit *)o, COMMON_REF | SEEN);
+                       if (!(o->flags & SEEN)) {
+                               rev_list_push((struct commit *)o, COMMON_REF | SEEN);
 
-                       mark_common((struct commit *)o, 1, 1);
+                               mark_common((struct commit *)o, 1, 1);
+                       }
                }
        }
 
@@ -777,6 +788,9 @@ static int everything_local(struct fetch_pack_args *args,
                print_verbose(args, _("already have %s (%s)"), oid_to_hex(remote),
                              ref->name);
        }
+
+       save_commit_buffer = old_save_commit_buffer;
+
        return retval;
 }
 
@@ -833,7 +847,7 @@ static int get_pack(struct fetch_pack_args *args,
                argv_array_push(&cmd.args, alternate_shallow_file);
        }
 
-       if (do_keep) {
+       if (do_keep || args->from_promisor) {
                if (pack_lockfile)
                        cmd.out = -1;
                cmd_name = "index-pack";
@@ -843,7 +857,7 @@ static int get_pack(struct fetch_pack_args *args,
                        argv_array_push(&cmd.args, "-v");
                if (args->use_thin_pack)
                        argv_array_push(&cmd.args, "--fix-thin");
-               if (args->lock_pack || unpack_limit) {
+               if (do_keep && (args->lock_pack || unpack_limit)) {
                        char hostname[HOST_NAME_MAX + 1];
                        if (xgethostname(hostname, sizeof(hostname)))
                                xsnprintf(hostname, sizeof(hostname), "localhost");
@@ -853,6 +867,8 @@ static int get_pack(struct fetch_pack_args *args,
                }
                if (args->check_self_contained_and_connected)
                        argv_array_push(&cmd.args, "--check-self-contained-and-connected");
+               if (args->from_promisor)
+                       argv_array_push(&cmd.args, "--promisor");
        }
        else {
                cmd_name = "unpack-objects";
@@ -964,6 +980,13 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
        else
                prefer_ofs_delta = 0;
 
+       if (server_supports("filter")) {
+               server_supports_filtering = 1;
+               print_verbose(args, _("Server supports filter"));
+       } else if (args->filter_options.choice) {
+               warning("filtering not recognized by server, ignoring");
+       }
+
        if ((agent_feature = server_feature_value("agent", &agent_len))) {
                agent_supported = 1;
                if (agent_len)
index b6aeb43..3e224a1 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "string-list.h"
 #include "run-command.h"
+#include "list-objects-filter-options.h"
 
 struct oid_array;
 
@@ -12,6 +13,7 @@ struct fetch_pack_args {
        int depth;
        const char *deepen_since;
        const struct string_list *deepen_not;
+       struct list_objects_filter_options filter_options;
        unsigned deepen_relative:1;
        unsigned quiet:1;
        unsigned keep_pack:1;
@@ -29,6 +31,14 @@ struct fetch_pack_args {
        unsigned cloning:1;
        unsigned update_shallow:1;
        unsigned deepen:1;
+       unsigned from_promisor:1;
+
+       /*
+        * If 1, fetch_pack() will also not modify any object flags.
+        * This allows fetch_pack() to safely be called by any function,
+        * regardless of which object flags it uses (if any).
+        */
+       unsigned no_dependents:1;
 };
 
 /*
index 28b325d..964c3a7 100755 (executable)
@@ -262,7 +262,7 @@ sub list_modified {
                }
        }
 
-       for (run_cmd_pipe(qw(git diff-files --numstat --summary --raw --), @ARGV)) {
+       for (run_cmd_pipe(qw(git diff-files --ignore-submodules=dirty --numstat --summary --raw --), @ARGV)) {
                if (($add, $del, $file) =
                    /^([-\d]+)  ([-\d]+)        (.*)/) {
                        $file = unquote_path($file);
index edcc6d3..bbf4dea 100755 (executable)
@@ -26,10 +26,11 @@ use Text::ParseWords;
 use Term::ANSIColor;
 use File::Temp qw/ tempdir tempfile /;
 use File::Spec::Functions qw(catdir catfile);
-use Error qw(:try);
+use Git::Error qw(:try);
 use Cwd qw(abs_path cwd);
 use Git;
 use Git::I18N;
+use Git::Mail::Address;
 
 Getopt::Long::Configure qw/ pass_through /;
 
@@ -489,7 +490,7 @@ my ($repoauthor, $repocommitter);
 ($repocommitter) = Git::ident_person(@repo, 'committer');
 
 sub parse_address_line {
-       return Git::parse_mailboxes($_[0]);
+       return map { $_->format } Mail::Address->parse($_[0]);
 }
 
 sub split_addrs {
index 1114005..fc8f8ae 100755 (executable)
@@ -322,10 +322,9 @@ push_stash () {
 
                if test $# != 0
                then
-                       git reset -q -- "$@"
-                       git ls-files -z --modified -- "$@" |
+                       git add -u -- "$@" |
                        git checkout-index -z --force --stdin
-                       git clean --force -q -d -- "$@"
+                       git diff-index -p --cached --binary HEAD -- "$@" | git apply --index -R
                else
                        git reset --hard -q
                fi
index 156255a..2491496 100755 (executable)
@@ -428,60 +428,7 @@ cmd_deinit()
                shift
        done
 
-       if test -n "$deinit_all" && test "$#" -ne 0
-       then
-               echo >&2 "$(eval_gettext "pathspec and --all are incompatible")"
-               usage
-       fi
-       if test $# = 0 && test -z "$deinit_all"
-       then
-               die "$(eval_gettext "Use '--all' if you really want to deinitialize all submodules")"
-       fi
-
-       {
-               git submodule--helper list --prefix "$wt_prefix" "$@" ||
-               echo "#unmatched" $?
-       } |
-       while read -r mode sha1 stage sm_path
-       do
-               die_if_unmatched "$mode" "$sha1"
-               name=$(git submodule--helper name "$sm_path") || exit
-
-               displaypath=$(git submodule--helper relative-path "$sm_path" "$wt_prefix")
-
-               # Remove the submodule work tree (unless the user already did it)
-               if test -d "$sm_path"
-               then
-                       # Protect submodules containing a .git directory
-                       if test -d "$sm_path/.git"
-                       then
-                               die "$(eval_gettext "\
-Submodule work tree '\$displaypath' contains a .git directory
-(use 'rm -rf' if you really want to remove it including all of its history)")"
-                       fi
-
-                       if test -z "$force"
-                       then
-                               git rm -qn "$sm_path" ||
-                               die "$(eval_gettext "Submodule work tree '\$displaypath' contains local modifications; use '-f' to discard them")"
-                       fi
-                       rm -rf "$sm_path" &&
-                       say "$(eval_gettext "Cleared directory '\$displaypath'")" ||
-                       say "$(eval_gettext "Could not remove submodule work tree '\$displaypath'")"
-               fi
-
-               mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$displaypath'")"
-
-               # Remove the .git/config entries (unless the user already did it)
-               if test -n "$(git config --get-regexp submodule."$name\.")"
-               then
-                       # Remove the whole section so we have a clean state when
-                       # the user later decides to init this submodule again
-                       url=$(git config submodule."$name".url)
-                       git config --remove-section submodule."$name" 2>/dev/null &&
-                       say "$(eval_gettext "Submodule '\$name' (\$url) unregistered for path '\$displaypath'")"
-               fi
-       done
+       git ${wt_prefix:+-C "$wt_prefix"} submodule--helper deinit ${GIT_QUIET:+--quiet} ${prefix:+--prefix "$prefix"} ${force:+--force} ${deinit_all:+--all} "$@"
 }
 
 is_tip_reachable () (
@@ -1036,63 +983,8 @@ cmd_sync()
                        ;;
                esac
        done
-       cd_to_toplevel
-       {
-               git submodule--helper list --prefix "$wt_prefix" "$@" ||
-               echo "#unmatched" $?
-       } |
-       while read -r mode sha1 stage sm_path
-       do
-               die_if_unmatched "$mode" "$sha1"
-
-               # skip inactive submodules
-               if ! git submodule--helper is-active "$sm_path"
-               then
-                       continue
-               fi
-
-               name=$(git submodule--helper name "$sm_path")
-               url=$(git config -f .gitmodules --get submodule."$name".url)
-
-               # Possibly a url relative to parent
-               case "$url" in
-               ./*|../*)
-                       # rewrite foo/bar as ../.. to find path from
-                       # submodule work tree to superproject work tree
-                       up_path="$(printf '%s\n' "$sm_path" | sed "s/[^/][^/]*/../g")" &&
-                       # guarantee a trailing /
-                       up_path=${up_path%/}/ &&
-                       # path from submodule work tree to submodule origin repo
-                       sub_origin_url=$(git submodule--helper resolve-relative-url "$url" "$up_path") &&
-                       # path from superproject work tree to submodule origin repo
-                       super_config_url=$(git submodule--helper resolve-relative-url "$url") || exit
-                       ;;
-               *)
-                       sub_origin_url="$url"
-                       super_config_url="$url"
-                       ;;
-               esac
-
-               displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
-               say "$(eval_gettext "Synchronizing submodule url for '\$displaypath'")"
-               git config submodule."$name".url "$super_config_url"
-
-               if test -e "$sm_path"/.git
-               then
-               (
-                       sanitize_submodule_env
-                       cd "$sm_path"
-                       remote=$(get_default_remote)
-                       git config remote."$remote".url "$sub_origin_url"
 
-                       if test -n "$recursive"
-                       then
-                               prefix="$prefix$sm_path/"
-                               eval cmd_sync
-                       fi
-               )
-               fi
-       done
+       git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper sync ${GIT_QUIET:+--quiet} ${recursive:+--recursive} "$@"
 }
 
 cmd_absorbgitdirs()
index aa242d4..a6b6c3e 100755 (executable)
@@ -931,6 +931,7 @@ sub cmd_dcommit {
                # information from different SVN repos, and paths
                # which are not underneath this repository root.
                my $rooturl = $gs->repos_root;
+               Git::SVN::remove_username($rooturl);
                foreach my $d (@$linear_refs) {
                        my %parentshash;
                        read_commit_parents(\%parentshash, $d);
@@ -1199,6 +1200,11 @@ sub cmd_branch {
        $ctx->copy($src, $rev, $dst)
                unless $_dry_run;
 
+       # Release resources held by ctx before creating another SVN::Ra
+       # so destruction is orderly.  This seems necessary with SVN 1.9.5
+       # to avoid segfaults.
+       $ctx = undef;
+
        $gs->fetch_all;
 }
 
index 7ce79f3..d375d9c 100644 (file)
--- a/hashmap.h
+++ b/hashmap.h
@@ -400,7 +400,6 @@ static inline void hashmap_disable_item_counting(struct hashmap *map)
  */
 static inline void hashmap_enable_item_counting(struct hashmap *map)
 {
-       void *item;
        unsigned int n = 0;
        struct hashmap_iter iter;
 
@@ -408,7 +407,7 @@ static inline void hashmap_enable_item_counting(struct hashmap *map)
                return;
 
        hashmap_iter_init(map, &iter);
-       while ((item = hashmap_iter_next(&iter)))
+       while (hashmap_iter_next(&iter))
                n++;
 
        map->do_count_items = 1;
index 14435ab..0913f8a 100644 (file)
@@ -915,6 +915,10 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
                                lock->timeout = -1;
                        }
                        XML_ParserFree(parser);
+               } else {
+                       fprintf(stderr,
+                               "error: curl result=%d, HTTP code=%ld\n",
+                               results.curl_result, results.http_code);
                }
        } else {
                fprintf(stderr, "Unable to start LOCK request\n");
index 1ae8363..07c2b1a 100644 (file)
@@ -544,8 +544,10 @@ static int fetch_object(struct walker *walker, unsigned char *sha1)
        } else if (hashcmp(obj_req->sha1, req->real_sha1)) {
                ret = error("File %s has bad hash", hex);
        } else if (req->rename < 0) {
-               ret = error("unable to write sha1 filename %s",
-                           sha1_file_name(req->sha1));
+               struct strbuf buf = STRBUF_INIT;
+               sha1_file_name(&buf, req->sha1);
+               ret = error("unable to write sha1 filename %s", buf.buf);
+               strbuf_release(&buf);
        }
 
        release_http_object_request(req);
diff --git a/http.c b/http.c
index 5977712..3175502 100644 (file)
--- a/http.c
+++ b/http.c
 #include "transport.h"
 #include "packfile.h"
 #include "protocol.h"
+#include "string-list.h"
 
 static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);
+static int trace_curl_data = 1;
+static struct string_list cookies_to_redact = STRING_LIST_INIT_DUP;
 #if LIBCURL_VERSION_NUM >= 0x070a08
 long int git_curl_ipresolve = CURL_IPRESOLVE_WHATEVER;
 #else
@@ -575,6 +578,54 @@ static void redact_sensitive_header(struct strbuf *header)
                /* Everything else is opaque and possibly sensitive */
                strbuf_setlen(header,  sensitive_header - header->buf);
                strbuf_addstr(header, " <redacted>");
+       } else if (cookies_to_redact.nr &&
+                  skip_prefix(header->buf, "Cookie:", &sensitive_header)) {
+               struct strbuf redacted_header = STRBUF_INIT;
+               char *cookie;
+
+               while (isspace(*sensitive_header))
+                       sensitive_header++;
+
+               /*
+                * The contents of header starting from sensitive_header will
+                * subsequently be overridden, so it is fine to mutate this
+                * string (hence the assignment to "char *").
+                */
+               cookie = (char *) sensitive_header;
+
+               while (cookie) {
+                       char *equals;
+                       char *semicolon = strstr(cookie, "; ");
+                       if (semicolon)
+                               *semicolon = 0;
+                       equals = strchrnul(cookie, '=');
+                       if (!equals) {
+                               /* invalid cookie, just append and continue */
+                               strbuf_addstr(&redacted_header, cookie);
+                               continue;
+                       }
+                       *equals = 0; /* temporarily set to NUL for lookup */
+                       if (string_list_lookup(&cookies_to_redact, cookie)) {
+                               strbuf_addstr(&redacted_header, cookie);
+                               strbuf_addstr(&redacted_header, "=<redacted>");
+                       } else {
+                               *equals = '=';
+                               strbuf_addstr(&redacted_header, cookie);
+                       }
+                       if (semicolon) {
+                               /*
+                                * There are more cookies. (Or, for some
+                                * reason, the input string ends in "; ".)
+                                */
+                               strbuf_addstr(&redacted_header, "; ");
+                               cookie = semicolon + strlen("; ");
+                       } else {
+                               cookie = NULL;
+                       }
+               }
+
+               strbuf_setlen(header, sensitive_header - header->buf);
+               strbuf_addbuf(header, &redacted_header);
        }
 }
 
@@ -645,24 +696,32 @@ static int curl_trace(CURL *handle, curl_infotype type, char *data, size_t size,
                curl_dump_header(text, (unsigned char *)data, size, DO_FILTER);
                break;
        case CURLINFO_DATA_OUT:
-               text = "=> Send data";
-               curl_dump_data(text, (unsigned char *)data, size);
+               if (trace_curl_data) {
+                       text = "=> Send data";
+                       curl_dump_data(text, (unsigned char *)data, size);
+               }
                break;
        case CURLINFO_SSL_DATA_OUT:
-               text = "=> Send SSL data";
-               curl_dump_data(text, (unsigned char *)data, size);
+               if (trace_curl_data) {
+                       text = "=> Send SSL data";
+                       curl_dump_data(text, (unsigned char *)data, size);
+               }
                break;
        case CURLINFO_HEADER_IN:
                text = "<= Recv header";
                curl_dump_header(text, (unsigned char *)data, size, NO_FILTER);
                break;
        case CURLINFO_DATA_IN:
-               text = "<= Recv data";
-               curl_dump_data(text, (unsigned char *)data, size);
+               if (trace_curl_data) {
+                       text = "<= Recv data";
+                       curl_dump_data(text, (unsigned char *)data, size);
+               }
                break;
        case CURLINFO_SSL_DATA_IN:
-               text = "<= Recv SSL data";
-               curl_dump_data(text, (unsigned char *)data, size);
+               if (trace_curl_data) {
+                       text = "<= Recv SSL data";
+                       curl_dump_data(text, (unsigned char *)data, size);
+               }
                break;
 
        default:                /* we ignore unknown types by default */
@@ -807,6 +866,13 @@ static CURL *get_curl_handle(void)
        if (getenv("GIT_CURL_VERBOSE"))
                curl_easy_setopt(result, CURLOPT_VERBOSE, 1L);
        setup_curl_trace(result);
+       if (getenv("GIT_TRACE_CURL_NO_DATA"))
+               trace_curl_data = 0;
+       if (getenv("GIT_REDACT_COOKIES")) {
+               string_list_split(&cookies_to_redact,
+                                 getenv("GIT_REDACT_COOKIES"), ',', -1);
+               string_list_sort(&cookies_to_redact);
+       }
 
        curl_easy_setopt(result, CURLOPT_USERAGENT,
                user_agent ? user_agent : git_user_agent());
@@ -2168,7 +2234,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
        unsigned char *sha1)
 {
        char *hex = sha1_to_hex(sha1);
-       const char *filename;
+       struct strbuf filename = STRBUF_INIT;
        char prevfile[PATH_MAX];
        int prevlocal;
        char prev_buf[PREV_BUF_SIZE];
@@ -2180,14 +2246,15 @@ struct http_object_request *new_http_object_request(const char *base_url,
        hashcpy(freq->sha1, sha1);
        freq->localfile = -1;
 
-       filename = sha1_file_name(sha1);
+       sha1_file_name(&filename, sha1);
        snprintf(freq->tmpfile, sizeof(freq->tmpfile),
-                "%s.temp", filename);
+                "%s.temp", filename.buf);
 
-       snprintf(prevfile, sizeof(prevfile), "%s.prev", filename);
+       snprintf(prevfile, sizeof(prevfile), "%s.prev", filename.buf);
        unlink_or_warn(prevfile);
        rename(freq->tmpfile, prevfile);
        unlink_or_warn(freq->tmpfile);
+       strbuf_release(&filename);
 
        if (freq->localfile != -1)
                error("fd leakage in start: %d", freq->localfile);
@@ -2302,6 +2369,7 @@ void process_http_object_request(struct http_object_request *freq)
 int finish_http_object_request(struct http_object_request *freq)
 {
        struct stat st;
+       struct strbuf filename = STRBUF_INIT;
 
        close(freq->localfile);
        freq->localfile = -1;
@@ -2327,8 +2395,10 @@ int finish_http_object_request(struct http_object_request *freq)
                unlink_or_warn(freq->tmpfile);
                return -1;
        }
-       freq->rename =
-               finalize_object_file(freq->tmpfile, sha1_file_name(freq->sha1));
+
+       sha1_file_name(&filename, freq->sha1);
+       freq->rename = finalize_object_file(freq->tmpfile, filename.buf);
+       strbuf_release(&filename);
 
        return freq->rename;
 }
index 4c5b34e..6a3cc98 100644 (file)
  * subordinate commands when necessary.  We also "intern" the arg for
  * the convenience of the current command.
  */
-int parse_list_objects_filter(struct list_objects_filter_options *filter_options,
-                             const char *arg)
+static int gently_parse_list_objects_filter(
+       struct list_objects_filter_options *filter_options,
+       const char *arg,
+       struct strbuf *errbuf)
 {
        const char *v0;
 
-       if (filter_options->choice)
-               die(_("multiple object filter types cannot be combined"));
+       if (filter_options->choice) {
+               if (errbuf) {
+                       strbuf_init(errbuf, 0);
+                       strbuf_addstr(
+                               errbuf,
+                               _("multiple filter-specs cannot be combined"));
+               }
+               return 1;
+       }
 
        filter_options->filter_spec = strdup(arg);
 
        if (!strcmp(arg, "blob:none")) {
                filter_options->choice = LOFC_BLOB_NONE;
                return 0;
-       }
 
-       if (skip_prefix(arg, "blob:limit=", &v0)) {
-               if (!git_parse_ulong(v0, &filter_options->blob_limit_value))
-                       die(_("invalid filter-spec expression '%s'"), arg);
-               filter_options->choice = LOFC_BLOB_LIMIT;
-               return 0;
-       }
+       } else if (skip_prefix(arg, "blob:limit=", &v0)) {
+               if (git_parse_ulong(v0, &filter_options->blob_limit_value)) {
+                       filter_options->choice = LOFC_BLOB_LIMIT;
+                       return 0;
+               }
 
-       if (skip_prefix(arg, "sparse:oid=", &v0)) {
+       } else if (skip_prefix(arg, "sparse:oid=", &v0)) {
                struct object_context oc;
                struct object_id sparse_oid;
 
@@ -57,15 +64,27 @@ int parse_list_objects_filter(struct list_objects_filter_options *filter_options
                        filter_options->sparse_oid_value = oiddup(&sparse_oid);
                filter_options->choice = LOFC_SPARSE_OID;
                return 0;
-       }
 
-       if (skip_prefix(arg, "sparse:path=", &v0)) {
+       } else if (skip_prefix(arg, "sparse:path=", &v0)) {
                filter_options->choice = LOFC_SPARSE_PATH;
                filter_options->sparse_path_value = strdup(v0);
                return 0;
        }
 
-       die(_("invalid filter-spec expression '%s'"), arg);
+       if (errbuf) {
+               strbuf_init(errbuf, 0);
+               strbuf_addf(errbuf, "invalid filter-spec '%s'", arg);
+       }
+       memset(filter_options, 0, sizeof(*filter_options));
+       return 1;
+}
+
+int parse_list_objects_filter(struct list_objects_filter_options *filter_options,
+                             const char *arg)
+{
+       struct strbuf buf = STRBUF_INIT;
+       if (gently_parse_list_objects_filter(filter_options, arg, &buf))
+               die("%s", buf.buf);
        return 0;
 }
 
@@ -75,7 +94,7 @@ int opt_parse_list_objects_filter(const struct option *opt,
        struct list_objects_filter_options *filter_options = opt->value;
 
        if (unset || !arg) {
-               list_objects_filter_release(filter_options);
+               list_objects_filter_set_no_filter(filter_options);
                return 0;
        }
 
@@ -90,3 +109,44 @@ void list_objects_filter_release(
        free(filter_options->sparse_path_value);
        memset(filter_options, 0, sizeof(*filter_options));
 }
+
+void partial_clone_register(
+       const char *remote,
+       const struct list_objects_filter_options *filter_options)
+{
+       /*
+        * Record the name of the partial clone remote in the
+        * config and in the global variable -- the latter is
+        * used throughout to indicate that partial clone is
+        * enabled and to expect missing objects.
+        */
+       if (repository_format_partial_clone &&
+           *repository_format_partial_clone &&
+           strcmp(remote, repository_format_partial_clone))
+               die(_("cannot change partial clone promisor remote"));
+
+       git_config_set("core.repositoryformatversion", "1");
+       git_config_set("extensions.partialclone", remote);
+
+       repository_format_partial_clone = xstrdup(remote);
+
+       /*
+        * Record the initial filter-spec in the config as
+        * the default for subsequent fetches from this remote.
+        */
+       core_partial_clone_filter_default =
+               xstrdup(filter_options->filter_spec);
+       git_config_set("core.partialclonefilter",
+                      core_partial_clone_filter_default);
+}
+
+void partial_clone_get_default_filter_spec(
+       struct list_objects_filter_options *filter_options)
+{
+       /*
+        * Parse default value, but silently ignore it if it is invalid.
+        */
+       gently_parse_list_objects_filter(filter_options,
+                                        core_partial_clone_filter_default,
+                                        NULL);
+}
index eea44a1..0000a61 100644 (file)
@@ -30,6 +30,11 @@ struct list_objects_filter_options {
         */
        enum list_objects_filter_choice choice;
 
+       /*
+        * Choice is LOFC_DISABLED because "--no-filter" was requested.
+        */
+       unsigned int no_filter : 1;
+
        /*
         * Parsed values (fields) from within the filter-spec.  These are
         * choice-specific; not all values will be defined for any given
@@ -58,4 +63,17 @@ int opt_parse_list_objects_filter(const struct option *opt,
 void list_objects_filter_release(
        struct list_objects_filter_options *filter_options);
 
+static inline void list_objects_filter_set_no_filter(
+       struct list_objects_filter_options *filter_options)
+{
+       list_objects_filter_release(filter_options);
+       filter_options->no_filter = 1;
+}
+
+void partial_clone_register(
+       const char *remote,
+       const struct list_objects_filter_options *filter_options);
+void partial_clone_get_default_filter_spec(
+       struct list_objects_filter_options *filter_options);
+
 #endif /* LIST_OBJECTS_FILTER_OPTIONS_H */
index 0966cdc..168bef6 100644 (file)
@@ -9,6 +9,7 @@
 #include "list-objects.h"
 #include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
+#include "packfile.h"
 
 static void process_blob(struct rev_info *revs,
                         struct blob *blob,
@@ -30,6 +31,20 @@ static void process_blob(struct rev_info *revs,
        if (obj->flags & (UNINTERESTING | SEEN))
                return;
 
+       /*
+        * Pre-filter known-missing objects when explicitly requested.
+        * Otherwise, a missing object error message may be reported
+        * later (depending on other filtering criteria).
+        *
+        * Note that this "--exclude-promisor-objects" pre-filtering
+        * may cause the actual filter to report an incomplete list
+        * of missing objects.
+        */
+       if (revs->exclude_promisor_objects &&
+           !has_object_file(&obj->oid) &&
+           is_promisor_object(&obj->oid))
+               return;
+
        pathlen = path->len;
        strbuf_addstr(path, name);
        if (filter_fn)
@@ -91,6 +106,8 @@ static void process_tree(struct rev_info *revs,
                all_entries_interesting: entry_not_interesting;
        int baselen = base->len;
        enum list_objects_filter_result r = LOFR_MARK_SEEN | LOFR_DO_SHOW;
+       int gently = revs->ignore_missing_links ||
+                    revs->exclude_promisor_objects;
 
        if (!revs->tree_objects)
                return;
@@ -98,9 +115,19 @@ static void process_tree(struct rev_info *revs,
                die("bad tree object");
        if (obj->flags & (UNINTERESTING | SEEN))
                return;
-       if (parse_tree_gently(tree, revs->ignore_missing_links) < 0) {
+       if (parse_tree_gently(tree, gently) < 0) {
                if (revs->ignore_missing_links)
                        return;
+
+               /*
+                * Pre-filter known-missing tree objects when explicitly
+                * requested.  This may cause the actual filter to report
+                * an incomplete list of missing objects.
+                */
+               if (revs->exclude_promisor_objects &&
+                   is_promisor_object(&obj->oid))
+                       return;
+
                die("bad tree object %s", oid_to_hex(&obj->oid));
        }
 
index a89db22..d04142c 100644 (file)
@@ -1167,11 +1167,13 @@ void clear_mailinfo(struct mailinfo *mi)
        strbuf_release(&mi->inbody_header_accum);
        free(mi->message_id);
 
-       for (i = 0; mi->p_hdr_data[i]; i++)
-               strbuf_release(mi->p_hdr_data[i]);
+       if (mi->p_hdr_data)
+               for (i = 0; mi->p_hdr_data[i]; i++)
+                       strbuf_release(mi->p_hdr_data[i]);
        free(mi->p_hdr_data);
-       for (i = 0; mi->s_hdr_data[i]; i++)
-               strbuf_release(mi->s_hdr_data[i]);
+       if (mi->s_hdr_data)
+               for (i = 0; mi->s_hdr_data[i]; i++)
+                       strbuf_release(mi->s_hdr_data[i]);
        free(mi->s_hdr_data);
 
        while (mi->content < mi->content_top) {
index cedb7c3..9d53f30 100644 (file)
@@ -1045,10 +1045,19 @@ static int merge_file_1(struct merge_options *o,
                                                       &b->oid,
                                                       !o->call_depth);
                } else if (S_ISLNK(a->mode)) {
-                       oidcpy(&result->oid, &a->oid);
-
-                       if (!oid_eq(&a->oid, &b->oid))
-                               result->clean = 0;
+                       switch (o->recursive_variant) {
+                       case MERGE_RECURSIVE_NORMAL:
+                               oidcpy(&result->oid, &a->oid);
+                               if (!oid_eq(&a->oid, &b->oid))
+                                       result->clean = 0;
+                               break;
+                       case MERGE_RECURSIVE_OURS:
+                               oidcpy(&result->oid, &a->oid);
+                               break;
+                       case MERGE_RECURSIVE_THEIRS:
+                               oidcpy(&result->oid, &b->oid);
+                               break;
+                       }
                } else
                        die("BUG: unsupported object type in the tree");
        }
diff --git a/mru.c b/mru.c
deleted file mode 100644 (file)
index 9dedae0..0000000
--- a/mru.c
+++ /dev/null
@@ -1,50 +0,0 @@
-#include "cache.h"
-#include "mru.h"
-
-void mru_append(struct mru *mru, void *item)
-{
-       struct mru_entry *cur = xmalloc(sizeof(*cur));
-       cur->item = item;
-       cur->prev = mru->tail;
-       cur->next = NULL;
-
-       if (mru->tail)
-               mru->tail->next = cur;
-       else
-               mru->head = cur;
-       mru->tail = cur;
-}
-
-void mru_mark(struct mru *mru, struct mru_entry *entry)
-{
-       /* If we're already at the front of the list, nothing to do */
-       if (mru->head == entry)
-               return;
-
-       /* Otherwise, remove us from our current slot... */
-       if (entry->prev)
-               entry->prev->next = entry->next;
-       if (entry->next)
-               entry->next->prev = entry->prev;
-       else
-               mru->tail = entry->prev;
-
-       /* And insert us at the beginning. */
-       entry->prev = NULL;
-       entry->next = mru->head;
-       if (mru->head)
-               mru->head->prev = entry;
-       mru->head = entry;
-}
-
-void mru_clear(struct mru *mru)
-{
-       struct mru_entry *p = mru->head;
-
-       while (p) {
-               struct mru_entry *to_free = p;
-               p = p->next;
-               free(to_free);
-       }
-       mru->head = mru->tail = NULL;
-}
diff --git a/mru.h b/mru.h
deleted file mode 100644 (file)
index 42e4aea..0000000
--- a/mru.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef MRU_H
-#define MRU_H
-
-/**
- * A simple most-recently-used cache, backed by a doubly-linked list.
- *
- * Usage is roughly:
- *
- *   // Create a list.  Zero-initialization is required.
- *   static struct mru cache;
- *   mru_append(&cache, item);
- *   ...
- *
- *   // Iterate in MRU order.
- *   struct mru_entry *p;
- *   for (p = cache.head; p; p = p->next) {
- *     if (matches(p->item))
- *             break;
- *   }
- *
- *   // Mark an item as used, moving it to the front of the list.
- *   mru_mark(&cache, p);
- *
- *   // Reset the list to empty, cleaning up all resources.
- *   mru_clear(&cache);
- *
- * Note that you SHOULD NOT call mru_mark() and then continue traversing the
- * list; it reorders the marked item to the front of the list, and therefore
- * you will begin traversing the whole list again.
- */
-
-struct mru_entry {
-       void *item;
-       struct mru_entry *prev, *next;
-};
-
-struct mru {
-       struct mru_entry *head, *tail;
-};
-
-void mru_append(struct mru *mru, void *item);
-void mru_mark(struct mru *mru, struct mru_entry *entry);
-void mru_clear(struct mru *mru);
-
-#endif /* MRU_H */
index b9a4a0e..9e6f9ff 100644 (file)
--- a/object.c
+++ b/object.c
@@ -252,7 +252,7 @@ struct object *parse_object(const struct object_id *oid)
        if (obj && obj->parsed)
                return obj;
 
-       if ((obj && obj->type == OBJ_BLOB) ||
+       if ((obj && obj->type == OBJ_BLOB && has_object_file(oid)) ||
            (!obj && has_object_file(oid) &&
             sha1_object_info(oid->hash, NULL) == OBJ_BLOB)) {
                if (check_sha1_signature(repl, NULL, 0, NULL) < 0) {
@@ -434,3 +434,14 @@ void clear_object_flags(unsigned flags)
                        obj->flags &= ~flags;
        }
 }
+
+void clear_commit_marks_all(unsigned int flags)
+{
+       int i;
+
+       for (i = 0; i < obj_hash_size; i++) {
+               struct object *obj = obj_hash[i];
+               if (obj && obj->type == OBJ_COMMIT)
+                       obj->flags &= ~flags;
+       }
+}
index f34461d..87563d9 100644 (file)
--- a/object.h
+++ b/object.h
@@ -149,4 +149,9 @@ void object_array_clear(struct object_array *array);
 
 void clear_object_flags(unsigned flags);
 
+/*
+ * Clear the specified object flags from all in-core commit objects.
+ */
+extern void clear_commit_marks_all(unsigned int flags);
+
 #endif /* OBJECT_H */
index 6db4fff..d9fb19b 100644 (file)
--- a/oidmap.c
+++ b/oidmap.c
@@ -33,12 +33,19 @@ void oidmap_free(struct oidmap *map, int free_entries)
 
 void *oidmap_get(const struct oidmap *map, const struct object_id *key)
 {
+       if (!map->map.cmpfn)
+               return NULL;
+
        return hashmap_get_from_hash(&map->map, hash(key), key);
 }
 
 void *oidmap_remove(struct oidmap *map, const struct object_id *key)
 {
        struct hashmap_entry entry;
+
+       if (!map->map.cmpfn)
+               oidmap_init(map, 0);
+
        hashmap_entry_init(&entry, hash(key));
        return hashmap_remove(&map->map, &entry, key);
 }
@@ -46,6 +53,10 @@ void *oidmap_remove(struct oidmap *map, const struct object_id *key)
 void *oidmap_put(struct oidmap *map, void *entry)
 {
        struct oidmap_entry *to_put = entry;
+
+       if (!map->map.cmpfn)
+               oidmap_init(map, 0);
+
        hashmap_entry_init(&to_put->internal_entry, hash(&to_put->oid));
        return hashmap_put(&map->map, to_put);
 }
index 1b7ebd8..ff5f62c 100644 (file)
@@ -134,10 +134,8 @@ static void create_pack_revindex(struct packed_git *p)
                        if (!(off & 0x80000000)) {
                                p->revindex[i].offset = off;
                        } else {
-                               p->revindex[i].offset =
-                                       ((uint64_t)ntohl(*off_64++)) << 32;
-                               p->revindex[i].offset |=
-                                       ntohl(*off_64++);
+                               p->revindex[i].offset = get_be64(off_64);
+                               off_64 += 2;
                        }
                        p->revindex[i].nr = i;
                }
index 4a5fe7a..7dbe873 100644 (file)
@@ -1,5 +1,5 @@
 #include "cache.h"
-#include "mru.h"
+#include "list.h"
 #include "pack.h"
 #include "dir.h"
 #include "mergesort.h"
@@ -8,6 +8,11 @@
 #include "list.h"
 #include "streaming.h"
 #include "sha1-lookup.h"
+#include "commit.h"
+#include "object.h"
+#include "tag.h"
+#include "tree-walk.h"
+#include "tree.h"
 
 char *odb_pack_name(struct strbuf *buf,
                    const unsigned char *sha1,
@@ -40,7 +45,7 @@ static unsigned int pack_max_fds;
 static size_t peak_pack_mapped;
 static size_t pack_mapped;
 struct packed_git *packed_git;
-struct mru packed_git_mru;
+LIST_HEAD(packed_git_mru);
 
 #define SZ_FMT PRIuMAX
 static inline uintmax_t sz_fmt(size_t s) { return s; }
@@ -643,10 +648,10 @@ struct packed_git *add_packed_git(const char *path, size_t path_len, int local)
                return NULL;
 
        /*
-        * ".pack" is long enough to hold any suffix we're adding (and
+        * ".promisor" is long enough to hold any suffix we're adding (and
         * the use xsnprintf double-checks that)
         */
-       alloc = st_add3(path_len, strlen(".pack"), 1);
+       alloc = st_add3(path_len, strlen(".promisor"), 1);
        p = alloc_packed_git(alloc);
        memcpy(p->pack_name, path, path_len);
 
@@ -654,6 +659,10 @@ struct packed_git *add_packed_git(const char *path, size_t path_len, int local)
        if (!access(p->pack_name, F_OK))
                p->pack_keep = 1;
 
+       xsnprintf(p->pack_name + path_len, alloc - path_len, ".promisor");
+       if (!access(p->pack_name, F_OK))
+               p->pack_promisor = 1;
+
        xsnprintf(p->pack_name + path_len, alloc - path_len, ".pack");
        if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode)) {
                free(p);
@@ -781,7 +790,8 @@ static void prepare_packed_git_one(char *objdir, int local)
                if (ends_with(de->d_name, ".idx") ||
                    ends_with(de->d_name, ".pack") ||
                    ends_with(de->d_name, ".bitmap") ||
-                   ends_with(de->d_name, ".keep"))
+                   ends_with(de->d_name, ".keep") ||
+                   ends_with(de->d_name, ".promisor"))
                        string_list_append(&garbage, path.buf);
                else
                        report_garbage(PACKDIR_FILE_GARBAGE, path.buf);
@@ -866,9 +876,10 @@ static void prepare_packed_git_mru(void)
 {
        struct packed_git *p;
 
-       mru_clear(&packed_git_mru);
+       INIT_LIST_HEAD(&packed_git_mru);
+
        for (p = packed_git; p; p = p->next)
-               mru_append(&packed_git_mru, p);
+               list_add_tail(&p->mru, &packed_git_mru);
 }
 
 static int prepare_packed_git_run_once = 0;
@@ -1702,8 +1713,7 @@ off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n)
                        return off;
                index += p->num_objects * 4 + (off & 0x7fffffff) * 8;
                check_pack_index_ptr(p, index);
-               return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) |
-                                  ntohl(*((uint32_t *)(index + 4)));
+               return get_be64(index);
        }
 }
 
@@ -1831,15 +1841,16 @@ static int fill_pack_entry(const unsigned char *sha1,
  */
 int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
 {
-       struct mru_entry *p;
+       struct list_head *pos;
 
        prepare_packed_git();
        if (!packed_git)
                return 0;
 
-       for (p = packed_git_mru.head; p; p = p->next) {
-               if (fill_pack_entry(sha1, e, p->item)) {
-                       mru_mark(&packed_git_mru, p);
+       list_for_each(pos, &packed_git_mru) {
+               struct packed_git *p = list_entry(pos, struct packed_git, mru);
+               if (fill_pack_entry(sha1, e, p)) {
+                       list_move(&p->mru, &packed_git_mru);
                        return 1;
                }
        }
@@ -1889,6 +1900,9 @@ int for_each_packed_object(each_packed_object_fn cb, void *data, unsigned flags)
        for (p = packed_git; p; p = p->next) {
                if ((flags & FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
                        continue;
+               if ((flags & FOR_EACH_OBJECT_PROMISOR_ONLY) &&
+                   !p->pack_promisor)
+                       continue;
                if (open_pack_index(p)) {
                        pack_errors = 1;
                        continue;
@@ -1899,3 +1913,61 @@ int for_each_packed_object(each_packed_object_fn cb, void *data, unsigned flags)
        }
        return r ? r : pack_errors;
 }
+
+static int add_promisor_object(const struct object_id *oid,
+                              struct packed_git *pack,
+                              uint32_t pos,
+                              void *set_)
+{
+       struct oidset *set = set_;
+       struct object *obj = parse_object(oid);
+       if (!obj)
+               return 1;
+
+       oidset_insert(set, oid);
+
+       /*
+        * If this is a tree, commit, or tag, the objects it refers
+        * to are also promisor objects. (Blobs refer to no objects.)
+        */
+       if (obj->type == OBJ_TREE) {
+               struct tree *tree = (struct tree *)obj;
+               struct tree_desc desc;
+               struct name_entry entry;
+               if (init_tree_desc_gently(&desc, tree->buffer, tree->size))
+                       /*
+                        * Error messages are given when packs are
+                        * verified, so do not print any here.
+                        */
+                       return 0;
+               while (tree_entry_gently(&desc, &entry))
+                       oidset_insert(set, entry.oid);
+       } else if (obj->type == OBJ_COMMIT) {
+               struct commit *commit = (struct commit *) obj;
+               struct commit_list *parents = commit->parents;
+
+               oidset_insert(set, &commit->tree->object.oid);
+               for (; parents; parents = parents->next)
+                       oidset_insert(set, &parents->item->object.oid);
+       } else if (obj->type == OBJ_TAG) {
+               struct tag *tag = (struct tag *) obj;
+               oidset_insert(set, &tag->tagged->oid);
+       }
+       return 0;
+}
+
+int is_promisor_object(const struct object_id *oid)
+{
+       static struct oidset promisor_objects;
+       static int promisor_objects_prepared;
+
+       if (!promisor_objects_prepared) {
+               if (repository_format_partial_clone) {
+                       for_each_packed_object(add_promisor_object,
+                                              &promisor_objects,
+                                              FOR_EACH_OBJECT_PROMISOR_ONLY);
+               }
+               promisor_objects_prepared = 1;
+       }
+       return oidset_contains(&promisor_objects, oid);
+}
index 0cdeb54..a7fca59 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef PACKFILE_H
 #define PACKFILE_H
 
+#include "oidset.h"
+
 /*
  * Generate the filename to be used for a pack file with checksum "sha1" and
  * extension "ext". The result is written into the strbuf "buf", overwriting
@@ -124,6 +126,11 @@ extern int has_sha1_pack(const unsigned char *sha1);
 
 extern int has_pack_index(const unsigned char *sha1);
 
+/*
+ * Only iterate over packs obtained from the promisor remote.
+ */
+#define FOR_EACH_OBJECT_PROMISOR_ONLY 2
+
 /*
  * Iterate over packed objects in both the local
  * repository and any alternates repositories (unless the
@@ -135,4 +142,10 @@ typedef int each_packed_object_fn(const struct object_id *oid,
                                  void *data);
 extern int for_each_packed_object(each_packed_object_fn, void *, unsigned flags);
 
+/*
+ * Return 1 if an object in a promisor packfile is or refers to the given
+ * object, 0 otherwise.
+ */
+extern int is_promisor_object(const struct object_id *oid);
+
 #endif
index fca7159..d02eb8b 100644 (file)
@@ -525,7 +525,7 @@ unknown:
 
 int parse_options_end(struct parse_opt_ctx_t *ctx)
 {
-       memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out));
+       MOVE_ARRAY(ctx->out + ctx->cpidx, ctx->argv, ctx->argc);
        ctx->out[ctx->cpidx + ctx->argc] = NULL;
        return ctx->cpidx + ctx->argc;
 }
index 0f1fc27..84c048a 100644 (file)
@@ -1,8 +1 @@
-perl.mak
-perl.mak.old
-MYMETA.json
-MYMETA.yml
-blib
-blibdirs
-pm_to_blib
-PM.stamp
+/build/
index ffa09ac..9d60d79 100644 (file)
@@ -101,7 +101,7 @@ increase notwithstanding).
 
 
 use Carp qw(carp croak); # but croak is bad - throw instead
-use Error qw(:try);
+use Git::Error qw(:try);
 use Cwd qw(abs_path cwd);
 use IPC::Open2 qw(open2);
 use Fcntl qw(SEEK_SET SEEK_CUR);
@@ -880,77 +880,6 @@ sub ident_person {
        return "$ident[0] <$ident[1]>";
 }
 
-=item parse_mailboxes
-
-Return an array of mailboxes extracted from a string.
-
-=cut
-
-# Very close to Mail::Address's parser, but we still have minor
-# differences in some cases (see t9000 for examples).
-sub parse_mailboxes {
-       my $re_comment = qr/\((?:[^)]*)\)/;
-       my $re_quote = qr/"(?:[^\"\\]|\\.)*"/;
-       my $re_word = qr/(?:[^]["\s()<>:;@\\,.]|\\.)+/;
-
-       # divide the string in tokens of the above form
-       my $re_token = qr/(?:$re_quote|$re_word|$re_comment|\S)/;
-       my @tokens = map { $_ =~ /\s*($re_token)\s*/g } @_;
-       my $end_of_addr_seen = 0;
-
-       # add a delimiter to simplify treatment for the last mailbox
-       push @tokens, ",";
-
-       my (@addr_list, @phrase, @address, @comment, @buffer) = ();
-       foreach my $token (@tokens) {
-               if ($token =~ /^[,;]$/) {
-                       # if buffer still contains undeterminated strings
-                       # append it at the end of @address or @phrase
-                       if ($end_of_addr_seen) {
-                               push @phrase, @buffer;
-                       } else {
-                               push @address, @buffer;
-                       }
-
-                       my $str_phrase = join ' ', @phrase;
-                       my $str_address = join '', @address;
-                       my $str_comment = join ' ', @comment;
-
-                       # quote are necessary if phrase contains
-                       # special characters
-                       if ($str_phrase =~ /[][()<>:;@\\,.\000-\037\177]/) {
-                               $str_phrase =~ s/(^|[^\\])"/$1/g;
-                               $str_phrase = qq["$str_phrase"];
-                       }
-
-                       # add "<>" around the address if necessary
-                       if ($str_address ne "" && $str_phrase ne "") {
-                               $str_address = qq[<$str_address>];
-                       }
-
-                       my $str_mailbox = "$str_phrase $str_address $str_comment";
-                       $str_mailbox =~ s/^\s*|\s*$//g;
-                       push @addr_list, $str_mailbox if ($str_mailbox);
-
-                       @phrase = @address = @comment = @buffer = ();
-                       $end_of_addr_seen = 0;
-               } elsif ($token =~ /^\(/) {
-                       push @comment, $token;
-               } elsif ($token eq "<") {
-                       push @phrase, (splice @address), (splice @buffer);
-               } elsif ($token eq ">") {
-                       $end_of_addr_seen = 1;
-                       push @address, (splice @buffer);
-               } elsif ($token eq "@" && !$end_of_addr_seen) {
-                       push @address, (splice @buffer), "@";
-               } else {
-                       push @buffer, $token;
-               }
-       }
-
-       return @addr_list;
-}
-
 =item hash_object ( TYPE, FILENAME )
 
 Compute the SHA1 object id of the given C<FILENAME> considering it is
diff --git a/perl/Git/Error.pm b/perl/Git/Error.pm
new file mode 100644 (file)
index 0000000..09bbc97
--- /dev/null
@@ -0,0 +1,46 @@
+package Git::Error;
+use 5.008;
+use strict;
+use warnings;
+
+=head1 NAME
+
+Git::Error - Wrapper for the L<Error> module, in case it's not installed
+
+=head1 DESCRIPTION
+
+Wraps the import function for the L<Error> module.
+
+This module is only intended to be used for code shipping in the
+C<git.git> repository. Use it for anything else at your peril!
+
+=cut
+
+sub import {
+    shift;
+    my $caller = caller;
+
+    eval {
+       require Error;
+       1;
+    } or do {
+       my $error = $@ || "Zombie Error";
+
+       my $Git_Error_pm_path = $INC{"Git/Error.pm"} || die "BUG: Should have our own path from %INC!";
+
+       require File::Basename;
+       my $Git_Error_pm_root = File::Basename::dirname($Git_Error_pm_path) || die "BUG: Can't figure out lib/Git dirname from '$Git_Error_pm_path'!";
+
+       require File::Spec;
+       my $Git_pm_FromCPAN_root = File::Spec->catdir($Git_Error_pm_root, 'FromCPAN');
+       die "BUG: '$Git_pm_FromCPAN_root' should be a directory!" unless -d $Git_pm_FromCPAN_root;
+
+       local @INC = ($Git_pm_FromCPAN_root, @INC);
+       require Error;
+    };
+
+    unshift @_, $caller;
+    goto &Error::import;
+}
+
+1;
diff --git a/perl/Git/FromCPAN/Mail/Address.pm b/perl/Git/FromCPAN/Mail/Address.pm
new file mode 100644 (file)
index 0000000..13b2ff7
--- /dev/null
@@ -0,0 +1,276 @@
+# Copyrights 1995-2017 by [Mark Overmeer <perl@overmeer.net>].
+#  For other contributors see ChangeLog.
+# See the manual pages for details on the licensing terms.
+# Pod stripped from pm file by OODoc 2.02.
+package Mail::Address;
+use vars '$VERSION';
+$VERSION = '2.19';
+
+use strict;
+
+use Carp;
+
+# use locale;   removed in version 1.78, because it causes taint problems
+
+sub Version { our $VERSION }
+
+
+
+# given a comment, attempt to extract a person's name
+sub _extract_name
+{   # This function can be called as method as well
+    my $self = @_ && ref $_[0] ? shift : undef;
+
+    local $_ = shift
+        or return '';
+
+    # Using encodings, too hard. See Mail::Message::Field::Full.
+    return '' if m/\=\?.*?\?\=/;
+
+    # trim whitespace
+    s/^\s+//;
+    s/\s+$//;
+    s/\s+/ /;
+
+    # Disregard numeric names (e.g. 123456.1234@compuserve.com)
+    return "" if /^[\d ]+$/;
+
+    s/^\((.*)\)$/$1/; # remove outermost parenthesis
+    s/^"(.*)"$/$1/;   # remove outer quotation marks
+    s/\(.*?\)//g;     # remove minimal embedded comments
+    s/\\//g;          # remove all escapes
+    s/^"(.*)"$/$1/;   # remove internal quotation marks
+    s/^([^\s]+) ?, ?(.*)$/$2 $1/; # reverse "Last, First M." if applicable
+    s/,.*//;
+
+    # Change casing only when the name contains only upper or only
+    # lower cased characters.
+    unless( m/[A-Z]/ && m/[a-z]/ )
+    {   # Set the case of the name to first char upper rest lower
+        s/\b(\w+)/\L\u$1/igo;  # Upcase first letter on name
+        s/\bMc(\w)/Mc\u$1/igo; # Scottish names such as 'McLeod'
+        s/\bo'(\w)/O'\u$1/igo; # Irish names such as 'O'Malley, O'Reilly'
+        s/\b(x*(ix)?v*(iv)?i*)\b/\U$1/igo; # Roman numerals, eg 'Level III Support'
+    }
+
+    # some cleanup
+    s/\[[^\]]*\]//g;
+    s/(^[\s'"]+|[\s'"]+$)//g;
+    s/\s{2,}/ /g;
+
+    $_;
+}
+
+sub _tokenise
+{   local $_ = join ',', @_;
+    my (@words,$snippet,$field);
+
+    s/\A\s+//;
+    s/[\r\n]+/ /g;
+
+    while ($_ ne '')
+    {   $field = '';
+        if(s/^\s*\(/(/ )    # (...)
+        {   my $depth = 0;
+
+     PAREN: while(s/^(\(([^\(\)\\]|\\.)*)//)
+            {   $field .= $1;
+                $depth++;
+                while(s/^(([^\(\)\\]|\\.)*\)\s*)//)
+                {   $field .= $1;
+                    last PAREN unless --$depth;
+                   $field .= $1 if s/^(([^\(\)\\]|\\.)+)//;
+                }
+            }
+
+            carp "Unmatched () '$field' '$_'"
+                if $depth;
+
+            $field =~ s/\s+\Z//;
+            push @words, $field;
+
+            next;
+        }
+
+        if( s/^("(?:[^"\\]+|\\.)*")\s*//       # "..."
+         || s/^(\[(?:[^\]\\]+|\\.)*\])\s*//    # [...]
+         || s/^([^\s()<>\@,;:\\".[\]]+)\s*//
+         || s/^([()<>\@,;:\\".[\]])\s*//
+          )
+        {   push @words, $1;
+            next;
+        }
+
+        croak "Unrecognised line: $_";
+    }
+
+    push @words, ",";
+    \@words;
+}
+
+sub _find_next
+{   my ($idx, $tokens, $len) = @_;
+
+    while($idx < $len)
+    {   my $c = $tokens->[$idx];
+        return $c if $c eq ',' || $c eq ';' || $c eq '<';
+        $idx++;
+    }
+
+    "";
+}
+
+sub _complete
+{   my ($class, $phrase, $address, $comment) = @_;
+
+    @$phrase || @$comment || @$address
+       or return undef;
+
+    my $o = $class->new(join(" ",@$phrase), join("",@$address), join(" ",@$comment));
+    @$phrase = @$address = @$comment = ();
+    $o;
+}
+
+#------------
+
+sub new(@)
+{   my $class = shift;
+    bless [@_], $class;
+}
+
+
+sub parse(@)
+{   my $class = shift;
+    my @line  = grep {defined} @_;
+    my $line  = join '', @line;
+
+    my (@phrase, @comment, @address, @objs);
+    my ($depth, $idx) = (0, 0);
+
+    my $tokens  = _tokenise @line;
+    my $len     = @$tokens;
+    my $next    = _find_next $idx, $tokens, $len;
+
+    local $_;
+    for(my $idx = 0; $idx < $len; $idx++)
+    {   $_ = $tokens->[$idx];
+
+        if(substr($_,0,1) eq '(') { push @comment, $_ }
+        elsif($_ eq '<')    { $depth++ }
+        elsif($_ eq '>')    { $depth-- if $depth }
+        elsif($_ eq ',' || $_ eq ';')
+        {   warn "Unmatched '<>' in $line" if $depth;
+            my $o = $class->_complete(\@phrase, \@address, \@comment);
+            push @objs, $o if defined $o;
+            $depth = 0;
+            $next = _find_next $idx+1, $tokens, $len;
+        }
+        elsif($depth)       { push @address, $_ }
+        elsif($next eq '<') { push @phrase,  $_ }
+        elsif( /^[.\@:;]$/ || !@address || $address[-1] =~ /^[.\@:;]$/ )
+        {   push @address, $_ }
+        else
+        {   warn "Unmatched '<>' in $line" if $depth;
+            my $o = $class->_complete(\@phrase, \@address, \@comment);
+            push @objs, $o if defined $o;
+            $depth = 0;
+            push @address, $_;
+        }
+    }
+    @objs;
+}
+
+#------------
+
+sub phrase  { shift->set_or_get(0, @_) }
+sub address { shift->set_or_get(1, @_) }
+sub comment { shift->set_or_get(2, @_) }
+
+sub set_or_get($)
+{   my ($self, $i) = (shift, shift);
+    @_ or return $self->[$i];
+
+    my $val = $self->[$i];
+    $self->[$i] = shift if @_;
+    $val;
+}
+
+
+my $atext = '[\-\w !#$%&\'*+/=?^`{|}~]';
+sub format
+{   my @addrs;
+
+    foreach (@_)
+    {   my ($phrase, $email, $comment) = @$_;
+        my @addr;
+
+        if(defined $phrase && length $phrase)
+        {   push @addr
+              , $phrase =~ /^(?:\s*$atext\s*)+$/o ? $phrase
+              : $phrase =~ /(?<!\\)"/             ? $phrase
+              :                                    qq("$phrase");
+
+            push @addr, "<$email>"
+                if defined $email && length $email;
+        }
+        elsif(defined $email && length $email)
+        {   push @addr, $email;
+        }
+
+        if(defined $comment && $comment =~ /\S/)
+        {   $comment =~ s/^\s*\(?/(/;
+            $comment =~ s/\)?\s*$/)/;
+        }
+
+        push @addr, $comment
+            if defined $comment && length $comment;
+
+        push @addrs, join(" ", @addr)
+            if @addr;
+    }
+
+    join ", ", @addrs;
+}
+
+#------------
+
+sub name
+{   my $self   = shift;
+    my $phrase = $self->phrase;
+    my $addr   = $self->address;
+
+    $phrase    = $self->comment
+        unless defined $phrase && length $phrase;
+
+    my $name   = $self->_extract_name($phrase);
+
+    # first.last@domain address
+    if($name eq '' && $addr =~ /([^\%\.\@_]+([\._][^\%\.\@_]+)+)[\@\%]/)
+    {   ($name  = $1) =~ s/[\._]+/ /g;
+       $name   = _extract_name $name;
+    }
+
+    if($name eq '' && $addr =~ m#/g=#i)    # X400 style address
+    {   my ($f) = $addr =~ m#g=([^/]*)#i;
+       my ($l) = $addr =~ m#s=([^/]*)#i;
+       $name   = _extract_name "$f $l";
+    }
+
+    length $name ? $name : undef;
+}
+
+
+sub host
+{   my $addr = shift->address || '';
+    my $i    = rindex $addr, '@';
+    $i >= 0 ? substr($addr, $i+1) : undef;
+}
+
+
+sub user
+{   my $addr = shift->address || '';
+    my $i    = rindex $addr, '@';
+    $i >= 0 ? substr($addr,0,$i) : $addr;
+}
+
+1;
index 836a5c2..dba96ff 100644 (file)
@@ -18,7 +18,7 @@ our @EXPORT_OK = @EXPORT;
 
 sub __bootstrap_locale_messages {
        our $TEXTDOMAIN = 'git';
-       our $TEXTDOMAINDIR = $ENV{GIT_TEXTDOMAINDIR} || '++LOCALEDIR++';
+       our $TEXTDOMAINDIR = $ENV{GIT_TEXTDOMAINDIR} || '@@LOCALEDIR@@';
 
        require POSIX;
        POSIX->import(qw(setlocale));
diff --git a/perl/Git/Mail/Address.pm b/perl/Git/Mail/Address.pm
new file mode 100755 (executable)
index 0000000..2ce3e84
--- /dev/null
@@ -0,0 +1,24 @@
+package Git::Mail::Address;
+use 5.008;
+use strict;
+use warnings;
+
+=head1 NAME
+
+Git::Mail::Address - Wrapper for the L<Mail::Address> module, in case it's not installed
+
+=head1 DESCRIPTION
+
+This module is only intended to be used for code shipping in the
+C<git.git> repository. Use it for anything else at your peril!
+
+=cut
+
+eval {
+    require Mail::Address;
+    1;
+} or do {
+    require Git::FromCPAN::Mail::Address;
+};
+
+1;
diff --git a/perl/Makefile b/perl/Makefile
deleted file mode 100644 (file)
index f657de2..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-#
-# Makefile for perl support modules and routine
-#
-makfile:=perl.mak
-modules =
-
-PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
-prefix_SQ = $(subst ','\'',$(prefix))
-localedir_SQ = $(subst ','\'',$(localedir))
-
-ifndef V
-       QUIET = @
-endif
-
-all install instlibdir: $(makfile)
-       $(QUIET)$(MAKE) -f $(makfile) $@
-
-clean:
-       $(QUIET)test -f $(makfile) && $(MAKE) -f $(makfile) $@ || exit 0
-       $(RM) ppport.h
-       $(RM) $(makfile)
-       $(RM) $(makfile).old
-       $(RM) PM.stamp
-
-$(makfile): PM.stamp
-
-ifdef NO_PERL_MAKEMAKER
-instdir_SQ = $(subst ','\'',$(prefix)/lib)
-
-modules += Git
-modules += Git/I18N
-modules += Git/IndexInfo
-modules += Git/Packet
-modules += Git/SVN
-modules += Git/SVN/Memoize/YAML
-modules += Git/SVN/Fetcher
-modules += Git/SVN/Editor
-modules += Git/SVN/GlobSpec
-modules += Git/SVN/Log
-modules += Git/SVN/Migration
-modules += Git/SVN/Prompt
-modules += Git/SVN/Ra
-modules += Git/SVN/Utils
-
-$(makfile): ../GIT-CFLAGS Makefile
-       echo all: private-Error.pm Git.pm Git/I18N.pm > $@
-       set -e; \
-       for i in $(modules); \
-       do \
-               if test $$i = $${i%/*}; \
-               then \
-                       subdir=; \
-               else \
-                       subdir=/$${i%/*}; \
-               fi; \
-               echo '  $(RM) blib/lib/'$$i'.pm' >> $@; \
-               echo '  mkdir -p blib/lib'$$subdir >> $@; \
-               echo '  cp '$$i'.pm blib/lib/'$$i'.pm' >> $@; \
-       done
-       echo '  $(RM) blib/lib/Error.pm' >> $@
-       '$(PERL_PATH_SQ)' -MError -e 'exit($$Error::VERSION < 0.15009)' || \
-       echo '  cp private-Error.pm blib/lib/Error.pm' >> $@
-       echo install: >> $@
-       set -e; \
-       for i in $(modules); \
-       do \
-               if test $$i = $${i%/*}; \
-               then \
-                       subdir=; \
-               else \
-                       subdir=/$${i%/*}; \
-               fi; \
-               echo '  $(RM) "$$(DESTDIR)$(instdir_SQ)/'$$i'.pm"' >> $@; \
-               echo '  mkdir -p "$$(DESTDIR)$(instdir_SQ)'$$subdir'"' >> $@; \
-               echo '  cp '$$i'.pm "$$(DESTDIR)$(instdir_SQ)/'$$i'.pm"' >> $@; \
-       done
-       echo '  $(RM) "$$(DESTDIR)$(instdir_SQ)/Error.pm"' >> $@
-       '$(PERL_PATH_SQ)' -MError -e 'exit($$Error::VERSION < 0.15009)' || \
-       echo '  cp private-Error.pm "$$(DESTDIR)$(instdir_SQ)/Error.pm"' >> $@
-       echo instlibdir: >> $@
-       echo '  echo $(instdir_SQ)' >> $@
-else
-$(makfile): Makefile.PL ../GIT-CFLAGS
-       $(PERL_PATH) $< PREFIX='$(prefix_SQ)' INSTALL_BASE='' --localedir='$(localedir_SQ)'
-endif
-
-# this is just added comfort for calling make directly in perl dir
-# (even though GIT-CFLAGS aren't used yet. If ever)
-../GIT-CFLAGS:
-       $(MAKE) -C .. GIT-CFLAGS
diff --git a/perl/Makefile.PL b/perl/Makefile.PL
deleted file mode 100644 (file)
index 3f29ba9..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-use strict;
-use warnings;
-use ExtUtils::MakeMaker;
-use Getopt::Long;
-use File::Find;
-
-# Don't forget to update the perl/Makefile, too.
-# Don't forget to test with NO_PERL_MAKEMAKER=YesPlease
-
-# Sanity: die at first unknown option
-Getopt::Long::Configure qw/ pass_through /;
-
-my $localedir = '';
-GetOptions("localedir=s" => \$localedir);
-
-sub MY::postamble {
-       return <<'MAKE_FRAG';
-instlibdir:
-       @echo '$(INSTALLSITELIB)'
-
-ifneq (,$(DESTDIR))
-ifeq (0,$(shell expr '$(MM_VERSION)' '>' 6.10))
-$(error ExtUtils::MakeMaker version "$(MM_VERSION)" is older than 6.11 and so \
-       is likely incompatible with the DESTDIR mechanism.  Try setting \
-       NO_PERL_MAKEMAKER=1 instead)
-endif
-endif
-
-MAKE_FRAG
-}
-
-# Find all the .pm files in "Git/" and Git.pm
-my %pm