summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml9
-rw-r--r--.github/workflows/main.yml4
-rw-r--r--Documentation/MyFirstObjectWalk.txt4
-rw-r--r--Documentation/config/gui.txt2
-rw-r--r--Documentation/config/transfer.txt14
-rw-r--r--Documentation/git-branch.txt3
-rw-r--r--Documentation/git-bugreport.txt4
-rw-r--r--Documentation/git-bundle.txt147
-rw-r--r--Documentation/git-column.txt2
-rw-r--r--Documentation/git-config.txt3
-rw-r--r--Documentation/git-merge.txt2
-rw-r--r--Documentation/git-pull.txt21
-rw-r--r--Documentation/git-rebase.txt27
-rw-r--r--Documentation/git-version.txt28
-rw-r--r--Documentation/git.txt4
-rw-r--r--Documentation/merge-options.txt44
-rw-r--r--Documentation/merge-strategies.txt48
-rw-r--r--Documentation/pretty-options.txt6
-rw-r--r--Documentation/technical/directory-rename-detection.txt14
-rw-r--r--Makefile7
-rw-r--r--advice.c5
-rw-r--r--advice.h1
-rw-r--r--apply.c22
-rw-r--r--archive.c7
-rw-r--r--builtin/add.c4
-rw-r--r--builtin/archive.c4
-rw-r--r--builtin/branch.c2
-rw-r--r--builtin/bugreport.c5
-rw-r--r--builtin/checkout.c2
-rw-r--r--builtin/column.c2
-rw-r--r--builtin/commit-tree.c4
-rw-r--r--builtin/commit.c5
-rw-r--r--builtin/diff-index.c6
-rw-r--r--builtin/fast-export.c1
-rw-r--r--builtin/fetch.c4
-rw-r--r--builtin/gc.c93
-rw-r--r--builtin/hash-object.c6
-rw-r--r--builtin/index-pack.c8
-rw-r--r--builtin/ls-remote.c2
-rw-r--r--builtin/mailsplit.c4
-rw-r--r--builtin/merge.c16
-rw-r--r--builtin/notes.c4
-rw-r--r--builtin/pack-objects.c23
-rw-r--r--builtin/pull.c55
-rw-r--r--builtin/rebase.c2
-rw-r--r--builtin/send-pack.c1
-rw-r--r--builtin/show-branch.c15
-rw-r--r--builtin/tag.c4
-rw-r--r--builtin/update-index.c4
-rw-r--r--cache.h1
-rwxr-xr-xcheck_bindir13
-rwxr-xr-xci/install-docker-dependencies.sh4
-rwxr-xr-xci/run-build-and-tests.sh10
-rw-r--r--compat/mmap.c7
-rw-r--r--config.c3
-rw-r--r--connect.c2
-rw-r--r--contrib/coccinelle/xopen.cocci16
-rw-r--r--contrib/completion/git-completion.bash14
-rw-r--r--contrib/completion/git-completion.tcsh5
-rw-r--r--credential.c1
-rw-r--r--csum-file.c8
-rwxr-xr-xdetect-compiler9
-rw-r--r--diff-lib.c4
-rw-r--r--diff-merges.c14
-rw-r--r--diff-merges.h2
-rw-r--r--entry.c7
-rw-r--r--entry.h3
-rw-r--r--environment.c1
-rwxr-xr-xgit-send-email.perl28
-rw-r--r--mailmap.c2
-rw-r--r--merge-ort.c2
-rw-r--r--pack-revindex.h4
-rw-r--r--pack-write.c11
-rw-r--r--pretty.c6
-rw-r--r--protocol-caps.c5
-rw-r--r--protocol.c2
-rw-r--r--range-diff.c32
-rw-r--r--remote.c6
-rw-r--r--run-command.c4
-rw-r--r--send-pack.c6
-rw-r--r--sequencer.c111
-rw-r--r--setup.c8
-rw-r--r--sparse-index.c1
-rw-r--r--t/lib-rebase.sh56
-rwxr-xr-xt/t0000-basic.sh23
-rwxr-xr-xt/t0021-conversion.sh71
-rwxr-xr-xt/t3200-branch.sh13
-rwxr-xr-xt/t3403-rebase-skip.sh13
-rwxr-xr-xt/t3404-rebase-interactive.sh13
-rwxr-xr-xt/t3418-rebase-continue.sh18
-rwxr-xr-xt/t3430-rebase-merges.sh38
-rwxr-xr-xt/t4013-diff-various.sh2
-rwxr-xr-xt/t4045-diff-relative.sh53
-rwxr-xr-xt/t4103-apply-binary.sh23
-rwxr-xr-xt/t4108-apply-threeway.sh45
-rwxr-xr-xt/t4210-log-i18n.sh7
-rwxr-xr-xt/t5300-pack-object.sh104
-rwxr-xr-xt/t5323-pack-redundant.sh4
-rwxr-xr-xt/t5510-fetch.sh13
-rwxr-xr-xt/t5516-fetch-push.sh4
-rwxr-xr-xt/t5520-pull.sh20
-rwxr-xr-xt/t5521-pull-options.sh4
-rwxr-xr-xt/t5524-pull-msg.sh4
-rwxr-xr-xt/t5549-fetch-push-http.sh72
-rwxr-xr-xt/t5553-set-upstream.sh14
-rw-r--r--t/t5562/invoke-with-content-length.pl16
-rwxr-xr-xt/t5582-fetch-negative-refspec.sh1
-rwxr-xr-xt/t5604-clone-reference.sh4
-rwxr-xr-xt/t5702-protocol-v2.sh6
-rwxr-xr-xt/t5703-upload-pack-ref-in-want.sh208
-rwxr-xr-xt/t5705-session-id-in-capabilities.sh11
-rwxr-xr-xt/t6300-for-each-ref.sh29
-rwxr-xr-xt/t6402-merge-rename.sh18
-rwxr-xr-xt/t6409-merge-subtree.sh6
-rwxr-xr-xt/t6417-merge-ours-theirs.sh10
-rwxr-xr-xt/t7500-commit-template-squash-signoff.sh9
-rwxr-xr-xt/t7601-merge-pull-config.sh244
-rwxr-xr-xt/t7603-merge-reduce-heads.sh2
-rwxr-xr-xt/t7900-maintenance.sh17
-rwxr-xr-xt/t9001-send-email.sh62
-rwxr-xr-xt/t9002-column.sh18
-rwxr-xr-xt/t9351-fast-export-anonymize.sh10
-rwxr-xr-xt/t9902-completion.sh15
-rw-r--r--t/test-lib.sh3
-rw-r--r--trace2.h2
-rw-r--r--tree-diff.c4
-rw-r--r--unicode-width.h44
-rw-r--r--unpack-trees.c2
-rw-r--r--upload-pack.c18
-rw-r--r--wrapper.c4
130 files changed, 1890 insertions, 543 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index c2f5fe385a..e114ffee1a 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -2,8 +2,15 @@ env:
CIRRUS_CLONE_DEPTH: 1
freebsd_12_task:
+ env:
+ GIT_PROVE_OPTS: "--timer --jobs 10"
+ GIT_TEST_OPTS: "--no-chain-lint --no-bin-wrappers"
+ MAKEFLAGS: "-j4"
+ DEFAULT_TEST_TARGET: prove
+ DEVELOPER: 1
freebsd_instance:
- image: freebsd-12-1-release-amd64
+ image_family: freebsd-12-2
+ memory: 2G
install_script:
pkg install -y gettext gmake perl5
create_user_script:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 47876a4f02..b053b01c66 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -259,6 +259,8 @@ jobs:
image: alpine
- jobname: Linux32
image: daald/ubuntu32:xenial
+ - jobname: pedantic
+ image: fedora
env:
jobname: ${{matrix.vector.jobname}}
runs-on: ubuntu-latest
@@ -271,7 +273,7 @@ jobs:
if: failure()
- name: Upload failed tests' directories
if: failure() && env.FAILED_TEST_ARTIFACTS != ''
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v1
with:
name: failed-tests-${{matrix.vector.jobname}}
path: ${{env.FAILED_TEST_ARTIFACTS}}
diff --git a/Documentation/MyFirstObjectWalk.txt b/Documentation/MyFirstObjectWalk.txt
index 2d10eea7a9..45eb84d8b4 100644
--- a/Documentation/MyFirstObjectWalk.txt
+++ b/Documentation/MyFirstObjectWalk.txt
@@ -691,7 +691,7 @@ help understand. In our case, that means we omit trees and blobs not directly
referenced by `HEAD` or `HEAD`'s history, because we begin the walk with only
`HEAD` in the `pending` list.)
-First, we'll need to `#include "list-objects-filter-options.h`" and set up the
+First, we'll need to `#include "list-objects-filter-options.h"` and set up the
`struct list_objects_filter_options` at the top of the function.
----
@@ -779,7 +779,7 @@ Count all the objects within and modify the print statement:
while ((oid = oidset_iter_next(&oit)))
omitted_count++;
- printf("commits %d\nblobs %d\ntags %d\ntrees%d\nomitted %d\n",
+ printf("commits %d\nblobs %d\ntags %d\ntrees %d\nomitted %d\n",
commit_count, blob_count, tag_count, tree_count, omitted_count);
----
diff --git a/Documentation/config/gui.txt b/Documentation/config/gui.txt
index d30831a130..0c087fd8c9 100644
--- a/Documentation/config/gui.txt
+++ b/Documentation/config/gui.txt
@@ -11,7 +11,7 @@ gui.displayUntracked::
in the file list. The default is "true".
gui.encoding::
- Specifies the default encoding to use for displaying of
+ Specifies the default character encoding to use for displaying of
file contents in linkgit:git-gui[1] and linkgit:gitk[1].
It can be overridden by setting the 'encoding' attribute
for relevant files (see linkgit:gitattributes[5]).
diff --git a/Documentation/config/transfer.txt b/Documentation/config/transfer.txt
index 505126a780..b49429eb4d 100644
--- a/Documentation/config/transfer.txt
+++ b/Documentation/config/transfer.txt
@@ -52,13 +52,17 @@ If you have multiple hideRefs values, later entries override earlier ones
(and entries in more-specific config files override less-specific ones).
+
If a namespace is in use, the namespace prefix is stripped from each
-reference before it is matched against `transfer.hiderefs` patterns.
+reference before it is matched against `transfer.hiderefs` patterns. In
+order to match refs before stripping, add a `^` in front of the ref name. If
+you combine `!` and `^`, `!` must be specified first.
++
For example, if `refs/heads/master` is specified in `transfer.hideRefs` and
the current namespace is `foo`, then `refs/namespaces/foo/refs/heads/master`
-is omitted from the advertisements but `refs/heads/master` and
-`refs/namespaces/bar/refs/heads/master` are still advertised as so-called
-"have" lines. In order to match refs before stripping, add a `^` in front of
-the ref name. If you combine `!` and `^`, `!` must be specified first.
+is omitted from the advertisements. If `uploadpack.allowRefInWant` is set,
+`upload-pack` will treat `want-ref refs/heads/master` in a protocol v2
+`fetch` command as if `refs/namespaces/foo/refs/heads/master` did not exist.
+`receive-pack`, on the other hand, will still advertise the object id the
+ref is pointing to without mentioning its name (a so-called ".have" line).
+
Even if you hide refs, a client may still be able to steal the target
objects via the techniques described in the "SECURITY" section of the
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 94dc9a54f2..5449767121 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -118,7 +118,8 @@ OPTIONS
Reset <branchname> to <startpoint>, even if <branchname> exists
already. Without `-f`, 'git branch' refuses to change an existing branch.
In combination with `-d` (or `--delete`), allow deleting the
- branch irrespective of its merged status. In combination with
+ branch irrespective of its merged status, or whether it even
+ points to a valid commit. In combination with
`-m` (or `--move`), allow renaming the branch even if the new
branch name already exists, the same applies for `-c` (or `--copy`).
diff --git a/Documentation/git-bugreport.txt b/Documentation/git-bugreport.txt
index 66e88c2e31..d8817bf3ce 100644
--- a/Documentation/git-bugreport.txt
+++ b/Documentation/git-bugreport.txt
@@ -40,8 +40,8 @@ OPTIONS
-------
-o <path>::
--output-directory <path>::
- Place the resulting bug report file in `<path>` instead of the root of
- the Git repository.
+ Place the resulting bug report file in `<path>` instead of the current
+ directory.
-s <format>::
--suffix <format>::
diff --git a/Documentation/git-bundle.txt b/Documentation/git-bundle.txt
index 53804cad4b..ac0d003835 100644
--- a/Documentation/git-bundle.txt
+++ b/Documentation/git-bundle.txt
@@ -18,21 +18,48 @@ SYNOPSIS
DESCRIPTION
-----------
-Some workflows require that one or more branches of development on one
-machine be replicated on another machine, but the two machines cannot
-be directly connected, and therefore the interactive Git protocols (git,
-ssh, http) cannot be used.
-
-The 'git bundle' command packages objects and references in an archive
-at the originating machine, which can then be imported into another
-repository using 'git fetch', 'git pull', or 'git clone',
-after moving the archive by some means (e.g., by sneakernet).
-
-As no
-direct connection between the repositories exists, the user must specify a
-basis for the bundle that is held by the destination repository: the
-bundle assumes that all objects in the basis are already in the
-destination repository.
+Create, unpack, and manipulate "bundle" files. Bundles are used for
+the "offline" transfer of Git objects without an active "server"
+sitting on the other side of the network connection.
+
+They can be used to create both incremental and full backups of a
+repository, and to relay the state of the references in one repository
+to another.
+
+Git commands that fetch or otherwise "read" via protocols such as
+`ssh://` and `https://` can also operate on bundle files. It is
+possible linkgit:git-clone[1] a new repository from a bundle, to use
+linkgit:git-fetch[1] to fetch from one, and to list the references
+contained within it with linkgit:git-ls-remote[1]. There's no
+corresponding "write" support, i.e.a 'git push' into a bundle is not
+supported.
+
+See the "EXAMPLES" section below for examples of how to use bundles.
+
+BUNDLE FORMAT
+-------------
+
+Bundles are `.pack` files (see linkgit:git-pack-objects[1]) with a
+header indicating what references are contained within the bundle.
+
+Like the the packed archive format itself bundles can either be
+self-contained, or be created using exclusions.
+See the "OBJECT PREREQUISITES" section below.
+
+Bundles created using revision exclusions are "thin packs" created
+using the `--thin` option to linkgit:git-pack-objects[1], and
+unbundled using the `--fix-thin` option to linkgit:git-index-pack[1].
+
+There is no option to create a "thick pack" when using revision
+exclusions, users should not be concerned about the difference. By
+using "thin packs" bundles created using exclusions are smaller in
+size. That they're "thin" under the hood is merely noted here as a
+curiosity, and as a reference to other documentation
+
+See link:technical/bundle-format.html[the `bundle-format`
+documentation] for more details and the discussion of "thin pack" in
+link:technical/pack-format.html[the pack format documentation] for
+further details.
OPTIONS
-------
@@ -117,28 +144,88 @@ unbundle <file>::
SPECIFYING REFERENCES
---------------------
-'git bundle' will only package references that are shown by
-'git show-ref': this includes heads, tags, and remote heads. References
-such as `master~1` cannot be packaged, but are perfectly suitable for
-defining the basis. More than one reference may be packaged, and more
-than one basis can be specified. The objects packaged are those not
-contained in the union of the given bases. Each basis can be
-specified explicitly (e.g. `^master~10`), or implicitly (e.g.
-`master~10..master`, `--since=10.days.ago master`).
+Revisions must accompanied by reference names to be packaged in a
+bundle.
+
+More than one reference may be packaged, and more than one set of prerequisite objects can
+be specified. The objects packaged are those not contained in the
+union of the prerequisites.
+
+The 'git bundle create' command resolves the reference names for you
+using the same rules as `git rev-parse --abbrev-ref=loose`. Each
+prerequisite can be specified explicitly (e.g. `^master~10`), or implicitly
+(e.g. `master~10..master`, `--since=10.days.ago master`).
+
+All of these simple cases are OK (assuming we have a "master" and
+"next" branch):
+
+----------------
+$ git bundle create master.bundle master
+$ echo master | git bundle create master.bundle --stdin
+$ git bundle create master-and-next.bundle master next
+$ (echo master; echo next) | git bundle create master-and-next.bundle --stdin
+----------------
+
+And so are these (and the same but omitted `--stdin` examples):
+
+----------------
+$ git bundle create recent-master.bundle master~10..master
+$ git bundle create recent-updates.bundle master~10..master next~5..next
+----------------
+
+A revision name or a range whose right-hand-side cannot be resolved to
+a reference is not accepted:
+
+----------------
+$ git bundle create HEAD.bundle $(git rev-parse HEAD)
+fatal: Refusing to create empty bundle.
+$ git bundle create master-yesterday.bundle master~10..master~5
+fatal: Refusing to create empty bundle.
+----------------
+
+OBJECT PREREQUISITES
+--------------------
+
+When creating bundles it is possible to create a self-contained bundle
+that can be unbundled in a repository with no common history, as well
+as providing negative revisions to exclude objects needed in the
+earlier parts of the history.
+
+Feeding a revision such as `new` to `git bundle create` will create a
+bundle file that contains all the objects reachable from the revision
+`new`. That bundle can be unbundled in any repository to obtain a full
+history that leads to the revision `new`:
+
+----------------
+$ git bundle create full.bundle new
+----------------
+
+A revision range such as `old..new` will produce a bundle file that
+will require the revision `old` (and any objects reachable from it)
+to exist for the bundle to be "unbundle"-able:
+
+----------------
+$ git bundle create full.bundle old..new
+----------------
+
+A self-contained bundle without any prerequisites can be extracted
+into anywhere, even into an empty repository, or be cloned from
+(i.e., `new`, but not `old..new`).
-It is very important that the basis used be held by the destination.
It is okay to err on the side of caution, causing the bundle file
to contain objects already in the destination, as these are ignored
when unpacking at the destination.
-`git clone` can use any bundle created without negative refspecs
-(e.g., `new`, but not `old..new`).
If you want to match `git clone --mirror`, which would include your
refs such as `refs/remotes/*`, use `--all`.
If you want to provide the same set of refs that a clone directly
from the source repository would get, use `--branches --tags` for
the `<git-rev-list-args>`.
+The 'git bundle verify' command can be used to check whether your
+recipient repository has the required prerequisite commits for a
+bundle.
+
EXAMPLES
--------
@@ -149,7 +236,7 @@ but we can move data from A to B via some mechanism (CD, email, etc.).
We want to update R2 with development made on the branch master in R1.
To bootstrap the process, you can first create a bundle that does not have
-any basis. You can use a tag to remember up to what commit you last
+any prerequisites. You can use a tag to remember up to what commit you last
processed, in order to make it easy to later update the other repository
with an incremental bundle:
@@ -200,7 +287,7 @@ machineB$ git pull
If you know up to what commit the intended recipient repository should
have the necessary objects, you can use that knowledge to specify the
-basis, giving a cut-off point to limit the revisions and objects that go
+prerequisites, giving a cut-off point to limit the revisions and objects that go
in the resulting bundle. The previous example used the lastR2bundle tag
for this purpose, but you can use any other options that you would give to
the linkgit:git-log[1] command. Here are more examples:
@@ -211,7 +298,7 @@ You can use a tag that is present in both:
$ git bundle create mybundle v1.0.0..master
----------------
-You can use a basis based on time:
+You can use a prerequisite based on time:
----------------
$ git bundle create mybundle --since=10.days master
@@ -224,7 +311,7 @@ $ git bundle create mybundle -10 master
----------------
You can run `git-bundle verify` to see if you can extract from a bundle
-that was created with a basis:
+that was created with a prerequisite:
----------------
$ git bundle verify mybundle
diff --git a/Documentation/git-column.txt b/Documentation/git-column.txt
index f58e9c43e6..6cea9ab463 100644
--- a/Documentation/git-column.txt
+++ b/Documentation/git-column.txt
@@ -39,7 +39,7 @@ OPTIONS
--indent=<string>::
String to be printed at the beginning of each line.
---nl=<N>::
+--nl=<string>::
String to be printed at the end of each line,
including newline character.
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 2dc4bae6da..992225f612 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -71,6 +71,9 @@ codes are:
On success, the command returns the exit code 0.
+A list of all available configuration variables can be obtained using the
+`git help --config` command.
+
[[OPTIONS]]
OPTIONS
-------
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 3819fadac1..e4f3352eb5 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -61,6 +61,8 @@ merge has resulted in conflicts.
OPTIONS
-------
+:git-merge: 1
+
include::merge-options.txt[]
-m <msg>::
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index 7f4b2d1982..aef757ec89 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -15,14 +15,17 @@ SYNOPSIS
DESCRIPTION
-----------
-Incorporates changes from a remote repository into the current
-branch. In its default mode, `git pull` is shorthand for
-`git fetch` followed by `git merge FETCH_HEAD`.
-
-More precisely, 'git pull' runs 'git fetch' with the given
-parameters and calls 'git merge' to merge the retrieved branch
-heads into the current branch.
-With `--rebase`, it runs 'git rebase' instead of 'git merge'.
+Incorporates changes from a remote repository into the current branch.
+If the current branch is behind the remote, then by default it will
+fast-forward the current branch to match the remote. If the current
+branch and the remote have diverged, the user needs to specify how to
+reconcile the divergent branches with `--rebase` or `--no-rebase` (or
+the corresponding configuration option in `pull.rebase`).
+
+More precisely, `git pull` runs `git fetch` with the given parameters
+and then depending on configuration options or command line flags,
+will call either `git rebase` or `git merge` to reconcile diverging
+branches.
<repository> should be the name of a remote repository as
passed to linkgit:git-fetch[1]. <refspec> can name an
@@ -132,7 +135,7 @@ published that history already. Do *not* use this option
unless you have read linkgit:git-rebase[1] carefully.
--no-rebase::
- Override earlier --rebase.
+ This is shorthand for --rebase=false.
Options related to fetching
~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 55af6fd24e..73d49ec8d9 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -340,9 +340,7 @@ See also INCOMPATIBLE OPTIONS below.
-m::
--merge::
- Use merging strategies to rebase. When the recursive (default) merge
- strategy is used, this allows rebase to be aware of renames on the
- upstream side. This is the default.
+ Using merging strategies to rebase (default).
+
Note that a rebase merge works by replaying each commit from the working
branch on top of the <upstream> branch. Because of this, when a merge
@@ -354,9 +352,8 @@ See also INCOMPATIBLE OPTIONS below.
-s <strategy>::
--strategy=<strategy>::
- Use the given merge strategy.
- If there is no `-s` option 'git merge-recursive' is used
- instead. This implies --merge.
+ Use the given merge strategy, instead of the default
+ `recursive`. This implies `--merge`.
+
Because 'git rebase' replays each commit from the working branch
on top of the <upstream> branch using the given strategy, using
@@ -530,7 +527,7 @@ The `--rebase-merges` mode is similar in spirit to the deprecated
where commits can be reordered, inserted and dropped at will.
+
It is currently only possible to recreate the merge commits using the
-`recursive` merge strategy; Different merge strategies can be used only via
+`recursive` merge strategy; different merge strategies can be used only via
explicit `exec git merge -s <strategy> [...]` commands.
+
See also REBASING MERGES and INCOMPATIBLE OPTIONS below.
@@ -1219,12 +1216,16 @@ successful merge so that the user can edit the message.
If a `merge` command fails for any reason other than merge conflicts (i.e.
when the merge operation did not even start), it is rescheduled immediately.
-At this time, the `merge` command will *always* use the `recursive`
-merge strategy for regular merges, and `octopus` for octopus merges,
-with no way to choose a different one. To work around
-this, an `exec` command can be used to call `git merge` explicitly,
-using the fact that the labels are worktree-local refs (the ref
-`refs/rewritten/onto` would correspond to the label `onto`, for example).
+By default, the `merge` command will use the `recursive` merge
+strategy for regular merges, and `octopus` for octopus merges. One
+can specify a default strategy for all merges using the `--strategy`
+argument when invoking rebase, or can override specific merges in the
+interactive list of commands by using an `exec` command to call `git
+merge` explicitly with a `--strategy` argument. Note that when
+calling `git merge` explicitly like this, you can make use of the fact
+that the labels are worktree-local refs (the ref `refs/rewritten/onto`
+would correspond to the label `onto`, for example) in order to refer
+to the branches you want to merge.
Note: the first command (`label onto`) labels the revision onto which
the commits are rebased; The name `onto` is just a convention, as a nod
diff --git a/Documentation/git-version.txt b/Documentation/git-version.txt
new file mode 100644
index 0000000000..80fa7754a6
--- /dev/null
+++ b/Documentation/git-version.txt
@@ -0,0 +1,28 @@
+git-version(1)
+==============
+
+NAME
+----
+git-version - Display version information about Git
+
+SYNOPSIS
+--------
+[verse]
+'git version' [--build-options]
+
+DESCRIPTION
+-----------
+With no options given, the version of 'git' is printed on the standard output.
+
+Note that `git --version` is identical to `git version` because the
+former is internally converted into the latter.
+
+OPTIONS
+-------
+--build-options::
+ Include additional information about how git was built for diagnostic
+ purposes.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 6dd241ef83..95fe6f31b4 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -41,6 +41,10 @@ OPTIONS
-------
--version::
Prints the Git suite version that the 'git' program came from.
++
+This option is internaly converted to `git version ...` and accepts
+the same options as the linkgit:git-version[1] command. If `--help` is
+also given, it takes precedence over `--version`.
--help::
Prints the synopsis and a list of the most commonly used
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index 52565014c1..86f277a994 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -2,6 +2,9 @@
--no-commit::
Perform the merge and commit the result. This option can
be used to override --no-commit.
+ifdef::git-pull[]
+ Only useful when merging.
+endif::git-pull[]
+
With --no-commit perform the merge and stop just before creating
a merge commit, to give the user a chance to inspect and further
@@ -39,6 +42,7 @@ set to `no` at the beginning of them.
to `MERGE_MSG` before being passed on to the commit machinery in the
case of a merge conflict.
+ifdef::git-merge[]
--ff::
--no-ff::
--ff-only::
@@ -47,6 +51,22 @@ set to `no` at the beginning of them.
default unless merging an annotated (and possibly signed) tag
that is not stored in its natural place in the `refs/tags/`
hierarchy, in which case `--no-ff` is assumed.
+endif::git-merge[]
+ifdef::git-pull[]
+--ff-only::
+ Only update to the new history if there is no divergent local
+ history. This is the default when no method for reconciling
+ divergent histories is provided (via the --rebase=* flags).
+
+--ff::
+--no-ff::
+ When merging rather than rebasing, specifies how a merge is
+ handled when the merged-in history is already a descendant of
+ the current history. If merging is requested, `--ff` is the
+ default unless merging an annotated (and possibly signed) tag
+ that is not stored in its natural place in the `refs/tags/`
+ hierarchy, in which case `--no-ff` is assumed.
+endif::git-pull[]
+
With `--ff`, when possible resolve the merge as a fast-forward (only
update the branch pointer to match the merged branch; do not create a
@@ -55,9 +75,11 @@ descendant of the current history), create a merge commit.
+
With `--no-ff`, create a merge commit in all cases, even when the merge
could instead be resolved as a fast-forward.
+ifdef::git-merge[]
+
With `--ff-only`, resolve the merge as a fast-forward when possible.
When not possible, refuse to merge and exit with a non-zero status.
+endif::git-merge[]
-S[<keyid>]::
--gpg-sign[=<keyid>]::
@@ -73,6 +95,9 @@ When not possible, refuse to merge and exit with a non-zero status.
In addition to branch names, populate the log message with
one-line descriptions from at most <n> actual commits that are being
merged. See also linkgit:git-fmt-merge-msg[1].
+ifdef::git-pull[]
+ Only useful when merging.
+endif::git-pull[]
+
With --no-log do not list one-line descriptions from the
actual commits being merged.
@@ -102,18 +127,25 @@ With --no-squash perform the merge and commit the result. This
option can be used to override --squash.
+
With --squash, --commit is not allowed, and will fail.
+ifdef::git-pull[]
++
+Only useful when merging.
+endif::git-pull[]
--no-verify::
This option bypasses the pre-merge and commit-msg hooks.
See also linkgit:githooks[5].
+ifdef::git-pull[]
+ Only useful when merging.
+endif::git-pull[]
-s <strategy>::
--strategy=<strategy>::
Use the given merge strategy; can be supplied more than
once to specify them in the order they should be tried.
If there is no `-s` option, a built-in list of strategies
- is used instead ('git merge-recursive' when merging a single
- head, 'git merge-octopus' otherwise).
+ is used instead (`recursive` when merging a single head,
+ `octopus` otherwise).
-X <option>::
--strategy-option=<option>::
@@ -127,6 +159,10 @@ With --squash, --commit is not allowed, and will fail.
default trust model, this means the signing key has been signed by
a trusted key. If the tip commit of the side branch is not signed
with a valid key, the merge is aborted.
+ifdef::git-pull[]
++
+Only useful when merging.
+endif::git-pull[]
--summary::
--no-summary::
@@ -167,3 +203,7 @@ endif::git-pull[]
projects that started their lives independently. As that is
a very rare occasion, no configuration variable to enable
this by default exists and will not be added.
+ifdef::git-pull[]
++
+Only useful when merging.
+endif::git-pull[]
diff --git a/Documentation/merge-strategies.txt b/Documentation/merge-strategies.txt
index 2912de706b..210f0f850b 100644
--- a/Documentation/merge-strategies.txt
+++ b/Documentation/merge-strategies.txt
@@ -6,13 +6,6 @@ backend 'merge strategies' to be chosen with `-s` option. Some strategies
can also take their own options, which can be passed by giving `-X<option>`
arguments to `git merge` and/or `git pull`.
-resolve::
- This can only resolve two heads (i.e. the current branch
- and another branch you pulled from) using a 3-way merge
- algorithm. It tries to carefully detect criss-cross
- merge ambiguities and is considered generally safe and
- fast.
-
recursive::
This can only resolve two heads using a 3-way merge
algorithm. When there is more than one common
@@ -23,9 +16,9 @@ recursive::
causing mismerges by tests done on actual merge commits
taken from Linux 2.6 kernel development history.
Additionally this can detect and handle merges involving
- renames, but currently cannot make use of detected
- copies. This is the default merge strategy when pulling
- or merging one branch.
+ renames. It does not make use of detected copies. This
+ is the default merge strategy when pulling or merging one
+ branch.
+
The 'recursive' strategy can take the following options:
@@ -44,17 +37,14 @@ theirs;;
no 'theirs' merge strategy to confuse this merge option with.
patience;;
- With this option, 'merge-recursive' spends a little extra time
- to avoid mismerges that sometimes occur due to unimportant
- matching lines (e.g., braces from distinct functions). Use
- this when the branches to be merged have diverged wildly.
- See also linkgit:git-diff[1] `--patience`.
+ Deprecated synonym for `diff-algorithm=patience`.
diff-algorithm=[patience|minimal|histogram|myers];;
- Tells 'merge-recursive' to use a different diff algorithm, which
- can help avoid mismerges that occur due to unimportant matching
- lines (such as braces from distinct functions). See also
- linkgit:git-diff[1] `--diff-algorithm`.
+ Use a different diff algorithm while merging, which can help
+ avoid mismerges that occur due to unimportant matching lines
+ (such as braces from distinct functions). See also
+ linkgit:git-diff[1] `--diff-algorithm`. Defaults to the
+ `diff.algorithm` config setting.
ignore-space-change;;
ignore-all-space;;
@@ -105,6 +95,26 @@ subtree[=<path>];;
is prefixed (or stripped from the beginning) to make the shape of
two trees to match.
+ort::
+ This is meant as a drop-in replacement for the `recursive`
+ algorithm (as reflected in its acronym -- "Ostensibly
+ Recursive's Twin"), and will likely replace it in the future.
+ It fixes corner cases that the `recursive` strategy handles
+ suboptimally, and is significantly faster in large
+ repositories -- especially when many renames are involved.
++
+The `ort` strategy takes all the same options as `recursive`.
+However, it ignores three of those options: `no-renames`,
+`patience` and `diff-algorithm`. It always runs with rename
+detection (it handles it much faster than `recursive` does), and
+it specifically uses `diff-algorithm=histogram`.
+
+resolve::
+ This can only resolve two heads (i.e. the current branch
+ and another branch you pulled from) using a 3-way merge
+ algorithm. It tries to carefully detect criss-cross
+ merge ambiguities. It does not handle renames.
+
octopus::
This resolves cases with more than two heads, but refuses to do
a complex merge that needs manual resolution. It is
diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt
index 27ddaf84a1..b3af850608 100644
--- a/Documentation/pretty-options.txt
+++ b/Documentation/pretty-options.txt
@@ -33,14 +33,16 @@ people using 80-column terminals.
used together.
--encoding=<encoding>::
- The commit objects record the encoding used for the log message
+ Commit objects record the character encoding used for the log message
in their encoding header; this option can be used to tell the
command to re-code the commit log message in the encoding
preferred by the user. For non plumbing commands this
defaults to UTF-8. Note that if an object claims to be encoded
in `X` and we are outputting in `X`, we will output the object
verbatim; this means that invalid sequences in the original
- commit may be copied to the output.
+ commit may be copied to the output. Likewise, if iconv(3) fails
+ to convert the commit, we will output the original object
+ verbatim, along with a warning.
--expand-tabs=<n>::
--expand-tabs::
diff --git a/Documentation/technical/directory-rename-detection.txt b/Documentation/technical/directory-rename-detection.txt
index 49b83ef3cc..029ee2cedc 100644
--- a/Documentation/technical/directory-rename-detection.txt
+++ b/Documentation/technical/directory-rename-detection.txt
@@ -2,9 +2,9 @@ Directory rename detection
==========================
Rename detection logic in diffcore-rename that checks for renames of
-individual files is aggregated and analyzed in merge-recursive for cases
-where combinations of renames indicate that a full directory has been
-renamed.
+individual files is also aggregated there and then analyzed in either
+merge-ort or merge-recursive for cases where combinations of renames
+indicate that a full directory has been renamed.
Scope of abilities
------------------
@@ -88,9 +88,11 @@ directory rename detection support in:
Folks have requested in the past that `git diff` detect directory
renames and somehow simplify its output. It is not clear whether this
would be desirable or how the output should be simplified, so this was
- simply not implemented. Further, to implement this, directory rename
- detection logic would need to move from merge-recursive to
- diffcore-rename.
+ simply not implemented. Also, while diffcore-rename has most of the
+ logic for detecting directory renames, some of the logic is still found
+ within merge-ort and merge-recursive. Fully supporting directory
+ rename detection in diffs would require copying or moving the remaining
+ bits of logic to the diff machinery.
* am
diff --git a/Makefile b/Makefile
index 9573190f1d..b88b91458f 100644
--- a/Makefile
+++ b/Makefile
@@ -2603,10 +2603,10 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS
$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)
$(LIB_FILE): $(LIB_OBJS)
- $(QUIET_AR)$(AR) $(ARFLAGS) $@ $^
+ $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
$(XDIFF_LIB): $(XDIFF_OBJS)
- $(QUIET_AR)$(AR) $(ARFLAGS) $@ $^
+ $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
export DEFAULT_EDITOR DEFAULT_PAGER
@@ -3090,8 +3090,7 @@ endif
ln "$$execdir/git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
ln -s "git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
cp "$$execdir/git-remote-http$X" "$$execdir/$$p" || exit; } \
- done && \
- ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
+ done
.PHONY: install-gitweb install-doc install-man install-man-perl install-html install-info install-pdf
.PHONY: quick-install-doc quick-install-man quick-install-html
diff --git a/advice.c b/advice.c
index 0b9c89c48a..337e8f342b 100644
--- a/advice.c
+++ b/advice.c
@@ -286,6 +286,11 @@ void NORETURN die_conclude_merge(void)
die(_("Exiting because of unfinished merge."));
}
+void NORETURN die_ff_impossible(void)
+{
+ die(_("Not possible to fast-forward, aborting."));
+}
+
void advise_on_updating_sparse_paths(struct string_list *pathspec_list)
{
struct string_list_item *item;
diff --git a/advice.h b/advice.h
index 9f8ffc7354..a7227ca703 100644
--- a/advice.h
+++ b/advice.h
@@ -96,6 +96,7 @@ void advise_if_enabled(enum advice_type type, const char *advice, ...);
int error_resolve_conflict(const char *me);
void NORETURN die_resolve_conflict(const char *me);
void NORETURN die_conclude_merge(void);
+void NORETURN die_ff_impossible(void);
void advise_on_updating_sparse_paths(struct string_list *pathspec_list);
void detach_advice(const char *new_name);
diff --git a/apply.c b/apply.c
index 44bc31d6eb..43a0aebf4e 100644
--- a/apply.c
+++ b/apply.c
@@ -1917,6 +1917,7 @@ static struct fragment *parse_binary_hunk(struct apply_state *state,
state->linenr++;
buffer += llen;
+ size -= llen;
while (1) {
int byte_length, max_byte_length, newsize;
llen = linelen(buffer, size);
@@ -3467,6 +3468,21 @@ static int load_preimage(struct apply_state *state,
return 0;
}
+static int resolve_to(struct image *image, const struct object_id *result_id)
+{
+ unsigned long size;
+ enum object_type type;
+
+ clear_image(image);
+
+ image->buf = read_object_file(result_id, &type, &size);
+ if (!image->buf || type != OBJ_BLOB)
+ die("unable to read blob object %s", oid_to_hex(result_id));
+ image->len = size;
+
+ return 0;
+}
+
static int three_way_merge(struct apply_state *state,
struct image *image,
char *path,
@@ -3478,6 +3494,12 @@ static int three_way_merge(struct apply_state *state,
mmbuffer_t result = { NULL };
int status;
+ /* resolve trivial cases first */
+ if (oideq(base, ours))
+ return resolve_to(image, theirs);
+ else if (oideq(base, theirs) || oideq(ours, theirs))
+ return resolve_to(image, ours);
+
read_mmblob(&base_file, base);
read_mmblob(&our_file, ours);
read_mmblob(&their_file, theirs);
diff --git a/archive.c b/archive.c
index 3c266d1d7b..a3bbb09125 100644
--- a/archive.c
+++ b/archive.c
@@ -191,7 +191,7 @@ static int write_archive_entry(const struct object_id *oid, const char *base,
return err;
}
-static void queue_directory(const unsigned char *sha1,
+static void queue_directory(const struct object_id *oid,
struct strbuf *base, const char *filename,
unsigned mode, struct archiver_context *c)
{
@@ -203,7 +203,7 @@ static void queue_directory(const unsigned char *sha1,
d->mode = mode;
c->bottom = d;
d->len = xsnprintf(d->path, len, "%.*s%s/", (int)base->len, base->buf, filename);
- oidread(&d->oid, sha1);
+ oidcpy(&d->oid, oid);
}
static int write_directory(struct archiver_context *c)
@@ -250,8 +250,7 @@ static int queue_or_write_archive_entry(const struct object_id *oid,
if (check_attr_export_ignore(check))
return 0;
- queue_directory(oid->hash, base, filename,
- mode, c);
+ queue_directory(oid, base, filename, mode, c);
return READ_TREE_RECURSIVE;
}
diff --git a/builtin/add.c b/builtin/add.c
index 09e684585d..c37c95b45b 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -313,9 +313,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
rev.diffopt.output_format = DIFF_FORMAT_PATCH;
rev.diffopt.use_color = 0;
rev.diffopt.flags.ignore_dirty_submodules = 1;
- out = open(file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
- if (out < 0)
- die(_("Could not open '%s' for writing."), file);
+ out = xopen(file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
rev.diffopt.file = xfdopen(out, "w");
rev.diffopt.close_file = 1;
if (run_diff_files(&rev, 0))
diff --git a/builtin/archive.c b/builtin/archive.c
index 45d11669aa..7176b041b6 100644
--- a/builtin/archive.c
+++ b/builtin/archive.c
@@ -12,9 +12,7 @@
static void create_output_file(const char *output_file)
{
- int output_fd = open(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
- if (output_fd < 0)
- die_errno(_("could not create archive file '%s'"), output_file);
+ int output_fd = xopen(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
if (output_fd != 1) {
if (dup2(output_fd, 1) < 0)
die_errno(_("could not redirect output"));
diff --git a/builtin/branch.c b/builtin/branch.c
index b23b1d1752..03c7b7253a 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -168,7 +168,7 @@ static int check_branch_commit(const char *branchname, const char *refname,
int kinds, int force)
{
struct commit *rev = lookup_commit_reference(the_repository, oid);
- if (!rev) {
+ if (!force && !rev) {
error(_("Couldn't look up commit object for '%s'"), refname);
return -1;
}
diff --git a/builtin/bugreport.c b/builtin/bugreport.c
index 9915a5841d..06ed10dc92 100644
--- a/builtin/bugreport.c
+++ b/builtin/bugreport.c
@@ -171,10 +171,7 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
get_populated_hooks(&buffer, !startup_info->have_repository);
/* fopen doesn't offer us an O_EXCL alternative, except with glibc. */
- report = open(report_path.buf, O_CREAT | O_EXCL | O_WRONLY, 0666);
-
- if (report < 0)
- die(_("couldn't create a new file at '%s'"), report_path.buf);
+ report = xopen(report_path.buf, O_CREAT | O_EXCL | O_WRONLY, 0666);
if (write_in_full(report, buffer.buf, buffer.len) < 0)
die_errno(_("unable to write to %s"), report_path.buf);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index b5d477919a..b23bc149d1 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -404,7 +404,7 @@ static int checkout_worktree(const struct checkout_opts *opts,
mem_pool_discard(&ce_mem_pool, should_validate_cache_entries());
remove_marked_cache_entries(&the_index, 1);
remove_scheduled_dirs();
- errs |= finish_delayed_checkout(&state, &nr_checkouts);
+ errs |= finish_delayed_checkout(&state, &nr_checkouts, opts->show_progress);
if (opts->count_checkout_paths) {
if (nr_unmerged)
diff --git a/builtin/column.c b/builtin/column.c
index 40d4b3bee2..158fdf53d9 100644
--- a/builtin/column.c
+++ b/builtin/column.c
@@ -29,7 +29,7 @@ int cmd_column(int argc, const char **argv, const char *prefix)
OPT_INTEGER(0, "raw-mode", &colopts, N_("layout to use")),
OPT_INTEGER(0, "width", &copts.width, N_("maximum width")),
OPT_STRING(0, "indent", &copts.indent, N_("string"), N_("padding space on left border")),
- OPT_INTEGER(0, "nl", &copts.nl, N_("padding space on right border")),
+ OPT_STRING(0, "nl", &copts.nl, N_("string"), N_("padding space on right border")),
OPT_INTEGER(0, "padding", &copts.padding, N_("padding space between columns")),
OPT_END()
};
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index 1031b9a491..63ea322933 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -88,9 +88,7 @@ static int parse_file_arg_callback(const struct option *opt,
if (!strcmp(arg, "-"))
fd = 0;
else {
- fd = open(arg, O_RDONLY);
- if (fd < 0)
- die_errno(_("git commit-tree: failed to open '%s'"), arg);
+ fd = xopen(arg, O_RDONLY);
}
if (strbuf_read(buf, fd, 0) < 0)
die_errno(_("git commit-tree: failed to read '%s'"), arg);
diff --git a/builtin/commit.c b/builtin/commit.c
index 243c626307..7c9b1e7be3 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1253,8 +1253,6 @@ static int parse_and_validate_options(int argc, const char *argv[],
if (logfile || have_option_m || use_message)
use_editor = 0;
- if (0 <= edit_flag)
- use_editor = edit_flag;
/* Sanity check options */
if (amend && !current_head)
@@ -1344,6 +1342,9 @@ static int parse_and_validate_options(int argc, const char *argv[],
}
}
+ if (0 <= edit_flag)
+ use_editor = edit_flag;
+
cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);
handle_untracked_files_arg(s);
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index cf09559e42..5fd23ab5b6 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -29,10 +29,10 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
prefix = precompose_argv_prefix(argc, argv, prefix);
/*
- * We need no diff for merges options, and we need to avoid conflict
- * with our own meaning of "-m".
+ * We need (some of) diff for merges options (e.g., --cc), and we need
+ * to avoid conflict with our own meaning of "-m".
*/
- diff_merges_suppress_options_parsing();
+ diff_merges_suppress_m_parsing();
argc = setup_revisions(argc, argv, &rev, NULL);
for (i = 1; i < argc; i++) {
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 3c20f164f0..95e8e89e81 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -821,6 +821,7 @@ static void handle_tag(const char *name, struct tag *tag)
static struct hashmap tags;
message = anonymize_str(&tags, anonymize_tag,
message, message_size, NULL);
+ message_size = strlen(message);
}
}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 25740c13df..e064687dbd 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -1428,7 +1428,9 @@ static void add_negotiation_tips(struct git_transport_options *smart_options)
if (!has_glob_specials(s)) {
struct object_id oid;
if (get_oid(s, &oid))
- die("%s is not a valid object", s);
+ die(_("%s is not a valid object"), s);
+ if (!has_object(the_repository, &oid, 0))
+ die(_("the object %s does not exist"), s);
oid_array_append(oids, &oid);
continue;
}
diff --git a/builtin/gc.c b/builtin/gc.c
index f05d2f0a1a..ac60662619 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -502,7 +502,7 @@ static int report_last_gc_error(void)
*/
warning(_("The last gc run reported the following. "
"Please correct the root cause\n"
- "and remove %s.\n"
+ "and remove %s\n"
"Automatic cleanup will not be performed "
"until the file is removed.\n\n"
"%s"),
@@ -1600,18 +1600,40 @@ static int launchctl_remove_plists(const char *cmd)
launchctl_remove_plist(SCHEDULE_WEEKLY, cmd);
}
+static int launchctl_list_contains_plist(const char *name, const char *cmd)
+{
+ int result;
+ struct child_process child = CHILD_PROCESS_INIT;
+ char *uid = launchctl_get_uid();
+
+ strvec_split(&child.args, cmd);
+ strvec_pushl(&child.args, "list", name, NULL);
+
+ child.no_stderr = 1;
+ child.no_stdout = 1;
+
+ if (start_command(&child))
+ die(_("failed to start launchctl"));
+
+ result = finish_command(&child);
+
+ free(uid);
+
+ /* Returns failure if 'name' doesn't exist. */
+ return !result;
+}
+
static int launchctl_schedule_plist(const char *exec_path, enum schedule_priority schedule, const char *cmd)
{
- FILE *plist;
- int i;
+ int i, fd;
const char *preamble, *repeat;
const char *frequency = get_frequency(schedule);
char *name = launchctl_service_name(frequency);
char *filename = launchctl_service_filename(name);
-
- if (safe_create_leading_directories(filename))
- die(_("failed to create directories for '%s'"), filename);
- plist = xfopen(filename, "w");
+ struct lock_file lk = LOCK_INIT;
+ static unsigned long lock_file_timeout_ms = ULONG_MAX;
+ struct strbuf plist = STRBUF_INIT, plist2 = STRBUF_INIT;
+ struct stat st;
preamble = "<?xml version=\"1.0\"?>\n"
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
@@ -1630,7 +1652,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
"</array>\n"
"<key>StartCalendarInterval</key>\n"
"<array>\n";
- fprintf(plist, preamble, name, exec_path, exec_path, frequency);
+ strbuf_addf(&plist, preamble, name, exec_path, exec_path, frequency);
switch (schedule) {
case SCHEDULE_HOURLY:
@@ -1639,7 +1661,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
"<key>Minute</key><integer>0</integer>\n"
"</dict>\n";
for (i = 1; i <= 23; i++)
- fprintf(plist, repeat, i);
+ strbuf_addf(&plist, repeat, i);
break;
case SCHEDULE_DAILY:
@@ -1649,32 +1671,59 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
"<key>Minute</key><integer>0</integer>\n"
"</dict>\n";
for (i = 1; i <= 6; i++)
- fprintf(plist, repeat, i);
+ strbuf_addf(&plist, repeat, i);
break;
case SCHEDULE_WEEKLY:
- fprintf(plist,
- "<dict>\n"
- "<key>Day</key><integer>0</integer>\n"
- "<key>Hour</key><integer>0</integer>\n"
- "<key>Minute</key><integer>0</integer>\n"
- "</dict>\n");
+ strbuf_addstr(&plist,
+ "<dict>\n"
+ "<key>Day</key><integer>0</integer>\n"
+ "<key>Hour</key><integer>0</integer>\n"
+ "<key>Minute</key><integer>0</integer>\n"
+ "</dict>\n");
break;
default:
/* unreachable */
break;
}
- fprintf(plist, "</array>\n</dict>\n</plist>\n");
- fclose(plist);
+ strbuf_addstr(&plist, "</array>\n</dict>\n</plist>\n");
+
+ if (safe_create_leading_directories(filename))
+ die(_("failed to create directories for '%s'"), filename);
+
+ if ((long)lock_file_timeout_ms < 0 &&
+ git_config_get_ulong("gc.launchctlplistlocktimeoutms",
+ &lock_file_timeout_ms))
+ lock_file_timeout_ms = 150;
+
+ fd = hold_lock_file_for_update_timeout(&lk, filename, LOCK_DIE_ON_ERROR,
+ lock_file_timeout_ms);
- /* bootout might fail if not already running, so ignore */
- launchctl_boot_plist(0, filename, cmd);
- if (launchctl_boot_plist(1, filename, cmd))
- die(_("failed to bootstrap service %s"), filename);
+ /*
+ * Does this file already exist? With the intended contents? Is it
+ * registered already? Then it does not need to be re-registered.
+ */
+ if (!stat(filename, &st) && st.st_size == plist.len &&
+ strbuf_read_file(&plist2, filename, plist.len) == plist.len &&
+ !strbuf_cmp(&plist, &plist2) &&
+ launchctl_list_contains_plist(name, cmd))
+ rollback_lock_file(&lk);
+ else {
+ if (write_in_full(fd, plist.buf, plist.len) < 0 ||
+ commit_lock_file(&lk))
+ die_errno(_("could not write '%s'"), filename);
+
+ /* bootout might fail if not already running, so ignore */
+ launchctl_boot_plist(0, filename, cmd);
+ if (launchctl_boot_plist(1, filename, cmd))
+ die(_("failed to bootstrap service %s"), filename);
+ }
free(filename);
free(name);
+ strbuf_release(&plist);
+ strbuf_release(&plist2);
return 0;
}
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index 640ef4ded5..c7b3ad74c6 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -53,9 +53,7 @@ static void hash_object(const char *path, const char *type, const char *vpath,
unsigned flags, int literally)
{
int fd;
- fd = open(path, O_RDONLY);
- if (fd < 0)
- die_errno("Cannot open '%s'", path);
+ fd = xopen(path, O_RDONLY);
hash_fd(fd, type, vpath, flags, literally);
}
@@ -117,7 +115,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
prefix = setup_git_directory_gently(&nongit);
if (vpath && prefix)
- vpath = xstrdup(prefix_filename(prefix, vpath));
+ vpath = prefix_filename(prefix, vpath);
git_config(git_default_config, NULL);
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index c889f5f964..f267dce49e 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -338,15 +338,11 @@ static const char *open_pack_file(const char *pack_name)
"pack/tmp_pack_XXXXXX");
pack_name = strbuf_detach(&tmp_file, NULL);
} else {
- output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
- if (output_fd < 0)
- die_errno(_("unable to create '%s'"), pack_name);
+ output_fd = xopen(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
}
nothread_data.pack_fd = output_fd;
} else {
- input_fd = open(pack_name, O_RDONLY);
- if (input_fd < 0)
- die_errno(_("cannot open packfile '%s'"), pack_name);
+ input_fd = xopen(pack_name, O_RDONLY);
output_fd = -1;
nothread_data.pack_fd = input_fd;
}
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index 1794548c71..f4fd823af8 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -84,6 +84,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
PARSE_OPT_STOP_AT_NON_OPTION);
dest = argv[0];
+ packet_trace_identity("ls-remote");
+
UNLEAK(sorting);
if (argc > 1) {
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 664400b816..7baef30569 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -75,9 +75,7 @@ static int split_one(FILE *mbox, const char *name, int allow_bare)
fprintf(stderr, "corrupt mailbox\n");
exit(1);
}
- fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
- if (fd < 0)
- die_errno("cannot open output file '%s'", name);
+ fd = xopen(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
output = xfdopen(fd, "w");
/* Copy it out, while searching for a line that begins with
diff --git a/builtin/merge.c b/builtin/merge.c
index 22f23990b3..8949a9c2fb 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -739,7 +739,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
for (x = 0; x < xopts_nr; x++)
if (parse_merge_opt(&o, xopts[x]))
- die(_("Unknown option for merge-recursive: -X%s"), xopts[x]);
+ die(_("unknown strategy option: -X%s"), xopts[x]);
o.branch1 = head_arg;
o.branch2 = merge_remote_util(remoteheads->item)->name;
@@ -862,9 +862,11 @@ static void prepare_to_commit(struct commit_list *remoteheads)
strbuf_commented_addf(&msg, "\n");
}
strbuf_commented_addf(&msg, _(merge_editor_comment));
- strbuf_commented_addf(&msg, _(cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS ?
- scissors_editor_comment :
- no_scissors_editor_comment), comment_line_char);
+ if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
+ strbuf_commented_addf(&msg, _(scissors_editor_comment));
+ else
+ strbuf_commented_addf(&msg,
+ _(no_scissors_editor_comment), comment_line_char);
}
if (signoff)
append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);
@@ -1136,9 +1138,7 @@ static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge
merge_names = &fetch_head_file;
filename = git_path_fetch_head(the_repository);
- fd = open(filename, O_RDONLY);
- if (fd < 0)
- die_errno(_("could not open '%s' for reading"), filename);
+ fd = xopen(filename, O_RDONLY);
if (strbuf_read(merge_names, fd, 0) < 0)
die_errno(_("could not read '%s'"), filename);
@@ -1622,7 +1622,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
}
if (fast_forward == FF_ONLY)
- die(_("Not possible to fast-forward, aborting."));
+ die_ff_impossible();
if (autostash)
create_autostash(the_repository,
diff --git a/builtin/notes.c b/builtin/notes.c
index 74bba39ca8..71c59583a1 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -172,9 +172,7 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
/* write the template message before editing: */
d->edit_path = git_pathdup("NOTES_EDITMSG");
- fd = open(d->edit_path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
- if (fd < 0)
- die_errno(_("could not create file '%s'"), d->edit_path);
+ fd = xopen(d->edit_path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (d->given)
write_or_die(fd, d->buf.buf, d->buf.len);
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 7a3632828c..a01767a384 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -3318,9 +3318,26 @@ static void read_packs_list_from_stdin(void)
}
/*
- * First handle all of the excluded packs, marking them as kept in-core
- * so that later calls to add_object_entry() discards any objects that
- * are also found in excluded packs.
+ * Arguments we got on stdin may not even be packs. First
+ * check that to avoid segfaulting later on in
+ * e.g. pack_mtime_cmp(), excluded packs are handled below.
+ *
+ * Since we first parsed our STDIN and then sorted the input
+ * lines the pack we error on will be whatever line happens to
+ * sort first. This is lazy, it's enough that we report one
+ * bad case here, we don't need to report the first/last one,
+ * or all of them.
+ */
+ for_each_string_list_item(item, &include_packs) {
+ struct packed_git *p = item->util;
+ if (!p)
+ die(_("could not find pack '%s'"), item->string);
+ }
+
+ /*
+ * Then, handle all of the excluded packs, marking them as
+ * kept in-core so that later calls to add_object_entry()
+ * discards any objects that are also found in excluded packs.
*/
for_each_string_list_item(item, &exclude_packs) {
struct packed_git *p = item->util;
diff --git a/builtin/pull.c b/builtin/pull.c
index 3e13f81084..b311ea6b9d 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -893,6 +893,8 @@ static int run_rebase(const struct object_id *newbase,
strvec_pushv(&args, opt_strategy_opts.v);
if (opt_gpg_sign)
strvec_push(&args, opt_gpg_sign);
+ if (opt_signoff)
+ strvec_push(&args, opt_signoff);
if (opt_autostash == 0)
strvec_push(&args, "--no-autostash");
else if (opt_autostash == 1)
@@ -911,12 +913,18 @@ static int run_rebase(const struct object_id *newbase,
return ret;
}
-static int get_can_ff(struct object_id *orig_head, struct object_id *orig_merge_head)
+static int get_can_ff(struct object_id *orig_head,
+ struct oid_array *merge_heads)
{
int ret;
struct commit_list *list = NULL;
struct commit *merge_head, *head;
+ struct object_id *orig_merge_head;
+ if (merge_heads->nr > 1)
+ return 0;
+
+ orig_merge_head = &merge_heads->oid[0];
head = lookup_commit_reference(the_repository, orig_head);
commit_list_insert(head, &list);
merge_head = lookup_commit_reference(the_repository, orig_merge_head);
@@ -927,9 +935,9 @@ static int get_can_ff(struct object_id *orig_head, struct object_id *orig_merge_
static void show_advice_pull_non_ff(void)
{
- advise(_("Pulling without specifying how to reconcile divergent branches is\n"
- "discouraged. You can squelch this message by running one of the following\n"
- "commands sometime before your next pull:\n"
+ advise(_("You have divergent branches and need to specify how to reconcile them.\n"
+ "You can do so by running one of the following commands sometime before\n"
+ "your next pull:\n"
"\n"
" git config pull.rebase false # merge (the default strategy)\n"
" git config pull.rebase true # rebase\n"
@@ -966,8 +974,22 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
parse_repo_refspecs(argc, argv, &repo, &refspecs);
- if (!opt_ff)
+ if (!opt_ff) {
opt_ff = xstrdup_or_null(config_get_ff());
+ /*
+ * A subtle point: opt_ff was set on the line above via
+ * reading from config. opt_rebase, in contrast, is set
+ * before this point via command line options. The setting
+ * of opt_rebase via reading from config (using
+ * config_get_rebase()) does not happen until later. We
+ * are relying on the next if-condition happening before
+ * the config_get_rebase() call so that an explicit
+ * "--rebase" can override a config setting of
+ * pull.ff=only.
+ */
+ if (opt_rebase >= 0 && opt_ff && !strcmp(opt_ff, "--ff-only"))
+ opt_ff = "--ff";
+ }
if (opt_rebase < 0)
opt_rebase = config_get_rebase(&rebase_unspecified);
@@ -1041,14 +1063,25 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
die(_("Cannot merge multiple branches into empty head."));
return pull_into_void(merge_heads.oid, &curr_head);
}
- if (opt_rebase && merge_heads.nr > 1)
- die(_("Cannot rebase onto multiple branches."));
+ if (merge_heads.nr > 1) {
+ if (opt_rebase)
+ die(_("Cannot rebase onto multiple branches."));
+ if (opt_ff && !strcmp(opt_ff, "--ff-only"))
+ die(_("Cannot fast-forward to multiple branches."));
+ }
- can_ff = get_can_ff(&orig_head, &merge_heads.oid[0]);
+ can_ff = get_can_ff(&orig_head, &merge_heads);
- if (rebase_unspecified && !opt_ff && !can_ff) {
- if (opt_verbosity >= 0)
- show_advice_pull_non_ff();
+ /* ff-only takes precedence over rebase */
+ if (opt_ff && !strcmp(opt_ff, "--ff-only")) {
+ if (!can_ff)
+ die_ff_impossible();
+ opt_rebase = REBASE_FALSE;
+ }
+ /* If no action specified and we can't fast forward, then warn. */
+ if (!opt_ff && rebase_unspecified && !can_ff) {
+ show_advice_pull_non_ff();
+ die(_("Need to specify how to reconcile divergent branches."));
}
if (opt_rebase) {
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 33e0961900..66a0a0f0d0 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1918,7 +1918,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
&options.orig_head))
options.head_name = NULL;
else
- die(_("fatal: no such branch/commit '%s'"),
+ die(_("no such branch/commit '%s'"),
branch_name);
} else if (argc == 0) {
/* Do not need to switch branches, we are already on it. */
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index a7e01667b0..729dea1d25 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -230,6 +230,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
args.atomic = atomic;
args.stateless_rpc = stateless_rpc;
args.push_options = push_options.nr ? &push_options : NULL;
+ args.url = dest;
if (from_stdin) {
if (args.stateless_rpc) {
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index d77ce7aeb3..bea4bbf468 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -482,10 +482,9 @@ static void snarf_refs(int head, int remotes)
}
}
-static int rev_is_head(const char *head, const char *name,
- unsigned char *head_sha1, unsigned char *sha1)
+static int rev_is_head(const char *head, const char *name)
{
- if (!head || (head_sha1 && sha1 && !hasheq(head_sha1, sha1)))
+ if (!head)
return 0;
skip_prefix(head, "refs/heads/", &head);
if (!skip_prefix(name, "refs/heads/", &name))
@@ -806,9 +805,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
/* We are only interested in adding the branch
* HEAD points at.
*/
- if (rev_is_head(head,
- ref_name[i],
- head_oid.hash, NULL))
+ if (rev_is_head(head, ref_name[i]))
has_head++;
}
if (!has_head) {
@@ -867,10 +864,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
if (1 < num_rev || extra < 0) {
for (i = 0; i < num_rev; i++) {
int j;
- int is_head = rev_is_head(head,
- ref_name[i],
- head_oid.hash,
- rev[i]->object.oid.hash);
+ int is_head = rev_is_head(head, ref_name[i]) &&
+ oideq(&head_oid, &rev[i]->object.oid);
if (extra < 0)
printf("%c [%s] ",
is_head ? '*' : ' ', ref_name[i]);
diff --git a/builtin/tag.c b/builtin/tag.c
index 82fcfc0982..87552ae6ac 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -293,9 +293,7 @@ static void create_tag(const struct object_id *object, const char *object_ref,
/* write the template message before editing: */
path = git_pathdup("TAG_EDITMSG");
- fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
- if (fd < 0)
- die_errno(_("could not create file '%s'"), path);
+ fd = xopen(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (opt->message_given) {
write_or_die(fd, buf->buf, buf->len);
diff --git a/builtin/update-index.c b/builtin/update-index.c
index f1f16f2de5..187203e8bb 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -95,9 +95,7 @@ static int create_file(const char *path)
{
int fd;
path = get_mtime_path(path);
- fd = open(path, O_CREAT | O_RDWR, 0644);
- if (fd < 0)
- die_errno(_("failed to create file %s"), path);
+ fd = xopen(path, O_CREAT | O_RDWR, 0644);
return fd;
}
diff --git a/cache.h b/cache.h
index bd4869beee..0a8d44ce71 100644
--- a/cache.h
+++ b/cache.h
@@ -958,7 +958,6 @@ extern char *apply_default_ignorewhitespace;
extern const char *git_attributes_file;
extern const char *git_hooks_path;
extern int zlib_compression_level;
-extern int core_compression_level;
extern int pack_compression_level;
extern size_t packed_git_window_size;
extern size_t packed_git_limit;
diff --git a/check_bindir b/check_bindir
deleted file mode 100755
index 623eadcbb7..0000000000
--- a/check_bindir
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/sh
-bindir="$1"
-gitexecdir="$2"
-gitcmd="$3"
-if test "$bindir" != "$gitexecdir" && test -x "$gitcmd"
-then
- echo
- echo "!! You have installed git-* commands to new gitexecdir."
- echo "!! Old version git-* commands still remain in bindir."
- echo "!! Mixing two versions of Git will lead to problems."
- echo "!! Please remove old version commands in bindir now."
- echo
-fi
diff --git a/ci/install-docker-dependencies.sh b/ci/install-docker-dependencies.sh
index 26a6689766..07a8c6b199 100755
--- a/ci/install-docker-dependencies.sh
+++ b/ci/install-docker-dependencies.sh
@@ -15,4 +15,8 @@ linux-musl)
apk add --update build-base curl-dev openssl-dev expat-dev gettext \
pcre2-dev python3 musl-libintl perl-utils ncurses >/dev/null
;;
+pedantic)
+ dnf -yq update >/dev/null &&
+ dnf -yq install make gcc findutils diffutils perl python3 gettext zlib-devel expat-devel openssl-devel curl-devel pcre2-devel >/dev/null
+ ;;
esac
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index 3ce81ffee9..f3aba5d6cb 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -10,6 +10,11 @@ windows*) cmd //c mklink //j t\\.prove "$(cygpath -aw "$cache_dir/.prove")";;
*) ln -s "$cache_dir/.prove" t/.prove;;
esac
+if test "$jobname" = "pedantic"
+then
+ export DEVOPTS=pedantic
+fi
+
make
case "$jobname" in
linux-gcc)
@@ -35,10 +40,9 @@ linux-clang)
export GIT_TEST_DEFAULT_HASH=sha256
make test
;;
-linux-gcc-4.8)
+linux-gcc-4.8|pedantic)
# Don't run the tests; we only care about whether Git can be
- # built with GCC 4.8, as it errors out on some undesired (C99)
- # constructs that newer compilers seem to quietly accept.
+ # built with GCC 4.8 or with pedantic
;;
*)
make test
diff --git a/compat/mmap.c b/compat/mmap.c
index 14d31010df..8d6c02d4bc 100644
--- a/compat/mmap.c
+++ b/compat/mmap.c
@@ -7,7 +7,12 @@ void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t of
if (start != NULL || flags != MAP_PRIVATE || prot != PROT_READ)
die("Invalid usage of mmap when built with NO_MMAP");
- start = xmalloc(length);
+ if (length == 0) {
+ errno = EINVAL;
+ return MAP_FAILED;
+ }
+
+ start = malloc(length);
if (start == NULL) {
errno = ENOMEM;
return MAP_FAILED;
diff --git a/config.c b/config.c
index f33abeab85..7a6ff18ffa 100644
--- a/config.c
+++ b/config.c
@@ -76,7 +76,6 @@ static struct key_value_info *current_config_kvi;
*/
static enum config_scope current_parsing_scope;
-static int core_compression_seen;
static int pack_compression_seen;
static int zlib_compression_seen;
@@ -1400,8 +1399,6 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
level = Z_DEFAULT_COMPRESSION;
else if (level < 0 || level > Z_BEST_COMPRESSION)
die(_("bad zlib compression level %d"), level);
- core_compression_level = level;
- core_compression_seen = 1;
if (!zlib_compression_seen)
zlib_compression_level = level;
if (!pack_compression_seen)
diff --git a/connect.c b/connect.c
index 70b13389ba..aff13a270e 100644
--- a/connect.c
+++ b/connect.c
@@ -164,6 +164,8 @@ enum protocol_version discover_version(struct packet_reader *reader)
BUG("unknown protocol version");
}
+ trace2_data_intmax("transfer", NULL, "negotiated-version", version);
+
return version;
}
diff --git a/contrib/coccinelle/xopen.cocci b/contrib/coccinelle/xopen.cocci
new file mode 100644
index 0000000000..814d7b8a1a
--- /dev/null
+++ b/contrib/coccinelle/xopen.cocci
@@ -0,0 +1,16 @@
+@@
+identifier fd;
+identifier die_fn =~ "^(die|die_errno)$";
+@@
+(
+ fd =
+- open
++ xopen
+ (...);
+|
+ int fd =
+- open
++ xopen
+ (...);
+)
+- if ( \( fd < 0 \| fd == -1 \) ) { die_fn(...); }
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 4bdd27ddc8..8108eda1e8 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -356,7 +356,7 @@ __gitcomp ()
local cur_="${3-$cur}"
case "$cur_" in
- --*=)
+ *=)
;;
--no-*)
local c i=0 IFS=$' \t\n'
@@ -421,7 +421,7 @@ __gitcomp_builtin ()
local incl="${2-}"
local excl="${3-}"
- local var=__gitcomp_builtin_"${cmd/-/_}"
+ local var=__gitcomp_builtin_"${cmd//-/_}"
local options
eval "options=\${$var-}"
@@ -2650,10 +2650,10 @@ __git_complete_config_variable_name ()
return
;;
branch.*)
- local pfx="${cur%.*}."
- cur_="${cur#*.}"
+ local pfx="${cur_%.*}."
+ cur_="${cur_#*.}"
__gitcomp_direct "$(__git_heads "$pfx" "$cur_" ".")"
- __gitcomp_nl_append $'autoSetupMerge\nautoSetupRebase\n' "$pfx" "$cur_" "$sfx"
+ __gitcomp_nl_append $'autoSetupMerge\nautoSetupRebase\n' "$pfx" "$cur_" "${sfx- }"
return
;;
guitool.*.*)
@@ -2687,7 +2687,7 @@ __git_complete_config_variable_name ()
local pfx="${cur_%.*}."
cur_="${cur_#*.}"
__git_compute_all_commands
- __gitcomp_nl "$__git_all_commands" "$pfx" "$cur_" "$sfx"
+ __gitcomp_nl "$__git_all_commands" "$pfx" "$cur_" "${sfx- }"
return
;;
remote.*.*)
@@ -2703,7 +2703,7 @@ __git_complete_config_variable_name ()
local pfx="${cur_%.*}."
cur_="${cur_#*.}"
__gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "."
- __gitcomp_nl_append "pushDefault" "$pfx" "$cur_" "$sfx"
+ __gitcomp_nl_append "pushDefault" "$pfx" "$cur_" "${sfx- }"
return
;;
url.*.*)
diff --git a/contrib/completion/git-completion.tcsh b/contrib/completion/git-completion.tcsh
index 4a790d8f4e..ba797e5b3c 100644
--- a/contrib/completion/git-completion.tcsh
+++ b/contrib/completion/git-completion.tcsh
@@ -80,8 +80,9 @@ else
COMP_CWORD=\$((\${#COMP_WORDS[@]}-1))
fi
-# Call _git() or _gitk() of the bash script, based on the first argument
-_\${1}
+# Call __git_wrap__git_main() or __git_wrap__gitk_main() of the bash script,
+# based on the first argument
+__git_wrap__\${1}_main
IFS=\$'\n'
if [ \${#COMPREPLY[*]} -eq 0 ]; then
diff --git a/credential.c b/credential.c
index 3c05c7c669..000ac7a8d4 100644
--- a/credential.c
+++ b/credential.c
@@ -128,6 +128,7 @@ static void credential_apply_config(struct credential *c)
normalized_url = url_normalize(url.buf, &config.url);
git_config(urlmatch_config_entry, &config);
+ string_list_clear(&config.vars, 1);
free(normalized_url);
strbuf_release(&url);
diff --git a/csum-file.c b/csum-file.c
index c951cf8277..26e8a6df44 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -131,12 +131,8 @@ struct hashfile *hashfd_check(const char *name)
int sink, check;
struct hashfile *f;
- sink = open("/dev/null", O_WRONLY);
- if (sink < 0)
- die_errno("unable to open /dev/null");
- check = open(name, O_RDONLY);
- if (check < 0)
- die_errno("unable to open '%s'", name);
+ sink = xopen("/dev/null", O_WRONLY);
+ check = xopen(name, O_RDONLY);
f = hashfd(sink, name);
f->check_fd = check;
f->check_buffer = xmalloc(f->buffer_len);
diff --git a/detect-compiler b/detect-compiler
index 70b754481c..11d60da5b7 100755
--- a/detect-compiler
+++ b/detect-compiler
@@ -13,11 +13,11 @@ get_version_line() {
}
get_family() {
- get_version_line | sed 's/^\(.*\) version [0-9][^ ]* .*/\1/'
+ get_version_line | sed 's/^\(.*\) version [0-9].*/\1/'
}
get_version() {
- get_version_line | sed 's/^.* version \([0-9][^ ]*\) .*/\1/'
+ get_version_line | sed 's/^.* version \([0-9][^ ]*\).*/\1/'
}
print_flags() {
@@ -38,10 +38,7 @@ case "$(get_family)" in
gcc)
print_flags gcc
;;
-clang)
- print_flags clang
- ;;
-"FreeBSD clang")
+clang | *" clang")
print_flags clang
;;
"Apple LLVM")
diff --git a/diff-lib.c b/diff-lib.c
index f9eadc4fc1..ca085a03ef 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -117,6 +117,10 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
if (!ce_path_match(istate, ce, &revs->prune_data, NULL))
continue;
+ if (revs->diffopt.prefix &&
+ strncmp(ce->name, revs->diffopt.prefix, revs->diffopt.prefix_length))
+ continue;
+
if (ce_stage(ce)) {
struct combine_diff_path *dpath;
struct diff_filepair *pair;
diff --git a/diff-merges.c b/diff-merges.c
index d897fd8a29..5060ccd890 100644
--- a/diff-merges.c
+++ b/diff-merges.c
@@ -6,7 +6,7 @@ typedef void (*diff_merges_setup_func_t)(struct rev_info *);
static void set_separate(struct rev_info *revs);
static diff_merges_setup_func_t set_to_default = set_separate;
-static int suppress_parsing;
+static int suppress_m_parsing;
static void suppress(struct rev_info *revs)
{
@@ -91,9 +91,9 @@ int diff_merges_config(const char *value)
return 0;
}
-void diff_merges_suppress_options_parsing(void)
+void diff_merges_suppress_m_parsing(void)
{
- suppress_parsing = 1;
+ suppress_m_parsing = 1;
}
int diff_merges_parse_opts(struct rev_info *revs, const char **argv)
@@ -102,10 +102,7 @@ int diff_merges_parse_opts(struct rev_info *revs, const char **argv)
const char *optarg;
const char *arg = argv[0];
- if (suppress_parsing)
- return 0;
-
- if (!strcmp(arg, "-m")) {
+ if (!suppress_m_parsing && !strcmp(arg, "-m")) {
set_to_default(revs);
} else if (!strcmp(arg, "-c")) {
set_combined(revs);
@@ -153,9 +150,6 @@ void diff_merges_set_dense_combined_if_unset(struct rev_info *revs)
void diff_merges_setup_revs(struct rev_info *revs)
{
- if (suppress_parsing)
- return;
-
if (revs->combine_merges == 0)
revs->dense_combined_merges = 0;
if (revs->separate_merges == 0)
diff --git a/diff-merges.h b/diff-merges.h
index b5d57f6563..19639689bb 100644
--- a/diff-merges.h
+++ b/diff-merges.h
@@ -11,7 +11,7 @@ struct rev_info;
int diff_merges_config(const char *value);
-void diff_merges_suppress_options_parsing(void);
+void diff_merges_suppress_m_parsing(void);
int diff_merges_parse_opts(struct rev_info *revs, const char **argv);
diff --git a/entry.c b/entry.c
index 125fabdbd5..044e8ec92c 100644
--- a/entry.c
+++ b/entry.c
@@ -159,7 +159,8 @@ static int remove_available_paths(struct string_list_item *item, void *cb_data)
return !available;
}
-int finish_delayed_checkout(struct checkout *state, int *nr_checkouts)
+int finish_delayed_checkout(struct checkout *state, int *nr_checkouts,
+ int show_progress)
{
int errs = 0;
unsigned delayed_object_count;
@@ -173,7 +174,9 @@ int finish_delayed_checkout(struct checkout *state, int *nr_checkouts)
dco->state = CE_RETRY;
delayed_object_count = dco->paths.nr;
- progress = start_delayed_progress(_("Filtering content"), delayed_object_count);
+ progress = show_progress
+ ? start_delayed_progress(_("Filtering content"), delayed_object_count)
+ : NULL;
while (dco->filters.nr > 0) {
for_each_string_list_item(filter, &dco->filters) {
struct string_list available_paths = STRING_LIST_INIT_NODUP;
diff --git a/entry.h b/entry.h
index b8c0e170dc..7c889e58fd 100644
--- a/entry.h
+++ b/entry.h
@@ -43,7 +43,8 @@ static inline int checkout_entry(struct cache_entry *ce,
}
void enable_delayed_checkout(struct checkout *state);
-int finish_delayed_checkout(struct checkout *state, int *nr_checkouts);
+int finish_delayed_checkout(struct checkout *state, int *nr_checkouts,
+ int show_progress);
/*
* Unlink the last component and schedule the leading directories for
diff --git a/environment.c b/environment.c
index d6b22ede7e..b4ba4fa22d 100644
--- a/environment.c
+++ b/environment.c
@@ -41,7 +41,6 @@ char *apply_default_ignorewhitespace;
const char *git_attributes_file;
const char *git_hooks_path;
int zlib_compression_level = Z_BEST_SPEED;
-int core_compression_level;
int pack_compression_level = Z_DEFAULT_COMPRESSION;
int fsync_object_files;
size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
diff --git a/git-send-email.perl b/git-send-email.perl
index e65d969d0b..5262d88ee3 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -376,7 +376,7 @@ sub read_config {
@$target = @values;
}
else {
- my $v = $known_keys->{$key}->[0];
+ my $v = $known_keys->{$key}->[-1];
next unless defined $v;
next if $configured->{$setting}++;
$$target = $v;
@@ -1697,7 +1697,6 @@ EOF
$in_reply_to = $initial_in_reply_to;
$references = $initial_in_reply_to || '';
-$subject = $initial_subject;
$message_num = 0;
# Prepares the email, prompts the user, sends it out
@@ -1720,6 +1719,7 @@ sub process_file {
@xh = ();
my $input_format = undef;
my @header = ();
+ $subject = $initial_subject;
$message = "";
$message_num++;
# First unfold multiline header fields
@@ -1926,15 +1926,23 @@ sub process_file {
}
# set up for the next message
- if ($thread && $message_was_sent &&
- ($chain_reply_to || !defined $in_reply_to || length($in_reply_to) == 0 ||
- $message_num == 1)) {
- $in_reply_to = $message_id;
- if (length $references > 0) {
- $references .= "\n $message_id";
- } else {
- $references = "$message_id";
+ if ($thread) {
+ if ($message_was_sent &&
+ ($chain_reply_to || !defined $in_reply_to || length($in_reply_to) == 0 ||
+ $message_num == 1)) {
+ $in_reply_to = $message_id;
+ if (length $references > 0) {
+ $references .= "\n $message_id";
+ } else {
+ $references = "$message_id";
+ }
}
+ } elsif (!defined $initial_in_reply_to) {
+ # --thread and --in-reply-to manage the "In-Reply-To" header and by
+ # extension the "References" header. If these commands are not used, reset
+ # the header values to their defaults.
+ $in_reply_to = undef;
+ $references = '';
}
$message_id = undef;
$num_sent++;
diff --git a/mailmap.c b/mailmap.c
index 462b395634..40ce152024 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -37,6 +37,7 @@ static void free_mailmap_info(void *p, const char *s)
s, debug_str(mi->name), debug_str(mi->email));
free(mi->name);
free(mi->email);
+ free(mi);
}
static void free_mailmap_entry(void *p, const char *s)
@@ -52,6 +53,7 @@ static void free_mailmap_entry(void *p, const char *s)
me->namemap.strdup_strings = 1;
string_list_clear_func(&me->namemap, free_mailmap_info);
+ free(me);
}
/*
diff --git a/merge-ort.c b/merge-ort.c
index 6eb910d6f0..b346e23ff2 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -4065,7 +4065,7 @@ static int record_conflicted_index_entries(struct merge_options *opt)
state.istate = index;
original_cache_nr = index->cache_nr;
- /* Put every entry from paths into plist, then sort */
+ /* Append every entry from conflicted into index, then sort */
strmap_for_each_entry(&opt->priv->conflicted, &iter, e) {
const char *path = e->key;
struct conflict_info *ci = e->value;
diff --git a/pack-revindex.h b/pack-revindex.h
index 479b8f2f9c..74f4eae668 100644
--- a/pack-revindex.h
+++ b/pack-revindex.h
@@ -109,7 +109,7 @@ off_t pack_pos_to_offset(struct packed_git *p, uint32_t pos);
* If the reverse index has not yet been loaded, or the position is out of
* bounds, this function aborts.
*
- * This function runs in time O(log N) with the number of objects in the MIDX.
+ * This function runs in constant time.
*/
uint32_t pack_pos_to_midx(struct multi_pack_index *m, uint32_t pos);
@@ -120,7 +120,7 @@ uint32_t pack_pos_to_midx(struct multi_pack_index *m, uint32_t pos);
* If the reverse index has not yet been loaded, or the position is out of
* bounds, this function aborts.
*
- * This function runs in constant time.
+ * This function runs in time O(log N) with the number of objects in the MIDX.
*/
int midx_to_pack_pos(struct multi_pack_index *midx, uint32_t at, uint32_t *pos);
diff --git a/pack-write.c b/pack-write.c
index 32ebf0cdf7..a5846f3a34 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -75,9 +75,7 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
index_name = strbuf_detach(&tmp_file, NULL);
} else {
unlink(index_name);
- fd = open(index_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
- if (fd < 0)
- die_errno("unable to create '%s'", index_name);
+ fd = xopen(index_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
}
f = hashfd(fd, index_name);
}
@@ -224,6 +222,9 @@ const char *write_rev_file(const char *rev_name,
uint32_t i;
const char *ret;
+ if (!(flags & WRITE_REV) && !(flags & WRITE_REV_VERIFY))
+ return NULL;
+
ALLOC_ARRAY(pack_order, nr_objects);
for (i = 0; i < nr_objects; i++)
pack_order[i] = i;
@@ -256,9 +257,7 @@ const char *write_rev_file_order(const char *rev_name,
rev_name = strbuf_detach(&tmp_file, NULL);
} else {
unlink(rev_name);
- fd = open(rev_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
- if (fd < 0)
- die_errno("unable to create '%s'", rev_name);
+ fd = xopen(rev_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
}
f = hashfd(fd, rev_name);
} else if (flags & WRITE_REV_VERIFY) {
diff --git a/pretty.c b/pretty.c
index 9631529c10..73b5ead509 100644
--- a/pretty.c
+++ b/pretty.c
@@ -671,7 +671,11 @@ const char *repo_logmsg_reencode(struct repository *r,
* If the re-encoding failed, out might be NULL here; in that
* case we just return the commit message verbatim.
*/
- return out ? out : msg;
+ if (!out) {
+ warning("unable to reencode commit to '%s'", output_encoding);
+ return msg;
+ }
+ return out;
}
static int mailmap_name(const char **email, size_t *email_len,
diff --git a/protocol-caps.c b/protocol-caps.c
index 13a9e63a04..901b6795e4 100644
--- a/protocol-caps.c
+++ b/protocol-caps.c
@@ -69,9 +69,10 @@ static void send_info(struct repository *r, struct packet_writer *writer,
}
}
- packet_writer_write(writer, "%s",
- strbuf_detach(&send_buffer, NULL));
+ packet_writer_write(writer, "%s", send_buffer.buf);
+ strbuf_reset(&send_buffer);
}
+ strbuf_release(&send_buffer);
}
int cap_object_info(struct repository *r, struct strvec *keys,
diff --git a/protocol.c b/protocol.c
index 052d7edbb9..c53f7df5be 100644
--- a/protocol.c
+++ b/protocol.c
@@ -73,6 +73,8 @@ enum protocol_version determine_protocol_version_server(void)
string_list_clear(&list, 0);
}
+ trace2_data_intmax("transfer", NULL, "negotiated-version", version);
+
return version;
}
diff --git a/range-diff.c b/range-diff.c
index e9479794b4..cac89a2f4f 100644
--- a/range-diff.c
+++ b/range-diff.c
@@ -26,17 +26,6 @@ struct patch_util {
struct object_id oid;
};
-static size_t find_end_of_line(char *buffer, unsigned long size)
-{
- char *eol = memchr(buffer, '\n', size);
-
- if (!eol)
- return size;
-
- *eol = '\0';
- return eol + 1 - buffer;
-}
-
/*
* Reads the patches into a string list, with the `util` field being populated
* as struct object_id (will need to be free()d).
@@ -49,7 +38,7 @@ static int read_patches(const char *range, struct string_list *list,
struct patch_util *util = NULL;
int in_header = 1;
char *line, *current_filename = NULL;
- int offset, len;
+ ssize_t len;
size_t size;
strvec_pushl(&cp.args, "log", "--no-color", "-p", "--no-merges",
@@ -86,11 +75,18 @@ static int read_patches(const char *range, struct string_list *list,
line = contents.buf;
size = contents.len;
- for (offset = 0; size > 0; offset += len, size -= len, line += len) {
+ for (; size > 0; size -= len, line += len) {
const char *p;
+ char *eol;
+
+ eol = memchr(line, '\n', size);
+ if (eol) {
+ *eol = '\0';
+ len = eol + 1 - line;
+ } else {
+ len = size;
+ }
- len = find_end_of_line(line, size);
- line[len - 1] = '\0';
if (skip_prefix(line, "commit ", &p)) {
if (util) {
string_list_append(list, buf.buf)->util = util;
@@ -132,7 +128,8 @@ static int read_patches(const char *range, struct string_list *list,
strbuf_addch(&buf, '\n');
if (!util->diff_offset)
util->diff_offset = buf.len;
- line[len - 1] = '\n';
+ if (eol)
+ *eol = '\n';
orig_len = len;
len = parse_git_diff_header(&root, &linenr, 0, line,
len, size, &patch);
@@ -485,6 +482,7 @@ static void output(struct string_list *a, struct string_list *b,
else
diff_setup(&opts);
+ opts.no_free = 1;
if (!opts.output_format)
opts.output_format = DIFF_FORMAT_PATCH;
opts.flags.suppress_diff_headers = 1;
@@ -545,6 +543,8 @@ static void output(struct string_list *a, struct string_list *b,
strbuf_release(&buf);
strbuf_release(&dashes);
strbuf_release(&indent);
+ opts.no_free = 0;
+ diff_free(&opts);
}
int show_range_diff(const char *range1, const char *range2,
diff --git a/remote.c b/remote.c
index dfb863d808..40e785da38 100644
--- a/remote.c
+++ b/remote.c
@@ -135,7 +135,7 @@ static inline void init_remotes_hash(void)
static struct remote *make_remote(const char *name, int len)
{
- struct remote *ret, *replaced;
+ struct remote *ret;
struct remotes_hash_key lookup;
struct hashmap_entry lookup_entry, *e;
@@ -162,8 +162,8 @@ static struct remote *make_remote(const char *name, int len)
remotes[remotes_nr++] = ret;
hashmap_entry_init(&ret->ent, lookup_entry.hash);
- replaced = hashmap_put_entry(&remotes_hash, ret, ent);
- assert(replaced == NULL); /* no previous entry overwritten */
+ if (hashmap_put_entry(&remotes_hash, ret, ent))
+ BUG("hashmap_put overwrote entry after hashmap_get returned NULL");
return ret;
}
diff --git a/run-command.c b/run-command.c
index f72e72cce7..2961f7e55e 100644
--- a/run-command.c
+++ b/run-command.c
@@ -761,9 +761,7 @@ fail_pipe:
notify_pipe[0] = notify_pipe[1] = -1;
if (cmd->no_stdin || cmd->no_stdout || cmd->no_stderr) {
- null_fd = open("/dev/null", O_RDWR | O_CLOEXEC);
- if (null_fd < 0)
- die_errno(_("open /dev/null failed"));
+ null_fd = xopen("/dev/null", O_RDWR | O_CLOEXEC);
set_cloexec(null_fd);
}
diff --git a/send-pack.c b/send-pack.c
index 5a79e0e711..b3a495b7b1 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -425,8 +425,10 @@ static void get_commons_through_negotiation(const char *url,
child.no_stdin = 1;
child.out = -1;
strvec_pushl(&child.args, "fetch", "--negotiate-only", NULL);
- for (ref = remote_refs; ref; ref = ref->next)
- strvec_pushf(&child.args, "--negotiation-tip=%s", oid_to_hex(&ref->new_oid));
+ for (ref = remote_refs; ref; ref = ref->next) {
+ if (!is_null_oid(&ref->new_oid))
+ strvec_pushf(&child.args, "--negotiation-tip=%s", oid_to_hex(&ref->new_oid));
+ }
strvec_push(&child.args, url);
if (start_command(&child))
diff --git a/sequencer.c b/sequencer.c
index 7f07cd00f3..e4627d9196 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -983,7 +983,8 @@ static int run_git_commit(const char *defmsg,
cmd.git_cmd = 1;
- if (is_rebase_i(opts) && read_env_script(&cmd.env_array)) {
+ if (is_rebase_i(opts) && !(!defmsg && (flags & AMEND_MSG)) &&
+ read_env_script(&cmd.env_array)) {
const char *gpg_opt = gpg_sign_opt_quoted(opts);
return error(_(staged_changes_advice),
@@ -2065,7 +2066,7 @@ static int do_pick_commit(struct repository *r,
/*
* We do not intend to commit immediately. We just want to
* merge the differences in, so let's compute the tree
- * that represents the "current" state for merge-recursive
+ * that represents the "current" state for the merge machinery
* to work on.
*/
if (write_index_as_tree(&head, r->index, r->index_file, 0, NULL))
@@ -3739,10 +3740,9 @@ static struct commit *lookup_label(const char *label, int len,
static int do_merge(struct repository *r,
struct commit *commit,
const char *arg, int arg_len,
- int flags, struct replay_opts *opts)
+ int flags, int *check_todo, struct replay_opts *opts)
{
- int run_commit_flags = (flags & TODO_EDIT_MERGE_MSG) ?
- EDIT_MSG | VERIFY_MSG : 0;
+ int run_commit_flags = 0;
struct strbuf ref_name = STRBUF_INIT;
struct commit *head_commit, *merge_commit, *i;
struct commit_list *bases, *j, *reversed = NULL;
@@ -3816,6 +3816,45 @@ static int do_merge(struct repository *r,
goto leave_merge;
}
+ /*
+ * If HEAD is not identical to the first parent of the original merge
+ * commit, we cannot fast-forward.
+ */
+ can_fast_forward = opts->allow_ff && commit && commit->parents &&
+ oideq(&commit->parents->item->object.oid,
+ &head_commit->object.oid);
+
+ /*
+ * If any merge head is different from the original one, we cannot
+ * fast-forward.
+ */
+ if (can_fast_forward) {
+ struct commit_list *p = commit->parents->next;
+
+ for (j = to_merge; j && p; j = j->next, p = p->next)
+ if (!oideq(&j->item->object.oid,
+ &p->item->object.oid)) {
+ can_fast_forward = 0;
+ break;
+ }
+ /*
+ * If the number of merge heads differs from the original merge
+ * commit, we cannot fast-forward.
+ */
+ if (j || p)
+ can_fast_forward = 0;
+ }
+
+ if (can_fast_forward) {
+ rollback_lock_file(&lock);
+ ret = fast_forward_to(r, &commit->object.oid,
+ &head_commit->object.oid, 0, opts);
+ if (flags & TODO_EDIT_MERGE_MSG)
+ goto fast_forward_edit;
+
+ goto leave_merge;
+ }
+
if (commit) {
const char *encoding = get_commit_output_encoding();
const char *message = logmsg_reencode(commit, NULL, encoding);
@@ -3865,46 +3904,6 @@ static int do_merge(struct repository *r,
}
}
- /*
- * If HEAD is not identical to the first parent of the original merge
- * commit, we cannot fast-forward.
- */
- can_fast_forward = opts->allow_ff && commit && commit->parents &&
- oideq(&commit->parents->item->object.oid,
- &head_commit->object.oid);
-
- /*
- * If any merge head is different from the original one, we cannot
- * fast-forward.
- */
- if (can_fast_forward) {
- struct commit_list *p = commit->parents->next;
-
- for (j = to_merge; j && p; j = j->next, p = p->next)
- if (!oideq(&j->item->object.oid,
- &p->item->object.oid)) {
- can_fast_forward = 0;
- break;
- }
- /*
- * If the number of merge heads differs from the original merge
- * commit, we cannot fast-forward.
- */
- if (j || p)
- can_fast_forward = 0;
- }
-
- if (can_fast_forward) {
- rollback_lock_file(&lock);
- ret = fast_forward_to(r, &commit->object.oid,
- &head_commit->object.oid, 0, opts);
- if (flags & TODO_EDIT_MERGE_MSG) {
- run_commit_flags |= AMEND_MSG;
- goto fast_forward_edit;
- }
- goto leave_merge;
- }
-
if (strategy || to_merge->next) {
/* Octopus merge */
struct child_process cmd = CHILD_PROCESS_INIT;
@@ -3935,7 +3934,10 @@ static int do_merge(struct repository *r,
strvec_pushf(&cmd.args,
"-X%s", opts->xopts[k]);
}
- strvec_push(&cmd.args, "--no-edit");
+ if (!(flags & TODO_EDIT_MERGE_MSG))
+ strvec_push(&cmd.args, "--no-edit");
+ else
+ strvec_push(&cmd.args, "--edit");
strvec_push(&cmd.args, "--no-ff");
strvec_push(&cmd.args, "--no-log");
strvec_push(&cmd.args, "--no-stat");
@@ -4035,10 +4037,17 @@ static int do_merge(struct repository *r,
* value (a negative one would indicate that the `merge`
* command needs to be rescheduled).
*/
- fast_forward_edit:
ret = !!run_git_commit(git_path_merge_msg(r), opts,
run_commit_flags);
+ if (!ret && flags & TODO_EDIT_MERGE_MSG) {
+ fast_forward_edit:
+ *check_todo = 1;
+ run_commit_flags |= AMEND_MSG | EDIT_MSG | VERIFY_MSG;
+ ret = !!run_git_commit(NULL, opts, run_commit_flags);
+ }
+
+
leave_merge:
strbuf_release(&ref_name);
rollback_lock_file(&lock);
@@ -4405,9 +4414,8 @@ static int pick_commits(struct repository *r,
if ((res = do_reset(r, arg, item->arg_len, opts)))
reschedule = 1;
} else if (item->command == TODO_MERGE) {
- if ((res = do_merge(r, item->commit,
- arg, item->arg_len,
- item->flags, opts)) < 0)
+ if ((res = do_merge(r, item->commit, arg, item->arg_len,
+ item->flags, &check_todo, opts)) < 0)
reschedule = 1;
else if (item->commit)
record_in_rewritten(&item->commit->object.oid,
@@ -4716,6 +4724,9 @@ static int commit_staged_changes(struct repository *r,
refs_delete_ref(get_main_ref_store(r), "",
"CHERRY_PICK_HEAD", NULL, 0))
return error(_("could not remove CHERRY_PICK_HEAD"));
+ if (unlink(git_path_merge_msg(r)) && errno != ENOENT)
+ return error_errno(_("could not remove '%s'"),
+ git_path_merge_msg(r));
if (!final_fixup)
return 0;
}
diff --git a/setup.c b/setup.c
index eb9367ca5c..347d7181ae 100644
--- a/setup.c
+++ b/setup.c
@@ -1423,11 +1423,9 @@ const char *resolve_gitdir_gently(const char *suspect, int *return_error_code)
/* if any standard file descriptor is missing open it to /dev/null */
void sanitize_stdfds(void)
{
- int fd = open("/dev/null", O_RDWR, 0);
- while (fd != -1 && fd < 2)
- fd = dup(fd);
- if (fd == -1)
- die_errno(_("open /dev/null or dup failed"));
+ int fd = xopen("/dev/null", O_RDWR);
+ while (fd < 2)
+ fd = xdup(fd);
if (fd > 2)
close(fd);
}
diff --git a/sparse-index.c b/sparse-index.c
index c6b4feec41..56eb65dc34 100644
--- a/sparse-index.c
+++ b/sparse-index.c
@@ -283,6 +283,7 @@ void ensure_full_index(struct index_state *istate)
/* Copy back into original index. */
memcpy(&istate->name_hash, &full->name_hash, sizeof(full->name_hash));
+ memcpy(&istate->dir_hash, &full->dir_hash, sizeof(full->dir_hash));
istate->sparse_index = 0;
free(istate->cache);
istate->cache = full->cache;
diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh
index dc75b83451..ec6b9b107d 100644
--- a/t/lib-rebase.sh
+++ b/t/lib-rebase.sh
@@ -151,3 +151,59 @@ test_editor_unchanged () {
EOF
test_cmp expect actual
}
+
+# Set up an editor for testing reword commands
+# Checks that there are no uncommitted changes when rewording and that the
+# todo-list is reread after each
+set_reword_editor () {
+ >reword-actual &&
+ >reword-oid &&
+
+ # Check rewording keeps the original authorship
+ GIT_AUTHOR_NAME="Reword Author"
+ GIT_AUTHOR_EMAIL="reword.author@example.com"
+ GIT_AUTHOR_DATE=@123456
+
+ write_script reword-sequence-editor.sh <<-\EOF &&
+ todo="$(cat "$1")" &&
+ echo "exec git log -1 --pretty=format:'%an <%ae> %at%n%B%n' \
+ >>reword-actual" >"$1" &&
+ printf "%s\n" "$todo" >>"$1"
+ EOF
+
+ write_script reword-editor.sh <<-EOF &&
+ # Save the oid of the first reworded commit so we can check rebase
+ # fast-forwards to it. Also check that we do not write .git/MERGE_MSG
+ # when fast-forwarding
+ if ! test -s reword-oid
+ then
+ git rev-parse HEAD >reword-oid &&
+ if test -f .git/MERGE_MSG
+ then
+ echo 1>&2 "error: .git/MERGE_MSG exists"
+ exit 1
+ fi
+ fi &&
+ # There should be no uncommited changes
+ git diff --exit-code HEAD &&
+ # The todo-list should be re-read after a reword
+ GIT_SEQUENCE_EDITOR="\"$PWD/reword-sequence-editor.sh\"" \
+ git rebase --edit-todo &&
+ echo edited >>"\$1"
+ EOF
+
+ test_set_editor "$PWD/reword-editor.sh"
+}
+
+# Check the results of a rebase after calling set_reword_editor
+# Pass the commits that were reworded in the order that they were picked
+# Expects the first pick to be a fast-forward
+check_reworded_commits () {
+ test_cmp_rev "$(cat reword-oid)" "$1^{commit}" &&
+ git log --format="%an <%ae> %at%n%B%nedited%n" --no-walk=unsorted "$@" \
+ >reword-expected &&
+ test_cmp reword-expected reword-actual &&
+ git log --format="%an <%ae> %at%n%B" -n $# --first-parent --reverse \
+ >reword-log &&
+ test_cmp reword-expected reword-log
+}
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index cb87768513..5c342de713 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -1271,28 +1271,29 @@ P=$(test_oid root)
test_expect_success 'git commit-tree records the correct tree in a commit' '
commit0=$(echo NO | git commit-tree $P) &&
- tree=$(git show --pretty=raw $commit0 |
- sed -n -e "s/^tree //p" -e "/^author /q") &&
+ git show --pretty=raw $commit0 >out &&
+ tree=$(sed -n -e "s/^tree //p" -e "/^author /q" out) &&
test "z$tree" = "z$P"
'
test_expect_success 'git commit-tree records the correct parent in a commit' '
commit1=$(echo NO | git commit-tree $P -p $commit0) &&
- parent=$(git show --pretty=raw $commit1 |
- sed -n -e "s/^parent //p" -e "/^author /q") &&
+ git show --pretty=raw $commit1 >out &&
+ parent=$(sed -n -e "s/^parent //p" -e "/^author /q" out) &&
test "z$commit0" = "z$parent"
'
test_expect_success 'git commit-tree omits duplicated parent in a commit' '
commit2=$(echo NO | git commit-tree $P -p $commit0 -p $commit0) &&
- parent=$(git show --pretty=raw $commit2 |
- sed -n -e "s/^parent //p" -e "/^author /q" |
- sort -u) &&
+ git show --pretty=raw $commit2 >out &&
+ cat >match.sed <<-\EOF &&
+ s/^parent //p
+ /^author /q
+ EOF
+ parent=$(sed -n -f match.sed out | sort -u) &&
test "z$commit0" = "z$parent" &&
- numparent=$(git show --pretty=raw $commit2 |
- sed -n -e "s/^parent //p" -e "/^author /q" |
- wc -l) &&
- test $numparent = 1
+ git show --pretty=raw $commit2 >out &&
+ test_stdout_line_count = 1 sed -n -f match.sed out
'
test_expect_success 'update-index D/F conflict' '
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index b5749f327d..33dfc9cd56 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -6,6 +6,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
TEST_ROOT="$PWD"
PATH=$TEST_ROOT:$PATH
@@ -1061,4 +1062,74 @@ test_expect_success PERL,SYMLINKS,CASE_INSENSITIVE_FS \
)
'
+test_expect_success PERL 'setup for progress tests' '
+ git init progress &&
+ (
+ cd progress &&
+ git config filter.delay.process "rot13-filter.pl delay-progress.log clean smudge delay" &&
+ git config filter.delay.required true &&
+
+ echo "*.a filter=delay" >.gitattributes &&
+ touch test-delay10.a &&
+ git add . &&
+ git commit -m files
+ )
+'
+
+test_delayed_checkout_progress () {
+ if test "$1" = "!"
+ then
+ local expect_progress=N &&
+ shift
+ else
+ local expect_progress=
+ fi &&
+
+ if test $# -lt 1
+ then
+ BUG "no command given to test_delayed_checkout_progress"
+ fi &&
+
+ (
+ cd progress &&
+ GIT_PROGRESS_DELAY=0 &&
+ export GIT_PROGRESS_DELAY &&
+ rm -f *.a delay-progress.log &&
+
+ "$@" 2>err &&
+ grep "IN: smudge test-delay10.a .* \\[DELAYED\\]" delay-progress.log &&
+ if test "$expect_progress" = N
+ then
+ ! grep "Filtering content" err
+ else
+ grep "Filtering content" err
+ fi
+ )
+}
+
+for mode in pathspec branch
+do
+ case "$mode" in
+ pathspec) opt='.' ;;
+ branch) opt='-f HEAD' ;;
+ esac
+
+ test_expect_success PERL,TTY "delayed checkout shows progress by default on tty ($mode checkout)" '
+ test_delayed_checkout_progress test_terminal git checkout $opt
+ '
+
+ test_expect_success PERL "delayed checkout ommits progress on non-tty ($mode checkout)" '
+ test_delayed_checkout_progress ! git checkout $opt
+ '
+
+ test_expect_success PERL,TTY "delayed checkout ommits progress with --quiet ($mode checkout)" '
+ test_delayed_checkout_progress ! test_terminal git checkout --quiet $opt
+ '
+
+ test_expect_success PERL,TTY "delayed checkout honors --[no]-progress ($mode checkout)" '
+ test_delayed_checkout_progress ! test_terminal git checkout --no-progress $opt &&
+ test_delayed_checkout_progress test_terminal git checkout --quiet --progress $opt
+ '
+done
+
test_done
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index cc4b10236e..e575ffb4ff 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -1272,6 +1272,19 @@ test_expect_success 'attempt to delete a branch merged to its base' '
test_must_fail git branch -d my10
'
+test_expect_success 'branch --delete --force removes dangling branch' '
+ git checkout main &&
+ test_commit unstable &&
+ hash=$(git rev-parse HEAD) &&
+ objpath=$(echo $hash | sed -e "s|^..|.git/objects/&/|") &&
+ git branch --no-track dangling &&
+ mv $objpath $objpath.x &&
+ test_when_finished "mv $objpath.x $objpath" &&
+ git branch --delete --force dangling &&
+ git for-each-ref refs/heads/dangling >actual &&
+ test_must_be_empty actual
+'
+
test_expect_success 'use --edit-description' '
write_script editor <<-\EOF &&
echo "New contents" >"$1"
diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh
index e26762d0b2..f6e4864497 100755
--- a/t/t3403-rebase-skip.sh
+++ b/t/t3403-rebase-skip.sh
@@ -20,6 +20,7 @@ test_expect_success setup '
git add hello &&
git commit -m "hello" &&
git branch skip-reference &&
+ git tag hello &&
echo world >> hello &&
git commit -a -m "hello world" &&
@@ -36,7 +37,8 @@ test_expect_success setup '
test_tick &&
GIT_AUTHOR_NAME="Another Author" \
GIT_AUTHOR_EMAIL="another.author@example.com" \
- git commit --amend --no-edit -m amended-goodbye &&
+ git commit --amend --no-edit -m amended-goodbye \
+ --reset-author &&
test_tick &&
git tag amended-goodbye &&
@@ -51,7 +53,7 @@ test_expect_success setup '
'
test_expect_success 'rebase with git am -3 (default)' '
- test_must_fail git rebase main
+ test_must_fail git rebase --apply main
'
test_expect_success 'rebase --skip can not be used with other options' '
@@ -95,6 +97,13 @@ test_expect_success 'moved back to branch correctly' '
test_debug 'gitk --all & sleep 1'
+test_expect_success 'skipping final pick removes .git/MERGE_MSG' '
+ test_must_fail git rebase --onto hello reverted-goodbye^ \
+ reverted-goodbye &&
+ git rebase --skip &&
+ test_path_is_missing .git/MERGE_MSG
+'
+
test_expect_success 'correct advice upon picking empty commit' '
test_when_finished "git rebase --abort" &&
test_must_fail git rebase -i --onto goodbye \
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 66bcbbf952..d877872e8f 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -839,6 +839,19 @@ test_expect_success 'reword' '
git show HEAD~2 | grep "C changed"
'
+test_expect_success 'no uncommited changes when rewording the todo list is reloaded' '
+ git checkout E &&
+ test_when_finished "git checkout @{-1}" &&
+ (
+ set_fake_editor &&
+ GIT_SEQUENCE_EDITOR="\"$PWD/fake-editor.sh\"" &&
+ export GIT_SEQUENCE_EDITOR &&
+ set_reword_editor &&
+ FAKE_LINES="reword 1 reword 2" git rebase -i C
+ ) &&
+ check_reworded_commits D E
+'
+
test_expect_success 'rebase -i can copy notes' '
git config notes.rewrite.rebase true &&
git config notes.rewriteRef "refs/notes/*" &&
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
index f4c2ee02bc..738fbae9b2 100755
--- a/t/t3418-rebase-continue.sh
+++ b/t/t3418-rebase-continue.sh
@@ -21,7 +21,7 @@ test_expect_success 'setup' '
git checkout main
'
-test_expect_success 'interactive rebase --continue works with touched file' '
+test_expect_success 'merge based rebase --continue with works with touched file' '
rm -fr .git/rebase-* &&
git reset --hard &&
git checkout main &&
@@ -31,12 +31,22 @@ test_expect_success 'interactive rebase --continue works with touched file' '
git rebase --continue
'
-test_expect_success 'non-interactive rebase --continue works with touched file' '
+test_expect_success 'merge based rebase --continue removes .git/MERGE_MSG' '
+ git checkout -f --detach topic &&
+
+ test_must_fail git rebase --onto main HEAD^ &&
+ git read-tree --reset -u HEAD &&
+ test_path_is_file .git/MERGE_MSG &&
+ git rebase --continue &&
+ test_path_is_missing .git/MERGE_MSG
+'
+
+test_expect_success 'apply based rebase --continue works with touched file' '
rm -fr .git/rebase-* &&
git reset --hard &&
git checkout main &&
- test_must_fail git rebase --onto main main topic &&
+ test_must_fail git rebase --apply --onto main main topic &&
echo "Resolved" >F2 &&
git add F2 &&
test-tool chmtime =-60 F1 &&
@@ -254,7 +264,7 @@ test_rerere_autoupdate () {
'
}
-test_rerere_autoupdate
+test_rerere_autoupdate --apply
test_rerere_autoupdate -m
GIT_SEQUENCE_EDITOR=: && export GIT_SEQUENCE_EDITOR
test_rerere_autoupdate -i
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index 6748070df5..43c82d9a33 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -172,19 +172,39 @@ test_expect_success 'failed `merge <branch>` does not crash' '
grep "^Merge branch ${SQ}G${SQ}$" .git/rebase-merge/message
'
-test_expect_success 'fast-forward merge -c still rewords' '
- git checkout -b fast-forward-merge-c H &&
+test_expect_success 'merge -c commits before rewording and reloads todo-list' '
+ cat >script-from-scratch <<-\EOF &&
+ merge -c E B
+ merge -c H G
+ EOF
+
+ git checkout -b merge-c H &&
(
- set_fake_editor &&
- FAKE_COMMIT_MESSAGE=edited \
- GIT_SEQUENCE_EDITOR="echo merge -c H G >" \
- git rebase -ir @^
+ set_reword_editor &&
+ GIT_SEQUENCE_EDITOR="\"$PWD/replace-editor.sh\"" \
+ git rebase -i -r D
) &&
- echo edited >expected &&
- git log --pretty=format:%B -1 >actual &&
- test_cmp expected actual
+ check_reworded_commits E H
'
+test_expect_success 'merge -c rewords when a strategy is given' '
+ git checkout -b merge-c-with-strategy H &&
+ write_script git-merge-override <<-\EOF &&
+ echo overridden$1 >G.t
+ git add G.t
+ EOF
+
+ PATH="$PWD:$PATH" \
+ GIT_SEQUENCE_EDITOR="echo merge -c H G >" \
+ GIT_EDITOR="echo edited >>" \
+ git rebase --no-ff -ir -s override -Xxopt E &&
+ test_write_lines overridden--xopt >expect &&
+ test_cmp expect G.t &&
+ test_write_lines H "" edited "" >expect &&
+ git log --format=%B -1 >actual &&
+ test_cmp expect actual
+
+'
test_expect_success 'with a branch tip that was cherry-picked already' '
git checkout -b already-upstream main &&
base="$(git rev-parse --verify HEAD)" &&
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index e561a8e485..28683d059d 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -65,7 +65,7 @@ test_expect_success setup '
export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
git checkout master &&
- git pull -s ours . side &&
+ git pull -s ours --no-rebase . side &&
GIT_AUTHOR_DATE="2006-06-26 00:05:00 +0000" &&
GIT_COMMITTER_DATE="2006-06-26 00:05:00 +0000" &&
diff --git a/t/t4045-diff-relative.sh b/t/t4045-diff-relative.sh
index 61ba5f707f..fab351b48a 100755
--- a/t/t4045-diff-relative.sh
+++ b/t/t4045-diff-relative.sh
@@ -162,4 +162,57 @@ check_diff_relative_option subdir file2 true --no-relative --relative
check_diff_relative_option . file2 false --no-relative --relative=subdir
check_diff_relative_option . file2 true --no-relative --relative=subdir
+test_expect_success 'setup diff --relative unmerged' '
+ test_commit zero file0 &&
+ test_commit base subdir/file0 &&
+ git switch -c br1 &&
+ test_commit one file0 &&
+ test_commit sub1 subdir/file0 &&
+ git switch -c br2 base &&
+ test_commit two file0 &&
+ git switch -c br3 &&
+ test_commit sub3 subdir/file0
+'
+
+test_expect_success 'diff --relative without change in subdir' '
+ git switch br2 &&
+ test_when_finished "git merge --abort" &&
+ test_must_fail git merge one &&
+ git -C subdir diff --relative >out &&
+ test_must_be_empty out &&
+ git -C subdir diff --relative --name-only >out &&
+ test_must_be_empty out
+'
+
+test_expect_success 'diff --relative --name-only with change in subdir' '
+ git switch br3 &&
+ test_when_finished "git merge --abort" &&
+ test_must_fail git merge sub1 &&
+ test_write_lines file0 file0 >expected &&
+ git -C subdir diff --relative --name-only >out &&
+ test_cmp expected out
+'
+
+test_expect_failure 'diff --relative with change in subdir' '
+ git switch br3 &&
+ br1_blob=$(git rev-parse --short --verify br1:subdir/file0) &&
+ br3_blob=$(git rev-parse --short --verify br3:subdir/file0) &&
+ test_when_finished "git merge --abort" &&
+ test_must_fail git merge br1 &&
+ cat >expected <<-EOF &&
+ diff --cc file0
+ index $br3_blob,$br1_blob..0000000
+ --- a/file0
+ +++ b/file0
+ @@@ -1,1 -1,1 +1,5 @@@
+ ++<<<<<<< HEAD
+ +sub3
+ ++=======
+ + sub1
+ ++>>>>>>> br1
+ EOF
+ git -C subdir diff --relative >out &&
+ test_cmp expected out
+'
+
test_done
diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh
index fad6d3f542..d370ecfe0d 100755
--- a/t/t4103-apply-binary.sh
+++ b/t/t4103-apply-binary.sh
@@ -158,4 +158,27 @@ test_expect_success 'apply binary -p0 diff' '
test -z "$(git diff --name-status binary -- file3)"
'
+test_expect_success 'reject truncated binary diff' '
+ do_reset &&
+
+ # this length is calculated to get us very close to
+ # the 8192-byte strbuf we will use to read in the patch.
+ test-tool genrandom foo 6205 >file1 &&
+ git diff --binary >patch &&
+
+ # truncate the patch at the second "literal" line,
+ # but exclude the trailing newline. We must use perl
+ # for this, since tools like "sed" cannot reliably
+ # produce output without the trailing newline.
+ perl -pe "
+ if (/^literal/ && \$count++ >= 1) {
+ chomp;
+ print;
+ exit 0;
+ }
+ " <patch >patch.trunc &&
+
+ do_reset &&
+ test_must_fail git apply patch.trunc
+'
test_done
diff --git a/t/t4108-apply-threeway.sh b/t/t4108-apply-threeway.sh
index 65147efdea..cc3aa3314a 100755
--- a/t/t4108-apply-threeway.sh
+++ b/t/t4108-apply-threeway.sh
@@ -230,4 +230,49 @@ test_expect_success 'apply with --3way --cached and conflicts' '
test_cmp expect.diff actual.diff
'
+test_expect_success 'apply binary file patch' '
+ git reset --hard main &&
+ cp "$TEST_DIRECTORY/test-binary-1.png" bin.png &&
+ git add bin.png &&
+ git commit -m "add binary file" &&
+
+ cp "$TEST_DIRECTORY/test-binary-2.png" bin.png &&
+
+ git diff --binary >bin.diff &&
+ git reset --hard &&
+
+ # Apply must succeed.
+ git apply bin.diff
+'
+
+test_expect_success 'apply binary file patch with 3way' '
+ git reset --hard main &&
+ cp "$TEST_DIRECTORY/test-binary-1.png" bin.png &&
+ git add bin.png &&
+ git commit -m "add binary file" &&
+
+ cp "$TEST_DIRECTORY/test-binary-2.png" bin.png &&
+
+ git diff --binary >bin.diff &&
+ git reset --hard &&
+
+ # Apply must succeed.
+ git apply --3way --index bin.diff
+'
+
+test_expect_success 'apply full-index patch with 3way' '
+ git reset --hard main &&
+ cp "$TEST_DIRECTORY/test-binary-1.png" bin.png &&
+ git add bin.png &&
+ git commit -m "add binary file" &&
+
+ cp "$TEST_DIRECTORY/test-binary-2.png" bin.png &&
+
+ git diff --full-index >bin.diff &&
+ git reset --hard &&
+
+ # Apply must succeed.
+ git apply --3way --index bin.diff
+'
+
test_done
diff --git a/t/t4210-log-i18n.sh b/t/t4210-log-i18n.sh
index d2dfcf164e..0141f36e33 100755
--- a/t/t4210-log-i18n.sh
+++ b/t/t4210-log-i18n.sh
@@ -131,4 +131,11 @@ do
fi
done
+test_expect_success 'log shows warning when conversion fails' '
+ enc=this-encoding-does-not-exist &&
+ git log -1 --encoding=$enc 2>err &&
+ echo "warning: unable to reencode commit to ${SQ}${enc}${SQ}" >expect &&
+ test_cmp expect err
+'
+
test_done
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index 5c5e53f0be..e13a884207 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -34,6 +34,110 @@ test_expect_success 'setup' '
} >expect
'
+test_expect_success 'setup pack-object <stdin' '
+ git init pack-object-stdin &&
+ test_commit -C pack-object-stdin one &&
+ test_commit -C pack-object-stdin two
+
+'
+
+test_expect_success 'pack-object <stdin parsing: basic [|--revs]' '
+ cat >in <<-EOF &&
+ $(git -C pack-object-stdin rev-parse one)
+ EOF
+
+ git -C pack-object-stdin pack-objects basic-stdin <in &&
+ idx=$(echo pack-object-stdin/basic-stdin-*.idx) &&
+ git show-index <"$idx" >actual &&
+ test_line_count = 1 actual &&
+
+ git -C pack-object-stdin pack-objects --revs basic-stdin-revs <in &&
+ idx=$(echo pack-object-stdin/basic-stdin-revs-*.idx) &&
+ git show-index <"$idx" >actual &&
+ test_line_count = 3 actual
+'
+
+test_expect_success 'pack-object <stdin parsing: [|--revs] bad line' '
+ cat >in <<-EOF &&
+ $(git -C pack-object-stdin rev-parse one)
+ garbage
+ $(git -C pack-object-stdin rev-parse two)
+ EOF
+
+ sed "s/^> //g" >err.expect <<-EOF &&
+ fatal: expected object ID, got garbage:
+ > garbage
+
+ EOF
+ test_must_fail git -C pack-object-stdin pack-objects bad-line-stdin <in 2>err.actual &&
+ test_cmp err.expect err.actual &&
+
+ cat >err.expect <<-EOF &&
+ fatal: bad revision '"'"'garbage'"'"'
+ EOF
+ test_must_fail git -C pack-object-stdin pack-objects --revs bad-line-stdin-revs <in 2>err.actual &&
+ test_cmp err.expect err.actual
+'
+
+test_expect_success 'pack-object <stdin parsing: [|--revs] empty line' '
+ cat >in <<-EOF &&
+ $(git -C pack-object-stdin rev-parse one)
+
+ $(git -C pack-object-stdin rev-parse two)
+ EOF
+
+ sed -e "s/^> //g" -e "s/Z$//g" >err.expect <<-EOF &&
+ fatal: expected object ID, got garbage:
+ > Z
+
+ EOF
+ test_must_fail git -C pack-object-stdin pack-objects empty-line-stdin <in 2>err.actual &&
+ test_cmp err.expect err.actual &&
+
+ git -C pack-object-stdin pack-objects --revs empty-line-stdin-revs <in &&
+ idx=$(echo pack-object-stdin/empty-line-stdin-revs-*.idx) &&
+ git show-index <"$idx" >actual &&
+ test_line_count = 3 actual
+'
+
+test_expect_success 'pack-object <stdin parsing: [|--revs] with --stdin' '
+ cat >in <<-EOF &&
+ $(git -C pack-object-stdin rev-parse one)
+ $(git -C pack-object-stdin rev-parse two)
+ EOF
+
+ # There is the "--stdin-packs is incompatible with --revs"
+ # test below, but we should make sure that the revision.c
+ # --stdin is not picked up
+ cat >err.expect <<-EOF &&
+ fatal: disallowed abbreviated or ambiguous option '"'"'stdin'"'"'
+ EOF
+ test_must_fail git -C pack-object-stdin pack-objects stdin-with-stdin-option --stdin <in 2>err.actual &&
+ test_cmp err.expect err.actual &&
+
+ test_must_fail git -C pack-object-stdin pack-objects --stdin --revs stdin-with-stdin-option-revs 2>err.actual <in &&
+ test_cmp err.expect err.actual
+'
+
+test_expect_success 'pack-object <stdin parsing: --stdin-packs handles garbage' '
+ cat >in <<-EOF &&
+ $(git -C pack-object-stdin rev-parse one)
+ $(git -C pack-object-stdin rev-parse two)
+ EOF
+
+ # That we get "two" and not "one" has to do with OID
+ # ordering. It happens to be the same here under SHA-1 and
+ # SHA-256. See commentary in pack-objects.c
+ cat >err.expect <<-EOF &&
+ fatal: could not find pack '"'"'$(git -C pack-object-stdin rev-parse two)'"'"'
+ EOF
+ test_must_fail git \
+ -C pack-object-stdin \
+ pack-objects stdin-with-stdin-option --stdin-packs \
+ <in 2>err.actual &&
+ test_cmp err.expect err.actual
+'
+
# usage: check_deltas <stderr_from_pack_objects> <cmp_op> <nr_deltas>
# e.g.: check_deltas stderr -gt 0
check_deltas() {
diff --git a/t/t5323-pack-redundant.sh b/t/t5323-pack-redundant.sh
index 8b01793845..8dbbcc5e51 100755
--- a/t/t5323-pack-redundant.sh
+++ b/t/t5323-pack-redundant.sh
@@ -114,9 +114,9 @@ test_expect_success 'setup main repo' '
create_commits_in "$main_repo" A B C D E F G H I J K L M N O P Q R
'
-test_expect_success 'master: pack-redundant works with no packfile' '
+test_expect_success 'main: pack-redundant works with no packfile' '
(
- cd "$master_repo" &&
+ cd "$main_repo" &&
cat >expect <<-EOF &&
fatal: Zero packs found!
EOF
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index e83b2a6506..a0faf0dd94 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -1214,6 +1214,19 @@ test_expect_success '--negotiation-tip understands abbreviated SHA-1' '
check_negotiation_tip
'
+test_expect_success '--negotiation-tip rejects missing OIDs' '
+ setup_negotiation_tip server server 0 &&
+ test_must_fail git -C client fetch \
+ --negotiation-tip=alpha_1 \
+ --negotiation-tip=$(test_oid zero) \
+ origin alpha_s beta_s 2>err &&
+ cat >fatal-expect <<-EOF &&
+ fatal: the object $(test_oid zero) does not exist
+EOF
+ grep fatal: err >fatal-actual &&
+ test_cmp fatal-expect fatal-actual
+'
+
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 0916f76302..4db8edd9c8 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -201,6 +201,7 @@ test_expect_success 'push with negotiation' '
# Without negotiation
mk_empty testrepo &&
git push testrepo $the_first_commit:refs/remotes/origin/first_commit &&
+ test_commit -C testrepo unrelated_commit &&
git -C testrepo config receive.hideRefs refs/remotes/origin/first_commit &&
echo now pushing without negotiation &&
GIT_TRACE2_EVENT="$(pwd)/event" git -c protocol.version=2 push testrepo refs/heads/main:refs/remotes/origin/main &&
@@ -210,6 +211,7 @@ test_expect_success 'push with negotiation' '
rm event &&
mk_empty testrepo &&
git push testrepo $the_first_commit:refs/remotes/origin/first_commit &&
+ test_commit -C testrepo unrelated_commit &&
git -C testrepo config receive.hideRefs refs/remotes/origin/first_commit &&
GIT_TRACE2_EVENT="$(pwd)/event" git -c protocol.version=2 -c push.negotiate=1 push testrepo refs/heads/main:refs/remotes/origin/main &&
grep_wrote 2 event # 1 commit, 1 tree
@@ -219,6 +221,7 @@ test_expect_success 'push with negotiation proceeds anyway even if negotiation f
rm event &&
mk_empty testrepo &&
git push testrepo $the_first_commit:refs/remotes/origin/first_commit &&
+ test_commit -C testrepo unrelated_commit &&
git -C testrepo config receive.hideRefs refs/remotes/origin/first_commit &&
GIT_TEST_PROTOCOL_VERSION=0 GIT_TRACE2_EVENT="$(pwd)/event" \
git -c push.negotiate=1 push testrepo refs/heads/main:refs/remotes/origin/main 2>err &&
@@ -1767,5 +1770,4 @@ test_expect_success 'denyCurrentBranch and worktrees' '
git -C cloned push origin HEAD:new-wt &&
test_must_fail git -C cloned push --delete origin new-wt
'
-
test_done
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index e2c0c51022..672001a18b 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -136,12 +136,12 @@ test_expect_success 'the default remote . should not break explicit pull' '
git reset --hard HEAD^ &&
echo file >expect &&
test_cmp expect file &&
- git pull . second &&
+ git pull --no-rebase . second &&
echo modified >expect &&
test_cmp expect file &&
git reflog -1 >reflog.actual &&
sed "s/^[0-9a-f][0-9a-f]*/OBJID/" reflog.actual >reflog.fuzzy &&
- echo "OBJID HEAD@{0}: pull . second: Fast-forward" >reflog.expected &&
+ echo "OBJID HEAD@{0}: pull --no-rebase . second: Fast-forward" >reflog.expected &&
test_cmp reflog.expected reflog.fuzzy
'
@@ -226,7 +226,7 @@ test_expect_success 'fail if the index has unresolved entries' '
test_commit modified2 file &&
git ls-files -u >unmerged &&
test_must_be_empty unmerged &&
- test_must_fail git pull . second &&
+ test_must_fail git pull --no-rebase . second &&
git ls-files -u >unmerged &&
test_file_not_empty unmerged &&
cp file expected &&
@@ -409,37 +409,37 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '
test_expect_success 'pull succeeds with dirty working directory and merge.autostash set' '
test_config merge.autostash true &&
- test_pull_autostash 2
+ test_pull_autostash 2 --no-rebase
'
test_expect_success 'pull --autostash & merge.autostash=true' '
test_config merge.autostash true &&
- test_pull_autostash 2 --autostash
+ test_pull_autostash 2 --autostash --no-rebase
'
test_expect_success 'pull --autostash & merge.autostash=false' '
test_config merge.autostash false &&
- test_pull_autostash 2 --autostash
+ test_pull_autostash 2 --autostash --no-rebase
'
test_expect_success 'pull --autostash & merge.autostash unset' '
test_unconfig merge.autostash &&
- test_pull_autostash 2 --autostash
+ test_pull_autostash 2 --autostash --no-rebase
'
test_expect_success 'pull --no-autostash & merge.autostash=true' '
test_config merge.autostash true &&
- test_pull_autostash_fail --no-autostash
+ test_pull_autostash_fail --no-autostash --no-rebase
'
test_expect_success 'pull --no-autostash & merge.autostash=false' '
test_config merge.autostash false &&
- test_pull_autostash_fail --no-autostash
+ test_pull_autostash_fail --no-autostash --no-rebase
'
test_expect_success 'pull --no-autostash & merge.autostash unset' '
test_unconfig merge.autostash &&
- test_pull_autostash_fail --no-autostash
+ test_pull_autostash_fail --no-autostash --no-rebase
'
test_expect_success 'pull.rebase' '
diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
index 63a688bdbf..7601c919fd 100755
--- a/t/t5521-pull-options.sh
+++ b/t/t5521-pull-options.sh
@@ -113,7 +113,7 @@ test_expect_success 'git pull --force' '
git pull two &&
test_commit A &&
git branch -f origin &&
- git pull --all --force
+ git pull --no-rebase --all --force
)
'
@@ -179,7 +179,7 @@ test_expect_success 'git pull --allow-unrelated-histories' '
(
cd dst &&
test_must_fail git pull ../src side &&
- git pull --allow-unrelated-histories ../src side
+ git pull --no-rebase --allow-unrelated-histories ../src side
)
'
diff --git a/t/t5524-pull-msg.sh b/t/t5524-pull-msg.sh
index c278adaa5a..b2be3605f5 100755
--- a/t/t5524-pull-msg.sh
+++ b/t/t5524-pull-msg.sh
@@ -28,7 +28,7 @@ test_expect_success setup '
test_expect_success pull '
(
cd cloned &&
- git pull --log &&
+ git pull --no-rebase --log &&
git log -2 &&
git cat-file commit HEAD >result &&
grep Dollar result
@@ -41,7 +41,7 @@ test_expect_success '--log=1 limits shortlog length' '
git reset --hard HEAD^ &&
test "$(cat afile)" = original &&
test "$(cat bfile)" = added &&
- git pull --log=1 &&
+ git pull --no-rebase --log=1 &&
git log -3 &&
git cat-file commit HEAD >result &&
grep Dollar result &&
diff --git a/t/t5549-fetch-push-http.sh b/t/t5549-fetch-push-http.sh
new file mode 100755
index 0000000000..2cdebcb735
--- /dev/null
+++ b/t/t5549-fetch-push-http.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+test_description='fetch/push functionality using the HTTP protocol'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server"
+URI="$HTTPD_URL/smart/server"
+
+grep_wrote () {
+ object_count=$1
+ file_name=$2
+ grep 'write_pack_file/wrote.*"value":"'$1'"' $2
+}
+
+setup_client_and_server () {
+ git init client &&
+ test_when_finished 'rm -rf client' &&
+ test_commit -C client first_commit &&
+ test_commit -C client second_commit &&
+
+ git init "$SERVER" &&
+ test_when_finished 'rm -rf "$SERVER"' &&
+ test_config -C "$SERVER" http.receivepack true &&
+ test_commit -C "$SERVER" unrelated_commit &&
+ git -C client push "$URI" first_commit:refs/remotes/origin/first_commit &&
+ git -C "$SERVER" config receive.hideRefs refs/remotes/origin/first_commit
+}
+
+test_expect_success 'push without negotiation (for comparing object counts with the next test)' '
+ setup_client_and_server &&
+
+ GIT_TRACE2_EVENT="$(pwd)/event" git -C client -c protocol.version=2 \
+ push "$URI" refs/heads/main:refs/remotes/origin/main &&
+ test_when_finished "rm -f event" &&
+ grep_wrote 6 event # 2 commits, 2 trees, 2 blobs
+'
+
+test_expect_success 'push with negotiation' '
+ setup_client_and_server &&
+
+ GIT_TRACE2_EVENT="$(pwd)/event" git -C client -c protocol.version=2 -c push.negotiate=1 \
+ push "$URI" refs/heads/main:refs/remotes/origin/main &&
+ test_when_finished "rm -f event" &&
+ grep_wrote 3 event # 1 commit, 1 tree, 1 blob
+'
+
+test_expect_success 'push with negotiation proceeds anyway even if negotiation fails' '
+ setup_client_and_server &&
+
+ # Use protocol v0 to make negotiation fail (because protocol v0 does
+ # not support the "wait-for-done" capability, which is required for
+ # push negotiation)
+ GIT_TEST_PROTOCOL_VERSION=0 GIT_TRACE2_EVENT="$(pwd)/event" git -C client -c push.negotiate=1 \
+ push "$URI" refs/heads/main:refs/remotes/origin/main 2>err &&
+ test_when_finished "rm -f event" &&
+ grep_wrote 6 event && # 2 commits, 2 trees, 2 blobs
+
+ cat >warning-expect <<-EOF &&
+ warning: --negotiate-only requires protocol v2
+ warning: push negotiation failed; proceeding anyway with push
+EOF
+ grep warning: err >warning-actual &&
+ test_cmp warning-expect warning-actual
+'
+
+test_done
diff --git a/t/t5553-set-upstream.sh b/t/t5553-set-upstream.sh
index b1d614ce18..9c12c0f8c3 100755
--- a/t/t5553-set-upstream.sh
+++ b/t/t5553-set-upstream.sh
@@ -108,27 +108,27 @@ test_expect_success 'setup commit on main and other pull' '
test_expect_success 'pull --set-upstream upstream main sets branch main but not other' '
clear_config main other &&
- git pull --set-upstream upstream main &&
+ git pull --no-rebase --set-upstream upstream main &&
check_config main upstream refs/heads/main &&
check_config_missing other
'
test_expect_success 'pull --set-upstream main:other2 does not set the branch other2' '
clear_config other2 &&
- git pull --set-upstream upstream main:other2 &&
+ git pull --no-rebase --set-upstream upstream main:other2 &&
check_config_missing other2
'
test_expect_success 'pull --set-upstream upstream other sets branch main' '
clear_config main other &&
- git pull --set-upstream upstream other &&
+ git pull --no-rebase --set-upstream upstream other &&
check_config main upstream refs/heads/other &&
check_config_missing other
'
test_expect_success 'pull --set-upstream upstream tag does not set the tag' '
clear_config three &&
- git pull --tags --set-upstream upstream three &&
+ git pull --no-rebase --tags --set-upstream upstream three &&
check_config_missing three
'
@@ -144,16 +144,16 @@ test_expect_success 'pull --set-upstream http://nosuchdomain.example.com fails w
test_expect_success 'pull --set-upstream upstream HEAD sets branch HEAD' '
clear_config main other &&
- git pull --set-upstream upstream HEAD &&
+ git pull --no-rebase --set-upstream upstream HEAD &&
check_config main upstream HEAD &&
git checkout other &&
- git pull --set-upstream upstream HEAD &&
+ git pull --no-rebase --set-upstream upstream HEAD &&
check_config other upstream HEAD
'
test_expect_success 'pull --set-upstream upstream with more than one branch does nothing' '
clear_config main three &&
- git pull --set-upstream upstream main three &&
+ git pull --no-rebase --set-upstream upstream main three &&
check_config_missing main &&
check_config_missing three
'
diff --git a/t/t5562/invoke-with-content-length.pl b/t/t5562/invoke-with-content-length.pl
index 0943474af2..718dd9b49d 100644
--- a/t/t5562/invoke-with-content-length.pl
+++ b/t/t5562/invoke-with-content-length.pl
@@ -13,11 +13,6 @@ my $body_data;
defined read($body_fh, $body_data, $body_size) or die "Cannot read $body_filename: $!";
close($body_fh);
-my $exited = 0;
-$SIG{"CHLD"} = sub {
- $exited = 1;
-};
-
# write data
my $pid = open(my $out, "|-", @command);
{
@@ -29,8 +24,13 @@ my $pid = open(my $out, "|-", @command);
}
print $out $body_data or die "Cannot write data: $!";
-sleep 60; # is interrupted by SIGCHLD
-if (!$exited) {
- close($out);
+$SIG{ALRM} = sub {
+ kill 'KILL', $pid;
die "Command did not exit after reading whole body";
+};
+alarm 60;
+
+my $ret = waitpid($pid, 0);
+if ($ret != $pid) {
+ die "confusing return from waitpid: $ret";
}
diff --git a/t/t5582-fetch-negative-refspec.sh b/t/t5582-fetch-negative-refspec.sh
index e5d2e79ad3..7a80e47c2b 100755
--- a/t/t5582-fetch-negative-refspec.sh
+++ b/t/t5582-fetch-negative-refspec.sh
@@ -105,7 +105,6 @@ test_expect_success "fetch with negative pattern refspec does not expand prefix"
'
test_expect_success "fetch with negative refspec avoids duplicate conflict" '
- cd "$D" &&
(
cd one &&
git branch dups/a &&
diff --git a/t/t5604-clone-reference.sh b/t/t5604-clone-reference.sh
index e845d621f6..24340e6d56 100755
--- a/t/t5604-clone-reference.sh
+++ b/t/t5604-clone-reference.sh
@@ -87,7 +87,7 @@ test_expect_success 'updating origin' '
'
test_expect_success 'pulling changes from origin' '
- git -C C pull origin
+ git -C C pull --no-rebase origin
'
# the 2 local objects are commit and tree from the merge
@@ -96,7 +96,7 @@ test_expect_success 'that alternate to origin gets used' '
'
test_expect_success 'pulling changes from origin' '
- git -C D pull origin
+ git -C D pull --no-rebase origin
'
# the 5 local objects are expected; file3 blob, commit in A to add it
diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh
index 78de1ff2ad..d3687b1a2e 100755
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -27,9 +27,9 @@ test_expect_success 'list refs with git:// using protocol v2' '
ls-remote --symref "$GIT_DAEMON_URL/parent" >actual &&
# Client requested to use protocol v2
- grep "git> .*\\\0\\\0version=2\\\0$" log &&
+ grep "ls-remote> .*\\\0\\\0version=2\\\0$" log &&
# Server responded using protocol v2
- grep "git< version 2" log &&
+ grep "ls-remote< version 2" log &&
git ls-remote --symref "$GIT_DAEMON_URL/parent" >expect &&
test_cmp expect actual
@@ -151,7 +151,7 @@ test_expect_success 'list refs with file:// using protocol v2' '
ls-remote --symref "file://$(pwd)/file_parent" >actual &&
# Server responded using protocol v2
- grep "git< version 2" log &&
+ grep "ls-remote< version 2" log &&
git ls-remote --symref "file://$(pwd)/file_parent" >expect &&
test_cmp expect actual
diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh
index e9e471621d..220098523a 100755
--- a/t/t5703-upload-pack-ref-in-want.sh
+++ b/t/t5703-upload-pack-ref-in-want.sh
@@ -40,6 +40,30 @@ write_command () {
fi
}
+# Write a complete fetch command to stdout, suitable for use with `test-tool
+# pkt-line`. "want-ref", "want", and "have" lines are read from stdin.
+#
+# Examples:
+#
+# write_fetch_command <<-EOF
+# want-ref refs/heads/main
+# have $(git rev-parse a)
+# EOF
+#
+# write_fetch_command <<-EOF
+# want $(git rev-parse b)
+# have $(git rev-parse a)
+# EOF
+#
+write_fetch_command () {
+ write_command fetch &&
+ echo "0001" &&
+ echo "no-progress" &&
+ cat &&
+ echo "done" &&
+ echo "0000"
+}
+
# c(o/foo) d(o/bar)
# \ /
# b e(baz) f(main)
@@ -77,15 +101,11 @@ test_expect_success 'config controls ref-in-want advertisement' '
'
test_expect_success 'invalid want-ref line' '
- test-tool pkt-line pack >in <<-EOF &&
- $(write_command fetch)
- 0001
- no-progress
+ write_fetch_command >pkt <<-EOF &&
want-ref refs/heads/non-existent
- done
- 0000
EOF
+ test-tool pkt-line pack <pkt >in &&
test_must_fail test-tool serve-v2 --stateless-rpc 2>out <in &&
grep "unknown ref" out
'
@@ -97,16 +117,11 @@ test_expect_success 'basic want-ref' '
EOF
git rev-parse f >expected_commits &&
- oid=$(git rev-parse a) &&
- test-tool pkt-line pack >in <<-EOF &&
- $(write_command fetch)
- 0001
- no-progress
+ write_fetch_command >pkt <<-EOF &&
want-ref refs/heads/main
- have $oid
- done
- 0000
+ have $(git rev-parse a)
EOF
+ test-tool pkt-line pack <pkt >in &&
test-tool serve-v2 --stateless-rpc >out <in &&
check_output
@@ -121,17 +136,12 @@ test_expect_success 'multiple want-ref lines' '
EOF
git rev-parse c d >expected_commits &&
- oid=$(git rev-parse b) &&
- test-tool pkt-line pack >in <<-EOF &&
- $(write_command fetch)
- 0001
- no-progress
+ write_fetch_command >pkt <<-EOF &&
want-ref refs/heads/o/foo
want-ref refs/heads/o/bar
- have $oid
- done
- 0000
+ have $(git rev-parse b)
EOF
+ test-tool pkt-line pack <pkt >in &&
test-tool serve-v2 --stateless-rpc >out <in &&
check_output
@@ -144,16 +154,12 @@ test_expect_success 'mix want and want-ref' '
EOF
git rev-parse e f >expected_commits &&
- test-tool pkt-line pack >in <<-EOF &&
- $(write_command fetch)
- 0001
- no-progress
+ write_fetch_command >pkt <<-EOF &&
want-ref refs/heads/main
want $(git rev-parse e)
have $(git rev-parse a)
- done
- 0000
EOF
+ test-tool pkt-line pack <pkt >in &&
test-tool serve-v2 --stateless-rpc >out <in &&
check_output
@@ -166,16 +172,11 @@ test_expect_success 'want-ref with ref we already have commit for' '
EOF
>expected_commits &&
- oid=$(git rev-parse c) &&
- test-tool pkt-line pack >in <<-EOF &&
- $(write_command fetch)
- 0001
- no-progress
+ write_fetch_command >pkt <<-EOF &&
want-ref refs/heads/o/foo
- have $oid
- done
- 0000
+ have $(git rev-parse c)
EOF
+ test-tool pkt-line pack <pkt >in &&
test-tool serve-v2 --stateless-rpc >out <in &&
check_output
@@ -298,6 +299,141 @@ test_expect_success 'fetching with wildcard that matches multiple refs' '
grep "want-ref refs/heads/o/bar" log
'
+REPO="$(pwd)/repo-ns"
+
+test_expect_success 'setup namespaced repo' '
+ (
+ git init -b main "$REPO" &&
+ cd "$REPO" &&
+ test_commit a &&
+ test_commit b &&
+ git checkout a &&
+ test_commit c &&
+ git checkout a &&
+ test_commit d &&
+ git update-ref refs/heads/ns-no b &&
+ git update-ref refs/namespaces/ns/refs/heads/ns-yes c &&
+ git update-ref refs/namespaces/ns/refs/heads/hidden d
+ ) &&
+ git -C "$REPO" config uploadpack.allowRefInWant true
+'
+
+test_expect_success 'with namespace: want-ref is considered relative to namespace' '
+ wanted_ref=refs/heads/ns-yes &&
+
+ oid=$(git -C "$REPO" rev-parse "refs/namespaces/ns/$wanted_ref") &&
+ cat >expected_refs <<-EOF &&
+ $oid $wanted_ref
+ EOF
+ cat >expected_commits <<-EOF &&
+ $oid
+ $(git -C "$REPO" rev-parse a)
+ EOF
+
+ write_fetch_command >pkt <<-EOF &&
+ want-ref $wanted_ref
+ EOF
+ test-tool pkt-line pack <pkt >in &&
+
+ GIT_NAMESPACE=ns test-tool -C "$REPO" serve-v2 --stateless-rpc >out <in &&
+ check_output
+'
+
+test_expect_success 'with namespace: want-ref outside namespace is unknown' '
+ wanted_ref=refs/heads/ns-no &&
+
+ write_fetch_command >pkt <<-EOF &&
+ want-ref $wanted_ref
+ EOF
+ test-tool pkt-line pack <pkt >in &&
+
+ test_must_fail env GIT_NAMESPACE=ns \
+ test-tool -C "$REPO" serve-v2 --stateless-rpc >out <in &&
+ grep "unknown ref" out
+'
+
+# Cross-check refs/heads/ns-no indeed exists
+test_expect_success 'without namespace: want-ref outside namespace succeeds' '
+ wanted_ref=refs/heads/ns-no &&
+
+ oid=$(git -C "$REPO" rev-parse $wanted_ref) &&
+ cat >expected_refs <<-EOF &&
+ $oid $wanted_ref
+ EOF
+ cat >expected_commits <<-EOF &&
+ $oid
+ $(git -C "$REPO" rev-parse a)
+ EOF
+
+ write_fetch_command >pkt <<-EOF &&
+ want-ref $wanted_ref
+ EOF
+ test-tool pkt-line pack <pkt >in &&
+
+ test-tool -C "$REPO" serve-v2 --stateless-rpc >out <in &&
+ check_output
+'
+
+test_expect_success 'with namespace: hideRefs is matched, relative to namespace' '
+ wanted_ref=refs/heads/hidden &&
+ git -C "$REPO" config transfer.hideRefs $wanted_ref &&
+
+ write_fetch_command >pkt <<-EOF &&
+ want-ref $wanted_ref
+ EOF
+ test-tool pkt-line pack <pkt >in &&
+
+ test_must_fail env GIT_NAMESPACE=ns \
+ test-tool -C "$REPO" serve-v2 --stateless-rpc >out <in &&
+ grep "unknown ref" out
+'
+
+# Cross-check refs/heads/hidden indeed exists
+test_expect_success 'with namespace: want-ref succeeds if hideRefs is removed' '
+ wanted_ref=refs/heads/hidden &&
+ git -C "$REPO" config --unset transfer.hideRefs $wanted_ref &&
+
+ oid=$(git -C "$REPO" rev-parse "refs/namespaces/ns/$wanted_ref") &&
+ cat >expected_refs <<-EOF &&
+ $oid $wanted_ref
+ EOF
+ cat >expected_commits <<-EOF &&
+ $oid
+ $(git -C "$REPO" rev-parse a)
+ EOF
+
+ write_fetch_command >pkt <<-EOF &&
+ want-ref $wanted_ref
+ EOF
+ test-tool pkt-line pack <pkt >in &&
+
+ GIT_NAMESPACE=ns test-tool -C "$REPO" serve-v2 --stateless-rpc >out <in &&
+ check_output
+'
+
+test_expect_success 'without namespace: relative hideRefs does not match' '
+ wanted_ref=refs/namespaces/ns/refs/heads/hidden &&
+ git -C "$REPO" config transfer.hideRefs refs/heads/hidden &&
+
+ oid=$(git -C "$REPO" rev-parse $wanted_ref) &&
+ cat >expected_refs <<-EOF &&
+ $oid $wanted_ref
+ EOF
+ cat >expected_commits <<-EOF &&
+ $oid
+ $(git -C "$REPO" rev-parse a)
+ EOF
+
+ write_fetch_command >pkt <<-EOF &&
+ want-ref $wanted_ref
+ EOF
+ test-tool pkt-line pack <pkt >in &&
+
+ test-tool -C "$REPO" serve-v2 --stateless-rpc >out <in &&
+ check_output
+'
+
+
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
diff --git a/t/t5705-session-id-in-capabilities.sh b/t/t5705-session-id-in-capabilities.sh
index f1d189d5bc..eb8c79aafd 100755
--- a/t/t5705-session-id-in-capabilities.sh
+++ b/t/t5705-session-id-in-capabilities.sh
@@ -73,6 +73,17 @@ do
grep \"key\":\"server-sid\" tr2-client-events &&
grep \"key\":\"client-sid\" tr2-server-events
'
+
+ test_expect_success "client & server log negotiated version (v${PROTO})" '
+ test_when_finished "rm -rf local tr2-client-events tr2-server-events" &&
+ cp -r "$LOCAL_PRISTINE" local &&
+ GIT_TRACE2_EVENT="$(pwd)/tr2-client-events" \
+ git -c protocol.version=$PROTO -C local fetch \
+ --upload-pack "GIT_TRACE2_EVENT=\"$(pwd)/tr2-server-events\" git-upload-pack" \
+ origin &&
+ grep \"key\":\"negotiated-version\",\"value\":\"$PROTO\" tr2-client-events &&
+ grep \"key\":\"negotiated-version\",\"value\":\"$PROTO\" tr2-server-events
+ '
done
test_done
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 9e0214076b..05a15a933a 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -59,18 +59,25 @@ test_atom() {
# Automatically test "contents:size" atom after testing "contents"
if test "$2" = "contents"
then
- case $(git cat-file -t "$ref") in
- tag)
- # We cannot use $3 as it expects sanitize_pgp to run
- expect=$(git cat-file tag $ref | tail -n +6 | wc -c) ;;
- tree | blob)
- expect='' ;;
- commit)
- expect=$(printf '%s' "$3" | wc -c) ;;
- esac
- # Leave $expect unquoted to lose possible leading whitespaces
- echo $expect >expected
+ # for commit leg, $3 is changed there
+ expect=$(printf '%s' "$3" | wc -c)
test_expect_${4:-success} $PREREQ "basic atom: $1 contents:size" '
+ type=$(git cat-file -t "$ref") &&
+ case $type in
+ tag)
+ # We cannot use $3 as it expects sanitize_pgp to run
+ git cat-file tag $ref >out &&
+ expect=$(tail -n +6 out | wc -c) &&
+ rm -f out ;;
+ tree | blob)
+ expect="" ;;
+ commit)
+ : "use the calculated expect" ;;
+ *)
+ BUG "unknown object type" ;;
+ esac &&
+ # Leave $expect unquoted to lose possible leading whitespaces
+ echo $expect >expected &&
git for-each-ref --format="%(contents:size)" "$ref" >actual &&
test_cmp expected actual
'
diff --git a/t/t6402-merge-rename.sh b/t/t6402-merge-rename.sh
index 3da2896e3b..3a32b1a45c 100755
--- a/t/t6402-merge-rename.sh
+++ b/t/t6402-merge-rename.sh
@@ -103,7 +103,7 @@ test_expect_success 'setup' '
test_expect_success 'pull renaming branch into unrenaming one' \
'
git show-branch &&
- test_expect_code 1 git pull . white &&
+ test_expect_code 1 git pull --no-rebase . white &&
git ls-files -s &&
test_stdout_line_count = 3 git ls-files -u B &&
test_stdout_line_count = 1 git ls-files -s N &&
@@ -119,7 +119,7 @@ test_expect_success 'pull renaming branch into another renaming one' \
rm -f B &&
git reset --hard &&
git checkout red &&
- test_expect_code 1 git pull . white &&
+ test_expect_code 1 git pull --no-rebase . white &&
test_stdout_line_count = 3 git ls-files -u B &&
test_stdout_line_count = 1 git ls-files -s N &&
sed -ne "/^g/{
@@ -133,7 +133,7 @@ test_expect_success 'pull unrenaming branch into renaming one' \
'
git reset --hard &&
git show-branch &&
- test_expect_code 1 git pull . main &&
+ test_expect_code 1 git pull --no-rebase . main &&
test_stdout_line_count = 3 git ls-files -u B &&
test_stdout_line_count = 1 git ls-files -s N &&
sed -ne "/^g/{
@@ -147,7 +147,7 @@ test_expect_success 'pull conflicting renames' \
'
git reset --hard &&
git show-branch &&
- test_expect_code 1 git pull . blue &&
+ test_expect_code 1 git pull --no-rebase . blue &&
test_stdout_line_count = 1 git ls-files -u A &&
test_stdout_line_count = 1 git ls-files -u B &&
test_stdout_line_count = 1 git ls-files -u C &&
@@ -163,7 +163,7 @@ test_expect_success 'interference with untracked working tree file' '
git reset --hard &&
git show-branch &&
echo >A this file should not matter &&
- test_expect_code 1 git pull . white &&
+ test_expect_code 1 git pull --no-rebase . white &&
test_path_is_file A
'
@@ -173,7 +173,7 @@ test_expect_success 'interference with untracked working tree file' '
git show-branch &&
rm -f A &&
echo >A this file should not matter &&
- test_expect_code 1 git pull . red &&
+ test_expect_code 1 git pull --no-rebase . red &&
test_path_is_file A
'
@@ -183,7 +183,7 @@ test_expect_success 'interference with untracked working tree file' '
git checkout -f main &&
git tag -f anchor &&
git show-branch &&
- git pull . yellow &&
+ git pull --no-rebase . yellow &&
test_path_is_missing M &&
git reset --hard anchor
'
@@ -210,7 +210,7 @@ test_expect_success 'updated working tree file should prevent the merge' '
echo >>M one line addition &&
cat M >M.saved &&
git update-index M &&
- test_expect_code 128 git pull . yellow &&
+ test_expect_code 128 git pull --no-rebase . yellow &&
test_cmp M M.saved &&
rm -f M.saved
'
@@ -222,7 +222,7 @@ test_expect_success 'interference with untracked working tree file' '
git tag -f anchor &&
git show-branch &&
echo >M this file should not matter &&
- git pull . main &&
+ git pull --no-rebase . main &&
test_path_is_file M &&
! {
git ls-files -s |
diff --git a/t/t6409-merge-subtree.sh b/t/t6409-merge-subtree.sh
index d406b2343c..ba7890ec52 100755
--- a/t/t6409-merge-subtree.sh
+++ b/t/t6409-merge-subtree.sh
@@ -100,7 +100,7 @@ test_expect_success 'merge update' '
git checkout -b topic_2 &&
git commit -m "update git-gui" &&
cd ../git &&
- git pull -s subtree gui topic_2 &&
+ git pull --no-rebase -s subtree gui topic_2 &&
git ls-files -s >actual &&
(
echo "100644 $o3 0 git-gui/git-gui.sh" &&
@@ -129,7 +129,7 @@ test_expect_success 'initial ambiguous subtree' '
test_expect_success 'merge using explicit' '
cd ../git &&
git reset --hard topic_2 &&
- git pull -Xsubtree=git-gui gui topic_2 &&
+ git pull --no-rebase -Xsubtree=git-gui gui topic_2 &&
git ls-files -s >actual &&
(
echo "100644 $o3 0 git-gui/git-gui.sh" &&
@@ -142,7 +142,7 @@ test_expect_success 'merge using explicit' '
test_expect_success 'merge2 using explicit' '
cd ../git &&
git reset --hard topic_2 &&
- git pull -Xsubtree=git-gui2 gui topic_2 &&
+ git pull --no-rebase -Xsubtree=git-gui2 gui topic_2 &&
git ls-files -s >actual &&
(
echo "100644 $o1 0 git-gui/git-gui.sh" &&
diff --git a/t/t6417-merge-ours-theirs.sh b/t/t6417-merge-ours-theirs.sh
index ac9aee9a66..ec065d6a65 100755
--- a/t/t6417-merge-ours-theirs.sh
+++ b/t/t6417-merge-ours-theirs.sh
@@ -69,11 +69,11 @@ test_expect_success 'binary file with -Xours/-Xtheirs' '
'
test_expect_success 'pull passes -X to underlying merge' '
- git reset --hard main && git pull -s recursive -Xours . side &&
- git reset --hard main && git pull -s recursive -X ours . side &&
- git reset --hard main && git pull -s recursive -Xtheirs . side &&
- git reset --hard main && git pull -s recursive -X theirs . side &&
- git reset --hard main && test_must_fail git pull -s recursive -X bork . side
+ git reset --hard main && git pull --no-rebase -s recursive -Xours . side &&
+ git reset --hard main && git pull --no-rebase -s recursive -X ours . side &&
+ git reset --hard main && git pull --no-rebase -s recursive -Xtheirs . side &&
+ git reset --hard main && git pull --no-rebase -s recursive -X theirs . side &&
+ git reset --hard main && test_must_fail git pull --no-rebase -s recursive -X bork . side
'
test_expect_success SYMLINKS 'symlink with -Xours/-Xtheirs' '
diff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh
index 54c2082acb..8dd0f98812 100755
--- a/t/t7500-commit-template-squash-signoff.sh
+++ b/t/t7500-commit-template-squash-signoff.sh
@@ -270,7 +270,7 @@ EOF
test_expect_success 'commit --fixup provides correct one-line commit message' '
commit_for_rebase_autosquash_setup &&
- git commit --fixup HEAD~1 &&
+ EDITOR="echo ignored >>" git commit --fixup HEAD~1 &&
commit_msg_is "fixup! target message subject line"
'
@@ -281,6 +281,13 @@ test_expect_success 'commit --fixup -m"something" -m"extra"' '
extra"
'
+test_expect_success 'commit --fixup --edit' '
+ commit_for_rebase_autosquash_setup &&
+ EDITOR="printf \"something\nextra\" >>" git commit --fixup HEAD~1 --edit &&
+ commit_msg_is "fixup! target message subject linesomething
+extra"
+'
+
get_commit_msg () {
rev="$1" &&
git log -1 --pretty=format:"%B" "$rev"
diff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh
index 52e8ccc933..1f652f433e 100755
--- a/t/t7601-merge-pull-config.sh
+++ b/t/t7601-merge-pull-config.sh
@@ -27,120 +27,324 @@ test_expect_success 'setup' '
git tag c3
'
-test_expect_success 'pull.rebase not set' '
+test_expect_success 'pull.rebase not set, ff possible' '
git reset --hard c0 &&
git pull . c1 2>err &&
- test_i18ngrep ! "Pulling without specifying how to reconcile" err
+ test_i18ngrep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and pull.ff=true' '
git reset --hard c0 &&
test_config pull.ff true &&
git pull . c1 2>err &&
- test_i18ngrep ! "Pulling without specifying how to reconcile" err
+ test_i18ngrep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and pull.ff=false' '
git reset --hard c0 &&
test_config pull.ff false &&
git pull . c1 2>err &&
- test_i18ngrep ! "Pulling without specifying how to reconcile" err
+ test_i18ngrep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and pull.ff=only' '
git reset --hard c0 &&
test_config pull.ff only &&
git pull . c1 2>err &&
- test_i18ngrep ! "Pulling without specifying how to reconcile" err
+ test_i18ngrep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --rebase given' '
git reset --hard c0 &&
git pull --rebase . c1 2>err &&
- test_i18ngrep ! "Pulling without specifying how to reconcile" err
+ test_i18ngrep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --no-rebase given' '
git reset --hard c0 &&
git pull --no-rebase . c1 2>err &&
- test_i18ngrep ! "Pulling without specifying how to reconcile" err
+ test_i18ngrep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --ff given' '
git reset --hard c0 &&
git pull --ff . c1 2>err &&
- test_i18ngrep ! "Pulling without specifying how to reconcile" err
+ test_i18ngrep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --no-ff given' '
git reset --hard c0 &&
git pull --no-ff . c1 2>err &&
- test_i18ngrep ! "Pulling without specifying how to reconcile" err
+ test_i18ngrep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --ff-only given' '
git reset --hard c0 &&
git pull --ff-only . c1 2>err &&
- test_i18ngrep ! "Pulling without specifying how to reconcile" err
+ test_i18ngrep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set (not-fast-forward)' '
git reset --hard c2 &&
- git -c color.advice=always pull . c1 2>err &&
+ test_must_fail git -c color.advice=always pull . c1 2>err &&
test_decode_color <err >decoded &&
test_i18ngrep "<YELLOW>hint: " decoded &&
- test_i18ngrep "Pulling without specifying how to reconcile" decoded
+ test_i18ngrep "You have divergent branches" decoded
'
test_expect_success 'pull.rebase not set and pull.ff=true (not-fast-forward)' '
git reset --hard c2 &&
test_config pull.ff true &&
git pull . c1 2>err &&
- test_i18ngrep ! "Pulling without specifying how to reconcile" err
+ test_i18ngrep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and pull.ff=false (not-fast-forward)' '
git reset --hard c2 &&
test_config pull.ff false &&
git pull . c1 2>err &&
- test_i18ngrep ! "Pulling without specifying how to reconcile" err
+ test_i18ngrep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and pull.ff=only (not-fast-forward)' '
git reset --hard c2 &&
test_config pull.ff only &&
test_must_fail git pull . c1 2>err &&
- test_i18ngrep ! "Pulling without specifying how to reconcile" err
+ test_i18ngrep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --rebase given (not-fast-forward)' '
git reset --hard c2 &&
git pull --rebase . c1 2>err &&
- test_i18ngrep ! "Pulling without specifying how to reconcile" err
+ test_i18ngrep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --no-rebase given (not-fast-forward)' '
git reset --hard c2 &&
git pull --no-rebase . c1 2>err &&
- test_i18ngrep ! "Pulling without specifying how to reconcile" err
+ test_i18ngrep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --ff given (not-fast-forward)' '
git reset --hard c2 &&
git pull --ff . c1 2>err &&
- test_i18ngrep ! "Pulling without specifying how to reconcile" err
+ test_i18ngrep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --no-ff given (not-fast-forward)' '
git reset --hard c2 &&
git pull --no-ff . c1 2>err &&
- test_i18ngrep ! "Pulling without specifying how to reconcile" err
+ test_i18ngrep ! "You have divergent branches" err
'
test_expect_success 'pull.rebase not set and --ff-only given (not-fast-forward)' '
git reset --hard c2 &&
test_must_fail git pull --ff-only . c1 2>err &&
- test_i18ngrep ! "Pulling without specifying how to reconcile" err
+ test_i18ngrep ! "You have divergent branches" err
+'
+
+test_does_rebase () {
+ git reset --hard c2 &&
+ git "$@" . c1 &&
+ # Check that we actually did a rebase
+ git rev-list --count HEAD >actual &&
+ git rev-list --merges --count HEAD >>actual &&
+ test_write_lines 3 0 >expect &&
+ test_cmp expect actual &&
+ rm actual expect
+}
+
+# Prefers merge over fast-forward
+test_does_merge_when_ff_possible () {
+ git reset --hard c0 &&
+ git "$@" . c1 &&
+ # Check that we actually did a merge
+ git rev-list --count HEAD >actual &&
+ git rev-list --merges --count HEAD >>actual &&
+ test_write_lines 3 1 >expect &&
+ test_cmp expect actual &&
+ rm actual expect
+}
+
+# Prefers fast-forward over merge or rebase
+test_does_fast_forward () {
+ git reset --hard c0 &&
+ git "$@" . c1 &&
+
+ # Check that we did not get any merges
+ git rev-list --count HEAD >actual &&
+ git rev-list --merges --count HEAD >>actual &&
+ test_write_lines 2 0 >expect &&
+ test_cmp expect actual &&
+
+ # Check that we ended up at c1
+ git rev-parse HEAD >actual &&
+ git rev-parse c1^{commit} >expect &&
+ test_cmp actual expect &&
+
+ # Remove temporary files
+ rm actual expect
+}
+
+# Doesn't fail when fast-forward not possible; does a merge
+test_falls_back_to_full_merge () {
+ git reset --hard c2 &&
+ git "$@" . c1 &&
+ # Check that we actually did a merge
+ git rev-list --count HEAD >actual &&
+ git rev-list --merges --count HEAD >>actual &&
+ test_write_lines 4 1 >expect &&
+ test_cmp expect actual &&
+ rm actual expect
+}
+
+# Attempts fast forward, which is impossible, and bails
+test_attempts_fast_forward () {
+ git reset --hard c2 &&
+ test_must_fail git "$@" . c1 2>err &&
+ test_i18ngrep "Not possible to fast-forward, aborting" err
+}
+
+#
+# Group 1: Interaction of --ff-only with --[no-]rebase
+# (And related interaction of pull.ff=only with pull.rebase)
+#
+test_expect_success '--ff-only overrides --rebase' '
+ test_attempts_fast_forward pull --rebase --ff-only
+'
+
+test_expect_success '--ff-only overrides --rebase even if first' '
+ test_attempts_fast_forward pull --ff-only --rebase
+'
+
+test_expect_success '--ff-only overrides --no-rebase' '
+ test_attempts_fast_forward pull --ff-only --no-rebase
+'
+
+test_expect_success 'pull.ff=only overrides pull.rebase=true' '
+ test_attempts_fast_forward -c pull.ff=only -c pull.rebase=true pull
+'
+
+test_expect_success 'pull.ff=only overrides pull.rebase=false' '
+ test_attempts_fast_forward -c pull.ff=only -c pull.rebase=false pull
+'
+
+# Group 2: --rebase=[!false] overrides --no-ff and --ff
+# (And related interaction of pull.rebase=!false and pull.ff=!only)
+test_expect_success '--rebase overrides --no-ff' '
+ test_does_rebase pull --rebase --no-ff
+'
+
+test_expect_success '--rebase overrides --ff' '
+ test_does_rebase pull --rebase --ff
+'
+
+test_expect_success '--rebase fast-forwards when possible' '
+ test_does_fast_forward pull --rebase --ff
+'
+
+test_expect_success 'pull.rebase=true overrides pull.ff=false' '
+ test_does_rebase -c pull.rebase=true -c pull.ff=false pull
+'
+
+test_expect_success 'pull.rebase=true overrides pull.ff=true' '
+ test_does_rebase -c pull.rebase=true -c pull.ff=true pull
+'
+
+# Group 3: command line flags take precedence over config
+test_expect_success '--ff-only takes precedence over pull.rebase=true' '
+ test_attempts_fast_forward -c pull.rebase=true pull --ff-only
+'
+
+test_expect_success '--ff-only takes precedence over pull.rebase=false' '
+ test_attempts_fast_forward -c pull.rebase=false pull --ff-only
+'
+
+test_expect_success '--no-rebase takes precedence over pull.ff=only' '
+ test_falls_back_to_full_merge -c pull.ff=only pull --no-rebase
+'
+
+test_expect_success '--rebase takes precedence over pull.ff=only' '
+ test_does_rebase -c pull.ff=only pull --rebase
+'
+
+test_expect_success '--rebase overrides pull.ff=true' '
+ test_does_rebase -c pull.ff=true pull --rebase
+'
+
+test_expect_success '--rebase overrides pull.ff=false' '
+ test_does_rebase -c pull.ff=false pull --rebase
+'
+
+test_expect_success '--rebase overrides pull.ff unset' '
+ test_does_rebase pull --rebase
+'
+
+# Group 4: --no-rebase heeds pull.ff=!only or explict --ff or --no-ff
+
+test_expect_success '--no-rebase works with --no-ff' '
+ test_does_merge_when_ff_possible pull --no-rebase --no-ff
+'
+
+test_expect_success '--no-rebase works with --ff' '
+ test_does_fast_forward pull --no-rebase --ff
+'
+
+test_expect_success '--no-rebase does ff if pull.ff unset' '
+ test_does_fast_forward pull --no-rebase
+'
+
+test_expect_success '--no-rebase heeds pull.ff=true' '
+ test_does_fast_forward -c pull.ff=true pull --no-rebase
+'
+
+test_expect_success '--no-rebase heeds pull.ff=false' '
+ test_does_merge_when_ff_possible -c pull.ff=false pull --no-rebase
+'
+
+# Group 5: pull.rebase=!false in combination with --no-ff or --ff
+test_expect_success 'pull.rebase=true and --no-ff' '
+ test_does_rebase -c pull.rebase=true pull --no-ff
+'
+
+test_expect_success 'pull.rebase=true and --ff' '
+ test_does_rebase -c pull.rebase=true pull --ff
+'
+
+test_expect_success 'pull.rebase=false and --no-ff' '
+ test_does_merge_when_ff_possible -c pull.rebase=false pull --no-ff
+'
+
+test_expect_success 'pull.rebase=false and --ff, ff possible' '
+ test_does_fast_forward -c pull.rebase=false pull --ff
+'
+
+test_expect_success 'pull.rebase=false and --ff, ff not possible' '
+ test_falls_back_to_full_merge -c pull.rebase=false pull --ff
+'
+
+# End of groupings for conflicting merge vs. rebase flags/options
+
+test_expect_success 'Multiple heads warns about inability to fast forward' '
+ git reset --hard c1 &&
+ test_must_fail git pull . c2 c3 2>err &&
+ test_i18ngrep "You have divergent branches" err
+'
+
+test_expect_success 'Multiple can never be fast forwarded' '
+ git reset --hard c0 &&
+ test_must_fail git -c pull.ff=only pull . c1 c2 c3 2>err &&
+ test_i18ngrep ! "You have divergent branches" err &&
+ # In addition to calling out "cannot fast-forward", we very much
+ # want the "multiple branches" piece to be called out to users.
+ test_i18ngrep "Cannot fast-forward to multiple branches" err
+'
+
+test_expect_success 'Cannot rebase with multiple heads' '
+ git reset --hard c0 &&
+ test_must_fail git -c pull.rebase=true pull . c1 c2 c3 2>err &&
+ test_i18ngrep ! "You have divergent branches" err &&
+ test_i18ngrep "Cannot rebase onto multiple branches." err
'
test_expect_success 'merge c1 with c2' '
diff --git a/t/t7603-merge-reduce-heads.sh b/t/t7603-merge-reduce-heads.sh
index 98948955ae..27cd94ad6f 100755
--- a/t/t7603-merge-reduce-heads.sh
+++ b/t/t7603-merge-reduce-heads.sh
@@ -68,7 +68,7 @@ test_expect_success 'merge c1 with c2, c3, c4, c5' '
test_expect_success 'pull c2, c3, c4, c5 into c1' '
git reset --hard c1 &&
- git pull . c2 c3 c4 c5 &&
+ git pull --no-rebase . c2 c3 c4 c5 &&
test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" &&
test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index 58f46c77e6..fc16ac2258 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -582,6 +582,23 @@ test_expect_success 'start and stop macOS maintenance' '
test_line_count = 0 actual
'
+test_expect_success 'use launchctl list to prevent extra work' '
+ # ensure we are registered
+ GIT_TEST_MAINT_SCHEDULER=launchctl:./print-args git maintenance start &&
+
+ # do it again on a fresh args file
+ rm -f args &&
+ GIT_TEST_MAINT_SCHEDULER=launchctl:./print-args git maintenance start &&
+
+ ls "$HOME/Library/LaunchAgents" >actual &&
+ cat >expect <<-\EOF &&
+ list org.git-scm.git.hourly
+ list org.git-scm.git.daily
+ list org.git-scm.git.weekly
+ EOF
+ test_cmp expect args
+'
+
test_expect_success 'start and stop Windows maintenance' '
write_script print-args <<-\EOF &&
echo $* >>args
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 57fc10e7f8..aa0c20499b 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -1533,6 +1533,21 @@ test_expect_success $PREREQ 'sendemail.8bitEncoding works' '
test_cmp content-type-decl actual
'
+test_expect_success $PREREQ 'sendemail.8bitEncoding in .git/config overrides --global .gitconfig' '
+ clean_fake_sendmail &&
+ git config sendemail.assume8bitEncoding UTF-8 &&
+ test_when_finished "rm -rf home" &&
+ mkdir home &&
+ git config -f home/.gitconfig sendemail.assume8bitEncoding "bogus too" &&
+ echo bogus |
+ env HOME="$(pwd)/home" DEBUG=1 \
+ git send-email --from=author@example.com --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ email-using-8bit >stdout &&
+ egrep "Content|MIME" msgtxt1 >actual &&
+ test_cmp content-type-decl actual
+'
+
test_expect_success $PREREQ '--8bit-encoding overrides sendemail.8bitEncoding' '
clean_fake_sendmail &&
git config sendemail.assume8bitEncoding "bogus too" &&
@@ -2198,7 +2213,7 @@ test_expect_success $PREREQ 'leading and trailing whitespaces are removed' '
test_expect_success $PREREQ 'test using command name with --sendmail-cmd' '
clean_fake_sendmail &&
- PATH="$(pwd):$PATH" \
+ PATH="$PWD:$PATH" \
git send-email \
--from="Example <nobody@example.com>" \
--to=nobody@example.com \
@@ -2227,6 +2242,51 @@ test_expect_success $PREREQ 'test shell expression with --sendmail-cmd' '
test_path_is_file commandline1
'
+test_expect_success $PREREQ 'set up in-reply-to/references patches' '
+ cat >has-reply.patch <<-\EOF &&
+ From: A U Thor <author@example.com>
+ Subject: patch with in-reply-to
+ Message-ID: <patch.with.in.reply.to@example.com>
+ In-Reply-To: <replied.to@example.com>
+ References: <replied.to@example.com>
+
+ This is the body.
+ EOF
+ cat >no-reply.patch <<-\EOF
+ From: A U Thor <author@example.com>
+ Subject: patch without in-reply-to
+ Message-ID: <patch.without.in.reply.to@example.com>
+
+ This is the body.
+ EOF
+'
+
+test_expect_success $PREREQ 'patch reply headers correct with --no-thread' '
+ clean_fake_sendmail &&
+ git send-email \
+ --no-thread \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ has-reply.patch no-reply.patch &&
+ grep "In-Reply-To: <replied.to@example.com>" msgtxt1 &&
+ grep "References: <replied.to@example.com>" msgtxt1 &&
+ ! grep replied.to@example.com msgtxt2
+'
+
+test_expect_success $PREREQ 'cmdline in-reply-to used with --no-thread' '
+ clean_fake_sendmail &&
+ git send-email \
+ --no-thread \
+ --in-reply-to="<cmdline.reply@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ has-reply.patch no-reply.patch &&
+ grep "In-Reply-To: <cmdline.reply@example.com>" msgtxt1 &&
+ grep "References: <cmdline.reply@example.com>" msgtxt1 &&
+ grep "In-Reply-To: <cmdline.reply@example.com>" msgtxt2 &&
+ grep "References: <cmdline.reply@example.com>" msgtxt2
+'
+
test_expect_success $PREREQ 'invoke hook' '
mkdir -p .git/hooks &&
diff --git a/t/t9002-column.sh b/t/t9002-column.sh
index 89983527b6..6d3dbde3fe 100755
--- a/t/t9002-column.sh
+++ b/t/t9002-column.sh
@@ -42,6 +42,24 @@ EOF
test_cmp expected actual
'
+test_expect_success '--nl' '
+ cat >expected <<\EOF &&
+oneZ
+twoZ
+threeZ
+fourZ
+fiveZ
+sixZ
+sevenZ
+eightZ
+nineZ
+tenZ
+elevenZ
+EOF
+ git column --nl="Z$LF" --mode=plain <lista >actual &&
+ test_cmp expected actual
+'
+
test_expect_success '80 columns' '
cat >expected <<\EOF &&
one two three four five six seven eight nine ten eleven
diff --git a/t/t9351-fast-export-anonymize.sh b/t/t9351-fast-export-anonymize.sh
index 1c6e6fcdaf..77047e250d 100755
--- a/t/t9351-fast-export-anonymize.sh
+++ b/t/t9351-fast-export-anonymize.sh
@@ -18,7 +18,8 @@ test_expect_success 'setup simple repo' '
git update-index --add --cacheinfo 160000,$fake_commit,link1 &&
git update-index --add --cacheinfo 160000,$fake_commit,link2 &&
git commit -m "add gitlink" &&
- git tag -m "annotated tag" mytag
+ git tag -m "annotated tag" mytag &&
+ git tag -m "annotated tag with long message" longtag
'
test_expect_success 'export anonymized stream' '
@@ -55,7 +56,8 @@ test_expect_success 'stream retains other as refname' '
test_expect_success 'stream omits other refnames' '
! grep main stream &&
- ! grep mytag stream
+ ! grep mytag stream &&
+ ! grep longtag stream
'
test_expect_success 'stream omits identities' '
@@ -118,9 +120,9 @@ test_expect_success 'identical gitlinks got identical oid' '
test_line_count = 1 commits
'
-test_expect_success 'tag points to branch tip' '
+test_expect_success 'all tags point to branch tip' '
git rev-parse $other_branch >expect &&
- git for-each-ref --format="%(*objectname)" | grep . >actual &&
+ git for-each-ref --format="%(*objectname)" | grep . | uniq >actual &&
test_cmp expect actual
'
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 11573936d5..5decc3b269 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -540,6 +540,15 @@ test_expect_success '__gitcomp - expand/narrow all negative options' '
EOF
'
+test_expect_success '__gitcomp - equal skip' '
+ test_gitcomp "--option=" "--option=" <<-\EOF &&
+
+ EOF
+ test_gitcomp "option=" "option=" <<-\EOF
+
+ EOF
+'
+
test_expect_success '__gitcomp - doesnt fail because of invalid variable name' '
__gitcomp "$invalid_variable_name"
'
@@ -2380,6 +2389,12 @@ test_expect_success 'git clone --config= - value' '
EOF
'
+test_expect_success 'options with value' '
+ test_completion "git merge -X diff-algorithm=" <<-\EOF
+
+ EOF
+'
+
test_expect_success 'sourcing the completion script clears cached commands' '
__git_compute_all_commands &&
verbose test -n "$__git_all_commands" &&
diff --git a/t/test-lib.sh b/t/test-lib.sh
index abcfbed6d6..fc1e521519 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1343,7 +1343,8 @@ fi
GIT_TEMPLATE_DIR="$GIT_BUILD_DIR"/templates/blt
GIT_CONFIG_NOSYSTEM=1
GIT_ATTR_NOSYSTEM=1
-export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_ATTR_NOSYSTEM
+GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY/.."
+export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_ATTR_NOSYSTEM GIT_CEILING_DIRECTORIES
if test -z "$GIT_TEST_CMP"
then
diff --git a/trace2.h b/trace2.h
index ede18c2e06..49c96ec26b 100644
--- a/trace2.h
+++ b/trace2.h
@@ -340,7 +340,7 @@ void trace2_def_repo_fl(const char *file, int line, struct repository *repo);
* being started, such as "read_recursive" or "do_read_index".
*
* The `repo` field, if set, will be used to get the "repo-id", so that
- * recursive oerations can be attributed to the correct repository.
+ * recursive operations can be attributed to the correct repository.
*/
void trace2_region_enter_fl(const char *file, int line, const char *category,
const char *label, const struct repository *repo, ...);
diff --git a/tree-diff.c b/tree-diff.c
index 1572615bd9..437c98a70e 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -21,7 +21,9 @@
ALLOC_ARRAY((x), nr); \
} while(0)
#define FAST_ARRAY_FREE(x, nr) do { \
- if ((nr) > 2) \
+ if ((nr) <= 2) \
+ xalloca_free((x)); \
+ else \
free((x)); \
} while(0)
diff --git a/unicode-width.h b/unicode-width.h
index b50e686bae..97c851b27d 100644
--- a/unicode-width.h
+++ b/unicode-width.h
@@ -26,7 +26,9 @@ static const struct interval zero_width[] = {
{ 0x0825, 0x0827 },
{ 0x0829, 0x082D },
{ 0x0859, 0x085B },
-{ 0x08D3, 0x0902 },
+{ 0x0890, 0x0891 },
+{ 0x0898, 0x089F },
+{ 0x08CA, 0x0902 },
{ 0x093A, 0x093A },
{ 0x093C, 0x093C },
{ 0x0941, 0x0948 },
@@ -66,6 +68,7 @@ static const struct interval zero_width[] = {
{ 0x0BCD, 0x0BCD },
{ 0x0C00, 0x0C00 },
{ 0x0C04, 0x0C04 },
+{ 0x0C3C, 0x0C3C },
{ 0x0C3E, 0x0C40 },
{ 0x0C46, 0x0C48 },
{ 0x0C4A, 0x0C4D },
@@ -116,7 +119,7 @@ static const struct interval zero_width[] = {
{ 0x1160, 0x11FF },
{ 0x135D, 0x135F },
{ 0x1712, 0x1714 },
-{ 0x1732, 0x1734 },
+{ 0x1732, 0x1733 },
{ 0x1752, 0x1753 },
{ 0x1772, 0x1773 },
{ 0x17B4, 0x17B5 },
@@ -124,7 +127,7 @@ static const struct interval zero_width[] = {
{ 0x17C6, 0x17C6 },
{ 0x17C9, 0x17D3 },
{ 0x17DD, 0x17DD },
-{ 0x180B, 0x180E },
+{ 0x180B, 0x180F },
{ 0x1885, 0x1886 },
{ 0x18A9, 0x18A9 },
{ 0x1920, 0x1922 },
@@ -140,7 +143,7 @@ static const struct interval zero_width[] = {
{ 0x1A65, 0x1A6C },
{ 0x1A73, 0x1A7C },
{ 0x1A7F, 0x1A7F },
-{ 0x1AB0, 0x1AC0 },
+{ 0x1AB0, 0x1ACE },
{ 0x1B00, 0x1B03 },
{ 0x1B34, 0x1B34 },
{ 0x1B36, 0x1B3A },
@@ -163,8 +166,7 @@ static const struct interval zero_width[] = {
{ 0x1CED, 0x1CED },
{ 0x1CF4, 0x1CF4 },
{ 0x1CF8, 0x1CF9 },
-{ 0x1DC0, 0x1DF9 },
-{ 0x1DFB, 0x1DFF },
+{ 0x1DC0, 0x1DFF },
{ 0x200B, 0x200F },
{ 0x202A, 0x202E },
{ 0x2060, 0x2064 },
@@ -227,12 +229,16 @@ static const struct interval zero_width[] = {
{ 0x10D24, 0x10D27 },
{ 0x10EAB, 0x10EAC },
{ 0x10F46, 0x10F50 },
+{ 0x10F82, 0x10F85 },
{ 0x11001, 0x11001 },
{ 0x11038, 0x11046 },
+{ 0x11070, 0x11070 },
+{ 0x11073, 0x11074 },
{ 0x1107F, 0x11081 },
{ 0x110B3, 0x110B6 },
{ 0x110B9, 0x110BA },
{ 0x110BD, 0x110BD },
+{ 0x110C2, 0x110C2 },
{ 0x110CD, 0x110CD },
{ 0x11100, 0x11102 },
{ 0x11127, 0x1112B },
@@ -315,6 +321,8 @@ static const struct interval zero_width[] = {
{ 0x16FE4, 0x16FE4 },
{ 0x1BC9D, 0x1BC9E },
{ 0x1BCA0, 0x1BCA3 },
+{ 0x1CF00, 0x1CF2D },
+{ 0x1CF30, 0x1CF46 },
{ 0x1D167, 0x1D169 },
{ 0x1D173, 0x1D182 },
{ 0x1D185, 0x1D18B },
@@ -332,6 +340,7 @@ static const struct interval zero_width[] = {
{ 0x1E023, 0x1E024 },
{ 0x1E026, 0x1E02A },
{ 0x1E130, 0x1E136 },
+{ 0x1E2AE, 0x1E2AE },
{ 0x1E2EC, 0x1E2EF },
{ 0x1E8D0, 0x1E8D6 },
{ 0x1E944, 0x1E94A },
@@ -404,7 +413,10 @@ static const struct interval double_width[] = {
{ 0x17000, 0x187F7 },
{ 0x18800, 0x18CD5 },
{ 0x18D00, 0x18D08 },
-{ 0x1B000, 0x1B11E },
+{ 0x1AFF0, 0x1AFF3 },
+{ 0x1AFF5, 0x1AFFB },
+{ 0x1AFFD, 0x1AFFE },
+{ 0x1B000, 0x1B122 },
{ 0x1B150, 0x1B152 },
{ 0x1B164, 0x1B167 },
{ 0x1B170, 0x1B2FB },
@@ -439,21 +451,23 @@ static const struct interval double_width[] = {
{ 0x1F6CC, 0x1F6CC },
{ 0x1F6D0, 0x1F6D2 },
{ 0x1F6D5, 0x1F6D7 },
+{ 0x1F6DD, 0x1F6DF },
{ 0x1F6EB, 0x1F6EC },
{ 0x1F6F4, 0x1F6FC },
{ 0x1F7E0, 0x1F7EB },
+{ 0x1F7F0, 0x1F7F0 },
{ 0x1F90C, 0x1F93A },
{ 0x1F93C, 0x1F945 },
-{ 0x1F947, 0x1F978 },
-{ 0x1F97A, 0x1F9CB },
-{ 0x1F9CD, 0x1F9FF },
+{ 0x1F947, 0x1F9FF },
{ 0x1FA70, 0x1FA74 },
-{ 0x1FA78, 0x1FA7A },
+{ 0x1FA78, 0x1FA7C },
{ 0x1FA80, 0x1FA86 },
-{ 0x1FA90, 0x1FAA8 },
-{ 0x1FAB0, 0x1FAB6 },
-{ 0x1FAC0, 0x1FAC2 },
-{ 0x1FAD0, 0x1FAD6 },
+{ 0x1FA90, 0x1FAAC },
+{ 0x1FAB0, 0x1FABA },
+{ 0x1FAC0, 0x1FAC5 },
+{ 0x1FAD0, 0x1FAD9 },
+{ 0x1FAE0, 0x1FAE7 },
+{ 0x1FAF0, 0x1FAF6 },
{ 0x20000, 0x2FFFD },
{ 0x30000, 0x3FFFD }
};
diff --git a/unpack-trees.c b/unpack-trees.c
index 5786645f31..f07304f1b7 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -479,7 +479,7 @@ static int check_updates(struct unpack_trees_options *o,
errs |= run_parallel_checkout(&state, pc_workers, pc_threshold,
progress, &cnt);
stop_progress(&progress);
- errs |= finish_delayed_checkout(&state, NULL);
+ errs |= finish_delayed_checkout(&state, NULL, o->verbose_update);
git_attr_set_direction(GIT_ATTR_CHECKIN);
if (o->clone)
diff --git a/upload-pack.c b/upload-pack.c
index 297b76fcb4..6ce07231d3 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -1417,21 +1417,25 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,
struct string_list *wanted_refs,
struct object_array *want_obj)
{
- const char *arg;
- if (skip_prefix(line, "want-ref ", &arg)) {
+ const char *refname_nons;
+ if (skip_prefix(line, "want-ref ", &refname_nons)) {
struct object_id oid;
struct string_list_item *item;
struct object *o;
+ struct strbuf refname = STRBUF_INIT;
- if (read_ref(arg, &oid)) {
- packet_writer_error(writer, "unknown ref %s", arg);
- die("unknown ref %s", arg);
+ strbuf_addf(&refname, "%s%s", get_git_namespace(), refname_nons);
+ if (ref_is_hidden(refname_nons, refname.buf) ||
+ read_ref(refname.buf, &oid)) {
+ packet_writer_error(writer, "unknown ref %s", refname_nons);
+ die("unknown ref %s", refname_nons);
}
+ strbuf_release(&refname);
- item = string_list_append(wanted_refs, arg);
+ item = string_list_append(wanted_refs, refname_nons);
item->util = oiddup(&oid);
- o = parse_object_or_die(&oid, arg);
+ o = parse_object_or_die(&oid, refname_nons);
if (!(o->flags & WANTED)) {
o->flags |= WANTED;
add_object_array(o, NULL, want_obj);
diff --git a/wrapper.c b/wrapper.c
index 563ad590df..7c6586af32 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -193,7 +193,9 @@ int xopen(const char *path, int oflag, ...)
if (errno == EINTR)
continue;
- if ((oflag & O_RDWR) == O_RDWR)
+ if ((oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ die_errno(_("unable to create '%s'"), path);
+ else if ((oflag & O_RDWR) == O_RDWR)
die_errno(_("could not open '%s' for reading and writing"), path);
else if ((oflag & O_WRONLY) == O_WRONLY)
die_errno(_("could not open '%s' for writing"), path);