Merge branch 'jc/how-to-document-api' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 21 Nov 2018 13:58:03 +0000 (22:58 +0900)
committerJunio C Hamano <gitster@pobox.com>
Wed, 21 Nov 2018 13:58:03 +0000 (22:58 +0900)
Doc update.

* jc/how-to-document-api:
CodingGuidelines: document the API in *.h files

136 files changed:
.gitattributes
.mailmap
.travis.yml
Documentation/Makefile
Documentation/RelNotes/2.14.5.txt [new file with mode: 0644]
Documentation/RelNotes/2.15.3.txt [new file with mode: 0644]
Documentation/RelNotes/2.16.5.txt [new file with mode: 0644]
Documentation/RelNotes/2.17.2.txt [new file with mode: 0644]
Documentation/RelNotes/2.18.1.txt [new file with mode: 0644]
Documentation/RelNotes/2.19.1.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/git-archimport.txt
Documentation/git-column.txt
Documentation/git-commit-graph.txt
Documentation/git-describe.txt
Documentation/git-interpret-trailers.txt
Documentation/git-rebase.txt
Documentation/git-update-ref.txt
Documentation/git.txt
Documentation/gitcredentials.txt
Documentation/rev-list-options.txt
Documentation/technical/api-history-graph.txt
Documentation/technical/commit-graph.txt
GIT-VERSION-GEN
Makefile
RelNotes
archive.c
attr.c
attr.h
builtin/add.c
builtin/cat-file.c
builtin/check-attr.c
builtin/commit-graph.c
builtin/commit.c
builtin/interpret-trailers.c
builtin/pack-objects.c
builtin/prune.c
builtin/receive-pack.c
builtin/remote.c
builtin/repack.c
builtin/replace.c
builtin/rev-list.c
builtin/submodule--helper.c
builtin/update-ref.c
cache.h
commit-graph.c
commit-graph.h
commit.c
commit.h
compat/mingw.c
config.mak.dev
convert.c
diff.c
fetch-object.c
fetch-object.h
fetch-pack.c
fetch-pack.h
fsck.c
graph.c
linear-assignment.c
ll-merge.c
lockfile.h
merge-recursive.c
pack-objects.c
pack-objects.h
pretty.c
read-cache.c
ref-filter.c
refs.c
refs.h
refs/iterator.c
refs/refs-internal.h
remote-curl.c
replace-object.c
replace-object.h
rerere.c
revision.c
revision.h
run-command.c
sequencer.c
sequencer.h
sha1-file.c
shallow.c
split-index.c
string-list.c
string-list.h
submodule-config.c
submodule.c
t/README
t/helper/test-repository.c
t/helper/test-tool.c
t/helper/test-tool.h
t/helper/test-windows-named-pipe.c [new file with mode: 0644]
t/t0051-windows-named-pipe.sh [new file with mode: 0755]
t/t0061-run-command.sh
t/t0090-cache-tree.sh
t/t0410-partial-clone.sh
t/t1400-update-ref.sh
t/t1450-fsck.sh
t/t1500-rev-parse.sh
t/t1700-split-index.sh
t/t1701-racy-split-index.sh [new file with mode: 0755]
t/t3206-range-diff.sh
t/t3404-rebase-interactive.sh
t/t3405-rebase-malformed.sh
t/t3415-rebase-autosquash.sh
t/t3505-cherry-pick-empty.sh
t/t3701-add-interactive.sh
t/t4053-diff-no-index.sh
t/t4200-rerere.sh
t/t4202-log.sh
t/t4205-log-pretty-formats.sh
t/t4214-log-graph-octopus.sh [new file with mode: 0755]
t/t5318-commit-graph.sh
t/t5321-pack-large-objects.sh [new file with mode: 0755]
t/t5505-remote.sh
t/t5516-fetch-push.sh
t/t5537-fetch-shallow.sh
t/t5551-http-fetch-smart.sh
t/t6135-pathspec-with-attrs.sh
t/t6300-for-each-ref.sh
t/t7416-submodule-dash-url.sh [new file with mode: 0755]
t/t7417-submodule-path-url.sh [new file with mode: 0755]
t/t7500-commit.sh
t/t7501-commit.sh
t/t7513-interpret-trailers.sh
t/t8002-blame.sh
t/test-lib.sh
tempfile.c
tempfile.h
trailer.c
trailer.h
unpack-trees.c
upload-pack.c
userdiff.c
ws.c
index 1bdc91e282c5393c527b3902a208227c19971b84..49b30516419c8dfe8c039ef368a3af984439ebcc 100644 (file)
@@ -9,3 +9,7 @@
 /command-list.txt eol=lf
 /GIT-VERSION-GEN eol=lf
 /mergetools/* eol=lf
+/Documentation/git-merge.txt conflict-marker-size=32
+/Documentation/gitk.txt conflict-marker-size=32
+/Documentation/user-manual.txt conflict-marker-size=32
+/t/t????-*.sh conflict-marker-size=32
index f165222a7821e982174adc8e1c3678275ae80753..bef3352b0d3c6e84a33304b2fe44f6a68f6044a3 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -25,7 +25,7 @@ Ben Walton <bdwalton@gmail.com> <bwalton@artsci.utoronto.ca>
 Benoit Sigoure <tsunanet@gmail.com> <tsuna@lrde.epita.fr>
 Bernt Hansen <bernt@norang.ca> <bernt@alumni.uwaterloo.ca>
 Brandon Casey <drafnel@gmail.com> <casey@nrlssc.navy.mil>
-brian m. carlson <sandals@crustytoothpaste.net> Brian M. Carlson <sandals@crustytoothpaste.ath.cx>
+brian m. carlson <sandals@crustytoothpaste.net>
 brian m. carlson <sandals@crustytoothpaste.net> <sandals@crustytoothpaste.ath.cx>
 Bryan Larsen <bryan@larsen.st> <bryan.larsen@gmail.com>
 Bryan Larsen <bryan@larsen.st> <bryanlarsen@yahoo.com>
index 4d4e26c9df519c7d708e779401b5076878af022d..8d2499739eb4bbeb17065aef3fc8af54c9302d0b 100644 (file)
@@ -1,7 +1,5 @@
 language: c
 
-sudo: false
-
 cache:
   directories:
     - $HOME/travis-cache
index a42dcfc74599a29bce540a54191b84755c161e05..95f6a321f239cb2c43e9a556f5ef4219bc35ef0a 100644 (file)
@@ -344,7 +344,7 @@ $(OBSOLETE_HTML): %.html : %.txto asciidoc.conf
        mv $@+ $@
 
 manpage-base-url.xsl: manpage-base-url.xsl.in
-       sed "s|@@MAN_BASE_URL@@|$(MAN_BASE_URL)|" $< > $@
+       $(QUIET_GEN)sed "s|@@MAN_BASE_URL@@|$(MAN_BASE_URL)|" $< > $@
 
 %.1 %.5 %.7 : %.xml manpage-base-url.xsl
        $(QUIET_XMLTO)$(RM) $@ && \
diff --git a/Documentation/RelNotes/2.14.5.txt b/Documentation/RelNotes/2.14.5.txt
new file mode 100644 (file)
index 0000000..130645f
--- /dev/null
@@ -0,0 +1,16 @@
+Git v2.14.5 Release Notes
+=========================
+
+This release is to address the recently reported CVE-2018-17456.
+
+Fixes since v2.14.4
+-------------------
+
+ * Submodules' "URL"s come from the untrusted .gitmodules file, but
+   we blindly gave it to "git clone" to clone submodules when "git
+   clone --recurse-submodules" was used to clone a project that has
+   such a submodule.  The code has been hardened to reject such
+   malformed URLs (e.g. one that begins with a dash).
+
+Credit for finding and fixing this vulnerability goes to joernchen
+and Jeff King, respectively.
diff --git a/Documentation/RelNotes/2.15.3.txt b/Documentation/RelNotes/2.15.3.txt
new file mode 100644 (file)
index 0000000..fd2e6f8
--- /dev/null
@@ -0,0 +1,6 @@
+Git v2.15.3 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.14.5 to address
+the recently reported CVE-2018-17456; see the release notes for that
+version for details.
diff --git a/Documentation/RelNotes/2.16.5.txt b/Documentation/RelNotes/2.16.5.txt
new file mode 100644 (file)
index 0000000..cb8ee02
--- /dev/null
@@ -0,0 +1,6 @@
+Git v2.16.5 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.14.5 to address
+the recently reported CVE-2018-17456; see the release notes for that
+version for details.
diff --git a/Documentation/RelNotes/2.17.2.txt b/Documentation/RelNotes/2.17.2.txt
new file mode 100644 (file)
index 0000000..ef021be
--- /dev/null
@@ -0,0 +1,12 @@
+Git v2.17.2 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.14.5 to address
+the recently reported CVE-2018-17456; see the release notes for that
+version for details.
+
+In addition, this release also teaches "fsck" and the server side
+logic to reject pushes to repositories that attempt to create such a
+problematic ".gitmodules" file as tracked contents, to help hosting
+sites protect their customers by preventing malicious contents from
+spreading.
diff --git a/Documentation/RelNotes/2.18.1.txt b/Documentation/RelNotes/2.18.1.txt
new file mode 100644 (file)
index 0000000..2098cdd
--- /dev/null
@@ -0,0 +1,6 @@
+Git v2.18.1 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.14.5 and in
+v2.17.2 to address the recently reported CVE-2018-17456; see the
+release notes for those versions for details.
diff --git a/Documentation/RelNotes/2.19.1.txt b/Documentation/RelNotes/2.19.1.txt
new file mode 100644 (file)
index 0000000..da76726
--- /dev/null
@@ -0,0 +1,6 @@
+Git v2.19.1 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.14.5 and in
+v2.17.2 to address the recently reported CVE-2018-17456; see the
+release notes for those versions for details.
index eb66a119753726b0260acd456f5728351dc05ba3..09a2385280111679cba2c32b471ef91a487b14cf 100644 (file)
@@ -2828,6 +2828,8 @@ protocol.version::
 * `1` - the original wire protocol with the addition of a version string
   in the initial response from the server.
 
+* `2` - link:technical/protocol-v2.html[wire protocol version 2].
+
 --
 
 pull.ff::
@@ -3632,15 +3634,15 @@ 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 support 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
 untrusted repositories).
 
+uploadpack.allowFilter::
+       If this option is set, `upload-pack` will support partial
+       clone and partial fetch object filtering.
+
 uploadpack.allowRefInWant::
        If this option is set, `upload-pack` will support the `ref-in-want`
        feature of the protocol version 2 `fetch` command.  This feature
index ea7065336920756d03329475472016391cec83dc..a595a0ffeee56d8426907c9297078fac6977e5a3 100644 (file)
@@ -3,7 +3,7 @@ git-archimport(1)
 
 NAME
 ----
-git-archimport - Import an Arch repository into Git
+git-archimport - Import a GNU Arch repository into Git
 
 
 SYNOPSIS
@@ -14,7 +14,8 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Imports a project from one or more Arch repositories. It will follow branches
+Imports a project from one or more GNU Arch repositories.
+It will follow branches
 and repositories within the namespaces defined by the <archive/branch>
 parameters supplied. If it cannot find the remote branch a merge comes from
 it will just import it as a regular commit. If it can find it, it will mark it
index 03d18465d4f2cc8327362917d1c808e523333d11..763afabb6dc7487009ab49f5bb258f9181f0da78 100644 (file)
@@ -13,7 +13,10 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-This command formats its input into multiple columns.
+This command formats the lines of its standard input into a table with
+multiple columns. Each input line occupies one cell of the table. It
+is used internally by other git commands to format output into
+columns.
 
 OPTIONS
 -------
@@ -23,7 +26,7 @@ OPTIONS
 
 --mode=<mode>::
        Specify layout mode. See configuration variable column.ui for option
-       syntax.
+       syntax in linkgit:git-config[1].
 
 --raw-mode=<n>::
        Same as --mode but take mode encoded as a number. This is mainly used
@@ -43,6 +46,34 @@ OPTIONS
 --padding=<N>::
        The number of spaces between columns. One space by default.
 
+EXAMPLES
+------
+
+Format data by columns:
+------------
+$ seq 1 24 | git column --mode=column --padding=5
+1      4      7      10     13     16     19     22
+2      5      8      11     14     17     20     23
+3      6      9      12     15     18     21     24
+------------
+
+Format data by rows:
+------------
+$ seq 1 21 | git column --mode=row --padding=5
+1      2      3      4      5      6      7
+8      9      10     11     12     13     14
+15     16     17     18     19     20     21
+------------
+
+List some tags in a table with unequal column widths:
+------------
+$ git tag --list 'v2.4.*' --column=row,dense
+v2.4.0  v2.4.0-rc0  v2.4.0-rc1  v2.4.0-rc2  v2.4.0-rc3
+v2.4.1  v2.4.10     v2.4.11     v2.4.12     v2.4.2
+v2.4.3  v2.4.4      v2.4.5      v2.4.6      v2.4.7
+v2.4.8  v2.4.9
+------------
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index dececb79d772447e5ca0d3ea8253f20ea288ddf6..624470e198202a47e91e89274bb32b2bc3d441d6 100644 (file)
@@ -3,7 +3,7 @@ git-commit-graph(1)
 
 NAME
 ----
-git-commit-graph - Write and verify Git commit graph files
+git-commit-graph - Write and verify Git commit-graph files
 
 
 SYNOPSIS
@@ -17,24 +17,24 @@ SYNOPSIS
 DESCRIPTION
 -----------
 
-Manage the serialized commit graph file.
+Manage the serialized commit-graph file.
 
 
 OPTIONS
 -------
 --object-dir::
-       Use given directory for the location of packfiles and commit graph
+       Use given directory for the location of packfiles and commit-graph
        file. This parameter exists to specify the location of an alternate
-       that only has the objects directory, not a full .git directory. The
-       commit graph file is expected to be at <dir>/info/commit-graph and
-       the packfiles are expected to be in <dir>/pack.
+       that only has the objects directory, not a full `.git` directory. The
+       commit-graph file is expected to be at `<dir>/info/commit-graph` and
+       the packfiles are expected to be in `<dir>/pack`.
 
 
 COMMANDS
 --------
 'write'::
 
-Write a commit graph file based on the commits found in packfiles.
+Write a commit-graph file based on the commits found in packfiles.
 +
 With the `--stdin-packs` option, generate the new commit graph by
 walking objects only in the specified pack-indexes. (Cannot be combined
@@ -54,8 +54,8 @@ existing commit-graph file.
 
 'read'::
 
-Read a graph file given by the commit-graph file and output basic
-details about the graph file. Used for debugging purposes.
+Read the commit-graph file and output basic details about it.
+Used for debugging purposes.
 
 'verify'::
 
@@ -66,27 +66,28 @@ database. Used to check for corrupted data.
 EXAMPLES
 --------
 
-* Write a commit graph file for the packed commits in your local .git folder.
+* Write a commit-graph file for the packed commits in your local `.git`
+  directory.
 +
 ------------------------------------------------
 $ git commit-graph write
 ------------------------------------------------
 
-* Write a graph file, extending the current graph file using commits
-* in <pack-index>.
+* Write a commit-graph file, extending the current commit-graph file
+  using commits in `<pack-index>`.
 +
 ------------------------------------------------
 $ echo <pack-index> | git commit-graph write --stdin-packs
 ------------------------------------------------
 
-* Write a graph file containing all reachable commits.
+* Write a commit-graph file containing all reachable commits.
 +
 ------------------------------------------------
 $ git show-ref -s | git commit-graph write --stdin-commits
 ------------------------------------------------
 
-* Write a graph file containing all commits in the current
-* commit-graph file along with those reachable from HEAD.
+* Write a commit-graph file containing all commits in the current
+  commit-graph file along with those reachable from `HEAD`.
 +
 ------------------------------------------------
 $ git rev-parse HEAD | git commit-graph write --stdin-commits --append
index e027fb8c4b8269bb2266ca98f87c9c18ca45fad3..ccdc5f83d6dcd297f1e4922b27b904f00d6f4721 100644 (file)
@@ -18,7 +18,9 @@ The command finds the most recent tag that is reachable from a
 commit.  If the tag points to the commit, then only the tag is
 shown.  Otherwise, it suffixes the tag name with the number of
 additional commits on top of the tagged object and the
-abbreviated object name of the most recent commit.
+abbreviated object name of the most recent commit. The result
+is a "human-readable" object name which can also be used to
+identify the commit to other git commands.
 
 By default (without --all or --tags) `git describe` only shows
 annotated tags.  For more information about creating annotated tags
index b8fafb1e8bdd6c3e061b88b227e1ef264bbfbd07..a5e8b36f62bcf5eeedfb5a04ac852c476d9f43f3 100644 (file)
@@ -56,8 +56,9 @@ least one Git-generated or user-configured trailer and consists of at
 least 25% trailers.
 The group must be preceded by one or more empty (or whitespace-only) lines.
 The group must either be at the end of the message or be the last
-non-whitespace lines before a line that starts with '---'. Such three
-minus signs start the patch part of the message.
+non-whitespace lines before a line that starts with '---' (followed by a
+space or the end of the line). Such three minus signs start the patch
+part of the message. See also `--no-divider` below.
 
 When reading trailers, there can be whitespaces after the
 token, the separator and the value. There can also be whitespaces
@@ -125,6 +126,11 @@ OPTIONS
        A convenience alias for `--only-trailers --only-input
        --unfold`.
 
+--no-divider::
+       Do not treat `---` as the end of the commit message. Use this
+       when you know your input contains just the commit message itself
+       (and not an email or the output of `git format-patch`).
+
 CONFIGURATION VARIABLES
 -----------------------
 
index 1fbc6ebcde0945fe6bc8cc85e5fd31247c4879e4..432baabe33e50d5e6f582aea8a9dee1e6de58fc8 100644 (file)
@@ -954,7 +954,7 @@ command fails, it is rescheduled immediately, with a helpful message how
 to proceed.
 
 The `reset` command resets the HEAD, index and worktree to the specified
-revision. It is isimilar to an `exec git reset --hard <label>`, but
+revision. It is similar to an `exec git reset --hard <label>`, but
 refuses to overwrite untracked files. If the `reset` command fails, it is
 rescheduled immediately, with a helpful message how to edit the todo list
 (this typically happens when a `reset` command was inserted into the todo
index bc8fdfd4691326b44e2cbdea3dc4925cf7440515..fda8516677237e9c0a8343f9ca96a1eb969b3492 100644 (file)
@@ -8,7 +8,7 @@ git-update-ref - Update the object name stored in a ref safely
 SYNOPSIS
 --------
 [verse]
-'git update-ref' [-m <reason>] (-d <ref> [<oldvalue>] | [--no-deref] [--create-reflog] <ref> <newvalue> [<oldvalue>] | --stdin [-z])
+'git update-ref' [-m <reason>] [--no-deref] (-d <ref> [<oldvalue>] | [--create-reflog] <ref> <newvalue> [<oldvalue>] | --stdin [-z])
 
 DESCRIPTION
 -----------
index dba7f0c18e33e7e26ca100992236e39b3d5b8a91..8e6a92e8ba64b64e663f96f4946f06af36e92759 100644 (file)
@@ -599,8 +599,8 @@ trace messages into this file descriptor.
 +
 Alternatively, if the variable is set to an absolute path
 (starting with a '/' character), Git will interpret this
-as a file path and will try to write the trace messages
-into it.
+as a file path and will try to append the trace messages
+to it.
 +
 Unsetting the variable, or setting it to empty, "0" or
 "false" (case insensitive) disables trace messages.
@@ -858,7 +858,9 @@ Reporting Bugs
 
 Report bugs to the Git mailing list <git@vger.kernel.org> where the
 development and maintenance is primarily done.  You do not have to be
-subscribed to the list to send a message there.
+subscribed to the list to send a message there.  See the list archive
+at https://public-inbox.org/git for previous bug reports and other
+discussions.
 
 Issues which are security relevant should be disclosed privately to
 the Git Security mailing list <git-security@googlegroups.com>.
index f970196bc1c52f489538ab515e1640ac435fead3..adc759612de99a2b86c89e353ff724e5eb25472e 100644 (file)
@@ -133,6 +133,12 @@ compares hostnames exactly, without considering whether two hosts are part of
 the same domain. Likewise, a config entry for `http://example.com` would not
 match: Git compares the protocols exactly.
 
+If the "pattern" URL does include a path component, then this too must match
+exactly: the context `https://example.com/bar/baz.git` will match a config
+entry for `https://example.com/bar/baz.git` (in addition to matching the config
+entry for `https://example.com`) but will not match a config entry for
+`https://example.com/bar`.
+
 
 CONFIGURATION OPTIONS
 ---------------------
index 7b273635de2b5bf3e3ba6ade8bcca3068a216395..21978ebbacf838328e6e686c333f97a2bddf1d5a 100644 (file)
@@ -756,7 +756,6 @@ 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
@@ -764,6 +763,7 @@ endif::git-rev-list[]
        stronger than `--missing=allow-promisor` because it limits the
        traversal, rather than just silencing errors about missing
        objects.
+endif::git-rev-list[]
 
 --no-walk[=(sorted|unsorted)]::
        Only show the given commits, but do not traverse their ancestors.
index 18142b6d29283bbf6e325ce71d50761a3f50a227..d0d1707c8c88560196eeb0d8bfe3be3362b682ea 100644 (file)
@@ -80,7 +80,7 @@ Calling sequence
   it is invoked.
 
 * For each commit, call `graph_next_line()` repeatedly, until
-  `graph_is_commit_finished()` returns non-zero.  Each call go
+  `graph_is_commit_finished()` returns non-zero.  Each call to
   `graph_next_line()` will output a single line of the graph.  The resulting
   lines will not contain any newlines.  `graph_next_line()` returns 1 if the
   resulting line contains the current commit, or 0 if this is merely a line
@@ -115,7 +115,6 @@ struct commit *commit;
 struct git_graph *graph = graph_init(opts);
 
 while ((commit = get_revision(opts)) != NULL) {
-       graph_update(graph, commit);
        while (!graph_is_commit_finished(graph))
        {
                struct strbuf sb;
index c664acbd765d06f000b2feeef1d7e98f1616ff62..7805b0968c828fda1601fe00c2b5b9d9359b38e2 100644 (file)
@@ -15,13 +15,13 @@ There are two main costs here:
 1. Decompressing and parsing commits.
 2. Walking the entire graph to satisfy topological order constraints.
 
-The commit graph file is a supplemental data structure that accelerates
+The commit-graph file is a supplemental data structure that accelerates
 commit graph walks. If a user downgrades or disables the 'core.commitGraph'
 config setting, then the existing ODB is sufficient. The file is stored
 as "commit-graph" either in the .git/objects/info directory or in the info
 directory of an alternate.
 
-The commit graph file stores the commit graph structure along with some
+The commit-graph file stores the commit graph structure along with some
 extra metadata to speed up graph walks. By listing commit OIDs in lexi-
 cographic order, we can identify an integer position for each commit and
 refer to the parents of a commit using those integer positions. We use
@@ -103,7 +103,7 @@ that of a parent.
 Design Details
 --------------
 
-- The commit graph file is stored in a file named 'commit-graph' in the
+- The commit-graph file is stored in a file named 'commit-graph' in the
   .git/objects/info directory. This could be stored in the info directory
   of an alternate.
 
@@ -112,12 +112,24 @@ Design Details
 - The file format includes parameters for the object ID hash function,
   so a future change of hash algorithm does not require a change in format.
 
+- Commit grafts and replace objects can change the shape of the commit
+  history. The latter can also be enabled/disabled on the fly using
+  `--no-replace-objects`. This leads to difficultly storing both possible
+  interpretations of a commit id, especially when computing generation
+  numbers. The commit-graph will not be read or written when
+  replace-objects or grafts are present.
+
+- Shallow clones create grafts of commits by dropping their parents. This
+  leads the commit-graph to think those commits have generation number 1.
+  If and when those commits are made unshallow, those generation numbers
+  become invalid. Since shallow clones are intended to restrict the commit
+  history to a very small set of commits, the commit-graph feature is less
+  helpful for these clones, anyway. The commit-graph will not be read or
+  written when shallow commits are present.
+
 Future Work
 -----------
 
-- The commit graph feature currently does not honor commit grafts. This can
-  be remedied by duplicating or refactoring the current graft logic.
-
 - After computing and storing generation numbers, we must make graph
   walks aware of generation numbers to gain the performance benefits they
   enable. This will mostly be accomplished by swapping a commit-date-ordered
@@ -127,7 +139,7 @@ Future Work
     - 'log --topo-order'
     - 'tag --merged'
 
-- A server could provide a commit graph file as part of the network protocol
+- A server could provide a commit-graph file as part of the network protocol
   to avoid extra calculations by clients. This feature is only of benefit if
   the user is willing to trust the file, because verifying the file is correct
   is as hard as computing it from scratch.
index e9dc8f7a01d4a9a96e55757b0c5b1f369049aefc..164fa4f49967817434d55ef2ff5f8ec851a42965 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.19.0
+DEF_VER=v2.19.1
 
 LF='
 '
index 5a969f5830a4105d3e3e6236eaa51e19880cc873..02a452b1f88314c957b9808996052043b3750953 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -738,6 +738,7 @@ TEST_BUILTINS_OBJS += test-submodule-config.o
 TEST_BUILTINS_OBJS += test-subprocess.o
 TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
 TEST_BUILTINS_OBJS += test-wildmatch.o
+TEST_BUILTINS_OBJS += test-windows-named-pipe.o
 TEST_BUILTINS_OBJS += test-write-cache.o
 
 TEST_PROGRAMS_NEED_X += test-dump-fsmonitor
index 5d139ba7f1e20412d24b1397b33e2d4cde016d19..5c08e40d2b4b4eb5c2b5122dbce75f7b941271fe 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.19.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.19.1.txt
\ No newline at end of file
index 0a07b140fedd8fb1f14dd844dae9844641842c57..c1870105eb453980ce421f937e9615d1d9dcd3b3 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -110,7 +110,8 @@ static const struct attr_check *get_archive_attrs(struct index_state *istate,
        static struct attr_check *check;
        if (!check)
                check = attr_check_initl("export-ignore", "export-subst", NULL);
-       return git_check_attr(istate, path, check) ? NULL : check;
+       git_check_attr(istate, path, check);
+       return check;
 }
 
 static int check_attr_export_ignore(const struct attr_check *check)
diff --git a/attr.c b/attr.c
index 98e4953f6e87f5f5ff777ee57cedf705086e109e..60d284796de728f59b839865eb035e1289a1a060 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -1143,9 +1143,9 @@ static void collect_some_attrs(const struct index_state *istate,
        fill(path, pathlen, basename_offset, check->stack, check->all_attrs, rem);
 }
 
-int git_check_attr(const struct index_state *istate,
-                  const char *path,
-                  struct attr_check *check)
+void git_check_attr(const struct index_state *istate,
+                   const char *path,
+                   struct attr_check *check)
 {
        int i;
 
@@ -1158,8 +1158,6 @@ int git_check_attr(const struct index_state *istate,
                        value = ATTR__UNSET;
                check->items[i].value = value;
        }
-
-       return 0;
 }
 
 void git_all_attrs(const struct index_state *istate,
diff --git a/attr.h b/attr.h
index 2be86db36e730938777066f1951360396ddc9676..b0378bfe5feadb91134535b339515e554622cfee 100644 (file)
--- a/attr.h
+++ b/attr.h
@@ -63,8 +63,8 @@ void attr_check_free(struct attr_check *check);
  */
 const char *git_attr_name(const struct git_attr *);
 
-int git_check_attr(const struct index_state *istate,
-                  const char *path, struct attr_check *check);
+void git_check_attr(const struct index_state *istate,
+                   const char *path, struct attr_check *check);
 
 /*
  * Retrieve all attributes that apply to the specified path.
index 9916498a29bbd8fa7c5c5d8e7bd32e1dc184909b..0b64bcdebe0f4581953adccf8addf1c3fd1eb61e 100644 (file)
@@ -454,7 +454,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
         * Check the "pathspec '%s' did not match any files" block
         * below before enabling new magic.
         */
-       parse_pathspec(&pathspec, 0,
+       parse_pathspec(&pathspec, PATHSPEC_ATTR,
                       PATHSPEC_PREFER_FULL |
                       PATHSPEC_SYMLINK_LEADING_PATH,
                       prefix, argv);
index 64ec1745ab2c20ef18a9292ef3b3c82efd46de17..0520cecc9a1d963841b384de6173d752e6d78fb7 100644 (file)
@@ -50,6 +50,13 @@ static int filter_object(const char *path, unsigned mode,
        return 0;
 }
 
+static int stream_blob(const struct object_id *oid)
+{
+       if (stream_blob_to_fd(1, oid, NULL, 0))
+               die("unable to stream %s to stdout", oid_to_hex(oid));
+       return 0;
+}
+
 static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
                        int unknown_type)
 {
@@ -131,7 +138,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
                }
 
                if (type == OBJ_BLOB)
-                       return stream_blob_to_fd(1, &oid, NULL, 0);
+                       return stream_blob(&oid);
                buf = read_object_file(&oid, &type, &size);
                if (!buf)
                        die("Cannot read object %s", obj_name);
@@ -154,7 +161,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
                                oidcpy(&blob_oid, &oid);
 
                        if (oid_object_info(the_repository, &blob_oid, NULL) == OBJ_BLOB)
-                               return stream_blob_to_fd(1, &blob_oid, NULL, 0);
+                               return stream_blob(&blob_oid);
                        /*
                         * we attempted to dereference a tag to a blob
                         * and failed; there may be new dereference
@@ -317,8 +324,9 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
                                BUG("invalid cmdmode: %c", opt->cmdmode);
                        batch_write(opt, contents, size);
                        free(contents);
-               } else if (stream_blob_to_fd(1, oid, NULL, 0) < 0)
-                       die("unable to stream %s to stdout", oid_to_hex(oid));
+               } else {
+                       stream_blob(oid);
+               }
        }
        else {
                enum object_type type;
index c05573ff9cd091cbe3b5ff1980fa7fb4815f6ebd..30a2f84274d32977769f778d5704accb599cf0e7 100644 (file)
@@ -65,8 +65,7 @@ static void check_attr(const char *prefix,
        if (collect_all) {
                git_all_attrs(&the_index, full_path, check);
        } else {
-               if (git_check_attr(&the_index, full_path, check))
-                       die("git_check_attr died");
+               git_check_attr(&the_index, full_path, check);
        }
        output_attr(check, file);
 
index 0bf0c486575a8557b729658ce05be02d28a1a8b8..da737df321528088d8aa20e3e87e803336b1f550 100644 (file)
@@ -120,6 +120,8 @@ static int graph_read(int argc, const char **argv)
        return 0;
 }
 
+extern int read_replace_refs;
+
 static int graph_write(int argc, const char **argv)
 {
        struct string_list *pack_indexes = NULL;
@@ -150,6 +152,8 @@ static int graph_write(int argc, const char **argv)
        if (!opts.obj_dir)
                opts.obj_dir = get_object_directory();
 
+       read_replace_refs = 0;
+
        if (opts.reachable) {
                write_commit_graph_reachable(opts.obj_dir, opts.append);
                return 0;
index 0d9828e29ebe89f037e68761d5cd5b407339cd48..83233ca1a54aaf1d25c7084df90d5f1fd4f49a24 100644 (file)
@@ -872,6 +872,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                s->use_color = 0;
                commitable = run_status(s->fp, index_file, prefix, 1, s);
                s->use_color = saved_color_setting;
+               string_list_clear(&s->change, 1);
        } else {
                struct object_id oid;
                const char *parent = "HEAD";
index b742539d4de20361bb43bcb4dc33cfc9165c42fb..4b87e0dd2e8854bf940e92e3020c5e4f66e8a6bd 100644 (file)
@@ -104,6 +104,7 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "unfold", &opts.unfold, N_("join whitespace-continued values")),
                { OPTION_CALLBACK, 0, "parse", &opts, NULL, N_("set parsing options"),
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_parse },
+               OPT_BOOL(0, "no-divider", &opts.no_divider, N_("do not treat --- specially")),
                OPT_CALLBACK(0, "trailer", &trailers, N_("trailer"),
                                N_("trailer(s) to add"), option_parse_trailer),
                OPT_END()
index d1144a8f7ef79f7efa5bf64141a9133cfeee66d1..2eba0a695b29874a4b002c08403847a67496726c 100644 (file)
@@ -951,8 +951,7 @@ static int no_try_delta(const char *path)
 
        if (!check)
                check = attr_check_initl("delta", NULL);
-       if (git_check_attr(&the_index, path, check))
-               return 0;
+       git_check_attr(&the_index, path, check);
        if (ATTR_FALSE(check->items[0].value))
                return 1;
        return 0;
@@ -2299,7 +2298,6 @@ static void init_threaded_search(void)
        pthread_mutex_init(&cache_mutex, NULL);
        pthread_mutex_init(&progress_mutex, NULL);
        pthread_cond_init(&progress_cond, NULL);
-       pthread_mutex_init(&to_pack.lock, NULL);
        old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads);
 }
 
@@ -2991,6 +2989,7 @@ static void get_object_list(int ac, const char **av)
 
        init_revisions(&revs, NULL);
        save_commit_buffer = 0;
+       revs.allow_exclude_promisor_objects_opt = 1;
        setup_revisions(ac, av, &revs, NULL);
 
        /* make sure shallows are read */
index 4916a4daa264ee695dbfbde81dbd8ef0072ac9c4..d356ad70f6c3ad06112420e5a5f984fa38f09473 100644 (file)
@@ -120,6 +120,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
        save_commit_buffer = 0;
        read_replace_refs = 0;
        ref_paranoia = 1;
+       revs.allow_exclude_promisor_objects_opt = 1;
        init_revisions(&revs, prefix);
 
        argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
@@ -161,7 +162,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
        free(s);
 
        if (is_repository_shallow(the_repository))
-               prune_shallow(show_only);
+               prune_shallow(show_only ? PRUNE_SHOW_ONLY : 0);
 
        return 0;
 }
index c17ce94e12ee34c5b822b0e09fcd6d7264e759ad..95eb7002eebeb2386796fb2ff5eb81efefaeec66 100644 (file)
@@ -465,7 +465,7 @@ static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)
        unsigned char sha1[GIT_SHA1_RAWSZ];
 
        strbuf_addf(&buf, "%s:%"PRItime, path, stamp);
-       hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));;
+       hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));
        strbuf_release(&buf);
 
        /* RFC 2104 5. HMAC-SHA1-80 */
@@ -1025,6 +1025,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
        const char *ret;
        struct object_id *old_oid = &cmd->old_oid;
        struct object_id *new_oid = &cmd->new_oid;
+       int do_update_worktree = 0;
 
        /* only refs/... are allowed */
        if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
@@ -1050,9 +1051,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                                refuse_unconfigured_deny();
                        return "branch is currently checked out";
                case DENY_UPDATE_INSTEAD:
-                       ret = update_worktree(new_oid->hash);
-                       if (ret)
-                               return ret;
+                       /* pass -- let other checks intervene first */
+                       do_update_worktree = 1;
                        break;
                }
        }
@@ -1117,6 +1117,12 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                return "hook declined";
        }
 
+       if (do_update_worktree) {
+               ret = update_worktree(new_oid->hash);
+               if (ret)
+                       return ret;
+       }
+
        if (is_null_oid(new_oid)) {
                struct strbuf err = STRBUF_INIT;
                if (!parse_object(the_repository, old_oid)) {
@@ -1833,7 +1839,7 @@ static void prepare_shallow_update(struct command *commands,
        /*
         * keep hooks happy by forcing a temporary shallow file via
         * env variable because we can't add --shallow-file to every
-        * command. check_everything_connected() will be done with
+        * command. check_connected() will be done with
         * true .git/shallow though.
         */
        setenv(GIT_SHALLOW_FILE_ENVIRONMENT, alt_shallow_file, 1);
index 7876db1c20d317e28e4a84880197c6a690bf9ef6..5fd1012faaeceaa0d8db18c3a15310b0d8570043 100644 (file)
@@ -625,7 +625,7 @@ static int mv(int argc, const char **argv)
 
        oldremote = remote_get(rename.old_name);
        if (!remote_is_configured(oldremote, 1))
-               die(_("No such remote: %s"), rename.old_name);
+               die(_("No such remote: '%s'"), rename.old_name);
 
        if (!strcmp(rename.old_name, rename.new_name) && oldremote->origin != REMOTE_CONFIG)
                return migrate_file(oldremote);
@@ -761,7 +761,7 @@ static int rm(int argc, const char **argv)
 
        remote = remote_get(argv[1]);
        if (!remote_is_configured(remote, 1))
-               die(_("No such remote: %s"), argv[1]);
+               die(_("No such remote: '%s'"), argv[1]);
 
        known_remotes.to_delete = remote;
        for_each_remote(add_known_remote, &known_remotes);
@@ -860,7 +860,7 @@ static int get_remote_ref_states(const char *name,
 
        states->remote = remote_get(name);
        if (!states->remote)
-               return error(_("No such remote: %s"), name);
+               return error(_("No such remote: '%s'"), name);
 
        read_branches();
 
index d5886039cc6656609962fd522a27f61eda6cd0ec..3c30e16ec5483c2a88cbe96f67475746487b31a9 100644 (file)
@@ -531,6 +531,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                if (!po_args.quiet && isatty(2))
                        opts |= PRUNE_PACKED_VERBOSE;
                prune_packed_objects(opts);
+
+               if (!keep_unreachable &&
+                   (!(pack_everything & LOOSEN_UNREACHABLE) ||
+                    unpack_unreachable) &&
+                   is_repository_shallow(the_repository))
+                       prune_shallow(PRUNE_QUICK);
        }
 
        if (!no_update_server_info)
index 4f05791f3e895f78ba511dd6571bd09abab9269c..17868a92dcfccce335d69854f54f0ef5d3a9ca1b 100644 (file)
@@ -39,7 +39,8 @@ struct show_data {
        enum replace_format format;
 };
 
-static int show_reference(const char *refname, const struct object_id *oid,
+static int show_reference(struct repository *r, const char *refname,
+                         const struct object_id *oid,
                          int flag, void *cb_data)
 {
        struct show_data *data = cb_data;
@@ -56,9 +57,8 @@ static int show_reference(const char *refname, const struct object_id *oid,
                        if (get_oid(refname, &object))
                                return error(_("failed to resolve '%s' as a valid ref"), refname);
 
-                       obj_type = oid_object_info(the_repository, &object,
-                                                  NULL);
-                       repl_type = oid_object_info(the_repository, oid, NULL);
+                       obj_type = oid_object_info(r, &object, NULL);
+                       repl_type = oid_object_info(r, oid, NULL);
 
                        printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type),
                               oid_to_hex(oid), type_name(repl_type));
index 5b07f3f4a2cf6cd5f73f20a930934ca2277c8391..41dd33c6a75507f32c7e0525ac977c6e33441fb8 100644 (file)
@@ -372,6 +372,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        git_config(git_default_config, NULL);
        init_revisions(&revs, prefix);
        revs.abbrev = DEFAULT_ABBREV;
+       revs.allow_exclude_promisor_objects_opt = 1;
        revs.commit_format = CMIT_FMT_UNSPECIFIED;
 
        /*
index f6fb8991f3a81b0b2dd895c1e8550b5bda5ea99a..5e6f2db4cdbcf9e88ddfc1890359f7f2f86b2cd2 100644 (file)
@@ -1233,6 +1233,7 @@ static int clone_submodule(const char *path, const char *gitdir, const char *url
        if (gitdir && *gitdir)
                argv_array_pushl(&cp.args, "--separate-git-dir", gitdir, NULL);
 
+       argv_array_push(&cp.args, "--");
        argv_array_push(&cp.args, url);
        argv_array_push(&cp.args, path);
 
index 4fa3c0a86fd7cbeb5214223e06aa10a2ecfa2d85..2d8f7f05785dca5a083ee6d003d16b4d008dc4d2 100644 (file)
@@ -14,7 +14,8 @@ static const char * const git_update_ref_usage[] = {
 };
 
 static char line_termination = '\n';
-static int update_flags;
+static unsigned int update_flags;
+static unsigned int default_flags;
 static unsigned create_reflog_flag;
 static const char *msg;
 
@@ -205,7 +206,7 @@ static const char *parse_cmd_update(struct ref_transaction *transaction,
                                   msg, &err))
                die("%s", err.buf);
 
-       update_flags = 0;
+       update_flags = default_flags;
        free(refname);
        strbuf_release(&err);
 
@@ -237,7 +238,7 @@ static const char *parse_cmd_create(struct ref_transaction *transaction,
                                   msg, &err))
                die("%s", err.buf);
 
-       update_flags = 0;
+       update_flags = default_flags;
        free(refname);
        strbuf_release(&err);
 
@@ -273,7 +274,7 @@ static const char *parse_cmd_delete(struct ref_transaction *transaction,
                                   update_flags, msg, &err))
                die("%s", err.buf);
 
-       update_flags = 0;
+       update_flags = default_flags;
        free(refname);
        strbuf_release(&err);
 
@@ -302,7 +303,7 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction,
                                   update_flags, &err))
                die("%s", err.buf);
 
-       update_flags = 0;
+       update_flags = default_flags;
        free(refname);
        strbuf_release(&err);
 
@@ -357,7 +358,6 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
        const char *refname, *oldval;
        struct object_id oid, oldoid;
        int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0;
-       unsigned int flags = 0;
        int create_reflog = 0;
        struct option options[] = {
                OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")),
@@ -378,6 +378,11 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
 
        create_reflog_flag = create_reflog ? REF_FORCE_CREATE_REFLOG : 0;
 
+       if (no_deref) {
+               default_flags = REF_NO_DEREF;
+               update_flags = default_flags;
+       }
+
        if (read_stdin) {
                struct strbuf err = STRBUF_INIT;
                struct ref_transaction *transaction;
@@ -385,7 +390,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
                transaction = ref_transaction_begin(&err);
                if (!transaction)
                        die("%s", err.buf);
-               if (delete || no_deref || argc > 0)
+               if (delete || argc > 0)
                        usage_with_options(git_update_ref_usage, options);
                if (end_null)
                        line_termination = '\0';
@@ -427,8 +432,6 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
                        die("%s: not a valid old SHA1", oldval);
        }
 
-       if (no_deref)
-               flags = REF_NO_DEREF;
        if (delete)
                /*
                 * For purposes of backwards compatibility, we treat
@@ -436,9 +439,9 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
                 */
                return delete_ref(msg, refname,
                                  (oldval && !is_null_oid(&oldoid)) ? &oldoid : NULL,
-                                 flags);
+                                 default_flags);
        else
                return update_ref(msg, refname, &oid, oldval ? &oldoid : NULL,
-                                 flags | create_reflog_flag,
+                                 default_flags | create_reflog_flag,
                                  UPDATE_REFS_DIE_ON_ERR);
 }
diff --git a/cache.h b/cache.h
index 4d014541ab7bc7692919c871a5306543bbf361c5..3f419b6c799c924b3e39cc93b1d5827aa64dfaed 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -781,6 +781,8 @@ extern void *read_blob_data_from_index(const struct index_state *, const char *,
 #define CE_MATCH_REFRESH               0x10
 /* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
 #define CE_MATCH_IGNORE_FSMONITOR 0X20
+extern int is_racy_timestamp(const struct index_state *istate,
+                            const struct cache_entry *ce);
 extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 
index 8a1bec7b8aa420dd3d4ecadc95dee31029533c07..7cfa779dcbdcf9a3e2137cc54c1c9ef62c5f6895 100644 (file)
@@ -13,6 +13,8 @@
 #include "commit-graph.h"
 #include "object-store.h"
 #include "alloc.h"
+#include "hashmap.h"
+#include "replace-object.h"
 
 #define GRAPH_SIGNATURE 0x43475048 /* "CGPH" */
 #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
@@ -56,6 +58,28 @@ static struct commit_graph *alloc_commit_graph(void)
        return g;
 }
 
+extern int read_replace_refs;
+
+static int commit_graph_compatible(struct repository *r)
+{
+       if (!r->gitdir)
+               return 0;
+
+       if (read_replace_refs) {
+               prepare_replace_object(r);
+               if (hashmap_get_size(&r->objects->replace_map->map))
+                       return 0;
+       }
+
+       prepare_commit_graft(r);
+       if (r->parsed_objects && r->parsed_objects->grafts_nr)
+               return 0;
+       if (is_repository_shallow(r))
+               return 0;
+
+       return 1;
+}
+
 struct commit_graph *load_commit_graph_one(const char *graph_file)
 {
        void *graph_map;
@@ -223,6 +247,9 @@ static int prepare_commit_graph(struct repository *r)
                 */
                return 0;
 
+       if (!commit_graph_compatible(r))
+               return 0;
+
        obj_dir = r->objects->objectdir;
        prepare_commit_graph_one(r, obj_dir);
        prepare_alt_odb(r);
@@ -233,10 +260,10 @@ static int prepare_commit_graph(struct repository *r)
        return !!r->objects->commit_graph;
 }
 
-static void close_commit_graph(void)
+void close_commit_graph(struct repository *r)
 {
-       free_commit_graph(the_repository->objects->commit_graph);
-       the_repository->objects->commit_graph = NULL;
+       free_commit_graph(r->objects->commit_graph);
+       r->objects->commit_graph = NULL;
 }
 
 static int bsearch_graph(struct commit_graph *g, struct object_id *oid, uint32_t *pos)
@@ -693,6 +720,9 @@ void write_commit_graph(const char *obj_dir,
        int num_extra_edges;
        struct commit_list *parent;
 
+       if (!commit_graph_compatible(the_repository))
+               return;
+
        oids.nr = 0;
        oids.alloc = approximate_object_count() / 4;
 
@@ -845,7 +875,7 @@ void write_commit_graph(const char *obj_dir,
        write_graph_chunk_data(f, GRAPH_OID_LEN, commits.list, commits.nr);
        write_graph_chunk_large_edges(f, commits.list, commits.nr);
 
-       close_commit_graph();
+       close_commit_graph(the_repository);
        finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
        commit_lock_file(&lk);
 
index eea62f8c0ee53b56630a1a2b0c2c716b4cd63670..b62d1b08373633afd545f7d8734cbd0d61963bb6 100644 (file)
@@ -60,6 +60,7 @@ void write_commit_graph(const char *obj_dir,
 
 int verify_commit_graph(struct repository *r, struct commit_graph *g);
 
+void close_commit_graph(struct repository *);
 void free_commit_graph(struct commit_graph *);
 
 #endif
index 449c1f4920cff631f5cf2479772c350c9b8b7325..fb4c99cb8908b53c2bbba43ef08de0e2e217533e 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -209,7 +209,7 @@ static int read_graft_file(struct repository *r, const char *graft_file)
        return 0;
 }
 
-static void prepare_commit_graft(struct repository *r)
+void prepare_commit_graft(struct repository *r)
 {
        char *graft_file;
 
@@ -1787,10 +1787,10 @@ const char *find_commit_header(const char *msg, const char *key, size_t *out_len
  * Returns the number of bytes from the tail to ignore, to be fed as
  * the second parameter to append_signoff().
  */
-int ignore_non_trailer(const char *buf, size_t len)
+size_t ignore_non_trailer(const char *buf, size_t len)
 {
-       int boc = 0;
-       int bol = 0;
+       size_t boc = 0;
+       size_t bol = 0;
        int in_old_conflicts_block = 0;
        size_t cutoff = wt_status_locate_end(buf, len);
 
index da0db36eba2bf16277dbb7aadcb3384e29191ecc..dc5107259fd090cb33d9b25d4bf8530108d7109e 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -202,6 +202,7 @@ typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);
 
 struct commit_graft *read_graft_line(struct strbuf *line);
 int register_commit_graft(struct repository *r, struct commit_graft *, int);
+void prepare_commit_graft(struct repository *r);
 struct commit_graft *lookup_commit_graft(struct repository *r, const struct object_id *oid);
 
 extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2);
@@ -255,7 +256,9 @@ extern void assign_shallow_commits_to_refs(struct shallow_info *info,
                                           uint32_t **used,
                                           int *ref_status);
 extern int delayed_reachability_test(struct shallow_info *si, int c);
-extern void prune_shallow(int show_only);
+#define PRUNE_SHOW_ONLY 1
+#define PRUNE_QUICK 2
+extern void prune_shallow(unsigned options);
 extern struct trace_key trace_shallow;
 
 int is_descendant_of(struct commit *, struct commit_list *);
@@ -322,7 +325,7 @@ extern const char *find_commit_header(const char *msg, const char *key,
                                      size_t *out_len);
 
 /* Find the end of the log message, the right place for a new trailer. */
-extern int ignore_non_trailer(const char *buf, size_t len);
+extern size_t ignore_non_trailer(const char *buf, size_t len);
 
 typedef int (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra,
                                 void *cb_data);
index 858ca14a57351062d2bba147c72ed460fb0aa533..18caf21969a5917e8e6c6d754beffbba2741e6c2 100644 (file)
@@ -341,6 +341,19 @@ int mingw_mkdir(const char *path, int mode)
        return ret;
 }
 
+/*
+ * Calling CreateFile() using FILE_APPEND_DATA and without FILE_WRITE_DATA
+ * is documented in [1] as opening a writable file handle in append mode.
+ * (It is believed that) this is atomic since it is maintained by the
+ * kernel unlike the O_APPEND flag which is racily maintained by the CRT.
+ *
+ * [1] https://docs.microsoft.com/en-us/windows/desktop/fileio/file-access-rights-constants
+ *
+ * This trick does not appear to work for named pipes.  Instead it creates
+ * a named pipe client handle that cannot be written to.  Callers should
+ * just use the regular _wopen() for them.  (And since client handle gets
+ * bound to a unique server handle, it isn't really an issue.)
+ */
 static int mingw_open_append(wchar_t const *wfilename, int oflags, ...)
 {
        HANDLE handle;
@@ -360,10 +373,12 @@ static int mingw_open_append(wchar_t const *wfilename, int oflags, ...)
                        NULL, create, FILE_ATTRIBUTE_NORMAL, NULL);
        if (handle == INVALID_HANDLE_VALUE)
                return errno = err_win_to_posix(GetLastError()), -1;
+
        /*
         * No O_APPEND here, because the CRT uses it only to reset the
-        * file pointer to EOF on write(); but that is not necessary
-        * for a file created with FILE_APPEND_DATA.
+        * file pointer to EOF before each write(); but that is not
+        * necessary (and may lead to races) for a file created with
+        * FILE_APPEND_DATA.
         */
        fd = _open_osfhandle((intptr_t)handle, O_BINARY);
        if (fd < 0)
@@ -371,6 +386,21 @@ static int mingw_open_append(wchar_t const *wfilename, int oflags, ...)
        return fd;
 }
 
+/*
+ * Does the pathname map to the local named pipe filesystem?
+ * That is, does it have a "//./pipe/" prefix?
+ */
+static int is_local_named_pipe_path(const char *filename)
+{
+       return (is_dir_sep(filename[0]) &&
+               is_dir_sep(filename[1]) &&
+               filename[2] == '.'  &&
+               is_dir_sep(filename[3]) &&
+               !strncasecmp(filename+4, "pipe", 4) &&
+               is_dir_sep(filename[8]) &&
+               filename[9]);
+}
+
 int mingw_open (const char *filename, int oflags, ...)
 {
        typedef int (*open_fn_t)(wchar_t const *wfilename, int oflags, ...);
@@ -387,7 +417,7 @@ int mingw_open (const char *filename, int oflags, ...)
        if (filename && !strcmp(filename, "/dev/null"))
                filename = "nul";
 
-       if (oflags & O_APPEND)
+       if ((oflags & O_APPEND) && !is_local_named_pipe_path(filename))
                open_fn = mingw_open_append;
        else
                open_fn = _wopen;
index 9a998149d99a8eeebf311d5e84d43f146ed0f90d..92d268137f61e825c1529e5eedca9e39c539dce2 100644 (file)
@@ -7,6 +7,7 @@ CFLAGS += -pedantic
 CFLAGS += -DUSE_PARENS_AROUND_GETTEXT_N=0
 endif
 CFLAGS += -Wdeclaration-after-statement
+CFLAGS += -Wformat-security
 CFLAGS += -Wno-format-zero-length
 CFLAGS += -Wold-style-definition
 CFLAGS += -Woverflow
index 6057f1f58015ad7cae9792309dd635a1e7933586..e0848226d21bbbeb26d9ac6217fe1e61ccc71fca 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -1297,6 +1297,7 @@ static void convert_attrs(const struct index_state *istate,
                          struct conv_attrs *ca, const char *path)
 {
        static struct attr_check *check;
+       struct attr_check_item *ccheck = NULL;
 
        if (!check) {
                check = attr_check_initl("crlf", "ident", "filter",
@@ -1306,30 +1307,25 @@ static void convert_attrs(const struct index_state *istate,
                git_config(read_convert_config, NULL);
        }
 
-       if (!git_check_attr(istate, path, check)) {
-               struct attr_check_item *ccheck = check->items;
-               ca->crlf_action = git_path_check_crlf(ccheck + 4);
-               if (ca->crlf_action == CRLF_UNDEFINED)
-                       ca->crlf_action = git_path_check_crlf(ccheck + 0);
-               ca->ident = git_path_check_ident(ccheck + 1);
-               ca->drv = git_path_check_convert(ccheck + 2);
-               if (ca->crlf_action != CRLF_BINARY) {
-                       enum eol eol_attr = git_path_check_eol(ccheck + 3);
-                       if (ca->crlf_action == CRLF_AUTO && eol_attr == EOL_LF)
-                               ca->crlf_action = CRLF_AUTO_INPUT;
-                       else if (ca->crlf_action == CRLF_AUTO && eol_attr == EOL_CRLF)
-                               ca->crlf_action = CRLF_AUTO_CRLF;
-                       else if (eol_attr == EOL_LF)
-                               ca->crlf_action = CRLF_TEXT_INPUT;
-                       else if (eol_attr == EOL_CRLF)
-                               ca->crlf_action = CRLF_TEXT_CRLF;
-               }
-               ca->working_tree_encoding = git_path_check_encoding(ccheck + 5);
-       } else {
-               ca->drv = NULL;
-               ca->crlf_action = CRLF_UNDEFINED;
-               ca->ident = 0;
+       git_check_attr(istate, path, check);
+       ccheck = check->items;
+       ca->crlf_action = git_path_check_crlf(ccheck + 4);
+       if (ca->crlf_action == CRLF_UNDEFINED)
+               ca->crlf_action = git_path_check_crlf(ccheck + 0);
+       ca->ident = git_path_check_ident(ccheck + 1);
+       ca->drv = git_path_check_convert(ccheck + 2);
+       if (ca->crlf_action != CRLF_BINARY) {
+               enum eol eol_attr = git_path_check_eol(ccheck + 3);
+               if (ca->crlf_action == CRLF_AUTO && eol_attr == EOL_LF)
+                       ca->crlf_action = CRLF_AUTO_INPUT;
+               else if (ca->crlf_action == CRLF_AUTO && eol_attr == EOL_CRLF)
+                       ca->crlf_action = CRLF_AUTO_CRLF;
+               else if (eol_attr == EOL_LF)
+                       ca->crlf_action = CRLF_TEXT_INPUT;
+               else if (eol_attr == EOL_CRLF)
+                       ca->crlf_action = CRLF_TEXT_CRLF;
        }
+       ca->working_tree_encoding = git_path_check_encoding(ccheck + 5);
 
        /* Save attr and make a decision for action */
        ca->attr_action = ca->crlf_action;
diff --git a/diff.c b/diff.c
index 145cfbae5929c69224f9f9e5bc473f2a221603de..db5e5e9640743e275ac44abcf3ffb2f3c59eb556 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -4256,12 +4256,12 @@ static void diff_fill_oid_info(struct diff_filespec *one)
 static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
 {
        /* Strip the prefix but do not molest /dev/null and absolute paths */
-       if (*namep && **namep != '/') {
+       if (*namep && !is_absolute_path(*namep)) {
                *namep += prefix_length;
                if (**namep == '/')
                        ++*namep;
        }
-       if (*otherp && **otherp != '/') {
+       if (*otherp && !is_absolute_path(*otherp)) {
                *otherp += prefix_length;
                if (**otherp == '/')
                        ++*otherp;
index 853624f811c59c17af88814ebeecf4154095a19c..42665488008bc0fffafa5d4e0455bc22b510b92a 100644 (file)
@@ -23,21 +23,16 @@ static void fetch_refs(const char *remote_name, struct ref *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)
+void fetch_objects(const char *remote_name, const struct object_id *oids,
+                  int oid_nr)
 {
        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]);
+       for (i = 0; i < oid_nr; i++) {
+               struct ref *new_ref = alloc_ref(oid_to_hex(&oids[i]));
+               oidcpy(&new_ref->old_oid, &oids[i]);
+               new_ref->exact_oid = 1;
                new_ref->next = ref;
                ref = new_ref;
        }
index 4b269d07ed635b3e6b1f5f7b2118d290d27e7e4e..d2f996d4e8c79c7bde78780dad5ad543910fcc29 100644 (file)
@@ -1,11 +1,7 @@
 #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);
+void fetch_objects(const char *remote_name, const struct object_id *oids,
+                  int oid_nr);
 
 #endif
index 88a078e9befd281cf5f03e9e64615b14ca768a35..79007f996c899272b6ae9044a9029776181f3438 100644 (file)
@@ -253,8 +253,10 @@ static int find_common(struct fetch_negotiator *negotiator,
        if (args->stateless_rpc && multi_ack == 1)
                die(_("--stateless-rpc requires multi_ack_detailed"));
 
-       mark_tips(negotiator, args->negotiation_tips);
-       for_each_cached_alternate(negotiator, insert_one_alternate_object);
+       if (!args->no_dependents) {
+               mark_tips(negotiator, args->negotiation_tips);
+               for_each_cached_alternate(negotiator, insert_one_alternate_object);
+       }
 
        fetching = 0;
        for ( ; refs ; refs = refs->next) {
@@ -271,8 +273,12 @@ static int find_common(struct fetch_negotiator *negotiator,
                 * We use lookup_object here because we are only
                 * interested in the case we *know* the object is
                 * reachable and we have already scanned it.
+                *
+                * Do this only if args->no_dependents is false (if it is true,
+                * we cannot trust the object flags).
                 */
-               if (((o = lookup_object(the_repository, remote->hash)) != NULL) &&
+               if (!args->no_dependents &&
+                   ((o = lookup_object(the_repository, remote->hash)) != NULL) &&
                                (o->flags & COMPLETE)) {
                        continue;
                }
@@ -710,31 +716,29 @@ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator,
 
        oidset_clear(&loose_oid_set);
 
-       if (!args->no_dependents) {
-               if (!args->deepen) {
-                       for_each_ref(mark_complete_oid, NULL);
-                       for_each_cached_alternate(NULL, mark_alternate_complete);
-                       commit_list_sort_by_date(&complete);
-                       if (cutoff)
-                               mark_recent_complete_commits(args, cutoff);
-               }
+       if (!args->deepen) {
+               for_each_ref(mark_complete_oid, NULL);
+               for_each_cached_alternate(NULL, 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(the_repository,
-                                                    lookup_object(the_repository,
-                                                    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(the_repository,
+                                            lookup_object(the_repository,
+                                            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;
 
-                       negotiator->known_common(negotiator,
-                                                (struct commit *)o);
-               }
+               negotiator->known_common(negotiator,
+                                        (struct commit *)o);
        }
 
        save_commit_buffer = old_save_commit_buffer;
@@ -990,11 +994,15 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
        if (!server_supports("deepen-relative") && args->deepen_relative)
                die(_("Server does not support --deepen"));
 
-       mark_complete_and_common_ref(&negotiator, args, &ref);
-       filter_refs(args, &ref, sought, nr_sought);
-       if (everything_local(args, &ref)) {
-               packet_flush(fd[1]);
-               goto all_done;
+       if (!args->no_dependents) {
+               mark_complete_and_common_ref(&negotiator, args, &ref);
+               filter_refs(args, &ref, sought, nr_sought);
+               if (everything_local(args, &ref)) {
+                       packet_flush(fd[1]);
+                       goto all_done;
+               }
+       } else {
+               filter_refs(args, &ref, sought, nr_sought);
        }
        if (find_common(&negotiator, args, fd, &oid, ref) < 0)
                if (!args->keep_pack)
@@ -1040,7 +1048,7 @@ static void add_shallow_requests(struct strbuf *req_buf,
        }
 }
 
-static void add_wants(const struct ref *wants, struct strbuf *req_buf)
+static void add_wants(int no_dependents, const struct ref *wants, struct strbuf *req_buf)
 {
        int use_ref_in_want = server_supports_feature("fetch", "ref-in-want", 0);
 
@@ -1057,8 +1065,12 @@ static void add_wants(const struct ref *wants, struct strbuf *req_buf)
                 * We use lookup_object here because we are only
                 * interested in the case we *know* the object is
                 * reachable and we have already scanned it.
+                *
+                * Do this only if args->no_dependents is false (if it is true,
+                * we cannot trust the object flags).
                 */
-               if (((o = lookup_object(the_repository, remote->hash)) != NULL) &&
+               if (!no_dependents &&
+                   ((o = lookup_object(the_repository, remote->hash)) != NULL) &&
                    (o->flags & COMPLETE)) {
                        continue;
                }
@@ -1155,7 +1167,7 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
        }
 
        /* add wants */
-       add_wants(wants, &req_buf);
+       add_wants(args->no_dependents, wants, &req_buf);
 
        if (args->no_dependents) {
                packet_buf_write(&req_buf, "done");
@@ -1346,16 +1358,21 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
                                args->deepen = 1;
 
                        /* Filter 'ref' by 'sought' and those that aren't local */
-                       mark_complete_and_common_ref(&negotiator, args, &ref);
-                       filter_refs(args, &ref, sought, nr_sought);
-                       if (everything_local(args, &ref))
-                               state = FETCH_DONE;
-                       else
+                       if (!args->no_dependents) {
+                               mark_complete_and_common_ref(&negotiator, args, &ref);
+                               filter_refs(args, &ref, sought, nr_sought);
+                               if (everything_local(args, &ref))
+                                       state = FETCH_DONE;
+                               else
+                                       state = FETCH_SEND_REQUEST;
+
+                               mark_tips(&negotiator, args->negotiation_tips);
+                               for_each_cached_alternate(&negotiator,
+                                                         insert_one_alternate_object);
+                       } else {
+                               filter_refs(args, &ref, sought, nr_sought);
                                state = FETCH_SEND_REQUEST;
-
-                       mark_tips(&negotiator, args->negotiation_tips);
-                       for_each_cached_alternate(&negotiator,
-                                                 insert_one_alternate_object);
+                       }
                        break;
                case FETCH_SEND_REQUEST:
                        if (send_fetch_request(&negotiator, fd[1], args, ref,
@@ -1598,6 +1615,20 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
        if (nr_sought)
                nr_sought = remove_duplicates_in_refs(sought, nr_sought);
 
+       if (args->no_dependents && !args->filter_options.choice) {
+               /*
+                * The protocol does not support requesting that only the
+                * wanted objects be sent, so approximate this by setting a
+                * "blob:none" filter if no filter is already set. This works
+                * for all object types: note that wanted blobs will still be
+                * sent because they are directly specified as a "want".
+                *
+                * NEEDSWORK: Add an option in the protocol to request that
+                * only the wanted objects be sent, and implement it.
+                */
+               parse_list_objects_filter(&args->filter_options, "blob:none");
+       }
+
        if (!ref) {
                packet_flush(fd[1]);
                die(_("no matching remote head"));
index 5b6e868802b53ca5fa59eb864199274ac538c242..43ec344d95b40c701b2fc703b4b2b1e3ff72a2d2 100644 (file)
@@ -43,6 +43,13 @@ struct fetch_pack_args {
        unsigned from_promisor:1;
 
        /*
+        * Attempt to fetch only the wanted objects, and not any objects
+        * referred to by them. Due to protocol limitations, extraneous
+        * objects may still be included. (When fetching non-blob
+        * objects, only blobs are excluded; when fetching a blob, the
+        * blob itself will still be sent. The client does not need to
+        * know whether a wanted object is a blob or not.)
+        *
         * 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).
diff --git a/fsck.c b/fsck.c
index a0cee0be590020e4ff4b42da86c322ba4d0010ae..0f56977d6aa6499c06e31eddb8ed4afe484fcdc4 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -67,6 +67,8 @@ static struct oidset gitmodules_done = OIDSET_INIT;
        FUNC(GITMODULES_LARGE, ERROR) \
        FUNC(GITMODULES_NAME, ERROR) \
        FUNC(GITMODULES_SYMLINK, ERROR) \
+       FUNC(GITMODULES_URL, ERROR) \
+       FUNC(GITMODULES_PATH, ERROR) \
        /* warnings */ \
        FUNC(BAD_FILEMODE, WARN) \
        FUNC(EMPTY_NAME, WARN) \
@@ -992,6 +994,18 @@ static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata)
                                    FSCK_MSG_GITMODULES_NAME,
                                    "disallowed submodule name: %s",
                                    name);
+       if (!strcmp(key, "url") && value &&
+           looks_like_command_line_option(value))
+               data->ret |= report(data->options, data->obj,
+                                   FSCK_MSG_GITMODULES_URL,
+                                   "disallowed submodule url: %s",
+                                   value);
+       if (!strcmp(key, "path") && value &&
+           looks_like_command_line_option(value))
+               data->ret |= report(data->options, data->obj,
+                                   FSCK_MSG_GITMODULES_PATH,
+                                   "disallowed submodule path: %s",
+                                   value);
        free(name);
 
        return 0;
diff --git a/graph.c b/graph.c
index e1f6d3bddb38aadd4e439aea30f542d286ecb98e..f53135485f565baf7a0de1a5e4cffe2a4b2b55bd 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -842,27 +842,55 @@ static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb)
 }
 
 /*
- * Draw an octopus merge and return the number of characters written.
+ * Draw the horizontal dashes of an octopus merge and return the number of
+ * characters written.
  */
 static int graph_draw_octopus_merge(struct git_graph *graph,
                                    struct strbuf *sb)
 {
        /*
-        * Here dashless_commits represents the number of parents
-        * which don't need to have dashes (because their edges fit
-        * neatly under the commit).
-        */
-       const int dashless_commits = 2;
-       int col_num, i;
-       int num_dashes =
-               ((graph->num_parents - dashless_commits) * 2) - 1;
-       for (i = 0; i < num_dashes; i++) {
-               col_num = (i / 2) + dashless_commits + graph->commit_index;
-               strbuf_write_column(sb, &graph->new_columns[col_num], '-');
+        * Here dashless_parents represents the number of parents which don't
+        * need to have dashes (the edges labeled "0" and "1").  And
+        * dashful_parents are the remaining ones.
+        *
+        * | *---.
+        * | |\ \ \
+        * | | | | |
+        * x 0 1 2 3
+        *
+        */
+       const int dashless_parents = 2;
+       int dashful_parents = graph->num_parents - dashless_parents;
+
+       /*
+        * Usually, we add one new column for each parent (like the diagram
+        * above) but sometimes the first parent goes into an existing column,
+        * like this:
+        *
+        * | *---.
+        * | |\ \ \
+        * |/ / / /
+        * x 0 1 2
+        *
+        * In which case the number of parents will be one greater than the
+        * number of added columns.
+        */
+       int added_cols = (graph->num_new_columns - graph->num_columns);
+       int parent_in_old_cols = graph->num_parents - added_cols;
+
+       /*
+        * In both cases, commit_index corresponds to the edge labeled "0".
+        */
+       int first_col = graph->commit_index + dashless_parents
+           - parent_in_old_cols;
+
+       int i;
+       for (i = 0; i < dashful_parents; i++) {
+               strbuf_write_column(sb, &graph->new_columns[i+first_col], '-');
+               strbuf_write_column(sb, &graph->new_columns[i+first_col],
+                                   i == dashful_parents-1 ? '.' : '-');
        }
-       col_num = (i / 2) + dashless_commits + graph->commit_index;
-       strbuf_write_column(sb, &graph->new_columns[col_num], '.');
-       return num_dashes + 1;
+       return 2 * dashful_parents;
 }
 
 static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
index 9b3e56e283ccb98c57ca8a7b85dcf48b05b15a4f..ecffc09be6ec5c1a9646ac152a899f3415f6321d 100644 (file)
@@ -19,6 +19,12 @@ void compute_assignment(int column_count, int row_count, int *cost,
        int *free_row, free_count = 0, saved_free_count, *pred, *col;
        int i, j, phase;
 
+       if (column_count < 2) {
+               memset(column2row, 0, sizeof(int) * column_count);
+               memset(row2column, 0, sizeof(int) * row_count);
+               return;
+       }
+
        memset(column2row, -1, sizeof(int) * column_count);
        memset(row2column, -1, sizeof(int) * row_count);
        ALLOC_ARRAY(v, column_count);
index 0e2800f7bb46bdf9e1713d210a5dc1c02fbbaa26..1936fee9e1c54e8c0dc049ba48ef1cd209967d31 100644 (file)
@@ -371,13 +371,12 @@ int ll_merge(mmbuffer_t *result_buf,
        if (!check)
                check = attr_check_initl("merge", "conflict-marker-size", NULL);
 
-       if (!git_check_attr(&the_index, path, check)) {
-               ll_driver_name = check->items[0].value;
-               if (check->items[1].value) {
-                       marker_size = atoi(check->items[1].value);
-                       if (marker_size <= 0)
-                               marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
-               }
+       git_check_attr(&the_index, path, check);
+       ll_driver_name = check->items[0].value;
+       if (check->items[1].value) {
+               marker_size = atoi(check->items[1].value);
+               if (marker_size <= 0)
+                       marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
        }
        driver = find_ll_merge_driver(ll_driver_name);
 
@@ -398,7 +397,8 @@ int ll_merge_marker_size(const char *path)
 
        if (!check)
                check = attr_check_initl("conflict-marker-size", NULL);
-       if (!git_check_attr(&the_index, path, check) && check->items[0].value) {
+       git_check_attr(&the_index, path, check);
+       if (check->items[0].value) {
                marker_size = atoi(check->items[0].value);
                if (marker_size <= 0)
                        marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
index f401c979f0ed907b40bfb509ab8f06ad2632c19e..35403ccc0d586c4732411a5813cb97761dddc435 100644 (file)
@@ -263,8 +263,8 @@ static inline int close_lock_file_gently(struct lock_file *lk)
  *   nobody else) to inspect the contents you wrote, while still
  *   holding the lock yourself.
  *
- * * `reopen_lock_file()` to reopen the lockfile. Make further updates
- *   to the contents.
+ * * `reopen_lock_file()` to reopen the lockfile, truncating the existing
+ *   contents. Write out the new contents.
  *
  * * `commit_lock_file()` to make the final version permanent.
  */
index e5243dbc542d786a645a9c038c855a6ca30acaba..1b5c25591814e5a7e30a78f1bece849a3fc3996e 100644 (file)
@@ -2239,7 +2239,7 @@ static struct dir_rename_entry *check_dir_renamed(const char *path,
 {
        char *temp = xstrdup(path);
        char *end;
-       struct dir_rename_entry *entry = NULL;;
+       struct dir_rename_entry *entry = NULL;
 
        while ((end = strrchr(temp, '/'))) {
                *end = '\0';
index 6ef87e5683aacdf738c86679712078988c0899fd..f73f609884ca0d9fdf3d4109b245981c4c073fcb 100644 (file)
@@ -148,6 +148,9 @@ void prepare_packing_data(struct packing_data *pdata)
                                             1U << OE_SIZE_BITS);
        pdata->oe_delta_size_limit = git_env_ulong("GIT_TEST_OE_DELTA_SIZE",
                                                   1UL << OE_DELTA_SIZE_BITS);
+#ifndef NO_PTHREADS
+       pthread_mutex_init(&pdata->lock, NULL);
+#endif
 }
 
 struct object_entry *packlist_alloc(struct packing_data *pdata,
index 62806ccc39ea31b425089f4f38121d81a02fe5dd..c34036166a20affd53785f58e74d270ee25e9209 100644 (file)
@@ -356,7 +356,7 @@ static inline unsigned long oe_delta_size(struct packing_data *pack,
                return e->delta_size_;
 
        /*
-        * pack->detla_size[] can't be NULL because oe_set_delta_size()
+        * pack->delta_size[] can't be NULL because oe_set_delta_size()
         * must have been called when a new delta is saved with
         * oe_set_delta().
         * If oe_delta() returns NULL (i.e. default state, which means
index 98cf5228f9e30fe5622bdfb4c54d996cb3153808..8ca29e92815608437ccb1fb9909e78d5b4989529 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -1304,6 +1304,9 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
 
        if (skip_prefix(placeholder, "(trailers", &arg)) {
                struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
+
+               opts.no_divider = 1;
+
                if (*arg == ':') {
                        arg++;
                        for (;;) {
index 7b1354d7590a70ecbd6e508bdd95eafd4793efcc..8f644f68b432e173dc31d5739944bbdfe9c4ad27 100644 (file)
@@ -337,7 +337,7 @@ static int is_racy_stat(const struct index_state *istate,
                );
 }
 
-static int is_racy_timestamp(const struct index_state *istate,
+int is_racy_timestamp(const struct index_state *istate,
                             const struct cache_entry *ce)
 {
        return (!S_ISGITLINK(ce->ce_mode) &&
index 0bccfceff2ae31200019838d9f2b67e13e32ef6f..3e8ee04d09b2271ea9b0a9a07de6ec932ad09387 100644 (file)
@@ -263,6 +263,8 @@ static int trailers_atom_parser(const struct ref_format *format, struct used_ato
        struct string_list params = STRING_LIST_INIT_DUP;
        int i;
 
+       atom->u.contents.trailer_opts.no_divider = 1;
+
        if (arg) {
                string_list_split(&params, arg, ',', -1);
                for (i = 0; i < params.nr; i++) {
diff --git a/refs.c b/refs.c
index de81c7be7ca8d3ca033b34a61f33b0bff069932f..9a318c8456c3fe8d157cf6c7c6aca5b635d75230 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1394,17 +1394,50 @@ struct ref_iterator *refs_ref_iterator_begin(
  * non-zero value, stop the iteration and return that value;
  * otherwise, return 0.
  */
+static int do_for_each_repo_ref(struct repository *r, const char *prefix,
+                               each_repo_ref_fn fn, int trim, int flags,
+                               void *cb_data)
+{
+       struct ref_iterator *iter;
+       struct ref_store *refs = get_main_ref_store(r);
+
+       if (!refs)
+               return 0;
+
+       iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
+
+       return do_for_each_repo_ref_iterator(r, iter, fn, cb_data);
+}
+
+struct do_for_each_ref_help {
+       each_ref_fn *fn;
+       void *cb_data;
+};
+
+static int do_for_each_ref_helper(struct repository *r,
+                                 const char *refname,
+                                 const struct object_id *oid,
+                                 int flags,
+                                 void *cb_data)
+{
+       struct do_for_each_ref_help *hp = cb_data;
+
+       return hp->fn(refname, oid, flags, hp->cb_data);
+}
+
 static int do_for_each_ref(struct ref_store *refs, const char *prefix,
                           each_ref_fn fn, int trim, int flags, void *cb_data)
 {
        struct ref_iterator *iter;
+       struct do_for_each_ref_help hp = { fn, cb_data };
 
        if (!refs)
                return 0;
 
        iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
 
-       return do_for_each_ref_iterator(iter, fn, cb_data);
+       return do_for_each_repo_ref_iterator(the_repository, iter,
+                                       do_for_each_ref_helper, &hp);
 }
 
 int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
@@ -1449,12 +1482,11 @@ int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
        return do_for_each_ref(refs, prefix, fn, 0, flag, cb_data);
 }
 
-int for_each_replace_ref(struct repository *r, each_ref_fn fn, void *cb_data)
+int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(get_main_ref_store(r),
-                              git_replace_ref_base, fn,
-                              strlen(git_replace_ref_base),
-                              DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
+       return do_for_each_repo_ref(r, git_replace_ref_base, fn,
+                                   strlen(git_replace_ref_base),
+                                   DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 }
 
 int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
@@ -2033,10 +2065,12 @@ int refs_verify_refname_available(struct ref_store *refs,
 int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
        struct ref_iterator *iter;
+       struct do_for_each_ref_help hp = { fn, cb_data };
 
        iter = refs->be->reflog_iterator_begin(refs);
 
-       return do_for_each_ref_iterator(iter, fn, cb_data);
+       return do_for_each_repo_ref_iterator(the_repository, iter,
+                                            do_for_each_ref_helper, &hp);
 }
 
 int for_each_reflog(each_ref_fn fn, void *cb_data)
diff --git a/refs.h b/refs.h
index bd52c1bbae3a68fe8ca8f9e6cae7cc54bdbf9852..6cc0397679fd55bfdfc72b7a7f4cbf3ee5d028a0 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -276,6 +276,16 @@ struct ref_transaction;
 typedef int each_ref_fn(const char *refname,
                        const struct object_id *oid, int flags, void *cb_data);
 
+/*
+ * The same as each_ref_fn, but also with a repository argument that
+ * contains the repository associated with the callback.
+ */
+typedef int each_repo_ref_fn(struct repository *r,
+                            const char *refname,
+                            const struct object_id *oid,
+                            int flags,
+                            void *cb_data);
+
 /*
  * The following functions invoke the specified callback function for
  * each reference indicated.  If the function ever returns a nonzero
@@ -309,7 +319,7 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,
 int for_each_tag_ref(each_ref_fn fn, void *cb_data);
 int for_each_branch_ref(each_ref_fn fn, void *cb_data);
 int for_each_remote_ref(each_ref_fn fn, void *cb_data);
-int for_each_replace_ref(struct repository *r, each_ref_fn fn, void *cb_data);
+int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data);
 int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
 int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
                         const char *prefix, void *cb_data);
index 2ac91ac3401c87108b9cdb4983acb7c165df82d9..629e00a122a7a867ae3350ce35469c2ceb1259a1 100644 (file)
@@ -407,15 +407,15 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
 
 struct ref_iterator *current_ref_iter = NULL;
 
-int do_for_each_ref_iterator(struct ref_iterator *iter,
-                            each_ref_fn fn, void *cb_data)
+int do_for_each_repo_ref_iterator(struct repository *r, struct ref_iterator *iter,
+                                 each_repo_ref_fn fn, void *cb_data)
 {
        int retval = 0, ok;
        struct ref_iterator *old_ref_iter = current_ref_iter;
 
        current_ref_iter = iter;
        while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
-               retval = fn(iter->refname, iter->oid, iter->flags, cb_data);
+               retval = fn(r, iter->refname, iter->oid, iter->flags, cb_data);
                if (retval) {
                        /*
                         * If ref_iterator_abort() returns ITER_ERROR,
index 04425d6d1e45e2401454e7b89b0ed4b3959926de..b425ae40b106eb639698d1bd4a52d0bd02345dee 100644 (file)
@@ -282,7 +282,7 @@ int refs_rename_ref_available(struct ref_store *refs,
  *
  *             // Access information about the current reference:
  *             if (!(iter->flags & REF_ISSYMREF))
- *                     printf("%s is %s\n", iter->refname, oid_to_hex(&iter->oid));
+ *                     printf("%s is %s\n", iter->refname, oid_to_hex(iter->oid));
  *
  *             // If you need to peel the reference:
  *             ref_iterator_peel(iter, &oid);
@@ -474,8 +474,9 @@ extern struct ref_iterator *current_ref_iter;
  * adapter between the callback style of reference iteration and the
  * iterator style.
  */
-int do_for_each_ref_iterator(struct ref_iterator *iter,
-                            each_ref_fn fn, void *cb_data);
+int do_for_each_repo_ref_iterator(struct repository *r,
+                                 struct ref_iterator *iter,
+                                 each_repo_ref_fn fn, void *cb_data);
 
 /*
  * Only include per-worktree refs in a do_for_each_ref*() iteration.
index fb28309e850518018667dc746c630acc1f497981..762a55a75f6d9d3c510bfbf9ff0d41bc0a1afb6e 100644 (file)
@@ -178,7 +178,7 @@ static int set_option(const char *name, const char *value)
                options.no_dependents = 1;
                return 0;
        } else if (!strcmp(name, "filter")) {
-               options.filter = xstrdup(value);;
+               options.filter = xstrdup(value);
                return 0;
        } else {
                return 1 /* unsupported */;
index 4ec77ce41848311a912256046bd2bf8dc9ee63c0..e295e87943102c2a1ad903aea1a634d34bf603a0 100644 (file)
@@ -6,7 +6,8 @@
 #include "repository.h"
 #include "commit.h"
 
-static int register_replace_ref(const char *refname,
+static int register_replace_ref(struct repository *r,
+                               const char *refname,
                                const struct object_id *oid,
                                int flag, void *cb_data)
 {
@@ -25,13 +26,13 @@ static int register_replace_ref(const char *refname,
        oidcpy(&repl_obj->replacement, oid);
 
        /* Register new object */
-       if (oidmap_put(the_repository->objects->replace_map, repl_obj))
+       if (oidmap_put(r->objects->replace_map, repl_obj))
                die(_("duplicate replace ref: %s"), refname);
 
        return 0;
 }
 
-static void prepare_replace_object(struct repository *r)
+void prepare_replace_object(struct repository *r)
 {
        if (r->objects->replace_map)
                return;
index 9345e105ddcaa38cf9e856df70d960245a3b696c..16528df942f79e3d7ce65d99e0103395d0c69f51 100644 (file)
@@ -10,6 +10,8 @@ struct replace_object {
        struct object_id replacement;
 };
 
+void prepare_replace_object(struct repository *r);
+
 /*
  * This internal function is only declared here for the benefit of
  * lookup_replace_object().  Please do not call it directly.
index c7787aa07f80f00589e3c4f4e4e3cc825c43044e..783d4dae2aa2d7a17c37dfac892e1acdeb4b1db8 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -533,7 +533,7 @@ static int check_one_conflict(int i, int *type)
        }
 
        *type = PUNTED;
-       while (ce_stage(active_cache[i]) == 1)
+       while (i < active_nr && ce_stage(active_cache[i]) == 1)
                i++;
 
        /* Only handle regular files with both stages #2 and #3 */
index de4dce600d00282655907e9014228791a352f813..e411802ebec37604a693993cb5e9f2df18dae895 100644 (file)
@@ -2133,7 +2133,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->limited = 1;
        } else if (!strcmp(arg, "--ignore-missing")) {
                revs->ignore_missing = 1;
-       } else if (!strcmp(arg, "--exclude-promisor-objects")) {
+       } else if (revs->allow_exclude_promisor_objects_opt &&
+                  !strcmp(arg, "--exclude-promisor-objects")) {
                if (fetch_if_missing)
                        BUG("exclude_promisor_objects can only be used when fetch_if_missing is 0");
                revs->exclude_promisor_objects = 1;
index 007278cc119fded9f97832d87ffe5d6b95a5ce41..701a4973ddfee9ad18713cc1419e741a54479d43 100644 (file)
@@ -127,6 +127,7 @@ struct rev_info {
                        tree_blobs_in_commit_order:1,
 
                        /* for internal use only */
+                       allow_exclude_promisor_objects_opt:1,
                        exclude_promisor_objects:1;
 
        /* Diff flags */
index 84b883c2132bb07bc7fa21434368d64664587b8b..d679cc267c39b9fb34f57078db5db12ed591b7e2 100644 (file)
@@ -380,7 +380,7 @@ static void child_err_spew(struct child_process *cmd, struct child_err *cerr)
        set_error_routine(old_errfn);
 }
 
-static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
+static int prepare_cmd(struct argv_array *out, const struct child_process *cmd)
 {
        if (!cmd->argv[0])
                BUG("command is empty");
@@ -403,16 +403,22 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
        /*
         * If there are no '/' characters in the command then perform a path
         * lookup and use the resolved path as the command to exec.  If there
-        * are no '/' characters or if the command wasn't found in the path,
-        * have exec attempt to invoke the command directly.
+        * are '/' characters, we have exec attempt to invoke the command
+        * directly.
         */
        if (!strchr(out->argv[1], '/')) {
                char *program = locate_in_PATH(out->argv[1]);
                if (program) {
                        free((char *)out->argv[1]);
                        out->argv[1] = program;
+               } else {
+                       argv_array_clear(out);
+                       errno = ENOENT;
+                       return -1;
                }
        }
+
+       return 0;
 }
 
 static char **prep_childenv(const char *const *deltaenv)
@@ -719,6 +725,12 @@ int start_command(struct child_process *cmd)
        struct child_err cerr;
        struct atfork_state as;
 
+       if (prepare_cmd(&argv, cmd) < 0) {
+               failed_errno = errno;
+               cmd->pid = -1;
+               goto end_of_spawn;
+       }
+
        if (pipe(notify_pipe))
                notify_pipe[0] = notify_pipe[1] = -1;
 
@@ -729,7 +741,6 @@ int start_command(struct child_process *cmd)
                set_cloexec(null_fd);
        }
 
-       prepare_cmd(&argv, cmd);
        childenv = prep_childenv(cmd->env);
        atfork_prepare(&as);
 
@@ -857,6 +868,8 @@ int start_command(struct child_process *cmd)
        argv_array_clear(&argv);
        free(childenv);
 }
+end_of_spawn:
+
 #else
 {
        int fhin = 0, fhout = 1, fherr = 2;
index dc2c58d464c14be033f4bcba2ab4332886ead327..24451bb6838cdc0f934c6300660e57aa9237e47c 100644 (file)
@@ -225,13 +225,16 @@ static const char *get_todo_path(const struct replay_opts *opts)
  * Returns 3 when sob exists within conforming footer as last entry
  */
 static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
-       int ignore_footer)
+       size_t ignore_footer)
 {
+       struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
        struct trailer_info info;
-       int i;
+       size_t i;
        int found_sob = 0, found_sob_last = 0;
 
-       trailer_info_get(&info, sb->buf);
+       opts.no_divider = 1;
+
+       trailer_info_get(&info, sb->buf, &opts);
 
        if (info.trailer_start == info.trailer_end)
                return 0;
@@ -899,7 +902,7 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
        if ((flags & ALLOW_EMPTY))
                argv_array_push(&cmd.args, "--allow-empty");
 
-       if (opts->allow_empty_message)
+       if (!(flags & EDIT_MSG))
                argv_array_push(&cmd.args, "--allow-empty-message");
 
        if (cmd.err == -1) {
@@ -1313,7 +1316,7 @@ static int try_to_commit(struct strbuf *msg, const char *author,
 
        if (cleanup != COMMIT_MSG_CLEANUP_NONE)
                strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
-       if (!opts->allow_empty_message && message_is_empty(msg, cleanup)) {
+       if ((flags & EDIT_MSG) && message_is_empty(msg, cleanup)) {
                res = 1; /* run 'git commit' to display error message */
                goto out;
        }
@@ -3607,9 +3610,20 @@ static int commit_staged_changes(struct replay_opts *opts,
                 * the commit message and if there was a squash, let the user
                 * edit it.
                 */
-               if (is_clean && !oidcmp(&head, &to_amend) &&
-                   opts->current_fixup_count > 0 &&
-                   file_exists(rebase_path_stopped_sha())) {
+               if (!is_clean || !opts->current_fixup_count)
+                       ; /* this is not the final fixup */
+               else if (oidcmp(&head, &to_amend) ||
+                        !file_exists(rebase_path_stopped_sha())) {
+                       /* was a final fixup or squash done manually? */
+                       if (!is_fixup(peek_command(todo_list, 0))) {
+                               unlink(rebase_path_fixup_msg());
+                               unlink(rebase_path_squash_msg());
+                               unlink(rebase_path_current_fixups());
+                               strbuf_reset(&opts->current_fixups);
+                               opts->current_fixup_count = 0;
+                       }
+               } else {
+                       /* we are in a fixup/squash chain */
                        const char *p = opts->current_fixups.buf;
                        int len = opts->current_fixups.len;
 
@@ -3828,7 +3842,7 @@ int sequencer_pick_revisions(struct replay_opts *opts)
        return res;
 }
 
-void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag)
+void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag)
 {
        unsigned no_dup_sob = flag & APPEND_SIGNOFF_DEDUP;
        struct strbuf sob = STRBUF_INIT;
@@ -4131,9 +4145,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                        struct object_id *oid = &parent->item->object.oid;
                        if (!oidset_contains(&interesting, oid))
                                continue;
-                       if (!oidset_contains(&child_seen, oid))
-                               oidset_insert(&child_seen, oid);
-                       else
+                       if (oidset_insert(&child_seen, oid))
                                label_oid(oid, "branch-point", &state);
                }
 
index c751c9d6e4f78e7d9e2700dcc3fb3157961fb049..c986bc825161f1f4702a0cd435c6d9705e3be2df 100644 (file)
@@ -90,7 +90,14 @@ int rearrange_squash(void);
 
 extern const char sign_off_header[];
 
-void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
+/*
+ * Append a signoff to the commit message in "msgbuf". The ignore_footer
+ * parameter specifies the number of bytes at the end of msgbuf that should
+ * not be considered at all. I.e., they are not checked for existing trailers,
+ * and the new signoff will be spliced into the buffer before those bytes.
+ */
+void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);
+
 void append_conflicts_hint(struct strbuf *msgbuf);
 int message_is_empty(const struct strbuf *sb,
                     enum commit_msg_cleanup_mode cleanup_mode);
index 97b74238483e00c3f07bd5ab0879eb84bf5c8dfa..03b86aec710c3afffcd989bd357d28241a0271a7 100644 (file)
@@ -1317,7 +1317,7 @@ int oid_object_info_extended(struct repository *r, const struct object_id *oid,
                         * TODO Pass a repository struct through fetch_object,
                         * such that arbitrary repositories work.
                         */
-                       fetch_object(repository_format_partial_clone, real->hash);
+                       fetch_objects(repository_format_partial_clone, real, 1);
                        already_retried = 1;
                        continue;
                }
@@ -2191,7 +2191,8 @@ static int check_stream_sha1(git_zstream *stream,
         * see the comment in unpack_sha1_rest for details.
         */
        while (total_read <= size &&
-              (status == Z_OK || status == Z_BUF_ERROR)) {
+              (status == Z_OK ||
+               (status == Z_BUF_ERROR && !stream->avail_out))) {
                stream->next_out = buf;
                stream->avail_out = sizeof(buf);
                if (size - total_read < stream->avail_out)
index dbe8a2a2906abf9b393eeca1cdad8c9425c3e4bb..c1b68533ca3d831afcef7fd655cce95d8db89547 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -246,6 +246,7 @@ static void check_shallow_file_for_update(struct repository *r)
 
 #define SEEN_ONLY 1
 #define VERBOSE   2
+#define QUICK 4
 
 struct write_shallow_data {
        struct strbuf *out;
@@ -260,7 +261,10 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
        const char *hex = oid_to_hex(&graft->oid);
        if (graft->nr_parent != -1)
                return 0;
-       if (data->flags & SEEN_ONLY) {
+       if (data->flags & QUICK) {
+               if (!has_object_file(&graft->oid))
+                       return 0;
+       } else if (data->flags & SEEN_ONLY) {
                struct commit *c = lookup_commit(the_repository, &graft->oid);
                if (!c || !(c->object.flags & SEEN)) {
                        if (data->flags & VERBOSE)
@@ -370,16 +374,23 @@ void advertise_shallow_grafts(int fd)
 
 /*
  * mark_reachable_objects() should have been run prior to this and all
- * reachable commits marked as "SEEN".
+ * reachable commits marked as "SEEN", except when quick_prune is non-zero,
+ * in which case lines are excised from the shallow file if they refer to
+ * commits that do not exist (any longer).
  */
-void prune_shallow(int show_only)
+void prune_shallow(unsigned options)
 {
        struct lock_file shallow_lock = LOCK_INIT;
        struct strbuf sb = STRBUF_INIT;
+       unsigned flags = SEEN_ONLY;
        int fd;
 
-       if (show_only) {
-               write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY | VERBOSE);
+       if (options & PRUNE_QUICK)
+               flags |= QUICK;
+
+       if (options & PRUNE_SHOW_ONLY) {
+               flags |= VERBOSE;
+               write_shallow_commits_1(&sb, 0, NULL, flags);
                strbuf_release(&sb);
                return;
        }
@@ -387,7 +398,7 @@ void prune_shallow(int show_only)
                                       git_path_shallow(the_repository),
                                       LOCK_DIE_ON_ERROR);
        check_shallow_file_for_update(the_repository);
-       if (write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY)) {
+       if (write_shallow_commits_1(&sb, 0, NULL, flags)) {
                if (write_in_full(fd, sb.buf, sb.len) < 0)
                        die_errno("failed to write to %s",
                                  get_lock_file_path(&shallow_lock));
index 84f067e10d2cec1d77ec1a51f0f38cc5a29dd89f..5820412dc5203032d1247575d79c6107a163295a 100644 (file)
@@ -111,7 +111,7 @@ static void mark_entry_for_delete(size_t pos, void *data)
                die("position for delete %d exceeds base index size %d",
                    (int)pos, istate->cache_nr);
        istate->cache[pos]->ce_flags |= CE_REMOVE;
-       istate->split_index->nr_deletions = 1;
+       istate->split_index->nr_deletions++;
 }
 
 static void replace_entry(size_t pos, void *data)
@@ -188,6 +188,30 @@ void merge_base_index(struct index_state *istate)
        si->saved_cache_nr = 0;
 }
 
+/*
+ * Compare most of the fields in two cache entries, i.e. all except the
+ * hashmap_entry and the name.
+ */
+static int compare_ce_content(struct cache_entry *a, struct cache_entry *b)
+{
+       const unsigned int ondisk_flags = CE_STAGEMASK | CE_VALID |
+                                         CE_EXTENDED_FLAGS;
+       unsigned int ce_flags = a->ce_flags;
+       unsigned int base_flags = b->ce_flags;
+       int ret;
+
+       /* only on-disk flags matter */
+       a->ce_flags &= ondisk_flags;
+       b->ce_flags &= ondisk_flags;
+       ret = memcmp(&a->ce_stat_data, &b->ce_stat_data,
+                    offsetof(struct cache_entry, name) -
+                    offsetof(struct cache_entry, ce_stat_data));
+       a->ce_flags = ce_flags;
+       b->ce_flags = base_flags;
+
+       return ret;
+}
+
 void prepare_to_write_split_index(struct index_state *istate)
 {
        struct split_index *si = init_split_index(istate);
@@ -207,38 +231,109 @@ void prepare_to_write_split_index(struct index_state *istate)
                 */
                for (i = 0; i < istate->cache_nr; i++) {
                        struct cache_entry *base;
-                       /* namelen is checked separately */
-                       const unsigned int ondisk_flags =
-                               CE_STAGEMASK | CE_VALID | CE_EXTENDED_FLAGS;
-                       unsigned int ce_flags, base_flags, ret;
                        ce = istate->cache[i];
-                       if (!ce->index)
+                       if (!ce->index) {
+                               /*
+                                * During simple update index operations this
+                                * is a cache entry that is not present in
+                                * the shared index.  It will be added to the
+                                * split index.
+                                *
+                                * However, it might also represent a file
+                                * that already has a cache entry in the
+                                * shared index, but a new index has just
+                                * been constructed by unpack_trees(), and
+                                * this entry now refers to different content
+                                * than what was recorded in the original
+                                * index, e.g. during 'read-tree -m HEAD^' or
+                                * 'checkout HEAD^'.  In this case the
+                                * original entry in the shared index will be
+                                * marked as deleted, and this entry will be
+                                * added to the split index.
+                                */
                                continue;
+                       }
                        if (ce->index > si->base->cache_nr) {
-                               ce->index = 0;
-                               continue;
+                               BUG("ce refers to a shared ce at %d, which is beyond the shared index size %d",
+                                   ce->index, si->base->cache_nr);
                        }
                        ce->ce_flags |= CE_MATCHED; /* or "shared" */
                        base = si->base->cache[ce->index - 1];
-                       if (ce == base)
+                       if (ce == base) {
+                               /* The entry is present in the shared index. */
+                               if (ce->ce_flags & CE_UPDATE_IN_BASE) {
+                                       /*
+                                        * Already marked for inclusion in
+                                        * the split index, either because
+                                        * the corresponding file was
+                                        * modified and the cached stat data
+                                        * was refreshed, or because there
+                                        * is already a replacement entry in
+                                        * the split index.
+                                        * Nothing more to do here.
+                                        */
+                               } else if (!ce_uptodate(ce) &&
+                                          is_racy_timestamp(istate, ce)) {
+                                       /*
+                                        * A racily clean cache entry stored
+                                        * only in the shared index: it must
+                                        * be added to the split index, so
+                                        * the subsequent do_write_index()
+                                        * can smudge its stat data.
+                                        */
+                                       ce->ce_flags |= CE_UPDATE_IN_BASE;
+                               } else {
+                                       /*
+                                        * The entry is only present in the
+                                        * shared index and it was not
+                                        * refreshed.
+                                        * Just leave it there.
+                                        */
+                               }
                                continue;
+                       }
                        if (ce->ce_namelen != base->ce_namelen ||
                            strcmp(ce->name, base->name)) {
                                ce->index = 0;
                                continue;
                        }
-                       ce_flags = ce->ce_flags;
-                       base_flags = base->ce_flags;
-                       /* only on-disk flags matter */
-                       ce->ce_flags   &= ondisk_flags;
-                       base->ce_flags &= ondisk_flags;
-                       ret = memcmp(&ce->ce_stat_data, &base->ce_stat_data,
-                                    offsetof(struct cache_entry, name) -
-                                    offsetof(struct cache_entry, ce_stat_data));
-                       ce->ce_flags = ce_flags;
-                       base->ce_flags = base_flags;
-                       if (ret)
+                       /*
+                        * This is the copy of a cache entry that is present
+                        * in the shared index, created by unpack_trees()
+                        * while it constructed a new index.
+                        */
+                       if (ce->ce_flags & CE_UPDATE_IN_BASE) {
+                               /*
+                                * Already marked for inclusion in the split
+                                * index, either because the corresponding
+                                * file was modified and the cached stat data
+                                * was refreshed, or because the original
+                                * entry already had a replacement entry in
+                                * the split index.
+                                * Nothing to do.
+                                */
+                       } else if (!ce_uptodate(ce) &&
+                                  is_racy_timestamp(istate, ce)) {
+                               /*
+                                * A copy of a racily clean cache entry from
+                                * the shared index.  It must be added to
+                                * the split index, so the subsequent
+                                * do_write_index() can smudge its stat data.
+                                */
                                ce->ce_flags |= CE_UPDATE_IN_BASE;
+                       } else {
+                               /*
+                                * Thoroughly compare the cached data to see
+                                * whether it should be marked for inclusion
+                                * in the split index.
+                                *
+                                * This comparison might be unnecessary, as
+                                * code paths modifying the cached data do
+                                * set CE_UPDATE_IN_BASE as well.
+                                */
+                               if (compare_ce_content(ce, base))
+                                       ce->ce_flags |= CE_UPDATE_IN_BASE;
+                       }
                        discard_cache_entry(base);
                        si->base->cache[ce->index - 1] = ce;
                }
index 771c4550980e24eb8bf9db14e27c49f983c1ed4b..1f6063f2a27812ee27b5d510dc066249198b48e3 100644 (file)
@@ -195,16 +195,6 @@ void string_list_clear_func(struct string_list *list, string_list_clear_func_t c
        list->nr = list->alloc = 0;
 }
 
-
-void print_string_list(const struct string_list *p, const char *text)
-{
-       int i;
-       if ( text )
-               printf("%s\n", text);
-       for (i = 0; i < p->nr; i++)
-               printf("%s:%p\n", p->items[i].string, p->items[i].util);
-}
-
 struct string_list_item *string_list_append_nodup(struct string_list *list,
                                                  char *string)
 {
index ff8f6094a3300d7bbdbb1a1ed58c8037daff5c7a..18c718c12ce84609ddf7d2baf471042bf9fafc7a 100644 (file)
@@ -113,14 +113,6 @@ typedef int (*string_list_each_func_t)(struct string_list_item *, void *);
 void filter_string_list(struct string_list *list, int free_util,
                        string_list_each_func_t want, void *cb_data);
 
-/**
- * Dump a string_list to stdout, useful mainly for debugging
- * purposes. It can take an optional header argument and it writes out
- * the string-pointer pairs of the string_list, each one in its own
- * line.
- */
-void print_string_list(const struct string_list *p, const char *text);
-
 /**
  * Free a string_list. The `string` pointer of the items will be freed
  * in case the `strdup_strings` member of the string_list is set. The
index fc2c41b947cb471deef42323c83f8b28f42780d6..0b2f1647abb935ba63e9230baf0ba7fcef95d19c 100644 (file)
@@ -384,6 +384,12 @@ static void warn_multiple_config(const struct object_id *treeish_name,
                        commit_string, name, option);
 }
 
+static void warn_command_line_option(const char *var, const char *value)
+{
+       warning(_("ignoring '%s' which may be interpreted as"
+                 " a command-line option: %s"), var, value);
+}
+
 struct parse_config_parameter {
        struct submodule_cache *cache;
        const struct object_id *treeish_name;
@@ -409,6 +415,8 @@ static int parse_config(const char *var, const char *value, void *data)
        if (!strcmp(item.buf, "path")) {
                if (!value)
                        ret = config_error_nonbool(var);
+               else if (looks_like_command_line_option(value))
+                       warn_command_line_option(var, value);
                else if (!me->overwrite && submodule->path)
                        warn_multiple_config(me->treeish_name, submodule->name,
                                        "path");
@@ -449,6 +457,8 @@ static int parse_config(const char *var, const char *value, void *data)
        } else if (!strcmp(item.buf, "url")) {
                if (!value) {
                        ret = config_error_nonbool(var);
+               } else if (looks_like_command_line_option(value)) {
+                       warn_command_line_option(var, value);
                } else if (!me->overwrite && submodule->url) {
                        warn_multiple_config(me->treeish_name, submodule->name,
                                        "url");
index a2b266fbfae2cd89b00a11008fbcd28bf09777e2..f27f14e86817607f7d11e376431d1b2864939aab 100644 (file)
@@ -65,8 +65,7 @@ int is_staging_gitmodules_ok(struct index_state *istate)
        if ((pos >= 0) && (pos < istate->cache_nr)) {
                struct stat st;
                if (lstat(GITMODULES_FILE, &st) == 0 &&
-                   ie_match_stat(istate, istate->cache[pos], &st,
-                                 CE_MATCH_IGNORE_FSMONITOR) & DATA_CHANGED)
+                   ie_match_stat(istate, istate->cache[pos], &st, 0) & DATA_CHANGED)
                        return 0;
        }
 
@@ -1880,7 +1879,7 @@ const char *get_superproject_working_tree(void)
                 * We're only interested in the name after the tab.
                 */
                super_sub = strchr(sb.buf, '\t') + 1;
-               super_sub_len = sb.buf + sb.len - super_sub - 1;
+               super_sub_len = strlen(super_sub);
 
                if (super_sub_len > cwd_len ||
                    strcmp(&cwd[cwd_len - super_sub_len], super_sub))
index 9028b47d923ca027a146da82060dae395d3f7999..0c31792d145817ec19d90325b80d7711ea3c11a4 100644 (file)
--- a/t/README
+++ b/t/README
@@ -154,6 +154,7 @@ appropriately before running "make".
        As the names depend on the tests' file names, it is safe to
        run the tests with this option in parallel.
 
+-V::
 --verbose-log::
        Write verbose output to the same logfile as `--tee`, but do
        _not_ write it to stdout. Unlike `--tee --verbose`, this option
index 2762ca656262baa9494d96a2647248f4042a2e01..6a84a53efbf6919c83d3f1fd73786acd92ee7abf 100644 (file)
@@ -15,7 +15,10 @@ static void test_parse_commit_in_graph(const char *gitdir, const char *worktree,
        struct commit *c;
        struct commit_list *parent;
 
-       repo_init(&r, gitdir, worktree);
+       setup_git_env(gitdir);
+
+       if (repo_init(&r, gitdir, worktree))
+               die("Couldn't init repo");
 
        c = lookup_commit(&r, commit_oid);
 
@@ -38,7 +41,10 @@ static void test_get_commit_tree_in_graph(const char *gitdir,
        struct commit *c;
        struct tree *tree;
 
-       repo_init(&r, gitdir, worktree);
+       setup_git_env(gitdir);
+
+       if (repo_init(&r, gitdir, worktree))
+               die("Couldn't init repo");
 
        c = lookup_commit(&r, commit_oid);
 
index 0edafcfd65db7586bc1521d2e1afa99fbde50292..db19131f719607e2beb29b1858822e5af7209189 100644 (file)
@@ -43,6 +43,9 @@ static struct test_cmd cmds[] = {
        { "subprocess", cmd__subprocess },
        { "urlmatch-normalization", cmd__urlmatch_normalization },
        { "wildmatch", cmd__wildmatch },
+#ifdef GIT_WINDOWS_NATIVE
+       { "windows-named-pipe", cmd__windows_named_pipe },
+#endif
        { "write-cache", cmd__write_cache },
 };
 
index e954e8c5222f77e882f577198091adde95cf2532..491400108608d4623704f30db0e4a8b5f3061dfe 100644 (file)
@@ -39,6 +39,9 @@ int cmd__submodule_config(int argc, const char **argv);
 int cmd__subprocess(int argc, const char **argv);
 int cmd__urlmatch_normalization(int argc, const char **argv);
 int cmd__wildmatch(int argc, const char **argv);
+#ifdef GIT_WINDOWS_NATIVE
+int cmd__windows_named_pipe(int argc, const char **argv);
+#endif
 int cmd__write_cache(int argc, const char **argv);
 
 #endif
diff --git a/t/helper/test-windows-named-pipe.c b/t/helper/test-windows-named-pipe.c
new file mode 100644 (file)
index 0000000..b4b752b
--- /dev/null
@@ -0,0 +1,72 @@
+#include "test-tool.h"
+#include "git-compat-util.h"
+#include "strbuf.h"
+
+#ifdef GIT_WINDOWS_NATIVE
+static const char *usage_string = "<pipe-filename>";
+
+#define TEST_BUFSIZE (4096)
+
+int cmd__windows_named_pipe(int argc, const char **argv)
+{
+       const char *filename;
+       struct strbuf pathname = STRBUF_INIT;
+       int err;
+       HANDLE h;
+       BOOL connected;
+       char buf[TEST_BUFSIZE + 1];
+
+       if (argc < 2)
+               goto print_usage;
+       filename = argv[1];
+       if (strchr(filename, '/') || strchr(filename, '\\'))
+               goto print_usage;
+       strbuf_addf(&pathname, "//./pipe/%s", filename);
+
+       /*
+        * Create a single instance of the server side of the named pipe.
+        * This will allow exactly one client instance to connect to it.
+        */
+       h = CreateNamedPipeA(
+               pathname.buf,
+               PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE,
+               PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
+               PIPE_UNLIMITED_INSTANCES,
+               TEST_BUFSIZE, TEST_BUFSIZE, 0, NULL);
+       if (h == INVALID_HANDLE_VALUE) {
+               err = err_win_to_posix(GetLastError());
+               fprintf(stderr, "CreateNamedPipe failed: %s\n",
+                       strerror(err));
+               return err;
+       }
+
+       connected = ConnectNamedPipe(h, NULL)
+               ? TRUE
+               : (GetLastError() == ERROR_PIPE_CONNECTED);
+       if (!connected) {
+               err = err_win_to_posix(GetLastError());
+               fprintf(stderr, "ConnectNamedPipe failed: %s\n",
+                       strerror(err));
+               CloseHandle(h);
+               return err;
+       }
+
+       while (1) {
+               DWORD nbr;
+               BOOL success = ReadFile(h, buf, TEST_BUFSIZE, &nbr, NULL);
+               if (!success || nbr == 0)
+                       break;
+               buf[nbr] = 0;
+
+               write(1, buf, nbr);
+       }
+
+       DisconnectNamedPipe(h);
+       CloseHandle(h);
+       return 0;
+
+print_usage:
+       fprintf(stderr, "usage: %s %s\n", argv[0], usage_string);
+       return 1;
+}
+#endif
diff --git a/t/t0051-windows-named-pipe.sh b/t/t0051-windows-named-pipe.sh
new file mode 100755 (executable)
index 0000000..10ac92d
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+test_description='Windows named pipes'
+
+. ./test-lib.sh
+
+test_expect_success MINGW 'o_append write to named pipe' '
+       GIT_TRACE="$(pwd)/expect" git status >/dev/null 2>&1 &&
+       { test-tool windows-named-pipe t0051 >actual 2>&1 & } &&
+       pid=$! &&
+       sleep 1 &&
+       GIT_TRACE=//./pipe/t0051 git status >/dev/null 2>warning &&
+       wait $pid &&
+       test_cmp expect actual
+'
+
+test_done
index 3e131c5325e363fa6188456e3f5f3f47f762801e..cf932c851411e5abd992005de474cc095abecead 100755 (executable)
@@ -12,10 +12,14 @@ cat >hello-script <<-EOF
        cat hello-script
 EOF
 
-test_expect_success 'start_command reports ENOENT' '
+test_expect_success 'start_command reports ENOENT (slash)' '
        test-tool run-command start-command-ENOENT ./does-not-exist
 '
 
+test_expect_success 'start_command reports ENOENT (no slash)' '
+       test-tool run-command start-command-ENOENT does-not-exist
+'
+
 test_expect_success 'run_command can run a command' '
        cat hello-script >hello.sh &&
        chmod +x hello.sh &&
@@ -25,6 +29,13 @@ test_expect_success 'run_command can run a command' '
        test_must_be_empty err
 '
 
+test_expect_success 'run_command is restricted to PATH' '
+       write_script should-not-run <<-\EOF &&
+       echo yikes
+       EOF
+       test_must_fail test-tool run-command run-command should-not-run
+'
+
 test_expect_success !MINGW 'run_command can run a script without a #! line' '
        cat >hello <<-\EOF &&
        cat hello-script
index 7de40141ca84dc53657d81c0254870b387339f79..504334e552251ed0c1f74536f6fd09ffccda615d 100755 (executable)
@@ -161,6 +161,24 @@ test_expect_success PERL 'commit --interactive gives cache-tree on partial commi
        test_cache_tree
 '
 
+test_expect_success PERL 'commit -p with shrinking cache-tree' '
+       mkdir -p deep/subdir &&
+       echo content >deep/subdir/file &&
+       git add deep &&
+       git commit -m add &&
+       git rm -r deep &&
+
+       before=$(wc -c <.git/index) &&
+       git commit -m delete -p &&
+       after=$(wc -c <.git/index) &&
+
+       # double check that the index shrank
+       test $before -gt $after &&
+
+       # and that our index was not corrupted
+       git fsck
+'
+
 test_expect_success 'commit in child dir has cache-tree' '
        mkdir dir &&
        >dir/child.t &&
@@ -243,13 +261,16 @@ test_expect_success 'no phantom error when switching trees' '
 '
 
 test_expect_success 'switching trees does not invalidate shared index' '
-       git update-index --split-index &&
-       >split &&
-       git add split &&
-       test-tool dump-split-index .git/index | grep -v ^own >before &&
-       git commit -m "as-is" &&
-       test-tool dump-split-index .git/index | grep -v ^own >after &&
-       test_cmp before after
+       (
+               sane_unset GIT_TEST_SPLIT_INDEX &&
+               git update-index --split-index &&
+               >split &&
+               git add split &&
+               test-tool dump-split-index .git/index | grep -v ^own >before &&
+               git commit -m "as-is" &&
+               test-tool dump-split-index .git/index | grep -v ^own >after &&
+               test_cmp before after
+       )
 '
 
 test_done
index 128130066499feb5bdad705b6fb0ef03bf446fe9..7762f89b3d4ab316539afe73067c8613536b4812 100755 (executable)
@@ -170,6 +170,59 @@ test_expect_success 'fetching of missing objects' '
        git verify-pack --verbose "$IDX" | grep "$HASH"
 '
 
+test_expect_success 'fetching of missing objects works with ref-in-want enabled' '
+       # ref-in-want requires protocol version 2
+       git -C server config protocol.version 2 &&
+       git -C server config uploadpack.allowrefinwant 1 &&
+       git -C repo config protocol.version 2 &&
+
+       rm -rf repo/.git/objects/* &&
+       rm -f trace &&
+       GIT_TRACE_PACKET="$(pwd)/trace" git -C repo cat-file -p "$HASH" &&
+       grep "git< fetch=.*ref-in-want" trace
+'
+
+test_expect_success 'fetching of missing blobs works' '
+       rm -rf server repo &&
+       test_create_repo server &&
+       test_commit -C server foo &&
+       git -C server repack -a -d --write-bitmap-index &&
+
+       git clone "file://$(pwd)/server" repo &&
+       git hash-object repo/foo.t >blobhash &&
+       rm -rf repo/.git/objects/* &&
+
+       git -C server config uploadpack.allowanysha1inwant 1 &&
+       git -C server config uploadpack.allowfilter 1 &&
+       git -C repo config core.repositoryformatversion 1 &&
+       git -C repo config extensions.partialclone "origin" &&
+
+       git -C repo cat-file -p $(cat blobhash)
+'
+
+test_expect_success 'fetching of missing trees does not fetch blobs' '
+       rm -rf server repo &&
+       test_create_repo server &&
+       test_commit -C server foo &&
+       git -C server repack -a -d --write-bitmap-index &&
+
+       git clone "file://$(pwd)/server" repo &&
+       git -C repo rev-parse foo^{tree} >treehash &&
+       git hash-object repo/foo.t >blobhash &&
+       rm -rf repo/.git/objects/* &&
+
+       git -C server config uploadpack.allowanysha1inwant 1 &&
+       git -C server config uploadpack.allowfilter 1 &&
+       git -C repo config core.repositoryformatversion 1 &&
+       git -C repo config extensions.partialclone "origin" &&
+       git -C repo cat-file -p $(cat treehash) &&
+
+       # Ensure that the tree, but not the blob, is fetched
+       git -C repo rev-list --objects --missing=print $(cat treehash) >objects &&
+       grep "^$(cat treehash)" objects &&
+       grep "^[?]$(cat blobhash)" objects
+'
+
 test_expect_success 'rev-list stops traversal at missing and promised commit' '
        rm -rf repo &&
        test_create_repo repo &&
index 7c8df2095574f66f6d434f7717228f6f76a2a908..b72beebe42ab18b64b53cf86c3879d04a17901e7 100755 (executable)
@@ -366,7 +366,7 @@ test_expect_success 'Query master@{2005-05-25} (before history)' '
        test_when_finished "rm -f o e" &&
        git rev-parse --verify master@{2005-05-25} >o 2>e &&
        test $C = $(cat o) &&
-       echo test "warning: Log for '\''master'\'' only goes back to $ed." = "$(cat e)"
+       test "warning: Log for '\''master'\'' only goes back to $ed." = "$(cat e)"
 '
 test_expect_success 'Query "master@{May 26 2005 23:31:59}" (1 second before history)' '
        test_when_finished "rm -f o e" &&
@@ -807,6 +807,37 @@ test_expect_success 'stdin delete symref works option no-deref' '
        test_cmp expect actual
 '
 
+test_expect_success 'stdin update symref works flag --no-deref' '
+       git symbolic-ref TESTSYMREFONE $b &&
+       git symbolic-ref TESTSYMREFTWO $b &&
+       cat >stdin <<-EOF &&
+       update TESTSYMREFONE $a $b
+       update TESTSYMREFTWO $a $b
+       EOF
+       git update-ref --no-deref --stdin <stdin &&
+       git rev-parse TESTSYMREFONE TESTSYMREFTWO >expect &&
+       git rev-parse $a $a >actual &&
+       test_cmp expect actual &&
+       git rev-parse $m~1 >expect &&
+       git rev-parse $b >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'stdin delete symref works flag --no-deref' '
+       git symbolic-ref TESTSYMREFONE $b &&
+       git symbolic-ref TESTSYMREFTWO $b &&
+       cat >stdin <<-EOF &&
+       delete TESTSYMREFONE $b
+       delete TESTSYMREFTWO $b
+       EOF
+       git update-ref --no-deref --stdin <stdin &&
+       test_must_fail git rev-parse --verify -q TESTSYMREFONE &&
+       test_must_fail git rev-parse --verify -q TESTSYMREFTWO &&
+       git rev-parse $m~1 >expect &&
+       git rev-parse $b >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'stdin delete ref works with right old value' '
        echo "delete $b $m~1" >stdin &&
        git update-ref --stdin <stdin &&
index 0f2dd26f74b4b2c19474ad563e7c34701838cb46..b5677d26a4268505139ec6cb7d65fe35db7debbe 100755 (executable)
@@ -673,16 +673,35 @@ test_expect_success 'fsck detects trailing loose garbage (commit)' '
        test_i18ngrep "garbage.*$commit" out
 '
 
-test_expect_success 'fsck detects trailing loose garbage (blob)' '
+test_expect_success 'fsck detects trailing loose garbage (large blob)' '
        blob=$(echo trailing | git hash-object -w --stdin) &&
        file=$(sha1_file $blob) &&
        test_when_finished "remove_object $blob" &&
        chmod +w "$file" &&
        echo garbage >>"$file" &&
-       test_must_fail git fsck 2>out &&
+       test_must_fail git -c core.bigfilethreshold=5 fsck 2>out &&
        test_i18ngrep "garbage.*$blob" out
 '
 
+test_expect_success 'fsck detects truncated loose object' '
+       # make it big enough that we know we will truncate in the data
+       # portion, not the header
+       test-tool genrandom truncate 4096 >file &&
+       blob=$(git hash-object -w file) &&
+       file=$(sha1_file $blob) &&
+       test_when_finished "remove_object $blob" &&
+       test_copy_bytes 1024 <"$file" >tmp &&
+       rm "$file" &&
+       mv -f tmp "$file" &&
+
+       # check both regular and streaming code paths
+       test_must_fail git fsck 2>out &&
+       test_i18ngrep corrupt.*$blob out &&
+
+       test_must_fail git -c core.bigfilethreshold=128 fsck 2>out &&
+       test_i18ngrep corrupt.*$blob out
+'
+
 # for each of type, we have one version which is referenced by another object
 # (and so while unreachable, not dangling), and another variant which really is
 # dangling.
index 5c715fe2cf6c7afe2c39e6a7197cdd3d33329b8b..01abee533dedfd1e2d8bd347d06fc5c0c8b7833a 100755 (executable)
@@ -141,6 +141,22 @@ test_expect_success 'showing the superproject correctly' '
        test_commit -C sub test_commit &&
        git -C super submodule add ../sub dir/sub &&
        echo $(pwd)/super >expect  &&
+       git -C super/dir/sub rev-parse --show-superproject-working-tree >out &&
+       test_cmp expect out &&
+
+       test_commit -C super submodule_add &&
+       git -C super checkout -b branch1 &&
+       git -C super/dir/sub checkout -b branch1 &&
+       test_commit -C super/dir/sub branch1_commit &&
+       git -C super add dir/sub &&
+       test_commit -C super branch1_commit &&
+       git -C super checkout -b branch2 master &&
+       git -C super/dir/sub checkout -b branch2 master &&
+       test_commit -C super/dir/sub branch2_commit &&
+       git -C super add dir/sub &&
+       test_commit -C super branch2_commit &&
+       test_must_fail git -C super merge branch1 &&
+
        git -C super/dir/sub rev-parse --show-superproject-working-tree >out &&
        test_cmp expect out
 '
index b3b4d83eafc4a031618a3a89fd8ca50353f1a844..f053bf83eb9f716c3dc99a3bdf1a8e3b49802163 100755 (executable)
@@ -6,8 +6,18 @@ test_description='split index mode tests'
 
 # We need total control of index splitting here
 sane_unset GIT_TEST_SPLIT_INDEX
+# A couple of tests expect the index to have a specific checksum,
+# but the presence of the optional FSMN extension would interfere
+# with those checks, so disable it in this test script.
 sane_unset GIT_FSMONITOR_TEST
 
+# Create a file named as $1 with content read from stdin.
+# Set the file's mtime to a few seconds in the past to avoid racy situations.
+create_non_racy_file () {
+       cat >"$1" &&
+       test-tool chmtime =-5 "$1"
+}
+
 test_expect_success 'enable split index' '
        git config splitIndex.maxPercentChange 100 &&
        git update-index --split-index &&
@@ -31,7 +41,7 @@ test_expect_success 'enable split index' '
 '
 
 test_expect_success 'add one file' '
-       : >one &&
+       create_non_racy_file one &&
        git update-index --add one &&
        git ls-files --stage >ls-files.actual &&
        cat >ls-files.expect <<-EOF &&
@@ -57,7 +67,7 @@ test_expect_success 'disable split index' '
        EOF
        test_cmp ls-files.expect ls-files.actual &&
 
-       BASE=$(test-tool dump-split-index .git/index | grep "^own" | sed "s/own/base/") &&
+       BASE=$(test-tool dump-split-index .git/index | sed -n "s/^own/base/p") &&
        test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
        cat >expect <<-EOF &&
        not a split index
@@ -83,7 +93,7 @@ test_expect_success 'enable split index again, "one" now belongs to base index"'
 '
 
 test_expect_success 'modify original file, base index untouched' '
-       echo modified >one &&
+       echo modified | create_non_racy_file one &&
        git update-index one &&
        git ls-files --stage >ls-files.actual &&
        cat >ls-files.expect <<-EOF &&
@@ -102,7 +112,7 @@ test_expect_success 'modify original file, base index untouched' '
 '
 
 test_expect_success 'add another file, which stays index' '
-       : >two &&
+       create_non_racy_file two &&
        git update-index --add two &&
        git ls-files --stage >ls-files.actual &&
        cat >ls-files.expect <<-EOF &&
@@ -155,7 +165,7 @@ test_expect_success 'remove file in base index' '
 '
 
 test_expect_success 'add original file back' '
-       : >one &&
+       create_non_racy_file one &&
        git update-index --add one &&
        git ls-files --stage >ls-files.actual &&
        cat >ls-files.expect <<-EOF &&
@@ -174,7 +184,7 @@ test_expect_success 'add original file back' '
 '
 
 test_expect_success 'add new file' '
-       : >two &&
+       create_non_racy_file two &&
        git update-index --add two &&
        git ls-files --stage >actual &&
        cat >expect <<-EOF &&
@@ -218,7 +228,7 @@ test_expect_success 'rev-parse --shared-index-path' '
 
 test_expect_success 'set core.splitIndex config variable to true' '
        git config core.splitIndex true &&
-       : >three &&
+       create_non_racy_file three &&
        git update-index --add three &&
        git ls-files --stage >ls-files.actual &&
        cat >ls-files.expect <<-EOF &&
@@ -253,9 +263,9 @@ test_expect_success 'set core.splitIndex config variable to false' '
        test_cmp expect actual
 '
 
-test_expect_success 'set core.splitIndex config variable to true' '
+test_expect_success 'set core.splitIndex config variable back to true' '
        git config core.splitIndex true &&
-       : >three &&
+       create_non_racy_file three &&
        git update-index --add three &&
        BASE=$(test-tool dump-split-index .git/index | grep "^base") &&
        test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
@@ -265,7 +275,7 @@ test_expect_success 'set core.splitIndex config variable to true' '
        deletions:
        EOF
        test_cmp expect actual &&
-       : >four &&
+       create_non_racy_file four &&
        git update-index --add four &&
        test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
        cat >expect <<-EOF &&
@@ -279,7 +289,7 @@ test_expect_success 'set core.splitIndex config variable to true' '
 
 test_expect_success 'check behavior with splitIndex.maxPercentChange unset' '
        git config --unset splitIndex.maxPercentChange &&
-       : >five &&
+       create_non_racy_file five &&
        git update-index --add five &&
        BASE=$(test-tool dump-split-index .git/index | grep "^base") &&
        test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
@@ -289,7 +299,7 @@ test_expect_success 'check behavior with splitIndex.maxPercentChange unset' '
        deletions:
        EOF
        test_cmp expect actual &&
-       : >six &&
+       create_non_racy_file six &&
        git update-index --add six &&
        test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
        cat >expect <<-EOF &&
@@ -303,7 +313,7 @@ test_expect_success 'check behavior with splitIndex.maxPercentChange unset' '
 
 test_expect_success 'check splitIndex.maxPercentChange set to 0' '
        git config splitIndex.maxPercentChange 0 &&
-       : >seven &&
+       create_non_racy_file seven &&
        git update-index --add seven &&
        BASE=$(test-tool dump-split-index .git/index | grep "^base") &&
        test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
@@ -313,7 +323,7 @@ test_expect_success 'check splitIndex.maxPercentChange set to 0' '
        deletions:
        EOF
        test_cmp expect actual &&
-       : >eight &&
+       create_non_racy_file eight &&
        git update-index --add eight &&
        BASE=$(test-tool dump-split-index .git/index | grep "^base") &&
        test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
@@ -326,17 +336,17 @@ test_expect_success 'check splitIndex.maxPercentChange set to 0' '
 '
 
 test_expect_success 'shared index files expire after 2 weeks by default' '
-       : >ten &&
+       create_non_racy_file ten &&
        git update-index --add ten &&
        test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
        just_under_2_weeks_ago=$((5-14*86400)) &&
        test-tool chmtime =$just_under_2_weeks_ago .git/sharedindex.* &&
-       : >eleven &&
+       create_non_racy_file eleven &&
        git update-index --add eleven &&
        test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
        just_over_2_weeks_ago=$((-1-14*86400)) &&
        test-tool chmtime =$just_over_2_weeks_ago .git/sharedindex.* &&
-       : >twelve &&
+       create_non_racy_file twelve &&
        git update-index --add twelve &&
        test $(ls .git/sharedindex.* | wc -l) -le 2
 '
@@ -344,12 +354,12 @@ test_expect_success 'shared index files expire after 2 weeks by default' '
 test_expect_success 'check splitIndex.sharedIndexExpire set to 16 days' '
        git config splitIndex.sharedIndexExpire "16.days.ago" &&
        test-tool chmtime =$just_over_2_weeks_ago .git/sharedindex.* &&
-       : >thirteen &&
+       create_non_racy_file thirteen &&
        git update-index --add thirteen &&
        test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
        just_over_16_days_ago=$((-1-16*86400)) &&
        test-tool chmtime =$just_over_16_days_ago .git/sharedindex.* &&
-       : >fourteen &&
+       create_non_racy_file fourteen &&
        git update-index --add fourteen &&
        test $(ls .git/sharedindex.* | wc -l) -le 2
 '
@@ -358,13 +368,13 @@ test_expect_success 'check splitIndex.sharedIndexExpire set to "never" and "now"
        git config splitIndex.sharedIndexExpire never &&
        just_10_years_ago=$((-365*10*86400)) &&
        test-tool chmtime =$just_10_years_ago .git/sharedindex.* &&
-       : >fifteen &&
+       create_non_racy_file fifteen &&
        git update-index --add fifteen &&
        test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
        git config splitIndex.sharedIndexExpire now &&
        just_1_second_ago=-1 &&
        test-tool chmtime =$just_1_second_ago .git/sharedindex.* &&
-       : >sixteen &&
+       create_non_racy_file sixteen &&
        git update-index --add sixteen &&
        test $(ls .git/sharedindex.* | wc -l) -le 2
 '
@@ -379,7 +389,7 @@ do
                # Create one new shared index file
                git config core.sharedrepository "$mode" &&
                git config core.splitIndex true &&
-               : >one &&
+               create_non_racy_file one &&
                git update-index --add one &&
                echo "$modebits" >expect &&
                test_modebits .git/index >actual &&
diff --git a/t/t1701-racy-split-index.sh b/t/t1701-racy-split-index.sh
new file mode 100755 (executable)
index 0000000..5dc221e
--- /dev/null
@@ -0,0 +1,214 @@
+#!/bin/sh
+
+# This test can give false success if your machine is sufficiently
+# slow or all trials happened to happen on second boundaries.
+
+test_description='racy split index'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       # Only split the index when the test explicitly says so.
+       sane_unset GIT_TEST_SPLIT_INDEX &&
+       git config splitIndex.maxPercentChange 100 &&
+
+       echo "cached content" >racy-file &&
+       git add racy-file &&
+       git commit -m initial &&
+
+       echo something >other-file &&
+       # No raciness with this file.
+       test-tool chmtime =-20 other-file &&
+
+       echo "+cached content" >expect
+'
+
+check_cached_diff () {
+       git diff-index --patch --cached $EMPTY_TREE racy-file >diff &&
+       tail -1 diff >actual &&
+       test_cmp expect actual
+}
+
+trials="0 1 2 3 4"
+for trial in $trials
+do
+       test_expect_success "split the index while adding a racily clean file #$trial" '
+               rm -f .git/index .git/sharedindex.* &&
+
+               # The next three commands must be run within the same
+               # second (so both writes to racy-file result in the same
+               # mtime) to create the interesting racy situation.
+               echo "cached content" >racy-file &&
+
+               # Update and split the index.  The cache entry of
+               # racy-file will be stored only in the shared index.
+               git update-index --split-index --add racy-file &&
+
+               # File size must stay the same.
+               echo "dirty worktree" >racy-file &&
+
+               # Subsequent git commands should notice that racy-file
+               # and the split index have the same mtime, and check
+               # the content of the file to see if it is actually
+               # clean.
+               check_cached_diff
+       '
+done
+
+for trial in $trials
+do
+       test_expect_success "add a racily clean file to an already split index #$trial" '
+               rm -f .git/index .git/sharedindex.* &&
+
+               git update-index --split-index &&
+
+               # The next three commands must be run within the same
+               # second.
+               echo "cached content" >racy-file &&
+
+               # Update the split index.  The cache entry of racy-file
+               # will be stored only in the split index.
+               git update-index --add racy-file &&
+
+               # File size must stay the same.
+               echo "dirty worktree" >racy-file &&
+
+               # Subsequent git commands should notice that racy-file
+               # and the split index have the same mtime, and check
+               # the content of the file to see if it is actually
+               # clean.
+               check_cached_diff
+       '
+done
+
+for trial in $trials
+do
+       test_expect_success "split the index when the index contains a racily clean cache entry #$trial" '
+               rm -f .git/index .git/sharedindex.* &&
+
+               # The next three commands must be run within the same
+               # second.
+               echo "cached content" >racy-file &&
+
+               git update-index --add racy-file &&
+
+               # File size must stay the same.
+               echo "dirty worktree" >racy-file &&
+
+               # Now wait a bit to ensure that the split index written
+               # below will get a more recent mtime than racy-file.
+               sleep 1 &&
+
+               # Update and split the index when the index contains
+               # the racily clean cache entry of racy-file.
+               # A corresponding replacement cache entry with smudged
+               # stat data should be added to the new split index.
+               git update-index --split-index --add other-file &&
+
+               # Subsequent git commands should notice the smudged
+               # stat data in the replacement cache entry and that it
+               # doesnt match with the file the worktree.
+               check_cached_diff
+       '
+done
+
+for trial in $trials
+do
+       test_expect_success "update the split index when it contains a new racily clean cache entry #$trial" '
+               rm -f .git/index .git/sharedindex.* &&
+
+               git update-index --split-index &&
+
+               # The next three commands must be run within the same
+               # second.
+               echo "cached content" >racy-file &&
+
+               # Update the split index.  The cache entry of racy-file
+               # will be stored only in the split index.
+               git update-index --add racy-file &&
+
+               # File size must stay the same.
+               echo "dirty worktree" >racy-file &&
+
+               # Now wait a bit to ensure that the split index written
+               # below will get a more recent mtime than racy-file.
+               sleep 1 &&
+
+               # Update the split index when the racily clean cache
+               # entry of racy-file is only stored in the split index.
+               # An updated cache entry with smudged stat data should
+               # be added to the new split index.
+               git update-index --add other-file &&
+
+               # Subsequent git commands should notice the smudged
+               # stat data.
+               check_cached_diff
+       '
+done
+
+for trial in $trials
+do
+       test_expect_success "update the split index when a racily clean cache entry is stored only in the shared index #$trial" '
+               rm -f .git/index .git/sharedindex.* &&
+
+               # The next three commands must be run within the same
+               # second.
+               echo "cached content" >racy-file &&
+
+               # Update and split the index.  The cache entry of
+               # racy-file will be stored only in the shared index.
+               git update-index --split-index --add racy-file &&
+
+               # File size must stay the same.
+               echo "dirty worktree" >racy-file &&
+
+               # Now wait a bit to ensure that the split index written
+               # below will get a more recent mtime than racy-file.
+               sleep 1 &&
+
+               # Update the split index when the racily clean cache
+               # entry of racy-file is only stored in the shared index.
+               # A corresponding replacement cache entry with smudged
+               # stat data should be added to the new split index.
+               git update-index --add other-file &&
+
+               # Subsequent git commands should notice the smudged
+               # stat data.
+               check_cached_diff
+       '
+done
+
+for trial in $trials
+do
+       test_expect_success "update the split index after unpack trees() copied a racily clean cache entry from the shared index #$trial" '
+               rm -f .git/index .git/sharedindex.* &&
+
+               # The next three commands must be run within the same
+               # second.
+               echo "cached content" >racy-file &&
+
+               # Update and split the index.  The cache entry of
+               # racy-file will be stored only in the shared index.
+               git update-index --split-index --add racy-file &&
+
+               # File size must stay the same.
+               echo "dirty worktree" >racy-file &&
+
+               # Now wait a bit to ensure that the split index written
+               # below will get a more recent mtime than racy-file.
+               sleep 1 &&
+
+               # Update the split index after unpack_trees() copied the
+               # racily clean cache entry of racy-file from the shared
+               # index.  A corresponding replacement cache entry
+               # with smudged stat data should be added to the new
+               # split index.
+               git read-tree -m HEAD &&
+
+               # Subsequent git commands should notice the smudged
+               # stat data.
+               check_cached_diff
+       '
+done
+
+test_done
index 2237c7f4af9464cbee087696ec3a86aeaa86e033..fb4c13a84ade63470f4dabc976ff5f5369c17e1e 100755 (executable)
@@ -142,4 +142,9 @@ test_expect_success 'changed message' '
        test_cmp expected actual
 '
 
+test_expect_success 'no commits on one side' '
+       git commit --amend -m "new message" &&
+       git range-diff master HEAD@{1} HEAD
+'
+
 test_done
index 86bba5ed7c23f78065521a9042244ea978a3a80f..ff89b6341a6fc19801959058e35039778bc6577b 100755 (executable)
@@ -569,16 +569,15 @@ test_expect_success '--continue tries to commit, even for "edit"' '
 '
 
 test_expect_success 'aborted --continue does not squash commits after "edit"' '
-       test_when_finished "git rebase --abort" &&
        old=$(git rev-parse HEAD) &&
        test_tick &&
        set_fake_editor &&
        FAKE_LINES="edit 1" git rebase -i HEAD^ &&
        echo "edited again" > file7 &&
        git add file7 &&
-       echo all the things >>conflict &&
-       test_must_fail git rebase --continue &&
-       test $old = $(git rev-parse HEAD)
+       test_must_fail env FAKE_COMMIT_MESSAGE=" " git rebase --continue &&
+       test $old = $(git rev-parse HEAD) &&
+       git rebase --abort
 '
 
 test_expect_success 'auto-amend only edited commits after "edit"' '
index da94dddc86d78830664d3a4b910ab157c2db2f0f..860e63e444d296c64773bae0d743fbb80fe674f1 100755 (executable)
@@ -83,7 +83,7 @@ test_expect_success 'rebase -m commit with empty message' '
 test_expect_success 'rebase -i commit with empty message' '
        git checkout diff-in-message &&
        set_fake_editor &&
-       env FAKE_COMMIT_MESSAGE=" " FAKE_LINES="reword 1" \
+       test_must_fail env FAKE_COMMIT_MESSAGE=" " FAKE_LINES="reword 1" \
                git rebase -i HEAD^
 '
 
index e364c12622f8566c3c99370de25ec5659a1e1546..13f5688135d853106702b7679eb302f6ae3d1450 100755 (executable)
@@ -330,4 +330,23 @@ test_expect_success 'wrapped original subject' '
        test $base = $parent
 '
 
+test_expect_success 'abort last squash' '
+       test_when_finished "test_might_fail git rebase --abort" &&
+       test_when_finished "git checkout master" &&
+
+       git checkout -b some-squashes &&
+       git commit --allow-empty -m first &&
+       git commit --allow-empty --squash HEAD &&
+       git commit --allow-empty -m second &&
+       git commit --allow-empty --squash HEAD &&
+
+       test_must_fail git -c core.editor="grep -q ^pick" \
+               rebase -ki --autosquash HEAD~4 &&
+       : do not finish the squash, but resolve it manually &&
+       git commit --allow-empty --amend -m edited-first &&
+       git rebase --skip &&
+       git show >actual &&
+       ! grep first actual
+'
+
 test_done
index fbdc47cfbdae6e3cec7d6762faa4a8e13554d803..5f911bb5290f33126380c45f3f5874d03f78a677 100755 (executable)
@@ -11,17 +11,14 @@ test_expect_success setup '
        test_tick &&
        git commit -m "first" &&
 
-       git checkout -b empty-branch &&
-       test_tick &&
-       git commit --allow-empty -m "empty" &&
-
+       git checkout -b empty-message-branch &&
        echo third >> file1 &&
        git add file1 &&
        test_tick &&
        git commit --allow-empty-message -m "" &&
 
        git checkout master &&
-       git checkout -b empty-branch2 &&
+       git checkout -b empty-change-branch &&
        test_tick &&
        git commit --allow-empty -m "empty"
 
@@ -29,7 +26,7 @@ test_expect_success setup '
 
 test_expect_success 'cherry-pick an empty commit' '
        git checkout master &&
-       test_expect_code 1 git cherry-pick empty-branch^
+       test_expect_code 1 git cherry-pick empty-change-branch
 '
 
 test_expect_success 'index lockfile was removed' '
@@ -37,8 +34,9 @@ test_expect_success 'index lockfile was removed' '
 '
 
 test_expect_success 'cherry-pick a commit with an empty message' '
+       test_when_finished "git reset --hard empty-message-branch~1" &&
        git checkout master &&
-       test_expect_code 1 git cherry-pick empty-branch
+       git cherry-pick empty-message-branch
 '
 
 test_expect_success 'index lockfile was removed' '
@@ -47,7 +45,7 @@ test_expect_success 'index lockfile was removed' '
 
 test_expect_success 'cherry-pick a commit with an empty message with --allow-empty-message' '
        git checkout -f master &&
-       git cherry-pick --allow-empty-message empty-branch
+       git cherry-pick --allow-empty-message empty-message-branch
 '
 
 test_expect_success 'cherry pick an empty non-ff commit without --allow-empty' '
@@ -55,12 +53,12 @@ test_expect_success 'cherry pick an empty non-ff commit without --allow-empty' '
        echo fourth >>file2 &&
        git add file2 &&
        git commit -m "fourth" &&
-       test_must_fail git cherry-pick empty-branch2
+       test_must_fail git cherry-pick empty-change-branch
 '
 
 test_expect_success 'cherry pick an empty non-ff commit with --allow-empty' '
        git checkout master &&
-       git cherry-pick --allow-empty empty-branch2
+       git cherry-pick --allow-empty empty-change-branch
 '
 
 test_expect_success 'cherry pick with --keep-redundant-commits' '
index 609fbfdc317137e420fab48e394f0095b0a1df69..65dfbc033a027df1a590cbfaf75ae47f4ed9c547 100755 (executable)
@@ -540,7 +540,7 @@ test_expect_success 'add -p does not expand argument lists' '
        # update it, but we want to be sure that our "." pathspec
        # was not expanded into the argument list of any command.
        # So look only for "not-changed".
-       ! grep not-changed trace.out
+       ! grep -E "^trace: (built-in|exec|run_command): .*not-changed" trace.out
 '
 
 test_expect_success 'hunk-editing handles custom comment char' '
index 453e6c35eb89fd82649401cb20a462c560a102cf..6e0dd6f9e5c482631147bb4770498dc4e7a8165f 100755 (executable)
@@ -127,4 +127,14 @@ test_expect_success 'diff --no-index from repo subdir respects config (implicit)
        test_cmp expect actual.head
 '
 
+test_expect_success 'diff --no-index from repo subdir with absolute paths' '
+       cat <<-EOF >expect &&
+       1       1       $(pwd)/non/git/{a => b}
+       EOF
+       test_expect_code 1 \
+               git -C repo/sub diff --numstat \
+               "$(pwd)/non/git/a" "$(pwd)/non/git/b" >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 65da74c76683c0ccd109fdec63766ab6bdfb08f0..313222d0d6251f33734b12758745999a79fe6bf2 100755 (executable)
@@ -577,4 +577,33 @@ test_expect_success 'multiple identical conflicts' '
        count_pre_post 0 0
 '
 
+test_expect_success 'setup simple stage 1 handling' '
+       test_create_repo stage_1_handling &&
+       (
+               cd stage_1_handling &&
+
+               test_seq 1 10 >original &&
+               git add original &&
+               git commit -m original &&
+
+               git checkout -b A master &&
+               git mv original A &&
+               git commit -m "rename to A" &&
+
+               git checkout -b B master &&
+               git mv original B &&
+               git commit -m "rename to B"
+       )
+'
+
+test_expect_success 'test simple stage 1 handling' '
+       (
+               cd stage_1_handling &&
+
+               git config rerere.enabled true &&
+               git checkout A^0 &&
+               test_must_fail git merge B^0
+       )
+'
+
 test_done
index 153a506151e2afe3f1e047cd5a00270f1378ce14..819c24d10eaa3cb4a58b72ae8a8e96151b5cabe9 100755 (executable)
@@ -1703,4 +1703,8 @@ test_expect_success 'log --source paints symmetric ranges' '
        test_cmp expect actual
 '
 
+test_expect_success '--exclude-promisor-objects does not BUG-crash' '
+       test_must_fail git log --exclude-promisor-objects source-a
+'
+
 test_done
index 2052cadb1109d3644b0c3f82b707f8e8bb35f945..978a8a66ff05055ad1967b5cc25f36880230a5e4 100755 (executable)
@@ -598,4 +598,27 @@ test_expect_success ':only and :unfold work together' '
        test_cmp expect actual
 '
 
+test_expect_success 'trailer parsing not fooled by --- line' '
+       git commit --allow-empty -F - <<-\EOF &&
+       this is the subject
+
+       This is the body. The message has a "---" line which would confuse a
+       message+patch parser. But here we know we have only a commit message,
+       so we get it right.
+
+       trailer: wrong
+       ---
+       This is more body.
+
+       trailer: right
+       EOF
+
+       {
+               echo "trailer: right" &&
+               echo
+       } >expect &&
+       git log --no-walk --format="%(trailers)" >actual &&
+       test_cmp expect actual
+'
+
 test_done
diff --git a/t/t4214-log-graph-octopus.sh b/t/t4214-log-graph-octopus.sh
new file mode 100755 (executable)
index 0000000..dab96c8
--- /dev/null
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+test_description='git log --graph of skewed left octopus merge.'
+
+. ./test-lib.sh
+
+test_expect_success 'set up merge history' '
+       cat >expect.uncolored <<-\EOF &&
+       * left
+       | *---.   octopus-merge
+       | |\ \ \
+       |/ / / /
+       | | | * 4
+       | | * | 3
+       | | |/
+       | * | 2
+       | |/
+       * | 1
+       |/
+       * initial
+       EOF
+       cat >expect.colors <<-\EOF &&
+       * left
+       <RED>|<RESET> *<BLUE>-<RESET><BLUE>-<RESET><MAGENTA>-<RESET><MAGENTA>.<RESET>   octopus-merge
+       <RED>|<RESET> <RED>|<RESET><YELLOW>\<RESET> <BLUE>\<RESET> <MAGENTA>\<RESET>
+       <RED>|<RESET><RED>/<RESET> <YELLOW>/<RESET> <BLUE>/<RESET> <MAGENTA>/<RESET>
+       <RED>|<RESET> <YELLOW>|<RESET> <BLUE>|<RESET> * 4
+       <RED>|<RESET> <YELLOW>|<RESET> * <MAGENTA>|<RESET> 3
+       <RED>|<RESET> <YELLOW>|<RESET> <MAGENTA>|<RESET><MAGENTA>/<RESET>
+       <RED>|<RESET> * <MAGENTA>|<RESET> 2
+       <RED>|<RESET> <MAGENTA>|<RESET><MAGENTA>/<RESET>
+       * <MAGENTA>|<RESET> 1
+       <MAGENTA>|<RESET><MAGENTA>/<RESET>
+       * initial
+       EOF
+       test_commit initial &&
+       for i in 1 2 3 4 ; do
+               git checkout master -b $i || return $?
+               # Make tag name different from branch name, to avoid
+               # ambiguity error when calling checkout.
+               test_commit $i $i $i tag$i || return $?
+       done &&
+       git checkout 1 -b merge &&
+       test_tick &&
+       git merge -m octopus-merge 1 2 3 4 &&
+       git checkout 1 -b L &&
+       test_commit left
+'
+
+test_expect_success 'log --graph with tricky octopus merge with colors' '
+       test_config log.graphColors red,green,yellow,blue,magenta,cyan &&
+       git log --color=always --graph --date-order --pretty=tformat:%s --all >actual.colors.raw &&
+       test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors &&
+       test_cmp expect.colors actual.colors
+'
+
+test_expect_success 'log --graph with tricky octopus merge, no color' '
+       git log --color=never --graph --date-order --pretty=tformat:%s --all >actual.raw &&
+       sed "s/ *\$//" actual.raw >actual &&
+       test_cmp expect.uncolored actual
+'
+
+# Repeat the previous two tests with "normal" octopus merge (i.e.,
+# without the first parent skewing to the "left" branch column).
+
+test_expect_success 'log --graph with normal octopus merge, no color' '
+       cat >expect.uncolored <<-\EOF &&
+       *---.   octopus-merge
+       |\ \ \
+       | | | * 4
+       | | * | 3
+       | | |/
+       | * | 2
+       | |/
+       * | 1
+       |/
+       * initial
+       EOF
+       git log --color=never --graph --date-order --pretty=tformat:%s merge >actual.raw &&
+       sed "s/ *\$//" actual.raw >actual &&
+       test_cmp expect.uncolored actual
+'
+
+test_expect_success 'log --graph with normal octopus merge with colors' '
+       cat >expect.colors <<-\EOF &&
+       *<YELLOW>-<RESET><YELLOW>-<RESET><BLUE>-<RESET><BLUE>.<RESET>   octopus-merge
+       <RED>|<RESET><GREEN>\<RESET> <YELLOW>\<RESET> <BLUE>\<RESET>
+       <RED>|<RESET> <GREEN>|<RESET> <YELLOW>|<RESET> * 4
+       <RED>|<RESET> <GREEN>|<RESET> * <BLUE>|<RESET> 3
+       <RED>|<RESET> <GREEN>|<RESET> <BLUE>|<RESET><BLUE>/<RESET>
+       <RED>|<RESET> * <BLUE>|<RESET> 2
+       <RED>|<RESET> <BLUE>|<RESET><BLUE>/<RESET>
+       * <BLUE>|<RESET> 1
+       <BLUE>|<RESET><BLUE>/<RESET>
+       * initial
+       EOF
+       test_config log.graphColors red,green,yellow,blue,magenta,cyan &&
+       git log --color=always --graph --date-order --pretty=tformat:%s merge >actual.colors.raw &&
+       test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors &&
+       test_cmp expect.colors actual.colors
+'
+test_done
index 0c500f7ca2641a2752f5d4819bb11efcf5f588bf..2799969f3e2a15fc636ed042c96d8a846e01ebe8 100755 (executable)
@@ -259,6 +259,66 @@ test_expect_success 'check that gc computes commit-graph' '
        test_cmp_bin commit-graph-after-gc $objdir/info/commit-graph
 '
 
+test_expect_success 'replace-objects invalidates commit-graph' '
+       cd "$TRASH_DIRECTORY" &&
+       test_when_finished rm -rf replace &&
+       git clone full replace &&
+       (
+               cd replace &&
+               git commit-graph write --reachable &&
+               test_path_is_file .git/objects/info/commit-graph &&
+               git replace HEAD~1 HEAD~2 &&
+               git -c core.commitGraph=false log >expect &&
+               git -c core.commitGraph=true log >actual &&
+               test_cmp expect actual &&
+               git commit-graph write --reachable &&
+               git -c core.commitGraph=false --no-replace-objects log >expect &&
+               git -c core.commitGraph=true --no-replace-objects log >actual &&
+               test_cmp expect actual &&
+               rm -rf .git/objects/info/commit-graph &&
+               git commit-graph write --reachable &&
+               test_path_is_file .git/objects/info/commit-graph
+       )
+'
+
+test_expect_success 'commit grafts invalidate commit-graph' '
+       cd "$TRASH_DIRECTORY" &&
+       test_when_finished rm -rf graft &&
+       git clone full graft &&
+       (
+               cd graft &&
+               git commit-graph write --reachable &&
+               test_path_is_file .git/objects/info/commit-graph &&
+               H1=$(git rev-parse --verify HEAD~1) &&
+               H3=$(git rev-parse --verify HEAD~3) &&
+               echo "$H1 $H3" >.git/info/grafts &&
+               git -c core.commitGraph=false log >expect &&
+               git -c core.commitGraph=true log >actual &&
+               test_cmp expect actual &&
+               git commit-graph write --reachable &&
+               git -c core.commitGraph=false --no-replace-objects log >expect &&
+               git -c core.commitGraph=true --no-replace-objects log >actual &&
+               test_cmp expect actual &&
+               rm -rf .git/objects/info/commit-graph &&
+               git commit-graph write --reachable &&
+               test_path_is_missing .git/objects/info/commit-graph
+       )
+'
+
+test_expect_success 'replace-objects invalidates commit-graph' '
+       cd "$TRASH_DIRECTORY" &&
+       test_when_finished rm -rf shallow &&
+       git clone --depth 2 "file://$TRASH_DIRECTORY/full" shallow &&
+       (
+               cd shallow &&
+               git commit-graph write --reachable &&
+               test_path_is_missing .git/objects/info/commit-graph &&
+               git fetch origin --unshallow &&
+               git commit-graph write --reachable &&
+               test_path_is_file .git/objects/info/commit-graph
+       )
+'
+
 # the verify tests below expect the commit-graph to contain
 # exactly the commits reachable from the commits/8 branch.
 # If the file changes the set of commits in the list, then the
diff --git a/t/t5321-pack-large-objects.sh b/t/t5321-pack-large-objects.sh
new file mode 100755 (executable)
index 0000000..a75eab8
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+# Copyright (c) 2018 Johannes Schindelin
+#
+
+test_description='git pack-object with "large" deltas
+
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-pack.sh
+
+# Two similar-ish objects that we have computed deltas between.
+A=01d7713666f4de822776c7622c10f1b07de280dc
+B=e68fe8129b546b101aee9510c5328e7f21ca1d18
+
+test_expect_success 'setup' '
+       clear_packs &&
+       {
+               pack_header 2 &&
+               pack_obj $A $B &&
+               pack_obj $B
+       } >ab.pack &&
+       pack_trailer ab.pack &&
+       git index-pack --stdin <ab.pack
+'
+
+test_expect_success 'repack large deltas' '
+       printf "%s\\n" $A $B |
+       GIT_TEST_OE_DELTA_SIZE=2 git pack-objects tmp-pack
+'
+
+test_done
index 241e6a319df4cefa612b6744c79e72e5cf929462..d2a2cdd453396b1dba5525f32b6325ac8a10d95d 100755 (executable)
@@ -145,7 +145,7 @@ test_expect_success 'remove remote protects local branches' '
 test_expect_success 'remove errors out early when deleting non-existent branch' '
        (
                cd test &&
-               echo "fatal: No such remote: foo" >expect &&
+               echo "fatal: No such remote: '\''foo'\''" >expect &&
                test_must_fail git remote rm foo 2>actual &&
                test_i18ncmp expect actual
        )
@@ -173,7 +173,7 @@ test_expect_success 'remove remote with a branch without configured merge' '
 test_expect_success 'rename errors out early when deleting non-existent branch' '
        (
                cd test &&
-               echo "fatal: No such remote: foo" >expect &&
+               echo "fatal: No such remote: '\''foo'\''" >expect &&
                test_must_fail git remote rename foo bar 2>actual &&
                test_i18ncmp expect actual
        )
index 539c25aadafdcf6aa9fbcce0988631702d3fb02d..bb0a36535c1a2f40d6d4cead5703af0f46b27301 100755 (executable)
@@ -1552,7 +1552,13 @@ test_expect_success 'receive.denyCurrentBranch = updateInstead' '
                test $(git -C .. rev-parse master) = $(git rev-parse HEAD) &&
                git diff --quiet &&
                git diff --cached --quiet
-       )
+       ) &&
+
+       # (6) updateInstead intervened by fast-forward check
+       test_must_fail git push void master^:master &&
+       test $(git -C void rev-parse HEAD) = $(git rev-parse master) &&
+       git -C void diff --quiet &&
+       git -C void diff --cached --quiet
 '
 
 test_expect_success 'updateInstead with push-to-checkout hook' '
index 7045685e2d3942161084ecd04a116faa83f35a44..6faf17e17a133f31245f89fd0fe9bad1687ac5fa 100755 (executable)
@@ -186,6 +186,33 @@ EOF
        test_cmp expect actual
 '
 
+test_expect_success '.git/shallow is edited by repack' '
+       git init shallow-server &&
+       test_commit -C shallow-server A &&
+       test_commit -C shallow-server B &&
+       git -C shallow-server checkout -b branch &&
+       test_commit -C shallow-server C &&
+       test_commit -C shallow-server E &&
+       test_commit -C shallow-server D &&
+       d="$(git -C shallow-server rev-parse --verify D^0)" &&
+       git -C shallow-server checkout master &&
+
+       git clone --depth=1 --no-tags --no-single-branch \
+               "file://$PWD/shallow-server" shallow-client &&
+
+       : now remove the branch and fetch with prune &&
+       git -C shallow-server branch -D branch &&
+       git -C shallow-client fetch --prune --depth=1 \
+               origin "+refs/heads/*:refs/remotes/origin/*" &&
+       git -C shallow-client repack -adfl &&
+       test_must_fail git -C shallow-client rev-parse --verify $d^0 &&
+       ! grep $d shallow-client/.git/shallow &&
+
+       git -C shallow-server branch branch-orig $d &&
+       git -C shallow-client fetch --prune --depth=2 \
+               origin "+refs/heads/*:refs/remotes/origin/*"
+'
+
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
index 771f36f9ff473d31507af374f1c96fac03192048..3dc8f8ecec2c03c63788bc5b0e9db88e55deb823 100755 (executable)
@@ -23,26 +23,26 @@ test_expect_success 'create http-accessible bare repository' '
 
 setup_askpass_helper
 
-cat >exp <<EOF
-> GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1
-> Accept: */*
-> Accept-Encoding: ENCODINGS
-> Pragma: no-cache
-< HTTP/1.1 200 OK
-< Pragma: no-cache
-< Cache-Control: no-cache, max-age=0, must-revalidate
-< Content-Type: application/x-git-upload-pack-advertisement
-> POST /smart/repo.git/git-upload-pack HTTP/1.1
-> Accept-Encoding: ENCODINGS
-> Content-Type: application/x-git-upload-pack-request
-> Accept: application/x-git-upload-pack-result
-> Content-Length: xxx
-< HTTP/1.1 200 OK
-< Pragma: no-cache
-< Cache-Control: no-cache, max-age=0, must-revalidate
-< Content-Type: application/x-git-upload-pack-result
-EOF
 test_expect_success 'clone http repository' '
+       cat >exp <<-\EOF &&
+       > GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1
+       > Accept: */*
+       > Accept-Encoding: ENCODINGS
+       > Pragma: no-cache
+       < HTTP/1.1 200 OK
+       < Pragma: no-cache
+       < Cache-Control: no-cache, max-age=0, must-revalidate
+       < Content-Type: application/x-git-upload-pack-advertisement
+       > POST /smart/repo.git/git-upload-pack HTTP/1.1
+       > Accept-Encoding: ENCODINGS
+       > Content-Type: application/x-git-upload-pack-request
+       > Accept: application/x-git-upload-pack-result
+       > Content-Length: xxx
+       < HTTP/1.1 200 OK
+       < Pragma: no-cache
+       < Cache-Control: no-cache, max-age=0, must-revalidate
+       < Content-Type: application/x-git-upload-pack-result
+       EOF
        GIT_TRACE_CURL=true git clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&
        test_cmp file clone/file &&
        tr '\''\015'\'' Q <err |
@@ -96,13 +96,13 @@ test_expect_success 'fetch changes via http' '
        test_cmp file clone/file
 '
 
-cat >exp <<EOF
-GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
-POST /smart/repo.git/git-upload-pack HTTP/1.1 200
-GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
-POST /smart/repo.git/git-upload-pack HTTP/1.1 200
-EOF
 test_expect_success 'used upload-pack service' '
+       cat >exp <<-\EOF &&
+       GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+       POST /smart/repo.git/git-upload-pack HTTP/1.1 200
+       GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+       POST /smart/repo.git/git-upload-pack HTTP/1.1 200
+       EOF
        check_access_log exp
 '
 
@@ -203,19 +203,19 @@ test_expect_success 'dumb clone via http-backend respects namespace' '
        test_cmp expect actual
 '
 
-cat >cookies.txt <<EOF
-127.0.0.1      FALSE   /smart_cookies/ FALSE   0       othername       othervalue
-EOF
-cat >expect_cookies.txt <<EOF
-
-127.0.0.1      FALSE   /smart_cookies/ FALSE   0       othername       othervalue
-127.0.0.1      FALSE   /smart_cookies/repo.git/info/   FALSE   0       name    value
-EOF
 test_expect_success 'cookies stored in http.cookiefile when http.savecookies set' '
+       cat >cookies.txt <<-\EOF &&
+       127.0.0.1       FALSE   /smart_cookies/ FALSE   0       othername       othervalue
+       EOF
+       sort >expect_cookies.txt <<-\EOF &&
+
+       127.0.0.1       FALSE   /smart_cookies/ FALSE   0       othername       othervalue
+       127.0.0.1       FALSE   /smart_cookies/repo.git/info/   FALSE   0       name    value
+       EOF
        git config http.cookiefile cookies.txt &&
        git config http.savecookies true &&
        git ls-remote $HTTPD_URL/smart_cookies/repo.git master &&
-       tail -3 cookies.txt >cookies_tail.txt &&
+       tail -3 cookies.txt | sort >cookies_tail.txt &&
        test_cmp expect_cookies.txt cookies_tail.txt
 '
 
index 77b8cef66198a870f669441857207af7142c0fbb..e436a7396241e6aad71a5f62500ffe0b9d7c0b7e 100755 (executable)
@@ -166,7 +166,7 @@ test_expect_success 'fail if attr magic is used places not implemented' '
        # though, but git-add is convenient as it has its own internal pathspec
        # parsing.
        test_must_fail git add ":(attr:labelB)" 2>actual &&
-       test_i18ngrep "unsupported magic" actual
+       test_i18ngrep "magic not supported" actual
 '
 
 test_expect_success 'abort on giving invalid label on the command line' '
index 024f8c06f7c58a424204d6fca69021ace2de3cbf..97bfbee6e8d69e46bd1ef1c94dae32d64be977b2 100755 (executable)
@@ -715,6 +715,29 @@ test_expect_success 'basic atom: head contents:trailers' '
        test_cmp expect actual.clean
 '
 
+test_expect_success 'trailer parsing not fooled by --- line' '
+       git commit --allow-empty -F - <<-\EOF &&
+       this is the subject
+
+       This is the body. The message has a "---" line which would confuse a
+       message+patch parser. But here we know we have only a commit message,
+       so we get it right.
+
+       trailer: wrong
+       ---
+       This is more body.
+
+       trailer: right
+       EOF
+
+       {
+               echo "trailer: right" &&
+               echo
+       } >expect &&
+       git for-each-ref --format="%(trailers)" refs/heads/master >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'Add symbolic ref for the following tests' '
        git symbolic-ref refs/heads/sym refs/heads/master
 '
diff --git a/t/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh
new file mode 100755 (executable)
index 0000000..1cd2c1c
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='check handling of .gitmodule url with dash'
+. ./test-lib.sh
+
+test_expect_success 'create submodule with protected dash in url' '
+       git init upstream &&
+       git -C upstream commit --allow-empty -m base &&
+       mv upstream ./-upstream &&
+       git submodule add ./-upstream sub &&
+       git add sub .gitmodules &&
+       git commit -m submodule
+'
+
+test_expect_success 'clone can recurse submodule' '
+       test_when_finished "rm -rf dst" &&
+       git clone --recurse-submodules . dst &&
+       echo base >expect &&
+       git -C dst/sub log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'fsck accepts protected dash' '
+       test_when_finished "rm -rf dst" &&
+       git init --bare dst &&
+       git -C dst config transfer.fsckObjects true &&
+       git push dst HEAD
+'
+
+test_expect_success 'remove ./ protection from .gitmodules url' '
+       perl -i -pe "s{\./}{}" .gitmodules &&
+       git commit -am "drop protection"
+'
+
+test_expect_success 'clone rejects unprotected dash' '
+       test_when_finished "rm -rf dst" &&
+       test_must_fail git clone --recurse-submodules . dst 2>err &&
+       test_i18ngrep ignoring err
+'
+
+test_expect_success 'fsck rejects unprotected dash' '
+       test_when_finished "rm -rf dst" &&
+       git init --bare dst &&
+       git -C dst config transfer.fsckObjects true &&
+       test_must_fail git push dst HEAD 2>err &&
+       grep gitmodulesUrl err
+'
+
+test_done
diff --git a/t/t7417-submodule-path-url.sh b/t/t7417-submodule-path-url.sh
new file mode 100755 (executable)
index 0000000..756af8c
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+test_description='check handling of .gitmodule path with dash'
+. ./test-lib.sh
+
+test_expect_success 'create submodule with dash in path' '
+       git init upstream &&
+       git -C upstream commit --allow-empty -m base &&
+       git submodule add ./upstream sub &&
+       git mv sub ./-sub &&
+       git commit -m submodule
+'
+
+test_expect_success 'clone rejects unprotected dash' '
+       test_when_finished "rm -rf dst" &&
+       git clone --recurse-submodules . dst 2>err &&
+       test_i18ngrep ignoring err
+'
+
+test_expect_success 'fsck rejects unprotected dash' '
+       test_when_finished "rm -rf dst" &&
+       git init --bare dst &&
+       git -C dst config transfer.fsckObjects true &&
+       test_must_fail git push dst HEAD 2>err &&
+       grep gitmodulesPath err
+'
+
+test_done
index 170b4810e052cc8092cea4ead4f3c098707f0f38..31ab608b6700829427b5aeafa65222469e494d26 100755 (executable)
@@ -359,4 +359,27 @@ test_expect_success 'new line found before status message in commit template' '
        test_i18ncmp expected-template editor-input
 '
 
+test_expect_success 'setup empty commit with unstaged rename and copy' '
+       test_create_repo unstaged_rename_and_copy &&
+       (
+               cd unstaged_rename_and_copy &&
+
+               echo content >orig &&
+               git add orig &&
+               test_commit orig &&
+
+               cp orig new_copy &&
+               mv orig new_rename &&
+               git add -N new_copy new_rename
+       )
+'
+
+test_expect_success 'check commit with unstaged rename and copy' '
+       (
+               cd unstaged_rename_and_copy &&
+
+               test_must_fail git -c diff.renames=copy commit
+       )
+'
+
 test_done
index 4cae92804d11f75f24bdd6f6517a88c834e7cfc7..1a6773ee6889939a0046664bd8a7cdcc4f21fe01 100755 (executable)
@@ -517,6 +517,22 @@ Myfooter: x" &&
        test_cmp expected actual
 '
 
+test_expect_success 'signoff not confused by ---' '
+       cat >expected <<-EOF &&
+               subject
+
+               body
+               ---
+               these dashes confuse the parser!
+
+               Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+       EOF
+       # should be a noop, since we already signed
+       git commit --allow-empty --signoff -F expected &&
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'multiple -m' '
 
        >negative &&
index 164719d1c9d3e76a08bbbeb968aaf535370db7d1..c44186133147838d7f17c4d42f8cb96a5df73b28 100755 (executable)
@@ -1417,4 +1417,46 @@ test_expect_success 'unfold' '
        test_cmp expected actual
 '
 
+test_expect_success 'handling of --- lines in input' '
+       echo "real-trailer: just right" >expected &&
+
+       git interpret-trailers --parse >actual <<-\EOF &&
+       subject
+
+       body
+
+       not-a-trailer: too soon
+       ------ this is just a line in the commit message with a bunch of
+       ------ dashes; it does not have any syntactic meaning.
+
+       real-trailer: just right
+       ---
+       below the dashed line may be a patch, etc.
+
+       not-a-trailer: too late
+       EOF
+
+       test_cmp expected actual
+'
+
+test_expect_success 'suppress --- handling' '
+       echo "real-trailer: just right" >expected &&
+
+       git interpret-trailers --parse --no-divider >actual <<-\EOF &&
+       subject
+
+       This commit message has a "---" in it, but because we tell
+       interpret-trailers not to respect that, it has no effect.
+
+       not-a-trailer: too soon
+       ---
+
+       This is still the commit message body.
+
+       real-trailer: just right
+       EOF
+
+       test_cmp expected actual
+'
+
 test_done
index 380e1c1054de5d55a80cb558fea0791884ad7f38..eea048e52ceb328fd5d97b17e996c8f821ae4abb 100755 (executable)
@@ -118,4 +118,8 @@ test_expect_success '--no-abbrev works like --abbrev=40' '
        check_abbrev 40 --no-abbrev
 '
 
+test_expect_success '--exclude-promisor-objects does not BUG-crash' '
+       test_must_fail git blame --exclude-promisor-objects one
+'
+
 test_done
index 44288cbb598435a5dfff05e8e895cca85e53e804..1373fd4592874237d3ee5575c0398a6f00b56369 100644 (file)
@@ -67,7 +67,7 @@ case "$GIT_TEST_TEE_STARTED, $* " in
 done,*)
        # do not redirect again
        ;;
-*' --tee '*|*' --va'*|*' --verbose-log '*)
+*' --tee '*|*' --va'*|*' -V '*|*' --verbose-log '*)
        mkdir -p "$TEST_OUTPUT_DIRECTORY/test-results"
        BASE="$TEST_OUTPUT_DIRECTORY/test-results/$(basename "$0" .sh)"
 
@@ -285,7 +285,7 @@ do
                        echo >&2 "warning: ignoring -x; '$0' is untraceable without BASH_XTRACEFD"
                fi
                shift ;;
-       --verbose-log)
+       -V|--verbose-log)
                verbose_log=t
                shift ;;
        *)
index 139ecd97f8eb88b597aab50c2eb2b171a11ef3ef..d43ad8c1912d977183270fabe7ac76c5bbb7a1a3 100644 (file)
@@ -279,7 +279,7 @@ int reopen_tempfile(struct tempfile *tempfile)
                BUG("reopen_tempfile called for an inactive object");
        if (0 <= tempfile->fd)
                BUG("reopen_tempfile called for an open object");
-       tempfile->fd = open(tempfile->filename.buf, O_WRONLY);
+       tempfile->fd = open(tempfile->filename.buf, O_WRONLY|O_TRUNC);
        return tempfile->fd;
 }
 
index 36434eb6fa64721bcafdb40db5f9e7d0400a50b4..61d8dc4d1bb2fc446ac7b452661064e615b4b5c9 100644 (file)
@@ -236,8 +236,8 @@ extern int close_tempfile_gently(struct tempfile *tempfile);
  *   it (and nobody else) to inspect or even modify the file's
  *   contents.
  *
- * * `reopen_tempfile()` to reopen the temporary file. Make further
- *   updates to the contents.
+ * * `reopen_tempfile()` to reopen the temporary file, truncating the existing
+ *   contents. Write out the new contents.
  *
  * * `rename_tempfile()` to move the file to its permanent location.
  */
index 4e309460d1367a7b35b61c2391525a165a3645e3..0796f326b36bac334cbf7ca5162cb22c4b62d1e8 100644 (file)
--- a/trailer.c
+++ b/trailer.c
@@ -585,7 +585,7 @@ static const char *token_from_item(struct arg_item *item, char *tok)
        return item->conf.name;
 }
 
-static int token_matches_item(const char *tok, struct arg_item *item, int tok_len)
+static int token_matches_item(const char *tok, struct arg_item *item, size_t tok_len)
 {
        if (!strncasecmp(tok, item->conf.name, tok_len))
                return 1;
@@ -603,7 +603,7 @@ static int token_matches_item(const char *tok, struct arg_item *item, int tok_le
  * distinguished from the non-well-formed-line case (in which this function
  * returns -1) because some callers of this function need such a distinction.
  */
-static int find_separator(const char *line, const char *separators)
+static ssize_t find_separator(const char *line, const char *separators)
 {
        int whitespace_found = 0;
        const char *c;
@@ -630,10 +630,10 @@ static int find_separator(const char *line, const char *separators)
  */
 static void parse_trailer(struct strbuf *tok, struct strbuf *val,
                         const struct conf_info **conf, const char *trailer,
-                        int separator_pos)
+                        ssize_t separator_pos)
 {
        struct arg_item *item;
-       int tok_len;
+       size_t tok_len;
        struct list_head *pos;
 
        if (separator_pos != -1) {
@@ -721,7 +721,7 @@ static void process_command_line_args(struct list_head *arg_head,
        list_for_each(pos, new_trailer_head) {
                struct new_trailer_item *tr =
                        list_entry(pos, struct new_trailer_item, list);
-               int separator_pos = find_separator(tr->text, cl_separators);
+               ssize_t separator_pos = find_separator(tr->text, cl_separators);
 
                if (separator_pos == 0) {
                        struct strbuf sb = STRBUF_INIT;
@@ -763,9 +763,9 @@ static const char *next_line(const char *str)
 /*
  * Return the position of the start of the last line. If len is 0, return -1.
  */
-static int last_line(const char *buf, size_t len)
+static ssize_t last_line(const char *buf, size_t len)
 {
-       int i;
+       ssize_t i;
        if (len == 0)
                return -1;
        if (len == 1)
@@ -788,12 +788,14 @@ static int last_line(const char *buf, size_t len)
  * Return the position of the start of the patch or the length of str if there
  * is no patch in the message.
  */
-static int find_patch_start(const char *str)
+static size_t find_patch_start(const char *str)
 {
        const char *s;
 
        for (s = str; *s; s = next_line(s)) {
-               if (starts_with(s, "---"))
+               const char *v;
+
+               if (skip_prefix(s, "---", &v) && isspace(*v))
                        return s - str;
        }
 
@@ -804,10 +806,11 @@ static int find_patch_start(const char *str)
  * Return the position of the first trailer line or len if there are no
  * trailers.
  */
-static int find_trailer_start(const char *buf, size_t len)
+static size_t find_trailer_start(const char *buf, size_t len)
 {
        const char *s;
-       int end_of_title, l, only_spaces = 1;
+       ssize_t end_of_title, l;
+       int only_spaces = 1;
        int recognized_prefix = 0, trailer_lines = 0, non_trailer_lines = 0;
        /*
         * Number of possible continuation lines encountered. This will be
@@ -838,7 +841,7 @@ static int find_trailer_start(const char *buf, size_t len)
             l = last_line(buf, l)) {
                const char *bol = buf + l;
                const char **p;
-               int separator_pos;
+               ssize_t separator_pos;
 
                if (bol[0] == comment_line_char) {
                        non_trailer_lines += possible_continuation_lines;
@@ -899,14 +902,14 @@ static int find_trailer_start(const char *buf, size_t len)
 }
 
 /* Return the position of the end of the trailers. */
-static int find_trailer_end(const char *buf, size_t len)
+static size_t find_trailer_end(const char *buf, size_t len)
 {
        return len - ignore_non_trailer(buf, len);
 }
 
 static int ends_with_blank_line(const char *buf, size_t len)
 {
-       int ll = last_line(buf, len);
+       ssize_t ll = last_line(buf, len);
        if (ll < 0)
                return 0;
        return is_blank_line(buf + ll);
@@ -939,17 +942,17 @@ static void unfold_value(struct strbuf *val)
        strbuf_release(&out);
 }
 
-static int process_input_file(FILE *outfile,
-                             const char *str,
-                             struct list_head *head,
-                             const struct process_trailer_options *opts)
+static size_t process_input_file(FILE *outfile,
+                                const char *str,
+                                struct list_head *head,
+                                const struct process_trailer_options *opts)
 {
        struct trailer_info info;
        struct strbuf tok = STRBUF_INIT;
        struct strbuf val = STRBUF_INIT;
-       int i;
+       size_t i;
 
-       trailer_info_get(&info, str);
+       trailer_info_get(&info, str, opts);
 
        /* Print lines before the trailers as is */
        if (!opts->only_trailers)
@@ -1032,7 +1035,7 @@ void process_trailers(const char *file,
 {
        LIST_HEAD(head);
        struct strbuf sb = STRBUF_INIT;
-       int trailer_end;
+       size_t trailer_end;
        FILE *outfile = stdout;
 
        ensure_configured();
@@ -1066,7 +1069,8 @@ void process_trailers(const char *file,
        strbuf_release(&sb);
 }
 
-void trailer_info_get(struct trailer_info *info, const char *str)
+void trailer_info_get(struct trailer_info *info, const char *str,
+                     const struct process_trailer_options *opts)
 {
        int patch_start, trailer_end, trailer_start;
        struct strbuf **trailer_lines, **ptr;
@@ -1076,7 +1080,11 @@ void trailer_info_get(struct trailer_info *info, const char *str)
 
        ensure_configured();
 
-       patch_start = find_patch_start(str);
+       if (opts->no_divider)
+               patch_start = strlen(str);
+       else
+               patch_start = find_patch_start(str);
+
        trailer_end = find_trailer_end(str, patch_start);
        trailer_start = find_trailer_start(str, trailer_end);
 
@@ -1111,7 +1119,7 @@ void trailer_info_get(struct trailer_info *info, const char *str)
 
 void trailer_info_release(struct trailer_info *info)
 {
-       int i;
+       size_t i;
        for (i = 0; i < info->trailer_nr; i++)
                free(info->trailers[i]);
        free(info->trailers);
@@ -1121,7 +1129,7 @@ static void format_trailer_info(struct strbuf *out,
                                const struct trailer_info *info,
                                const struct process_trailer_options *opts)
 {
-       int i;
+       size_t i;
 
        /* If we want the whole block untouched, we can take the fast path. */
        if (!opts->only_trailers && !opts->unfold) {
@@ -1132,7 +1140,7 @@ static void format_trailer_info(struct strbuf *out,
 
        for (i = 0; i < info->trailer_nr; i++) {
                char *trailer = info->trailers[i];
-               int separator_pos = find_separator(trailer, separators);
+               ssize_t separator_pos = find_separator(trailer, separators);
 
                if (separator_pos >= 1) {
                        struct strbuf tok = STRBUF_INIT;
@@ -1158,7 +1166,7 @@ void format_trailers_from_commit(struct strbuf *out, const char *msg,
 {
        struct trailer_info info;
 
-       trailer_info_get(&info, msg);
+       trailer_info_get(&info, msg, opts);
        format_trailer_info(out, &info, opts);
        trailer_info_release(&info);
 }
index 9c10026c358326ce0c0098ca52341ce8160c5bbe..b997739649a37e8791e8448c2e7daefe8023bb71 100644 (file)
--- a/trailer.h
+++ b/trailer.h
@@ -71,6 +71,7 @@ struct process_trailer_options {
        int only_trailers;
        int only_input;
        int unfold;
+       int no_divider;
 };
 
 #define PROCESS_TRAILER_OPTIONS_INIT {0}
@@ -79,7 +80,8 @@ void process_trailers(const char *file,
                      const struct process_trailer_options *opts,
                      struct list_head *new_trailer_head);
 
-void trailer_info_get(struct trailer_info *info, const char *str);
+void trailer_info_get(struct trailer_info *info, const char *str,
+                     const struct process_trailer_options *opts);
 
 void trailer_info_release(struct trailer_info *info);
 
index f25089b878a8b0842a9d6407cb6b1821867a737c..82a83dbc67ba8410e8745c0a508bf9d0dd7b6027 100644 (file)
@@ -392,7 +392,7 @@ static int check_updates(struct unpack_trees_options *o)
                }
                if (to_fetch.nr)
                        fetch_objects(repository_format_partial_clone,
-                                     &to_fetch);
+                                     to_fetch.oid, to_fetch.nr);
                fetch_if_missing = fetch_if_missing_store;
                oid_array_clear(&to_fetch);
        }
index 82b393ec31917c0c2bcd904668a60b5aaef00633..2ae9d9bb475214070529e22cc3f186afebbc6c7c 100644 (file)
@@ -24,6 +24,7 @@
 #include "quote.h"
 #include "upload-pack.h"
 #include "serve.h"
+#include "commit-graph.h"
 
 /* Remember to update object flag allocation in object.h */
 #define THEY_HAVE      (1u << 11)
@@ -740,6 +741,7 @@ static void deepen_by_rev_list(int ac, const char **av,
 {
        struct commit_list *result;
 
+       close_commit_graph(the_repository);
        result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW);
        send_shallow(result);
        free_commit_list(result);
index f3f4be579c9810d0fcf94badd4bf21770b99c91b..f565f6731d1336c761b527a8ce8a62a21b453dcc 100644 (file)
@@ -278,8 +278,7 @@ struct userdiff_driver *userdiff_find_by_path(const char *path)
                check = attr_check_initl("diff", NULL);
        if (!path)
                return NULL;
-       if (git_check_attr(&the_index, path, check))
-               return NULL;
+       git_check_attr(&the_index, path, check);
 
        if (ATTR_TRUE(check->items[0].value))
                return &driver_true;
diff --git a/ws.c b/ws.c
index 5b67b426e7b41d92f9b11bd82fd7e9bd09c89d79..a64ab51e09a99e190e5f1912de19b1e093feb360 100644 (file)
--- a/ws.c
+++ b/ws.c
@@ -74,35 +74,31 @@ unsigned parse_whitespace_rule(const char *string)
 unsigned whitespace_rule(const char *pathname)
 {
        static struct attr_check *attr_whitespace_rule;
+       const char *value;
 
        if (!attr_whitespace_rule)
                attr_whitespace_rule = attr_check_initl("whitespace", NULL);
 
-       if (!git_check_attr(&the_index, pathname, attr_whitespace_rule)) {
-               const char *value;
-
-               value = attr_whitespace_rule->items[0].value;
-               if (ATTR_TRUE(value)) {
-                       /* true (whitespace) */
-                       unsigned all_rule = ws_tab_width(whitespace_rule_cfg);
-                       int i;
-                       for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)
-                               if (!whitespace_rule_names[i].loosens_error &&
-                                   !whitespace_rule_names[i].exclude_default)
-                                       all_rule |= whitespace_rule_names[i].rule_bits;
-                       return all_rule;
-               } else if (ATTR_FALSE(value)) {
-                       /* false (-whitespace) */
-                       return ws_tab_width(whitespace_rule_cfg);
-               } else if (ATTR_UNSET(value)) {
-                       /* reset to default (!whitespace) */
-                       return whitespace_rule_cfg;
-               } else {
-                       /* string */
-                       return parse_whitespace_rule(value);
-               }
-       } else {
+       git_check_attr(&the_index, pathname, attr_whitespace_rule);
+       value = attr_whitespace_rule->items[0].value;
+       if (ATTR_TRUE(value)) {
+               /* true (whitespace) */
+               unsigned all_rule = ws_tab_width(whitespace_rule_cfg);
+               int i;
+               for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)
+                       if (!whitespace_rule_names[i].loosens_error &&
+                           !whitespace_rule_names[i].exclude_default)
+                               all_rule |= whitespace_rule_names[i].rule_bits;
+               return all_rule;
+       } else if (ATTR_FALSE(value)) {
+               /* false (-whitespace) */
+               return ws_tab_width(whitespace_rule_cfg);
+       } else if (ATTR_UNSET(value)) {
+               /* reset to default (!whitespace) */
                return whitespace_rule_cfg;
+       } else {
+               /* string */
+               return parse_whitespace_rule(value);
        }
 }