summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--Documentation/BreakingChanges.adoc4
-rw-r--r--Documentation/Makefile5
-rw-r--r--Documentation/RelNotes/2.49.0.adoc59
-rw-r--r--Documentation/config/http.adoc15
-rw-r--r--Documentation/config/merge.adoc3
-rw-r--r--Documentation/git-backfill.adoc71
-rw-r--r--Documentation/git-commit.adoc8
-rw-r--r--Documentation/git-config.adoc4
-rw-r--r--Documentation/git-merge-tree.adoc11
-rw-r--r--Documentation/git-rebase.adoc8
-rw-r--r--Documentation/git-refs.adoc11
-rw-r--r--Documentation/git.adoc5
-rw-r--r--Documentation/gitattributes.adoc4
-rw-r--r--Documentation/gitprotocol-v2.adoc10
-rw-r--r--Documentation/howto/meson.build2
-rw-r--r--Documentation/merge-strategies.adoc12
-rw-r--r--Documentation/meson.build14
-rw-r--r--Documentation/pretty-formats.adoc8
-rw-r--r--Documentation/rev-list-options.adoc19
-rw-r--r--Documentation/technical/api-path-walk.adoc11
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile9
-rw-r--r--builtin.h1
-rw-r--r--builtin/backfill.c147
-rw-r--r--builtin/bugreport.c13
-rw-r--r--builtin/check-mailmap.c2
-rw-r--r--builtin/difftool.c95
-rw-r--r--builtin/merge-tree.c11
-rw-r--r--builtin/refs.c3
-rw-r--r--builtin/rev-list.c106
-rw-r--r--builtin/update-server-info.c8
-rw-r--r--command-list.txt1
-rw-r--r--commit.c8
-rw-r--r--compat/mingw.c4
-rw-r--r--connect.c2
-rw-r--r--contrib/credential/libsecret/Makefile3
-rw-r--r--contrib/credential/osxkeychain/Makefile1
-rw-r--r--contrib/credential/wincred/Makefile3
-rw-r--r--contrib/diff-highlight/Makefile3
-rw-r--r--contrib/diff-highlight/t/Makefile5
-rw-r--r--contrib/mw-to-git/Makefile5
-rw-r--r--contrib/mw-to-git/t/Makefile3
-rw-r--r--contrib/persistent-https/Makefile5
-rw-r--r--contrib/subtree/t/Makefile5
-rwxr-xr-xcontrib/thunderbird-patch-inline/appp.sh2
-rw-r--r--diff.c4
-rw-r--r--dir.c10
-rw-r--r--dir.h3
-rw-r--r--git-gui/Makefile1
-rw-r--r--git-gui/po/glossary/Makefile3
-rw-r--r--git.c1
-rw-r--r--gitk-git/Makefile5
-rwxr-xr-xgitk-git/generate-tcl.sh11
-rwxr-xr-xgitk-git/gitk213
-rw-r--r--gitk-git/meson.build30
-rw-r--r--gitk-git/po/meson.build19
-rw-r--r--http.c3
-rw-r--r--merge-recursive.c15
-rw-r--r--meson.build174
-rw-r--r--meson_options.txt4
-rw-r--r--oss-fuzz/meson.build2
-rw-r--r--path-walk.c28
-rw-r--r--path-walk.h11
-rw-r--r--refs.c8
-rw-r--r--refs.h5
-rw-r--r--refspec.c34
-rw-r--r--refspec.h9
-rw-r--r--remote.c6
-rw-r--r--send-pack.c10
-rw-r--r--send-pack.h13
-rw-r--r--sequencer.c12
-rwxr-xr-xt/aggregate-results.sh2
-rw-r--r--t/helper/meson.build4
-rw-r--r--t/helper/test-path-walk.c22
-rw-r--r--t/interop/Makefile5
-rw-r--r--t/meson.build5
-rw-r--r--t/perf/Makefile5
-rwxr-xr-xt/t1460-refs-migrate.sh28
-rwxr-xr-xt/t3404-rebase-interactive.sh14
-rwxr-xr-xt/t3430-rebase-merges.sh20
-rwxr-xr-xt/t4203-mailmap.sh12
-rwxr-xr-xt/t4209-log-pickaxe.sh16
-rwxr-xr-xt/t5504-fetch-receive-strict.sh35
-rwxr-xr-xt/t5540-http-push-webdav.sh10
-rwxr-xr-xt/t5543-atomic-push.sh30
-rwxr-xr-xt/t5548-push-porcelain.sh443
-rwxr-xr-xt/t5620-backfill.sh211
-rwxr-xr-xt/t5701-git-serve.sh26
-rwxr-xr-xt/t6022-rev-list-missing.sh53
-rwxr-xr-xt/t6601-path-walk.sh32
-rwxr-xr-xt/t7603-merge-reduce-heads.sh24
-rw-r--r--t/test-lib-functions.sh8
-rw-r--r--templates/Makefile5
-rw-r--r--transport.c17
-rw-r--r--version.c69
-rw-r--r--version.h10
-rw-r--r--xdiff/xdiffi.c3
-rw-r--r--xdiff/xemit.c2
-rw-r--r--xdiff/xhistogram.c8
-rw-r--r--xdiff/xinclude.h2
-rw-r--r--xdiff/xpatience.c3
-rw-r--r--xdiff/xutils.c4
104 files changed, 1995 insertions, 501 deletions
diff --git a/.gitignore b/.gitignore
index acdd8ce7c7..08a66ca508 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@
/git-apply
/git-archimport
/git-archive
+/git-backfill
/git-bisect
/git-blame
/git-branch
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index bb7d0c9ef1..ab2cbb4534 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -164,7 +164,7 @@ build:msvc-meson:
extends: .msvc-meson
stage: build
script:
- - meson setup build -Dperl=disabled -Dcredential_helpers=wincred
+ - meson setup build -Dperl=disabled -Dbackend_max_links=1 -Dcredential_helpers=wincred
- meson compile -C build
artifacts:
paths:
diff --git a/Documentation/BreakingChanges.adoc b/Documentation/BreakingChanges.adoc
index 7c388e56c8..042709a461 100644
--- a/Documentation/BreakingChanges.adoc
+++ b/Documentation/BreakingChanges.adoc
@@ -169,8 +169,8 @@ started to migrate away from ".git/remotes/" in favor of config-based remotes,
and we have marked the directory as legacy in 3d3d282146 (Documentation:
Grammar correction, wording fixes and cleanup, 2011-08-23)
+
-As our documentation mentions, these directories are not to be found in modern
-repositories at all and most users aren't even aware of these mechanisms. They
+As our documentation mentions, these directories are unlikely to be used in
+modern repositories and most users aren't even aware of these mechanisms. They
have been deprecated for almost 20 years and 14 years respectively, and we are
not aware of any active users that have complained about this deprecation.
Furthermore, the ".git/branches/" directory is nowadays misleadingly named and
diff --git a/Documentation/Makefile b/Documentation/Makefile
index a734c6d624..c9a7cf662f 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -1,3 +1,6 @@
+# The default target of this Makefile is...
+all::
+
# Import tree-wide shared Makefile behavior and libraries
include ../shared.mak
@@ -238,7 +241,7 @@ DEFAULT_EDITOR_SQ = $(subst ','\'',$(DEFAULT_EDITOR))
ASCIIDOC_EXTRA += -a 'git-default-editor=$(DEFAULT_EDITOR_SQ)'
endif
-all: html man
+all:: html man
html: $(DOC_HTML)
diff --git a/Documentation/RelNotes/2.49.0.adoc b/Documentation/RelNotes/2.49.0.adoc
index 05b720f79a..aa6b378aba 100644
--- a/Documentation/RelNotes/2.49.0.adoc
+++ b/Documentation/RelNotes/2.49.0.adoc
@@ -22,6 +22,33 @@ UI, Workflows & Features
* "git clone" learned to make a shallow clone for a single commit
that is not necessarily be at the tip of any branch.
+ * Lazy-loading missing files in a blobless clone on demand is costly
+ as it tends to be one-blob-at-a-time. "git backfill" is introduced
+ to help bulk-download necessary files beforehand.
+
+ * "git push --atomic --porcelain" used to ignore failures from the
+ other side, losing the error status from the child process, which
+ has been corrected.
+
+ * "git rev-list --missing=" learned to accept "print-info" that gives
+ known details expected of the missing objects, like path and type.
+
+ * Comes with an updated "gitk".
+
+ * The documentation of "git commit" and "git rebase" now refer to
+ commit titles as such, not "subject".
+
+ * The value of "uname -s" is by default sent over the wire as a part
+ of the "version" capability.
+
+ * "git refs migrate" can optionally be told not to migrate the reflog.
+
+ * The netrc support (via the cURL library) for the HTTP transport has
+ been re-enabled.
+
+ * Removal of ".git/branches" and ".git/remotes" support in the
+ BreakingChanges document has been further clarified.
+
Performance, Internal Implementation, Development Support etc.
--------------------------------------------------------------
@@ -55,6 +82,8 @@ Performance, Internal Implementation, Development Support etc.
* All the documentation .txt files have been renamed to .adoc to help
content aware editors.
+ * "git difftool" code clean-up.
+
Fixes since v2.48
-----------------
@@ -186,6 +215,32 @@ Fixes since v2.48
has been corrected.
(merge 93dc16483a bf/fetch-set-head-fix later to maint).
+ * A thunderbird helper script lost its bashism.
+ (merge 59d26bd961 bc/contrib-thunderbird-patch-inline-fix later to maint).
+
+ * The -G/-S options to the "diff" family of commands caused us to hit
+ a BUG() when they get no values; they have been corrected.
+ (merge a620046b29 bc/diff-reject-empty-arg-to-pickaxe later to maint).
+
+ * "git merge-tree --stdin" has been improved (including a workaround
+ for a deadlock).
+ (merge 6a9ae81015 pw/merge-tree-stdin-deadlock-fix later to maint).
+
+ * Correct the default target in Documentation/Makefile, and
+ future-proof all Makefiles from similar breakages by declaring the
+ default target (which happens to be "all") upfront.
+ (merge 5309c1e9fb ad/set-default-target-in-makefiles later to maint).
+
+ * "git check-mailmap" used to segfault when queried without human
+ readable name.
+ (merge bb60c52131 jk/check-mailmap-wo-name-fix later to maint).
+
+ * Support for renaming of symbolic links on Windows has been improved.
+
+ * "git rebase -i" failed to allow rewording an empty commit that has
+ been fast-forwarded.
+ (merge af8fc7be10 pw/rebase-i-ff-empty-commit later to maint).
+
* Other code cleanup, docfix, build fix, etc.
(merge ddb5287894 jk/t7407-use-test-grep later to maint).
(merge 21e1b44865 aj/difftool-config-doc-fix later to maint).
@@ -200,3 +255,7 @@ Fixes since v2.48
(merge 8705c9bd13 kn/pack-write-with-reduced-globals later to maint).
(merge 087740d65a ps/leakfixes-0129 later to maint).
(merge 6bba6f604b jp/doc-trailer-config later to maint).
+ (merge f1cc562b77 lo/t7603-path-is-file-update later to maint).
+ (merge 45761988ac en/doc-renormalize later to maint).
+ (merge 832f56f06a jc/doc-boolean-synonyms later to maint).
+ (merge 3eeed876a9 ac/doc-http-ssl-type-config later to maint).
diff --git a/Documentation/config/http.adoc b/Documentation/config/http.adoc
index a14371b5c9..22a8803dea 100644
--- a/Documentation/config/http.adoc
+++ b/Documentation/config/http.adoc
@@ -216,6 +216,21 @@ http.sslBackend::
This option is ignored if cURL lacks support for choosing the SSL
backend at runtime.
+http.sslCertType::
+ Type of client certificate used when fetching or pushing over HTTPS.
+ "PEM", "DER" are supported when using openssl or gnutls backends. "P12"
+ is supported on "openssl", "schannel", "securetransport", and gnutls 8.11+.
+ See also libcurl `CURLOPT_SSLCERTTYPE`. Can be overridden by the
+ `GIT_SSL_CERT_TYPE` environment variable.
+
+http.sslKeyType::
+ Type of client private key used when fetching or pushing over HTTPS. (e.g.
+ "PEM", "DER", or "ENG"). Only applicable when using "openssl" backend. "DER"
+ is not supported with openssl. Particularly useful when set to "ENG" for
+ authenticating with PKCS#11 tokens, with a PKCS#11 URL in sslCert option.
+ See also libcurl `CURLOPT_SSLKEYTYPE`. Can be overridden by the
+ `GIT_SSL_KEY_TYPE` environment variable.
+
http.schannelCheckRevoke::
Used to enforce or disable certificate revocation checks in cURL
when http.sslBackend is set to "schannel". Defaults to `true` if
diff --git a/Documentation/config/merge.adoc b/Documentation/config/merge.adoc
index 857de5b40b..d2d0f21a71 100644
--- a/Documentation/config/merge.adoc
+++ b/Documentation/config/merge.adoc
@@ -69,7 +69,8 @@ merge.renormalize::
Tell Git that canonical representation of files in the
repository has changed over time (e.g. earlier commits record
text files with CRLF line endings, but recent ones use LF line
- endings). In such a repository, Git can convert the data
+ endings). In such a repository, for each file where a
+ three-way content merge is needed, Git can convert the data
recorded in commits to a canonical form before performing a
merge to reduce unnecessary conflicts. For more information,
see section "Merging branches with differing checkin/checkout
diff --git a/Documentation/git-backfill.adoc b/Documentation/git-backfill.adoc
new file mode 100644
index 0000000000..95623051f7
--- /dev/null
+++ b/Documentation/git-backfill.adoc
@@ -0,0 +1,71 @@
+git-backfill(1)
+===============
+
+NAME
+----
+git-backfill - Download missing objects in a partial clone
+
+
+SYNOPSIS
+--------
+[synopsis]
+git backfill [--min-batch-size=<n>] [--[no-]sparse]
+
+DESCRIPTION
+-----------
+
+Blobless partial clones are created using `git clone --filter=blob:none`
+and then configure the local repository such that the Git client avoids
+downloading blob objects unless they are required for a local operation.
+This initially means that the clone and later fetches download reachable
+commits and trees but no blobs. Later operations that change the `HEAD`
+pointer, such as `git checkout` or `git merge`, may need to download
+missing blobs in order to complete their operation.
+
+In the worst cases, commands that compute blob diffs, such as `git blame`,
+become very slow as they download the missing blobs in single-blob
+requests to satisfy the missing object as the Git command needs it. This
+leads to multiple download requests and no ability for the Git server to
+provide delta compression across those objects.
+
+The `git backfill` command provides a way for the user to request that
+Git downloads the missing blobs (with optional filters) such that the
+missing blobs representing historical versions of files can be downloaded
+in batches. The `backfill` command attempts to optimize the request by
+grouping blobs that appear at the same path, hopefully leading to good
+delta compression in the packfile sent by the server.
+
+In this way, `git backfill` provides a mechanism to break a large clone
+into smaller chunks. Starting with a blobless partial clone with `git
+clone --filter=blob:none` and then running `git backfill` in the local
+repository provides a way to download all reachable objects in several
+smaller network calls than downloading the entire repository at clone
+time.
+
+By default, `git backfill` downloads all blobs reachable from the `HEAD`
+commit. This set can be restricted or expanded using various options.
+
+THIS COMMAND IS EXPERIMENTAL. ITS BEHAVIOR MAY CHANGE IN THE FUTURE.
+
+
+OPTIONS
+-------
+
+`--min-batch-size=<n>`::
+ Specify a minimum size for a batch of missing objects to request
+ from the server. This size may be exceeded by the last set of
+ blobs seen at a given path. The default minimum batch size is
+ 50,000.
+
+`--[no-]sparse`::
+ Only download objects if they appear at a path that matches the
+ current sparse-checkout. If the sparse-checkout feature is enabled,
+ then `--sparse` is assumed and can be disabled with `--no-sparse`.
+
+SEE ALSO
+--------
+linkgit:git-clone[1].
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-commit.adoc b/Documentation/git-commit.adoc
index dfb78169cb..dc219025f1 100644
--- a/Documentation/git-commit.adoc
+++ b/Documentation/git-commit.adoc
@@ -98,8 +98,8 @@ OPTIONS
replaces the log message of _<commit>_ with its own log message
but makes no changes to the content of _<commit>_.
+
-The commit created by plain `--fixup=<commit>` has a subject
-composed of "fixup!" followed by the subject line from _<commit>_,
+The commit created by plain `--fixup=<commit>` has a title
+composed of "fixup!" followed by the title of _<commit>_,
and is recognized specially by `git rebase --autosquash`. The `-m`
option may be used to supplement the log message of the created
commit, but the additional commentary will be thrown away once the
@@ -107,7 +107,7 @@ commit, but the additional commentary will be thrown away once the
`git rebase --autosquash`.
+
The commit created by `--fixup=amend:<commit>` is similar but its
-subject is instead prefixed with "amend!". The log message of
+title is instead prefixed with "amend!". The log message of
_<commit>_ is copied into the log message of the "amend!" commit and
opened in an editor so it can be refined. When `git rebase
--autosquash` squashes the "amend!" commit into _<commit>_, the
@@ -128,7 +128,7 @@ See linkgit:git-rebase[1] for details.
`--squash=<commit>`::
Construct a commit message for use with `git rebase --autosquash`.
- The commit message subject line is taken from the specified
+ The commit message title is taken from the specified
commit with a prefix of "squash! ". Can be used with additional
commit message options (`-m`/`-c`/`-C`/`-F`). See
linkgit:git-rebase[1] for details.
diff --git a/Documentation/git-config.adoc b/Documentation/git-config.adoc
index 888f8ba54b..936e0c5130 100644
--- a/Documentation/git-config.adoc
+++ b/Documentation/git-config.adoc
@@ -213,7 +213,9 @@ See also <<FILES>>.
+
Valid `<type>`'s include:
+
-- 'bool': canonicalize values as either "true" or "false".
+- 'bool': canonicalize values `true`, `yes`,`on`, and positive
+ numbers as "true", and values `false`, `no`, `off` and `0` as
+ "false".
- 'int': canonicalize values as simple decimal numbers. An optional suffix of
'k', 'm', or 'g' will cause the value to be multiplied by 1024, 1048576, or
1073741824 upon input.
diff --git a/Documentation/git-merge-tree.adoc b/Documentation/git-merge-tree.adoc
index 0b6a8a19b1..cf0578f9b5 100644
--- a/Documentation/git-merge-tree.adoc
+++ b/Documentation/git-merge-tree.adoc
@@ -40,11 +40,17 @@ After the merge completes, a new toplevel tree object is created. See
OPTIONS
-------
+--stdin::
+ Read the commits to merge from the standard input rather than
+ the command-line. See <<INPUT,INPUT FORMAT>> below for more
+ information. Implies `-z`.
+
-z::
Do not quote filenames in the <Conflicted file info> section,
and end each filename with a NUL character rather than
newline. Also begin the messages section with a NUL character
- instead of a newline. See <<OUTPUT>> below for more information.
+ instead of a newline. See <<OUTPUT,OUTPUT>> below for more
+ information.
--name-only::
In the Conflicted file info section, instead of writing a list
@@ -116,8 +122,6 @@ This is an integer status followed by a NUL character. The integer status is:
0: merge had conflicts
1: merge was clean
- <0: something prevented the merge from running (e.g. access to repository
- objects denied by filesystem)
[[OIDTLT]]
OID of toplevel tree
@@ -235,6 +239,7 @@ with linkgit:git-merge[1]:
* any messages that would have been printed to stdout (the
<<IM,Informational messages>>)
+[[INPUT]]
INPUT FORMAT
------------
'git merge-tree --stdin' input format is fully text based. Each line
diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc
index 133fe8c5e6..153cb69a4f 100644
--- a/Documentation/git-rebase.adoc
+++ b/Documentation/git-rebase.adoc
@@ -599,11 +599,11 @@ See also INCOMPATIBLE OPTIONS below.
--no-autosquash::
Automatically squash commits with specially formatted messages into
previous commits being rebased. If a commit message starts with
- "squash! ", "fixup! " or "amend! ", the remainder of the subject line
+ "squash! ", "fixup! " or "amend! ", the remainder of the title
is taken as a commit specifier, which matches a previous commit if it
- matches the subject line or the hash of that commit. If no commit
+ matches the title or the hash of that commit. If no commit
matches fully, matches of the specifier with the start of commit
- subjects are considered.
+ titles are considered.
+
In the rebase todo list, the actions of squash, fixup and amend commits are
changed from `pick` to `squash`, `fixup` or `fixup -C`, respectively, and they
@@ -613,7 +613,7 @@ be used to review and edit the todo list before proceeding.
The recommended way to create commits with squash markers is by using the
`--squash`, `--fixup`, `--fixup=amend:` or `--fixup=reword:` options of
linkgit:git-commit[1], which take the target commit as an argument and
-automatically fill in the subject line of the new commit from that.
+automatically fill in the title of the new commit from that.
+
Setting configuration variable `rebase.autoSquash` to true enables
auto-squashing by default for interactive rebase. The `--no-autosquash`
diff --git a/Documentation/git-refs.adoc b/Documentation/git-refs.adoc
index 95f25776aa..4d6dc994f9 100644
--- a/Documentation/git-refs.adoc
+++ b/Documentation/git-refs.adoc
@@ -8,9 +8,9 @@ git-refs - Low-level access to refs
SYNOPSIS
--------
-[verse]
-'git refs migrate' --ref-format=<format> [--dry-run]
-'git refs verify' [--strict] [--verbose]
+[synopsis]
+git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]
+git refs verify [--strict] [--verbose]
DESCRIPTION
-----------
@@ -43,6 +43,11 @@ include::ref-storage-format.adoc[]
can be used to double check that the migration works as expected before
performing the actual migration.
+--reflog::
+--no-reflog::
+ Choose between migrating the reflog data to the new backend,
+ and discarding them. The default is "--reflog", to migrate.
+
The following options are specific to 'git refs verify':
--strict::
diff --git a/Documentation/git.adoc b/Documentation/git.adoc
index a9c1183318..743b7b00e4 100644
--- a/Documentation/git.adoc
+++ b/Documentation/git.adoc
@@ -472,8 +472,9 @@ Environment Variables
---------------------
Various Git commands pay attention to environment variables and change
their behavior. The environment variables marked as "Boolean" take
-their values the same way as Boolean valued configuration variables, e.g.
-"true", "yes", "on" and positive numbers are taken as "yes".
+their values the same way as Boolean valued configuration variables, i.e.,
+"true", "yes", "on" and positive numbers are taken as "yes", while "false",
+"no", "off", and "0" are taken as "no".
Here are the variables:
diff --git a/Documentation/gitattributes.adoc b/Documentation/gitattributes.adoc
index 5d12b78549..7eaca89972 100644
--- a/Documentation/gitattributes.adoc
+++ b/Documentation/gitattributes.adoc
@@ -701,8 +701,8 @@ where the attribute is not in place would normally cause merge
conflicts.
To prevent these unnecessary merge conflicts, Git can be told to run a
-virtual check-out and check-in of all three stages of a file when
-resolving a three-way merge by setting the `merge.renormalize`
+virtual check-out and check-in of all three stages of each file that
+needs a three-way content merge, by setting the `merge.renormalize`
configuration variable. This prevents changes caused by check-in
conversion from causing spurious merge conflicts when a converted file
is merged with an unconverted file.
diff --git a/Documentation/gitprotocol-v2.adoc b/Documentation/gitprotocol-v2.adoc
index 1652fef3ae..9f6350bbf2 100644
--- a/Documentation/gitprotocol-v2.adoc
+++ b/Documentation/gitprotocol-v2.adoc
@@ -184,9 +184,13 @@ form `agent=X`) to notify the client that the server is running version
the `agent` capability with a value `Y` (in the form `agent=Y`) in its
request to the server (but it MUST NOT do so if the server did not
advertise the agent capability). The `X` and `Y` strings may contain any
-printable ASCII characters except space (i.e., the byte range 32 < x <
-127), and are typically of the form "package/version" (e.g.,
-"git/1.8.3.1"). The agent strings are purely informative for statistics
+printable ASCII characters except space (i.e., the byte range 33 <= x <=
+126), and are typically of the form "package/version-os" (e.g.,
+"git/1.8.3.1-Linux") where `os` is the operating system name (e.g.,
+"Linux"). `X` and `Y` can be configured using the GIT_USER_AGENT
+environment variable and it takes priority. The `os` is
+retrieved using the 'sysname' field of the `uname(2)` system call
+or its equivalent. The agent strings are purely informative for statistics
and debugging purposes, and MUST NOT be used to programmatically assume
the presence or absence of particular features.
diff --git a/Documentation/howto/meson.build b/Documentation/howto/meson.build
index c023c10416..92a08b13ee 100644
--- a/Documentation/howto/meson.build
+++ b/Documentation/howto/meson.build
@@ -41,7 +41,7 @@ custom_target(
foreach howto : howto_sources
howto_stripped = custom_target(
command: [
- find_program('sed'),
+ sed,
'-e',
'1,/^$/d',
'@INPUT@',
diff --git a/Documentation/merge-strategies.adoc b/Documentation/merge-strategies.adoc
index 5fc54ec060..93822ebc4e 100644
--- a/Documentation/merge-strategies.adoc
+++ b/Documentation/merge-strategies.adoc
@@ -22,6 +22,13 @@ ort::
was written as a replacement for the previous default
algorithm, `recursive`.
+
+In the case where the path is a submodule, if the submodule commit used on
+one side of the merge is a descendant of the submodule commit used on the
+other side of the merge, Git attempts to fast-forward to the
+descendant. Otherwise, Git will treat this case as a conflict, suggesting
+as a resolution a submodule commit that is descendant of the conflicting
+ones, if one exists.
++
The 'ort' strategy can take the following options:
ours;;
@@ -56,7 +63,7 @@ ignore-cr-at-eol;;
renormalize;;
This runs a virtual check-out and check-in of all three stages
- of a file when resolving a three-way merge. This option is
+ of any file which needs a three-way merge. This option is
meant to be used when merging branches with different clean
filters or end-of-line normalization rules. See "Merging
branches with differing checkin/checkout attributes" in
@@ -96,6 +103,9 @@ recursive::
the default strategy for resolving two heads from Git v0.99.9k
until v2.33.0.
+
+For a path that is a submodule, the same caution as 'ort' applies to this
+strategy.
++
The 'recursive' strategy takes the same options as 'ort'. However,
there are three additional options that 'ort' ignores (not documented
above) that are potentially useful with the 'recursive' strategy:
diff --git a/Documentation/meson.build b/Documentation/meson.build
index ead8e48213..0a0f2bfa14 100644
--- a/Documentation/meson.build
+++ b/Documentation/meson.build
@@ -6,6 +6,7 @@ manpages = {
'git-apply.adoc' : 1,
'git-archimport.adoc' : 1,
'git-archive.adoc' : 1,
+ 'git-backfill.adoc' : 1,
'git-bisect.adoc' : 1,
'git-blame.adoc' : 1,
'git-branch.adoc' : 1,
@@ -206,9 +207,9 @@ manpages = {
docs_backend = get_option('docs_backend')
if docs_backend == 'auto'
- if find_program('asciidoc', required: false).found()
+ if find_program('asciidoc', dirs: program_path, required: false).found()
docs_backend = 'asciidoc'
- elif find_program('asciidoctor', required: false).found()
+ elif find_program('asciidoctor', dirs: program_path, required: false).found()
docs_backend = 'asciidoctor'
else
error('Neither asciidoc nor asciidoctor were found.')
@@ -216,7 +217,7 @@ if docs_backend == 'auto'
endif
if docs_backend == 'asciidoc'
- asciidoc = find_program('asciidoc', required: true)
+ asciidoc = find_program('asciidoc', dirs: program_path)
asciidoc_html = 'xhtml11'
asciidoc_docbook = 'docbook'
xmlto_extra = [ ]
@@ -245,7 +246,7 @@ if docs_backend == 'asciidoc'
asciidoc_conf,
]
elif docs_backend == 'asciidoctor'
- asciidoctor = find_program('asciidoctor', required: true)
+ asciidoctor = find_program('asciidoctor', dirs: program_path)
asciidoc_html = 'xhtml5'
asciidoc_docbook = 'docbook5'
xmlto_extra = [
@@ -283,8 +284,7 @@ elif docs_backend == 'asciidoctor'
]
endif
-git = find_program('git', required: false)
-xmlto = find_program('xmlto')
+xmlto = find_program('xmlto', dirs: program_path)
cmd_lists = [
'cmds-ancillaryinterrogators.adoc',
@@ -405,7 +405,7 @@ if get_option('docs').contains('html')
pointing_to: 'git.html',
)
- xsltproc = find_program('xsltproc')
+ xsltproc = find_program('xsltproc', dirs: program_path)
user_manual_xml = custom_target(
command: asciidoc_common_options + [
diff --git a/Documentation/pretty-formats.adoc b/Documentation/pretty-formats.adoc
index 8ee940b6a4..07475de8c3 100644
--- a/Documentation/pretty-formats.adoc
+++ b/Documentation/pretty-formats.adoc
@@ -339,10 +339,10 @@ insert an empty string unless we are traversing reflog entries (e.g., by
decoration format if `--decorate` was not already provided on the command
line.
-The boolean options accept an optional value `[=<bool-value>]`. The values
-`true`, `false`, `on`, `off` etc. are all accepted. See the "boolean"
-sub-section in "EXAMPLES" in linkgit:git-config[1]. If a boolean
-option is given with no value, it's enabled.
+The boolean options accept an optional value `[=<bool-value>]`. The
+values taken by `--type=bool` git-config[1], like `yes` and `off`,
+are all accepted. Giving a boolean option without `=<value>` is
+equivalent to giving it with `=true`.
If you add a `+` (plus sign) after '%' of a placeholder, a line-feed
is inserted immediately before the expansion if and only if the
diff --git a/Documentation/rev-list-options.adoc b/Documentation/rev-list-options.adoc
index 196b2781cc..785c0786e0 100644
--- a/Documentation/rev-list-options.adoc
+++ b/Documentation/rev-list-options.adoc
@@ -1024,6 +1024,25 @@ Unexpected missing objects will raise an error.
The form '--missing=print' is like 'allow-any', but will also print a
list of the missing objects. Object IDs are prefixed with a ``?'' character.
+
+The form '--missing=print-info' is like 'print', but will also print additional
+information about the missing object inferred from its containing object. The
+information is all printed on the same line with the missing object ID in the
+form: `?<oid> [<token>=<value>]...`. The `<token>=<value>` pairs containing
+additional information are separated from each other by a SP. The value is
+encoded in a token specific fashion, but SP or LF contained in value are always
+expected to be represented in such a way that the resulting encoded value does
+not have either of these two problematic bytes. Each `<token>=<value>` may be
+one of the following:
++
+--
+* The `path=<path>` shows the path of the missing object inferred from a
+ containing object. A path containing SP or special characters is enclosed in
+ double-quotes in the C style as needed.
++
+* The `type=<type>` shows the type of the missing object inferred from a
+ containing object.
+--
++
If some tips passed to the traversal are missing, they will be
considered as missing too, and the traversal will ignore them. In case
we cannot get their Object ID though, an error will be raised.
diff --git a/Documentation/technical/api-path-walk.adoc b/Documentation/technical/api-path-walk.adoc
index 7075d0d5ab..3e089211fb 100644
--- a/Documentation/technical/api-path-walk.adoc
+++ b/Documentation/technical/api-path-walk.adoc
@@ -56,8 +56,17 @@ better off using the revision walk API instead.
the revision walk so that the walk emits commits marked with the
`UNINTERESTING` flag.
+`pl`::
+ This pattern list pointer allows focusing the path-walk search to
+ a set of patterns, only emitting paths that match the given
+ patterns. See linkgit:gitignore[5] or
+ linkgit:git-sparse-checkout[1] for details about pattern lists.
+ When the pattern list uses cone-mode patterns, then the path-walk
+ API can prune the set of paths it walks to improve performance.
+
Examples
--------
See example usages in:
- `t/helper/test-path-walk.c`
+ `t/helper/test-path-walk.c`,
+ `builtin/backfill.c`
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 56b45333c1..67fa0401cf 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,6 +1,6 @@
#!/bin/sh
-DEF_VER=v2.48.GIT
+DEF_VER=v2.49.0-rc0
LF='
'
diff --git a/Makefile b/Makefile
index a3483e15c4..a9b2de0692 100644
--- a/Makefile
+++ b/Makefile
@@ -1212,6 +1212,7 @@ BUILTIN_OBJS += builtin/am.o
BUILTIN_OBJS += builtin/annotate.o
BUILTIN_OBJS += builtin/apply.o
BUILTIN_OBJS += builtin/archive.o
+BUILTIN_OBJS += builtin/backfill.o
BUILTIN_OBJS += builtin/bisect.o
BUILTIN_OBJS += builtin/blame.o
BUILTIN_OBJS += builtin/branch.o
@@ -1703,16 +1704,16 @@ IMAP_SEND_LDFLAGS += $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO)
ifdef ZLIB_NG
BASIC_CFLAGS += -DHAVE_ZLIB_NG
- ifdef ZLIB_NG_PATH
+ ifdef ZLIB_NG_PATH
BASIC_CFLAGS += -I$(ZLIB_NG_PATH)/include
EXTLIBS += $(call libpath_template,$(ZLIB_NG_PATH)/$(lib))
- endif
+ endif
EXTLIBS += -lz-ng
else
- ifdef ZLIB_PATH
+ ifdef ZLIB_PATH
BASIC_CFLAGS += -I$(ZLIB_PATH)/include
EXTLIBS += $(call libpath_template,$(ZLIB_PATH)/$(lib))
- endif
+ endif
EXTLIBS += -lz
endif
diff --git a/builtin.h b/builtin.h
index f7b166b334..89928ccf92 100644
--- a/builtin.h
+++ b/builtin.h
@@ -120,6 +120,7 @@ int cmd_am(int argc, const char **argv, const char *prefix, struct repository *r
int cmd_annotate(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_apply(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_archive(int argc, const char **argv, const char *prefix, struct repository *repo);
+int cmd_backfill(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_bisect(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_blame(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_branch(int argc, const char **argv, const char *prefix, struct repository *repo);
diff --git a/builtin/backfill.c b/builtin/backfill.c
new file mode 100644
index 0000000000..33e1ea2f84
--- /dev/null
+++ b/builtin/backfill.c
@@ -0,0 +1,147 @@
+/* We need this macro to access core_apply_sparse_checkout */
+#define USE_THE_REPOSITORY_VARIABLE
+
+#include "builtin.h"
+#include "git-compat-util.h"
+#include "config.h"
+#include "parse-options.h"
+#include "repository.h"
+#include "commit.h"
+#include "dir.h"
+#include "environment.h"
+#include "hex.h"
+#include "tree.h"
+#include "tree-walk.h"
+#include "object.h"
+#include "object-store-ll.h"
+#include "oid-array.h"
+#include "oidset.h"
+#include "promisor-remote.h"
+#include "strmap.h"
+#include "string-list.h"
+#include "revision.h"
+#include "trace2.h"
+#include "progress.h"
+#include "packfile.h"
+#include "path-walk.h"
+
+static const char * const builtin_backfill_usage[] = {
+ N_("git backfill [--min-batch-size=<n>] [--[no-]sparse]"),
+ NULL
+};
+
+struct backfill_context {
+ struct repository *repo;
+ struct oid_array current_batch;
+ size_t min_batch_size;
+ int sparse;
+};
+
+static void backfill_context_clear(struct backfill_context *ctx)
+{
+ oid_array_clear(&ctx->current_batch);
+}
+
+static void download_batch(struct backfill_context *ctx)
+{
+ promisor_remote_get_direct(ctx->repo,
+ ctx->current_batch.oid,
+ ctx->current_batch.nr);
+ oid_array_clear(&ctx->current_batch);
+
+ /*
+ * We likely have a new packfile. Add it to the packed list to
+ * avoid possible duplicate downloads of the same objects.
+ */
+ reprepare_packed_git(ctx->repo);
+}
+
+static int fill_missing_blobs(const char *path UNUSED,
+ struct oid_array *list,
+ enum object_type type,
+ void *data)
+{
+ struct backfill_context *ctx = data;
+
+ if (type != OBJ_BLOB)
+ return 0;
+
+ for (size_t i = 0; i < list->nr; i++) {
+ if (!has_object(ctx->repo, &list->oid[i],
+ OBJECT_INFO_FOR_PREFETCH))
+ oid_array_append(&ctx->current_batch, &list->oid[i]);
+ }
+
+ if (ctx->current_batch.nr >= ctx->min_batch_size)
+ download_batch(ctx);
+
+ return 0;
+}
+
+static int do_backfill(struct backfill_context *ctx)
+{
+ struct rev_info revs;
+ struct path_walk_info info = PATH_WALK_INFO_INIT;
+ int ret;
+
+ if (ctx->sparse) {
+ CALLOC_ARRAY(info.pl, 1);
+ if (get_sparse_checkout_patterns(info.pl)) {
+ path_walk_info_clear(&info);
+ return error(_("problem loading sparse-checkout"));
+ }
+ }
+
+ repo_init_revisions(ctx->repo, &revs, "");
+ handle_revision_arg("HEAD", &revs, 0, 0);
+
+ info.blobs = 1;
+ info.tags = info.commits = info.trees = 0;
+
+ info.revs = &revs;
+ info.path_fn = fill_missing_blobs;
+ info.path_fn_data = ctx;
+
+ ret = walk_objects_by_path(&info);
+
+ /* Download the objects that did not fill a batch. */
+ if (!ret)
+ download_batch(ctx);
+
+ path_walk_info_clear(&info);
+ release_revisions(&revs);
+ return ret;
+}
+
+int cmd_backfill(int argc, const char **argv, const char *prefix, struct repository *repo)
+{
+ int result;
+ struct backfill_context ctx = {
+ .repo = repo,
+ .current_batch = OID_ARRAY_INIT,
+ .min_batch_size = 50000,
+ .sparse = 0,
+ };
+ struct option options[] = {
+ OPT_INTEGER(0, "min-batch-size", &ctx.min_batch_size,
+ N_("Minimum number of objects to request at a time")),
+ OPT_BOOL(0, "sparse", &ctx.sparse,
+ N_("Restrict the missing objects to the current sparse-checkout")),
+ OPT_END(),
+ };
+
+ show_usage_with_options_if_asked(argc, argv,
+ builtin_backfill_usage, options);
+
+ argc = parse_options(argc, argv, prefix, options, builtin_backfill_usage,
+ 0);
+
+ repo_config(repo, git_default_config, NULL);
+
+ if (ctx.sparse < 0)
+ ctx.sparse = core_apply_sparse_checkout;
+
+ result = do_backfill(&ctx);
+ backfill_context_clear(&ctx);
+ return result;
+}
diff --git a/builtin/bugreport.c b/builtin/bugreport.c
index 0ac59cc8dc..66d64bfd5a 100644
--- a/builtin/bugreport.c
+++ b/builtin/bugreport.c
@@ -12,10 +12,10 @@
#include "diagnose.h"
#include "object-file.h"
#include "setup.h"
+#include "version.h"
static void get_system_info(struct strbuf *sys_info)
{
- struct utsname uname_info;
char *shell = NULL;
/* get git version from native cmd */
@@ -24,16 +24,7 @@ static void get_system_info(struct strbuf *sys_info)
/* system call for other version info */
strbuf_addstr(sys_info, "uname: ");
- if (uname(&uname_info))
- strbuf_addf(sys_info, _("uname() failed with error '%s' (%d)\n"),
- strerror(errno),
- errno);
- else
- strbuf_addf(sys_info, "%s %s %s %s\n",
- uname_info.sysname,
- uname_info.release,
- uname_info.version,
- uname_info.machine);
+ get_uname_info(sys_info, 1);
strbuf_addstr(sys_info, _("compiler info: "));
get_compiler_info(sys_info);
diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c
index df00b5ee13..be2cebe121 100644
--- a/builtin/check-mailmap.c
+++ b/builtin/check-mailmap.c
@@ -35,7 +35,7 @@ static void check_mailmap(struct string_list *mailmap, const char *contact)
mail = ident.mail_begin;
maillen = ident.mail_end - ident.mail_begin;
} else {
- name = NULL;
+ name = "";
namelen = 0;
mail = contact;
maillen = strlen(contact);
diff --git a/builtin/difftool.c b/builtin/difftool.c
index 03a8bb92a9..41cd00066c 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -12,8 +12,6 @@
* Copyright (C) 2016 Johannes Schindelin
*/
-#define USE_THE_REPOSITORY_VARIABLE
-
#include "builtin.h"
#include "abspath.h"
@@ -36,18 +34,27 @@
#include "entry.h"
#include "setup.h"
-static int trust_exit_code;
-
static const char *const builtin_difftool_usage[] = {
N_("git difftool [<options>] [<commit> [<commit>]] [--] [<path>...]"),
NULL
};
+struct difftool_options {
+ int has_symlinks;
+ int symlinks;
+ int trust_exit_code;
+};
+
static int difftool_config(const char *var, const char *value,
const struct config_context *ctx, void *cb)
{
+ struct difftool_options *dt_options = (struct difftool_options *)cb;
if (!strcmp(var, "difftool.trustexitcode")) {
- trust_exit_code = git_config_bool(var, value);
+ dt_options->trust_exit_code = git_config_bool(var, value);
+ return 0;
+ }
+ if (!strcmp(var, "core.symlinks")) {
+ dt_options->has_symlinks = git_config_bool(var, value);
return 0;
}
@@ -63,7 +70,8 @@ static int print_tool_help(void)
return run_command(&cmd);
}
-static int parse_index_info(char *p, int *mode1, int *mode2,
+static int parse_index_info(struct repository *repo,
+ char *p, int *mode1, int *mode2,
struct object_id *oid1, struct object_id *oid2,
char *status)
{
@@ -75,11 +83,11 @@ static int parse_index_info(char *p, int *mode1, int *mode2,
*mode2 = (int)strtol(p + 1, &p, 8);
if (*p != ' ')
return error("expected ' ', got '%c'", *p);
- if (parse_oid_hex(++p, oid1, (const char **)&p))
+ if (parse_oid_hex_algop(++p, oid1, (const char **)&p, repo->hash_algo))
return error("expected object ID, got '%s'", p);
if (*p != ' ')
return error("expected ' ', got '%c'", *p);
- if (parse_oid_hex(++p, oid2, (const char **)&p))
+ if (parse_oid_hex_algop(++p, oid2, (const char **)&p, repo->hash_algo))
return error("expected object ID, got '%s'", p);
if (*p != ' ')
return error("expected ' ', got '%c'", *p);
@@ -106,7 +114,8 @@ static void add_path(struct strbuf *buf, size_t base_len, const char *path)
/*
* Determine whether we can simply reuse the file in the worktree.
*/
-static int use_wt_file(const char *workdir, const char *name,
+static int use_wt_file(struct repository *repo,
+ const char *workdir, const char *name,
struct object_id *oid)
{
struct strbuf buf = STRBUF_INIT;
@@ -121,7 +130,7 @@ static int use_wt_file(const char *workdir, const char *name,
int fd = open(buf.buf, O_RDONLY);
if (fd >= 0 &&
- !index_fd(the_repository->index, &wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
+ !index_fd(repo->index, &wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
if (is_null_oid(oid)) {
oidcpy(oid, &wt_oid);
use = 1;
@@ -212,13 +221,14 @@ static int path_entry_cmp(const void *cmp_data UNUSED,
return strcmp(a->path, key ? key : b->path);
}
-static void changed_files(struct hashmap *result, const char *index_path,
+static void changed_files(struct repository *repo,
+ struct hashmap *result, const char *index_path,
const char *workdir)
{
struct child_process update_index = CHILD_PROCESS_INIT;
struct child_process diff_files = CHILD_PROCESS_INIT;
struct strbuf buf = STRBUF_INIT;
- const char *git_dir = absolute_path(repo_get_git_dir(the_repository));
+ const char *git_dir = absolute_path(repo_get_git_dir(repo));
FILE *fp;
strvec_pushl(&update_index.args,
@@ -291,13 +301,15 @@ static int ensure_leading_directories(char *path)
* to compare the readlink(2) result as text, even on a filesystem that is
* capable of doing a symbolic link.
*/
-static char *get_symlink(const struct object_id *oid, const char *path)
+static char *get_symlink(struct repository *repo,
+ struct difftool_options *dt_options,
+ const struct object_id *oid, const char *path)
{
char *data;
if (is_null_oid(oid)) {
/* The symlink is unknown to Git so read from the filesystem */
struct strbuf link = STRBUF_INIT;
- if (has_symlinks) {
+ if (dt_options->has_symlinks) {
if (strbuf_readlink(&link, path, strlen(path)))
die(_("could not read symlink %s"), path);
} else if (strbuf_read_file(&link, path, 128))
@@ -307,8 +319,7 @@ static char *get_symlink(const struct object_id *oid, const char *path)
} else {
enum object_type type;
unsigned long size;
- data = repo_read_object_file(the_repository, oid, &type,
- &size);
+ data = repo_read_object_file(repo, oid, &type, &size);
if (!data)
die(_("could not read object %s for symlink %s"),
oid_to_hex(oid), path);
@@ -355,7 +366,9 @@ static void write_standin_files(struct pair_entry *entry,
write_file_in_directory(rdir, rdir_len, entry->path, entry->right);
}
-static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
+static int run_dir_diff(struct repository *repo,
+ struct difftool_options *dt_options,
+ const char *extcmd, const char *prefix,
struct child_process *child)
{
struct strbuf info = STRBUF_INIT, lpath = STRBUF_INIT;
@@ -375,7 +388,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
struct hashmap symlinks2 = HASHMAP_INIT(pair_cmp, NULL);
struct hashmap_iter iter;
struct pair_entry *entry;
- struct index_state wtindex = INDEX_STATE_INIT(the_repository);
+ struct index_state wtindex = INDEX_STATE_INIT(repo);
struct checkout lstate, rstate;
int err = 0;
struct child_process cmd = CHILD_PROCESS_INIT;
@@ -383,7 +396,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
struct hashmap tmp_modified = HASHMAP_INIT(path_entry_cmp, NULL);
int indices_loaded = 0;
- workdir = repo_get_work_tree(the_repository);
+ workdir = repo_get_work_tree(repo);
/* Setup temp directories */
tmp = getenv("TMPDIR");
@@ -438,8 +451,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
"not supported in\n"
"directory diff mode ('-d' and '--dir-diff')."));
- if (parse_index_info(info.buf, &lmode, &rmode, &loid, &roid,
- &status))
+ if (parse_index_info(repo, info.buf, &lmode, &rmode, &loid, &roid, &status))
break;
if (strbuf_getline_nul(&lpath, fp))
break;
@@ -469,13 +481,13 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
}
if (S_ISLNK(lmode)) {
- char *content = get_symlink(&loid, src_path);
+ char *content = get_symlink(repo, dt_options, &loid, src_path);
add_left_or_right(&symlinks2, src_path, content, 0);
free(content);
}
if (S_ISLNK(rmode)) {
- char *content = get_symlink(&roid, dst_path);
+ char *content = get_symlink(repo, dt_options, &roid, dst_path);
add_left_or_right(&symlinks2, dst_path, content, 1);
free(content);
}
@@ -500,7 +512,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
}
hashmap_add(&working_tree_dups, &entry->entry);
- if (!use_wt_file(workdir, dst_path, &roid)) {
+ if (!use_wt_file(repo, workdir, dst_path, &roid)) {
if (checkout_path(rmode, &roid, dst_path,
&rstate)) {
ret = error("could not write '%s'",
@@ -528,7 +540,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
goto finish;
}
add_path(&wtdir, wtdir_len, dst_path);
- if (symlinks) {
+ if (dt_options->symlinks) {
if (symlink(wtdir.buf, rdir.buf)) {
ret = error_errno("could not symlink '%s' to '%s'", wtdir.buf, rdir.buf);
goto finish;
@@ -614,7 +626,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
if (lstat(rdir.buf, &st))
continue;
- if ((symlinks && S_ISLNK(st.st_mode)) || !S_ISREG(st.st_mode))
+ if ((dt_options->symlinks && S_ISLNK(st.st_mode)) || !S_ISREG(st.st_mode))
continue;
if (!indices_loaded) {
@@ -626,9 +638,9 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
ret = error("could not write %s", buf.buf);
goto finish;
}
- changed_files(&wt_modified, buf.buf, workdir);
+ changed_files(repo, &wt_modified, buf.buf, workdir);
strbuf_setlen(&rdir, rdir_len);
- changed_files(&tmp_modified, buf.buf, rdir.buf);
+ changed_files(repo, &tmp_modified, buf.buf, rdir.buf);
add_path(&rdir, rdir_len, name);
indices_loaded = 1;
}
@@ -702,11 +714,15 @@ static int run_file_diff(int prompt, const char *prefix,
int cmd_difftool(int argc,
const char **argv,
const char *prefix,
- struct repository *repo UNUSED)
+ struct repository *repo)
{
- int use_gui_tool = -1, dir_diff = 0, prompt = -1, symlinks = 0,
- tool_help = 0, no_index = 0;
+ int use_gui_tool = -1, dir_diff = 0, prompt = -1, tool_help = 0, no_index = 0;
static char *difftool_cmd = NULL, *extcmd = NULL;
+ struct difftool_options dt_options = {
+ .has_symlinks = 1,
+ .symlinks = 1,
+ .trust_exit_code = 0
+ };
struct option builtin_difftool_options[] = {
OPT_BOOL('g', "gui", &use_gui_tool,
N_("use `diff.guitool` instead of `diff.tool`")),
@@ -717,14 +733,14 @@ int cmd_difftool(int argc,
0, PARSE_OPT_NONEG),
OPT_SET_INT_F(0, "prompt", &prompt, NULL,
1, PARSE_OPT_NONEG | PARSE_OPT_HIDDEN),
- OPT_BOOL(0, "symlinks", &symlinks,
+ OPT_BOOL(0, "symlinks", &dt_options.symlinks,
N_("use symlinks in dir-diff mode")),
OPT_STRING('t', "tool", &difftool_cmd, N_("tool"),
N_("use the specified diff tool")),
OPT_BOOL(0, "tool-help", &tool_help,
N_("print a list of diff tools that may be used with "
"`--tool`")),
- OPT_BOOL(0, "trust-exit-code", &trust_exit_code,
+ OPT_BOOL(0, "trust-exit-code", &dt_options.trust_exit_code,
N_("make 'git-difftool' exit when an invoked diff "
"tool returns a non-zero exit code")),
OPT_STRING('x', "extcmd", &extcmd, N_("command"),
@@ -734,8 +750,9 @@ int cmd_difftool(int argc,
};
struct child_process child = CHILD_PROCESS_INIT;
- git_config(difftool_config, NULL);
- symlinks = has_symlinks;
+ if (repo)
+ repo_config(repo, difftool_config, &dt_options);
+ dt_options.symlinks = dt_options.has_symlinks;
argc = parse_options(argc, argv, prefix, builtin_difftool_options,
builtin_difftool_usage, PARSE_OPT_KEEP_UNKNOWN_OPT |
@@ -749,8 +766,8 @@ int cmd_difftool(int argc,
if (!no_index){
setup_work_tree();
- setenv(GIT_DIR_ENVIRONMENT, absolute_path(repo_get_git_dir(the_repository)), 1);
- setenv(GIT_WORK_TREE_ENVIRONMENT, absolute_path(repo_get_work_tree(the_repository)), 1);
+ setenv(GIT_DIR_ENVIRONMENT, absolute_path(repo_get_git_dir(repo)), 1);
+ setenv(GIT_WORK_TREE_ENVIRONMENT, absolute_path(repo_get_work_tree(repo)), 1);
} else if (dir_diff)
die(_("options '%s' and '%s' cannot be used together"), "--dir-diff", "--no-index");
@@ -783,7 +800,7 @@ int cmd_difftool(int argc,
}
setenv("GIT_DIFFTOOL_TRUST_EXIT_CODE",
- trust_exit_code ? "true" : "false", 1);
+ dt_options.trust_exit_code ? "true" : "false", 1);
/*
* In directory diff mode, 'git-difftool--helper' is called once
@@ -799,6 +816,6 @@ int cmd_difftool(int argc,
strvec_pushv(&child.args, argv);
if (dir_diff)
- return run_dir_diff(extcmd, symlinks, prefix, &child);
+ return run_dir_diff(repo, &dt_options, extcmd, prefix, &child);
return run_file_diff(prompt, prefix, &child);
}
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 9a6c8b4e4c..3ec7127b3a 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -18,6 +18,7 @@
#include "tree.h"
#include "config.h"
#include "strvec.h"
+#include "write-or-die.h"
static int line_termination = '\n';
@@ -575,7 +576,7 @@ int cmd_merge_tree(int argc,
};
/* Init merge options */
- init_ui_merge_options(&o.merge_options, the_repository);
+ init_basic_merge_options(&o.merge_options, the_repository);
/* Parse arguments */
original_argc = argc - 1; /* ignoring argv[0] */
@@ -600,7 +601,6 @@ int cmd_merge_tree(int argc,
line_termination = '\0';
while (strbuf_getline_lf(&buf, stdin) != EOF) {
struct strbuf **split;
- int result;
const char *input_merge_base = NULL;
split = strbuf_split(&buf, ' ');
@@ -617,15 +617,14 @@ int cmd_merge_tree(int argc,
if (input_merge_base && split[2] && split[3] && !split[4]) {
strbuf_rtrim(split[2]);
strbuf_rtrim(split[3]);
- result = real_merge(&o, input_merge_base, split[2]->buf, split[3]->buf, prefix);
+ real_merge(&o, input_merge_base, split[2]->buf, split[3]->buf, prefix);
} else if (!input_merge_base && !split[2]) {
- result = real_merge(&o, NULL, split[0]->buf, split[1]->buf, prefix);
+ real_merge(&o, NULL, split[0]->buf, split[1]->buf, prefix);
} else {
die(_("malformed input line: '%s'."), buf.buf);
}
+ maybe_flush_or_die(stdout, "stdout");
- if (result < 0)
- die(_("merging cannot continue; got unclean result of %d"), result);
strbuf_list_free(split);
}
strbuf_release(&buf);
diff --git a/builtin/refs.c b/builtin/refs.c
index a29f195834..c459507d51 100644
--- a/builtin/refs.c
+++ b/builtin/refs.c
@@ -30,6 +30,9 @@ static int cmd_refs_migrate(int argc, const char **argv, const char *prefix,
OPT_BIT(0, "dry-run", &flags,
N_("perform a non-destructive dry-run"),
REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN),
+ OPT_BIT(0, "no-reflog", &flags,
+ N_("drop reflogs entirely during the migration"),
+ REPO_MIGRATE_REF_STORAGE_FORMAT_SKIP_REFLOG),
OPT_END(),
};
struct strbuf errbuf = STRBUF_INIT;
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index beb8c2529d..bb26bee0d4 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -22,7 +22,10 @@
#include "progress.h"
#include "reflog-walk.h"
#include "oidset.h"
+#include "oidmap.h"
#include "packfile.h"
+#include "quote.h"
+#include "strbuf.h"
static const char rev_list_usage[] =
"git rev-list [<options>] <commit>... [--] [<path>...]\n"
@@ -73,11 +76,17 @@ static unsigned progress_counter;
static struct oidset omitted_objects;
static int arg_print_omitted; /* print objects omitted by filter */
-static struct oidset missing_objects;
+struct missing_objects_map_entry {
+ struct oidmap_entry entry;
+ const char *path;
+ unsigned type;
+};
+static struct oidmap missing_objects;
enum missing_action {
MA_ERROR = 0, /* fail if any missing objects are encountered */
MA_ALLOW_ANY, /* silently allow ALL missing objects */
MA_PRINT, /* print ALL missing objects in special section */
+ MA_PRINT_INFO, /* same as MA_PRINT but also prints missing object info */
MA_ALLOW_PROMISOR, /* silently allow all missing PROMISOR objects */
};
static enum missing_action arg_missing_action;
@@ -101,7 +110,49 @@ static off_t get_object_disk_usage(struct object *obj)
return size;
}
-static inline void finish_object__ma(struct object *obj)
+static void add_missing_object_entry(struct object_id *oid, const char *path,
+ unsigned type)
+{
+ struct missing_objects_map_entry *entry;
+
+ if (oidmap_get(&missing_objects, oid))
+ return;
+
+ CALLOC_ARRAY(entry, 1);
+ entry->entry.oid = *oid;
+ entry->type = type;
+ if (path)
+ entry->path = xstrdup(path);
+ oidmap_put(&missing_objects, entry);
+}
+
+static void print_missing_object(struct missing_objects_map_entry *entry,
+ int print_missing_info)
+{
+ struct strbuf sb = STRBUF_INIT;
+
+ if (!print_missing_info) {
+ printf("?%s\n", oid_to_hex(&entry->entry.oid));
+ return;
+ }
+
+ if (entry->path && *entry->path) {
+ struct strbuf path = STRBUF_INIT;
+
+ strbuf_addstr(&sb, " path=");
+ quote_path(entry->path, NULL, &path, QUOTE_PATH_QUOTE_SP);
+ strbuf_addbuf(&sb, &path);
+
+ strbuf_release(&path);
+ }
+ if (entry->type)
+ strbuf_addf(&sb, " type=%s", type_name(entry->type));
+
+ printf("?%s%s\n", oid_to_hex(&entry->entry.oid), sb.buf);
+ strbuf_release(&sb);
+}
+
+static inline void finish_object__ma(struct object *obj, const char *name)
{
/*
* Whether or not we try to dynamically fetch missing objects
@@ -119,7 +170,8 @@ static inline void finish_object__ma(struct object *obj)
return;
case MA_PRINT:
- oidset_insert(&missing_objects, &obj->oid);
+ case MA_PRINT_INFO:
+ add_missing_object_entry(&obj->oid, name, obj->type);
return;
case MA_ALLOW_PROMISOR:
@@ -152,7 +204,7 @@ static void show_commit(struct commit *commit, void *data)
if (revs->do_not_die_on_missing_objects &&
oidset_contains(&revs->missing_commits, &commit->object.oid)) {
- finish_object__ma(&commit->object);
+ finish_object__ma(&commit->object, NULL);
return;
}
@@ -268,12 +320,11 @@ static void show_commit(struct commit *commit, void *data)
finish_commit(commit);
}
-static int finish_object(struct object *obj, const char *name UNUSED,
- void *cb_data)
+static int finish_object(struct object *obj, const char *name, void *cb_data)
{
struct rev_list_info *info = cb_data;
if (oid_object_info_extended(the_repository, &obj->oid, NULL, 0) < 0) {
- finish_object__ma(obj);
+ finish_object__ma(obj, name);
return 1;
}
if (info->revs->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT)
@@ -414,6 +465,12 @@ static inline int parse_missing_action_value(const char *value)
return 1;
}
+ if (!strcmp(value, "print-info")) {
+ arg_missing_action = MA_PRINT_INFO;
+ fetch_if_missing = 0;
+ return 1;
+ }
+
if (!strcmp(value, "allow-promisor")) {
arg_missing_action = MA_ALLOW_PROMISOR;
fetch_if_missing = 0;
@@ -781,10 +838,18 @@ int cmd_rev_list(int argc,
if (arg_print_omitted)
oidset_init(&omitted_objects, DEFAULT_OIDSET_SIZE);
- if (arg_missing_action == MA_PRINT) {
- oidset_init(&missing_objects, DEFAULT_OIDSET_SIZE);
+ if (arg_missing_action == MA_PRINT ||
+ arg_missing_action == MA_PRINT_INFO) {
+ struct oidset_iter iter;
+ struct object_id *oid;
+
+ oidmap_init(&missing_objects, DEFAULT_OIDSET_SIZE);
+ oidset_iter_init(&revs.missing_commits, &iter);
+
/* Add missing tips */
- oidset_insert_from_set(&missing_objects, &revs.missing_commits);
+ while ((oid = oidset_iter_next(&iter)))
+ add_missing_object_entry(oid, NULL, 0);
+
oidset_clear(&revs.missing_commits);
}
@@ -800,13 +865,20 @@ int cmd_rev_list(int argc,
printf("~%s\n", oid_to_hex(oid));
oidset_clear(&omitted_objects);
}
- if (arg_missing_action == MA_PRINT) {
- struct oidset_iter iter;
- struct object_id *oid;
- oidset_iter_init(&missing_objects, &iter);
- while ((oid = oidset_iter_next(&iter)))
- printf("?%s\n", oid_to_hex(oid));
- oidset_clear(&missing_objects);
+ if (arg_missing_action == MA_PRINT ||
+ arg_missing_action == MA_PRINT_INFO) {
+ struct missing_objects_map_entry *entry;
+ struct oidmap_iter iter;
+
+ oidmap_iter_init(&missing_objects, &iter);
+
+ while ((entry = oidmap_iter_next(&iter))) {
+ print_missing_object(entry, arg_missing_action ==
+ MA_PRINT_INFO);
+ free((void *)entry->path);
+ }
+
+ oidmap_free(&missing_objects, true);
}
stop_progress(&progress);
diff --git a/builtin/update-server-info.c b/builtin/update-server-info.c
index 47a3f0bdd9..d7467290a8 100644
--- a/builtin/update-server-info.c
+++ b/builtin/update-server-info.c
@@ -1,4 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
#include "builtin.h"
#include "config.h"
#include "gettext.h"
@@ -13,7 +12,7 @@ static const char * const update_server_info_usage[] = {
int cmd_update_server_info(int argc,
const char **argv,
const char *prefix,
- struct repository *repo UNUSED)
+ struct repository *repo)
{
int force = 0;
struct option options[] = {
@@ -21,11 +20,12 @@ int cmd_update_server_info(int argc,
OPT_END()
};
- git_config(git_default_config, NULL);
+ if (repo)
+ repo_config(repo, git_default_config, NULL);
argc = parse_options(argc, argv, prefix, options,
update_server_info_usage, 0);
if (argc > 0)
usage_with_options(update_server_info_usage, options);
- return !!update_server_info(the_repository, force);
+ return !!update_server_info(repo, force);
}
diff --git a/command-list.txt b/command-list.txt
index e0bb87b3b5..c537114b46 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -60,6 +60,7 @@ git-annotate ancillaryinterrogators
git-apply plumbingmanipulators complete
git-archimport foreignscminterface
git-archive mainporcelain
+git-backfill mainporcelain history
git-bisect mainporcelain info
git-blame ancillaryinterrogators complete
git-branch mainporcelain history
diff --git a/commit.c b/commit.c
index 540660359d..6efdb03997 100644
--- a/commit.c
+++ b/commit.c
@@ -780,14 +780,14 @@ static void clear_commit_marks_1(struct commit_list **plist,
void clear_commit_marks_many(size_t nr, struct commit **commit, unsigned int mark)
{
- struct commit_list *list = NULL;
-
for (size_t i = 0; i < nr; i++) {
+ struct commit_list *list = NULL;
+
clear_commit_marks_1(&list, *commit, mark);
+ while (list)
+ clear_commit_marks_1(&list, pop_commit(&list), mark);
commit++;
}
- while (list)
- clear_commit_marks_1(&list, pop_commit(&list), mark);
}
void clear_commit_marks(struct commit *commit, unsigned int mark)
diff --git a/compat/mingw.c b/compat/mingw.c
index 1d5b211b54..f524c54d06 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2278,7 +2278,9 @@ repeat:
old_handle = CreateFileW(wpold, DELETE,
FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
- NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
+ NULL);
if (old_handle == INVALID_HANDLE_VALUE) {
errno = err_win_to_posix(GetLastError());
return -1;
diff --git a/connect.c b/connect.c
index 91f3990014..e6e25a0479 100644
--- a/connect.c
+++ b/connect.c
@@ -624,7 +624,7 @@ const char *parse_feature_value(const char *feature_list, const char *feature, s
*offset = found + len - orig_start;
return value;
}
- /* feature with a value (e.g., "agent=git/1.2.3") */
+ /* feature with a value (e.g., "agent=git/1.2.3-Linux") */
else if (*value == '=') {
size_t end;
diff --git a/contrib/credential/libsecret/Makefile b/contrib/credential/libsecret/Makefile
index 3e67552cc5..97ce9c92fb 100644
--- a/contrib/credential/libsecret/Makefile
+++ b/contrib/credential/libsecret/Makefile
@@ -1,3 +1,6 @@
+# The default target of this Makefile is...
+all::
+
MAIN:=git-credential-libsecret
all:: $(MAIN)
diff --git a/contrib/credential/osxkeychain/Makefile b/contrib/credential/osxkeychain/Makefile
index 238f5f8c36..0948297e20 100644
--- a/contrib/credential/osxkeychain/Makefile
+++ b/contrib/credential/osxkeychain/Makefile
@@ -1,3 +1,4 @@
+# The default target of this Makefile is...
all:: git-credential-osxkeychain
CC = gcc
diff --git a/contrib/credential/wincred/Makefile b/contrib/credential/wincred/Makefile
index 6e992c0866..5b795fc9fe 100644
--- a/contrib/credential/wincred/Makefile
+++ b/contrib/credential/wincred/Makefile
@@ -1,4 +1,5 @@
-all: git-credential-wincred.exe
+# The default target of this Makefile is...
+all:: git-credential-wincred.exe
-include ../../../config.mak.autogen
-include ../../../config.mak
diff --git a/contrib/diff-highlight/Makefile b/contrib/diff-highlight/Makefile
index f2be7cc924..33c2ccc9f7 100644
--- a/contrib/diff-highlight/Makefile
+++ b/contrib/diff-highlight/Makefile
@@ -1,4 +1,5 @@
-all: diff-highlight
+# The default target of this Makefile is...
+all:: diff-highlight
PERL_PATH = /usr/bin/perl
-include ../../config.mak
diff --git a/contrib/diff-highlight/t/Makefile b/contrib/diff-highlight/t/Makefile
index 5ff5275496..2a98541477 100644
--- a/contrib/diff-highlight/t/Makefile
+++ b/contrib/diff-highlight/t/Makefile
@@ -1,3 +1,6 @@
+# The default target of this Makefile is...
+all::
+
-include ../../../config.mak.autogen
-include ../../../config.mak
@@ -6,7 +9,7 @@ SHELL_PATH ?= $(SHELL)
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
-all: test
+all:: test
test: $(T)
.PHONY: help clean all test $(T)
diff --git a/contrib/mw-to-git/Makefile b/contrib/mw-to-git/Makefile
index 4e603512a3..497ac434d6 100644
--- a/contrib/mw-to-git/Makefile
+++ b/contrib/mw-to-git/Makefile
@@ -12,6 +12,9 @@
#
# make install
+# The default target of this Makefile is...
+all::
+
GIT_MEDIAWIKI_PM=Git/Mediawiki.pm
SCRIPT_PERL=git-remote-mediawiki.perl
SCRIPT_PERL+=git-mw.perl
@@ -27,7 +30,7 @@ INSTLIBDIR=$(shell $(MAKE) -C $(GIT_ROOT_DIR)/ \
DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
INSTLIBDIR_SQ = $(subst ','\'',$(INSTLIBDIR))
-all: build
+all:: build
test: all
$(MAKE) -C t
diff --git a/contrib/mw-to-git/t/Makefile b/contrib/mw-to-git/t/Makefile
index f422203fa0..6c9f377caa 100644
--- a/contrib/mw-to-git/t/Makefile
+++ b/contrib/mw-to-git/t/Makefile
@@ -8,7 +8,8 @@
#
## Test git-remote-mediawiki
-all: test
+# The default target of this Makefile is...
+all:: test
-include ../../../config.mak.autogen
-include ../../../config.mak
diff --git a/contrib/persistent-https/Makefile b/contrib/persistent-https/Makefile
index 52b84ba3d4..691737e76b 100644
--- a/contrib/persistent-https/Makefile
+++ b/contrib/persistent-https/Makefile
@@ -12,10 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# The default target of this Makefile is...
+all::
+
BUILD_LABEL=$(shell cut -d" " -f3 ../../GIT-VERSION-FILE)
TAR_OUT=$(shell go env GOOS)_$(shell go env GOARCH).tar.gz
-all: git-remote-persistent-https git-remote-persistent-https--proxy \
+all:: git-remote-persistent-https git-remote-persistent-https--proxy \
git-remote-persistent-http
git-remote-persistent-https--proxy: git-remote-persistent-https
diff --git a/contrib/subtree/t/Makefile b/contrib/subtree/t/Makefile
index 093399c788..2a85f5ee84 100644
--- a/contrib/subtree/t/Makefile
+++ b/contrib/subtree/t/Makefile
@@ -3,6 +3,9 @@
# Copyright (c) 2005 Junio C Hamano
#
+# The default target of this Makefile is...
+all::
+
-include ../../../config.mak.autogen
-include ../../../config.mak
@@ -31,7 +34,7 @@ TSVN = $(sort $(wildcard t91[0-9][0-9]-*.sh))
TGITWEB = $(sort $(wildcard t95[0-9][0-9]-*.sh))
THELPERS = $(sort $(filter-out $(T),$(wildcard *.sh)))
-all: $(DEFAULT_TEST_TARGET)
+all:: $(DEFAULT_TEST_TARGET)
test: pre-clean $(TEST_LINT)
$(MAKE) aggregate-results-and-cleanup
diff --git a/contrib/thunderbird-patch-inline/appp.sh b/contrib/thunderbird-patch-inline/appp.sh
index 1053872eea..fdcc948352 100755
--- a/contrib/thunderbird-patch-inline/appp.sh
+++ b/contrib/thunderbird-patch-inline/appp.sh
@@ -31,7 +31,7 @@ BODY=$(sed -e "1,/${SEP}/d" $1)
CMT_MSG=$(sed -e '1,/^$/d' -e '/^---$/,$d' "${PATCH}")
DIFF=$(sed -e '1,/^---$/d' "${PATCH}")
-CCS=$(echo -e "$CMT_MSG\n$HEADERS" | sed -n -e 's/^Cc: \(.*\)$/\1,/gp' \
+CCS=$(printf '%s\n%s\n' "$CMT_MSG" "$HEADERS" | sed -n -e 's/^Cc: \(.*\)$/\1,/gp' \
-e 's/^Signed-off-by: \(.*\)/\1,/gp')
echo "$SUBJECT" > $1
diff --git a/diff.c b/diff.c
index 019fb893a7..c89c15d98e 100644
--- a/diff.c
+++ b/diff.c
@@ -5493,6 +5493,8 @@ static int diff_opt_pickaxe_regex(const struct option *opt,
BUG_ON_OPT_NEG(unset);
options->pickaxe = arg;
options->pickaxe_opts |= DIFF_PICKAXE_KIND_G;
+ if (arg && !*arg)
+ return error(_("-G requires a non-empty argument"));
return 0;
}
@@ -5504,6 +5506,8 @@ static int diff_opt_pickaxe_string(const struct option *opt,
BUG_ON_OPT_NEG(unset);
options->pickaxe = arg;
options->pickaxe_opts |= DIFF_PICKAXE_KIND_S;
+ if (arg && !*arg)
+ return error(_("-S requires a non-empty argument"));
return 0;
}
diff --git a/dir.c b/dir.c
index 5b2181e589..16ccfe7e4e 100644
--- a/dir.c
+++ b/dir.c
@@ -1093,10 +1093,6 @@ static void invalidate_directory(struct untracked_cache *uc,
dir->dirs[i]->recurse = 0;
}
-static int add_patterns_from_buffer(char *buf, size_t size,
- const char *base, int baselen,
- struct pattern_list *pl);
-
/* Flags for add_patterns() */
#define PATTERN_NOFOLLOW (1<<0)
@@ -1186,9 +1182,9 @@ static int add_patterns(const char *fname, const char *base, int baselen,
return 0;
}
-static int add_patterns_from_buffer(char *buf, size_t size,
- const char *base, int baselen,
- struct pattern_list *pl)
+int add_patterns_from_buffer(char *buf, size_t size,
+ const char *base, int baselen,
+ struct pattern_list *pl)
{
char *orig = buf;
int i, lineno = 1;
diff --git a/dir.h b/dir.h
index a3a2f00f5d..6cfef5df66 100644
--- a/dir.h
+++ b/dir.h
@@ -467,6 +467,9 @@ void add_patterns_from_file(struct dir_struct *, const char *fname);
int add_patterns_from_blob_to_list(struct object_id *oid,
const char *base, int baselen,
struct pattern_list *pl);
+int add_patterns_from_buffer(char *buf, size_t size,
+ const char *base, int baselen,
+ struct pattern_list *pl);
void parse_path_pattern(const char **string, int *patternlen, unsigned *flags, int *nowildcardlen);
void add_pattern(const char *string, const char *base,
int baselen, struct pattern_list *pl, int srcpos);
diff --git a/git-gui/Makefile b/git-gui/Makefile
index 667c39ed56..6c5a12bc32 100644
--- a/git-gui/Makefile
+++ b/git-gui/Makefile
@@ -1,3 +1,4 @@
+# The default target of this Makefile is...
all::
# Define V=1 to have a more verbose compile.
diff --git a/git-gui/po/glossary/Makefile b/git-gui/po/glossary/Makefile
index 749aa2e7ec..e656b0d2b0 100644
--- a/git-gui/po/glossary/Makefile
+++ b/git-gui/po/glossary/Makefile
@@ -1,3 +1,6 @@
+# The default target of this Makefile is...
+update-po::
+
PO_TEMPLATE = git-gui-glossary.pot
ALL_POFILES = $(wildcard *.po)
diff --git a/git.c b/git.c
index b23761480f..450d6aaa86 100644
--- a/git.c
+++ b/git.c
@@ -506,6 +506,7 @@ static struct cmd_struct commands[] = {
{ "annotate", cmd_annotate, RUN_SETUP },
{ "apply", cmd_apply, RUN_SETUP_GENTLY },
{ "archive", cmd_archive, RUN_SETUP_GENTLY },
+ { "backfill", cmd_backfill, RUN_SETUP },
{ "bisect", cmd_bisect, RUN_SETUP },
{ "blame", cmd_blame, RUN_SETUP },
{ "branch", cmd_branch, RUN_SETUP | DELAY_PAGER_CONFIG },
diff --git a/gitk-git/Makefile b/gitk-git/Makefile
index e1f0aff4a1..3a3c56c318 100644
--- a/gitk-git/Makefile
+++ b/gitk-git/Makefile
@@ -8,6 +8,7 @@ gitk_libdir ?= $(sharedir)/gitk/lib
msgsdir ?= $(gitk_libdir)/msgs
msgsdir_SQ = $(subst ','\'',$(msgsdir))
+SHELL_PATH ?= /bin/sh
TCL_PATH ?= tclsh
TCLTK_PATH ?= wish
INSTALL ?= install
@@ -64,9 +65,7 @@ clean::
gitk-wish: gitk GIT-TCLTK-VARS
$(QUIET_GEN)$(RM) $@ $@+ && \
- sed -e '1,3s|^exec .* "$$0"|exec $(subst |,'\|',$(TCLTK_PATH_SQ)) "$$0"|' <gitk >$@+ && \
- chmod +x $@+ && \
- mv -f $@+ $@
+ $(SHELL_PATH) ./generate-tcl.sh "$(TCLTK_PATH_SQ)" "$<" "$@"
$(PO_TEMPLATE): gitk
$(XGETTEXT) -kmc -LTcl -o $@ gitk
diff --git a/gitk-git/generate-tcl.sh b/gitk-git/generate-tcl.sh
new file mode 100755
index 0000000000..46bba6d246
--- /dev/null
+++ b/gitk-git/generate-tcl.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+set -e
+
+WISH=$(echo "$1" | sed 's/|/\\|/g')
+INPUT="$2"
+OUTPUT="$3"
+
+sed -e "1,3s|^exec .* \"\$0\"|exec $WISH \"\$0\"|" "$INPUT" >"$OUTPUT"+
+chmod a+x "$OUTPUT"+
+mv "$OUTPUT"+ "$OUTPUT"
diff --git a/gitk-git/gitk b/gitk-git/gitk
index 47a7c1d29c..bc9efa1856 100755
--- a/gitk-git/gitk
+++ b/gitk-git/gitk
@@ -9,6 +9,141 @@ exec wish "$0" -- "$@"
package require Tk
+######################################################################
+##
+## Enabling platform-specific code paths
+
+proc is_MacOSX {} {
+ if {[tk windowingsystem] eq {aqua}} {
+ return 1
+ }
+ return 0
+}
+
+proc is_Windows {} {
+ if {$::tcl_platform(platform) eq {windows}} {
+ return 1
+ }
+ return 0
+}
+
+set _iscygwin {}
+proc is_Cygwin {} {
+ global _iscygwin
+ if {$_iscygwin eq {}} {
+ if {[string match "CYGWIN_*" $::tcl_platform(os)]} {
+ set _iscygwin 1
+ } else {
+ set _iscygwin 0
+ }
+ }
+ return $_iscygwin
+}
+
+######################################################################
+##
+## PATH lookup
+
+set _search_path {}
+proc _which {what args} {
+ global env _search_exe _search_path
+
+ if {$_search_path eq {}} {
+ if {[is_Cygwin] && [regexp {^(/|\.:)} $env(PATH)]} {
+ set _search_path [split [exec cygpath \
+ --windows \
+ --path \
+ --absolute \
+ $env(PATH)] {;}]
+ set _search_exe .exe
+ } elseif {[is_Windows]} {
+ set gitguidir [file dirname [info script]]
+ regsub -all ";" $gitguidir "\\;" gitguidir
+ set env(PATH) "$gitguidir;$env(PATH)"
+ set _search_path [split $env(PATH) {;}]
+ # Skip empty `PATH` elements
+ set _search_path [lsearch -all -inline -not -exact \
+ $_search_path ""]
+ set _search_exe .exe
+ } else {
+ set _search_path [split $env(PATH) :]
+ set _search_exe {}
+ }
+ }
+
+ if {[is_Windows] && [lsearch -exact $args -script] >= 0} {
+ set suffix {}
+ } else {
+ set suffix $_search_exe
+ }
+
+ foreach p $_search_path {
+ set p [file join $p $what$suffix]
+ if {[file exists $p]} {
+ return [file normalize $p]
+ }
+ }
+ return {}
+}
+
+proc sanitize_command_line {command_line from_index} {
+ set i $from_index
+ while {$i < [llength $command_line]} {
+ set cmd [lindex $command_line $i]
+ if {[file pathtype $cmd] ne "absolute"} {
+ set fullpath [_which $cmd]
+ if {$fullpath eq ""} {
+ throw {NOT-FOUND} "$cmd not found in PATH"
+ }
+ lset command_line $i $fullpath
+ }
+
+ # handle piped commands, e.g. `exec A | B`
+ for {incr i} {$i < [llength $command_line]} {incr i} {
+ if {[lindex $command_line $i] eq "|"} {
+ incr i
+ break
+ }
+ }
+ }
+ return $command_line
+}
+
+# Override `exec` to avoid unsafe PATH lookup
+
+rename exec real_exec
+
+proc exec {args} {
+ # skip options
+ for {set i 0} {$i < [llength $args]} {incr i} {
+ set arg [lindex $args $i]
+ if {$arg eq "--"} {
+ incr i
+ break
+ }
+ if {[string range $arg 0 0] ne "-"} {
+ break
+ }
+ }
+ set args [sanitize_command_line $args $i]
+ uplevel 1 real_exec $args
+}
+
+# Override `open` to avoid unsafe PATH lookup
+
+rename open real_open
+
+proc open {args} {
+ set arg0 [lindex $args 0]
+ if {[string range $arg0 0 0] eq "|"} {
+ set command_line [string trim [string range $arg0 1 end]]
+ lset args 0 "| [sanitize_command_line $command_line 0]"
+ }
+ uplevel 1 real_open $args
+}
+
+# End of safe PATH lookup stuff
+
proc hasworktree {} {
return [expr {[exec git rev-parse --is-bare-repository] == "false" &&
[exec git rev-parse --is-inside-git-dir] == "false"}]
@@ -2103,7 +2238,7 @@ proc makewindow {} {
global headctxmenu progresscanv progressitem progresscoords statusw
global fprogitem fprogcoord lastprogupdate progupdatepending
global rprogitem rprogcoord rownumsel numcommits
- global have_tk85 use_ttk NS
+ global have_tk85 have_tk86 use_ttk NS
global git_version
global worddiff
@@ -2601,8 +2736,13 @@ proc makewindow {} {
bind . <Key-Down> "selnextline 1"
bind . <Shift-Key-Up> "dofind -1 0"
bind . <Shift-Key-Down> "dofind 1 0"
- bindkey <Key-Right> "goforw"
- bindkey <Key-Left> "goback"
+ if {$have_tk86} {
+ bindkey <<NextChar>> "goforw"
+ bindkey <<PrevChar>> "goback"
+ } else {
+ bindkey <Key-Right> "goforw"
+ bindkey <Key-Left> "goback"
+ }
bind . <Key-Prior> "selnextpage -1"
bind . <Key-Next> "selnextpage 1"
bind . <$M1B-Home> "allcanvs yview moveto 0.0"
@@ -7720,7 +7860,7 @@ proc gettreeline {gtf id} {
if {[string index $fname 0] eq "\""} {
set fname [lindex $fname 0]
}
- set fname [encoding convertfrom $fname]
+ set fname [encoding convertfrom utf-8 $fname]
lappend treefilelist($id) $fname
}
if {![eof $gtf]} {
@@ -7982,7 +8122,7 @@ proc gettreediffline {gdtf ids} {
if {[string index $file 0] eq "\""} {
set file [lindex $file 0]
}
- set file [encoding convertfrom $file]
+ set file [encoding convertfrom utf-8 $file]
if {$file ne [lindex $treediff end]} {
lappend treediff $file
lappend sublist $file
@@ -8127,7 +8267,7 @@ proc makediffhdr {fname ids} {
global ctext curdiffstart treediffs diffencoding
global ctext_file_names jump_to_here targetline diffline
- set fname [encoding convertfrom $fname]
+ set fname [encoding convertfrom utf-8 $fname]
set diffencoding [get_path_encoding $fname]
set i [lsearch -exact $treediffs($ids) $fname]
if {$i >= 0} {
@@ -8189,7 +8329,7 @@ proc parseblobdiffline {ids line} {
if {![string compare -length 5 "diff " $line]} {
if {![regexp {^diff (--cc|--git) } $line m type]} {
- set line [encoding convertfrom $line]
+ set line [encoding convertfrom utf-8 $line]
$ctext insert end "$line\n" hunksep
continue
}
@@ -8238,7 +8378,7 @@ proc parseblobdiffline {ids line} {
makediffhdr $fname $ids
} elseif {![string compare -length 16 "* Unmerged path " $line]} {
- set fname [encoding convertfrom [string range $line 16 end]]
+ set fname [encoding convertfrom utf-8 [string range $line 16 end]]
$ctext insert end "\n"
set curdiffstart [$ctext index "end - 1c"]
lappend ctext_file_names $fname
@@ -8291,7 +8431,7 @@ proc parseblobdiffline {ids line} {
if {[string index $fname 0] eq "\""} {
set fname [lindex $fname 0]
}
- set fname [encoding convertfrom $fname]
+ set fname [encoding convertfrom utf-8 $fname]
set i [lsearch -exact $treediffs($ids) $fname]
if {$i >= 0} {
setinlist difffilestart $i $curdiffstart
@@ -8310,6 +8450,7 @@ proc parseblobdiffline {ids line} {
set diffinhdr 0
return
}
+ set line [encoding convertfrom utf-8 $line]
$ctext insert end "$line\n" filesep
} else {
@@ -10068,7 +10209,7 @@ proc showrefs {} {
text $top.list -background $bgcolor -foreground $fgcolor \
-selectbackground $selectbgcolor -font mainfont \
-xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \
- -width 30 -height 20 -cursor $maincursor \
+ -width 60 -height 20 -cursor $maincursor \
-spacing1 1 -spacing3 1 -state disabled
$top.list tag configure highlight -background $selectbgcolor
if {![lsearch -exact $bglist $top.list]} {
@@ -12305,7 +12446,7 @@ proc cache_gitattr {attr pathlist} {
foreach row [split $rlist "\n"] {
if {[regexp "(.*): $attr: (.*)" $row m path value]} {
if {[string index $path 0] eq "\""} {
- set path [encoding convertfrom [lindex $path 0]]
+ set path [encoding convertfrom utf-8 [lindex $path 0]]
}
set path_attr_cache($attr,$path) $value
}
@@ -12335,7 +12476,6 @@ if { [info exists ::env(GITK_MSGSDIR)] } {
set gitk_prefix [file dirname [file dirname [file normalize $argv0]]]
set gitk_libdir [file join $gitk_prefix share gitk lib]
set gitk_msgsdir [file join $gitk_libdir msgs]
- unset gitk_prefix
}
## Internationalization (i18n) through msgcat and gettext. See
@@ -12637,6 +12777,7 @@ set nullid2 "0000000000000000000000000000000000000001"
set nullfile "/dev/null"
set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
+set have_tk86 [expr {[package vcompare $tk_version "8.6"] >= 0}]
if {![info exists have_ttk]} {
set have_ttk [llength [info commands ::ttk::style]]
}
@@ -12701,28 +12842,32 @@ if {[expr {[exec git rev-parse --is-inside-work-tree] == "true"}]} {
set worktree [gitworktree]
setcoords
makewindow
-catch {
- image create photo gitlogo -width 16 -height 16
-
- image create photo gitlogominus -width 4 -height 2
- gitlogominus put #C00000 -to 0 0 4 2
- gitlogo copy gitlogominus -to 1 5
- gitlogo copy gitlogominus -to 6 5
- gitlogo copy gitlogominus -to 11 5
- image delete gitlogominus
-
- image create photo gitlogoplus -width 4 -height 4
- gitlogoplus put #008000 -to 1 0 3 4
- gitlogoplus put #008000 -to 0 1 4 3
- gitlogo copy gitlogoplus -to 1 9
- gitlogo copy gitlogoplus -to 6 9
- gitlogo copy gitlogoplus -to 11 9
- image delete gitlogoplus
-
- image create photo gitlogo32 -width 32 -height 32
- gitlogo32 copy gitlogo -zoom 2 2
-
- wm iconphoto . -default gitlogo gitlogo32
+if {$::tcl_platform(platform) eq {windows} && [file exists $gitk_prefix/etc/git.ico]} {
+ wm iconbitmap . -default $gitk_prefix/etc/git.ico
+} else {
+ catch {
+ image create photo gitlogo -width 16 -height 16
+
+ image create photo gitlogominus -width 4 -height 2
+ gitlogominus put #C00000 -to 0 0 4 2
+ gitlogo copy gitlogominus -to 1 5
+ gitlogo copy gitlogominus -to 6 5
+ gitlogo copy gitlogominus -to 11 5
+ image delete gitlogominus
+
+ image create photo gitlogoplus -width 4 -height 4
+ gitlogoplus put #008000 -to 1 0 3 4
+ gitlogoplus put #008000 -to 0 1 4 3
+ gitlogo copy gitlogoplus -to 1 9
+ gitlogo copy gitlogoplus -to 6 9
+ gitlogo copy gitlogoplus -to 11 9
+ image delete gitlogoplus
+
+ image create photo gitlogo32 -width 32 -height 32
+ gitlogo32 copy gitlogo -zoom 2 2
+
+ wm iconphoto . -default gitlogo gitlogo32
+ }
}
# wait for the window to become visible
if {![winfo viewable .]} {tkwait visibility .}
diff --git a/gitk-git/meson.build b/gitk-git/meson.build
new file mode 100644
index 0000000000..ca3c0cec58
--- /dev/null
+++ b/gitk-git/meson.build
@@ -0,0 +1,30 @@
+project('gitk')
+
+shell = find_program('sh')
+wish = find_program('wish')
+
+# Verify that dependencies of "generate-tcl.sh" are satisfied.
+foreach dependency : [ 'chmod', 'mv', 'sed' ]
+ find_program(dependency)
+endforeach
+
+custom_target(
+ command: [
+ shell,
+ meson.current_source_dir() / 'generate-tcl.sh',
+ wish.full_path(),
+ '@INPUT@',
+ '@OUTPUT@',
+ ],
+ input: 'gitk',
+ output: 'gitk',
+ depend_files: [
+ 'generate-tcl.sh',
+ ],
+ install: true,
+ install_dir: get_option('bindir'),
+)
+
+if find_program('msgfmt').found()
+ subdir('po')
+endif
diff --git a/gitk-git/po/meson.build b/gitk-git/po/meson.build
new file mode 100644
index 0000000000..b1ed019828
--- /dev/null
+++ b/gitk-git/po/meson.build
@@ -0,0 +1,19 @@
+import('i18n').gettext('gitk',
+ languages: [
+ 'bg',
+ 'ca',
+ 'de',
+ 'es',
+ 'fr',
+ 'hu',
+ 'it',
+ 'ja',
+ 'pt_br',
+ 'pt_pt',
+ 'ru',
+ 'sv',
+ 'vi',
+ 'zh_cn',
+ ],
+ install: true,
+)
diff --git a/http.c b/http.c
index f4504133e8..0c9a872809 100644
--- a/http.c
+++ b/http.c
@@ -598,8 +598,7 @@ static void init_curl_http_auth(CURL *result)
{
if ((!http_auth.username || !*http_auth.username) &&
(!http_auth.credential || !*http_auth.credential)) {
- int empty_auth = curl_empty_auth_enabled();
- if ((empty_auth != -1 && !always_auth_proactively()) || empty_auth == 1) {
+ if (!always_auth_proactively() && curl_empty_auth_enabled()) {
curl_easy_setopt(result, CURLOPT_USERPWD, ":");
return;
} else if (!always_auth_proactively()) {
diff --git a/merge-recursive.c b/merge-recursive.c
index 5dfaf32b2c..884ccf99a5 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -2758,23 +2758,22 @@ static int process_renames(struct merge_options *opt,
const struct rename *sre;
/*
- * FIXME: As string-list.h notes, it's O(n^2) to build a sorted
- * string_list one-by-one, but O(n log n) to build it unsorted and
- * then sort it. Note that as we build the list, we do not need to
- * check if the existing destination path is already in the list,
- * because the structure of diffcore_rename guarantees we won't
- * have duplicates.
+ * Note that as we build the list, we do not need to check if the
+ * existing destination path is already in the list, because the
+ * structure of diffcore_rename guarantees we won't have duplicates.
*/
for (i = 0; i < a_renames->nr; i++) {
sre = a_renames->items[i].util;
- string_list_insert(&a_by_dst, sre->pair->two->path)->util
+ string_list_append(&a_by_dst, sre->pair->two->path)->util
= (void *)sre;
}
for (i = 0; i < b_renames->nr; i++) {
sre = b_renames->items[i].util;
- string_list_insert(&b_by_dst, sre->pair->two->path)->util
+ string_list_append(&b_by_dst, sre->pair->two->path)->util
= (void *)sre;
}
+ string_list_sort(&a_by_dst);
+ string_list_sort(&b_by_dst);
for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) {
struct string_list *renames1, *renames2Dst;
diff --git a/meson.build b/meson.build
index 20159cef83..e86085b0a4 100644
--- a/meson.build
+++ b/meson.build
@@ -191,30 +191,29 @@ project('git', 'c',
fs = import('fs')
program_path = []
-# Git for Windows provides all the tools we need to build Git.
-if host_machine.system() == 'windows'
- program_path += [ 'C:/Program Files/Git/bin', 'C:/Program Files/Git/usr/bin' ]
+if get_option('sane_tool_path').length() != 0
+ program_path = get_option('sane_tool_path')
+elif host_machine.system() == 'windows'
+ # Git for Windows provides all the tools we need to build Git.
+ program_path = [ 'C:/Program Files/Git/bin', 'C:/Program Files/Git/usr/bin' ]
endif
cygpath = find_program('cygpath', dirs: program_path, required: false)
diff = find_program('diff', dirs: program_path)
+git = find_program('git', dirs: program_path, required: false)
+sed = find_program('sed', dirs: program_path)
shell = find_program('sh', dirs: program_path)
tar = find_program('tar', dirs: program_path)
-script_environment = environment()
-foreach tool : ['cat', 'cut', 'grep', 'sed', 'sort', 'tr', 'uname']
- program = find_program(tool, dirs: program_path)
- script_environment.prepend('PATH', fs.parent(program.full_path()))
+# Sanity-check that programs required for the build exist.
+foreach tool : ['cat', 'cut', 'grep', 'sort', 'tr', 'uname']
+ find_program(tool, dirs: program_path)
endforeach
-git = find_program('git', dirs: program_path, required: false)
-if git.found()
- script_environment.prepend('PATH', fs.parent(git.full_path()))
-endif
-
-if get_option('sane_tool_path') != ''
- script_environment.prepend('PATH', get_option('sane_tool_path'))
-endif
+script_environment = environment()
+foreach path : program_path
+ script_environment.prepend('PATH', path)
+endforeach
# The environment used by GIT-VERSION-GEN. Note that we explicitly override
# environment variables that might be set by the user. This is by design so
@@ -479,6 +478,7 @@ libgit_sources = [
'userdiff.c',
'utf8.c',
'varint.c',
+ 'version.c',
'versioncmp.c',
'walker.c',
'wildmatch.c',
@@ -510,6 +510,7 @@ builtin_sources = [
'builtin/annotate.c',
'builtin/apply.c',
'builtin/archive.c',
+ 'builtin/backfill.c',
'builtin/bisect.c',
'builtin/blame.c',
'builtin/branch.c',
@@ -677,8 +678,9 @@ else
build_options_config.set('WITH_BREAKING_CHANGES', '')
endif
-if get_option('sane_tool_path') != ''
- build_options_config.set_quoted('BROKEN_PATH_FIX', 's|^\# @BROKEN_PATH_FIX@$|git_broken_path_fix "' + get_option('sane_tool_path') + '"|')
+if get_option('sane_tool_path').length() != 0
+ sane_tool_path = (host_machine.system() == 'windows' ? ';' : ':').join(get_option('sane_tool_path'))
+ build_options_config.set_quoted('BROKEN_PATH_FIX', 's|^\# @BROKEN_PATH_FIX@$|git_broken_path_fix "' + sane_tool_path + '"|')
else
build_options_config.set_quoted('BROKEN_PATH_FIX', '/^\# @BROKEN_PATH_FIX@$/d')
endif
@@ -698,7 +700,6 @@ libgit_c_args = [
'-DETC_GITATTRIBUTES="' + get_option('gitattributes') + '"',
'-DETC_GITCONFIG="' + get_option('gitconfig') + '"',
'-DFALLBACK_RUNTIME_PREFIX="' + get_option('prefix') + '"',
- '-DGIT_EXEC_PATH="' + get_option('prefix') / get_option('libexecdir') / 'git-core"',
'-DGIT_HOST_CPU="' + host_machine.cpu_family() + '"',
'-DGIT_HTML_PATH="' + get_option('datadir') / 'doc/git-doc"',
'-DGIT_INFO_PATH="' + get_option('infodir') + '"',
@@ -777,7 +778,22 @@ endif
# Note that we only set NO_PERL if the Perl features were disabled by the user.
# It may not be set when we have found Perl, but only use it to run tests.
-perl = find_program('perl', version: '>=5.8.1', dirs: program_path, required: perl_required)
+#
+# At the time of writing, executing `perl --version` results in a string
+# similar to the following output:
+#
+# This is perl 5, version 40, subversion 0 (v5.40.0) built for x86_64-linux-thread-multi
+#
+# Meson picks up the "40" as version number instead of using "v5.40.0"
+# due to the regular expression it uses. This got fixed in Meson 1.7.0,
+# but meanwhile we have to either use `-V:version` instead of `--version`,
+# which we can do starting with Meson 1.5.0 and newer, or we have to
+# match against the minor version.
+if meson.version().version_compare('>=1.5.0')
+ perl = find_program('perl', dirs: program_path, required: perl_required, version: '>=5.26.0', version_argument: '-V:version')
+else
+ perl = find_program('perl', dirs: program_path, required: perl_required, version: '>=26')
+endif
perl_features_enabled = perl.found() and get_option('perl').allowed()
if perl_features_enabled
build_options_config.set('NO_PERL', '')
@@ -947,7 +963,9 @@ if curl.found()
use_curl_for_imap_send = true
endif
- libgit_dependencies += curl
+ # Most executables don't have to link against libcurl, but we still need its
+ # include directories so that we can resolve LIBCURL_VERSION in "help.c".
+ libgit_dependencies += curl.partial_dependency(includes: true)
libgit_c_args += '-DCURL_DISABLE_TYPECHECK'
build_options_config.set('NO_CURL', '')
else
@@ -1372,7 +1390,11 @@ if https_backend == 'auto' and security_framework.found()
endif
openssl_required = 'openssl' in [csprng_backend, https_backend, sha1_backend, sha1_unsafe_backend, sha256_backend]
-openssl = dependency('openssl', required: openssl_required, default_options: ['default_library=static'])
+openssl = dependency('openssl',
+ required: openssl_required,
+ allow_fallback: openssl_required or https_backend == 'auto',
+ default_options: ['default_library=static'],
+)
if https_backend == 'auto' and openssl.found()
https_backend = 'openssl'
endif
@@ -1386,6 +1408,7 @@ elif https_backend == 'openssl'
else
# We either couldn't find any dependencies with 'auto' or the user requested
# 'none'. Both cases are benign.
+ https_backend = 'none'
endif
if https_backend != 'openssl'
@@ -1485,6 +1508,7 @@ endif
if get_option('runtime_prefix')
libgit_c_args += '-DRUNTIME_PREFIX'
build_options_config.set('RUNTIME_PREFIX', 'true')
+ git_exec_path = get_option('libexecdir') / 'git-core'
if compiler.has_header('mach-o/dyld.h')
libgit_c_args += '-DHAVE_NS_GET_EXECUTABLE_PATH'
@@ -1521,7 +1545,9 @@ if get_option('runtime_prefix')
endif
else
build_options_config.set('RUNTIME_PREFIX', 'false')
+ git_exec_path = get_option('prefix') / get_option('libexecdir') / 'git-core'
endif
+libgit_c_args += '-DGIT_EXEC_PATH="' + git_exec_path + '"'
git_version_file = custom_target(
command: [
@@ -1552,32 +1578,18 @@ version_def_h = custom_target(
depends: [git_version_file],
env: version_gen_environment,
)
-
-# Build a separate library for "version.c" so that we do not have to rebuild
-# everything when the current Git commit changes.
-libgit_version_library = static_library('git-version',
- sources: [
- 'version.c',
- version_def_h,
- ],
- c_args: libgit_c_args + [
- '-DGIT_VERSION_H="' + version_def_h.full_path() + '"',
- ],
- dependencies: libgit_dependencies,
- include_directories: libgit_include_directories,
-)
-
-libgit_library = static_library('git',
- sources: libgit_sources,
- c_args: libgit_c_args,
- link_with: libgit_version_library,
- dependencies: libgit_dependencies,
- include_directories: libgit_include_directories,
-)
+libgit_sources += version_def_h
libgit = declare_dependency(
+ link_with: static_library('git',
+ sources: libgit_sources,
+ c_args: libgit_c_args + [
+ '-DGIT_VERSION_H="' + version_def_h.full_path() + '"',
+ ],
+ dependencies: libgit_dependencies,
+ include_directories: libgit_include_directories,
+ ),
compile_args: libgit_c_args,
- link_with: libgit_library,
dependencies: libgit_dependencies,
include_directories: libgit_include_directories,
)
@@ -1618,88 +1630,89 @@ if host_machine.system() == 'windows'
error('Unsupported compiler ' + compiler.get_id())
endif
endif
-common_main_library = static_library('common-main',
- sources: common_main_sources,
- c_args: libgit_c_args,
- dependencies: libgit_dependencies,
- include_directories: libgit_include_directories,
-)
-common_main = declare_dependency(
- link_with: common_main_library,
+
+libgit_commonmain = declare_dependency(
+ link_with: static_library('common-main',
+ sources: common_main_sources,
+ dependencies: [ libgit ],
+ ),
link_args: common_main_link_args,
+ dependencies: [ libgit ],
)
bin_wrappers = [ ]
test_dependencies = [ ]
-git = executable('git',
+git_builtin = executable('git',
sources: builtin_sources + 'git.c',
- dependencies: [libgit, common_main],
+ dependencies: [libgit_commonmain],
install: true,
install_dir: get_option('libexecdir') / 'git-core',
)
-bin_wrappers += git
+bin_wrappers += git_builtin
test_dependencies += executable('git-daemon',
sources: 'daemon.c',
- dependencies: [libgit, common_main],
+ dependencies: [libgit_commonmain],
install: true,
install_dir: get_option('libexecdir') / 'git-core',
)
test_dependencies += executable('git-sh-i18n--envsubst',
sources: 'sh-i18n--envsubst.c',
- dependencies: [libgit, common_main],
+ dependencies: [libgit_commonmain],
install: true,
install_dir: get_option('libexecdir') / 'git-core',
)
bin_wrappers += executable('git-shell',
sources: 'shell.c',
- dependencies: [libgit, common_main],
+ dependencies: [libgit_commonmain],
install: true,
install_dir: get_option('libexecdir') / 'git-core',
)
test_dependencies += executable('git-http-backend',
sources: 'http-backend.c',
- dependencies: [libgit, common_main],
+ dependencies: [libgit_commonmain],
install: true,
install_dir: get_option('libexecdir') / 'git-core',
)
bin_wrappers += executable('scalar',
sources: 'scalar.c',
- dependencies: [libgit, common_main],
+ dependencies: [libgit_commonmain],
install: true,
install_dir: get_option('libexecdir') / 'git-core',
)
if get_option('curl').enabled()
- curl_sources = [
- 'http.c',
- 'http-walker.c',
- ]
+ libgit_curl = declare_dependency(
+ sources: [
+ 'http.c',
+ 'http-walker.c',
+ ],
+ dependencies: [libgit_commonmain, curl],
+ )
- git_remote_http = executable('git-remote-http',
- sources: curl_sources + 'remote-curl.c',
- dependencies: [libgit, common_main],
+ test_dependencies += executable('git-remote-http',
+ sources: 'remote-curl.c',
+ dependencies: [libgit_curl],
install: true,
install_dir: get_option('libexecdir') / 'git-core',
)
- test_dependencies += git_remote_http
test_dependencies += executable('git-http-fetch',
- sources: curl_sources + 'http-fetch.c',
- dependencies: [libgit, common_main],
+ sources: 'http-fetch.c',
+ dependencies: [libgit_curl],
install: true,
install_dir: get_option('libexecdir') / 'git-core',
)
if expat.found()
test_dependencies += executable('git-http-push',
- sources: curl_sources + 'http-push.c',
- dependencies: [libgit, common_main],
+ sources: 'http-push.c',
+ dependencies: [libgit_curl],
install: true,
install_dir: get_option('libexecdir') / 'git-core',
)
@@ -1707,8 +1720,8 @@ if get_option('curl').enabled()
foreach alias : [ 'git-remote-https', 'git-remote-ftp', 'git-remote-ftps' ]
test_dependencies += executable(alias,
- objects: git_remote_http.extract_all_objects(recursive: false),
- dependencies: [libgit, common_main],
+ sources: 'remote-curl.c',
+ dependencies: [libgit_curl],
)
install_symlink(alias + executable_suffix,
@@ -1718,22 +1731,17 @@ if get_option('curl').enabled()
endforeach
endif
-imap_send_sources = ['imap-send.c']
-if use_curl_for_imap_send
- imap_send_sources += curl_sources
-endif
-
test_dependencies += executable('git-imap-send',
- sources: imap_send_sources,
- dependencies: [libgit, common_main],
+ sources: 'imap-send.c',
+ dependencies: [ use_curl_for_imap_send ? libgit_curl : libgit_commonmain ],
install: true,
install_dir: get_option('libexecdir') / 'git-core',
)
foreach alias : [ 'git-receive-pack', 'git-upload-archive', 'git-upload-pack' ]
bin_wrappers += executable(alias,
- objects: git.extract_all_objects(recursive: false),
- dependencies: [libgit, common_main],
+ objects: git_builtin.extract_all_objects(recursive: false),
+ dependencies: [libgit_commonmain],
)
install_symlink(alias + executable_suffix,
diff --git a/meson_options.txt b/meson_options.txt
index afa908d6c5..78d172a740 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -13,8 +13,8 @@ option('perl_cpan_fallback', type: 'boolean', value: true,
description: 'Install bundled copies of CPAN modules that serve as a fallback in case the modules are not available on the system.')
option('runtime_prefix', type: 'boolean', value: false,
description: 'Resolve ancillary tooling and support files relative to the location of the runtime binary instead of hard-coding them into the binary.')
-option('sane_tool_path', type: 'string', value: '',
- description: 'A colon-separated list of paths to prepend to PATH if your tools in /usr/bin are broken.')
+option('sane_tool_path', type: 'array', value: [],
+ description: 'An array of paths to pick up tools from in case the normal tools are broken or lacking.')
# Build information compiled into Git and other parts like documentation.
option('build_date', type: 'string', value: '',
diff --git a/oss-fuzz/meson.build b/oss-fuzz/meson.build
index ed79665501..878afd8426 100644
--- a/oss-fuzz/meson.build
+++ b/oss-fuzz/meson.build
@@ -15,6 +15,6 @@ foreach fuzz_program : fuzz_programs
'dummy-cmd-main.c',
fuzz_program,
],
- dependencies: [libgit, common_main],
+ dependencies: [libgit_commonmain],
)
endforeach
diff --git a/path-walk.c b/path-walk.c
index 9715a5550e..341bdd2ba4 100644
--- a/path-walk.c
+++ b/path-walk.c
@@ -12,6 +12,7 @@
#include "object.h"
#include "oid-array.h"
#include "prio-queue.h"
+#include "repository.h"
#include "revision.h"
#include "string-list.h"
#include "strmap.h"
@@ -172,6 +173,23 @@ static int add_tree_entries(struct path_walk_context *ctx,
if (type == OBJ_TREE)
strbuf_addch(&path, '/');
+ if (ctx->info->pl) {
+ int dtype;
+ enum pattern_match_result match;
+ match = path_matches_pattern_list(path.buf, path.len,
+ path.buf + base_len, &dtype,
+ ctx->info->pl,
+ ctx->repo->index);
+
+ if (ctx->info->pl->use_cone_patterns &&
+ match == NOT_MATCHED)
+ continue;
+ else if (!ctx->info->pl->use_cone_patterns &&
+ type == OBJ_BLOB &&
+ match != MATCHED)
+ continue;
+ }
+
if (!(list = strmap_get(&ctx->paths_to_lists, path.buf))) {
CALLOC_ARRAY(list, 1);
list->type = type;
@@ -582,10 +600,10 @@ void path_walk_info_init(struct path_walk_info *info)
memcpy(info, &empty, sizeof(empty));
}
-void path_walk_info_clear(struct path_walk_info *info UNUSED)
+void path_walk_info_clear(struct path_walk_info *info)
{
- /*
- * This destructor is empty for now, as info->revs
- * is not owned by 'struct path_walk_info'.
- */
+ if (info->pl) {
+ clear_pattern_list(info->pl);
+ free(info->pl);
+ }
}
diff --git a/path-walk.h b/path-walk.h
index 414d6db23c..473ee9d361 100644
--- a/path-walk.h
+++ b/path-walk.h
@@ -6,6 +6,7 @@
struct rev_info;
struct oid_array;
+struct pattern_list;
/**
* The type of a function pointer for the method that is called on a list of
@@ -48,6 +49,16 @@ struct path_walk_info {
* walk the children of such trees.
*/
int prune_all_uninteresting;
+
+ /**
+ * Specify a sparse-checkout definition to match our paths to. Do not
+ * walk outside of this sparse definition. If the patterns are in
+ * cone mode, then the search may prune directories that are outside
+ * of the cone. If not in cone mode, then all tree paths will be
+ * explored but the path_fn will only be called when the path matches
+ * the sparse-checkout patterns.
+ */
+ struct pattern_list *pl;
};
#define PATH_WALK_INFO_INIT { \
diff --git a/refs.c b/refs.c
index e1a6a2d189..91da5325d7 100644
--- a/refs.c
+++ b/refs.c
@@ -3043,9 +3043,11 @@ int repo_migrate_ref_storage_format(struct repository *repo,
if (ret < 0)
goto done;
- ret = refs_for_each_reflog(old_refs, migrate_one_reflog, &data);
- if (ret < 0)
- goto done;
+ if (!(flags & REPO_MIGRATE_REF_STORAGE_FORMAT_SKIP_REFLOG)) {
+ ret = refs_for_each_reflog(old_refs, migrate_one_reflog, &data);
+ if (ret < 0)
+ goto done;
+ }
ret = ref_transaction_commit(transaction, errbuf);
if (ret < 0)
diff --git a/refs.h b/refs.h
index 09be47afbe..2ed14fdb16 100644
--- a/refs.h
+++ b/refs.h
@@ -1143,8 +1143,11 @@ int is_pseudo_ref(const char *refname);
* - REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN: perform a dry-run migration
* without touching the main repository. The result will be written into a
* temporary ref storage directory.
+ *
+ * - REPO_MIGRATE_REF_STORAGE_FORMAT_SKIP_REFLOG: skip migration of reflogs.
*/
-#define REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN (1 << 0)
+#define REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN (1 << 0)
+#define REPO_MIGRATE_REF_STORAGE_FORMAT_SKIP_REFLOG (1 << 1)
/*
* Migrate the ref storage format used by the repository to the
diff --git a/refspec.c b/refspec.c
index 3d6cf4dc92..4cb80b5208 100644
--- a/refspec.c
+++ b/refspec.c
@@ -269,28 +269,28 @@ void refspec_ref_prefixes(const struct refspec *rs,
}
}
-int match_name_with_pattern(const char *key, const char *name,
- const char *value, char **result)
+int match_refname_with_pattern(const char *pattern, const char *refname,
+ const char *replacement, char **result)
{
- const char *kstar = strchr(key, '*');
+ const char *kstar = strchr(pattern, '*');
size_t klen;
size_t ksuffixlen;
size_t namelen;
int ret;
if (!kstar)
- die(_("key '%s' of pattern had no '*'"), key);
- klen = kstar - key;
+ die(_("pattern '%s' has no '*'"), pattern);
+ klen = kstar - pattern;
ksuffixlen = strlen(kstar + 1);
- namelen = strlen(name);
- ret = !strncmp(name, key, klen) && namelen >= klen + ksuffixlen &&
- !memcmp(name + namelen - ksuffixlen, kstar + 1, ksuffixlen);
- if (ret && value) {
+ namelen = strlen(refname);
+ ret = !strncmp(refname, pattern, klen) && namelen >= klen + ksuffixlen &&
+ !memcmp(refname + namelen - ksuffixlen, kstar + 1, ksuffixlen);
+ if (ret && replacement) {
struct strbuf sb = STRBUF_INIT;
- const char *vstar = strchr(value, '*');
+ const char *vstar = strchr(replacement, '*');
if (!vstar)
- die(_("value '%s' of pattern has no '*'"), value);
- strbuf_add(&sb, value, vstar - value);
- strbuf_add(&sb, name + klen, namelen - klen - ksuffixlen);
+ die(_("replacement '%s' has no '*'"), replacement);
+ strbuf_add(&sb, replacement, vstar - replacement);
+ strbuf_add(&sb, refname + klen, namelen - klen - ksuffixlen);
strbuf_addstr(&sb, vstar + 1);
*result = strbuf_detach(&sb, NULL);
}
@@ -301,7 +301,7 @@ static int refspec_match(const struct refspec_item *refspec,
const char *name)
{
if (refspec->pattern)
- return match_name_with_pattern(refspec->src, name, NULL, NULL);
+ return match_refname_with_pattern(refspec->src, name, NULL, NULL);
return !strcmp(refspec->src, name);
}
@@ -352,7 +352,7 @@ static int refspec_find_negative_match(struct refspec *rs, struct refspec_item *
const char *key = refspec->dst ? refspec->dst : refspec->src;
const char *value = refspec->src;
- if (match_name_with_pattern(key, needle, value, &expn_name))
+ if (match_refname_with_pattern(key, needle, value, &expn_name))
string_list_append_nodup(&reversed, expn_name);
} else if (refspec->matching) {
/* For the special matching refspec, any query should match */
@@ -397,7 +397,7 @@ void refspec_find_all_matches(struct refspec *rs,
if (!refspec->dst || refspec->negative)
continue;
if (refspec->pattern) {
- if (match_name_with_pattern(key, needle, value, result))
+ if (match_refname_with_pattern(key, needle, value, result))
string_list_append_nodup(results, *result);
} else if (!strcmp(needle, key)) {
string_list_append(results, value);
@@ -426,7 +426,7 @@ int refspec_find_match(struct refspec *rs, struct refspec_item *query)
if (!refspec->dst || refspec->negative)
continue;
if (refspec->pattern) {
- if (match_name_with_pattern(key, needle, value, result)) {
+ if (match_refname_with_pattern(key, needle, value, result)) {
query->force = refspec->force;
return 0;
}
diff --git a/refspec.h b/refspec.h
index f62f83a7ee..e2b5cc54ef 100644
--- a/refspec.h
+++ b/refspec.h
@@ -75,11 +75,12 @@ void refspec_ref_prefixes(const struct refspec *rs,
int refname_matches_negative_refspec_item(const char *refname, struct refspec *rs);
/*
- * Checks whether a name matches a pattern and optionally generates a result.
- * Returns 1 if the name matches the pattern, 0 otherwise.
+ * Checks if a refname matches a globbing refspec pattern.
+ * If replacement is provided, computes the corresponding mapped refname.
+ * Returns 1 if refname matches pattern, 0 otherwise.
*/
-int match_name_with_pattern(const char *key, const char *name,
- const char *value, char **result);
+int match_refname_with_pattern(const char *pattern, const char *refname,
+ const char *replacement, char **result);
/*
* Queries a refspec for a match and updates the query item.
diff --git a/remote.c b/remote.c
index c1cf363cca..c9c1384c1d 100644
--- a/remote.c
+++ b/remote.c
@@ -1322,9 +1322,9 @@ static char *get_ref_match(const struct refspec *rs, const struct ref *ref,
const char *dst_side = item->dst ? item->dst : item->src;
int match;
if (direction == FROM_SRC)
- match = match_name_with_pattern(item->src, ref->name, dst_side, &name);
+ match = match_refname_with_pattern(item->src, ref->name, dst_side, &name);
else
- match = match_name_with_pattern(dst_side, ref->name, item->src, &name);
+ match = match_refname_with_pattern(dst_side, ref->name, item->src, &name);
if (match) {
matching_refs = i;
break;
@@ -1942,7 +1942,7 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,
if (strchr(ref->name, '^'))
continue; /* a dereference item */
- if (match_name_with_pattern(refspec->src, ref->name,
+ if (match_refname_with_pattern(refspec->src, ref->name,
refspec->dst, &expn_name) &&
!ignore_symref_update(expn_name, &scratch)) {
struct ref *cpy = copy_ref(ref);
diff --git a/send-pack.c b/send-pack.c
index 772c7683a0..856a65d5f5 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -632,7 +632,8 @@ int send_pack(struct repository *r,
reject_atomic_push(remote_refs, args->send_mirror);
error("atomic push failed for ref %s. status: %d",
ref->name, ref->status);
- ret = args->porcelain ? 0 : -1;
+ ret = ERROR_SEND_PACK_BAD_REF_STATUS;
+ packet_flush(out);
goto out;
}
/* else fallthrough */
@@ -763,11 +764,6 @@ int send_pack(struct repository *r,
if (ret < 0)
goto out;
- if (args->porcelain) {
- ret = 0;
- goto out;
- }
-
for (ref = remote_refs; ref; ref = ref->next) {
switch (ref->status) {
case REF_STATUS_NONE:
@@ -775,7 +771,7 @@ int send_pack(struct repository *r,
case REF_STATUS_OK:
break;
default:
- ret = -1;
+ ret = ERROR_SEND_PACK_BAD_REF_STATUS;
goto out;
}
}
diff --git a/send-pack.h b/send-pack.h
index d256715681..c5ded2d200 100644
--- a/send-pack.h
+++ b/send-pack.h
@@ -13,6 +13,9 @@ struct repository;
#define SEND_PACK_PUSH_CERT_IF_ASKED 1
#define SEND_PACK_PUSH_CERT_ALWAYS 2
+/* At least one reference has been rejected by the remote side. */
+#define ERROR_SEND_PACK_BAD_REF_STATUS 1
+
struct send_pack_args {
const char *url;
unsigned verbose:1,
@@ -36,6 +39,16 @@ struct option;
int option_parse_push_signed(const struct option *opt,
const char *arg, int unset);
+/*
+ * Compute a packfile and write it to a file descriptor. The `fd` array needs
+ * to contain two file descriptors: `fd[0]` is the file descriptor used as
+ * input for the packet reader, whereas `fd[1]` is the file descriptor the
+ * packfile will be written to.
+ *
+ * Returns 0 on success, non-zero otherwise. Negative return values indicate a
+ * generic error, whereas positive return values indicate specific error
+ * conditions as documented with the `ERROR_SEND_PACK_*` constants.
+ */
int send_pack(struct repository *r, struct send_pack_args *args,
int fd[], struct child_process *conn,
struct ref *remote_refs, struct oid_array *extra_have);
diff --git a/sequencer.c b/sequencer.c
index 407ee4e90f..ad0ab75c8d 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2510,9 +2510,15 @@ static int do_pick_commit(struct repository *r,
*check_todo = !!(flags & EDIT_MSG);
if (!res && reword) {
fast_forward_edit:
- res = run_git_commit(NULL, opts, EDIT_MSG |
- VERIFY_MSG | AMEND_MSG |
- (flags & ALLOW_EMPTY));
+ /*
+ * To reword we amend the commit we just
+ * picked or fast-forwarded. As the commit has
+ * already been picked we want to use the same
+ * set of commit flags regardless of how we
+ * got here.
+ */
+ flags = EDIT_MSG | VERIFY_MSG | AMEND_MSG | ALLOW_EMPTY;
+ res = run_git_commit(NULL, opts, flags);
*check_todo = 1;
}
}
diff --git a/t/aggregate-results.sh b/t/aggregate-results.sh
index 6e3bcc4aec..6cb0ff11de 100755
--- a/t/aggregate-results.sh
+++ b/t/aggregate-results.sh
@@ -44,7 +44,7 @@ then
tr -s "," "\n" |
grep -v '^$' |
sort -u |
- paste -s -d ' ')
+ paste -s -d ' ' -)
if test -n "$unique_missing_prereq"
then
printf "\nmissing prereq: $unique_missing_prereq\n\n"
diff --git a/t/helper/meson.build b/t/helper/meson.build
index 1d6154ce97..d2cabaa2bc 100644
--- a/t/helper/meson.build
+++ b/t/helper/meson.build
@@ -80,14 +80,14 @@ test_tool_sources = [
test_tool = executable('test-tool',
sources: test_tool_sources,
- dependencies: [libgit, common_main],
+ dependencies: [libgit_commonmain],
)
bin_wrappers += test_tool
test_dependencies += test_tool
test_fake_ssh = executable('test-fake-ssh',
sources: 'test-fake-ssh.c',
- dependencies: [libgit, common_main],
+ dependencies: [libgit_commonmain],
)
bin_wrappers += test_fake_ssh
test_dependencies += test_fake_ssh
diff --git a/t/helper/test-path-walk.c b/t/helper/test-path-walk.c
index 7f2d409c5b..61e845e5ec 100644
--- a/t/helper/test-path-walk.c
+++ b/t/helper/test-path-walk.c
@@ -1,6 +1,7 @@
#define USE_THE_REPOSITORY_VARIABLE
#include "test-tool.h"
+#include "dir.h"
#include "environment.h"
#include "hex.h"
#include "object-name.h"
@@ -9,6 +10,7 @@
#include "revision.h"
#include "setup.h"
#include "parse-options.h"
+#include "strbuf.h"
#include "path-walk.h"
#include "oid-array.h"
@@ -65,7 +67,7 @@ static int emit_block(const char *path, struct oid_array *oids,
int cmd__path_walk(int argc, const char **argv)
{
- int res;
+ int res, stdin_pl = 0;
struct rev_info revs = REV_INFO_INIT;
struct path_walk_info info = PATH_WALK_INFO_INIT;
struct path_walk_test_data data = { 0 };
@@ -80,6 +82,8 @@ int cmd__path_walk(int argc, const char **argv)
N_("toggle inclusion of tree objects")),
OPT_BOOL(0, "prune", &info.prune_all_uninteresting,
N_("toggle pruning of uninteresting paths")),
+ OPT_BOOL(0, "stdin-pl", &stdin_pl,
+ N_("read a pattern list over stdin")),
OPT_END(),
};
@@ -99,6 +103,17 @@ int cmd__path_walk(int argc, const char **argv)
info.path_fn = emit_block;
info.path_fn_data = &data;
+ if (stdin_pl) {
+ struct strbuf in = STRBUF_INIT;
+ CALLOC_ARRAY(info.pl, 1);
+
+ info.pl->use_cone_patterns = 1;
+
+ strbuf_fread(&in, 2048, stdin);
+ add_patterns_from_buffer(in.buf, in.len, "", 0, info.pl);
+ strbuf_release(&in);
+ }
+
res = walk_objects_by_path(&info);
printf("commits:%" PRIuMAX "\n"
@@ -107,6 +122,11 @@ int cmd__path_walk(int argc, const char **argv)
"tags:%" PRIuMAX "\n",
data.commit_nr, data.tree_nr, data.blob_nr, data.tag_nr);
+ if (info.pl) {
+ clear_pattern_list(info.pl);
+ free(info.pl);
+ }
+
release_revisions(&revs);
return res;
}
diff --git a/t/interop/Makefile b/t/interop/Makefile
index 6911c2915a..4ff4ed0616 100644
--- a/t/interop/Makefile
+++ b/t/interop/Makefile
@@ -1,3 +1,6 @@
+# The default target of this Makefile is...
+all::
+
# Import tree-wide shared Makefile behavior and libraries
include ../../shared.mak
@@ -8,7 +11,7 @@ SHELL_PATH ?= $(SHELL)
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
T = $(sort $(wildcard i[0-9][0-9][0-9][0-9]-*.sh))
-all: $(T)
+all:: $(T)
$(T):
@echo "*** $@ ***"; '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
diff --git a/t/meson.build b/t/meson.build
index a03ebc81fd..25ce072707 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -43,7 +43,7 @@ clar_sources += custom_target(
clar_unit_tests = executable('unit-tests',
sources: clar_sources + clar_test_suites,
- dependencies: [libgit, common_main],
+ dependencies: [libgit_commonmain],
)
test('unit-tests', clar_unit_tests)
@@ -72,7 +72,7 @@ foreach unit_test_program : unit_test_programs
'unit-tests/lib-reftable.c',
unit_test_program,
],
- dependencies: [libgit, common_main],
+ dependencies: [libgit_commonmain],
)
test(unit_test_name, unit_test,
workdir: meson.current_source_dir(),
@@ -721,6 +721,7 @@ integration_tests = [
't5617-clone-submodules-remote.sh',
't5618-alternate-refs.sh',
't5619-clone-local-ambiguous-transport.sh',
+ 't5620-backfill.sh',
't5621-clone-revision.sh',
't5700-protocol-v1.sh',
't5701-git-serve.sh',
diff --git a/t/perf/Makefile b/t/perf/Makefile
index e4808aebed..9b3090c4ed 100644
--- a/t/perf/Makefile
+++ b/t/perf/Makefile
@@ -1,10 +1,13 @@
+# The default target of this Makefile is...
+all::
+
# Import tree-wide shared Makefile behavior and libraries
include ../../shared.mak
-include ../../config.mak
export GIT_TEST_OPTIONS
-all: test-lint perf
+all:: test-lint perf
perf: pre-clean
./run
diff --git a/t/t1460-refs-migrate.sh b/t/t1460-refs-migrate.sh
index a6d9b35a46..2ab97e1b7d 100755
--- a/t/t1460-refs-migrate.sh
+++ b/t/t1460-refs-migrate.sh
@@ -9,14 +9,21 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
# Migrate the provided repository from one format to the other and
# verify that the references and logs are migrated over correctly.
-# Usage: test_migration <repo> <format> <skip_reflog_verify>
+# Usage: test_migration <repo> <format> [<skip_reflog_verify> [<options...>]]
# <repo> is the relative path to the repo to be migrated.
# <format> is the ref format to be migrated to.
-# <skip_reflog_verify> (true or false) whether to skip reflog verification.
+# <skip_reflog_verify> (default: false) whether to skip reflog verification.
+# <options...> are other options be passed directly to 'git refs migrate'.
test_migration () {
repo=$1 &&
format=$2 &&
- skip_reflog_verify=${3:-false} &&
+ shift 2 &&
+ skip_reflog_verify=false &&
+ if test $# -ge 1
+ then
+ skip_reflog_verify=$1
+ shift
+ fi &&
git -C "$repo" for-each-ref --include-root-refs \
--format='%(refname) %(objectname) %(symref)' >expect &&
if ! $skip_reflog_verify
@@ -25,7 +32,7 @@ test_migration () {
git -C "$repo" reflog list >expect_log_list
fi &&
- git -C "$repo" refs migrate --ref-format="$2" &&
+ git -C "$repo" refs migrate --ref-format="$format" "$@" &&
git -C "$repo" for-each-ref --include-root-refs \
--format='%(refname) %(objectname) %(symref)' >actual &&
@@ -241,6 +248,19 @@ do
test_cmp expect.reflog actual.reflog
)
'
+
+ test_expect_success "$from_format -> $to_format: skip reflog with --skip-reflog" '
+ test_when_finished "rm -rf repo" &&
+ git init --ref-format=$from_format repo &&
+ test_commit -C repo initial &&
+ # we see that the repository contains reflogs.
+ git -C repo reflog --all >reflogs &&
+ test_line_count = 2 reflogs &&
+ test_migration repo "$to_format" true --no-reflog &&
+ # there should be no reflogs post migration.
+ git -C repo reflog --all >reflogs &&
+ test_must_be_empty reflogs
+ '
done
done
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index ecfc02062c..2aee9789a2 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -791,6 +791,20 @@ test_expect_success 'reword' '
grep "C changed" actual
'
+test_expect_success 'reword fast-forwarded empty commit' '
+ git commit --allow-empty -m "empty commit" --only &&
+ (
+ set_fake_editor &&
+ FAKE_COMMIT_AMEND=edited FAKE_LINES="reword 1" \
+ git rebase -i HEAD^
+ ) &&
+ test_commit_message HEAD <<-\EOF
+ empty commit
+
+ edited
+ EOF
+'
+
test_expect_success 'no uncommitted changes when rewording and the todo list is reloaded' '
git checkout E &&
test_when_finished "git checkout @{-1}" &&
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index 2593711fec..b84d68c4b9 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -610,4 +610,24 @@ test_expect_success 'truncate label names' '
grep "label 0123456789-$" out
'
+test_expect_success 'reword fast-forwarded empty merge commit' '
+ oid="$(git commit-tree -m "D1" -p A D^{tree})" &&
+ oid="$(git commit-tree -m "empty merge" -p D -p $oid D^{tree})" &&
+
+ write_script sequence-editor.sh <<-\EOF &&
+ sed /^merge/s/-C/-c/ "$1" >"$1.tmp"
+ mv "$1.tmp" "$1"
+ EOF
+
+ (
+ test_set_sequence_editor "$(pwd)/sequence-editor.sh" &&
+ GIT_EDITOR="echo edited >>" git rebase -i -r D $oid
+ ) &&
+ test_commit_message HEAD <<-\EOF
+ empty merge
+
+ edited
+ EOF
+'
+
test_done
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index 2421491931..4a6242ff99 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -113,6 +113,18 @@ test_expect_success 'check-mailmap --stdin simple address: no mapping' '
test_cmp expect actual
'
+test_expect_success 'check-mailmap name and address: mapping' '
+ test_when_finished "rm .mailmap" &&
+ cat >.mailmap <<-EOF &&
+ Bug Reports <bugs-new@company.xx> Bugs <bugs@company.xx>
+ EOF
+ cat >expect <<-EOF &&
+ <bugs@company.xx>
+ EOF
+ git check-mailmap "bugs@company.xx" >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'No mailmap' '
cat >expect <<-EOF &&
$GIT_AUTHOR_NAME (1):
diff --git a/t/t4209-log-pickaxe.sh b/t/t4209-log-pickaxe.sh
index a675ace081..0e2f80a268 100755
--- a/t/t4209-log-pickaxe.sh
+++ b/t/t4209-log-pickaxe.sh
@@ -93,6 +93,22 @@ test_expect_success 'usage: --no-pickaxe-regex' '
test_cmp expect actual
'
+test_expect_success 'usage: -G and -S with empty argument' '
+ cat >expect <<-\EOF &&
+ error: -S requires a non-empty argument
+ EOF
+
+ test_expect_code 129 git log -S "" 2>actual &&
+ test_cmp expect actual &&
+
+ cat >expect <<-\EOF &&
+ error: -G requires a non-empty argument
+ EOF
+
+ test_expect_code 129 git log -G "" 2>actual &&
+ test_cmp expect actual
+'
+
test_log expect_initial --grep initial
test_log expect_nomatch --grep InItial
test_log_icase expect_initial --grep InItial
diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh
index e273ab29c7..58074506c5 100755
--- a/t/t5504-fetch-receive-strict.sh
+++ b/t/t5504-fetch-receive-strict.sh
@@ -64,12 +64,6 @@ test_expect_success 'fetch with transfer.fsckobjects' '
)
'
-cat >exp <<EOF
-To dst
-! refs/heads/main:refs/heads/test [remote rejected] (missing necessary objects)
-Done
-EOF
-
test_expect_success 'push without strict' '
rm -rf dst &&
git init dst &&
@@ -78,6 +72,11 @@ test_expect_success 'push without strict' '
git config fetch.fsckobjects false &&
git config transfer.fsckobjects false
) &&
+ cat >exp <<-\EOF &&
+ To dst
+ ! refs/heads/main:refs/heads/test [remote rejected] (missing necessary objects)
+ Done
+ EOF
test_must_fail git push --porcelain dst main:refs/heads/test >act &&
test_cmp exp act
'
@@ -94,11 +93,6 @@ test_expect_success 'push with !receive.fsckobjects' '
test_cmp exp act
'
-cat >exp <<EOF
-To dst
-! refs/heads/main:refs/heads/test [remote rejected] (unpacker error)
-EOF
-
test_expect_success 'push with receive.fsckobjects' '
rm -rf dst &&
git init dst &&
@@ -107,6 +101,10 @@ test_expect_success 'push with receive.fsckobjects' '
git config receive.fsckobjects true &&
git config transfer.fsckobjects false
) &&
+ cat >exp <<-\EOF &&
+ To dst
+ ! refs/heads/main:refs/heads/test [remote rejected] (unpacker error)
+ EOF
test_must_fail git push --porcelain dst main:refs/heads/test >act &&
test_cmp exp act
'
@@ -129,15 +127,14 @@ test_expect_success 'repair the "corrupt or missing" object' '
git fsck
'
-cat >bogus-commit <<EOF
-tree $EMPTY_TREE
-author Bugs Bunny 1234567890 +0000
-committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000
-
-This commit object intentionally broken
-EOF
-
test_expect_success 'setup bogus commit' '
+ cat >bogus-commit <<-EOF &&
+ tree $EMPTY_TREE
+ author Bugs Bunny 1234567890 +0000
+ committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000
+
+ This commit object intentionally broken
+ EOF
commit="$(git hash-object --literally -t commit -w --stdin <bogus-commit)"
'
diff --git a/t/t5540-http-push-webdav.sh b/t/t5540-http-push-webdav.sh
index 37db3dec0c..3fa05ff185 100755
--- a/t/t5540-http-push-webdav.sh
+++ b/t/t5540-http-push-webdav.sh
@@ -201,4 +201,14 @@ test_expect_failure 'push to password-protected repository (no user in URL)' '
test_cmp expect actual
'
+test_expect_success 'push to password-protected repository (netrc)' '
+ test_commit pw-netrc &&
+ echo "default login user@host password pass@host" >"$HOME/.netrc" &&
+ GIT_TRACE=1 GIT_CURL_VERBOSE=1 git push "$HTTPD_URL/auth/dumb/test_repo.git" HEAD &&
+ git rev-parse --verify HEAD >expect &&
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
+ rev-parse --verify HEAD >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5543-atomic-push.sh b/t/t5543-atomic-push.sh
index 04b47ad84a..3a700b0676 100755
--- a/t/t5543-atomic-push.sh
+++ b/t/t5543-atomic-push.sh
@@ -280,4 +280,34 @@ test_expect_success 'atomic push reports (reject by non-ff)' '
test_cmp expect actual
'
+test_expect_success 'atomic push reports exit code failure' '
+ write_script receive-pack-wrapper <<-\EOF &&
+ git-receive-pack "$@"
+ exit 1
+ EOF
+ test_must_fail git -C workbench push --atomic \
+ --receive-pack="${SQ}$(pwd)${SQ}/receive-pack-wrapper" \
+ up HEAD:refs/heads/no-conflict 2>err &&
+ cat >expect <<-EOF &&
+ To ../upstream
+ * [new branch] HEAD -> no-conflict
+ error: failed to push some refs to ${SQ}../upstream${SQ}
+ EOF
+ test_cmp expect err
+'
+
+test_expect_success 'atomic push reports exit code failure with porcelain' '
+ write_script receive-pack-wrapper <<-\EOF &&
+ git-receive-pack "$@"
+ exit 1
+ EOF
+ test_must_fail git -C workbench push --atomic --porcelain \
+ --receive-pack="${SQ}$(pwd)${SQ}/receive-pack-wrapper" \
+ up HEAD:refs/heads/no-conflict-porcelain 2>err &&
+ cat >expect <<-EOF &&
+ error: failed to push some refs to ${SQ}../upstream${SQ}
+ EOF
+ test_cmp expect err
+'
+
test_done
diff --git a/t/t5548-push-porcelain.sh b/t/t5548-push-porcelain.sh
index 6282728eaf..4c19404ebe 100755
--- a/t/t5548-push-porcelain.sh
+++ b/t/t5548-push-porcelain.sh
@@ -54,29 +54,67 @@ format_and_save_expect () {
sed -e 's/^> //' -e 's/Z$//' >expect
}
+create_upstream_template () {
+ git init --bare upstream-template.git &&
+ git clone upstream-template.git tmp_work_dir &&
+ create_commits_in tmp_work_dir A B &&
+ (
+ cd tmp_work_dir &&
+ git push origin \
+ $B:refs/heads/main \
+ $A:refs/heads/foo \
+ $A:refs/heads/bar \
+ $A:refs/heads/baz
+ ) &&
+ rm -rf tmp_work_dir
+}
+
+setup_upstream () {
+ if test $# -ne 1
+ then
+ BUG "location of upstream repository is not provided"
+ fi &&
+ rm -rf "$1" &&
+ if ! test -d upstream-template.git
+ then
+ create_upstream_template
+ fi &&
+ git clone --mirror upstream-template.git "$1" &&
+ # The upstream repository provides services using the HTTP protocol.
+ if ! test "$1" = "upstream.git"
+ then
+ git -C "$1" config http.receivepack true
+ fi
+}
+
setup_upstream_and_workbench () {
- # Upstream after setup : main(B) foo(A) bar(A) baz(A)
- # Workbench after setup : main(A)
+ if test $# -ne 1
+ then
+ BUG "location of upstream repository is not provided"
+ fi
+ upstream="$1"
+
+ # Upstream after setup: main(B) foo(A) bar(A) baz(A)
+ # Workbench after setup: main(A) baz(A) next(A)
test_expect_success "setup upstream repository and workbench" '
- rm -rf upstream.git workbench &&
- git init --bare upstream.git &&
- git init workbench &&
- create_commits_in workbench A B &&
+ setup_upstream "$upstream" &&
+ rm -rf workbench &&
+ git clone "$upstream" workbench &&
(
cd workbench &&
+ git update-ref refs/heads/main $A &&
+ git update-ref refs/heads/baz $A &&
+ git update-ref refs/heads/next $A &&
# Try to make a stable fixed width for abbreviated commit ID,
# this fixed-width oid will be replaced with "<OID>".
git config core.abbrev 7 &&
- git remote add origin ../upstream.git &&
- git update-ref refs/heads/main $A &&
- git push origin \
- $B:refs/heads/main \
- $A:refs/heads/foo \
- $A:refs/heads/bar \
- $A:refs/heads/baz
+ git config advice.pushUpdateRejected false
) &&
- git -C "workbench" config advice.pushUpdateRejected false &&
- upstream=upstream.git
+ # The upstream repository provides services using the HTTP protocol.
+ if ! test "$upstream" = "upstream.git"
+ then
+ git -C workbench remote set-url origin "$HTTPD_URL/smart/upstream.git"
+ fi
'
}
@@ -88,34 +126,29 @@ run_git_push_porcelain_output_test() {
;;
file)
PROTOCOL="builtin protocol"
- URL_PREFIX="\.\."
+ URL_PREFIX=".*"
;;
esac
# Refs of upstream : main(B) foo(A) bar(A) baz(A)
# Refs of workbench: main(A) baz(A) next(A)
# git-push : main(A) NULL (B) baz(A) next(A)
- test_expect_success "porcelain output of successful git-push ($PROTOCOL)" '
- (
- cd workbench &&
- git update-ref refs/heads/main $A &&
- git update-ref refs/heads/baz $A &&
- git update-ref refs/heads/next $A &&
- git push --porcelain --force origin \
- main \
- :refs/heads/foo \
- $B:bar \
- baz \
- next
- ) >out &&
+ test_expect_success ".. git-push --porcelain ($PROTOCOL)" '
+ test_when_finished "setup_upstream \"$upstream\"" &&
+ test_must_fail git -C workbench push --porcelain origin \
+ main \
+ :refs/heads/foo \
+ $B:bar \
+ baz \
+ next >out &&
make_user_friendly_and_stable_output <out >actual &&
- format_and_save_expect <<-EOF &&
+ format_and_save_expect <<-\EOF &&
> To <URL/of/upstream.git>
> = refs/heads/baz:refs/heads/baz [up to date]
> <COMMIT-B>:refs/heads/bar <COMMIT-A>..<COMMIT-B>
> - :refs/heads/foo [deleted]
- > + refs/heads/main:refs/heads/main <COMMIT-B>...<COMMIT-A> (forced update)
> * refs/heads/next:refs/heads/next [new branch]
+ > ! refs/heads/main:refs/heads/main [rejected] (non-fast-forward)
> Done
EOF
test_cmp expect actual &&
@@ -125,34 +158,32 @@ run_git_push_porcelain_output_test() {
cat >expect <<-EOF &&
<COMMIT-B> refs/heads/bar
<COMMIT-A> refs/heads/baz
- <COMMIT-A> refs/heads/main
+ <COMMIT-B> refs/heads/main
<COMMIT-A> refs/heads/next
EOF
test_cmp expect actual
'
- # Refs of upstream : main(A) bar(B) baz(A) next(A)
- # Refs of workbench: main(B) bar(A) baz(A) next(A)
- # git-push : main(B) bar(A) NULL next(A)
- test_expect_success "atomic push failed ($PROTOCOL)" '
- (
- cd workbench &&
- git update-ref refs/heads/main $B &&
- git update-ref refs/heads/bar $A &&
- test_must_fail git push --atomic --porcelain origin \
- main \
- bar \
- :baz \
- next
- ) >out &&
+ # Refs of upstream : main(B) foo(A) bar(A) baz(A)
+ # Refs of workbench: main(A) baz(A) next(A)
+ # git-push : main(A) NULL (B) baz(A) next(A)
+ test_expect_success ".. git-push --porcelain --force ($PROTOCOL)" '
+ test_when_finished "setup_upstream \"$upstream\"" &&
+ git -C workbench push --porcelain --force origin \
+ main \
+ :refs/heads/foo \
+ $B:bar \
+ baz \
+ next >out &&
make_user_friendly_and_stable_output <out >actual &&
format_and_save_expect <<-EOF &&
- To <URL/of/upstream.git>
- > = refs/heads/next:refs/heads/next [up to date]
- > ! refs/heads/bar:refs/heads/bar [rejected] (non-fast-forward)
- > ! (delete):refs/heads/baz [rejected] (atomic push failed)
- > ! refs/heads/main:refs/heads/main [rejected] (atomic push failed)
- Done
+ > To <URL/of/upstream.git>
+ > = refs/heads/baz:refs/heads/baz [up to date]
+ > <COMMIT-B>:refs/heads/bar <COMMIT-A>..<COMMIT-B>
+ > - :refs/heads/foo [deleted]
+ > + refs/heads/main:refs/heads/main <COMMIT-B>...<COMMIT-A> (forced update)
+ > * refs/heads/next:refs/heads/next [new branch]
+ > Done
EOF
test_cmp expect actual &&
@@ -167,34 +198,129 @@ run_git_push_porcelain_output_test() {
test_cmp expect actual
'
- test_expect_success "prepare pre-receive hook ($PROTOCOL)" '
- test_hook --setup -C "$upstream" pre-receive <<-EOF
- exit 1
+ # Refs of upstream : main(B) foo(A) bar(A) baz(A)
+ # Refs of workbench: main(A) baz(A) next(A)
+ # git-push : main(A) NULL (B) baz(A) next(A)
+ test_expect_success ".. git push --porcelain --atomic ($PROTOCOL)" '
+ test_when_finished "setup_upstream \"$upstream\"" &&
+ test_must_fail git -C workbench push --porcelain --atomic origin \
+ main \
+ :refs/heads/foo \
+ $B:bar \
+ baz \
+ next >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ format_and_save_expect <<-EOF &&
+ > To <URL/of/upstream.git>
+ > = refs/heads/baz:refs/heads/baz [up to date]
+ > ! <COMMIT-B>:refs/heads/bar [rejected] (atomic push failed)
+ > ! (delete):refs/heads/foo [rejected] (atomic push failed)
+ > ! refs/heads/main:refs/heads/main [rejected] (non-fast-forward)
+ > ! refs/heads/next:refs/heads/next [rejected] (atomic push failed)
+ > Done
+ EOF
+ test_cmp expect actual &&
+
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/bar
+ <COMMIT-A> refs/heads/baz
+ <COMMIT-A> refs/heads/foo
+ <COMMIT-B> refs/heads/main
+ EOF
+ test_cmp expect actual
+ '
+
+ # Refs of upstream : main(B) foo(A) bar(A) baz(A)
+ # Refs of workbench: main(A) baz(A) next(A)
+ # git-push : main(A) NULL (B) baz(A) next(A)
+ test_expect_success ".. pre-receive hook declined ($PROTOCOL)" '
+ test_when_finished "rm -f \"$upstream/hooks/pre-receive\" &&
+ setup_upstream \"$upstream\"" &&
+ test_hook --setup -C "$upstream" pre-receive <<-EOF &&
+ exit 1
+ EOF
+ test_must_fail git -C workbench push --porcelain --force origin \
+ main \
+ :refs/heads/foo \
+ $B:bar \
+ baz \
+ next >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ format_and_save_expect <<-EOF &&
+ > To <URL/of/upstream.git>
+ > = refs/heads/baz:refs/heads/baz [up to date]
+ > ! <COMMIT-B>:refs/heads/bar [remote rejected] (pre-receive hook declined)
+ > ! :refs/heads/foo [remote rejected] (pre-receive hook declined)
+ > ! refs/heads/main:refs/heads/main [remote rejected] (pre-receive hook declined)
+ > ! refs/heads/next:refs/heads/next [remote rejected] (pre-receive hook declined)
+ > Done
+ EOF
+ test_cmp expect actual &&
+
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/bar
+ <COMMIT-A> refs/heads/baz
+ <COMMIT-A> refs/heads/foo
+ <COMMIT-B> refs/heads/main
EOF
+ test_cmp expect actual
'
- # Refs of upstream : main(A) bar(B) baz(A) next(A)
- # Refs of workbench: main(B) bar(A) baz(A) next(A)
- # git-push : main(B) bar(A) NULL next(A)
- test_expect_success "pre-receive hook declined ($PROTOCOL)" '
+ # Refs of upstream : main(B) foo(A) bar(A) baz(A)
+ # Refs of workbench: main(A) baz(A) next(A)
+ # git-push : main(A) next(A)
+ test_expect_success ".. non-fastforward push ($PROTOCOL)" '
+ test_when_finished "setup_upstream \"$upstream\"" &&
(
cd workbench &&
- git update-ref refs/heads/main $B &&
- git update-ref refs/heads/bar $A &&
- test_must_fail git push --porcelain --force origin \
+ test_must_fail git push --porcelain origin \
main \
- bar \
- :baz \
next
) >out &&
make_user_friendly_and_stable_output <out >actual &&
format_and_save_expect <<-EOF &&
- To <URL/of/upstream.git>
- > = refs/heads/next:refs/heads/next [up to date]
- > ! refs/heads/bar:refs/heads/bar [remote rejected] (pre-receive hook declined)
- > ! :refs/heads/baz [remote rejected] (pre-receive hook declined)
- > ! refs/heads/main:refs/heads/main [remote rejected] (pre-receive hook declined)
- Done
+ > To <URL/of/upstream.git>
+ > * refs/heads/next:refs/heads/next [new branch]
+ > ! refs/heads/main:refs/heads/main [rejected] (non-fast-forward)
+ > Done
+ EOF
+ test_cmp expect actual &&
+
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/bar
+ <COMMIT-A> refs/heads/baz
+ <COMMIT-A> refs/heads/foo
+ <COMMIT-B> refs/heads/main
+ <COMMIT-A> refs/heads/next
+ EOF
+ test_cmp expect actual
+ '
+
+ # Refs of upstream : main(B) foo(A) bar(A) baz(A)
+ # Refs of workbench: main(A) baz(A) next(A)
+ # git-push : main(A) NULL (B) baz(A) next(A)
+ test_expect_success ".. git push --porcelain --atomic --force ($PROTOCOL)" '
+ git -C workbench push --porcelain --atomic --force origin \
+ main \
+ :refs/heads/foo \
+ $B:bar \
+ baz \
+ next >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ format_and_save_expect <<-\EOF &&
+ > To <URL/of/upstream.git>
+ > = refs/heads/baz:refs/heads/baz [up to date]
+ > <COMMIT-B>:refs/heads/bar <COMMIT-A>..<COMMIT-B>
+ > - :refs/heads/foo [deleted]
+ > + refs/heads/main:refs/heads/main <COMMIT-B>...<COMMIT-A> (forced update)
+ > * refs/heads/next:refs/heads/next [new branch]
+ > Done
EOF
test_cmp expect actual &&
@@ -208,71 +334,174 @@ run_git_push_porcelain_output_test() {
EOF
test_cmp expect actual
'
+}
- test_expect_success "remove pre-receive hook ($PROTOCOL)" '
- rm "$upstream/hooks/pre-receive"
+run_git_push_dry_run_porcelain_output_test() {
+ case $1 in
+ http)
+ PROTOCOL="HTTP protocol"
+ URL_PREFIX="http://.*"
+ ;;
+ file)
+ PROTOCOL="builtin protocol"
+ URL_PREFIX=".*"
+ ;;
+ esac
+
+ # Refs of upstream : main(B) foo(A) bar(A) baz(A)
+ # Refs of workbench: main(A) baz(A) next(A)
+ # git-push : main(A) NULL (B) baz(A) next(A)
+ test_expect_success ".. git-push --porcelain --dry-run ($PROTOCOL)" '
+ test_must_fail git -C workbench push --porcelain --dry-run origin \
+ main \
+ :refs/heads/foo \
+ $B:bar \
+ baz \
+ next >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ format_and_save_expect <<-EOF &&
+ > To <URL/of/upstream.git>
+ > = refs/heads/baz:refs/heads/baz [up to date]
+ > <COMMIT-B>:refs/heads/bar <COMMIT-A>..<COMMIT-B>
+ > - :refs/heads/foo [deleted]
+ > * refs/heads/next:refs/heads/next [new branch]
+ > ! refs/heads/main:refs/heads/main [rejected] (non-fast-forward)
+ > Done
+ EOF
+ test_cmp expect actual &&
+
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/bar
+ <COMMIT-A> refs/heads/baz
+ <COMMIT-A> refs/heads/foo
+ <COMMIT-B> refs/heads/main
+ EOF
+ test_cmp expect actual
'
- # Refs of upstream : main(A) bar(B) baz(A) next(A)
- # Refs of workbench: main(B) bar(A) baz(A) next(A)
- # git-push : main(B) bar(A) NULL next(A)
- test_expect_success "non-fastforward push ($PROTOCOL)" '
- (
- cd workbench &&
- test_must_fail git push --porcelain origin \
- main \
- bar \
- :baz \
- next
- ) >out &&
+ # Refs of upstream : main(B) foo(A) bar(A) baz(A)
+ # Refs of workbench: main(A) baz(A) next(A)
+ # push : main(A) NULL (B) baz(A) next(A)
+ test_expect_success ".. git-push --porcelain --dry-run --force ($PROTOCOL)" '
+ git -C workbench push --porcelain --dry-run --force origin \
+ main \
+ :refs/heads/foo \
+ $B:bar \
+ baz \
+ next >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ format_and_save_expect <<-EOF &&
+ > To <URL/of/upstream.git>
+ > = refs/heads/baz:refs/heads/baz [up to date]
+ > <COMMIT-B>:refs/heads/bar <COMMIT-A>..<COMMIT-B>
+ > - :refs/heads/foo [deleted]
+ > + refs/heads/main:refs/heads/main <COMMIT-B>...<COMMIT-A> (forced update)
+ > * refs/heads/next:refs/heads/next [new branch]
+ > Done
+ EOF
+ test_cmp expect actual &&
+
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/bar
+ <COMMIT-A> refs/heads/baz
+ <COMMIT-A> refs/heads/foo
+ <COMMIT-B> refs/heads/main
+ EOF
+ test_cmp expect actual
+ '
+
+ # Refs of upstream : main(B) foo(A) bar(A) baz(A)
+ # Refs of workbench: main(A) baz(A) next(A)
+ # git-push : main(A) NULL (B) baz(A) next(A)
+ test_expect_success ".. git-push --porcelain --dry-run --atomic ($PROTOCOL)" '
+ test_must_fail git -C workbench push --porcelain --dry-run --atomic origin \
+ main \
+ :refs/heads/foo \
+ $B:bar \
+ baz \
+ next >out &&
make_user_friendly_and_stable_output <out >actual &&
format_and_save_expect <<-EOF &&
- To <URL/of/upstream.git>
- > = refs/heads/next:refs/heads/next [up to date]
- > - :refs/heads/baz [deleted]
- > refs/heads/main:refs/heads/main <COMMIT-A>..<COMMIT-B>
- > ! refs/heads/bar:refs/heads/bar [rejected] (non-fast-forward)
- Done
+ > To <URL/of/upstream.git>
+ > = refs/heads/baz:refs/heads/baz [up to date]
+ > ! <COMMIT-B>:refs/heads/bar [rejected] (atomic push failed)
+ > ! (delete):refs/heads/foo [rejected] (atomic push failed)
+ > ! refs/heads/main:refs/heads/main [rejected] (non-fast-forward)
+ > ! refs/heads/next:refs/heads/next [rejected] (atomic push failed)
+ > Done
EOF
test_cmp expect actual &&
git -C "$upstream" show-ref >out &&
make_user_friendly_and_stable_output <out >actual &&
cat >expect <<-EOF &&
- <COMMIT-B> refs/heads/bar
+ <COMMIT-A> refs/heads/bar
+ <COMMIT-A> refs/heads/baz
+ <COMMIT-A> refs/heads/foo
+ <COMMIT-B> refs/heads/main
+ EOF
+ test_cmp expect actual
+ '
+
+ # Refs of upstream : main(B) foo(A) bar(A) baz(A)
+ # Refs of workbench: main(A) baz(A) next(A)
+ # push : main(A) NULL (B) baz(A) next(A)
+ test_expect_success ".. git-push --porcelain --dry-run --atomic --force ($PROTOCOL)" '
+ git -C workbench push --porcelain --dry-run --atomic --force origin \
+ main \
+ :refs/heads/foo \
+ $B:bar \
+ baz \
+ next >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ format_and_save_expect <<-EOF &&
+ > To <URL/of/upstream.git>
+ > = refs/heads/baz:refs/heads/baz [up to date]
+ > <COMMIT-B>:refs/heads/bar <COMMIT-A>..<COMMIT-B>
+ > - :refs/heads/foo [deleted]
+ > + refs/heads/main:refs/heads/main <COMMIT-B>...<COMMIT-A> (forced update)
+ > * refs/heads/next:refs/heads/next [new branch]
+ > Done
+ EOF
+ test_cmp expect actual &&
+
+ git -C "$upstream" show-ref >out &&
+ make_user_friendly_and_stable_output <out >actual &&
+ cat >expect <<-EOF &&
+ <COMMIT-A> refs/heads/bar
+ <COMMIT-A> refs/heads/baz
+ <COMMIT-A> refs/heads/foo
<COMMIT-B> refs/heads/main
- <COMMIT-A> refs/heads/next
EOF
test_cmp expect actual
'
}
-# Initialize the upstream repository and local workbench.
-setup_upstream_and_workbench
+setup_upstream_and_workbench upstream.git
-# Run git-push porcelain test on builtin protocol
run_git_push_porcelain_output_test file
+setup_upstream_and_workbench upstream.git
+
+run_git_push_dry_run_porcelain_output_test file
+
ROOT_PATH="$PWD"
. "$TEST_DIRECTORY"/lib-gpg.sh
. "$TEST_DIRECTORY"/lib-httpd.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
start_httpd
+setup_askpass_helper
-# Re-initialize the upstream repository and local workbench.
-setup_upstream_and_workbench
-
-test_expect_success "setup for http" '
- git -C upstream.git config http.receivepack true &&
- upstream="$HTTPD_DOCUMENT_ROOT_PATH/upstream.git" &&
- mv upstream.git "$upstream" &&
+setup_upstream_and_workbench "$HTTPD_DOCUMENT_ROOT_PATH/upstream.git"
- git -C workbench remote set-url origin $HTTPD_URL/smart/upstream.git
-'
+run_git_push_porcelain_output_test http
-setup_askpass_helper
+setup_upstream_and_workbench "$HTTPD_DOCUMENT_ROOT_PATH/upstream.git"
-# Run git-push porcelain test on HTTP protocol
-run_git_push_porcelain_output_test http
+run_git_push_dry_run_porcelain_output_test http
test_done
diff --git a/t/t5620-backfill.sh b/t/t5620-backfill.sh
new file mode 100755
index 0000000000..58c81556e7
--- /dev/null
+++ b/t/t5620-backfill.sh
@@ -0,0 +1,211 @@
+#!/bin/sh
+
+test_description='git backfill on partial clones'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+# We create objects in the 'src' repo.
+test_expect_success 'setup repo for object creation' '
+ echo "{print \$1}" >print_1.awk &&
+ echo "{print \$2}" >print_2.awk &&
+
+ git init src &&
+
+ mkdir -p src/a/b/c &&
+ mkdir -p src/d/e &&
+
+ for i in 1 2
+ do
+ for n in 1 2 3 4
+ do
+ echo "Version $i of file $n" > src/file.$n.txt &&
+ echo "Version $i of file a/$n" > src/a/file.$n.txt &&
+ echo "Version $i of file a/b/$n" > src/a/b/file.$n.txt &&
+ echo "Version $i of file a/b/c/$n" > src/a/b/c/file.$n.txt &&
+ echo "Version $i of file d/$n" > src/d/file.$n.txt &&
+ echo "Version $i of file d/e/$n" > src/d/e/file.$n.txt &&
+ git -C src add . &&
+ git -C src commit -m "Iteration $n" || return 1
+ done
+ done
+'
+
+# Clone 'src' into 'srv.bare' so we have a bare repo to be our origin
+# server for the partial clone.
+test_expect_success 'setup bare clone for server' '
+ git clone --bare "file://$(pwd)/src" srv.bare &&
+ git -C srv.bare config --local uploadpack.allowfilter 1 &&
+ git -C srv.bare config --local uploadpack.allowanysha1inwant 1
+'
+
+# do basic partial clone from "srv.bare"
+test_expect_success 'do partial clone 1, backfill gets all objects' '
+ git clone --no-checkout --filter=blob:none \
+ --single-branch --branch=main \
+ "file://$(pwd)/srv.bare" backfill1 &&
+
+ # Backfill with no options gets everything reachable from HEAD.
+ GIT_TRACE2_EVENT="$(pwd)/backfill-file-trace" git \
+ -C backfill1 backfill &&
+
+ # We should have engaged the partial clone machinery
+ test_trace2_data promisor fetch_count 48 <backfill-file-trace &&
+
+ # No more missing objects!
+ git -C backfill1 rev-list --quiet --objects --missing=print HEAD >revs2 &&
+ test_line_count = 0 revs2
+'
+
+test_expect_success 'do partial clone 2, backfill min batch size' '
+ git clone --no-checkout --filter=blob:none \
+ --single-branch --branch=main \
+ "file://$(pwd)/srv.bare" backfill2 &&
+
+ GIT_TRACE2_EVENT="$(pwd)/batch-trace" git \
+ -C backfill2 backfill --min-batch-size=20 &&
+
+ # Batches were used
+ test_trace2_data promisor fetch_count 20 <batch-trace >matches &&
+ test_line_count = 2 matches &&
+ test_trace2_data promisor fetch_count 8 <batch-trace &&
+
+ # No more missing objects!
+ git -C backfill2 rev-list --quiet --objects --missing=print HEAD >revs2 &&
+ test_line_count = 0 revs2
+'
+
+test_expect_success 'backfill --sparse without sparse-checkout fails' '
+ git init not-sparse &&
+ test_must_fail git -C not-sparse backfill --sparse 2>err &&
+ grep "problem loading sparse-checkout" err
+'
+
+test_expect_success 'backfill --sparse' '
+ git clone --sparse --filter=blob:none \
+ --single-branch --branch=main \
+ "file://$(pwd)/srv.bare" backfill3 &&
+
+ # Initial checkout includes four files at root.
+ git -C backfill3 rev-list --quiet --objects --missing=print HEAD >missing &&
+ test_line_count = 44 missing &&
+
+ # Initial sparse-checkout is just the files at root, so we get the
+ # older versions of the four files at tip.
+ GIT_TRACE2_EVENT="$(pwd)/sparse-trace1" git \
+ -C backfill3 backfill --sparse &&
+ test_trace2_data promisor fetch_count 4 <sparse-trace1 &&
+ test_trace2_data path-walk paths 5 <sparse-trace1 &&
+ git -C backfill3 rev-list --quiet --objects --missing=print HEAD >missing &&
+ test_line_count = 40 missing &&
+
+ # Expand the sparse-checkout to include 'd' recursively. This
+ # engages the algorithm to skip the trees for 'a'. Note that
+ # the "sparse-checkout set" command downloads the objects at tip
+ # to satisfy the current checkout.
+ git -C backfill3 sparse-checkout set d &&
+ GIT_TRACE2_EVENT="$(pwd)/sparse-trace2" git \
+ -C backfill3 backfill --sparse &&
+ test_trace2_data promisor fetch_count 8 <sparse-trace2 &&
+ test_trace2_data path-walk paths 15 <sparse-trace2 &&
+ git -C backfill3 rev-list --quiet --objects --missing=print HEAD >missing &&
+ test_line_count = 24 missing &&
+
+ # Disabling the --sparse option (on by default) will download everything
+ git -C backfill3 backfill --no-sparse &&
+ git -C backfill3 rev-list --quiet --objects --missing=print HEAD >missing &&
+ test_line_count = 0 missing
+'
+
+test_expect_success 'backfill --sparse without cone mode (positive)' '
+ git clone --no-checkout --filter=blob:none \
+ --single-branch --branch=main \
+ "file://$(pwd)/srv.bare" backfill4 &&
+
+ # No blobs yet
+ git -C backfill4 rev-list --quiet --objects --missing=print HEAD >missing &&
+ test_line_count = 48 missing &&
+
+ # Define sparse-checkout by filename regardless of parent directory.
+ # This downloads 6 blobs to satisfy the checkout.
+ git -C backfill4 sparse-checkout set --no-cone "**/file.1.txt" &&
+ git -C backfill4 checkout main &&
+
+ # Track new blob count
+ git -C backfill4 rev-list --quiet --objects --missing=print HEAD >missing &&
+ test_line_count = 42 missing &&
+
+ GIT_TRACE2_EVENT="$(pwd)/no-cone-trace1" git \
+ -C backfill4 backfill --sparse &&
+ test_trace2_data promisor fetch_count 6 <no-cone-trace1 &&
+
+ # This walk needed to visit all directories to search for these paths.
+ test_trace2_data path-walk paths 12 <no-cone-trace1 &&
+ git -C backfill4 rev-list --quiet --objects --missing=print HEAD >missing &&
+ test_line_count = 36 missing
+'
+
+test_expect_success 'backfill --sparse without cone mode (negative)' '
+ git clone --no-checkout --filter=blob:none \
+ --single-branch --branch=main \
+ "file://$(pwd)/srv.bare" backfill5 &&
+
+ # No blobs yet
+ git -C backfill5 rev-list --quiet --objects --missing=print HEAD >missing &&
+ test_line_count = 48 missing &&
+
+ # Define sparse-checkout by filename regardless of parent directory.
+ # This downloads 18 blobs to satisfy the checkout
+ git -C backfill5 sparse-checkout set --no-cone "**/file*" "!**/file.1.txt" &&
+ git -C backfill5 checkout main &&
+
+ # Track new blob count
+ git -C backfill5 rev-list --quiet --objects --missing=print HEAD >missing &&
+ test_line_count = 30 missing &&
+
+ GIT_TRACE2_EVENT="$(pwd)/no-cone-trace2" git \
+ -C backfill5 backfill --sparse &&
+ test_trace2_data promisor fetch_count 18 <no-cone-trace2 &&
+
+ # This walk needed to visit all directories to search for these paths, plus
+ # 12 extra "file.?.txt" paths than the previous test.
+ test_trace2_data path-walk paths 24 <no-cone-trace2 &&
+ git -C backfill5 rev-list --quiet --objects --missing=print HEAD >missing &&
+ test_line_count = 12 missing
+'
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'create a partial clone over HTTP' '
+ SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server" &&
+ rm -rf "$SERVER" repo &&
+ git clone --bare "file://$(pwd)/src" "$SERVER" &&
+ test_config -C "$SERVER" uploadpack.allowfilter 1 &&
+ test_config -C "$SERVER" uploadpack.allowanysha1inwant 1 &&
+
+ git clone --no-checkout --filter=blob:none \
+ "$HTTPD_URL/smart/server" backfill-http
+'
+
+test_expect_success 'backfilling over HTTP succeeds' '
+ GIT_TRACE2_EVENT="$(pwd)/backfill-http-trace" git \
+ -C backfill-http backfill &&
+
+ # We should have engaged the partial clone machinery
+ test_trace2_data promisor fetch_count 48 <backfill-http-trace &&
+
+ # Confirm all objects are present, none missing.
+ git -C backfill-http rev-list --objects --all >rev-list-out &&
+ awk "{print \$1;}" <rev-list-out >oids &&
+ GIT_TRACE2_EVENT="$(pwd)/walk-trace" git -C backfill-http \
+ cat-file --batch-check <oids >batch-out &&
+ ! grep missing batch-out
+'
+
+# DO NOT add non-httpd-specific tests here, because the last part of this
+# test script is only executed when httpd is available and enabled.
+
+test_done
diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh
index de904c1655..678a346ed0 100755
--- a/t/t5701-git-serve.sh
+++ b/t/t5701-git-serve.sh
@@ -7,24 +7,40 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
-test_expect_success 'test capability advertisement' '
+test_expect_success 'setup to generate files with expected content' '
+ printf "agent=git/%s" "$(git version | cut -d" " -f3)" >agent_capability &&
+
test_oid_cache <<-EOF &&
wrong_algo sha1:sha256
wrong_algo sha256:sha1
EOF
+
+ if test_have_prereq WINDOWS
+ then
+ printf "agent=FAKE\n" >agent_capability
+ else
+ printf -- "-%s\n" $(uname -s | test_redact_non_printables) >>agent_capability
+ fi &&
cat >expect.base <<-EOF &&
version 2
- agent=git/$(git version | cut -d" " -f3)
+ $(cat agent_capability)
ls-refs=unborn
fetch=shallow wait-for-done
server-option
object-format=$(test_oid algo)
EOF
- cat >expect.trailer <<-EOF &&
+ cat >expect.trailer <<-EOF
0000
EOF
+'
+
+test_expect_success 'test capability advertisement' '
cat expect.base expect.trailer >expect &&
+ if test_have_prereq WINDOWS
+ then
+ GIT_USER_AGENT=FAKE && export GIT_USER_AGENT
+ fi &&
GIT_TEST_SIDEBAND_ALL=0 test-tool serve-v2 \
--advertise-capabilities >out &&
test-tool pkt-line unpack <out >actual &&
@@ -355,6 +371,10 @@ test_expect_success 'test capability advertisement with uploadpack.advertiseBund
expect.extra \
expect.trailer >expect &&
+ if test_have_prereq WINDOWS
+ then
+ GIT_USER_AGENT=FAKE && export GIT_USER_AGENT
+ fi &&
GIT_TEST_SIDEBAND_ALL=0 test-tool serve-v2 \
--advertise-capabilities >out &&
test-tool pkt-line unpack <out >actual &&
diff --git a/t/t6022-rev-list-missing.sh b/t/t6022-rev-list-missing.sh
index 7553a9cca2..3e2790d4c8 100755
--- a/t/t6022-rev-list-missing.sh
+++ b/t/t6022-rev-list-missing.sh
@@ -145,4 +145,57 @@ do
done
done
+for obj in "HEAD~1" "HEAD^{tree}" "HEAD:foo" "HEAD:foo/bar" "HEAD:baz baz"
+do
+ test_expect_success "--missing=print-info with missing '$obj'" '
+ test_when_finished rm -rf missing-info &&
+
+ git init missing-info &&
+ (
+ cd missing-info &&
+ git commit --allow-empty -m first &&
+
+ mkdir foo &&
+ echo bar >foo/bar &&
+ echo baz >"baz baz" &&
+ echo bat >bat\" &&
+ git add -A &&
+ git commit -m second &&
+
+ oid="$(git rev-parse "$obj")" &&
+ path=".git/objects/$(test_oid_to_path $oid)" &&
+ type_info=" type=$(git cat-file -t $oid)" &&
+
+ case $obj in
+ HEAD:foo)
+ path_info=" path=foo"
+ ;;
+ HEAD:foo/bar)
+ path_info=" path=foo/bar"
+ ;;
+ "HEAD:baz baz")
+ path_info=" path=\"baz baz\""
+ ;;
+ "HEAD:bat\"")
+ path_info=" path=\"bat\\\"\""
+ ;;
+ esac &&
+
+ # Before the object is made missing, we use rev-list to
+ # get the expected oids.
+ git rev-list --objects --no-object-names \
+ HEAD ^"$obj" >expect.raw &&
+ echo "?$oid$path_info$type_info" >>expect.raw &&
+
+ mv "$path" "$path.hidden" &&
+ git rev-list --objects --no-object-names \
+ --missing=print-info HEAD >actual.raw &&
+
+ sort actual.raw >actual &&
+ sort expect.raw >expect &&
+ test_cmp expect actual
+ )
+ '
+done
+
test_done
diff --git a/t/t6601-path-walk.sh b/t/t6601-path-walk.sh
index 5f04acb8a2..c89b0f1e19 100755
--- a/t/t6601-path-walk.sh
+++ b/t/t6601-path-walk.sh
@@ -176,6 +176,38 @@ test_expect_success 'branches and indexed objects mix well' '
test_cmp_sorted expect out
'
+test_expect_success 'base & topic, sparse' '
+ cat >patterns <<-EOF &&
+ /*
+ !/*/
+ /left/
+ EOF
+
+ test-tool path-walk --stdin-pl -- base topic <patterns >out &&
+
+ cat >expect <<-EOF &&
+ 0:commit::$(git rev-parse topic)
+ 0:commit::$(git rev-parse base)
+ 0:commit::$(git rev-parse base~1)
+ 0:commit::$(git rev-parse base~2)
+ 1:tree::$(git rev-parse topic^{tree})
+ 1:tree::$(git rev-parse base^{tree})
+ 1:tree::$(git rev-parse base~1^{tree})
+ 1:tree::$(git rev-parse base~2^{tree})
+ 2:blob:a:$(git rev-parse base~2:a)
+ 3:tree:left/:$(git rev-parse base:left)
+ 3:tree:left/:$(git rev-parse base~2:left)
+ 4:blob:left/b:$(git rev-parse base~2:left/b)
+ 4:blob:left/b:$(git rev-parse base:left/b)
+ blobs:3
+ commits:4
+ tags:0
+ trees:6
+ EOF
+
+ test_cmp_sorted expect out
+'
+
test_expect_success 'topic only' '
test-tool path-walk -- topic >out &&
diff --git a/t/t7603-merge-reduce-heads.sh b/t/t7603-merge-reduce-heads.sh
index 4887ca705b..1f8c3b7ccb 100755
--- a/t/t7603-merge-reduce-heads.sh
+++ b/t/t7603-merge-reduce-heads.sh
@@ -52,12 +52,12 @@ test_expect_success 'merge c1 with c2, c3, c4, c5' '
test "$(git rev-parse c3)" = "$(git rev-parse HEAD^3)" &&
test "$(git rev-parse c5)" = "$(git rev-parse HEAD^4)" &&
git diff --exit-code &&
- test -f c0.c &&
- test -f c1.c &&
- test -f c2.c &&
- test -f c3.c &&
- test -f c4.c &&
- test -f c5.c &&
+ test_path_is_file c0.c &&
+ test_path_is_file c1.c &&
+ test_path_is_file c2.c &&
+ test_path_is_file c3.c &&
+ test_path_is_file c4.c &&
+ test_path_is_file c5.c &&
git show --format=%s -s >actual &&
! grep c1 actual &&
grep c2 actual &&
@@ -75,12 +75,12 @@ test_expect_success 'pull c2, c3, c4, c5 into c1' '
test "$(git rev-parse c3)" = "$(git rev-parse HEAD^3)" &&
test "$(git rev-parse c5)" = "$(git rev-parse HEAD^4)" &&
git diff --exit-code &&
- test -f c0.c &&
- test -f c1.c &&
- test -f c2.c &&
- test -f c3.c &&
- test -f c4.c &&
- test -f c5.c &&
+ test_path_is_file c0.c &&
+ test_path_is_file c1.c &&
+ test_path_is_file c2.c &&
+ test_path_is_file c3.c &&
+ test_path_is_file c4.c &&
+ test_path_is_file c5.c &&
git show --format=%s -s >actual &&
! grep c1 actual &&
grep c2 actual &&
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index b93736e0d5..79377bc0fc 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -2043,3 +2043,11 @@ test_trailing_hash () {
test-tool hexdump |
sed "s/ //g"
}
+
+# Trim and replace each character with ascii code below 32 or above
+# 127 (included) using a dot '.' character.
+# Octal intervals \001-\040 and \177-\377
+# correspond to decimal intervals 1-32 and 127-255
+test_redact_non_printables () {
+ tr -d "\n\r" | tr "[\001-\040][\177-\377]" "."
+}
diff --git a/templates/Makefile b/templates/Makefile
index bd1e9e30c1..722755338d 100644
--- a/templates/Makefile
+++ b/templates/Makefile
@@ -1,3 +1,6 @@
+# The default target of this Makefile is...
+all::
+
# Import tree-wide shared Makefile behavior and libraries
include ../shared.mak
@@ -23,7 +26,7 @@ PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
template_instdir_SQ = $(subst ','\'',$(template_instdir))
-all: boilerplates.made custom
+all:: boilerplates.made custom
# Put templates that can be copied straight from the source
# in a file direc--tory--file in the source. They will be
diff --git a/transport.c b/transport.c
index d6851dc475..6c2801bcbd 100644
--- a/transport.c
+++ b/transport.c
@@ -935,6 +935,13 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
case protocol_v0:
ret = send_pack(the_repository, &args, data->fd, data->conn, remote_refs,
&data->extra_have);
+ /*
+ * Ignore the specific error code to maintain consistent behavior
+ * with the "push_refs()" function across different transports,
+ * such as "push_refs_with_push()" for HTTP protocol.
+ */
+ if (ret == ERROR_SEND_PACK_BAD_REF_STATUS)
+ ret = 0;
break;
case protocol_unknown_version:
BUG("unknown protocol version");
@@ -942,15 +949,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
close(data->fd[1]);
close(data->fd[0]);
- /*
- * Atomic push may abort the connection early and close the pipe,
- * which may cause an error for `finish_connect()`. Ignore this error
- * for atomic git-push.
- */
- if (ret || args.atomic)
- finish_connect(data->conn);
- else
- ret = finish_connect(data->conn);
+ ret |= finish_connect(data->conn);
data->conn = NULL;
data->finished_handshake = 0;
diff --git a/version.c b/version.c
index 4786c4e0a5..279269cc50 100644
--- a/version.c
+++ b/version.c
@@ -1,6 +1,9 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "version.h"
#include "strbuf.h"
+#include "gettext.h"
#ifndef GIT_VERSION_H
# include "version-def.h"
@@ -11,6 +14,19 @@
const char git_version_string[] = GIT_VERSION;
const char git_built_from_commit_string[] = GIT_BUILT_FROM_COMMIT;
+/*
+ * Trim and replace each character with ascii code below 32 or above
+ * 127 (included) using a dot '.' character.
+ */
+static void redact_non_printables(struct strbuf *buf)
+{
+ strbuf_trim(buf);
+ for (size_t i = 0; i < buf->len; i++) {
+ if (!isprint(buf->buf[i]) || buf->buf[i] == ' ')
+ buf->buf[i] = '.';
+ }
+}
+
const char *git_user_agent(void)
{
static const char *agent = NULL;
@@ -24,6 +40,27 @@ const char *git_user_agent(void)
return agent;
}
+/*
+ Retrieve, sanitize and cache operating system info for subsequent
+ calls. Return a pointer to the sanitized operating system info
+ string.
+*/
+static const char *os_info(void)
+{
+ static const char *os = NULL;
+
+ if (!os) {
+ struct strbuf buf = STRBUF_INIT;
+
+ get_uname_info(&buf, 0);
+ /* Sanitize the os information immediately */
+ redact_non_printables(&buf);
+ os = strbuf_detach(&buf, NULL);
+ }
+
+ return os;
+}
+
const char *git_user_agent_sanitized(void)
{
static const char *agent = NULL;
@@ -32,13 +69,35 @@ const char *git_user_agent_sanitized(void)
struct strbuf buf = STRBUF_INIT;
strbuf_addstr(&buf, git_user_agent());
- strbuf_trim(&buf);
- for (size_t i = 0; i < buf.len; i++) {
- if (buf.buf[i] <= 32 || buf.buf[i] >= 127)
- buf.buf[i] = '.';
+
+ if (!getenv("GIT_USER_AGENT")) {
+ strbuf_addch(&buf, '-');
+ strbuf_addstr(&buf, os_info());
}
- agent = buf.buf;
+ redact_non_printables(&buf);
+ agent = strbuf_detach(&buf, NULL);
}
return agent;
}
+
+int get_uname_info(struct strbuf *buf, unsigned int full)
+{
+ struct utsname uname_info;
+
+ if (uname(&uname_info)) {
+ strbuf_addf(buf, _("uname() failed with error '%s' (%d)\n"),
+ strerror(errno),
+ errno);
+ return -1;
+ }
+ if (full)
+ strbuf_addf(buf, "%s %s %s %s\n",
+ uname_info.sysname,
+ uname_info.release,
+ uname_info.version,
+ uname_info.machine);
+ else
+ strbuf_addf(buf, "%s\n", uname_info.sysname);
+ return 0;
+}
diff --git a/version.h b/version.h
index 7c62e80577..bbde6d371a 100644
--- a/version.h
+++ b/version.h
@@ -1,10 +1,20 @@
#ifndef VERSION_H
#define VERSION_H
+struct repository;
+
extern const char git_version_string[];
extern const char git_built_from_commit_string[];
const char *git_user_agent(void);
const char *git_user_agent_sanitized(void);
+/*
+ Try to get information about the system using uname(2).
+ Return -1 and put an error message into 'buf' in case of uname()
+ error. Return 0 and put uname info into 'buf' otherwise.
+*/
+int get_uname_info(struct strbuf *buf, unsigned int full);
+
+
#endif /* VERSION_H */
diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index 4685ba6137..8889b8b62a 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -19,7 +19,6 @@
* Davide Libenzi <davidel@xmailserver.org>
*
*/
-#define DISABLE_SIGN_COMPARE_WARNINGS
#include "xinclude.h"
@@ -1014,7 +1013,7 @@ static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags)
static int record_matches_regex(xrecord_t *rec, xpparam_t const *xpp) {
regmatch_t regmatch;
- int i;
+ size_t i;
for (i = 0; i < xpp->ignore_regex_nr; i++)
if (!regexec_buf(xpp->ignore_regex[i], rec->ptr, rec->size, 1,
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index 75f0fe4986..f8e3f25b03 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -54,7 +54,7 @@ xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
xdchange_t *xch, *xchp, *lxch;
long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
long max_ignorable = xecfg->ctxlen;
- unsigned long ignored = 0; /* number of ignored blank lines */
+ long ignored = 0; /* number of ignored blank lines */
/* remove ignorable changes that are too far before other changes */
for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {
diff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c
index 16a8fe2f3f..040d81e0bc 100644
--- a/xdiff/xhistogram.c
+++ b/xdiff/xhistogram.c
@@ -106,7 +106,7 @@ static int scanA(struct histindex *index, int line1, int count1)
unsigned int chain_len;
struct record **rec_chain, *rec;
- for (ptr = LINE_END(1); line1 <= ptr; ptr--) {
+ for (ptr = LINE_END(1); (unsigned int)line1 <= ptr; ptr--) {
tbl_idx = TABLE_HASH(index, 1, ptr);
rec_chain = index->records + tbl_idx;
rec = *rec_chain;
@@ -181,14 +181,14 @@ static int try_lcs(struct histindex *index, struct region *lcs, int b_ptr,
be = bs;
rc = rec->cnt;
- while (line1 < as && line2 < bs
+ while ((unsigned int)line1 < as && (unsigned int)line2 < bs
&& CMP(index, 1, as - 1, 2, bs - 1)) {
as--;
bs--;
if (1 < rc)
rc = XDL_MIN(rc, CNT(index, as));
}
- while (ae < LINE_END(1) && be < LINE_END(2)
+ while (ae < (unsigned int)LINE_END(1) && be < (unsigned int)LINE_END(2)
&& CMP(index, 1, ae + 1, 2, be + 1)) {
ae++;
be++;
@@ -313,7 +313,7 @@ redo:
if (count1 <= 0 && count2 <= 0)
return 0;
- if (LINE_END(1) >= MAX_PTR)
+ if ((unsigned int)LINE_END(1) >= MAX_PTR)
return -1;
if (!count1) {
diff --git a/xdiff/xinclude.h b/xdiff/xinclude.h
index 7e56542526..a4285ac0eb 100644
--- a/xdiff/xinclude.h
+++ b/xdiff/xinclude.h
@@ -23,8 +23,6 @@
#if !defined(XINCLUDE_H)
#define XINCLUDE_H
-#define DISABLE_SIGN_COMPARE_WARNINGS
-
#include "git-compat-util.h"
#include "xmacros.h"
#include "xdiff.h"
diff --git a/xdiff/xpatience.c b/xdiff/xpatience.c
index a2d8955537..82f663004e 100644
--- a/xdiff/xpatience.c
+++ b/xdiff/xpatience.c
@@ -19,6 +19,7 @@
* Davide Libenzi <davidel@xmailserver.org>
*
*/
+
#include "xinclude.h"
/*
@@ -75,7 +76,7 @@ struct hashmap {
static int is_anchor(xpparam_t const *xpp, const char *line)
{
- int i;
+ size_t i;
for (i = 0; i < xpp->anchors_nr; i++) {
if (!strncmp(line, xpp->anchors[i], strlen(xpp->anchors[i])))
return 1;
diff --git a/xdiff/xutils.c b/xdiff/xutils.c
index 9e36f24875..444a108f87 100644
--- a/xdiff/xutils.c
+++ b/xdiff/xutils.c
@@ -375,7 +375,7 @@ static int xdl_format_hunk_hdr(long s1, long c1, long s2, long c2,
nb += 3;
if (func && funclen) {
buf[nb++] = ' ';
- if (funclen > sizeof(buf) - nb - 1)
+ if ((size_t)funclen > sizeof(buf) - nb - 1)
funclen = sizeof(buf) - nb - 1;
memcpy(buf + nb, func, funclen);
nb += funclen;
@@ -437,7 +437,7 @@ void* xdl_alloc_grow_helper(void *p, long nr, long *alloc, size_t size)
{
void *tmp = NULL;
size_t n = ((LONG_MAX - 16) / 2 >= *alloc) ? 2 * *alloc + 16 : LONG_MAX;
- if (nr > n)
+ if ((size_t)nr > n)
n = nr;
if (SIZE_MAX / size >= n)
tmp = xdl_realloc(p, n * size);