summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Documentation/RelNotes/2.35.1.txt6
-rw-r--r--Documentation/RelNotes/2.36.0.txt112
-rw-r--r--Documentation/config.txt35
-rw-r--r--Documentation/config/gpg.txt4
-rw-r--r--Documentation/fetch-options.txt1
-rw-r--r--Documentation/git-cat-file.txt10
-rw-r--r--Documentation/git-hook.txt45
-rw-r--r--Documentation/git-name-rev.txt30
-rw-r--r--Documentation/gitattributes.txt11
-rw-r--r--Documentation/gitcli.txt19
-rw-r--r--Documentation/githooks.txt4
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile35
-rw-r--r--README.md14
l---------RelNotes2
-rw-r--r--add-patch.c20
-rw-r--r--apply.c42
-rw-r--r--apply.h26
-rw-r--r--builtin.h1
-rw-r--r--builtin/am.c6
-rw-r--r--builtin/cat-file.c182
-rw-r--r--builtin/checkout.c3
-rw-r--r--builtin/clone.c15
-rw-r--r--builtin/fetch.c41
-rw-r--r--builtin/gc.c3
-rw-r--r--builtin/hook.c84
-rw-r--r--builtin/merge.c8
-rw-r--r--builtin/name-rev.c30
-rw-r--r--builtin/pull.c7
-rw-r--r--builtin/rebase.c3
-rw-r--r--builtin/receive-pack.c40
-rw-r--r--builtin/reflog.c176
-rw-r--r--builtin/sparse-checkout.c3
-rw-r--r--builtin/stash.c10
-rw-r--r--builtin/update-index.c11
-rw-r--r--builtin/worktree.c26
-rw-r--r--cache.h2
-rw-r--r--command-list.txt1
-rw-r--r--commit.c31
-rw-r--r--commit.h5
-rw-r--r--compat/qsort_s.c14
-rw-r--r--compat/winansi.c6
-rw-r--r--config.c132
-rw-r--r--config.h46
-rw-r--r--config.mak.uname8
-rw-r--r--contrib/buildsystems/CMakeLists.txt2
-rw-r--r--git-compat-util.h19
-rwxr-xr-xgit-p4.py254
-rwxr-xr-xgit-send-email.perl22
-rw-r--r--git.c1
-rw-r--r--gpg-interface.c34
-rw-r--r--grep.c70
-rw-r--r--hook.c131
-rw-r--r--hook.h57
-rw-r--r--mem-pool.c26
-rw-r--r--merge-ort.c14
-rw-r--r--object-name.c8
-rw-r--r--parse-options.c13
-rw-r--r--parse-options.h15
-rw-r--r--read-cache.c8
-rw-r--r--refs.c51
-rw-r--r--refs.h7
-rw-r--r--refs/files-backend.c31
-rw-r--r--remote.c3
-rw-r--r--reset.c3
-rw-r--r--run-command.c33
-rw-r--r--run-command.h17
-rw-r--r--sequencer.c15
-rw-r--r--sparse-index.c2
-rw-r--r--split-index.c3
-rw-r--r--stable-qsort.c16
-rw-r--r--t/helper/test-csprng.c29
-rw-r--r--t/helper/test-ref-store.c3
-rw-r--r--t/helper/test-tool.c1
-rw-r--r--t/helper/test-tool.h1
-rwxr-xr-xt/t0027-auto-crlf.sh6
-rwxr-xr-xt/t1006-cat-file.sh92
-rwxr-xr-xt/t1091-sparse-checkout-builtin.sh60
-rwxr-xr-xt/t1300-config.sh118
-rwxr-xr-xt/t1800-hook.sh134
-rwxr-xr-xt/t2108-update-index-refresh-racy.sh64
-rwxr-xr-xt/t3400-rebase.sh21
-rwxr-xr-xt/t3412-rebase-root.sh2
-rwxr-xr-xt/t3701-add-interactive.sh48
-rwxr-xr-xt/t4202-log.sh2
-rwxr-xr-xt/t5516-fetch-push.sh12
-rwxr-xr-xt/t5520-pull.sh13
-rwxr-xr-xt/t5700-protocol-v1.sh15
-rwxr-xr-xt/t5702-protocol-v2.sh12
-rwxr-xr-xt/t6007-rev-list-cherry-pick-file.sh26
-rwxr-xr-xt/t6012-rev-list-simplify.sh2
-rwxr-xr-xt/t6111-rev-list-treesame.sh3
-rwxr-xr-xt/t6120-describe.sh9
-rwxr-xr-xt/t6429-merge-sequence-rename-caching.sh67
-rwxr-xr-xt/t7508-status.sh30
-rwxr-xr-xt/t8007-cat-file-textconv.sh42
-rwxr-xr-xt/t9001-send-email.sh4
-rw-r--r--t/test-lib-functions.sh33
-rw-r--r--t/test-lib.sh2
-rw-r--r--worktree.c11
-rw-r--r--wrapper.c81
102 files changed, 2300 insertions, 811 deletions
diff --git a/.gitignore b/.gitignore
index 054249b20a..f817c509ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -77,6 +77,7 @@
/git-grep
/git-hash-object
/git-help
+/git-hook
/git-http-backend
/git-http-fetch
/git-http-push
diff --git a/Documentation/RelNotes/2.35.1.txt b/Documentation/RelNotes/2.35.1.txt
new file mode 100644
index 0000000000..726ba250ef
--- /dev/null
+++ b/Documentation/RelNotes/2.35.1.txt
@@ -0,0 +1,6 @@
+Git v2.35.1 Release Notes
+=========================
+
+Git 2.35 shipped with a regression that broke use of "rebase" and
+"stash" in a secondary worktree. This maintenance release ought to
+fix it.
diff --git a/Documentation/RelNotes/2.36.0.txt b/Documentation/RelNotes/2.36.0.txt
new file mode 100644
index 0000000000..94db2458c7
--- /dev/null
+++ b/Documentation/RelNotes/2.36.0.txt
@@ -0,0 +1,112 @@
+Git 2.36 Release Notes
+======================
+
+Updates since Git 2.35
+----------------------
+
+Backward compatibility warts
+
+ * "git name-rev --stdin" has been deprecated and issues a warning
+ when used; use "git name-rev --annotate-stdin" instead.
+
+
+Note to those who build from the source
+
+ *
+
+
+UI, Workflows & Features
+
+ * Assorted updates to "git cat-file", especially "-h".
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * "git apply" (ab)used the util pointer of the string-list to keep
+ track of how each symbolic link needs to be handled, which has been
+ simplified by using strset.
+
+ * Fix a hand-rolled alloca() imitation that may have violated
+ alignment requirement of data being sorted in compatibility
+ implementation of qsort_s() and stable qsort().
+
+ * Use the parse-options API in "git reflog" command.
+
+ * The conditional inclusion mechanism of configuration files using
+ "[includeIf <condition>]" learns to base its decision on the
+ URL of the remote repository the repository interacts with.
+ (merge 399b198489 jt/conditional-config-on-remote-url later to maint).
+
+ * "git name-rev --stdin" does not behave like usual "--stdin" at
+ all. Start the process of renaming it to "--annotate-stdin".
+ (merge a2585719b3 jc/name-rev-stdin later to maint).
+
+
+Fixes since v2.35
+-----------------
+
+ * "rebase" and "stash" in secondary worktrees are broken in
+ Git 2.35.0, which has been corrected.
+
+ * "git pull --rebase" ignored the rebase.autostash configuration
+ variable when the remote history is a descendant of our history,
+ which has been corrected.
+ (merge 3013d98d7a pb/pull-rebase-autostash-fix later to maint).
+
+ * "git update-index --refresh" has been taught to deal better with
+ racy timestamps (just like "git status" already does).
+ (merge 2ede073fd2 ms/update-index-racy later to maint).
+
+ * Avoid tests that are run under GIT_TRACE2 set from failing
+ unnecessarily.
+ (merge 944d808e42 js/test-unset-trace2-parents later to maint).
+
+ * The merge-ort misbehaved when merge.renameLimit configuration is
+ set too low and failed to find all renames.
+ (merge 9ae39fef7f en/merge-ort-restart-optim-fix later to maint).
+
+ * We explain that revs come first before the pathspec among command
+ line arguments, but did not spell out that dashed options come
+ before other args, which has been corrected.
+ (merge c11f95010c tl/doc-cli-options-first later to maint).
+
+ * "git add -p" rewritten in C regressed hunk splitting in some cases,
+ which has been corrected.
+ (merge 7008ddc645 pw/add-p-hunk-split-fix later to maint).
+
+ * "git fetch --negotiate-only" is an internal command used by "git
+ push" to figure out which part of our history is missing from the
+ other side. It should never recurse into submodules even when
+ fetch.recursesubmodules configuration variable is set, nor it
+ should trigger "gc". The code has been tightened up to ensure it
+ only does common ancestry discovery and nothing else.
+ (merge de4eaae63a gc/fetch-negotiate-only-early-return later to maint).
+
+ * The code path that verifies signatures made with ssh were made to
+ work better on a system with CRLF line endings.
+ (merge caeef01ea7 fs/ssh-signing-crlf later to maint).
+
+ * "git sparse-checkout init" failed to write into $GIT_DIR/info
+ directory when the repository was created without one, which has
+ been corrected to auto-create it.
+ (merge 7f44842ac1 jt/sparse-checkout-leading-dir-fix later to maint).
+
+ * Cloning from a repository that does not yet have any branches or
+ tags but has other refs resulted in a "remote transport reported
+ error", which has been corrected.
+ (merge dccea605b6 jt/clone-not-quite-empty later to maint).
+
+ * Mark in various places in the code that the sparse index and the
+ split index features are mutually incompatible.
+ (merge 451b66c533 js/sparse-vs-split-index later to maint).
+
+ * Update the logic to compute alignment requirement for our mem-pool.
+ (merge e38bcc66d8 jc/mem-pool-alignment later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+ (merge cfc5cf428b jc/find-header later to maint).
+ (merge 40e7cfdd46 jh/p4-fix-use-of-process-error-exception later to maint).
+ (merge 727e6ea350 jh/p4-spawning-external-commands-cleanup later to maint).
+ (merge 0a6adc26e2 rs/grep-expr-cleanup later to maint).
+ (merge 4ed7dfa713 po/readme-mention-contributor-hints later to maint).
+ (merge 6046f7a91c en/plug-leaks-in-merge later to maint).
diff --git a/Documentation/config.txt b/Documentation/config.txt
index b168f02dc3..bf3e512921 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -159,6 +159,33 @@ all branches that begin with `foo/`. This is useful if your branches are
organized hierarchically and you would like to apply a configuration to
all the branches in that hierarchy.
+`hasconfig:remote.*.url:`::
+ The data that follows this keyword is taken to
+ be a pattern with standard globbing wildcards and two
+ additional ones, `**/` and `/**`, that can match multiple
+ components. The first time this keyword is seen, the rest of
+ the config files will be scanned for remote URLs (without
+ applying any values). If there exists at least one remote URL
+ that matches this pattern, the include condition is met.
++
+Files included by this option (directly or indirectly) are not allowed
+to contain remote URLs.
++
+Note that unlike other includeIf conditions, resolving this condition
+relies on information that is not yet known at the point of reading the
+condition. A typical use case is this option being present as a
+system-level or global-level config, and the remote URL being in a
+local-level config; hence the need to scan ahead when resolving this
+condition. In order to avoid the chicken-and-egg problem in which
+potentially-included files can affect whether such files are potentially
+included, Git breaks the cycle by prohibiting these files from affecting
+the resolution of these conditions (thus, prohibiting them from
+declaring remote URLs).
++
+As for the naming of this keyword, it is for forwards compatibiliy with
+a naming scheme that supports more variable-based include conditions,
+but currently Git only supports the exact keyword described above.
+
A few more notes on matching via `gitdir` and `gitdir/i`:
* Symlinks in `$GIT_DIR` are not resolved before matching.
@@ -226,6 +253,14 @@ Example
; currently checked out
[includeIf "onbranch:foo-branch"]
path = foo.inc
+
+; include only if a remote with the given URL exists (note
+; that such a URL may be provided later in a file or in a
+; file read after this file is read, as seen in this example)
+[includeIf "hasconfig:remote.*.url:https://example.com/**"]
+ path = foo.inc
+[remote "origin"]
+ url = https://example.com/git
----
Values
diff --git a/Documentation/config/gpg.txt b/Documentation/config/gpg.txt
index 0cb189a077..86892ada77 100644
--- a/Documentation/config/gpg.txt
+++ b/Documentation/config/gpg.txt
@@ -37,7 +37,7 @@ gpg.minTrustLevel::
gpg.ssh.defaultKeyCommand::
This command that will be run when user.signingkey is not set and a ssh
signature is requested. On successful exit a valid ssh public key is
- expected in the first line of its output. To automatically use the first
+ expected in the first line of its output. To automatically use the first
available key from your ssh-agent set this to "ssh-add -L".
gpg.ssh.allowedSignersFile::
@@ -66,7 +66,7 @@ This way only committers with an already valid key can add or change keys in the
+
Since OpensSSH 8.8 this file allows specifying a key lifetime using valid-after &
valid-before options. Git will mark signatures as valid if the signing key was
-valid at the time of the signatures creation. This allows users to change a
+valid at the time of the signature's creation. This allows users to change a
signing key without invalidating all previously made signatures.
+
Using a SSH CA key with the cert-authority option
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index e967ff1874..f903683189 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -71,6 +71,7 @@ configuration variables documented in linkgit:git-config[1], and the
ancestors of the provided `--negotiation-tip=*` arguments,
which we have in common with the server.
+
+This is incompatible with `--recurse-submodules=[yes|on-demand]`.
Internally this is used to implement the `push.negotiate` option, see
linkgit:git-config[1].
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index 27b27e2b30..bef76f4dd0 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -9,8 +9,14 @@ git-cat-file - Provide content or type and size information for repository objec
SYNOPSIS
--------
[verse]
-'git cat-file' (-t [--allow-unknown-type]| -s [--allow-unknown-type]| -e | -p | <type> | --textconv | --filters ) [--path=<path>] <object>
-'git cat-file' (--batch[=<format>] | --batch-check[=<format>]) [ --textconv | --filters ] [--follow-symlinks]
+'git cat-file' <type> <object>
+'git cat-file' (-e | -p) <object>
+'git cat-file' (-t | -s) [--allow-unknown-type] <object>
+'git cat-file' (--batch | --batch-check) [--batch-all-objects]
+ [--buffer] [--follow-symlinks] [--unordered]
+ [--textconv | --filters]
+'git cat-file' (--textconv | --filters)
+ [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]
DESCRIPTION
-----------
diff --git a/Documentation/git-hook.txt b/Documentation/git-hook.txt
new file mode 100644
index 0000000000..77c3a8ad90
--- /dev/null
+++ b/Documentation/git-hook.txt
@@ -0,0 +1,45 @@
+git-hook(1)
+===========
+
+NAME
+----
+git-hook - Run git hooks
+
+SYNOPSIS
+--------
+[verse]
+'git hook' run [--ignore-missing] <hook-name> [-- <hook-args>]
+
+DESCRIPTION
+-----------
+
+A command interface to running git hooks (see linkgit:githooks[5]),
+for use by other scripted git commands.
+
+SUBCOMMANDS
+-----------
+
+run::
+ Run the `<hook-name>` hook. See linkgit:githooks[5] for
+ supported hook names.
++
+
+Any positional arguments to the hook should be passed after a
+mandatory `--` (or `--end-of-options`, see linkgit:gitcli[7]). See
+linkgit:githooks[5] for arguments hooks might expect (if any).
+
+OPTIONS
+-------
+
+--ignore-missing::
+ Ignore any missing hook by quietly returning zero. Used for
+ tools that want to do a blind one-shot run of a hook that may
+ or may not be present.
+
+SEE ALSO
+--------
+linkgit:githooks[5]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt
index 5cb0eb0855..ec8a27ce8b 100644
--- a/Documentation/git-name-rev.txt
+++ b/Documentation/git-name-rev.txt
@@ -42,11 +42,37 @@ OPTIONS
--all::
List all commits reachable from all refs
---stdin::
+--annotate-stdin::
Transform stdin by substituting all the 40-character SHA-1
hexes (say $hex) with "$hex ($rev_name)". When used with
--name-only, substitute with "$rev_name", omitting $hex
- altogether. Intended for the scripter's use.
+ altogether.
++
+For example:
++
+-----------
+$ cat sample.txt
+
+An abbreviated revision 2ae0a9cb82 will not be substituted.
+The full name after substitution is 2ae0a9cb8298185a94e5998086f380a355dd8907,
+while its tree object is 70d105cc79e63b81cfdcb08a15297c23e60b07ad
+
+$ git name-rev --annotate-stdin <sample.txt
+
+An abbreviated revision 2ae0a9cb82 will not be substituted.
+The full name after substitution is 2ae0a9cb8298185a94e5998086f380a355dd8907 (master),
+while its tree object is 70d105cc79e63b81cfdcb08a15297c23e60b07ad
+
+$ git name-rev --name-only --annotate-stdin <sample.txt
+
+An abbreviated revision 2ae0a9cb82 will not be substituted.
+The full name after substitution is master,
+while its tree object is 70d105cc79e63b81cfdcb08a15297c23e60b07ad
+-----------
+
+--stdin::
+ This option is deprecated in favor of 'git name-rev --annotate-stdin'.
+ They are functionally equivalent.
--name-only::
Instead of printing both the SHA-1 and the name, print only
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 83fd4e19a4..60984a4682 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -160,11 +160,12 @@ unspecified.
^^^^^
This attribute sets a specific line-ending style to be used in the
-working directory. It enables end-of-line conversion without any
-content checks, effectively setting the `text` attribute. Note that
-setting this attribute on paths which are in the index with CRLF line
-endings may make the paths to be considered dirty. Adding the path to
-the index again will normalize the line endings in the index.
+working directory. This attribute has effect only if the `text`
+attribute is set or unspecified, or if it is set to `auto` and the file
+is detected as text. Note that setting this attribute on paths which
+are in the index with CRLF line endings may make the paths to be
+considered dirty. Adding the path to the index again will normalize the
+line endings in the index.
Set to string value "crlf"::
diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt
index 92e4ba6a2f..1819a5a185 100644
--- a/Documentation/gitcli.txt
+++ b/Documentation/gitcli.txt
@@ -19,6 +19,15 @@ Many commands take revisions (most often "commits", but sometimes
"tree-ish", depending on the context and command) and paths as their
arguments. Here are the rules:
+ * Options come first and then args.
+ A subcommand may take dashed options (which may take their own
+ arguments, e.g. "--max-parents 2") and arguments. You SHOULD
+ give dashed options first and then arguments. Some commands may
+ accept dashed options after you have already gave non-option
+ arguments (which may make the command ambiguous), but you should
+ not rely on it (because eventually we may find a way to fix
+ these ambiguity by enforcing the "options then args" rule).
+
* Revisions come first and then paths.
E.g. in `git diff v1.0 v2.0 arch/x86 include/asm-x86`,
`v1.0` and `v2.0` are revisions and `arch/x86` and `include/asm-x86`
@@ -72,24 +81,24 @@ you will.
Here are the rules regarding the "flags" that you should follow when you are
scripting Git:
- * it's preferred to use the non-dashed form of Git commands, which means that
+ * It's preferred to use the non-dashed form of Git commands, which means that
you should prefer `git foo` to `git-foo`.
- * splitting short options to separate words (prefer `git foo -a -b`
+ * Splitting short options to separate words (prefer `git foo -a -b`
to `git foo -ab`, the latter may not even work).
- * when a command-line option takes an argument, use the 'stuck' form. In
+ * When a command-line option takes an argument, use the 'stuck' form. In
other words, write `git foo -oArg` instead of `git foo -o Arg` for short
options, and `git foo --long-opt=Arg` instead of `git foo --long-opt Arg`
for long options. An option that takes optional option-argument must be
written in the 'stuck' form.
- * when you give a revision parameter to a command, make sure the parameter is
+ * When you give a revision parameter to a command, make sure the parameter is
not ambiguous with a name of a file in the work tree. E.g. do not write
`git log -1 HEAD` but write `git log -1 HEAD --`; the former will not work
if you happen to have a file called `HEAD` in the work tree.
- * many commands allow a long option `--option` to be abbreviated
+ * Many commands allow a long option `--option` to be abbreviated
only to their unique prefix (e.g. if there is no other option
whose name begins with `opt`, you may be able to spell `--opt` to
invoke the `--option` flag), but you should fully spell them out
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index b51959ff94..a16e62bc8c 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -698,6 +698,10 @@ and "0" meaning they were not.
Only one parameter should be set to "1" when the hook runs. The hook
running passing "1", "1" should not be possible.
+SEE ALSO
+--------
+linkgit:git-hook[1]
+
GIT
---
Part of the linkgit:git[1] suite
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 1467b45c1d..f964f9ba7d 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.35.0
+DEF_VER=v2.35.GIT
LF='
'
diff --git a/Makefile b/Makefile
index 5580859afd..186f9ab619 100644
--- a/Makefile
+++ b/Makefile
@@ -234,6 +234,14 @@ all::
# Define NO_TRUSTABLE_FILEMODE if your filesystem may claim to support
# the executable mode bit, but doesn't really do so.
#
+# Define CSPRNG_METHOD to "arc4random" if your system has arc4random and
+# arc4random_buf, "libbsd" if your system has those functions from libbsd,
+# "getrandom" if your system has getrandom, "getentropy" if your system has
+# getentropy, "rtlgenrandom" for RtlGenRandom (Windows only), or "openssl" if
+# you'd want to use the OpenSSL CSPRNG. You may set multiple options with
+# spaces, in which case a suitable option will be chosen. If unset or set to
+# anything else, defaults to using "/dev/urandom".
+#
# Define NEEDS_MODE_TRANSLATION if your OS strays from the typical file type
# bits in mode values (e.g. z/OS defines I_SFMT to 0xFF000000 as opposed to the
# usual 0xF000).
@@ -693,6 +701,7 @@ TEST_BUILTINS_OBJS += test-bloom.o
TEST_BUILTINS_OBJS += test-chmtime.o
TEST_BUILTINS_OBJS += test-config.o
TEST_BUILTINS_OBJS += test-crontab.o
+TEST_BUILTINS_OBJS += test-csprng.o
TEST_BUILTINS_OBJS += test-ctype.o
TEST_BUILTINS_OBJS += test-date.o
TEST_BUILTINS_OBJS += test-delta.o
@@ -1109,6 +1118,7 @@ BUILTIN_OBJS += builtin/get-tar-commit-id.o
BUILTIN_OBJS += builtin/grep.o
BUILTIN_OBJS += builtin/hash-object.o
BUILTIN_OBJS += builtin/help.o
+BUILTIN_OBJS += builtin/hook.o
BUILTIN_OBJS += builtin/index-pack.o
BUILTIN_OBJS += builtin/init-db.o
BUILTIN_OBJS += builtin/interpret-trailers.o
@@ -1908,6 +1918,31 @@ ifdef HAVE_GETDELIM
BASIC_CFLAGS += -DHAVE_GETDELIM
endif
+ifneq ($(findstring arc4random,$(CSPRNG_METHOD)),)
+ BASIC_CFLAGS += -DHAVE_ARC4RANDOM
+endif
+
+ifneq ($(findstring libbsd,$(CSPRNG_METHOD)),)
+ BASIC_CFLAGS += -DHAVE_ARC4RANDOM_LIBBSD
+ EXTLIBS += -lbsd
+endif
+
+ifneq ($(findstring getrandom,$(CSPRNG_METHOD)),)
+ BASIC_CFLAGS += -DHAVE_GETRANDOM
+endif
+
+ifneq ($(findstring getentropy,$(CSPRNG_METHOD)),)
+ BASIC_CFLAGS += -DHAVE_GETENTROPY
+endif
+
+ifneq ($(findstring rtlgenrandom,$(CSPRNG_METHOD)),)
+ BASIC_CFLAGS += -DHAVE_RTLGENRANDOM
+endif
+
+ifneq ($(findstring openssl,$(CSPRNG_METHOD)),)
+ BASIC_CFLAGS += -DHAVE_OPENSSL_CSPRNG
+endif
+
ifneq ($(PROCFS_EXECUTABLE_PATH),)
procfs_executable_path_SQ = $(subst ','\'',$(PROCFS_EXECUTABLE_PATH))
BASIC_CFLAGS += '-DPROCFS_EXECUTABLE_PATH="$(procfs_executable_path_SQ)"'
diff --git a/README.md b/README.md
index f6f43e78de..7ce4f05bae 100644
--- a/README.md
+++ b/README.md
@@ -32,10 +32,16 @@ installed).
The user discussion and development of Git take place on the Git
mailing list -- everyone is welcome to post bug reports, feature
requests, comments and patches to git@vger.kernel.org (read
-[Documentation/SubmittingPatches][] for instructions on patch submission).
+[Documentation/SubmittingPatches][] for instructions on patch submission
+and [Documentation/CodingGuidelines][]).
+
+Those wishing to help with error message, usage and informational message
+string translations (localization l10) should see [po/README.md][]
+(a `po` file is a Portable Object file that holds the translations).
+
To subscribe to the list, send an email with just "subscribe git" in
-the body to majordomo@vger.kernel.org. The mailing list archives are
-available at <https://lore.kernel.org/git/>,
+the body to majordomo@vger.kernel.org (not the Git list). The mailing
+list archives are available at <https://lore.kernel.org/git/>,
<http://marc.info/?l=git> and other archival sites.
Issues which are security relevant should be disclosed privately to
@@ -64,3 +70,5 @@ and the name as (depending on your mood):
[Documentation/giteveryday.txt]: Documentation/giteveryday.txt
[Documentation/gitcvs-migration.txt]: Documentation/gitcvs-migration.txt
[Documentation/SubmittingPatches]: Documentation/SubmittingPatches
+[Documentation/CodingGuidelines]: Documentation/CodingGuidelines
+[po/README.md]: po/README.md
diff --git a/RelNotes b/RelNotes
index c27c205001..81052262e0 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.35.0.txt \ No newline at end of file
+Documentation/RelNotes/2.36.0.txt \ No newline at end of file
diff --git a/add-patch.c b/add-patch.c
index 573eef0cc4..55d719f784 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -383,6 +383,17 @@ static int is_octal(const char *p, size_t len)
return 1;
}
+static void complete_file(char marker, struct hunk *hunk)
+{
+ if (marker == '-' || marker == '+')
+ /*
+ * Last hunk ended in non-context line (i.e. it
+ * appended lines to the file, so there are no
+ * trailing context lines).
+ */
+ hunk->splittable_into++;
+}
+
static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
{
struct strvec args = STRVEC_INIT;
@@ -472,6 +483,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
eol = pend;
if (starts_with(p, "diff ")) {
+ complete_file(marker, hunk);
ALLOC_GROW_BY(s->file_diff, s->file_diff_nr, 1,
file_diff_alloc);
file_diff = s->file_diff + s->file_diff_nr - 1;
@@ -598,13 +610,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
file_diff->hunk->colored_end = hunk->colored_end;
}
}
-
- if (marker == '-' || marker == '+')
- /*
- * Last hunk ended in non-context line (i.e. it appended lines
- * to the file, so there are no trailing context lines).
- */
- hunk->splittable_into++;
+ complete_file(marker, hunk);
/* non-colored shorter than colored? */
if (colored_p != colored_pend) {
diff --git a/apply.c b/apply.c
index 7ffadc3b17..f66d48ba2f 100644
--- a/apply.c
+++ b/apply.c
@@ -103,7 +103,8 @@ int init_apply_state(struct apply_state *state,
state->linenr = 1;
string_list_init_nodup(&state->fn_table);
string_list_init_nodup(&state->limit_by_name);
- string_list_init_nodup(&state->symlink_changes);
+ strset_init(&state->removed_symlinks);
+ strset_init(&state->kept_symlinks);
strbuf_init(&state->root, 0);
git_apply_config();
@@ -117,7 +118,8 @@ int init_apply_state(struct apply_state *state,
void clear_apply_state(struct apply_state *state)
{
string_list_clear(&state->limit_by_name, 0);
- string_list_clear(&state->symlink_changes, 0);
+ strset_clear(&state->removed_symlinks);
+ strset_clear(&state->kept_symlinks);
strbuf_release(&state->root);
/* &state->fn_table is cleared at the end of apply_patch() */
@@ -3814,59 +3816,31 @@ static int check_to_create(struct apply_state *state,
return 0;
}
-static uintptr_t register_symlink_changes(struct apply_state *state,
- const char *path,
- uintptr_t what)
-{
- struct string_list_item *ent;
-
- ent = string_list_lookup(&state->symlink_changes, path);
- if (!ent) {
- ent = string_list_insert(&state->symlink_changes, path);
- ent->util = (void *)0;
- }
- ent->util = (void *)(what | ((uintptr_t)ent->util));
- return (uintptr_t)ent->util;
-}
-
-static uintptr_t check_symlink_changes(struct apply_state *state, const char *path)
-{
- struct string_list_item *ent;
-
- ent = string_list_lookup(&state->symlink_changes, path);
- if (!ent)
- return 0;
- return (uintptr_t)ent->util;
-}
-
static void prepare_symlink_changes(struct apply_state *state, struct patch *patch)
{
for ( ; patch; patch = patch->next) {
if ((patch->old_name && S_ISLNK(patch->old_mode)) &&
(patch->is_rename || patch->is_delete))
/* the symlink at patch->old_name is removed */
- register_symlink_changes(state, patch->old_name, APPLY_SYMLINK_GOES_AWAY);
+ strset_add(&state->removed_symlinks, patch->old_name);
if (patch->new_name && S_ISLNK(patch->new_mode))
/* the symlink at patch->new_name is created or remains */
- register_symlink_changes(state, patch->new_name, APPLY_SYMLINK_IN_RESULT);
+ strset_add(&state->kept_symlinks, patch->new_name);
}
}
static int path_is_beyond_symlink_1(struct apply_state *state, struct strbuf *name)
{
do {
- unsigned int change;
-
while (--name->len && name->buf[name->len] != '/')
; /* scan backwards */
if (!name->len)
break;
name->buf[name->len] = '\0';
- change = check_symlink_changes(state, name->buf);
- if (change & APPLY_SYMLINK_IN_RESULT)
+ if (strset_contains(&state->kept_symlinks, name->buf))
return 1;
- if (change & APPLY_SYMLINK_GOES_AWAY)
+ if (strset_contains(&state->removed_symlinks, name->buf))
/*
* This cannot be "return 0", because we may
* see a new one created at a higher level.
diff --git a/apply.h b/apply.h
index 16202da160..4052da50c0 100644
--- a/apply.h
+++ b/apply.h
@@ -4,6 +4,7 @@
#include "hash.h"
#include "lockfile.h"
#include "string-list.h"
+#include "strmap.h"
struct repository;
@@ -25,20 +26,6 @@ enum apply_verbosity {
verbosity_verbose = 1
};
-/*
- * We need to keep track of how symlinks in the preimage are
- * manipulated by the patches. A patch to add a/b/c where a/b
- * is a symlink should not be allowed to affect the directory
- * the symlink points at, but if the same patch removes a/b,
- * it is perfectly fine, as the patch removes a/b to make room
- * to create a directory a/b so that a/b/c can be created.
- *
- * See also "struct string_list symlink_changes" in "struct
- * apply_state".
- */
-#define APPLY_SYMLINK_GOES_AWAY 01
-#define APPLY_SYMLINK_IN_RESULT 02
-
struct apply_state {
const char *prefix;
@@ -86,7 +73,16 @@ struct apply_state {
/* Various "current state" */
int linenr; /* current line number */
- struct string_list symlink_changes; /* we have to track symlinks */
+ /*
+ * We need to keep track of how symlinks in the preimage are
+ * manipulated by the patches. A patch to add a/b/c where a/b
+ * is a symlink should not be allowed to affect the directory
+ * the symlink points at, but if the same patch removes a/b,
+ * it is perfectly fine, as the patch removes a/b to make room
+ * to create a directory a/b so that a/b/c can be created.
+ */
+ struct strset removed_symlinks;
+ struct strset kept_symlinks;
/*
* For "diff-stat" like behaviour, we keep track of the biggest change
diff --git a/builtin.h b/builtin.h
index 8a58743ed6..83379f3832 100644
--- a/builtin.h
+++ b/builtin.h
@@ -164,6 +164,7 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
int cmd_grep(int argc, const char **argv, const char *prefix);
int cmd_hash_object(int argc, const char **argv, const char *prefix);
int cmd_help(int argc, const char **argv, const char *prefix);
+int cmd_hook(int argc, const char **argv, const char *prefix);
int cmd_index_pack(int argc, const char **argv, const char *prefix);
int cmd_init_db(int argc, const char **argv, const char *prefix);
int cmd_interpret_trailers(int argc, const char **argv, const char *prefix);
diff --git a/builtin/am.c b/builtin/am.c
index b6be1f1cb1..7de2c89ef2 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -474,7 +474,7 @@ static int run_applypatch_msg_hook(struct am_state *state)
int ret;
assert(state->msg);
- ret = run_hook_le(NULL, "applypatch-msg", am_path(state, "final-commit"), NULL);
+ ret = run_hooks_l("applypatch-msg", am_path(state, "final-commit"), NULL);
if (!ret) {
FREE_AND_NULL(state->msg);
@@ -1636,7 +1636,7 @@ static void do_commit(const struct am_state *state)
const char *reflog_msg, *author, *committer = NULL;
struct strbuf sb = STRBUF_INIT;
- if (run_hook_le(NULL, "pre-applypatch", NULL))
+ if (run_hooks("pre-applypatch"))
exit(1);
if (write_cache_as_tree(&tree, 0, NULL))
@@ -1688,7 +1688,7 @@ static void do_commit(const struct am_state *state)
fclose(fp);
}
- run_hook_le(NULL, "post-applypatch", NULL);
+ run_hooks("post-applypatch");
strbuf_release(&sb);
}
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index d94050e6c1..7b3f42950e 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -73,14 +73,17 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
struct object_info oi = OBJECT_INFO_INIT;
struct strbuf sb = STRBUF_INIT;
unsigned flags = OBJECT_INFO_LOOKUP_REPLACE;
+ unsigned get_oid_flags = GET_OID_RECORD_PATH | GET_OID_ONLY_TO_DIE;
const char *path = force_path;
+ const int opt_cw = (opt == 'c' || opt == 'w');
+ if (!path && opt_cw)
+ get_oid_flags |= GET_OID_REQUIRE_PATH;
if (unknown_type)
flags |= OBJECT_INFO_ALLOW_UNKNOWN_TYPE;
- if (get_oid_with_context(the_repository, obj_name,
- GET_OID_RECORD_PATH,
- &oid, &obj_context))
+ if (get_oid_with_context(the_repository, obj_name, get_oid_flags, &oid,
+ &obj_context))
die("Not a valid object name %s", obj_name);
if (!path)
@@ -112,9 +115,6 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
return !has_object_file(&oid);
case 'w':
- if (!path)
- die("git cat-file --filters %s: <object> must be "
- "<sha1:path>", obj_name);
if (filter_object(path, obj_context.mode,
&oid, &buf, &size))
@@ -122,10 +122,6 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
break;
case 'c':
- if (!path)
- die("git cat-file --textconv %s: <object> must be <sha1:path>",
- obj_name);
-
if (textconv_object(the_repository, path, obj_context.mode,
&oid, 1, &buf, &size))
break;
@@ -618,12 +614,6 @@ static int batch_objects(struct batch_options *opt)
return retval;
}
-static const char * const cat_file_usage[] = {
- N_("git cat-file (-t [--allow-unknown-type] | -s [--allow-unknown-type] | -e | -p | <type> | --textconv | --filters) [--path=<path>] <object>"),
- N_("git cat-file (--batch[=<format>] | --batch-check[=<format>]) [--follow-symlinks] [--textconv | --filters]"),
- NULL
-};
-
static int git_cat_file_config(const char *var, const char *value, void *cb)
{
if (userdiff_config(var, value) < 0)
@@ -654,90 +644,138 @@ static int batch_option_callback(const struct option *opt,
int cmd_cat_file(int argc, const char **argv, const char *prefix)
{
int opt = 0;
+ int opt_cw = 0;
+ int opt_epts = 0;
const char *exp_type = NULL, *obj_name = NULL;
struct batch_options batch = {0};
int unknown_type = 0;
+ const char * const usage[] = {
+ N_("git cat-file <type> <object>"),
+ N_("git cat-file (-e | -p) <object>"),
+ N_("git cat-file (-t | -s) [--allow-unknown-type] <object>"),
+ N_("git cat-file (--batch | --batch-check) [--batch-all-objects]\n"
+ " [--buffer] [--follow-symlinks] [--unordered]\n"
+ " [--textconv | --filters]"),
+ N_("git cat-file (--textconv | --filters)\n"
+ " [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"),
+ NULL
+ };
const struct option options[] = {
- OPT_GROUP(N_("<type> can be one of: blob, tree, commit, tag")),
- OPT_CMDMODE('t', NULL, &opt, N_("show object type"), 't'),
- OPT_CMDMODE('s', NULL, &opt, N_("show object size"), 's'),
+ /* Simple queries */
+ OPT_GROUP(N_("Check object existence or emit object contents")),
OPT_CMDMODE('e', NULL, &opt,
- N_("exit with zero when there's no error"), 'e'),
- OPT_CMDMODE('p', NULL, &opt, N_("pretty-print object's content"), 'p'),
- OPT_CMDMODE(0, "textconv", &opt,
- N_("for blob objects, run textconv on object's content"), 'c'),
- OPT_CMDMODE(0, "filters", &opt,
- N_("for blob objects, run filters on object's content"), 'w'),
- OPT_STRING(0, "path", &force_path, N_("blob"),
- N_("use a specific path for --textconv/--filters")),
+ N_("check if <object> exists"), 'e'),
+ OPT_CMDMODE('p', NULL, &opt, N_("pretty-print <object> content"), 'p'),
+
+ OPT_GROUP(N_("Emit [broken] object attributes")),
+ OPT_CMDMODE('t', NULL, &opt, N_("show object type (one of 'blob', 'tree', 'commit', 'tag', ...)"), 't'),
+ OPT_CMDMODE('s', NULL, &opt, N_("show object size"), 's'),
OPT_BOOL(0, "allow-unknown-type", &unknown_type,
N_("allow -s and -t to work with broken/corrupt objects")),
- OPT_BOOL(0, "buffer", &batch.buffer_output, N_("buffer --batch output")),
- OPT_CALLBACK_F(0, "batch", &batch, "format",
- N_("show info and content of objects fed from the standard input"),
+ /* Batch mode */
+ OPT_GROUP(N_("Batch objects requested on stdin (or --batch-all-objects)")),
+ OPT_CALLBACK_F(0, "batch", &batch, N_("format"),
+ N_("show full <object> or <rev> contents"),
PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
batch_option_callback),
- OPT_CALLBACK_F(0, "batch-check", &batch, "format",
- N_("show info about objects fed from the standard input"),
+ OPT_CALLBACK_F(0, "batch-check", &batch, N_("format"),
+ N_("like --batch, but don't emit <contents>"),
PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
batch_option_callback),
+ OPT_CMDMODE(0, "batch-all-objects", &opt,
+ N_("with --batch[-check]: ignores stdin, batches all known objects"), 'b'),
+ /* Batch-specific options */
+ OPT_GROUP(N_("Change or optimize batch output")),
+ OPT_BOOL(0, "buffer", &batch.buffer_output, N_("buffer --batch output")),
OPT_BOOL(0, "follow-symlinks", &batch.follow_symlinks,
- N_("follow in-tree symlinks (used with --batch or --batch-check)")),
- OPT_BOOL(0, "batch-all-objects", &batch.all_objects,
- N_("show all objects with --batch or --batch-check")),
+ N_("follow in-tree symlinks")),
OPT_BOOL(0, "unordered", &batch.unordered,
- N_("do not order --batch-all-objects output")),
+ N_("do not order objects before emitting them")),
+ /* Textconv options, stand-ole*/
+ OPT_GROUP(N_("Emit object (blob or tree) with conversion or filter (stand-alone, or with batch)")),
+ OPT_CMDMODE(0, "textconv", &opt,
+ N_("run textconv on object's content"), 'c'),
+ OPT_CMDMODE(0, "filters", &opt,
+ N_("run filters on object's content"), 'w'),
+ OPT_STRING(0, "path", &force_path, N_("blob|tree"),
+ N_("use a <path> for (--textconv | --filters); Not with 'batch'")),
OPT_END()
};
git_config(git_cat_file_config, NULL);
batch.buffer_output = -1;
- argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0);
-
- if (opt) {
- if (batch.enabled && (opt == 'c' || opt == 'w'))
- batch.cmdmode = opt;
- else if (argc == 1)
- obj_name = argv[0];
- else
- usage_with_options(cat_file_usage, options);
- }
- if (!opt && !batch.enabled) {
- if (argc == 2) {
- exp_type = argv[0];
- obj_name = argv[1];
- } else
- usage_with_options(cat_file_usage, options);
- }
- if (batch.enabled) {
- if (batch.cmdmode != opt || argc)
- usage_with_options(cat_file_usage, options);
- if (batch.cmdmode && batch.all_objects)
- die("--batch-all-objects cannot be combined with "
- "--textconv nor with --filters");
- }
- if ((batch.follow_symlinks || batch.all_objects) && !batch.enabled) {
- usage_with_options(cat_file_usage, options);
- }
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
+ opt_cw = (opt == 'c' || opt == 'w');
+ opt_epts = (opt == 'e' || opt == 'p' || opt == 't' || opt == 's');
- if (force_path && opt != 'c' && opt != 'w') {
- error("--path=<path> needs --textconv or --filters");
- usage_with_options(cat_file_usage, options);
- }
+ /* --batch-all-objects? */
+ if (opt == 'b')
+ batch.all_objects = 1;
- if (force_path && batch.enabled) {
- error("options '--path=<path>' and '--batch' cannot be used together");
- usage_with_options(cat_file_usage, options);
- }
+ /* Option compatibility */
+ if (force_path && !opt_cw)
+ usage_msg_optf(_("'%s=<%s>' needs '%s' or '%s'"),
+ usage, options,
+ "--path", _("path|tree-ish"), "--filters",
+ "--textconv");
+ /* Option compatibility with batch mode */
+ if (batch.enabled)
+ ;
+ else if (batch.follow_symlinks)
+ usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
+ "--follow-symlinks");
+ else if (batch.buffer_output >= 0)
+ usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
+ "--buffer");
+ else if (batch.all_objects)
+ usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
+ "--batch-all-objects");
+
+ /* Batch defaults */
if (batch.buffer_output < 0)
batch.buffer_output = batch.all_objects;
- if (batch.enabled)
+ /* Return early if we're in batch mode? */
+ if (batch.enabled) {
+ if (opt_cw)
+ batch.cmdmode = opt;
+ else if (opt && opt != 'b')
+ usage_msg_optf(_("'-%c' is incompatible with batch mode"),
+ usage, options, opt);
+ else if (argc)
+ usage_msg_opt(_("batch modes take no arguments"), usage,
+ options);
+
return batch_objects(&batch);
+ }
+
+ if (opt) {
+ if (!argc && opt == 'c')
+ usage_msg_optf(_("<rev> required with '%s'"),
+ usage, options, "--textconv");
+ else if (!argc && opt == 'w')
+ usage_msg_optf(_("<rev> required with '%s'"),
+ usage, options, "--filters");
+ else if (!argc && opt_epts)
+ usage_msg_optf(_("<object> required with '-%c'"),
+ usage, options, opt);
+ else if (argc == 1)
+ obj_name = argv[0];
+ else
+ usage_msg_opt(_("too many arguments"), usage, options);
+ } else if (!argc) {
+ usage_with_options(usage, options);
+ } else if (argc != 2) {
+ usage_msg_optf(_("only two arguments allowed in <type> <object> mode, not %d"),
+ usage, options, argc);
+ } else if (argc) {
+ exp_type = argv[0];
+ obj_name = argv[1];
+ }
if (unknown_type && opt != 't' && opt != 's')
die("git cat-file --allow-unknown-type: use with -s or -t");
diff --git a/builtin/checkout.c b/builtin/checkout.c
index cc804ba8e1..8f010412a9 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -9,6 +9,7 @@
#include "config.h"
#include "diff.h"
#include "dir.h"
+#include "hook.h"
#include "ll-merge.h"
#include "lockfile.h"
#include "merge-recursive.h"
@@ -114,7 +115,7 @@ static void branch_info_release(struct branch_info *info)
static int post_checkout_hook(struct commit *old_commit, struct commit *new_commit,
int changed)
{
- return run_hook_le(NULL, "post-checkout",
+ return run_hooks_l("post-checkout",
oid_to_hex(old_commit ? &old_commit->object.oid : null_oid()),
oid_to_hex(new_commit ? &new_commit->object.oid : null_oid()),
changed ? "1" : "0", NULL);
diff --git a/builtin/clone.c b/builtin/clone.c
index 727e16e0ae..9c29093b35 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -32,6 +32,7 @@
#include "connected.h"
#include "packfile.h"
#include "list-objects-filter-options.h"
+#include "hook.h"
/*
* Overall FIXMEs:
@@ -705,7 +706,7 @@ static int checkout(int submodule_progress)
if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
- err |= run_hook_le(NULL, "post-checkout", oid_to_hex(null_oid()),
+ err |= run_hooks_l("post-checkout", oid_to_hex(null_oid()),
oid_to_hex(&oid), "1", NULL);
if (!err && (option_recurse_submodules.nr > 0)) {
@@ -862,7 +863,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
const struct ref *refs, *remote_head;
struct ref *remote_head_points_at = NULL;
const struct ref *our_head_points_at;
- struct ref *mapped_refs;
+ struct ref *mapped_refs = NULL;
const struct ref *ref;
struct strbuf key = STRBUF_INIT;
struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
@@ -1184,7 +1185,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
refs = transport_get_remote_refs(transport, &transport_ls_refs_options);
- if (refs) {
+ if (refs)
+ mapped_refs = wanted_peer_refs(refs, &remote->fetch);
+
+ if (mapped_refs) {
int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
/*
@@ -1193,8 +1197,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
*/
initialize_repository_version(hash_algo, 1);
repo_set_hash_algo(the_repository, hash_algo);
-
- mapped_refs = wanted_peer_refs(refs, &remote->fetch);
/*
* transport_get_remote_refs() may return refs with null sha-1
* in mapped_refs (see struct transport->get_refs_list
@@ -1240,7 +1242,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
option_branch, remote_name);
warning(_("You appear to have cloned an empty repository."));
- mapped_refs = NULL;
our_head_points_at = NULL;
remote_head_points_at = NULL;
remote_head = NULL;
@@ -1271,7 +1272,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (is_local)
clone_local(path, git_dir);
- else if (refs && complete_refs_before_fetch) {
+ else if (mapped_refs && complete_refs_before_fetch) {
if (transport_fetch_refs(transport, mapped_refs))
die(_("remote transport reported error"));
}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 5f06b21f8e..5b3b18a72f 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -76,6 +76,7 @@ static struct transport *gtransport;
static struct transport *gsecondary;
static const char *submodule_prefix = "";
static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
+static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
static int shown_url = 0;
static struct refspec refmap = REFSPEC_INIT_FETCH;
@@ -167,7 +168,7 @@ static struct option builtin_fetch_options[] = {
N_("prune remote-tracking branches no longer on remote")),
OPT_BOOL('P', "prune-tags", &prune_tags,
N_("prune local tags no longer on remote and clobber changed tags")),
- OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules, N_("on-demand"),
+ OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules_cli, N_("on-demand"),
N_("control recursive fetching of submodules"),
PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
OPT_BOOL(0, "dry-run", &dry_run,
@@ -2019,6 +2020,28 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix,
builtin_fetch_options, builtin_fetch_usage, 0);
+
+ if (recurse_submodules_cli != RECURSE_SUBMODULES_DEFAULT)
+ recurse_submodules = recurse_submodules_cli;
+
+ if (negotiate_only) {
+ switch (recurse_submodules_cli) {
+ case RECURSE_SUBMODULES_OFF:
+ case RECURSE_SUBMODULES_DEFAULT:
+ /*
+ * --negotiate-only should never recurse into
+ * submodules. Skip it by setting recurse_submodules to
+ * RECURSE_SUBMODULES_OFF.
+ */
+ recurse_submodules = RECURSE_SUBMODULES_OFF;
+ break;
+
+ default:
+ die(_("options '%s' and '%s' cannot be used together"),
+ "--negotiate-only", "--recurse-submodules");
+ }
+ }
+
if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
int *sfjc = submodule_fetch_jobs_config == -1
? &submodule_fetch_jobs_config : NULL;
@@ -2100,7 +2123,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
gtransport->smart_options->acked_commits = &acked_commits;
} else {
warning(_("protocol does not support --negotiate-only, exiting"));
- return 1;
+ result = 1;
+ goto cleanup;
}
if (server_options.nr)
gtransport->server_options = &server_options;
@@ -2156,7 +2180,16 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
strvec_clear(&options);
}
- string_list_clear(&list, 0);
+ /*
+ * Skip irrelevant tasks because we know objects were not
+ * fetched.
+ *
+ * NEEDSWORK: as a future optimization, we can return early
+ * whenever objects were not fetched e.g. if we already have all
+ * of them.
+ */
+ if (negotiate_only)
+ goto cleanup;
prepare_repo_settings(the_repository);
if (fetch_write_commit_graph > 0 ||
@@ -2175,5 +2208,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
if (enable_auto_gc)
run_auto_maintenance(verbosity < 0);
+ cleanup:
+ string_list_clear(&list, 0);
return result;
}
diff --git a/builtin/gc.c b/builtin/gc.c
index 8e60ef1eab..ffaf0daf5d 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -32,6 +32,7 @@
#include "remote.h"
#include "object-store.h"
#include "exec-cmd.h"
+#include "hook.h"
#define FAILED_RUN "failed to run %s"
@@ -394,7 +395,7 @@ static int need_to_gc(void)
else
return 0;
- if (run_hook_le(NULL, "pre-auto-gc", NULL))
+ if (run_hooks("pre-auto-gc"))
return 0;
return 1;
}
diff --git a/builtin/hook.c b/builtin/hook.c
new file mode 100644
index 0000000000..54e5c6ec93
--- /dev/null
+++ b/builtin/hook.c
@@ -0,0 +1,84 @@
+#include "cache.h"
+#include "builtin.h"
+#include "config.h"
+#include "hook.h"
+#include "parse-options.h"
+#include "strbuf.h"
+#include "strvec.h"
+
+#define BUILTIN_HOOK_RUN_USAGE \
+ N_("git hook run [--ignore-missing] <hook-name> [-- <hook-args>]")
+
+static const char * const builtin_hook_usage[] = {
+ BUILTIN_HOOK_RUN_USAGE,
+ NULL
+};
+
+static const char * const builtin_hook_run_usage[] = {
+ BUILTIN_HOOK_RUN_USAGE,
+ NULL
+};
+
+static int run(int argc, const char **argv, const char *prefix)
+{
+ int i;
+ struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
+ int ignore_missing = 0;
+ const char *hook_name;
+ struct option run_options[] = {
+ OPT_BOOL(0, "ignore-missing", &ignore_missing,
+ N_("silently ignore missing requested <hook-name>")),
+ OPT_END(),
+ };
+ int ret;
+
+ argc = parse_options(argc, argv, prefix, run_options,
+ builtin_hook_run_usage,
+ PARSE_OPT_KEEP_DASHDASH);
+
+ if (!argc)
+ goto usage;
+
+ /*
+ * Having a -- for "run" when providing <hook-args> is
+ * mandatory.
+ */
+ if (argc > 1 && strcmp(argv[1], "--") &&
+ strcmp(argv[1], "--end-of-options"))
+ goto usage;
+
+ /* Add our arguments, start after -- */
+ for (i = 2 ; i < argc; i++)
+ strvec_push(&opt.args, argv[i]);
+
+ /* Need to take into account core.hooksPath */
+ git_config(git_default_config, NULL);
+
+ hook_name = argv[0];
+ if (!ignore_missing)
+ opt.error_if_missing = 1;
+ ret = run_hooks_opt(hook_name, &opt);
+ if (ret < 0) /* error() return */
+ ret = 1;
+ return ret;
+usage:
+ usage_with_options(builtin_hook_run_usage, run_options);
+}
+
+int cmd_hook(int argc, const char **argv, const char *prefix)
+{
+ struct option builtin_hook_options[] = {
+ OPT_END(),
+ };
+
+ argc = parse_options(argc, argv, NULL, builtin_hook_options,
+ builtin_hook_usage, PARSE_OPT_STOP_AT_NON_OPTION);
+ if (!argc)
+ goto usage;
+
+ if (!strcmp(argv[0], "run"))
+ return run(argc, argv, prefix);
+
+usage:
+ usage_with_options(builtin_hook_usage, builtin_hook_options);
+}
diff --git a/builtin/merge.c b/builtin/merge.c
index 74e53cf20a..133831d42f 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -490,7 +490,7 @@ static void finish(struct commit *head_commit,
}
/* Run a post-merge hook */
- run_hook_le(NULL, "post-merge", squash ? "1" : "0", NULL);
+ run_hooks_l("post-merge", squash ? "1" : "0", NULL);
apply_autostash(git_path_merge_autostash(the_repository));
strbuf_release(&reflog_message);
@@ -1273,7 +1273,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
struct commit_list *common = NULL;
const char *best_strategy = NULL, *wt_strategy = NULL;
- struct commit_list *remoteheads, *p;
+ struct commit_list *remoteheads = NULL, *p;
void *branch_to_free;
int orig_argc = argc;
@@ -1752,6 +1752,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
ret = suggest_conflicts();
done:
+ if (!automerge_was_ok) {
+ free_commit_list(common);
+ free_commit_list(remoteheads);
+ }
strbuf_release(&buf);
free(branch_to_free);
return ret;
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 27f60153a6..138e3c30a2 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -527,7 +527,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
int cmd_name_rev(int argc, const char **argv, const char *prefix)
{
struct object_array revs = OBJECT_ARRAY_INIT;
- int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
+ int all = 0, annotate_stdin = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
struct option opts[] = {
OPT_BOOL(0, "name-only", &data.name_only, N_("print only ref-based names (no object names)")),
@@ -538,7 +538,8 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
N_("ignore refs matching <pattern>")),
OPT_GROUP(""),
OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")),
- OPT_BOOL(0, "stdin", &transform_stdin, N_("read from stdin")),
+ OPT_BOOL(0, "stdin", &transform_stdin, N_("deprecated: use annotate-stdin instead")),
+ OPT_BOOL(0, "annotate-stdin", &annotate_stdin, N_("annotate text from stdin")),
OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")),
OPT_BOOL(0, "always", &always,
N_("show abbreviated commit object as fallback")),
@@ -554,11 +555,19 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
init_commit_rev_name(&rev_names);
git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0);
- if (all + transform_stdin + !!argc > 1) {
+
+ if (transform_stdin) {
+ warning("--stdin is deprecated. Please use --annotate-stdin instead, "
+ "which is functionally equivalent.\n"
+ "This option will be removed in a future release.");
+ annotate_stdin = 1;
+ }
+
+ if (all + annotate_stdin + !!argc > 1) {
error("Specify either a list, or --all, not both!");
usage_with_options(name_rev_usage, opts);
}
- if (all || transform_stdin)
+ if (all || annotate_stdin)
cutoff = 0;
for (; argc; argc--, argv++) {
@@ -613,15 +622,14 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
for_each_ref(name_ref, &data);
name_tips();
- if (transform_stdin) {
- char buffer[2048];
+ if (annotate_stdin) {
+ struct strbuf sb = STRBUF_INIT;
- while (!feof(stdin)) {
- char *p = fgets(buffer, sizeof(buffer), stdin);
- if (!p)
- break;
- name_rev_line(p, &data);
+ while (strbuf_getline(&sb, stdin) != EOF) {
+ strbuf_addch(&sb, '\n');
+ name_rev_line(sb.buf, &data);
}
+ strbuf_release(&sb);
} else if (all) {
int i, max;
diff --git a/builtin/pull.c b/builtin/pull.c
index 100cbf9fb8..8f37880a48 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -1038,14 +1038,13 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
oidclr(&orig_head);
if (opt_rebase) {
- int autostash = config_autostash;
- if (opt_autostash != -1)
- autostash = opt_autostash;
+ if (opt_autostash == -1)
+ opt_autostash = config_autostash;
if (is_null_oid(&orig_head) && !is_cache_unborn())
die(_("Updating an unborn branch with changes added to the index."));
- if (!autostash)
+ if (!opt_autostash)
require_clean_work_tree(the_repository,
N_("pull with rebase"),
_("please commit or stash them."), 1, 0);
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 36490d06c8..2e6d8fa34e 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -28,6 +28,7 @@
#include "sequencer.h"
#include "rebase-interactive.h"
#include "reset.h"
+#include "hook.h"
#define DEFAULT_REFLOG_ACTION "rebase"
@@ -1712,7 +1713,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
/* If a hook exists, give it a chance to interrupt*/
if (!ok_to_skip_pre_rebase &&
- run_hook_le(NULL, "pre-rebase", options.upstream_arg,
+ run_hooks_l("pre-rebase", options.upstream_arg,
argc ? argv[0] : NULL, NULL))
die(_("The pre-rebase hook refused to rebase."));
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 9f4a0b816c..c427ca09aa 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -581,32 +581,19 @@ static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)
return strbuf_detach(&buf, NULL);
}
-/*
- * NEEDSWORK: reuse find_commit_header() from jk/commit-author-parsing
- * after dropping "_commit" from its name and possibly moving it out
- * of commit.c
- */
static char *find_header(const char *msg, size_t len, const char *key,
const char **next_line)
{
- int key_len = strlen(key);
- const char *line = msg;
-
- while (line && line < msg + len) {
- const char *eol = strchrnul(line, '\n');
-
- if ((msg + len <= eol) || line == eol)
- return NULL;
- if (line + key_len < eol &&
- !memcmp(line, key, key_len) && line[key_len] == ' ') {
- int offset = key_len + 1;
- if (next_line)
- *next_line = *eol ? eol + 1 : eol;
- return xmemdupz(line + offset, (eol - line) - offset);
- }
- line = *eol ? eol + 1 : NULL;
- }
- return NULL;
+ size_t out_len;
+ const char *val = find_header_mem(msg, len, key, &out_len);
+
+ if (!val)
+ return NULL;
+
+ if (next_line)
+ *next_line = val + out_len + 1;
+
+ return xmemdupz(val, out_len);
}
/*
@@ -1424,9 +1411,12 @@ static const char *push_to_checkout(unsigned char *hash,
struct strvec *env,
const char *work_tree)
{
+ struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
+
strvec_pushf(env, "GIT_WORK_TREE=%s", absolute_path(work_tree));
- if (run_hook_le(env->v, push_to_checkout_hook,
- hash_to_hex(hash), NULL))
+ strvec_pushv(&opt.env, env->v);
+ strvec_push(&opt.args, hash_to_hex(hash));
+ if (run_hooks_opt(push_to_checkout_hook, &opt))
return "push-to-checkout hook declined";
else
return NULL;
diff --git a/builtin/reflog.c b/builtin/reflog.c
index a4b1dd27e1..85b838720c 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -12,15 +12,6 @@
#include "reachable.h"
#include "worktree.h"
-/* NEEDSWORK: switch to using parse_options */
-static const char reflog_expire_usage[] =
-N_("git reflog expire [--expire=<time>] "
- "[--expire-unreachable=<time>] "
- "[--rewrite] [--updateref] [--stale-fix] [--dry-run | -n] "
- "[--verbose] [--all] <refs>...");
-static const char reflog_delete_usage[] =
-N_("git reflog delete [--rewrite] [--updateref] "
- "[--dry-run | -n] [--verbose] <refs>...");
static const char reflog_exists_usage[] =
N_("git reflog exists <ref>");
@@ -29,6 +20,7 @@ static timestamp_t default_reflog_expire_unreachable;
struct cmd_reflog_expire_cb {
int stalefix;
+ int explicit_expiry;
timestamp_t expire_total;
timestamp_t expire_unreachable;
int recno;
@@ -520,18 +512,18 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
return 0;
}
-static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, const char *ref)
+static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, const char *ref)
{
struct reflog_expire_cfg *ent;
- if (slot == (EXPIRE_TOTAL|EXPIRE_UNREACH))
+ if (cb->explicit_expiry == (EXPIRE_TOTAL|EXPIRE_UNREACH))
return; /* both given explicitly -- nothing to tweak */
for (ent = reflog_expire_cfg; ent; ent = ent->next) {
if (!wildmatch(ent->pattern, ref, 0)) {
- if (!(slot & EXPIRE_TOTAL))
+ if (!(cb->explicit_expiry & EXPIRE_TOTAL))
cb->expire_total = ent->expire_total;
- if (!(slot & EXPIRE_UNREACH))
+ if (!(cb->explicit_expiry & EXPIRE_UNREACH))
cb->expire_unreachable = ent->expire_unreachable;
return;
}
@@ -541,29 +533,89 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c
* If unconfigured, make stash never expire
*/
if (!strcmp(ref, "refs/stash")) {
- if (!(slot & EXPIRE_TOTAL))
+ if (!(cb->explicit_expiry & EXPIRE_TOTAL))
cb->expire_total = 0;
- if (!(slot & EXPIRE_UNREACH))
+ if (!(cb->explicit_expiry & EXPIRE_UNREACH))
cb->expire_unreachable = 0;
return;
}
/* Nothing matched -- use the default value */
- if (!(slot & EXPIRE_TOTAL))
+ if (!(cb->explicit_expiry & EXPIRE_TOTAL))
cb->expire_total = default_reflog_expire;
- if (!(slot & EXPIRE_UNREACH))
+ if (!(cb->explicit_expiry & EXPIRE_UNREACH))
cb->expire_unreachable = default_reflog_expire_unreachable;
}
+static const char * reflog_expire_usage[] = {
+ N_("git reflog expire [--expire=<time>] "
+ "[--expire-unreachable=<time>] "
+ "[--rewrite] [--updateref] [--stale-fix] [--dry-run | -n] "
+ "[--verbose] [--all] <refs>..."),
+ NULL
+};
+
+static int expire_unreachable_callback(const struct option *opt,
+ const char *arg,
+ int unset)
+{
+ struct cmd_reflog_expire_cb *cmd = opt->value;
+
+ if (parse_expiry_date(arg, &cmd->expire_unreachable))
+ die(_("invalid timestamp '%s' given to '--%s'"),
+ arg, opt->long_name);
+
+ cmd->explicit_expiry |= EXPIRE_UNREACH;
+ return 0;
+}
+
+static int expire_total_callback(const struct option *opt,
+ const char *arg,
+ int unset)
+{
+ struct cmd_reflog_expire_cb *cmd = opt->value;
+
+ if (parse_expiry_date(arg, &cmd->expire_total))
+ die(_("invalid timestamp '%s' given to '--%s'"),
+ arg, opt->long_name);
+
+ cmd->explicit_expiry |= EXPIRE_TOTAL;
+ return 0;
+}
+
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
{
struct cmd_reflog_expire_cb cmd = { 0 };
timestamp_t now = time(NULL);
int i, status, do_all, all_worktrees = 1;
- int explicit_expiry = 0;
unsigned int flags = 0;
int verbose = 0;
reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
+ const struct option options[] = {
+ OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"),
+ EXPIRE_REFLOGS_DRY_RUN),
+ OPT_BIT(0, "rewrite", &flags,
+ N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"),
+ EXPIRE_REFLOGS_REWRITE),
+ OPT_BIT(0, "updateref", &flags,
+ N_("update the reference to the value of the top reflog entry"),
+ EXPIRE_REFLOGS_UPDATE_REF),
+ OPT_BOOL(0, "verbose", &verbose, N_("print extra information on screen.")),
+ OPT_CALLBACK_F(0, "expire", &cmd, N_("timestamp"),
+ N_("prune entries older than the specified time"),
+ PARSE_OPT_NONEG,
+ expire_total_callback),
+ OPT_CALLBACK_F(0, "expire-unreachable", &cmd, N_("timestamp"),
+ N_("prune entries older than <time> that are not reachable from the current tip of the branch"),
+ PARSE_OPT_NONEG,
+ expire_unreachable_callback),
+ OPT_BOOL(0, "stale-fix", &cmd.stalefix,
+ N_("prune any reflog entries that point to broken commits")),
+ OPT_BOOL(0, "all", &do_all, N_("process the reflogs of all references")),
+ OPT_BOOL(1, "single-worktree", &all_worktrees,
+ N_("limits processing to reflogs from the current worktree only.")),
+ OPT_END()
+ };
default_reflog_expire_unreachable = now - 30 * 24 * 3600;
default_reflog_expire = now - 90 * 24 * 3600;
@@ -572,45 +624,11 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
save_commit_buffer = 0;
do_all = status = 0;
+ cmd.explicit_expiry = 0;
cmd.expire_total = default_reflog_expire;
cmd.expire_unreachable = default_reflog_expire_unreachable;
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
-
- if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
- flags |= EXPIRE_REFLOGS_DRY_RUN;
- else if (skip_prefix(arg, "--expire=", &arg)) {
- if (parse_expiry_date(arg, &cmd.expire_total))
- die(_("'%s' is not a valid timestamp"), arg);
- explicit_expiry |= EXPIRE_TOTAL;
- }
- else if (skip_prefix(arg, "--expire-unreachable=", &arg)) {
- if (parse_expiry_date(arg, &cmd.expire_unreachable))
- die(_("'%s' is not a valid timestamp"), arg);
- explicit_expiry |= EXPIRE_UNREACH;
- }
- else if (!strcmp(arg, "--stale-fix"))
- cmd.stalefix = 1;
- else if (!strcmp(arg, "--rewrite"))
- flags |= EXPIRE_REFLOGS_REWRITE;
- else if (!strcmp(arg, "--updateref"))
- flags |= EXPIRE_REFLOGS_UPDATE_REF;
- else if (!strcmp(arg, "--all"))
- do_all = 1;
- else if (!strcmp(arg, "--single-worktree"))
- all_worktrees = 0;
- else if (!strcmp(arg, "--verbose"))
- verbose = 1;
- else if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- else if (arg[0] == '-')
- usage(_(reflog_expire_usage));
- else
- break;
- }
+ argc = parse_options(argc, argv, prefix, options, reflog_expire_usage, 0);
if (verbose)
should_prune_fn = should_expire_reflog_ent_verbose;
@@ -657,7 +675,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
.dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
};
- set_reflog_expiry_param(&cb.cmd, explicit_expiry, item->string);
+ set_reflog_expiry_param(&cb.cmd, item->string);
status |= reflog_expire(item->string, flags,
reflog_expiry_prepare,
should_prune_fn,
@@ -667,7 +685,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
string_list_clear(&collected.reflogs, 0);
}
- for (; i < argc; i++) {
+ for (i = 0; i < argc; i++) {
char *ref;
struct expire_reflog_policy_cb cb = { .cmd = cmd };
@@ -675,7 +693,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
status |= error(_("%s points nowhere!"), argv[i]);
continue;
}
- set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref);
+ set_reflog_expiry_param(&cb.cmd, ref);
status |= reflog_expire(ref, flags,
reflog_expiry_prepare,
should_prune_fn,
@@ -696,6 +714,12 @@ static int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
return 0;
}
+static const char * reflog_delete_usage[] = {
+ N_("git reflog delete [--rewrite] [--updateref] "
+ "[--dry-run | -n] [--verbose] <refs>..."),
+ NULL
+};
+
static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
{
struct cmd_reflog_expire_cb cmd = { 0 };
@@ -703,34 +727,28 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
unsigned int flags = 0;
int verbose = 0;
reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
-
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
- if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
- flags |= EXPIRE_REFLOGS_DRY_RUN;
- else if (!strcmp(arg, "--rewrite"))
- flags |= EXPIRE_REFLOGS_REWRITE;
- else if (!strcmp(arg, "--updateref"))
- flags |= EXPIRE_REFLOGS_UPDATE_REF;
- else if (!strcmp(arg, "--verbose"))
- verbose = 1;
- else if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- else if (arg[0] == '-')
- usage(_(reflog_delete_usage));
- else
- break;
- }
+ const struct option options[] = {
+ OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"),
+ EXPIRE_REFLOGS_DRY_RUN),
+ OPT_BIT(0, "rewrite", &flags,
+ N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"),
+ EXPIRE_REFLOGS_REWRITE),
+ OPT_BIT(0, "updateref", &flags,
+ N_("update the reference to the value of the top reflog entry"),
+ EXPIRE_REFLOGS_UPDATE_REF),
+ OPT_BOOL(0, "verbose", &verbose, N_("print extra information on screen.")),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options, reflog_delete_usage, 0);
if (verbose)
should_prune_fn = should_expire_reflog_ent_verbose;
- if (argc - i < 1)
+ if (argc < 1)
return error(_("no reflog specified to delete"));
- for ( ; i < argc; i++) {
+ for (i = 0; i < argc; i++) {
const char *spec = strstr(argv[i], "@{");
char *ep, *ref;
int recno;
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 679c107036..2b0e1db2d2 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -471,6 +471,9 @@ static int sparse_checkout_init(int argc, const char **argv)
FILE *fp;
/* assume we are in a fresh repo, but update the sparse-checkout file */
+ if (safe_create_leading_directories(sparse_filename))
+ die(_("unable to create leading directories of %s"),
+ sparse_filename);
fp = xfopen(sparse_filename, "w");
if (!fp)
die(_("failed to open '%s'"), sparse_filename);
diff --git a/builtin/stash.c b/builtin/stash.c
index 1ef2017c59..9638c56303 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1539,8 +1539,12 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
- if (startup_info->original_cwd)
+ if (startup_info->original_cwd) {
cp.dir = startup_info->original_cwd;
+ strvec_pushf(&cp.env_array, "%s=%s",
+ GIT_WORK_TREE_ENVIRONMENT,
+ the_repository->worktree);
+ }
strvec_pushl(&cp.args, "clean", "--force",
"--quiet", "-d", ":/", NULL);
if (include_untracked == INCLUDE_ALL_FILES)
@@ -1815,8 +1819,8 @@ int cmd_stash(int argc, const char **argv, const char *prefix)
else if (!strcmp(argv[0], "save"))
return !!save_stash(argc, argv, prefix);
else if (*argv[0] != '-')
- usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]),
- git_stash_usage, options);
+ usage_msg_optf(_("unknown subcommand: %s"),
+ git_stash_usage, options, argv[0]);
/* Assume 'stash push' */
strvec_push(&args, "push");
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 187203e8bb..7e0a0d9bf8 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -787,6 +787,17 @@ static int refresh(struct refresh_params *o, unsigned int flag)
setup_work_tree();
read_cache();
*o->has_errors |= refresh_cache(o->flags | flag);
+ if (has_racy_timestamp(&the_index)) {
+ /*
+ * Even if nothing else has changed, updating the file
+ * increases the chance that racy timestamps become
+ * non-racy, helping future run-time performance.
+ * We do that even in case of "errors" returned by
+ * refresh_cache() as these are no actual errors.
+ * cmd_status() does the same.
+ */
+ active_cache_changed |= SOMETHING_CHANGED;
+ }
return 0;
}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 2838254f7f..0d0809276f 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -382,21 +382,17 @@ done:
* is_junk is cleared, but do return appropriate code when hook fails.
*/
if (!ret && opts->checkout) {
- const char *hook = find_hook("post-checkout");
- if (hook) {
- const char *env[] = { "GIT_DIR", "GIT_WORK_TREE", NULL };
- struct child_process cp = CHILD_PROCESS_INIT;
- cp.no_stdin = 1;
- cp.stdout_to_stderr = 1;
- cp.dir = path;
- strvec_pushv(&cp.env_array, env);
- cp.trace2_hook_name = "post-checkout";
- strvec_pushl(&cp.args, absolute_path(hook),
- oid_to_hex(null_oid()),
- oid_to_hex(&commit->object.oid),
- "1", NULL);
- ret = run_command(&cp);
- }
+ struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
+
+ strvec_pushl(&opt.env, "GIT_DIR", "GIT_WORK_TREE", NULL);
+ strvec_pushl(&opt.args,
+ oid_to_hex(null_oid()),
+ oid_to_hex(&commit->object.oid),
+ "1",
+ NULL);
+ opt.dir = path;
+
+ ret = run_hooks_opt("post-checkout", &opt);
}
strvec_clear(&child_env);
diff --git a/cache.h b/cache.h
index 281f00ab1b..4148b6322d 100644
--- a/cache.h
+++ b/cache.h
@@ -889,6 +889,7 @@ void *read_blob_data_from_index(struct index_state *, const char *, unsigned lon
#define CE_MATCH_IGNORE_FSMONITOR 0X20
int is_racy_timestamp(const struct index_state *istate,
const struct cache_entry *ce);
+int has_racy_timestamp(struct index_state *istate);
int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
@@ -1375,6 +1376,7 @@ struct object_context {
#define GET_OID_FOLLOW_SYMLINKS 0100
#define GET_OID_RECORD_PATH 0200
#define GET_OID_ONLY_TO_DIE 04000
+#define GET_OID_REQUIRE_PATH 010000
#define GET_OID_DISAMBIGUATORS \
(GET_OID_COMMIT | GET_OID_COMMITTISH | \
diff --git a/command-list.txt b/command-list.txt
index 675c28f0bd..9bd6f3c48f 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -103,6 +103,7 @@ git-grep mainporcelain info
git-gui mainporcelain
git-hash-object plumbingmanipulators
git-help ancillaryinterrogators complete
+git-hook purehelpers
git-http-backend synchingrepositories
git-http-fetch synchelpers
git-http-push synchelpers
diff --git a/commit.c b/commit.c
index a348f085b2..d400f5dfa2 100644
--- a/commit.c
+++ b/commit.c
@@ -21,6 +21,7 @@
#include "commit-reach.h"
#include "run-command.h"
#include "shallow.h"
+#include "hook.h"
static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
@@ -1631,12 +1632,20 @@ struct commit_list **commit_list_append(struct commit *commit,
return &new_commit->next;
}
-const char *find_commit_header(const char *msg, const char *key, size_t *out_len)
+const char *find_header_mem(const char *msg, size_t len,
+ const char *key, size_t *out_len)
{
int key_len = strlen(key);
const char *line = msg;
- while (line) {
+ /*
+ * NEEDSWORK: It's possible for strchrnul() to scan beyond the range
+ * given by len. However, current callers are safe because they compute
+ * len by scanning a NUL-terminated block of memory starting at msg.
+ * Nonetheless, it would be better to ensure the function does not look
+ * at msg beyond the len provided by the caller.
+ */
+ while (line && line < msg + len) {
const char *eol = strchrnul(line, '\n');
if (line == eol)
@@ -1653,6 +1662,10 @@ const char *find_commit_header(const char *msg, const char *key, size_t *out_len
return NULL;
}
+const char *find_commit_header(const char *msg, const char *key, size_t *out_len)
+{
+ return find_header_mem(msg, strlen(msg), key, out_len);
+}
/*
* Inspect the given string and determine the true "end" of the log message, in
* order to find where to put a new Signed-off-by trailer. Ignored are
@@ -1702,22 +1715,22 @@ size_t ignore_non_trailer(const char *buf, size_t len)
int run_commit_hook(int editor_is_used, const char *index_file,
const char *name, ...)
{
- struct strvec hook_env = STRVEC_INIT;
+ struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
va_list args;
- int ret;
+ const char *arg;
- strvec_pushf(&hook_env, "GIT_INDEX_FILE=%s", index_file);
+ strvec_pushf(&opt.env, "GIT_INDEX_FILE=%s", index_file);
/*
* Let the hook know that no editor will be launched.
*/
if (!editor_is_used)
- strvec_push(&hook_env, "GIT_EDITOR=:");
+ strvec_push(&opt.env, "GIT_EDITOR=:");
va_start(args, name);
- ret = run_hook_ve(hook_env.v, name, args);
+ while ((arg = va_arg(args, const char *)))
+ strvec_push(&opt.args, arg);
va_end(args);
- strvec_clear(&hook_env);
- return ret;
+ return run_hooks_opt(name, &opt);
}
diff --git a/commit.h b/commit.h
index 3ea32766bc..38cc542661 100644
--- a/commit.h
+++ b/commit.h
@@ -290,12 +290,17 @@ void free_commit_extra_headers(struct commit_extra_header *extra);
/*
* Search the commit object contents given by "msg" for the header "key".
+ * Reads up to "len" bytes of "msg".
* Returns a pointer to the start of the header contents, or NULL. The length
* of the header, up to the first newline, is returned via out_len.
*
* Note that some headers (like mergetag) may be multi-line. It is the caller's
* responsibility to parse further in this case!
*/
+const char *find_header_mem(const char *msg, size_t len,
+ const char *key,
+ size_t *out_len);
+
const char *find_commit_header(const char *msg, const char *key,
size_t *out_len);
diff --git a/compat/qsort_s.c b/compat/qsort_s.c
index 52d1f0a73d..0f7ff30f5f 100644
--- a/compat/qsort_s.c
+++ b/compat/qsort_s.c
@@ -49,21 +49,15 @@ int git_qsort_s(void *b, size_t n, size_t s,
int (*cmp)(const void *, const void *, void *), void *ctx)
{
const size_t size = st_mult(n, s);
- char buf[1024];
+ char *tmp;
if (!n)
return 0;
if (!b || !cmp)
return -1;
- if (size < sizeof(buf)) {
- /* The temporary array fits on the small on-stack buffer. */
- msort_with_tmp(b, n, s, cmp, buf, ctx);
- } else {
- /* It's somewhat large, so malloc it. */
- char *tmp = xmalloc(size);
- msort_with_tmp(b, n, s, cmp, tmp, ctx);
- free(tmp);
- }
+ tmp = xmalloc(size);
+ msort_with_tmp(b, n, s, cmp, tmp, ctx);
+ free(tmp);
return 0;
}
diff --git a/compat/winansi.c b/compat/winansi.c
index 4fceecf14c..936a80a5f0 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -3,6 +3,12 @@
*/
#undef NOGDI
+
+/*
+ * Including the appropriate header file for RtlGenRandom causes MSVC to see a
+ * redefinition of types in an incompatible way when including headers below.
+ */
+#undef HAVE_RTLGENRANDOM
#include "../git-compat-util.h"
#include <wingdi.h>
#include <winreg.h>
diff --git a/config.c b/config.c
index 2bffa8d4a0..e0c03d154c 100644
--- a/config.c
+++ b/config.c
@@ -120,6 +120,22 @@ static long config_buf_ftell(struct config_source *conf)
return conf->u.buf.pos;
}
+struct config_include_data {
+ int depth;
+ config_fn_t fn;
+ void *data;
+ const struct config_options *opts;
+ struct git_config_source *config_source;
+
+ /*
+ * All remote URLs discovered when reading all config files.
+ */
+ struct string_list *remote_urls;
+};
+#define CONFIG_INCLUDE_INIT { 0 }
+
+static int git_config_include(const char *var, const char *value, void *data);
+
#define MAX_INCLUDE_DEPTH 10
static const char include_depth_advice[] = N_(
"exceeded maximum include depth (%d) while including\n"
@@ -294,9 +310,92 @@ static int include_by_branch(const char *cond, size_t cond_len)
return ret;
}
-static int include_condition_is_true(const struct config_options *opts,
+static int add_remote_url(const char *var, const char *value, void *data)
+{
+ struct string_list *remote_urls = data;
+ const char *remote_name;
+ size_t remote_name_len;
+ const char *key;
+
+ if (!parse_config_key(var, "remote", &remote_name, &remote_name_len,
+ &key) &&
+ remote_name &&
+ !strcmp(key, "url"))
+ string_list_append(remote_urls, value);
+ return 0;
+}
+
+static void populate_remote_urls(struct config_include_data *inc)
+{
+ struct config_options opts;
+
+ struct config_source *store_cf = cf;
+ struct key_value_info *store_kvi = current_config_kvi;
+ enum config_scope store_scope = current_parsing_scope;
+
+ opts = *inc->opts;
+ opts.unconditional_remote_url = 1;
+
+ cf = NULL;
+ current_config_kvi = NULL;
+ current_parsing_scope = 0;
+
+ inc->remote_urls = xmalloc(sizeof(*inc->remote_urls));
+ string_list_init_dup(inc->remote_urls);
+ config_with_options(add_remote_url, inc->remote_urls, inc->config_source, &opts);
+
+ cf = store_cf;
+ current_config_kvi = store_kvi;
+ current_parsing_scope = store_scope;
+}
+
+static int forbid_remote_url(const char *var, const char *value, void *data)
+{
+ const char *remote_name;
+ size_t remote_name_len;
+ const char *key;
+
+ if (!parse_config_key(var, "remote", &remote_name, &remote_name_len,
+ &key) &&
+ remote_name &&
+ !strcmp(key, "url"))
+ die(_("remote URLs cannot be configured in file directly or indirectly included by includeIf.hasconfig:remote.*.url"));
+ return 0;
+}
+
+static int at_least_one_url_matches_glob(const char *glob, int glob_len,
+ struct string_list *remote_urls)
+{
+ struct strbuf pattern = STRBUF_INIT;
+ struct string_list_item *url_item;
+ int found = 0;
+
+ strbuf_add(&pattern, glob, glob_len);
+ for_each_string_list_item(url_item, remote_urls) {
+ if (!wildmatch(pattern.buf, url_item->string, WM_PATHNAME)) {
+ found = 1;
+ break;
+ }
+ }
+ strbuf_release(&pattern);
+ return found;
+}
+
+static int include_by_remote_url(struct config_include_data *inc,
+ const char *cond, size_t cond_len)
+{
+ if (inc->opts->unconditional_remote_url)
+ return 1;
+ if (!inc->remote_urls)
+ populate_remote_urls(inc);
+ return at_least_one_url_matches_glob(cond, cond_len,
+ inc->remote_urls);
+}
+
+static int include_condition_is_true(struct config_include_data *inc,
const char *cond, size_t cond_len)
{
+ const struct config_options *opts = inc->opts;
if (skip_prefix_mem(cond, cond_len, "gitdir:", &cond, &cond_len))
return include_by_gitdir(opts, cond, cond_len, 0);
@@ -304,12 +403,15 @@ static int include_condition_is_true(const struct config_options *opts,
return include_by_gitdir(opts, cond, cond_len, 1);
else if (skip_prefix_mem(cond, cond_len, "onbranch:", &cond, &cond_len))
return include_by_branch(cond, cond_len);
+ else if (skip_prefix_mem(cond, cond_len, "hasconfig:remote.*.url:", &cond,
+ &cond_len))
+ return include_by_remote_url(inc, cond, cond_len);
/* unknown conditionals are always false */
return 0;
}
-int git_config_include(const char *var, const char *value, void *data)
+static int git_config_include(const char *var, const char *value, void *data)
{
struct config_include_data *inc = data;
const char *cond, *key;
@@ -328,9 +430,15 @@ int git_config_include(const char *var, const char *value, void *data)
ret = handle_path_include(value, inc);
if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
- (cond && include_condition_is_true(inc->opts, cond, cond_len)) &&
- !strcmp(key, "path"))
+ cond && include_condition_is_true(inc, cond, cond_len) &&
+ !strcmp(key, "path")) {
+ config_fn_t old_fn = inc->fn;
+
+ if (inc->opts->unconditional_remote_url)
+ inc->fn = forbid_remote_url;
ret = handle_path_include(value, inc);
+ inc->fn = old_fn;
+ }
return ret;
}
@@ -1929,11 +2037,13 @@ int config_with_options(config_fn_t fn, void *data,
const struct config_options *opts)
{
struct config_include_data inc = CONFIG_INCLUDE_INIT;
+ int ret;
if (opts->respect_includes) {
inc.fn = fn;
inc.data = data;
inc.opts = opts;
+ inc.config_source = config_source;
fn = git_config_include;
data = &inc;
}
@@ -1946,17 +2056,23 @@ int config_with_options(config_fn_t fn, void *data,
* regular lookup sequence.
*/
if (config_source && config_source->use_stdin) {
- return git_config_from_stdin(fn, data);
+ ret = git_config_from_stdin(fn, data);
} else if (config_source && config_source->file) {
- return git_config_from_file(fn, config_source->file, data);
+ ret = git_config_from_file(fn, config_source->file, data);
} else if (config_source && config_source->blob) {
struct repository *repo = config_source->repo ?
config_source->repo : the_repository;
- return git_config_from_blob_ref(fn, repo, config_source->blob,
+ ret = git_config_from_blob_ref(fn, repo, config_source->blob,
data);
+ } else {
+ ret = do_git_config_sequence(opts, fn, data);
}
- return do_git_config_sequence(opts, fn, data);
+ if (inc.remote_urls) {
+ string_list_clear(inc.remote_urls, 0);
+ FREE_AND_NULL(inc.remote_urls);
+ }
+ return ret;
}
static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
diff --git a/config.h b/config.h
index f119de0130..ab0106d287 100644
--- a/config.h
+++ b/config.h
@@ -89,6 +89,15 @@ struct config_options {
unsigned int ignore_worktree : 1;
unsigned int ignore_cmdline : 1;
unsigned int system_gently : 1;
+
+ /*
+ * For internal use. Include all includeif.hasremoteurl paths without
+ * checking if the repo has that remote URL, and when doing so, verify
+ * that files included in this way do not configure any remote URLs
+ * themselves.
+ */
+ unsigned int unconditional_remote_url : 1;
+
const char *commondir;
const char *git_dir;
config_parser_event_fn_t event_fn;
@@ -126,6 +135,8 @@ int git_default_config(const char *, const char *, void *);
/**
* Read a specific file in git-config format.
* This function takes the same callback and data parameters as `git_config`.
+ *
+ * Unlike git_config(), this function does not respect includes.
*/
int git_config_from_file(config_fn_t fn, const char *, void *);
@@ -158,6 +169,8 @@ void read_very_early_config(config_fn_t cb, void *data);
* will first feed the user-wide one to the callback, and then the
* repo-specific one; by overwriting, the higher-priority repo-specific
* value is left at the end).
+ *
+ * Unlike git_config_from_file(), this function respects includes.
*/
void git_config(config_fn_t fn, void *);
@@ -338,39 +351,6 @@ const char *current_config_origin_type(void);
const char *current_config_name(void);
int current_config_line(void);
-/**
- * Include Directives
- * ------------------
- *
- * By default, the config parser does not respect include directives.
- * However, a caller can use the special `git_config_include` wrapper
- * callback to support them. To do so, you simply wrap your "real" callback
- * function and data pointer in a `struct config_include_data`, and pass
- * the wrapper to the regular config-reading functions. For example:
- *
- * -------------------------------------------
- * int read_file_with_include(const char *file, config_fn_t fn, void *data)
- * {
- * struct config_include_data inc = CONFIG_INCLUDE_INIT;
- * inc.fn = fn;
- * inc.data = data;
- * return git_config_from_file(git_config_include, file, &inc);
- * }
- * -------------------------------------------
- *
- * `git_config` respects includes automatically. The lower-level
- * `git_config_from_file` does not.
- *
- */
-struct config_include_data {
- int depth;
- config_fn_t fn;
- void *data;
- const struct config_options *opts;
-};
-#define CONFIG_INCLUDE_INIT { 0 }
-int git_config_include(const char *name, const char *value, void *data);
-
/*
* Match and parse a config key of the form:
*
diff --git a/config.mak.uname b/config.mak.uname
index c48db45106..ff6485a0a4 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -146,6 +146,7 @@ ifeq ($(uname_S),Darwin)
HAVE_BSD_SYSCTL = YesPlease
FREAD_READS_DIRECTORIES = UnfortunatelyYes
HAVE_NS_GET_EXECUTABLE_PATH = YesPlease
+ CSPRNG_METHOD = arc4random
# Workaround for `gettext` being keg-only and not even being linked via
# `brew link --force gettext`, should be obsolete as of
@@ -261,6 +262,7 @@ ifeq ($(uname_S),FreeBSD)
HAVE_PATHS_H = YesPlease
HAVE_BSD_SYSCTL = YesPlease
HAVE_BSD_KERN_PROC_SYSCTL = YesPlease
+ CSPRNG_METHOD = arc4random
PAGER_ENV = LESS=FRX LV=-c MORE=FRX
FREAD_READS_DIRECTORIES = UnfortunatelyYes
FILENO_IS_A_MACRO = UnfortunatelyYes
@@ -279,6 +281,7 @@ ifeq ($(uname_S),OpenBSD)
HAVE_PATHS_H = YesPlease
HAVE_BSD_SYSCTL = YesPlease
HAVE_BSD_KERN_PROC_SYSCTL = YesPlease
+ CSPRNG_METHOD = arc4random
PROCFS_EXECUTABLE_PATH = /proc/curproc/file
FREAD_READS_DIRECTORIES = UnfortunatelyYes
FILENO_IS_A_MACRO = UnfortunatelyYes
@@ -290,6 +293,7 @@ ifeq ($(uname_S),MirBSD)
NEEDS_LIBICONV = YesPlease
HAVE_PATHS_H = YesPlease
HAVE_BSD_SYSCTL = YesPlease
+ CSPRNG_METHOD = arc4random
endif
ifeq ($(uname_S),NetBSD)
ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
@@ -301,6 +305,7 @@ ifeq ($(uname_S),NetBSD)
HAVE_PATHS_H = YesPlease
HAVE_BSD_SYSCTL = YesPlease
HAVE_BSD_KERN_PROC_SYSCTL = YesPlease
+ CSPRNG_METHOD = arc4random
PROCFS_EXECUTABLE_PATH = /proc/curproc/exe
endif
ifeq ($(uname_S),AIX)
@@ -430,6 +435,7 @@ ifeq ($(uname_S),Windows)
NO_STRTOUMAX = YesPlease
NO_MKDTEMP = YesPlease
NO_INTTYPES_H = YesPlease
+ CSPRNG_METHOD = rtlgenrandom
# VS2015 with UCRT claims that snprintf and friends are C99 compliant,
# so we don't need this:
#
@@ -599,6 +605,7 @@ ifeq ($(uname_S),NONSTOP_KERNEL)
NO_MMAP = YesPlease
NO_POLL = YesPlease
NO_INTPTR_T = UnfortunatelyYes
+ CSPRNG_METHOD = openssl
SANE_TOOL_PATH = /usr/coreutils/bin:/usr/local/bin
SHELL_PATH = /usr/coreutils/bin/bash
endif
@@ -634,6 +641,7 @@ ifeq ($(uname_S),MINGW)
NO_POSIX_GOODIES = UnfortunatelyYes
DEFAULT_HELP_FORMAT = html
HAVE_PLATFORM_PROCINFO = YesPlease
+ CSPRNG_METHOD = rtlgenrandom
BASIC_LDFLAGS += -municode
COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index 5100f56bb3..e44232f85d 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -260,7 +260,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
_CONSOLE DETECT_MSYS_TTY STRIP_EXTENSION=".exe" NO_SYMLINK_HEAD UNRELIABLE_FSTAT
NOGDI OBJECT_CREATION_MODE=1 __USE_MINGW_ANSI_STDIO=0
USE_NED_ALLOCATOR OVERRIDE_STRDUP MMAP_PREVENTS_DELETE USE_WIN32_MMAP
- UNICODE _UNICODE HAVE_WPGMPTR ENSURE_MSYSTEM_IS_SET)
+ UNICODE _UNICODE HAVE_WPGMPTR ENSURE_MSYSTEM_IS_SET HAVE_RTLGENRANDOM)
list(APPEND compat_SOURCES compat/mingw.c compat/winansi.c compat/win32/path-utils.c
compat/win32/pthread.c compat/win32mmap.c compat/win32/syslog.c
compat/win32/trace2_win32_process_info.c compat/win32/dirent.c
diff --git a/git-compat-util.h b/git-compat-util.h
index 1229c8296b..350b122221 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -197,6 +197,12 @@
#endif
#include <windows.h>
#define GIT_WINDOWS_NATIVE
+#ifdef HAVE_RTLGENRANDOM
+/* This is required to get access to RtlGenRandom. */
+#define SystemFunction036 NTAPI SystemFunction036
+#include <NTSecAPI.h>
+#undef SystemFunction036
+#endif
#endif
#include <unistd.h>
@@ -267,6 +273,12 @@
#else
#include <stdint.h>
#endif
+#ifdef HAVE_ARC4RANDOM_LIBBSD
+#include <bsd/stdlib.h>
+#endif
+#ifdef HAVE_GETRANDOM
+#include <sys/random.h>
+#endif
#ifdef NO_INTPTR_T
/*
* On I16LP32, ILP32 and LP64 "long" is the safe bet, however
@@ -1432,4 +1444,11 @@ static inline void *container_of_or_null_offset(void *ptr, size_t offset)
void sleep_millisec(int millisec);
+/*
+ * Generate len bytes from the system cryptographically secure PRNG.
+ * Returns 0 on success and -1 on error, setting errno. The inability to
+ * satisfy the full request is an error.
+ */
+int csprng_bytes(void *buf, size_t len);
+
#endif
diff --git a/git-p4.py b/git-p4.py
index cb37545455..a9b1f90441 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -108,10 +108,7 @@ def p4_build_cmd(cmd):
# Provide a way to not pass this option by setting git-p4.retries to 0
real_cmd += ["-r", str(retries)]
- if not isinstance(cmd, list):
- real_cmd = ' '.join(real_cmd) + ' ' + cmd
- else:
- real_cmd += cmd
+ real_cmd += cmd
# now check that we can actually talk to the server
global p4_access_checked
@@ -223,153 +220,93 @@ def decode_path(path):
def run_git_hook(cmd, param=[]):
"""Execute a hook if the hook exists."""
+ args = ['git', 'hook', 'run', '--ignore-missing', cmd]
+ if param:
+ args.append("--")
+ for p in param:
+ args.append(p)
+ return subprocess.call(args) == 0
+
+def write_pipe(c, stdin, *k, **kw):
if verbose:
- sys.stderr.write("Looking for hook: %s\n" % cmd)
- sys.stderr.flush()
-
- hooks_path = gitConfig("core.hooksPath")
- if len(hooks_path) <= 0:
- hooks_path = os.path.join(os.environ["GIT_DIR"], "hooks")
-
- if not isinstance(param, list):
- param=[param]
-
- # resolve hook file name, OS depdenent
- hook_file = os.path.join(hooks_path, cmd)
- if platform.system() == 'Windows':
- if not os.path.isfile(hook_file):
- # look for the file with an extension
- files = glob.glob(hook_file + ".*")
- if not files:
- return True
- files.sort()
- hook_file = files.pop()
- while hook_file.upper().endswith(".SAMPLE"):
- # The file is a sample hook. We don't want it
- if len(files) > 0:
- hook_file = files.pop()
- else:
- return True
-
- if not os.path.isfile(hook_file) or not os.access(hook_file, os.X_OK):
- return True
-
- return run_hook_command(hook_file, param) == 0
-
-def run_hook_command(cmd, param):
- """Executes a git hook command
- cmd = the command line file to be executed. This can be
- a file that is run by OS association.
-
- param = a list of parameters to pass to the cmd command
-
- On windows, the extension is checked to see if it should
- be run with the Git for Windows Bash shell. If there
- is no file extension, the file is deemed a bash shell
- and will be handed off to sh.exe. Otherwise, Windows
- will be called with the shell to handle the file assocation.
-
- For non Windows operating systems, the file is called
- as an executable.
- """
- cli = [cmd] + param
- use_shell = False
- if platform.system() == 'Windows':
- (root,ext) = os.path.splitext(cmd)
- if ext == "":
- exe_path = os.environ.get("EXEPATH")
- if exe_path is None:
- exe_path = ""
- else:
- exe_path = os.path.join(exe_path, "bin")
- cli = [os.path.join(exe_path, "SH.EXE")] + cli
- else:
- use_shell = True
- return subprocess.call(cli, shell=use_shell)
-
-
-def write_pipe(c, stdin):
- if verbose:
- sys.stderr.write('Writing pipe: %s\n' % str(c))
+ sys.stderr.write('Writing pipe: {}\n'.format(' '.join(c)))
- expand = not isinstance(c, list)
- p = subprocess.Popen(c, stdin=subprocess.PIPE, shell=expand)
+ p = subprocess.Popen(c, stdin=subprocess.PIPE, *k, **kw)
pipe = p.stdin
val = pipe.write(stdin)
pipe.close()
if p.wait():
- die('Command failed: %s' % str(c))
+ die('Command failed: {}'.format(' '.join(c)))
return val
-def p4_write_pipe(c, stdin):
+def p4_write_pipe(c, stdin, *k, **kw):
real_cmd = p4_build_cmd(c)
if bytes is not str and isinstance(stdin, str):
stdin = encode_text_stream(stdin)
- return write_pipe(real_cmd, stdin)
+ return write_pipe(real_cmd, stdin, *k, **kw)
-def read_pipe_full(c):
+def read_pipe_full(c, *k, **kw):
""" Read output from command. Returns a tuple
of the return status, stdout text and stderr
text.
"""
if verbose:
- sys.stderr.write('Reading pipe: %s\n' % str(c))
+ sys.stderr.write('Reading pipe: {}\n'.format(' '.join(c)))
- expand = not isinstance(c, list)
- p = subprocess.Popen(c, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=expand)
+ p = subprocess.Popen(
+ c, stdout=subprocess.PIPE, stderr=subprocess.PIPE, *k, **kw)
(out, err) = p.communicate()
return (p.returncode, out, decode_text_stream(err))
-def read_pipe(c, ignore_error=False, raw=False):
+def read_pipe(c, ignore_error=False, raw=False, *k, **kw):
""" Read output from command. Returns the output text on
success. On failure, terminates execution, unless
ignore_error is True, when it returns an empty string.
If raw is True, do not attempt to decode output text.
"""
- (retcode, out, err) = read_pipe_full(c)
+ (retcode, out, err) = read_pipe_full(c, *k, **kw)
if retcode != 0:
if ignore_error:
out = ""
else:
- die('Command failed: %s\nError: %s' % (str(c), err))
+ die('Command failed: {}\nError: {}'.format(' '.join(c), err))
if not raw:
out = decode_text_stream(out)
return out
-def read_pipe_text(c):
+def read_pipe_text(c, *k, **kw):
""" Read output from a command with trailing whitespace stripped.
On error, returns None.
"""
- (retcode, out, err) = read_pipe_full(c)
+ (retcode, out, err) = read_pipe_full(c, *k, **kw)
if retcode != 0:
return None
else:
return decode_text_stream(out).rstrip()
-def p4_read_pipe(c, ignore_error=False, raw=False):
+def p4_read_pipe(c, ignore_error=False, raw=False, *k, **kw):
real_cmd = p4_build_cmd(c)
- return read_pipe(real_cmd, ignore_error, raw=raw)
+ return read_pipe(real_cmd, ignore_error, raw=raw, *k, **kw)
-def read_pipe_lines(c, raw=False):
+def read_pipe_lines(c, raw=False, *k, **kw):
if verbose:
- sys.stderr.write('Reading pipe: %s\n' % str(c))
+ sys.stderr.write('Reading pipe: {}\n'.format(' '.join(c)))
- expand = not isinstance(c, list)
- p = subprocess.Popen(c, stdout=subprocess.PIPE, shell=expand)
+ p = subprocess.Popen(c, stdout=subprocess.PIPE, *k, **kw)
pipe = p.stdout
lines = pipe.readlines()
if not raw:
lines = [decode_text_stream(line) for line in lines]
if pipe.close() or p.wait():
- die('Command failed: %s' % str(c))
+ die('Command failed: {}'.format(' '.join(c)))
return lines
-def p4_read_pipe_lines(c):
+def p4_read_pipe_lines(c, *k, **kw):
"""Specifically invoke p4 on the command supplied. """
real_cmd = p4_build_cmd(c)
- return read_pipe_lines(real_cmd)
+ return read_pipe_lines(real_cmd, *k, **kw)
def p4_has_command(cmd):
"""Ask p4 for help on this command. If it returns an error, the
@@ -400,23 +337,22 @@ def p4_has_move_command():
# assume it failed because @... was invalid changelist
return True
-def system(cmd, ignore_error=False):
- expand = not isinstance(cmd, list)
+def system(cmd, ignore_error=False, *k, **kw):
if verbose:
- sys.stderr.write("executing %s\n" % str(cmd))
- retcode = subprocess.call(cmd, shell=expand)
+ sys.stderr.write("executing {}\n".format(
+ ' '.join(cmd) if isinstance(cmd, list) else cmd))
+ retcode = subprocess.call(cmd, *k, **kw)
if retcode and not ignore_error:
- raise CalledProcessError(retcode, cmd)
+ raise subprocess.CalledProcessError(retcode, cmd)
return retcode
-def p4_system(cmd):
+def p4_system(cmd, *k, **kw):
"""Specifically invoke p4 as the system command. """
real_cmd = p4_build_cmd(cmd)
- expand = not isinstance(real_cmd, list)
- retcode = subprocess.call(real_cmd, shell=expand)
+ retcode = subprocess.call(real_cmd, *k, **kw)
if retcode:
- raise CalledProcessError(retcode, real_cmd)
+ raise subprocess.CalledProcessError(retcode, real_cmd)
def die_bad_access(s):
die("failure accessing depot: {0}".format(s.rstrip()))
@@ -735,18 +671,11 @@ def isModeExecChanged(src_mode, dst_mode):
return isModeExec(src_mode) != isModeExec(dst_mode)
def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None, skip_info=False,
- errors_as_exceptions=False):
-
- if not isinstance(cmd, list):
- cmd = "-G " + cmd
- expand = True
- else:
- cmd = ["-G"] + cmd
- expand = False
+ errors_as_exceptions=False, *k, **kw):
- cmd = p4_build_cmd(cmd)
+ cmd = p4_build_cmd(["-G"] + cmd)
if verbose:
- sys.stderr.write("Opening pipe: %s\n" % str(cmd))
+ sys.stderr.write("Opening pipe: {}\n".format(' '.join(cmd)))
# Use a temporary file to avoid deadlocks without
# subprocess.communicate(), which would put another copy
@@ -763,10 +692,8 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None, skip_info=False,
stdin_file.flush()
stdin_file.seek(0)
- p4 = subprocess.Popen(cmd,
- shell=expand,
- stdin=stdin_file,
- stdout=subprocess.PIPE)
+ p4 = subprocess.Popen(
+ cmd, stdin=stdin_file, stdout=subprocess.PIPE, *k, **kw)
result = []
try:
@@ -819,8 +746,8 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None, skip_info=False,
return result
-def p4Cmd(cmd):
- list = p4CmdList(cmd)
+def p4Cmd(cmd, *k, **kw):
+ list = p4CmdList(cmd, *k, **kw)
result = {}
for entry in list:
result.update(entry)
@@ -869,7 +796,7 @@ def isValidGitDir(path):
return git_dir(path) != None
def parseRevision(ref):
- return read_pipe("git rev-parse %s" % ref).strip()
+ return read_pipe(["git", "rev-parse", ref]).strip()
def branchExists(ref):
rev = read_pipe(["git", "rev-parse", "-q", "--verify", ref],
@@ -975,11 +902,11 @@ def p4BranchesInGit(branchesAreInRemotes=True):
branches = {}
- cmdline = "git rev-parse --symbolic "
+ cmdline = ["git", "rev-parse", "--symbolic"]
if branchesAreInRemotes:
- cmdline += "--remotes"
+ cmdline.append("--remotes")
else:
- cmdline += "--branches"
+ cmdline.append("--branches")
for line in read_pipe_lines(cmdline):
line = line.strip()
@@ -1044,7 +971,7 @@ def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent
originPrefix = "origin/p4/"
- for line in read_pipe_lines("git rev-parse --symbolic --remotes"):
+ for line in read_pipe_lines(["git", "rev-parse", "--symbolic", "--remotes"]):
line = line.strip()
if (not line.startswith(originPrefix)) or line.endswith("HEAD"):
continue
@@ -1082,7 +1009,7 @@ def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent
remoteHead, ','.join(settings['depot-paths'])))
if update:
- system("git update-ref %s %s" % (remoteHead, originHead))
+ system(["git", "update-ref", remoteHead, originHead])
def originP4BranchesExist():
return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master")
@@ -1196,7 +1123,7 @@ def getClientSpec():
"""Look at the p4 client spec, create a View() object that contains
all the mappings, and return it."""
- specList = p4CmdList("client -o")
+ specList = p4CmdList(["client", "-o"])
if len(specList) != 1:
die('Output from "client -o" is %d lines, expecting 1' %
len(specList))
@@ -1225,7 +1152,7 @@ def getClientSpec():
def getClientRoot():
"""Grab the client directory."""
- output = p4CmdList("client -o")
+ output = p4CmdList(["client", "-o"])
if len(output) != 1:
die('Output from "client -o" is %d lines, expecting 1' % len(output))
@@ -1480,7 +1407,7 @@ class P4UserMap:
if self.myP4UserId:
return self.myP4UserId
- results = p4CmdList("user -o")
+ results = p4CmdList(["user", "-o"])
for r in results:
if 'User' in r:
self.myP4UserId = r['User']
@@ -1505,7 +1432,7 @@ class P4UserMap:
self.users = {}
self.emails = {}
- for output in p4CmdList("users"):
+ for output in p4CmdList(["users"]):
if "User" not in output:
continue
self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
@@ -1629,7 +1556,7 @@ class P4Submit(Command, P4UserMap):
die("Large file system not supported for git-p4 submit command. Please remove it from config.")
def check(self):
- if len(p4CmdList("opened ...")) > 0:
+ if len(p4CmdList(["opened", "..."])) > 0:
die("You have files opened with perforce! Close them before starting the sync.")
def separate_jobs_from_description(self, message):
@@ -1733,7 +1660,7 @@ class P4Submit(Command, P4UserMap):
# then gets used to patch up the username in the change. If the same
# client spec is being used by multiple processes then this might go
# wrong.
- results = p4CmdList("client -o") # find the current client
+ results = p4CmdList(["client", "-o"]) # find the current client
client = None
for r in results:
if 'Client' in r:
@@ -1749,7 +1676,7 @@ class P4Submit(Command, P4UserMap):
def modifyChangelistUser(self, changelist, newUser):
# fixup the user field of a changelist after it has been submitted.
- changes = p4CmdList("change -o %s" % changelist)
+ changes = p4CmdList(["change", "-o", changelist])
if len(changes) != 1:
die("Bad output from p4 change modifying %s to user %s" %
(changelist, newUser))
@@ -1760,7 +1687,7 @@ class P4Submit(Command, P4UserMap):
# p4 does not understand format version 3 and above
input = marshal.dumps(c, 2)
- result = p4CmdList("change -f -i", stdin=input)
+ result = p4CmdList(["change", "-f", "-i"], stdin=input)
for r in result:
if 'code' in r:
if r['code'] == 'error':
@@ -1866,7 +1793,7 @@ class P4Submit(Command, P4UserMap):
if "P4EDITOR" in os.environ and (os.environ.get("P4EDITOR") != ""):
editor = os.environ.get("P4EDITOR")
else:
- editor = read_pipe("git var GIT_EDITOR").strip()
+ editor = read_pipe(["git", "var", "GIT_EDITOR"]).strip()
system(["sh", "-c", ('%s "$@"' % editor), editor, template_file])
# If the file was not saved, prompt to see if this patch should
@@ -1924,7 +1851,8 @@ class P4Submit(Command, P4UserMap):
(p4User, gitEmail) = self.p4UserForCommit(id)
- diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (self.diffOpts, id, id))
+ diff = read_pipe_lines(
+ ["git", "diff-tree", "-r"] + self.diffOpts + ["{}^".format(id), id])
filesToAdd = set()
filesToChangeType = set()
filesToDelete = set()
@@ -2060,7 +1988,7 @@ class P4Submit(Command, P4UserMap):
#
# Apply the patch for real, and do add/delete/+x handling.
#
- system(applyPatchCmd)
+ system(applyPatchCmd, shell=True)
for f in filesToChangeType:
p4_edit(f, "-t", "auto")
@@ -2410,17 +2338,17 @@ class P4Submit(Command, P4UserMap):
#
if self.detectRenames:
# command-line -M arg
- self.diffOpts = "-M"
+ self.diffOpts = ["-M"]
else:
# If not explicitly set check the config variable
detectRenames = gitConfig("git-p4.detectRenames")
if detectRenames.lower() == "false" or detectRenames == "":
- self.diffOpts = ""
+ self.diffOpts = []
elif detectRenames.lower() == "true":
- self.diffOpts = "-M"
+ self.diffOpts = ["-M"]
else:
- self.diffOpts = "-M%s" % detectRenames
+ self.diffOpts = ["-M{}".format(detectRenames)]
# no command-line arg for -C or --find-copies-harder, just
# config variables
@@ -2428,12 +2356,12 @@ class P4Submit(Command, P4UserMap):
if detectCopies.lower() == "false" or detectCopies == "":
pass
elif detectCopies.lower() == "true":
- self.diffOpts += " -C"
+ self.diffOpts.append("-C")
else:
- self.diffOpts += " -C%s" % detectCopies
+ self.diffOpts.append("-C{}".format(detectCopies))
if gitConfigBool("git-p4.detectCopiesHarder"):
- self.diffOpts += " --find-copies-harder"
+ self.diffOpts.append("--find-copies-harder")
num_shelves = len(self.update_shelve)
if num_shelves > 0 and num_shelves != len(commits):
@@ -3381,12 +3309,9 @@ class P4Sync(Command, P4UserMap):
lostAndFoundBranches = set()
user = gitConfig("git-p4.branchUser")
- if len(user) > 0:
- command = "branches -u %s" % user
- else:
- command = "branches"
- for info in p4CmdList(command):
+ for info in p4CmdList(
+ ["branches"] + (["-u", user] if len(user) > 0 else [])):
details = p4Cmd(["branch", "-o", info["branch"]])
viewIdx = 0
while "View%s" % viewIdx in details:
@@ -3477,7 +3402,8 @@ class P4Sync(Command, P4UserMap):
while True:
if self.verbose:
print("trying: earliest %s latest %s" % (earliestCommit, latestCommit))
- next = read_pipe("git rev-list --bisect %s %s" % (latestCommit, earliestCommit)).strip()
+ next = read_pipe(["git", "rev-list", "--bisect",
+ latestCommit, earliestCommit]).strip()
if len(next) == 0:
if self.verbose:
print("argh")
@@ -3633,7 +3559,7 @@ class P4Sync(Command, P4UserMap):
if self.hasOrigin:
if not self.silent:
print('Syncing with origin first, using "git fetch origin"')
- system("git fetch origin")
+ system(["git", "fetch", "origin"])
def importHeadRevision(self, revision):
print("Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch))
@@ -3800,8 +3726,8 @@ class P4Sync(Command, P4UserMap):
if len(self.branch) == 0:
self.branch = self.refPrefix + "master"
if gitBranchExists("refs/heads/p4") and self.importIntoRemotes:
- system("git update-ref %s refs/heads/p4" % self.branch)
- system("git branch -D p4")
+ system(["git", "update-ref", self.branch, "refs/heads/p4"])
+ system(["git", "branch", "-D", "p4"])
# accept either the command-line option, or the configuration variable
if self.useClientSpec:
@@ -4004,7 +3930,7 @@ class P4Sync(Command, P4UserMap):
# Cleanup temporary branches created during import
if self.tempBranches != []:
for branch in self.tempBranches:
- read_pipe("git update-ref -d %s" % branch)
+ read_pipe(["git", "update-ref", "-d", branch])
os.rmdir(os.path.join(os.environ.get("GIT_DIR", ".git"), self.tempBranchLocation))
# Create a symbolic ref p4/HEAD pointing to p4/<branch> to allow
@@ -4036,7 +3962,7 @@ class P4Rebase(Command):
def rebase(self):
if os.system("git update-index --refresh") != 0:
die("Some files in your working directory are modified and different than what is in your index. You can use git update-index <filename> to bring the index up to date or stash away all your changes with git stash.");
- if len(read_pipe("git diff-index HEAD --")) > 0:
+ if len(read_pipe(["git", "diff-index", "HEAD", "--"])) > 0:
die("You have uncommitted changes. Please commit them before rebasing or stash them away with git stash.");
[upstream, settings] = findUpstreamBranchPoint()
@@ -4047,9 +3973,10 @@ class P4Rebase(Command):
upstream = re.sub("~[0-9]+$", "", upstream)
print("Rebasing the current branch onto %s" % upstream)
- oldHead = read_pipe("git rev-parse HEAD").strip()
- system("git rebase %s" % upstream)
- system("git diff-tree --stat --summary -M %s HEAD --" % oldHead)
+ oldHead = read_pipe(["git", "rev-parse", "HEAD"]).strip()
+ system(["git", "rebase", upstream])
+ system(["git", "diff-tree", "--stat", "--summary", "-M", oldHead,
+ "HEAD", "--"])
return True
class P4Clone(P4Sync):
@@ -4110,7 +4037,7 @@ class P4Clone(P4Sync):
init_cmd.append("--bare")
retcode = subprocess.call(init_cmd)
if retcode:
- raise CalledProcessError(retcode, init_cmd)
+ raise subprocess.CalledProcessError(retcode, init_cmd)
if not P4Sync.run(self, depotPaths):
return False
@@ -4126,7 +4053,7 @@ class P4Clone(P4Sync):
# auto-set this variable if invoked with --use-client-spec
if self.useClientSpec_from_options:
- system("git config --bool git-p4.useclientspec true")
+ system(["git", "config", "--bool", "git-p4.useclientspec", "true"])
return True
@@ -4257,10 +4184,7 @@ class P4Branches(Command):
if originP4BranchesExist():
createOrUpdateBranchesFromOrigin()
- cmdline = "git rev-parse --symbolic "
- cmdline += " --remotes"
-
- for line in read_pipe_lines(cmdline):
+ for line in read_pipe_lines(["git", "rev-parse", "--symbolic", "--remotes"]):
line = line.strip()
if not line.startswith('p4/') or line == "p4/HEAD":
@@ -4343,9 +4267,9 @@ def main():
cmd.gitdir = os.path.abspath(".git")
if not isValidGitDir(cmd.gitdir):
# "rev-parse --git-dir" without arguments will try $PWD/.git
- cmd.gitdir = read_pipe("git rev-parse --git-dir").strip()
+ cmd.gitdir = read_pipe(["git", "rev-parse", "--git-dir"]).strip()
if os.path.exists(cmd.gitdir):
- cdup = read_pipe("git rev-parse --show-cdup").strip()
+ cdup = read_pipe(["git", "rev-parse", "--show-cdup"]).strip()
if len(cdup) > 0:
chdir(cdup);
diff --git a/git-send-email.perl b/git-send-email.perl
index 04087221aa..a98460bdb9 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -225,13 +225,13 @@ my $multiedit;
my $editor;
sub system_or_msg {
- my ($args, $msg) = @_;
+ my ($args, $msg, $cmd_name) = @_;
system(@$args);
my $signalled = $? & 127;
my $exit_code = $? >> 8;
return unless $signalled or $exit_code;
- my @sprintf_args = ($args->[0], $exit_code);
+ my @sprintf_args = ($cmd_name ? $cmd_name : $args->[0], $exit_code);
if (defined $msg) {
# Quiet the 'redundant' warning category, except we
# need to support down to Perl 5.8, so we can't do a
@@ -2075,10 +2075,10 @@ sub validate_patch {
my ($fn, $xfer_encoding) = @_;
if ($repo) {
+ my $hook_name = 'sendemail-validate';
my $hooks_path = $repo->command_oneline('rev-parse', '--git-path', 'hooks');
require File::Spec;
- my $validate_hook = File::Spec->catfile($hooks_path,
- 'sendemail-validate');
+ my $validate_hook = File::Spec->catfile($hooks_path, $hook_name);
my $hook_error;
if (-x $validate_hook) {
require Cwd;
@@ -2088,13 +2088,19 @@ sub validate_patch {
chdir($repo->wc_path() or $repo->repo_path())
or die("chdir: $!");
local $ENV{"GIT_DIR"} = $repo->repo_path();
- $hook_error = system_or_msg([$validate_hook, $target]);
+ my @cmd = ("git", "hook", "run", "--ignore-missing",
+ $hook_name, "--");
+ my @cmd_msg = (@cmd, "<patch>");
+ my @cmd_run = (@cmd, $target);
+ $hook_error = system_or_msg(\@cmd_run, undef, "@cmd_msg");
chdir($cwd_save) or die("chdir: $!");
}
if ($hook_error) {
- die sprintf(__("fatal: %s: rejected by sendemail-validate hook\n" .
- "%s\n" .
- "warning: no patches were sent\n"), $fn, $hook_error);
+ $hook_error = sprintf(__("fatal: %s: rejected by %s hook\n" .
+ $hook_error . "\n" .
+ "warning: no patches were sent\n"),
+ $fn, $hook_name);
+ die $hook_error;
}
}
diff --git a/git.c b/git.c
index edda922ce6..340665d4a0 100644
--- a/git.c
+++ b/git.c
@@ -541,6 +541,7 @@ static struct cmd_struct commands[] = {
{ "grep", cmd_grep, RUN_SETUP_GENTLY },
{ "hash-object", cmd_hash_object },
{ "help", cmd_help },
+ { "hook", cmd_hook, RUN_SETUP },
{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY | NO_PARSEOPT },
{ "init", cmd_init_db },
{ "init-db", cmd_init_db },
diff --git a/gpg-interface.c b/gpg-interface.c
index b52eb0e2e0..17b1e44baa 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -433,7 +433,6 @@ static int verify_ssh_signed_buffer(struct signature_check *sigc,
struct tempfile *buffer_file;
int ret = -1;
const char *line;
- size_t trust_size;
char *principal;
struct strbuf ssh_principals_out = STRBUF_INIT;
struct strbuf ssh_principals_err = STRBUF_INIT;
@@ -502,15 +501,30 @@ static int verify_ssh_signed_buffer(struct signature_check *sigc,
ret = -1;
} else {
/* Check every principal we found (one per line) */
- for (line = ssh_principals_out.buf; *line;
- line = strchrnul(line + 1, '\n')) {
- while (*line == '\n')
- line++;
- if (!*line)
- break;
-
- trust_size = strcspn(line, "\n");
- principal = xmemdupz(line, trust_size);
+ const char *next;
+ for (line = ssh_principals_out.buf;
+ *line;
+ line = next) {
+ const char *end_of_text;
+
+ next = end_of_text = strchrnul(line, '\n');
+
+ /* Did we find a LF, and did we have CR before it? */
+ if (*end_of_text &&
+ line < end_of_text &&
+ end_of_text[-1] == '\r')
+ end_of_text--;
+
+ /* Unless we hit NUL, skip over the LF we found */
+ if (*next)
+ next++;
+
+ /* Not all lines are data. Skip empty ones */
+ if (line == end_of_text)
+ continue;
+
+ /* We now know we have an non-empty line. Process it */
+ principal = xmemdupz(line, end_of_text - line);
child_process_init(&ssh_keygen);
strbuf_release(&ssh_keygen_out);
diff --git a/grep.c b/grep.c
index 7bb0360869..5bec7fd793 100644
--- a/grep.c
+++ b/grep.c
@@ -595,6 +595,35 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
}
}
+static struct grep_expr *grep_not_expr(struct grep_expr *expr)
+{
+ struct grep_expr *z = xcalloc(1, sizeof(*z));
+ z->node = GREP_NODE_NOT;
+ z->u.unary = expr;
+ return z;
+}
+
+static struct grep_expr *grep_binexp(enum grep_expr_node kind,
+ struct grep_expr *left,
+ struct grep_expr *right)
+{
+ struct grep_expr *z = xcalloc(1, sizeof(*z));
+ z->node = kind;
+ z->u.binary.left = left;
+ z->u.binary.right = right;
+ return z;
+}
+
+static struct grep_expr *grep_or_expr(struct grep_expr *left, struct grep_expr *right)
+{
+ return grep_binexp(GREP_NODE_OR, left, right);
+}
+
+static struct grep_expr *grep_and_expr(struct grep_expr *left, struct grep_expr *right)
+{
+ return grep_binexp(GREP_NODE_AND, left, right);
+}
+
static struct grep_expr *compile_pattern_or(struct grep_pat **);
static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
{
@@ -638,12 +667,10 @@ static struct grep_expr *compile_pattern_not(struct grep_pat **list)
if (!p->next)
die("--not not followed by pattern expression");
*list = p->next;
- CALLOC_ARRAY(x, 1);
- x->node = GREP_NODE_NOT;
- x->u.unary = compile_pattern_not(list);
- if (!x->u.unary)
+ x = compile_pattern_not(list);
+ if (!x)
die("--not followed by non pattern expression");
- return x;
+ return grep_not_expr(x);
default:
return compile_pattern_atom(list);
}
@@ -652,7 +679,7 @@ static struct grep_expr *compile_pattern_not(struct grep_pat **list)
static struct grep_expr *compile_pattern_and(struct grep_pat **list)
{
struct grep_pat *p;
- struct grep_expr *x, *y, *z;
+ struct grep_expr *x, *y;
x = compile_pattern_not(list);
p = *list;
@@ -665,11 +692,7 @@ static struct grep_expr *compile_pattern_and(struct grep_pat **list)
y = compile_pattern_and(list);
if (!y)
die("--and not followed by pattern expression");
- CALLOC_ARRAY(z, 1);
- z->node = GREP_NODE_AND;
- z->u.binary.left = x;
- z->u.binary.right = y;
- return z;
+ return grep_and_expr(x, y);
}
return x;
}
@@ -677,7 +700,7 @@ static struct grep_expr *compile_pattern_and(struct grep_pat **list)
static struct grep_expr *compile_pattern_or(struct grep_pat **list)
{
struct grep_pat *p;
- struct grep_expr *x, *y, *z;
+ struct grep_expr *x, *y;
x = compile_pattern_and(list);
p = *list;
@@ -685,11 +708,7 @@ static struct grep_expr *compile_pattern_or(struct grep_pat **list)
y = compile_pattern_or(list);
if (!y)
die("not a pattern expression %s", p->pattern);
- CALLOC_ARRAY(z, 1);
- z->node = GREP_NODE_OR;
- z->u.binary.left = x;
- z->u.binary.right = y;
- return z;
+ return grep_or_expr(x, y);
}
return x;
}
@@ -699,14 +718,6 @@ static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
return compile_pattern_or(list);
}
-static struct grep_expr *grep_not_expr(struct grep_expr *expr)
-{
- struct grep_expr *z = xcalloc(1, sizeof(*z));
- z->node = GREP_NODE_NOT;
- z->u.unary = expr;
- return z;
-}
-
static struct grep_expr *grep_true_expr(void)
{
struct grep_expr *z = xcalloc(1, sizeof(*z));
@@ -714,15 +725,6 @@ static struct grep_expr *grep_true_expr(void)
return z;
}
-static struct grep_expr *grep_or_expr(struct grep_expr *left, struct grep_expr *right)
-{
- struct grep_expr *z = xcalloc(1, sizeof(*z));
- z->node = GREP_NODE_OR;
- z->u.binary.left = left;
- z->u.binary.right = right;
- return z;
-}
-
static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
{
struct grep_pat *p;
diff --git a/hook.c b/hook.c
index 55e1145a4b..69a215b2c3 100644
--- a/hook.c
+++ b/hook.c
@@ -1,6 +1,7 @@
#include "cache.h"
#include "hook.h"
#include "run-command.h"
+#include "config.h"
const char *find_hook(const char *name)
{
@@ -40,3 +41,133 @@ int hook_exists(const char *name)
{
return !!find_hook(name);
}
+
+static int pick_next_hook(struct child_process *cp,
+ struct strbuf *out,
+ void *pp_cb,
+ void **pp_task_cb)
+{
+ struct hook_cb_data *hook_cb = pp_cb;
+ const char *hook_path = hook_cb->hook_path;
+
+ if (!hook_path)
+ return 0;
+
+ cp->no_stdin = 1;
+ strvec_pushv(&cp->env_array, hook_cb->options->env.v);
+ cp->stdout_to_stderr = 1;
+ cp->trace2_hook_name = hook_cb->hook_name;
+ cp->dir = hook_cb->options->dir;
+
+ strvec_push(&cp->args, hook_path);
+ strvec_pushv(&cp->args, hook_cb->options->args.v);
+
+ /* Provide context for errors if necessary */
+ *pp_task_cb = (char *)hook_path;
+
+ /*
+ * This pick_next_hook() will be called again, we're only
+ * running one hook, so indicate that no more work will be
+ * done.
+ */
+ hook_cb->hook_path = NULL;
+
+ return 1;
+}
+
+static int notify_start_failure(struct strbuf *out,
+ void *pp_cb,
+ void *pp_task_cp)
+{
+ struct hook_cb_data *hook_cb = pp_cb;
+ const char *hook_path = pp_task_cp;
+
+ hook_cb->rc |= 1;
+
+ strbuf_addf(out, _("Couldn't start hook '%s'\n"),
+ hook_path);
+
+ return 1;
+}
+
+static int notify_hook_finished(int result,
+ struct strbuf *out,
+ void *pp_cb,
+ void *pp_task_cb)
+{
+ struct hook_cb_data *hook_cb = pp_cb;
+
+ hook_cb->rc |= result;
+
+ return 0;
+}
+
+static void run_hooks_opt_clear(struct run_hooks_opt *options)
+{
+ strvec_clear(&options->env);
+ strvec_clear(&options->args);
+}
+
+int run_hooks_opt(const char *hook_name, struct run_hooks_opt *options)
+{
+ struct strbuf abs_path = STRBUF_INIT;
+ struct hook_cb_data cb_data = {
+ .rc = 0,
+ .hook_name = hook_name,
+ .options = options,
+ };
+ const char *const hook_path = find_hook(hook_name);
+ int jobs = 1;
+ int ret = 0;
+
+ if (!options)
+ BUG("a struct run_hooks_opt must be provided to run_hooks");
+
+ if (!hook_path && !options->error_if_missing)
+ goto cleanup;
+
+ if (!hook_path) {
+ ret = error("cannot find a hook named %s", hook_name);
+ goto cleanup;
+ }
+
+ cb_data.hook_path = hook_path;
+ if (options->dir) {
+ strbuf_add_absolute_path(&abs_path, hook_path);
+ cb_data.hook_path = abs_path.buf;
+ }
+
+ run_processes_parallel_tr2(jobs,
+ pick_next_hook,
+ notify_start_failure,
+ notify_hook_finished,
+ &cb_data,
+ "hook",
+ hook_name);
+ ret = cb_data.rc;
+cleanup:
+ strbuf_release(&abs_path);
+ run_hooks_opt_clear(options);
+ return ret;
+}
+
+int run_hooks(const char *hook_name)
+{
+ struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
+
+ return run_hooks_opt(hook_name, &opt);
+}
+
+int run_hooks_l(const char *hook_name, ...)
+{
+ struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
+ va_list ap;
+ const char *arg;
+
+ va_start(ap, hook_name);
+ while ((arg = va_arg(ap, const char *)))
+ strvec_push(&opt.args, arg);
+ va_end(ap);
+
+ return run_hooks_opt(hook_name, &opt);
+}
diff --git a/hook.h b/hook.h
index 6aa36fc7ff..18d90aedf1 100644
--- a/hook.h
+++ b/hook.h
@@ -1,5 +1,37 @@
#ifndef HOOK_H
#define HOOK_H
+#include "strvec.h"
+
+struct run_hooks_opt
+{
+ /* Environment vars to be set for each hook */
+ struct strvec env;
+
+ /* Args to be passed to each hook */
+ struct strvec args;
+
+ /* Emit an error if the hook is missing */
+ unsigned int error_if_missing:1;
+
+ /**
+ * An optional initial working directory for the hook,
+ * translates to "struct child_process"'s "dir" member.
+ */
+ const char *dir;
+};
+
+#define RUN_HOOKS_OPT_INIT { \
+ .env = STRVEC_INIT, \
+ .args = STRVEC_INIT, \
+}
+
+struct hook_cb_data {
+ /* rc reflects the cumulative failure state */
+ int rc;
+ const char *hook_name;
+ const char *hook_path;
+ struct run_hooks_opt *options;
+};
/*
* Returns the path to the hook file, or NULL if the hook is missing
@@ -13,4 +45,29 @@ const char *find_hook(const char *name);
*/
int hook_exists(const char *hookname);
+/**
+ * Takes a `hook_name`, resolves it to a path with find_hook(), and
+ * runs the hook for you with the options specified in "struct
+ * run_hooks opt". Will free memory associated with the "struct run_hooks_opt".
+ *
+ * Returns the status code of the run hook, or a negative value on
+ * error().
+ */
+int run_hooks_opt(const char *hook_name, struct run_hooks_opt *options);
+
+/**
+ * A wrapper for run_hooks_opt() which provides a dummy "struct
+ * run_hooks_opt" initialized with "RUN_HOOKS_OPT_INIT".
+ */
+int run_hooks(const char *hook_name);
+
+/**
+ * Like run_hooks(), a wrapper for run_hooks_opt().
+ *
+ * In addition to the wrapping behavior provided by run_hooks(), this
+ * wrapper takes a list of strings terminated by a NULL
+ * argument. These things will be used as positional arguments to the
+ * hook. This function behaves like the old run_hook_le() API.
+ */
+int run_hooks_l(const char *hook_name, ...);
#endif
diff --git a/mem-pool.c b/mem-pool.c
index ccdcad2e3d..599d8e895f 100644
--- a/mem-pool.c
+++ b/mem-pool.c
@@ -8,6 +8,26 @@
#define BLOCK_GROWTH_SIZE (1024 * 1024 - sizeof(struct mp_block))
/*
+ * The inner union is an approximation for C11's max_align_t, and the
+ * struct + offsetof computes _Alignof. This can all just be replaced
+ * with _Alignof(max_align_t) if/when C11 is part of the baseline.
+ * Note that _Alignof(X) need not be the same as sizeof(X); it's only
+ * required to be a (possibly trivial) factor. They are the same for
+ * most architectures, but m68k for example has only 2-byte alignment
+ * for its 4-byte and 8-byte types, so using sizeof would waste space.
+ *
+ * Add more types to the union if the current set is insufficient.
+ */
+struct git_max_alignment {
+ char unalign;
+ union {
+ uintmax_t max_align_uintmax;
+ void *max_align_pointer;
+ } aligned;
+};
+#define GIT_MAX_ALIGNMENT offsetof(struct git_max_alignment, aligned)
+
+/*
* Allocate a new mp_block and insert it after the block specified in
* `insert_after`. If `insert_after` is NULL, then insert block at the
* head of the linked list.
@@ -69,9 +89,9 @@ void *mem_pool_alloc(struct mem_pool *pool, size_t len)
struct mp_block *p = NULL;
void *r;
- /* round up to a 'uintmax_t' alignment */
- if (len & (sizeof(uintmax_t) - 1))
- len += sizeof(uintmax_t) - (len & (sizeof(uintmax_t) - 1));
+ /* round up to a 'GIT_MAX_ALIGNMENT' alignment */
+ if (len & (GIT_MAX_ALIGNMENT - 1))
+ len += GIT_MAX_ALIGNMENT - (len & (GIT_MAX_ALIGNMENT - 1));
if (pool->mp_block &&
pool->mp_block->end - pool->mp_block->next_free >= len)
diff --git a/merge-ort.c b/merge-ort.c
index c319797021..d455fca7d2 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -3060,6 +3060,10 @@ static int detect_and_process_renames(struct merge_options *opt,
trace2_region_enter("merge", "regular renames", opt->repo);
detection_run |= detect_regular_renames(opt, MERGE_SIDE1);
detection_run |= detect_regular_renames(opt, MERGE_SIDE2);
+ if (renames->needed_limit) {
+ renames->cached_pairs_valid_side = 0;
+ renames->redo_after_renames = 0;
+ }
if (renames->redo_after_renames && detection_run) {
int i, side;
struct diff_filepair *p;
@@ -4575,7 +4579,7 @@ static void merge_ort_internal(struct merge_options *opt,
struct commit *h2,
struct merge_result *result)
{
- struct commit_list *iter;
+ struct commit *next;
struct commit *merged_merge_bases;
const char *ancestor_name;
struct strbuf merge_base_abbrev = STRBUF_INIT;
@@ -4604,7 +4608,8 @@ static void merge_ort_internal(struct merge_options *opt,
ancestor_name = merge_base_abbrev.buf;
}
- for (iter = merge_bases; iter; iter = iter->next) {
+ for (next = pop_commit(&merge_bases); next;
+ next = pop_commit(&merge_bases)) {
const char *saved_b1, *saved_b2;
struct commit *prev = merged_merge_bases;
@@ -4621,7 +4626,7 @@ static void merge_ort_internal(struct merge_options *opt,
saved_b2 = opt->branch2;
opt->branch1 = "Temporary merge branch 1";
opt->branch2 = "Temporary merge branch 2";
- merge_ort_internal(opt, NULL, prev, iter->item, result);
+ merge_ort_internal(opt, NULL, prev, next, result);
if (result->clean < 0)
return;
opt->branch1 = saved_b1;
@@ -4632,8 +4637,7 @@ static void merge_ort_internal(struct merge_options *opt,
result->tree,
"merged tree");
commit_list_insert(prev, &merged_merge_bases->parents);
- commit_list_insert(iter->item,
- &merged_merge_bases->parents->next);
+ commit_list_insert(next, &merged_merge_bases->parents->next);
clear_or_reinit_internal_opts(opt->priv, 1);
}
diff --git a/object-name.c b/object-name.c
index fdff4601b2..92862eeb1a 100644
--- a/object-name.c
+++ b/object-name.c
@@ -1795,13 +1795,13 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo,
const char *cp;
int only_to_die = flags & GET_OID_ONLY_TO_DIE;
- if (only_to_die)
- flags |= GET_OID_QUIETLY;
-
memset(oc, 0, sizeof(*oc));
oc->mode = S_IFINVALID;
strbuf_init(&oc->symlink_path, 0);
ret = get_oid_1(repo, name, namelen, oid, flags);
+ if (!ret && flags & GET_OID_REQUIRE_PATH)
+ die(_("<object>:<path> required, only <object> '%s' given"),
+ name);
if (!ret)
return ret;
/*
@@ -1932,7 +1932,7 @@ void maybe_die_on_misspelt_object_name(struct repository *r,
{
struct object_context oc;
struct object_id oid;
- get_oid_with_context_1(r, name, GET_OID_ONLY_TO_DIE,
+ get_oid_with_context_1(r, name, GET_OID_ONLY_TO_DIE | GET_OID_QUIETLY,
prefix, &oid, &oc);
}
diff --git a/parse-options.c b/parse-options.c
index a8283037be..2437ad3bcd 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -1079,3 +1079,16 @@ void NORETURN usage_msg_opt(const char *msg,
die_message("%s\n", msg); /* The extra \n is intentional */
usage_with_options(usagestr, options);
}
+
+void NORETURN usage_msg_optf(const char * const fmt,
+ const char * const *usagestr,
+ const struct option *options, ...)
+{
+ struct strbuf msg = STRBUF_INIT;
+ va_list ap;
+ va_start(ap, options);
+ strbuf_vaddf(&msg, fmt, ap);
+ va_end(ap);
+
+ usage_msg_opt(msg.buf, usagestr, options);
+}
diff --git a/parse-options.h b/parse-options.h
index e22846d3b7..659a4c28b2 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -85,6 +85,11 @@ typedef enum parse_opt_result parse_opt_ll_cb(struct parse_opt_ctx_t *ctx,
* token to explain the kind of argument this option wants. Does not
* begin in capital letter, and does not end with a full stop.
* Should be wrapped by N_() for translation.
+ * Is automatically enclosed in brackets when printed, unless it
+ * contains any of the following characters: ()<>[]|
+ * E.g. "name" is shown as "<name>" to indicate that a name value
+ * needs to be supplied, not the literal string "name", but
+ * "<start>,<end>" and "(this|that)" are printed verbatim.
*
* `help`::
* the short help associated to what the option does.
@@ -225,6 +230,16 @@ NORETURN void usage_msg_opt(const char *msg,
const char * const *usagestr,
const struct option *options);
+/**
+ * usage_msg_optf() is like usage_msg_opt() except that the first
+ * argument is a format string, and optional format arguments follow
+ * after the 3rd option.
+ */
+__attribute__((format (printf,1,4)))
+void NORETURN usage_msg_optf(const char *fmt,
+ const char * const *usagestr,
+ const struct option *options, ...);
+
/*
* Use these assertions for callbacks that expect to be called with NONEG and
* NOARG respectively, and do not otherwise handle the "unset" and "arg"
diff --git a/read-cache.c b/read-cache.c
index cbe73f14e5..63a792e578 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -28,6 +28,7 @@
#include "sparse-index.h"
#include "csum-file.h"
#include "promisor-remote.h"
+#include "hook.h"
/* Mask for the name length in ce_flags in the on-disk index */
@@ -2775,7 +2776,7 @@ static int repo_verify_index(struct repository *repo)
return verify_index_from(repo->index, repo->index_file);
}
-static int has_racy_timestamp(struct index_state *istate)
+int has_racy_timestamp(struct index_state *istate)
{
int entries = istate->cache_nr;
int i;
@@ -3009,6 +3010,9 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
!is_null_oid(&istate->split_index->base_oid)) {
struct strbuf sb = STRBUF_INIT;
+ if (istate->sparse_index)
+ die(_("cannot write split index for a sparse index"));
+
err = write_link_extension(&sb, istate) < 0 ||
write_index_ext_header(f, eoie_c, CACHE_EXT_LINK,
sb.len) < 0;
@@ -3150,7 +3154,7 @@ static int do_write_locked_index(struct index_state *istate, struct lock_file *l
else
ret = close_lock_file_gently(lock);
- run_hook_le(NULL, "post-index-change",
+ run_hooks_l("post-index-change",
istate->updated_workdir ? "1" : "0",
istate->updated_skipworktree ? "1" : "0", NULL);
istate->updated_workdir = 0;
diff --git a/refs.c b/refs.c
index addb26293b..7017ae5980 100644
--- a/refs.c
+++ b/refs.c
@@ -269,10 +269,9 @@ char *refs_resolve_refdup(struct ref_store *refs,
struct object_id *oid, int *flags)
{
const char *result;
- int ignore_errno;
result = refs_resolve_ref_unsafe(refs, refname, resolve_flags,
- oid, flags, &ignore_errno);
+ oid, flags);
return xstrdup_or_null(result);
}
@@ -294,11 +293,10 @@ struct ref_filter {
int read_ref_full(const char *refname, int resolve_flags, struct object_id *oid, int *flags)
{
- int ignore_errno;
struct ref_store *refs = get_main_ref_store(the_repository);
if (refs_resolve_ref_unsafe(refs, refname, resolve_flags,
- oid, flags, &ignore_errno))
+ oid, flags))
return 0;
return -1;
}
@@ -310,9 +308,8 @@ int read_ref(const char *refname, struct object_id *oid)
int refs_ref_exists(struct ref_store *refs, const char *refname)
{
- int ignore_errno;
return !!refs_resolve_ref_unsafe(refs, refname, RESOLVE_REF_READING,
- NULL, NULL, &ignore_errno);
+ NULL, NULL);
}
int ref_exists(const char *refname)
@@ -656,15 +653,13 @@ int expand_ref(struct repository *repo, const char *str, int len,
struct object_id *this_result;
int flag;
struct ref_store *refs = get_main_ref_store(repo);
- int ignore_errno;
this_result = refs_found ? &oid_from_ref : oid;
strbuf_reset(&fullref);
strbuf_addf(&fullref, *p, len, str);
r = refs_resolve_ref_unsafe(refs, fullref.buf,
RESOLVE_REF_READING,
- this_result, &flag,
- &ignore_errno);
+ this_result, &flag);
if (r) {
if (!refs_found++)
*ref = xstrdup(r);
@@ -693,14 +688,12 @@ int repo_dwim_log(struct repository *r, const char *str, int len,
for (p = ref_rev_parse_rules; *p; p++) {
struct object_id hash;
const char *ref, *it;
- int ignore_errno;
strbuf_reset(&path);
strbuf_addf(&path, *p, len, str);
ref = refs_resolve_ref_unsafe(refs, path.buf,
RESOLVE_REF_READING,
- oid ? &hash : NULL, NULL,
- &ignore_errno);
+ oid ? &hash : NULL, NULL);
if (!ref)
continue;
if (refs_reflog_exists(refs, path.buf))
@@ -1390,10 +1383,9 @@ int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
struct object_id oid;
int flag;
- int ignore_errno;
if (refs_resolve_ref_unsafe(refs, "HEAD", RESOLVE_REF_READING,
- &oid, &flag, &ignore_errno))
+ &oid, &flag))
return fn("HEAD", &oid, flag, cb_data);
return 0;
@@ -1682,15 +1674,13 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
const char *refname,
int resolve_flags,
struct object_id *oid,
- int *flags, int *failure_errno)
+ int *flags)
{
static struct strbuf sb_refname = STRBUF_INIT;
struct object_id unused_oid;
int unused_flags;
int symref_count;
- assert(failure_errno);
-
if (!oid)
oid = &unused_oid;
if (!flags)
@@ -1700,10 +1690,8 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
- !refname_is_safe(refname)) {
- *failure_errno = EINVAL;
+ !refname_is_safe(refname))
return NULL;
- }
/*
* dwim_ref() uses REF_ISBROKEN to distinguish between
@@ -1718,9 +1706,10 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
for (symref_count = 0; symref_count < SYMREF_MAXDEPTH; symref_count++) {
unsigned int read_flags = 0;
+ int failure_errno;
if (refs_read_raw_ref(refs, refname, oid, &sb_refname,
- &read_flags, failure_errno)) {
+ &read_flags, &failure_errno)) {
*flags |= read_flags;
/* In reading mode, refs must eventually resolve */
@@ -1732,9 +1721,9 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
* may show errors besides ENOENT if there are
* similarly-named refs.
*/
- if (*failure_errno != ENOENT &&
- *failure_errno != EISDIR &&
- *failure_errno != ENOTDIR)
+ if (failure_errno != ENOENT &&
+ failure_errno != EISDIR &&
+ failure_errno != ENOTDIR)
return NULL;
oidclr(oid);
@@ -1760,16 +1749,13 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
}
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
- !refname_is_safe(refname)) {
- *failure_errno = EINVAL;
+ !refname_is_safe(refname))
return NULL;
- }
*flags |= REF_ISBROKEN | REF_BAD_NAME;
}
}
- *failure_errno = ELOOP;
return NULL;
}
@@ -1784,10 +1770,8 @@ int refs_init_db(struct strbuf *err)
const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
struct object_id *oid, int *flags)
{
- int ignore_errno;
-
return refs_resolve_ref_unsafe(get_main_ref_store(the_repository), refname,
- resolve_flags, oid, flags, &ignore_errno);
+ resolve_flags, oid, flags);
}
int resolve_gitlink_ref(const char *submodule, const char *refname,
@@ -1795,15 +1779,14 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,
{
struct ref_store *refs;
int flags;
- int ignore_errno;
refs = get_submodule_ref_store(submodule);
if (!refs)
return -1;
- if (!refs_resolve_ref_unsafe(refs, refname, 0, oid, &flags,
- &ignore_errno) || is_null_oid(oid))
+ if (!refs_resolve_ref_unsafe(refs, refname, 0, oid, &flags) ||
+ is_null_oid(oid))
return -1;
return 0;
}
diff --git a/refs.h b/refs.h
index 8f91a7f9ff..cd2d0c1ac0 100644
--- a/refs.h
+++ b/refs.h
@@ -58,11 +58,6 @@ struct worktree;
* resolved. The function returns NULL for such ref names.
* Caps and underscores refers to the special refs, such as HEAD,
* FETCH_HEAD and friends, that all live outside of the refs/ directory.
- *
- * Callers should not inspect "errno" on failure, but rather pass in a
- * "failure_errno" parameter, on failure the "errno" will indicate the
- * type of failure encountered, but not necessarily one that came from
- * a syscall. We might have faked it up.
*/
#define RESOLVE_REF_READING 0x01
#define RESOLVE_REF_NO_RECURSE 0x02
@@ -72,7 +67,7 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
const char *refname,
int resolve_flags,
struct object_id *oid,
- int *flags, int *failure_errno);
+ int *flags);
const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
struct object_id *oid, int *flags);
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 43a3b882d7..a40267b3ae 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -277,11 +277,10 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
create_dir_entry(dir->cache, refname.buf,
refname.len));
} else {
- int ignore_errno;
if (!refs_resolve_ref_unsafe(&refs->base,
refname.buf,
RESOLVE_REF_READING,
- &oid, &flag, &ignore_errno)) {
+ &oid, &flag)) {
oidclr(&oid);
flag |= REF_ISBROKEN;
} else if (is_null_oid(&oid)) {
@@ -1006,7 +1005,6 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
{
struct strbuf ref_file = STRBUF_INIT;
struct ref_lock *lock;
- int ignore_errno;
files_assert_main_repository(refs, "lock_ref_oid_basic");
assert(err);
@@ -1034,7 +1032,7 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
}
if (!refs_resolve_ref_unsafe(&refs->base, lock->ref_name, 0,
- &lock->old_oid, NULL, &ignore_errno))
+ &lock->old_oid, NULL))
oidclr(&lock->old_oid);
goto out;
@@ -1399,7 +1397,6 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
struct strbuf tmp_renamed_log = STRBUF_INIT;
int log, ret;
struct strbuf err = STRBUF_INIT;
- int ignore_errno;
files_reflog_path(refs, &sb_oldref, oldrefname);
files_reflog_path(refs, &sb_newref, newrefname);
@@ -1413,7 +1410,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
if (!refs_resolve_ref_unsafe(&refs->base, oldrefname,
RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
- &orig_oid, &flag, &ignore_errno)) {
+ &orig_oid, &flag)) {
ret = error("refname %s not found", oldrefname);
goto out;
}
@@ -1459,7 +1456,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
*/
if (!copy && refs_resolve_ref_unsafe(&refs->base, newrefname,
RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
- NULL, NULL, &ignore_errno) &&
+ NULL, NULL) &&
refs_delete_ref(&refs->base, NULL, newrefname,
NULL, REF_NO_DEREF)) {
if (errno == EISDIR) {
@@ -1828,12 +1825,10 @@ static int commit_ref_update(struct files_ref_store *refs,
*/
int head_flag;
const char *head_ref;
- int ignore_errno;
head_ref = refs_resolve_ref_unsafe(&refs->base, "HEAD",
RESOLVE_REF_READING,
- NULL, &head_flag,
- &ignore_errno);
+ NULL, &head_flag);
if (head_ref && (head_flag & REF_ISSYMREF) &&
!strcmp(head_ref, lock->ref_name)) {
struct strbuf log_err = STRBUF_INIT;
@@ -1877,12 +1872,10 @@ static void update_symref_reflog(struct files_ref_store *refs,
{
struct strbuf err = STRBUF_INIT;
struct object_id new_oid;
- int ignore_errno;
if (logmsg &&
refs_resolve_ref_unsafe(&refs->base, target,
- RESOLVE_REF_READING, &new_oid, NULL,
- &ignore_errno) &&
+ RESOLVE_REF_READING, &new_oid, NULL) &&
files_log_ref_write(refs, refname, &lock->old_oid,
&new_oid, logmsg, 0, &err)) {
error("%s", err.buf);
@@ -2156,7 +2149,6 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
(struct files_reflog_iterator *)ref_iterator;
struct dir_iterator *diter = iter->dir_iterator;
int ok;
- int ignore_errno;
while ((ok = dir_iterator_advance(diter)) == ITER_OK) {
int flags;
@@ -2170,8 +2162,7 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
if (!refs_resolve_ref_unsafe(iter->ref_store,
diter->relative_path, 0,
- &iter->oid, &flags,
- &ignore_errno)) {
+ &iter->oid, &flags)) {
error("bad ref for %s", diter->path.buf);
continue;
}
@@ -2515,11 +2506,9 @@ static int lock_ref_for_update(struct files_ref_store *refs,
* the transaction, so we have to read it here
* to record and possibly check old_oid:
*/
- int ignore_errno;
if (!refs_resolve_ref_unsafe(&refs->base,
referent.buf, 0,
- &lock->old_oid, NULL,
- &ignore_errno)) {
+ &lock->old_oid, NULL)) {
if (update->flags & REF_HAVE_OLD) {
strbuf_addf(err, "cannot lock ref '%s': "
"error reading reference",
@@ -3208,14 +3197,12 @@ static int files_reflog_expire(struct ref_store *ref_store,
if ((expire_flags & EXPIRE_REFLOGS_UPDATE_REF) &&
!is_null_oid(&cb.last_kept_oid)) {
- int ignore_errno;
int type;
const char *ref;
ref = refs_resolve_ref_unsafe(&refs->base, refname,
RESOLVE_REF_NO_RECURSE,
- NULL, &type,
- &ignore_errno);
+ NULL, &type);
update = !!(ref && !(type & REF_ISSYMREF));
}
diff --git a/remote.c b/remote.c
index a6d8ec6c1a..c97c626eaa 100644
--- a/remote.c
+++ b/remote.c
@@ -508,9 +508,8 @@ static void read_config(struct repository *repo)
repo->remote_state->current_branch = NULL;
if (startup_info->have_repository) {
- int ignore_errno;
const char *head_ref = refs_resolve_ref_unsafe(
- get_main_ref_store(repo), "HEAD", 0, NULL, &flag, &ignore_errno);
+ get_main_ref_store(repo), "HEAD", 0, NULL, &flag);
if (head_ref && (flag & REF_ISSYMREF) &&
skip_prefix(head_ref, "refs/heads/", &head_ref)) {
repo->remote_state->current_branch = make_branch(
diff --git a/reset.c b/reset.c
index f214df3d96..0881e63691 100644
--- a/reset.c
+++ b/reset.c
@@ -7,6 +7,7 @@
#include "tree-walk.h"
#include "tree.h"
#include "unpack-trees.h"
+#include "hook.h"
int reset_head(struct repository *r, struct object_id *oid, const char *action,
const char *switch_to_branch, unsigned flags,
@@ -127,7 +128,7 @@ reset_head_refs:
reflog_head);
}
if (run_hook)
- run_hook_le(NULL, "post-checkout",
+ run_hooks_l("post-checkout",
oid_to_hex(orig ? orig : null_oid()),
oid_to_hex(oid), "1", NULL);
diff --git a/run-command.c b/run-command.c
index 69dde42f1e..a8501e38ce 100644
--- a/run-command.c
+++ b/run-command.c
@@ -1307,39 +1307,6 @@ int async_with_fork(void)
#endif
}
-int run_hook_ve(const char *const *env, const char *name, va_list args)
-{
- struct child_process hook = CHILD_PROCESS_INIT;
- const char *p;
-
- p = find_hook(name);
- if (!p)
- return 0;
-
- strvec_push(&hook.args, p);
- while ((p = va_arg(args, const char *)))
- strvec_push(&hook.args, p);
- if (env)
- strvec_pushv(&hook.env_array, (const char **)env);
- hook.no_stdin = 1;
- hook.stdout_to_stderr = 1;
- hook.trace2_hook_name = name;
-
- return run_command(&hook);
-}
-
-int run_hook_le(const char *const *env, const char *name, ...)
-{
- va_list args;
- int ret;
-
- va_start(args, name);
- ret = run_hook_ve(env, name, args);
- va_end(args);
-
- return ret;
-}
-
struct io_pump {
/* initialized by caller */
int fd;
diff --git a/run-command.h b/run-command.h
index 2be5f5d642..07bed6c31b 100644
--- a/run-command.h
+++ b/run-command.h
@@ -220,23 +220,6 @@ int finish_command_in_signal(struct child_process *);
*/
int run_command(struct child_process *);
-/**
- * Run a hook.
- * The first argument is a pathname to an index file, or NULL
- * if the hook uses the default index file or no index is needed.
- * The second argument is the name of the hook.
- * The further arguments correspond to the hook arguments.
- * The last argument has to be NULL to terminate the arguments list.
- * If the hook does not exist or is not executable, the return
- * value will be zero.
- * If it is executable, the hook will be executed and the exit
- * status of the hook is returned.
- * On execution, .stdout_to_stderr and .no_stdin will be set.
- */
-LAST_ARG_MUST_BE_NULL
-int run_hook_le(const char *const *env, const char *name, ...);
-int run_hook_ve(const char *const *env, const char *name, va_list args);
-
/*
* Trigger an auto-gc
*/
diff --git a/sequencer.c b/sequencer.c
index 6abd72160c..fb978a53a0 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1281,7 +1281,6 @@ void print_commit_summary(struct repository *r,
struct strbuf author_ident = STRBUF_INIT;
struct strbuf committer_ident = STRBUF_INIT;
struct ref_store *refs;
- int resolve_errno;
commit = lookup_commit(r, oid);
if (!commit)
@@ -1332,12 +1331,9 @@ void print_commit_summary(struct repository *r,
diff_setup_done(&rev.diffopt);
refs = get_main_ref_store(the_repository);
- head = refs_resolve_ref_unsafe(refs, "HEAD", 0, NULL, NULL,
- &resolve_errno);
- if (!head) {
- errno = resolve_errno;
- die_errno(_("unable to resolve HEAD after creating commit"));
- }
+ head = refs_resolve_ref_unsafe(refs, "HEAD", 0, NULL, NULL);
+ if (!head)
+ die(_("unable to resolve HEAD after creating commit"));
if (!strcmp(head, "HEAD"))
head = _("detached HEAD");
else
@@ -4223,8 +4219,11 @@ static int run_git_checkout(struct repository *r, struct replay_opts *opts,
cmd.git_cmd = 1;
- if (startup_info->original_cwd)
+ if (startup_info->original_cwd) {
cmd.dir = startup_info->original_cwd;
+ strvec_pushf(&cmd.env_array, "%s=%s",
+ GIT_WORK_TREE_ENVIRONMENT, r->worktree);
+ }
strvec_push(&cmd.args, "checkout");
strvec_push(&cmd.args, commit);
strvec_pushf(&cmd.env_array, GIT_REFLOG_ACTION "=%s", action);
diff --git a/sparse-index.c b/sparse-index.c
index a1d505d50e..08f54747bb 100644
--- a/sparse-index.c
+++ b/sparse-index.c
@@ -136,7 +136,7 @@ static int is_sparse_index_allowed(struct index_state *istate, int flags)
/*
* The sparse index is not (yet) integrated with a split index.
*/
- if (istate->split_index)
+ if (istate->split_index || git_env_bool("GIT_TEST_SPLIT_INDEX", 0))
return 0;
/*
* The GIT_TEST_SPARSE_INDEX environment variable triggers the
diff --git a/split-index.c b/split-index.c
index 8e52e891c3..9d0ccc30d0 100644
--- a/split-index.c
+++ b/split-index.c
@@ -5,6 +5,9 @@
struct split_index *init_split_index(struct index_state *istate)
{
if (!istate->split_index) {
+ if (istate->sparse_index)
+ die(_("cannot use split index with a sparse index"));
+
CALLOC_ARRAY(istate->split_index, 1);
istate->split_index->refcount = 1;
}
diff --git a/stable-qsort.c b/stable-qsort.c
index 6cbaf39f7b..7ff12467cd 100644
--- a/stable-qsort.c
+++ b/stable-qsort.c
@@ -48,15 +48,9 @@ void git_stable_qsort(void *b, size_t n, size_t s,
int (*cmp)(const void *, const void *))
{
const size_t size = st_mult(n, s);
- char buf[1024];
-
- if (size < sizeof(buf)) {
- /* The temporary array fits on the small on-stack buffer. */
- msort_with_tmp(b, n, s, cmp, buf);
- } else {
- /* It's somewhat large, so malloc it. */
- char *tmp = xmalloc(size);
- msort_with_tmp(b, n, s, cmp, tmp);
- free(tmp);
- }
+ char *tmp;
+
+ tmp = xmalloc(size);
+ msort_with_tmp(b, n, s, cmp, tmp);
+ free(tmp);
}
diff --git a/t/helper/test-csprng.c b/t/helper/test-csprng.c
new file mode 100644
index 0000000000..65d14973c5
--- /dev/null
+++ b/t/helper/test-csprng.c
@@ -0,0 +1,29 @@
+#include "test-tool.h"
+#include "git-compat-util.h"
+
+
+int cmd__csprng(int argc, const char **argv)
+{
+ unsigned long count;
+ unsigned char buf[1024];
+
+ if (argc > 2) {
+ fprintf(stderr, "usage: %s [<size>]\n", argv[0]);
+ return 2;
+ }
+
+ count = (argc == 2) ? strtoul(argv[1], NULL, 0) : -1L;
+
+ while (count) {
+ unsigned long chunk = count < sizeof(buf) ? count : sizeof(buf);
+ if (csprng_bytes(buf, chunk) < 0) {
+ perror("failed to read");
+ return 5;
+ }
+ if (fwrite(buf, chunk, 1, stdout) != chunk)
+ return 1;
+ count -= chunk;
+ }
+
+ return 0;
+}
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 3e4ddaee70..9646d85fc8 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -180,10 +180,9 @@ static int cmd_resolve_ref(struct ref_store *refs, const char **argv)
int resolve_flags = arg_flags(*argv++, "resolve-flags", empty_flags);
int flags;
const char *ref;
- int ignore_errno;
ref = refs_resolve_ref_unsafe(refs, refname, resolve_flags,
- &oid, &flags, &ignore_errno);
+ &oid, &flags);
printf("%s %s 0x%x\n", oid_to_hex(&oid), ref ? ref : "(null)", flags);
return ref ? 0 : 1;
}
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index 338a57b104..e6ec69cf32 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -20,6 +20,7 @@ static struct test_cmd cmds[] = {
{ "chmtime", cmd__chmtime },
{ "config", cmd__config },
{ "crontab", cmd__crontab },
+ { "csprng", cmd__csprng },
{ "ctype", cmd__ctype },
{ "date", cmd__date },
{ "delta", cmd__delta },
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index 48cee1f4a2..20756eefdd 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -10,6 +10,7 @@ int cmd__bloom(int argc, const char **argv);
int cmd__chmtime(int argc, const char **argv);
int cmd__config(int argc, const char **argv);
int cmd__crontab(int argc, const char **argv);
+int cmd__csprng(int argc, const char **argv);
int cmd__ctype(int argc, const char **argv);
int cmd__date(int argc, const char **argv);
int cmd__delta(int argc, const char **argv);
diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh
index 4a5c5c602c..c5f7ac63b0 100755
--- a/t/t0027-auto-crlf.sh
+++ b/t/t0027-auto-crlf.sh
@@ -597,6 +597,12 @@ do
# auto: core.autocrlf=false and core.eol unset(or native) uses native eol
checkout_files auto "$id" "" false "" $NL CRLF CRLF_mix_LF LF_mix_CR LF_nul
checkout_files auto "$id" "" false native $NL CRLF CRLF_mix_LF LF_mix_CR LF_nul
+ # core.autocrlf false, .gitattributes sets eol
+ checkout_files "" "$id" "lf" false "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
+ checkout_files "" "$id" "crlf" false "" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+ # core.autocrlf true, .gitattributes sets eol
+ checkout_files "" "$id" "lf" true "" LF CRLF CRLF_mix_LF LF_mix_CR LF_nul
+ checkout_files "" "$id" "crlf" true "" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
done
# The rest of the tests are unique; do the usual linting.
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index 39382fa195..145eee11df 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -4,6 +4,98 @@ test_description='git cat-file'
. ./test-lib.sh
+test_cmdmode_usage () {
+ test_expect_code 129 "$@" 2>err &&
+ grep "^error:.*is incompatible with" err
+}
+
+for switches in \
+ '-e -p' \
+ '-p -t' \
+ '-t -s' \
+ '-s --textconv' \
+ '--textconv --filters' \
+ '--batch-all-objects -e'
+do
+ test_expect_success "usage: cmdmode $switches" '
+ test_cmdmode_usage git cat-file $switches
+ '
+done
+
+test_incompatible_usage () {
+ test_expect_code 129 "$@" 2>err &&
+ grep -E "^(fatal|error):.*(requires|incompatible with|needs)" err
+}
+
+for opt in --batch --batch-check
+do
+ test_expect_success "usage: incompatible options: --path with $opt" '
+ test_incompatible_usage git cat-file --path=foo $opt
+ '
+done
+
+test_missing_usage () {
+ test_expect_code 129 "$@" 2>err &&
+ grep -E "^fatal:.*required" err
+}
+
+short_modes="-e -p -t -s"
+cw_modes="--textconv --filters"
+
+for opt in $cw_modes
+do
+ test_expect_success "usage: $opt requires another option" '
+ test_missing_usage git cat-file $opt
+ '
+done
+
+for opt in $short_modes
+do
+ test_expect_success "usage: $opt requires another option" '
+ test_missing_usage git cat-file $opt
+ '
+
+ for opt2 in --batch \
+ --batch-check \
+ --follow-symlinks \
+ "--path=foo HEAD:some-path.txt"
+ do
+ test_expect_success "usage: incompatible options: $opt and $opt2" '
+ test_incompatible_usage git cat-file $opt $opt2
+ '
+ done
+done
+
+test_too_many_arguments () {
+ test_expect_code 129 "$@" 2>err &&
+ grep -E "^fatal: too many arguments$" err
+}
+
+for opt in $short_modes $cw_modes
+do
+ args="one two three"
+ test_expect_success "usage: too many arguments: $opt $args" '
+ test_too_many_arguments git cat-file $opt $args
+ '
+
+ for opt2 in --buffer --follow-symlinks
+ do
+ test_expect_success "usage: incompatible arguments: $opt with batch option $opt2" '
+ test_incompatible_usage git cat-file $opt $opt2
+ '
+ done
+done
+
+for opt in --buffer \
+ --follow-symlinks \
+ --batch-all-objects
+do
+ test_expect_success "usage: bad option combination: $opt without batch mode" '
+ test_incompatible_usage git cat-file $opt &&
+ test_incompatible_usage git cat-file $opt commit HEAD
+ '
+done
+
echo_without_newline () {
printf '%s' "$*"
}
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index 42776984fe..3592d12442 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -5,6 +5,9 @@ test_description='sparse checkout builtin tests'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+GIT_TEST_SPLIT_INDEX=false
+export GIT_TEST_SPLIT_INDEX
+
. ./test-lib.sh
list_files() {
@@ -79,6 +82,12 @@ test_expect_success 'git sparse-checkout init' '
check_files repo a
'
+test_expect_success 'git sparse-checkout init in empty repo' '
+ test_when_finished rm -rf empty-repo blank-template &&
+ git init --template= empty-repo &&
+ git -C empty-repo sparse-checkout init
+'
+
test_expect_success 'git sparse-checkout list after init' '
git -C repo sparse-checkout list >actual &&
cat >expect <<-\EOF &&
@@ -228,36 +237,31 @@ test_expect_success 'sparse-checkout disable' '
'
test_expect_success 'sparse-index enabled and disabled' '
- (
- sane_unset GIT_TEST_SPLIT_INDEX &&
- git -C repo update-index --no-split-index &&
-
- git -C repo sparse-checkout init --cone --sparse-index &&
- test_cmp_config -C repo true index.sparse &&
- git -C repo ls-files --sparse >sparse &&
- git -C repo sparse-checkout disable &&
- git -C repo ls-files --sparse >full &&
-
- cat >expect <<-\EOF &&
- @@ -1,4 +1,7 @@
- a
- -deep/
- -folder1/
- -folder2/
- +deep/a
- +deep/deeper1/a
- +deep/deeper1/deepest/a
- +deep/deeper2/a
- +folder1/a
- +folder2/a
- EOF
+ git -C repo sparse-checkout init --cone --sparse-index &&
+ test_cmp_config -C repo true index.sparse &&
+ git -C repo ls-files --sparse >sparse &&
+ git -C repo sparse-checkout disable &&
+ git -C repo ls-files --sparse >full &&
- diff -u sparse full | tail -n +3 >actual &&
- test_cmp expect actual &&
+ cat >expect <<-\EOF &&
+ @@ -1,4 +1,7 @@
+ a
+ -deep/
+ -folder1/
+ -folder2/
+ +deep/a
+ +deep/deeper1/a
+ +deep/deeper1/deepest/a
+ +deep/deeper2/a
+ +folder1/a
+ +folder2/a
+ EOF
+
+ diff -u sparse full | tail -n +3 >actual &&
+ test_cmp expect actual &&
- git -C repo config --list >config &&
- ! grep index.sparse config
- )
+ git -C repo config --list >config &&
+ ! grep index.sparse config
'
test_expect_success 'cone mode: init and set' '
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 78359f1f4a..7dd9b325d9 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -2388,4 +2388,122 @@ test_expect_success '--get and --get-all with --fixed-value' '
test_must_fail git config --file=config --get-regexp --fixed-value fixed+ non-existent
'
+test_expect_success 'includeIf.hasconfig:remote.*.url' '
+ git init hasremoteurlTest &&
+ test_when_finished "rm -rf hasremoteurlTest" &&
+
+ cat >include-this <<-\EOF &&
+ [user]
+ this = this-is-included
+ EOF
+ cat >dont-include-that <<-\EOF &&
+ [user]
+ that = that-is-not-included
+ EOF
+ cat >>hasremoteurlTest/.git/config <<-EOF &&
+ [includeIf "hasconfig:remote.*.url:foourl"]
+ path = "$(pwd)/include-this"
+ [includeIf "hasconfig:remote.*.url:barurl"]
+ path = "$(pwd)/dont-include-that"
+ [remote "foo"]
+ url = foourl
+ EOF
+
+ echo this-is-included >expect-this &&
+ git -C hasremoteurlTest config --get user.this >actual-this &&
+ test_cmp expect-this actual-this &&
+
+ test_must_fail git -C hasremoteurlTest config --get user.that
+'
+
+test_expect_success 'includeIf.hasconfig:remote.*.url respects last-config-wins' '
+ git init hasremoteurlTest &&
+ test_when_finished "rm -rf hasremoteurlTest" &&
+
+ cat >include-two-three <<-\EOF &&
+ [user]
+ two = included-config
+ three = included-config
+ EOF
+ cat >>hasremoteurlTest/.git/config <<-EOF &&
+ [remote "foo"]
+ url = foourl
+ [user]
+ one = main-config
+ two = main-config
+ [includeIf "hasconfig:remote.*.url:foourl"]
+ path = "$(pwd)/include-two-three"
+ [user]
+ three = main-config
+ EOF
+
+ echo main-config >expect-main-config &&
+ echo included-config >expect-included-config &&
+
+ git -C hasremoteurlTest config --get user.one >actual &&
+ test_cmp expect-main-config actual &&
+
+ git -C hasremoteurlTest config --get user.two >actual &&
+ test_cmp expect-included-config actual &&
+
+ git -C hasremoteurlTest config --get user.three >actual &&
+ test_cmp expect-main-config actual
+'
+
+test_expect_success 'includeIf.hasconfig:remote.*.url globs' '
+ git init hasremoteurlTest &&
+ test_when_finished "rm -rf hasremoteurlTest" &&
+
+ printf "[user]\ndss = yes\n" >double-star-start &&
+ printf "[user]\ndse = yes\n" >double-star-end &&
+ printf "[user]\ndsm = yes\n" >double-star-middle &&
+ printf "[user]\nssm = yes\n" >single-star-middle &&
+ printf "[user]\nno = no\n" >no &&
+
+ cat >>hasremoteurlTest/.git/config <<-EOF &&
+ [remote "foo"]
+ url = https://foo/bar/baz
+ [includeIf "hasconfig:remote.*.url:**/baz"]
+ path = "$(pwd)/double-star-start"
+ [includeIf "hasconfig:remote.*.url:**/nomatch"]
+ path = "$(pwd)/no"
+ [includeIf "hasconfig:remote.*.url:https:/**"]
+ path = "$(pwd)/double-star-end"
+ [includeIf "hasconfig:remote.*.url:nomatch:/**"]
+ path = "$(pwd)/no"
+ [includeIf "hasconfig:remote.*.url:https:/**/baz"]
+ path = "$(pwd)/double-star-middle"
+ [includeIf "hasconfig:remote.*.url:https:/**/nomatch"]
+ path = "$(pwd)/no"
+ [includeIf "hasconfig:remote.*.url:https://*/bar/baz"]
+ path = "$(pwd)/single-star-middle"
+ [includeIf "hasconfig:remote.*.url:https://*/baz"]
+ path = "$(pwd)/no"
+ EOF
+
+ git -C hasremoteurlTest config --get user.dss &&
+ git -C hasremoteurlTest config --get user.dse &&
+ git -C hasremoteurlTest config --get user.dsm &&
+ git -C hasremoteurlTest config --get user.ssm &&
+ test_must_fail git -C hasremoteurlTest config --get user.no
+'
+
+test_expect_success 'includeIf.hasconfig:remote.*.url forbids remote url in such included files' '
+ git init hasremoteurlTest &&
+ test_when_finished "rm -rf hasremoteurlTest" &&
+
+ cat >include-with-url <<-\EOF &&
+ [remote "bar"]
+ url = barurl
+ EOF
+ cat >>hasremoteurlTest/.git/config <<-EOF &&
+ [includeIf "hasconfig:remote.*.url:foourl"]
+ path = "$(pwd)/include-with-url"
+ EOF
+
+ # test with any Git command
+ test_must_fail git -C hasremoteurlTest status 2>err &&
+ grep "fatal: remote URLs cannot be configured in file directly or indirectly included by includeIf.hasconfig:remote.*.url" err
+'
+
test_done
diff --git a/t/t1800-hook.sh b/t/t1800-hook.sh
new file mode 100755
index 0000000000..29718aa991
--- /dev/null
+++ b/t/t1800-hook.sh
@@ -0,0 +1,134 @@
+#!/bin/sh
+
+test_description='git-hook command'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'git hook usage' '
+ test_expect_code 129 git hook &&
+ test_expect_code 129 git hook run &&
+ test_expect_code 129 git hook run -h &&
+ test_expect_code 129 git hook run --unknown 2>err &&
+ grep "unknown option" err
+'
+
+test_expect_success 'git hook run: nonexistent hook' '
+ cat >stderr.expect <<-\EOF &&
+ error: cannot find a hook named test-hook
+ EOF
+ test_expect_code 1 git hook run test-hook 2>stderr.actual &&
+ test_cmp stderr.expect stderr.actual
+'
+
+test_expect_success 'git hook run: nonexistent hook with --ignore-missing' '
+ git hook run --ignore-missing does-not-exist 2>stderr.actual &&
+ test_must_be_empty stderr.actual
+'
+
+test_expect_success 'git hook run: basic' '
+ write_script .git/hooks/test-hook <<-EOF &&
+ echo Test hook
+ EOF
+
+ cat >expect <<-\EOF &&
+ Test hook
+ EOF
+ git hook run test-hook 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git hook run: stdout and stderr both write to our stderr' '
+ write_script .git/hooks/test-hook <<-EOF &&
+ echo >&1 Will end up on stderr
+ echo >&2 Will end up on stderr
+ EOF
+
+ cat >stderr.expect <<-\EOF &&
+ Will end up on stderr
+ Will end up on stderr
+ EOF
+ git hook run test-hook >stdout.actual 2>stderr.actual &&
+ test_cmp stderr.expect stderr.actual &&
+ test_must_be_empty stdout.actual
+'
+
+test_expect_success 'git hook run: exit codes are passed along' '
+ write_script .git/hooks/test-hook <<-EOF &&
+ exit 1
+ EOF
+
+ test_expect_code 1 git hook run test-hook &&
+
+ write_script .git/hooks/test-hook <<-EOF &&
+ exit 2
+ EOF
+
+ test_expect_code 2 git hook run test-hook &&
+
+ write_script .git/hooks/test-hook <<-EOF &&
+ exit 128
+ EOF
+
+ test_expect_code 128 git hook run test-hook &&
+
+ write_script .git/hooks/test-hook <<-EOF &&
+ exit 129
+ EOF
+
+ test_expect_code 129 git hook run test-hook
+'
+
+test_expect_success 'git hook run arg u ments without -- is not allowed' '
+ test_expect_code 129 git hook run test-hook arg u ments
+'
+
+test_expect_success 'git hook run -- pass arguments' '
+ write_script .git/hooks/test-hook <<-\EOF &&
+ echo $1
+ echo $2
+ EOF
+
+ cat >expect <<-EOF &&
+ arg
+ u ments
+ EOF
+
+ git hook run test-hook -- arg "u ments" 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git hook run -- out-of-repo runs excluded' '
+ write_script .git/hooks/test-hook <<-EOF &&
+ echo Test hook
+ EOF
+
+ nongit test_must_fail git hook run test-hook
+'
+
+test_expect_success 'git -c core.hooksPath=<PATH> hook run' '
+ mkdir my-hooks &&
+ write_script my-hooks/test-hook <<-\EOF &&
+ echo Hook ran $1 >>actual
+ EOF
+
+ cat >expect <<-\EOF &&
+ Test hook
+ Hook ran one
+ Hook ran two
+ Hook ran three
+ Hook ran four
+ EOF
+
+ # Test various ways of specifying the path. See also
+ # t1350-config-hooks-path.sh
+ >actual &&
+ git hook run test-hook -- ignored 2>>actual &&
+ git -c core.hooksPath=my-hooks hook run test-hook -- one 2>>actual &&
+ git -c core.hooksPath=my-hooks/ hook run test-hook -- two 2>>actual &&
+ git -c core.hooksPath="$PWD/my-hooks" hook run test-hook -- three 2>>actual &&
+ git -c core.hooksPath="$PWD/my-hooks/" hook run test-hook -- four 2>>actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t2108-update-index-refresh-racy.sh b/t/t2108-update-index-refresh-racy.sh
new file mode 100755
index 0000000000..bc5f2886fa
--- /dev/null
+++ b/t/t2108-update-index-refresh-racy.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+test_description='update-index refresh tests related to racy timestamps'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+reset_files () {
+ echo content >file &&
+ echo content >other &&
+ test_set_magic_mtime file &&
+ test_set_magic_mtime other
+}
+
+update_assert_changed () {
+ test_set_magic_mtime .git/index &&
+ test_might_fail git update-index "$1" &&
+ ! test_is_magic_mtime .git/index
+}
+
+test_expect_success 'setup' '
+ reset_files &&
+ # we are calling reset_files() a couple of times during tests;
+ # test-tool chmtime does not change the ctime; to not weaken
+ # or even break our tests, disable ctime-checks entirely
+ git config core.trustctime false &&
+ git add file other &&
+ git commit -m "initial import"
+'
+
+test_expect_success '--refresh has no racy timestamps to fix' '
+ reset_files &&
+ # set the index time far enough to the future;
+ # it must be at least 3 seconds for VFAT
+ test_set_magic_mtime .git/index +60 &&
+ git update-index --refresh &&
+ test_is_magic_mtime .git/index +60
+'
+
+test_expect_success '--refresh should fix racy timestamp' '
+ reset_files &&
+ update_assert_changed --refresh
+'
+
+test_expect_success '--really-refresh should fix racy timestamp' '
+ reset_files &&
+ update_assert_changed --really-refresh
+'
+
+test_expect_success '--refresh should fix racy timestamp if other file needs update' '
+ reset_files &&
+ echo content2 >other &&
+ test_set_magic_mtime other &&
+ update_assert_changed --refresh
+'
+
+test_expect_success '--refresh should fix racy timestamp if racy file needs update' '
+ reset_files &&
+ echo content2 >file &&
+ test_set_magic_mtime file &&
+ update_assert_changed --refresh
+'
+
+test_done
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 23dbd3c82e..71b1735e1d 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -416,4 +416,25 @@ test_expect_success MINGW,SYMLINKS_WINDOWS 'rebase when .git/logs is a symlink'
mv actual_logs .git/logs
'
+test_expect_success 'rebase when inside worktree subdirectory' '
+ git init main-wt &&
+ (
+ cd main-wt &&
+ git commit --allow-empty -m "initial" &&
+ mkdir -p foo/bar &&
+ test_commit foo/bar/baz &&
+ mkdir -p a/b &&
+ test_commit a/b/c &&
+ # create another branch for our other worktree
+ git branch other &&
+ git worktree add ../other-wt other &&
+ cd ../other-wt &&
+ # create and cd into a subdirectory
+ mkdir -p random/dir &&
+ cd random/dir &&
+ # now do the rebase
+ git rebase --onto HEAD^^ HEAD^ # drops the HEAD^ commit
+ )
+'
+
test_done
diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh
index 19c6f4acbf..1e9f7833dd 100755
--- a/t/t3412-rebase-root.sh
+++ b/t/t3412-rebase-root.sh
@@ -11,7 +11,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
log_with_names () {
git rev-list --topo-order --parents --pretty="tformat:%s" HEAD |
- git name-rev --stdin --name-only --refs=refs/heads/$1
+ git name-rev --annotate-stdin --name-only --refs=refs/heads/$1
}
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 207714655f..94537a6b40 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -326,7 +326,9 @@ test_expect_success 'correct message when there is nothing to do' '
test_expect_success 'setup again' '
git reset --hard &&
test_chmod +x file &&
- echo content >>file
+ echo content >>file &&
+ test_write_lines A B C D>file2 &&
+ git add file2
'
# Write the patch file with a new line at the top and bottom
@@ -341,13 +343,27 @@ test_expect_success 'setup patch' '
content
+lastline
\ No newline at end of file
+ diff --git a/file2 b/file2
+ index 8422d40..35b930a 100644
+ --- a/file2
+ +++ b/file2
+ @@ -1,4 +1,5 @@
+ -A
+ +Z
+ B
+ +Y
+ C
+ -D
+ +X
EOF
'
# Expected output, diff is similar to the patch but w/ diff at the top
test_expect_success 'setup expected' '
echo diff --git a/file b/file >expected &&
- cat patch |sed "/^index/s/ 100644/ 100755/" >>expected &&
+ sed -e "/^index 180b47c/s/ 100644/ 100755/" \
+ -e /1,5/s//1,4/ \
+ -e /Y/d patch >>expected &&
cat >expected-output <<-\EOF
--- a/file
+++ b/file
@@ -366,6 +382,28 @@ test_expect_success 'setup expected' '
content
+lastline
\ No newline at end of file
+ --- a/file2
+ +++ b/file2
+ @@ -1,4 +1,5 @@
+ -A
+ +Z
+ B
+ +Y
+ C
+ -D
+ +X
+ @@ -1,2 +1,2 @@
+ -A
+ +Z
+ B
+ @@ -2,2 +2,3 @@
+ B
+ +Y
+ C
+ @@ -3,2 +4,2 @@
+ C
+ -D
+ +X
EOF
'
@@ -373,9 +411,9 @@ test_expect_success 'setup expected' '
test_expect_success 'add first line works' '
git commit -am "clear local changes" &&
git apply patch &&
- printf "%s\n" s y y | git add -p file 2>error |
- sed -n -e "s/^([1-2]\/[1-2]) Stage this hunk[^@]*\(@@ .*\)/\1/" \
- -e "/^[-+@ \\\\]"/p >output &&
+ test_write_lines s y y s y n y | git add -p 2>error >raw-output &&
+ sed -n -e "s/^([1-9]\/[1-9]) Stage this hunk[^@]*\(@@ .*\)/\1/" \
+ -e "/^[-+@ \\\\]"/p raw-output >output &&
test_must_be_empty error &&
git diff --cached >diff &&
diff_cmp expected diff &&
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 5049559861..dc884107de 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -659,7 +659,7 @@ EOF
test_expect_success 'log --graph with full output' '
git log --graph --date-order --pretty=short |
- git name-rev --name-only --stdin |
+ git name-rev --name-only --annotate-stdin |
sed "s/Merge:.*/Merge: A B/;s/ *\$//" >actual &&
test_cmp expect actual
'
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 2f04cf9a1c..87881726ed 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -229,6 +229,18 @@ test_expect_success 'push with negotiation proceeds anyway even if negotiation f
test_i18ngrep "push negotiation failed" err
'
+test_expect_success 'push with negotiation does not attempt to fetch submodules' '
+ mk_empty submodule_upstream &&
+ test_commit -C submodule_upstream submodule_commit &&
+ git submodule add ./submodule_upstream submodule &&
+ mk_empty testrepo &&
+ git push testrepo $the_first_commit:refs/remotes/origin/first_commit &&
+ test_commit -C testrepo unrelated_commit &&
+ git -C testrepo config receive.hideRefs refs/remotes/origin/first_commit &&
+ git -c submodule.recurse=true -c protocol.version=2 -c push.negotiate=1 push testrepo refs/heads/main:refs/remotes/origin/main 2>err &&
+ ! grep "Fetching submodule" err
+'
+
test_expect_success 'push without wildcard' '
mk_empty testrepo &&
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 93ecfcdd24..081808009b 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -330,6 +330,19 @@ test_expect_success '--rebase --autostash fast forward' '
test_cmp_rev HEAD to-rebase-ff
'
+test_expect_success '--rebase with rebase.autostash succeeds on ff' '
+ test_when_finished "rm -fr src dst actual" &&
+ git init src &&
+ test_commit -C src "initial" file "content" &&
+ git clone src dst &&
+ test_commit -C src --printf "more_content" file "more content\ncontent\n" &&
+ echo "dirty" >>dst/file &&
+ test_config -C dst rebase.autostash true &&
+ git -C dst pull --rebase >actual 2>&1 &&
+ grep -q "Fast-forward" actual &&
+ grep -q "Applied autostash." actual
+'
+
test_expect_success '--rebase with conflicts shows advice' '
test_when_finished "git rebase --abort; git checkout -f to-rebase" &&
git checkout -b seq &&
diff --git a/t/t5700-protocol-v1.sh b/t/t5700-protocol-v1.sh
index 468bd3e13e..6c8d4c6cf1 100755
--- a/t/t5700-protocol-v1.sh
+++ b/t/t5700-protocol-v1.sh
@@ -149,6 +149,21 @@ test_expect_success 'push with file:// using protocol v1' '
grep "push< version 1" log
'
+test_expect_success 'cloning branchless tagless but not refless remote' '
+ rm -rf server client &&
+
+ git -c init.defaultbranch=main init server &&
+ echo foo >server/foo.txt &&
+ git -C server add foo.txt &&
+ git -C server commit -m "message" &&
+ git -C server update-ref refs/notbranch/alsonottag HEAD &&
+ git -C server checkout --detach &&
+ git -C server branch -D main &&
+ git -C server symbolic-ref HEAD refs/heads/nonexistentbranch &&
+
+ git -c protocol.version=1 clone "file://$(pwd)/server" client
+'
+
# Test protocol v1 with 'ssh://' transport
#
test_expect_success 'setup ssh wrapper' '
diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh
index 710f33e2aa..900254947f 100755
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -628,6 +628,18 @@ test_expect_success 'usage: --negotiate-only without --negotiation-tip' '
test_cmp err.expect err.actual
'
+test_expect_success 'usage: --negotiate-only with --recurse-submodules' '
+ cat >err.expect <<-\EOF &&
+ fatal: options '\''--negotiate-only'\'' and '\''--recurse-submodules'\'' cannot be used together
+ EOF
+
+ test_must_fail git -c protocol.version=2 -C client fetch \
+ --negotiate-only \
+ --recurse-submodules \
+ origin 2>err.actual &&
+ test_cmp err.expect err.actual
+'
+
test_expect_success 'file:// --negotiate-only' '
SERVER="server" &&
URI="file://$(pwd)/server" &&
diff --git a/t/t6007-rev-list-cherry-pick-file.sh b/t/t6007-rev-list-cherry-pick-file.sh
index aebe4b69e1..6f3e543977 100755
--- a/t/t6007-rev-list-cherry-pick-file.sh
+++ b/t/t6007-rev-list-cherry-pick-file.sh
@@ -58,7 +58,7 @@ EOF
test_expect_success '--left-right' '
git rev-list --left-right B...C > actual &&
- git name-rev --stdin --name-only --refs="*tags/*" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp expect actual.named
'
@@ -78,14 +78,14 @@ EOF
test_expect_success '--cherry-pick bar does not come up empty' '
git rev-list --left-right --cherry-pick B...C -- bar > actual &&
- git name-rev --stdin --name-only --refs="*tags/*" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp expect actual.named
'
test_expect_success 'bar does not come up empty' '
git rev-list --left-right B...C -- bar > actual &&
- git name-rev --stdin --name-only --refs="*tags/*" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp expect actual.named
'
@@ -97,14 +97,14 @@ EOF
test_expect_success '--cherry-pick bar does not come up empty (II)' '
git rev-list --left-right --cherry-pick F...E -- bar > actual &&
- git name-rev --stdin --name-only --refs="*tags/*" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp expect actual.named
'
test_expect_success 'name-rev multiple --refs combine inclusive' '
git rev-list --left-right --cherry-pick F...E -- bar >actual &&
- git name-rev --stdin --name-only --refs="*tags/F" --refs="*tags/E" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/F" --refs="*tags/E" \
<actual >actual.named &&
test_cmp expect actual.named
'
@@ -116,7 +116,7 @@ EOF
test_expect_success 'name-rev --refs excludes non-matched patterns' '
git rev-list --left-right --right-only --cherry-pick F...E -- bar >>expect &&
git rev-list --left-right --cherry-pick F...E -- bar >actual &&
- git name-rev --stdin --name-only --refs="*tags/F" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/F" \
<actual >actual.named &&
test_cmp expect actual.named
'
@@ -128,14 +128,14 @@ EOF
test_expect_success 'name-rev --exclude excludes matched patterns' '
git rev-list --left-right --right-only --cherry-pick F...E -- bar >>expect &&
git rev-list --left-right --cherry-pick F...E -- bar >actual &&
- git name-rev --stdin --name-only --refs="*tags/*" --exclude="*E" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" --exclude="*E" \
<actual >actual.named &&
test_cmp expect actual.named
'
test_expect_success 'name-rev --no-refs clears the refs list' '
git rev-list --left-right --cherry-pick F...E -- bar >expect &&
- git name-rev --stdin --name-only --refs="*tags/F" --refs="*tags/E" --no-refs --refs="*tags/G" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/F" --refs="*tags/E" --no-refs --refs="*tags/G" \
<expect >actual &&
test_cmp expect actual
'
@@ -149,7 +149,7 @@ EOF
test_expect_success '--cherry-mark' '
git rev-list --cherry-mark F...E -- bar > actual &&
- git name-rev --stdin --name-only --refs="*tags/*" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp expect actual.named
'
@@ -163,7 +163,7 @@ EOF
test_expect_success '--cherry-mark --left-right' '
git rev-list --cherry-mark --left-right F...E -- bar > actual &&
- git name-rev --stdin --name-only --refs="*tags/*" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp expect actual.named
'
@@ -174,14 +174,14 @@ EOF
test_expect_success '--cherry-pick --right-only' '
git rev-list --cherry-pick --right-only F...E -- bar > actual &&
- git name-rev --stdin --name-only --refs="*tags/*" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp expect actual.named
'
test_expect_success '--cherry-pick --left-only' '
git rev-list --cherry-pick --left-only E...F -- bar > actual &&
- git name-rev --stdin --name-only --refs="*tags/*" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp expect actual.named
'
@@ -193,7 +193,7 @@ EOF
test_expect_success '--cherry' '
git rev-list --cherry F...E -- bar > actual &&
- git name-rev --stdin --name-only --refs="*tags/*" \
+ git name-rev --annotate-stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp expect actual.named
'
diff --git a/t/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh
index 4f7fa8b6c0..4fedc614fa 100755
--- a/t/t6012-rev-list-simplify.sh
+++ b/t/t6012-rev-list-simplify.sh
@@ -12,7 +12,7 @@ note () {
}
unnote () {
- git name-rev --tags --stdin | sed -e "s|$OID_REGEX (tags/\([^)]*\)) |\1 |g"
+ git name-rev --tags --annotate-stdin | sed -e "s|$OID_REGEX (tags/\([^)]*\)) |\1 |g"
}
#
diff --git a/t/t6111-rev-list-treesame.sh b/t/t6111-rev-list-treesame.sh
index e07b6070e0..90ff141640 100755
--- a/t/t6111-rev-list-treesame.sh
+++ b/t/t6111-rev-list-treesame.sh
@@ -23,7 +23,8 @@ note () {
}
unnote () {
- git name-rev --tags --stdin | sed -e "s|$OID_REGEX (tags/\([^)]*\))\([ ]\)|\1\2|g"
+ git name-rev --tags --annotate-stdin | \
+ sed -e "s|$OID_REGEX (tags/\([^)]*\))\([ ]\)|\1\2|g"
}
test_expect_success setup '
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index d8af2bb9d2..9781b92aed 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -270,7 +270,7 @@ test_expect_success 'name-rev --all' '
test_cmp expect actual
'
-test_expect_success 'name-rev --stdin' '
+test_expect_success 'name-rev --annotate-stdin' '
>expect.unsorted &&
for rev in $(git rev-list --all)
do
@@ -278,11 +278,16 @@ test_expect_success 'name-rev --stdin' '
echo "$rev ($name)" >>expect.unsorted || return 1
done &&
sort <expect.unsorted >expect &&
- git rev-list --all | git name-rev --stdin >actual.unsorted &&
+ git rev-list --all | git name-rev --annotate-stdin >actual.unsorted &&
sort <actual.unsorted >actual &&
test_cmp expect actual
'
+test_expect_success 'name-rev --stdin deprecated' "
+ git rev-list --all | git name-rev --stdin 2>actual &&
+ grep -E 'warning: --stdin is deprecated' actual
+"
+
test_expect_success 'describe --contains with the exact tags' '
echo "A^0" >expect &&
tag_object=$(git rev-parse refs/tags/A) &&
diff --git a/t/t6429-merge-sequence-rename-caching.sh b/t/t6429-merge-sequence-rename-caching.sh
index 035edc40b1..f2bc8a7d2a 100755
--- a/t/t6429-merge-sequence-rename-caching.sh
+++ b/t/t6429-merge-sequence-rename-caching.sh
@@ -697,4 +697,71 @@ test_expect_success 'caching renames only on upstream side, part 2' '
)
'
+#
+# The following testcase just creates two simple renames (slightly modified
+# on both sides but without conflicting changes), and a directory full of
+# files that are otherwise uninteresting. The setup is as follows:
+#
+# base: unrelated/<BUNCH OF FILES>
+# numbers
+# values
+# upstream: modify: numbers
+# modify: values
+# topic: add: unrelated/foo
+# modify: numbers
+# modify: values
+# rename: numbers -> sequence
+# rename: values -> progression
+#
+# This is a trivial rename case, but we're curious what happens with a very
+# low renameLimit interacting with the restart optimization trying to notice
+# that unrelated/ looks like a trivial merge candidate.
+#
+test_expect_success 'avoid assuming we detected renames' '
+ git init redo-weirdness &&
+ (
+ cd redo-weirdness &&
+
+ mkdir unrelated &&
+ for i in $(test_seq 1 10)
+ do
+ >unrelated/$i
+ done &&
+ test_seq 2 10 >numbers &&
+ test_seq 12 20 >values &&
+ git add numbers values unrelated/ &&
+ git commit -m orig &&
+
+ git branch upstream &&
+ git branch topic &&
+
+ git switch upstream &&
+ test_seq 1 10 >numbers &&
+ test_seq 11 20 >values &&
+ git add numbers &&
+ git commit -m "Some tweaks" &&
+
+ git switch topic &&
+
+ >unrelated/foo &&
+ test_seq 2 12 >numbers &&
+ test_seq 12 22 >values &&
+ git add numbers values unrelated/ &&
+ git mv numbers sequence &&
+ git mv values progression &&
+ git commit -m A &&
+
+ #
+ # Actual testing
+ #
+
+ git switch --detach topic^0 &&
+
+ test_must_fail git -c merge.renameLimit=1 rebase upstream &&
+
+ git ls-files -u >actual &&
+ ! test_file_is_empty actual
+ )
+'
+
test_done
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index 05c6c02435..2b7ef6c41a 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -1647,13 +1647,33 @@ test_expect_success '"Initial commit" should not be noted in commit template' '
'
test_expect_success '--no-optional-locks prevents index update' '
- test-tool chmtime =1234567890 .git/index &&
+ test_set_magic_mtime .git/index &&
git --no-optional-locks status &&
- test-tool chmtime --get .git/index >out &&
- grep ^1234567890 out &&
+ test_is_magic_mtime .git/index &&
git status &&
- test-tool chmtime --get .git/index >out &&
- ! grep ^1234567890 out
+ ! test_is_magic_mtime .git/index
+'
+
+test_expect_success 'racy timestamps will be fixed for clean worktree' '
+ echo content >racy-dirty &&
+ echo content >racy-racy &&
+ git add racy* &&
+ git commit -m "racy test files" &&
+ # let status rewrite the index, if necessary; after that we expect
+ # no more index writes unless caused by racy timestamps; note that
+ # timestamps may already be racy now (depending on previous tests)
+ git status &&
+ test_set_magic_mtime .git/index &&
+ git status &&
+ ! test_is_magic_mtime .git/index
+'
+
+test_expect_success 'racy timestamps will be fixed for dirty worktree' '
+ echo content2 >racy-dirty &&
+ git status &&
+ test_set_magic_mtime .git/index &&
+ git status &&
+ ! test_is_magic_mtime .git/index
'
test_done
diff --git a/t/t8007-cat-file-textconv.sh b/t/t8007-cat-file-textconv.sh
index eacd49ade6..b067983ba1 100755
--- a/t/t8007-cat-file-textconv.sh
+++ b/t/t8007-cat-file-textconv.sh
@@ -19,6 +19,48 @@ test_expect_success 'setup ' '
GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00"
'
+test_expect_success 'usage: <bad rev>' '
+ cat >expect <<-\EOF &&
+ fatal: Not a valid object name HEAD2
+ EOF
+ test_must_fail git cat-file --textconv HEAD2 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'usage: <bad rev>:<bad path>' '
+ cat >expect <<-\EOF &&
+ fatal: invalid object name '\''HEAD2'\''.
+ EOF
+ test_must_fail git cat-file --textconv HEAD2:two.bin 2>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'usage: <rev>:<bad path>' '
+ cat >expect <<-\EOF &&
+ fatal: path '\''two.bin'\'' does not exist in '\''HEAD'\''
+ EOF
+ test_must_fail git cat-file --textconv HEAD:two.bin 2>actual &&
+ test_cmp expect actual
+'
+
+
+test_expect_success 'usage: <rev> with no <path>' '
+ cat >expect <<-\EOF &&
+ fatal: <object>:<path> required, only <object> '\''HEAD'\'' given
+ EOF
+ test_must_fail git cat-file --textconv HEAD 2>actual &&
+ test_cmp expect actual
+'
+
+
+test_expect_success 'usage: <bad rev>:<good (in HEAD) path>' '
+ cat >expect <<-\EOF &&
+ fatal: invalid object name '\''HEAD2'\''.
+ EOF
+ test_must_fail git cat-file --textconv HEAD2:one.bin 2>actual &&
+ test_cmp expect actual
+'
+
cat >expected <<EOF
bin: test version 2
EOF
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index aa0c20499b..84d0f40d76 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -539,7 +539,7 @@ test_expect_success $PREREQ "--validate respects relative core.hooksPath path" '
test_path_is_file my-hooks.ran &&
cat >expect <<-EOF &&
fatal: longline.patch: rejected by sendemail-validate hook
- fatal: command '"'"'my-hooks/sendemail-validate'"'"' died with exit code 1
+ fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch>'"'"' died with exit code 1
warning: no patches were sent
EOF
test_cmp expect actual
@@ -558,7 +558,7 @@ test_expect_success $PREREQ "--validate respects absolute core.hooksPath path" '
test_path_is_file my-hooks.ran &&
cat >expect <<-EOF &&
fatal: longline.patch: rejected by sendemail-validate hook
- fatal: command '"'"'$hooks_path/sendemail-validate'"'"' died with exit code 1
+ fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch>'"'"' died with exit code 1
warning: no patches were sent
EOF
test_cmp expect actual
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index c3d38aaccb..85385d2ede 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1840,3 +1840,36 @@ test_region () {
test_readlink () {
perl -le 'print readlink($_) for @ARGV' "$@"
}
+
+# Set mtime to a fixed "magic" timestamp in mid February 2009, before we
+# run an operation that may or may not touch the file. If the file was
+# touched, its timestamp will not accidentally have such an old timestamp,
+# as long as your filesystem clock is reasonably correct. To verify the
+# timestamp, follow up with test_is_magic_mtime.
+#
+# An optional increment to the magic timestamp may be specified as second
+# argument.
+test_set_magic_mtime () {
+ local inc=${2:-0} &&
+ local mtime=$((1234567890 + $inc)) &&
+ test-tool chmtime =$mtime "$1" &&
+ test_is_magic_mtime "$1" $inc
+}
+
+# Test whether the given file has the "magic" mtime set. This is meant to
+# be used in combination with test_set_magic_mtime.
+#
+# An optional increment to the magic timestamp may be specified as second
+# argument. Usually, this should be the same increment which was used for
+# the associated test_set_magic_mtime.
+test_is_magic_mtime () {
+ local inc=${2:-0} &&
+ local mtime=$((1234567890 + $inc)) &&
+ echo $mtime >.git/test-mtime-expect &&
+ test-tool chmtime --get "$1" >.git/test-mtime-actual &&
+ test_cmp .git/test-mtime-expect .git/test-mtime-actual
+ local ret=$?
+ rm -f .git/test-mtime-expect
+ rm -f .git/test-mtime-actual
+ return $ret
+}
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 0f7a137c7d..e4716b0b86 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -449,6 +449,8 @@ unset VISUAL EMAIL LANGUAGE $("$PERL_PATH" -e '
unset XDG_CACHE_HOME
unset XDG_CONFIG_HOME
unset GITPERLLIB
+unset GIT_TRACE2_PARENT_NAME
+unset GIT_TRACE2_PARENT_SID
TEST_AUTHOR_LOCALNAME=author
TEST_AUTHOR_DOMAIN=example.com
GIT_AUTHOR_EMAIL=${TEST_AUTHOR_LOCALNAME}@${TEST_AUTHOR_DOMAIN}
diff --git a/worktree.c b/worktree.c
index 6f598dcfcd..e8f6f6ae6f 100644
--- a/worktree.c
+++ b/worktree.c
@@ -28,13 +28,11 @@ static void add_head_info(struct worktree *wt)
{
int flags;
const char *target;
- int ignore_errno;
target = refs_resolve_ref_unsafe(get_worktree_ref_store(wt),
"HEAD",
0,
- &wt->head_oid, &flags,
- &ignore_errno);
+ &wt->head_oid, &flags);
if (!target)
return;
@@ -416,7 +414,6 @@ const struct worktree *find_shared_symref(struct worktree **worktrees,
const char *symref_target;
struct ref_store *refs;
int flags;
- int ignore_errno;
if (wt->is_bare)
continue;
@@ -434,8 +431,7 @@ const struct worktree *find_shared_symref(struct worktree **worktrees,
refs = get_worktree_ref_store(wt);
symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
- NULL, &flags,
- &ignore_errno);
+ NULL, &flags);
if ((flags & REF_ISSYMREF) &&
symref_target && !strcmp(symref_target, target)) {
existing = wt;
@@ -563,7 +559,6 @@ int other_head_refs(each_ref_fn fn, void *cb_data)
struct worktree *wt = *p;
struct object_id oid;
int flag;
- int ignore_errno;
if (wt->is_current)
continue;
@@ -573,7 +568,7 @@ int other_head_refs(each_ref_fn fn, void *cb_data)
if (refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
refname.buf,
RESOLVE_REF_READING,
- &oid, &flag, &ignore_errno))
+ &oid, &flag))
ret = fn(refname.buf, &oid, flag, cb_data);
if (ret)
break;
diff --git a/wrapper.c b/wrapper.c
index 36e12119d7..3258cdb171 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -463,8 +463,6 @@ int git_mkstemps_mode(char *pattern, int suffix_len, int mode)
static const int num_letters = ARRAY_SIZE(letters) - 1;
static const char x_pattern[] = "XXXXXX";
static const int num_x = ARRAY_SIZE(x_pattern) - 1;
- uint64_t value;
- struct timeval tv;
char *filename_template;
size_t len;
int fd, count;
@@ -485,12 +483,13 @@ int git_mkstemps_mode(char *pattern, int suffix_len, int mode)
* Replace pattern's XXXXXX characters with randomness.
* Try TMP_MAX different filenames.
*/
- gettimeofday(&tv, NULL);
- value = ((uint64_t)tv.tv_usec << 16) ^ tv.tv_sec ^ getpid();
filename_template = &pattern[len - num_x - suffix_len];
for (count = 0; count < TMP_MAX; ++count) {
- uint64_t v = value;
int i;
+ uint64_t v;
+ if (csprng_bytes(&v, sizeof(v)) < 0)
+ return error_errno("unable to get random bytes for temporary file");
+
/* Fill in the random bits. */
for (i = 0; i < num_x; i++) {
filename_template[i] = letters[v % num_letters];
@@ -506,12 +505,6 @@ int git_mkstemps_mode(char *pattern, int suffix_len, int mode)
*/
if (errno != EEXIST)
break;
- /*
- * This is a random value. It is only necessary that
- * the next TMP_MAX values generated by adding 7777 to
- * VALUE are different with (module 2^32).
- */
- value += 7777;
}
/* We return the null string if we can't find a unique file name. */
pattern[0] = '\0';
@@ -702,3 +695,69 @@ int open_nofollow(const char *path, int flags)
return open(path, flags);
#endif
}
+
+int csprng_bytes(void *buf, size_t len)
+{
+#if defined(HAVE_ARC4RANDOM) || defined(HAVE_ARC4RANDOM_LIBBSD)
+ /* This function never returns an error. */
+ arc4random_buf(buf, len);
+ return 0;
+#elif defined(HAVE_GETRANDOM)
+ ssize_t res;
+ char *p = buf;
+ while (len) {
+ res = getrandom(p, len, 0);
+ if (res < 0)
+ return -1;
+ len -= res;
+ p += res;
+ }
+ return 0;
+#elif defined(HAVE_GETENTROPY)
+ int res;
+ char *p = buf;
+ while (len) {
+ /* getentropy has a maximum size of 256 bytes. */
+ size_t chunk = len < 256 ? len : 256;
+ res = getentropy(p, chunk);
+ if (res < 0)
+ return -1;
+ len -= chunk;
+ p += chunk;
+ }
+ return 0;
+#elif defined(HAVE_RTLGENRANDOM)
+ if (!RtlGenRandom(buf, len))
+ return -1;
+ return 0;
+#elif defined(HAVE_OPENSSL_CSPRNG)
+ int res = RAND_bytes(buf, len);
+ if (res == 1)
+ return 0;
+ if (res == -1)
+ errno = ENOTSUP;
+ else
+ errno = EIO;
+ return -1;
+#else
+ ssize_t res;
+ char *p = buf;
+ int fd, err;
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd < 0)
+ return -1;
+ while (len) {
+ res = xread(fd, p, len);
+ if (res < 0) {
+ err = errno;
+ close(fd);
+ errno = err;
+ return -1;
+ }
+ len -= res;
+ p += res;
+ }
+ close(fd);
+ return 0;
+#endif
+}