summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.mailmap5
-rw-r--r--CODE_OF_CONDUCT.md2
-rw-r--r--Documentation/RelNotes/2.36.0.txt41
-rw-r--r--Documentation/config/clone.txt5
-rw-r--r--Documentation/config/extensions.txt31
-rw-r--r--Documentation/git-clone.txt7
-rw-r--r--Documentation/git-config.txt8
-rw-r--r--Documentation/git-sparse-checkout.txt16
-rw-r--r--Documentation/git-submodule.txt6
-rw-r--r--Documentation/git-worktree.txt275
-rw-r--r--Documentation/gitattributes.txt11
-rw-r--r--add-interactive.c6
-rw-r--r--archive-zip.c1
-rw-r--r--archive.c2
-rw-r--r--bisect.c3
-rw-r--r--bisect.h4
-rw-r--r--blame.c3
-rw-r--r--builtin/am.c9
-rw-r--r--builtin/bisect--helper.c93
-rw-r--r--builtin/blame.c8
-rw-r--r--builtin/clone.c36
-rw-r--r--builtin/commit.c36
-rw-r--r--builtin/count-objects.c2
-rw-r--r--builtin/difftool.c5
-rw-r--r--builtin/fast-export.c2
-rw-r--r--builtin/fast-import.c1
-rw-r--r--builtin/fetch.c12
-rw-r--r--builtin/grep.c35
-rw-r--r--builtin/hash-object.c2
-rw-r--r--builtin/help.c4
-rw-r--r--builtin/log.c18
-rw-r--r--builtin/ls-tree.c2
-rw-r--r--builtin/merge-base.c6
-rw-r--r--builtin/mktag.c2
-rw-r--r--builtin/mktree.c2
-rw-r--r--builtin/name-rev.c2
-rw-r--r--builtin/notes.c6
-rw-r--r--builtin/pack-objects.c2
-rw-r--r--builtin/prune-packed.c2
-rw-r--r--builtin/pull.c6
-rw-r--r--builtin/push.c2
-rw-r--r--builtin/rebase.c2
-rw-r--r--builtin/reflog.c2
-rw-r--r--builtin/remote.c2
-rw-r--r--builtin/replace.c2
-rw-r--r--builtin/reset.c1
-rw-r--r--builtin/rev-list.c2
-rw-r--r--builtin/send-pack.c2
-rw-r--r--builtin/shortlog.c1
-rw-r--r--builtin/show-branch.c1
-rw-r--r--builtin/sparse-checkout.c114
-rw-r--r--builtin/stripspace.c4
-rw-r--r--builtin/submodule--helper.c32
-rw-r--r--builtin/tag.c1
-rw-r--r--builtin/update-server-info.c2
-rw-r--r--builtin/worktree.c97
-rw-r--r--cache.h50
-rw-r--r--config.c40
-rw-r--r--config.h9
-rwxr-xr-xcontrib/rerere-train.sh2
-rw-r--r--date.c9
-rw-r--r--date.h74
-rw-r--r--diff-merges.c2
-rw-r--r--diff.c2
-rw-r--r--dir.c4
-rw-r--r--fetch-pack.c40
-rwxr-xr-xgit-submodule.sh17
-rw-r--r--git.c1
-rw-r--r--gpg-interface.c6
-rw-r--r--graph.c12
-rw-r--r--graph.h5
-rw-r--r--grep.c115
-rw-r--r--grep.h31
-rw-r--r--http-backend.c1
-rw-r--r--ident.c1
-rw-r--r--ls-refs.c3
-rw-r--r--merge-ort.c41
-rw-r--r--notes-merge.c2
-rw-r--r--object-name.c122
-rw-r--r--pack-bitmap-write.c6
-rw-r--r--parallel-checkout.c4
-rw-r--r--parse-options.c34
-rw-r--r--parse-options.h16
-rw-r--r--pretty.h10
-rw-r--r--progress.c66
-rw-r--r--progress.h9
-rw-r--r--ref-filter.c3
-rw-r--r--reflog-walk.h1
-rw-r--r--refs.c1
-rw-r--r--revision.c24
-rw-r--r--revision.h1
-rw-r--r--sequencer.c2
-rw-r--r--setup.c3
-rw-r--r--sparse-index.c10
-rw-r--r--strbuf.c1
-rw-r--r--submodule-config.c2
-rw-r--r--t/helper/test-date.c5
-rw-r--r--t/helper/test-progress.c50
-rwxr-xr-xt/t0001-init.sh3
-rwxr-xr-xt/t0006-date.sh2
-rwxr-xr-xt/t0500-progress-display.sh109
-rwxr-xr-xt/t1091-sparse-checkout-builtin.sh125
-rwxr-xr-xt/t1410-reflog.sh5
-rwxr-xr-xt/t1512-rev-parse-disambiguation.sh81
-rwxr-xr-xt/t2400-worktree-add.sh58
-rwxr-xr-xt/t3903-stash.sh21
-rwxr-xr-xt/t4150-am.sh2
-rwxr-xr-xt/t4202-log.sh93
-rwxr-xr-xt/t5316-pack-delta-depth.sh6
-rwxr-xr-xt/t5571-pre-push-hook.sh94
-rwxr-xr-xt/t5617-clone-submodules-remote.sh41
-rwxr-xr-xt/t6030-bisect-porcelain.sh45
-rwxr-xr-xt/t7500-commit-template-squash-signoff.sh2
-rwxr-xr-xt/t7810-grep.sh186
-rwxr-xr-xt/t7814-grep-recurse-submodules.sh41
-rw-r--r--t/test-lib-functions.sh29
-rw-r--r--worktree.c73
-rw-r--r--worktree.h21
118 files changed, 2093 insertions, 767 deletions
diff --git a/.mailmap b/.mailmap
index 9c6a446bdf..07db36a9bb 100644
--- a/.mailmap
+++ b/.mailmap
@@ -59,8 +59,9 @@ David Reiss <dreiss@facebook.com> <dreiss@dreiss-vmware.(none)>
David S. Miller <davem@davemloft.net>
David Turner <novalis@novalis.org> <dturner@twopensource.com>
David Turner <novalis@novalis.org> <dturner@twosigma.com>
-Derrick Stolee <dstolee@microsoft.com> <stolee@gmail.com>
-Derrick Stolee <dstolee@microsoft.com> Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com>
+Derrick Stolee <derrickstolee@github.com> <stolee@gmail.com>
+Derrick Stolee <derrickstolee@github.com> Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com>
+Derrick Stolee <derrickstolee@github.com> <dstolee@microsoft.com>
Deskin Miller <deskinm@umich.edu>
Đoàn Trần Công Danh <congdanhqx@gmail.com> Doan Tran Cong Danh
Dirk Süsserott <newsletter@dirk.my1.cc>
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 65651beada..0215b1fd4c 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -70,8 +70,8 @@ git@sfconservancy.org, or individually:
- Ævar Arnfjörð Bjarmason <avarab@gmail.com>
- Christian Couder <christian.couder@gmail.com>
- - Jeff King <peff@peff.net>
- Junio C Hamano <gitster@pobox.com>
+ - Taylor Blau <me@ttaylorr.com>
All complaints will be reviewed and investigated promptly and fairly.
diff --git a/Documentation/RelNotes/2.36.0.txt b/Documentation/RelNotes/2.36.0.txt
index 4e8309701b..de1e11e25a 100644
--- a/Documentation/RelNotes/2.36.0.txt
+++ b/Documentation/RelNotes/2.36.0.txt
@@ -9,6 +9,10 @@ Backward compatibility warts
* "git name-rev --stdin" has been deprecated and issues a warning
when used; use "git name-rev --annotate-stdin" instead.
+ * "git clone --filter=... --recurse-submodules" only makes the
+ top-level a partial clone, while submodules are fully cloned. This
+ behaviour is changed to pass the same filter down to the submodules.
+
Note to those who build from the source
@@ -65,6 +69,10 @@ Performance, Internal Implementation, Development Support etc.
spawning "git checkout" in "rebase", and update code paths that are
involved in the change.
+ * Messages "ort" merge backend prepares while dealing with conflicted
+ paths were unnecessarily confusing since it did not differentiate
+ inner merges and outer merges.
+
Fixes since v2.35
-----------------
@@ -194,6 +202,36 @@ Fixes since v2.35
"worktree" itself wasn't, which has been corrected.
(merge 2df5387ed0 jc/glossary-worktree later to maint).
+ * L10n support for a few error messages.
+ (merge 3d3c23b3a7 bs/forbid-i18n-of-protocol-token-in-fetch-pack later to maint).
+
+ * Test modernization.
+ (merge d4fe066e4b sy/t0001-use-path-is-helper later to maint).
+
+ * "git log --graph --graph" used to leak a graph structure, and there
+ was no way to countermand "--graph" that appear earlier on the
+ command line. A "--no-graph" option has been added and resource
+ leakage has been plugged.
+
+ * Error output given in response to an ambiguous object name has been
+ improved.
+ (merge 3a73c1dfaf ab/ambiguous-object-name later to maint).
+
+ * "git sparse-checkout" wants to work with per-worktree configuration,
+ but did not work well in a worktree attached to a bare repository.
+ (merge 3ce1138272 ds/sparse-checkout-requires-per-worktree-config later to maint).
+
+ * Setting core.untrackedCache to true failed to add the untracked
+ cache extension to the index.
+
+ * Workaround we have for versions of PCRE2 before their version 10.36
+ were in effect only for their versions newer than 10.36 by mistake,
+ which has been corrected.
+ (merge 97169fc361 rs/pcre-invalid-utf8-fix-fix later to maint).
+
+ * Document Taylor as a new member of Git PLC at SFC. Welcome.
+ (merge e8d56ca863 tb/coc-plc-update 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).
@@ -215,3 +253,6 @@ Fixes since v2.35
(merge cd26cd6c7c sy/modernize-t-lib-read-tree-m-3way later to maint).
(merge d17294a05e ab/hash-object-leakfix later to maint).
(merge b8403129d3 jd/t0015-modernize later to maint).
+ (merge 332acc248d ds/mailmap later to maint).
+ (merge 04bf052eef ab/grep-patterntype later to maint).
+ (merge 6ee36364eb ab/diff-free-more later to maint).
diff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt
index 7bcfbd18a5..26f4fb137a 100644
--- a/Documentation/config/clone.txt
+++ b/Documentation/config/clone.txt
@@ -6,3 +6,8 @@ clone.defaultRemoteName::
clone.rejectShallow::
Reject to clone a repository if it is a shallow one, can be overridden by
passing option `--reject-shallow` in command line. See linkgit:git-clone[1]
+
+clone.filterSubmodules::
+ If a partial clone filter is provided (see `--filter` in
+ linkgit:git-rev-list[1]) and `--recurse-submodules` is used, also apply
+ the filter to submodules.
diff --git a/Documentation/config/extensions.txt b/Documentation/config/extensions.txt
index 4e23d73cdc..bccaec7a96 100644
--- a/Documentation/config/extensions.txt
+++ b/Documentation/config/extensions.txt
@@ -6,3 +6,34 @@ extensions.objectFormat::
Note that this setting should only be set by linkgit:git-init[1] or
linkgit:git-clone[1]. Trying to change it after initialization will not
work and will produce hard-to-diagnose issues.
+
+extensions.worktreeConfig::
+ If enabled, then worktrees will load config settings from the
+ `$GIT_DIR/config.worktree` file in addition to the
+ `$GIT_COMMON_DIR/config` file. Note that `$GIT_COMMON_DIR` and
+ `$GIT_DIR` are the same for the main working tree, while other
+ working trees have `$GIT_DIR` equal to
+ `$GIT_COMMON_DIR/worktrees/<id>/`. The settings in the
+ `config.worktree` file will override settings from any other
+ config files.
++
+When enabling `extensions.worktreeConfig`, you must be careful to move
+certain values from the common config file to the main working tree's
+`config.worktree` file, if present:
++
+* `core.worktree` must be moved from `$GIT_COMMON_DIR/config` to
+ `$GIT_COMMON_DIR/config.worktree`.
+* If `core.bare` is true, then it must be moved from `$GIT_COMMON_DIR/config`
+ to `$GIT_COMMON_DIR/config.worktree`.
++
+It may also be beneficial to adjust the locations of `core.sparseCheckout`
+and `core.sparseCheckoutCone` depending on your desire for customizable
+sparse-checkout settings for each worktree. By default, the `git
+sparse-checkout` builtin enables `extensions.worktreeConfig`, assigns
+these config values on a per-worktree basis, and uses the
+`$GIT_DIR/info/sparse-checkout` file to specify the sparsity for each
+worktree independently. See linkgit:git-sparse-checkout[1] for more
+details.
++
+For historical reasons, `extensions.worktreeConfig` is respected
+regardless of the `core.repositoryFormatVersion` setting.
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 984d194934..632bd1348e 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -16,7 +16,7 @@ SYNOPSIS
[--depth <depth>] [--[no-]single-branch] [--no-tags]
[--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]
[--[no-]remote-submodules] [--jobs <n>] [--sparse] [--[no-]reject-shallow]
- [--filter=<filter>] [--] <repository>
+ [--filter=<filter> [--also-filter-submodules]] [--] <repository>
[<directory>]
DESCRIPTION
@@ -182,6 +182,11 @@ objects from the source repository into a pack in the cloned repository.
at least `<size>`. For more details on filter specifications, see
the `--filter` option in linkgit:git-rev-list[1].
+--also-filter-submodules::
+ Also apply the partial clone filter to any submodules in the repository.
+ Requires `--filter` and `--recurse-submodules`. This can be turned on by
+ default by setting the `clone.filterSubmodules` config option.
+
--mirror::
Set up a mirror of the source repository. This implies `--bare`.
Compared to `--bare`, `--mirror` not only maps local branches of the
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 2285effb36..bdcfd94b64 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -141,9 +141,13 @@ from all available files.
See also <<FILES>>.
--worktree::
- Similar to `--local` except that `.git/config.worktree` is
+ Similar to `--local` except that `$GIT_DIR/config.worktree` is
read from or written to if `extensions.worktreeConfig` is
- present. If not it's the same as `--local`.
+ enabled. If not it's the same as `--local`. Note that `$GIT_DIR`
+ is equal to `$GIT_COMMON_DIR` for the main working tree, but is of
+ the form `$GIT_DIR/worktrees/<id>/` for other working trees. See
+ linkgit:git-worktree[1] to learn how to enable
+ `extensions.worktreeConfig`.
-f <config-file>::
--file <config-file>::
diff --git a/Documentation/git-sparse-checkout.txt b/Documentation/git-sparse-checkout.txt
index b81dbe0654..94dad137b9 100644
--- a/Documentation/git-sparse-checkout.txt
+++ b/Documentation/git-sparse-checkout.txt
@@ -31,13 +31,21 @@ COMMANDS
Describe the patterns in the sparse-checkout file.
'set'::
- Enable the necessary config settings
- (extensions.worktreeConfig, core.sparseCheckout,
- core.sparseCheckoutCone) if they are not already enabled, and
- write a set of patterns to the sparse-checkout file from the
+ Enable the necessary sparse-checkout config settings
+ (`core.sparseCheckout`, `core.sparseCheckoutCone`, and
+ `index.sparse`) if they are not already set to the desired values,
+ and write a set of patterns to the sparse-checkout file from the
list of arguments following the 'set' subcommand. Update the
working directory to match the new patterns.
+
+To ensure that adjusting the sparse-checkout settings within a worktree
+does not alter the sparse-checkout settings in other worktrees, the 'set'
+subcommand will upgrade your repository config to use worktree-specific
+config if not already present. The sparsity defined by the arguments to
+the 'set' subcommand are stored in the worktree-specific sparse-checkout
+file. See linkgit:git-worktree[1] and the documentation of
+`extensions.worktreeConfig` in linkgit:git-config[1] for more details.
++
When the `--stdin` option is provided, the patterns are read from
standard in as a newline-delimited list instead of from the arguments.
+
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index 7e5f995f77..4d3ab6b9f9 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -133,7 +133,7 @@ If you really want to remove a submodule from the repository and commit
that use linkgit:git-rm[1] instead. See linkgit:gitsubmodules[7] for removal
options.
-update [--init] [--remote] [-N|--no-fetch] [--[no-]recommend-shallow] [-f|--force] [--checkout|--rebase|--merge] [--reference <repository>] [--depth <depth>] [--recursive] [--jobs <n>] [--[no-]single-branch] [--] [<path>...]::
+update [--init] [--remote] [-N|--no-fetch] [--[no-]recommend-shallow] [-f|--force] [--checkout|--rebase|--merge] [--reference <repository>] [--depth <depth>] [--recursive] [--jobs <n>] [--[no-]single-branch] [--filter <filter spec>] [--] [<path>...]::
+
--
Update the registered submodules to match what the superproject
@@ -177,6 +177,10 @@ submodule with the `--init` option.
If `--recursive` is specified, this command will recurse into the
registered submodules, and update any nested submodules within.
+
+If `--filter <filter spec>` is specified, the given partial clone filter will be
+applied to the submodule. See linkgit:git-rev-list[1] for details on filter
+specifications.
--
set-branch (-b|--branch) <branch> [--] <path>::
set-branch (-d|--default) [--] <path>::
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 9e862fbcf7..453e155022 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -25,45 +25,49 @@ Manage multiple working trees attached to the same repository.
A git repository can support multiple working trees, allowing you to check
out more than one branch at a time. With `git worktree add` a new working
-tree is associated with the repository. This new working tree is called a
-"linked working tree" as opposed to the "main working tree" prepared by
-linkgit:git-init[1] or linkgit:git-clone[1].
-A repository has one main working tree (if it's not a
-bare repository) and zero or more linked working trees. When you are done
-with a linked working tree, remove it with `git worktree remove`.
+tree is associated with the repository, along with additional metadata
+that differentiates that working tree from others in the same repository.
+The working tree, along with this metadata, is called a "worktree".
+
+This new worktree is called a "linked worktree" as opposed to the "main
+worktree" prepared by linkgit:git-init[1] or linkgit:git-clone[1].
+A repository has one main worktree (if it's not a bare repository) and
+zero or more linked worktrees. When you are done with a linked worktree,
+remove it with `git worktree remove`.
In its simplest form, `git worktree add <path>` automatically creates a
new branch whose name is the final component of `<path>`, which is
convenient if you plan to work on a new topic. For instance, `git
worktree add ../hotfix` creates new branch `hotfix` and checks it out at
-path `../hotfix`. To instead work on an existing branch in a new working
-tree, use `git worktree add <path> <branch>`. On the other hand, if you
-just plan to make some experimental changes or do testing without
-disturbing existing development, it is often convenient to create a
-'throwaway' working tree not associated with any branch. For instance,
-`git worktree add -d <path>` creates a new working tree with a detached
-`HEAD` at the same commit as the current branch.
+path `../hotfix`. To instead work on an existing branch in a new worktree,
+use `git worktree add <path> <branch>`. On the other hand, if you just
+plan to make some experimental changes or do testing without disturbing
+existing development, it is often convenient to create a 'throwaway'
+worktree not associated with any branch. For instance,
+`git worktree add -d <path>` creates a new worktree with a detached `HEAD`
+at the same commit as the current branch.
If a working tree is deleted without using `git worktree remove`, then
its associated administrative files, which reside in the repository
(see "DETAILS" below), will eventually be removed automatically (see
`gc.worktreePruneExpire` in linkgit:git-config[1]), or you can run
-`git worktree prune` in the main or any linked working tree to
-clean up any stale administrative files.
+`git worktree prune` in the main or any linked worktree to clean up any
+stale administrative files.
-If a linked working tree is stored on a portable device or network share
-which is not always mounted, you can prevent its administrative files from
-being pruned by issuing the `git worktree lock` command, optionally
-specifying `--reason` to explain why the working tree is locked.
+If the working tree for a linked worktree is stored on a portable device
+or network share which is not always mounted, you can prevent its
+administrative files from being pruned by issuing the `git worktree lock`
+command, optionally specifying `--reason` to explain why the worktree is
+locked.
COMMANDS
--------
add <path> [<commit-ish>]::
-Create `<path>` and checkout `<commit-ish>` into it. The new working directory
-is linked to the current repository, sharing everything except working
-directory specific files such as `HEAD`, `index`, etc. As a convenience,
-`<commit-ish>` may be a bare "`-`", which is synonymous with `@{-1}`.
+Create a worktree at `<path>` and checkout `<commit-ish>` into it. The new worktree
+is linked to the current repository, sharing everything except per-worktree
+files such as `HEAD`, `index`, etc. As a convenience, `<commit-ish>` may
+be a bare "`-`", which is synonymous with `@{-1}`.
+
If `<commit-ish>` is a branch name (call it `<branch>`) and is not found,
and neither `-b` nor `-B` nor `--detach` are used, but there does
@@ -84,100 +88,97 @@ branches from there if `<branch>` is ambiguous but exists on the
linkgit:git-config[1].
+
If `<commit-ish>` is omitted and neither `-b` nor `-B` nor `--detach` used,
-then, as a convenience, the new working tree is associated with a branch
-(call it `<branch>`) named after `$(basename <path>)`. If `<branch>`
-doesn't exist, a new branch based on `HEAD` is automatically created as
-if `-b <branch>` was given. If `<branch>` does exist, it will be
-checked out in the new working tree, if it's not checked out anywhere
-else, otherwise the command will refuse to create the working tree (unless
-`--force` is used).
+then, as a convenience, the new worktree is associated with a branch (call
+it `<branch>`) named after `$(basename <path>)`. If `<branch>` doesn't
+exist, a new branch based on `HEAD` is automatically created as if
+`-b <branch>` was given. If `<branch>` does exist, it will be checked out
+in the new worktree, if it's not checked out anywhere else, otherwise the
+command will refuse to create the worktree (unless `--force` is used).
list::
-List details of each working tree. The main working tree is listed first,
-followed by each of the linked working trees. The output details include
-whether the working tree is bare, the revision currently checked out, the
+List details of each worktree. The main worktree is listed first,
+followed by each of the linked worktrees. The output details include
+whether the worktree is bare, the revision currently checked out, the
branch currently checked out (or "detached HEAD" if none), "locked" if
-the worktree is locked, "prunable" if the worktree can be pruned by `prune`
-command.
+the worktree is locked, "prunable" if the worktree can be pruned by the
+`prune` command.
lock::
-If a working tree is on a portable device or network share which
-is not always mounted, lock it to prevent its administrative
-files from being pruned automatically. This also prevents it from
-being moved or deleted. Optionally, specify a reason for the lock
-with `--reason`.
+If a worktree is on a portable device or network share which is not always
+mounted, lock it to prevent its administrative files from being pruned
+automatically. This also prevents it from being moved or deleted.
+Optionally, specify a reason for the lock with `--reason`.
move::
-Move a working tree to a new location. Note that the main working tree
-or linked working trees containing submodules cannot be moved with this
-command. (The `git worktree repair` command, however, can reestablish
-the connection with linked working trees if you move the main working
-tree manually.)
+Move a worktree to a new location. Note that the main worktree or linked
+worktrees containing submodules cannot be moved with this command. (The
+`git worktree repair` command, however, can reestablish the connection
+with linked worktrees if you move the main worktree manually.)
prune::
-Prune working tree information in `$GIT_DIR/worktrees`.
+Prune worktree information in `$GIT_DIR/worktrees`.
remove::
-Remove a working tree. Only clean working trees (no untracked files
-and no modification in tracked files) can be removed. Unclean working
-trees or ones with submodules can be removed with `--force`. The main
-working tree cannot be removed.
+Remove a worktree. Only clean worktrees (no untracked files and no
+modification in tracked files) can be removed. Unclean worktrees or ones
+with submodules can be removed with `--force`. The main worktree cannot be
+removed.
repair [<path>...]::
-Repair working tree administrative files, if possible, if they have
-become corrupted or outdated due to external factors.
+Repair worktree administrative files, if possible, if they have become
+corrupted or outdated due to external factors.
+
-For instance, if the main working tree (or bare repository) is moved,
-linked working trees will be unable to locate it. Running `repair` in
-the main working tree will reestablish the connection from linked
-working trees back to the main working tree.
+For instance, if the main worktree (or bare repository) is moved, linked
+worktrees will be unable to locate it. Running `repair` in the main
+worktree will reestablish the connection from linked worktrees back to the
+main worktree.
+
-Similarly, if a linked working tree is moved without using `git worktree
-move`, the main working tree (or bare repository) will be unable to
-locate it. Running `repair` within the recently-moved working tree will
-reestablish the connection. If multiple linked working trees are moved,
-running `repair` from any working tree with each tree's new `<path>` as
-an argument, will reestablish the connection to all the specified paths.
+Similarly, if the working tree for a linked worktree is moved without
+using `git worktree move`, the main worktree (or bare repository) will be
+unable to locate it. Running `repair` within the recently-moved worktree
+will reestablish the connection. If multiple linked worktrees are moved,
+running `repair` from any worktree with each tree's new `<path>` as an
+argument, will reestablish the connection to all the specified paths.
+
-If both the main working tree and linked working trees have been moved
-manually, then running `repair` in the main working tree and specifying the
-new `<path>` of each linked working tree will reestablish all connections
-in both directions.
+If both the main worktree and linked worktrees have been moved manually,
+then running `repair` in the main worktree and specifying the new `<path>`
+of each linked worktree will reestablish all connections in both
+directions.
unlock::
-Unlock a working tree, allowing it to be pruned, moved or deleted.
+Unlock a worktree, allowing it to be pruned, moved or deleted.
OPTIONS
-------
-f::
--force::
- By default, `add` refuses to create a new working tree when
+ By default, `add` refuses to create a new worktree when
`<commit-ish>` is a branch name and is already checked out by
- another working tree, or if `<path>` is already assigned to some
- working tree but is missing (for instance, if `<path>` was deleted
+ another worktree, or if `<path>` is already assigned to some
+ worktree but is missing (for instance, if `<path>` was deleted
manually). This option overrides these safeguards. To add a missing but
- locked working tree path, specify `--force` twice.
+ locked worktree path, specify `--force` twice.
+
-`move` refuses to move a locked working tree unless `--force` is specified
-twice. If the destination is already assigned to some other working tree but is
+`move` refuses to move a locked worktree unless `--force` is specified
+twice. If the destination is already assigned to some other worktree but is
missing (for instance, if `<new-path>` was deleted manually), then `--force`
allows the move to proceed; use `--force` twice if the destination is locked.
+
-`remove` refuses to remove an unclean working tree unless `--force` is used.
-To remove a locked working tree, specify `--force` twice.
+`remove` refuses to remove an unclean worktree unless `--force` is used.
+To remove a locked worktree, specify `--force` twice.
-b <new-branch>::
-B <new-branch>::
With `add`, create a new branch named `<new-branch>` starting at
- `<commit-ish>`, and check out `<new-branch>` into the new working tree.
+ `<commit-ish>`, and check out `<new-branch>` into the new worktree.
If `<commit-ish>` is omitted, it defaults to `HEAD`.
By default, `-b` refuses to create a new branch if it already
exists. `-B` overrides this safeguard, resetting `<new-branch>` to
@@ -185,7 +186,7 @@ To remove a locked working tree, specify `--force` twice.
-d::
--detach::
- With `add`, detach `HEAD` in the new working tree. See "DETACHED HEAD"
+ With `add`, detach `HEAD` in the new worktree. See "DETACHED HEAD"
in linkgit:git-checkout[1].
--[no-]checkout::
@@ -211,7 +212,7 @@ This can also be set up as the default behaviour by using the
`--track` in linkgit:git-branch[1] for details.
--lock::
- Keep the working tree locked after creation. This is the
+ Keep the worktree locked after creation. This is the
equivalent of `git worktree lock` after `git worktree add`,
but without a race condition.
@@ -236,43 +237,42 @@ This can also be set up as the default behaviour by using the
With `list`, output additional information about worktrees (see below).
--expire <time>::
- With `prune`, only expire unused working trees older than `<time>`.
+ With `prune`, only expire unused worktrees older than `<time>`.
+
-With `list`, annotate missing working trees as prunable if they are
-older than `<time>`.
+With `list`, annotate missing worktrees as prunable if they are older than
+`<time>`.
--reason <string>::
- With `lock` or with `add --lock`, an explanation why the working tree is locked.
+ With `lock` or with `add --lock`, an explanation why the worktree
+ is locked.
<worktree>::
- Working trees can be identified by path, either relative or
- absolute.
+ Worktrees can be identified by path, either relative or absolute.
+
-If the last path components in the working tree's path is unique among
-working trees, it can be used to identify a working tree. For example if
-you only have two working trees, at `/abc/def/ghi` and `/abc/def/ggg`,
-then `ghi` or `def/ghi` is enough to point to the former working tree.
+If the last path components in the worktree's path is unique among
+worktrees, it can be used to identify a worktree. For example if you only
+have two worktrees, at `/abc/def/ghi` and `/abc/def/ggg`, then `ghi` or
+`def/ghi` is enough to point to the former worktree.
REFS
----
-In multiple working trees, some refs may be shared between all working
-trees and some refs are local. One example is `HEAD` which is different for each
-working tree. This section is about the sharing rules and how to access
-refs of one working tree from another.
-
-In general, all pseudo refs are per working tree and all refs starting
-with `refs/` are shared. Pseudo refs are ones like `HEAD` which are
-directly under `$GIT_DIR` instead of inside `$GIT_DIR/refs`. There are
-exceptions, however: refs inside `refs/bisect` and `refs/worktree` are not
-shared.
-
-Refs that are per working tree can still be accessed from another
-working tree via two special paths, `main-worktree` and `worktrees`. The
-former gives access to per-working tree refs of the main working tree,
-while the latter to all linked working trees.
+When using multiple worktrees, some refs are shared between all worktrees,
+but others are specific to an individual worktree. One example is `HEAD`,
+which is different for each worktree. This section is about the sharing
+rules and how to access refs of one worktree from another.
+
+In general, all pseudo refs are per-worktree and all refs starting with
+`refs/` are shared. Pseudo refs are ones like `HEAD` which are directly
+under `$GIT_DIR` instead of inside `$GIT_DIR/refs`. There are exceptions,
+however: refs inside `refs/bisect` and `refs/worktree` are not shared.
+
+Refs that are per-worktree can still be accessed from another worktree via
+two special paths, `main-worktree` and `worktrees`. The former gives
+access to per-worktree refs of the main worktree, while the latter to all
+linked worktrees.
For example, `main-worktree/HEAD` or `main-worktree/refs/bisect/good`
-resolve to the same value as the main working tree's `HEAD` and
+resolve to the same value as the main worktree's `HEAD` and
`refs/bisect/good` respectively. Similarly, `worktrees/foo/HEAD` or
`worktrees/bar/refs/bisect/bad` are the same as
`$GIT_COMMON_DIR/worktrees/foo/HEAD` and
@@ -284,13 +284,13 @@ which will handle refs correctly.
CONFIGURATION FILE
------------------
-By default, the repository `config` file is shared across all working
-trees. If the config variables `core.bare` or `core.worktree` are
-already present in the config file, they will be applied to the main
-working trees only.
+By default, the repository `config` file is shared across all worktrees.
+If the config variables `core.bare` or `core.worktree` are present in the
+common config file and `extensions.worktreeConfig` is disabled, then they
+will be applied to the main worktree only.
-In order to have configuration specific to working trees, you can turn
-on the `worktreeConfig` extension, e.g.:
+In order to have worktree-specific configuration, you can turn on the
+`worktreeConfig` extension, e.g.:
------------
$ git config extensions.worktreeConfig true
@@ -303,40 +303,45 @@ versions will refuse to access repositories with this extension.
Note that in this file, the exception for `core.bare` and `core.worktree`
is gone. If they exist in `$GIT_DIR/config`, you must move
-them to the `config.worktree` of the main working tree. You may also
-take this opportunity to review and move other configuration that you
-do not want to share to all working trees:
+them to the `config.worktree` of the main worktree. You may also take this
+opportunity to review and move other configuration that you do not want to
+share to all worktrees:
+
+ - `core.worktree` should never be shared.
+
+ - `core.bare` should not be shared if the value is `core.bare=true`.
- - `core.worktree` and `core.bare` should never be shared
+ - `core.sparseCheckout` should not be shared, unless you are sure you
+ always use sparse checkout for all worktrees.
- - `core.sparseCheckout` is recommended per working tree, unless you
- are sure you always use sparse checkout for all working trees.
+See the documentation of `extensions.worktreeConfig` in
+linkgit:git-config[1] for more details.
DETAILS
-------
-Each linked working tree has a private sub-directory in the repository's
+Each linked worktree has a private sub-directory in the repository's
`$GIT_DIR/worktrees` directory. The private sub-directory's name is usually
-the base name of the linked working tree's path, possibly appended with a
+the base name of the linked worktree's path, possibly appended with a
number to make it unique. For example, when `$GIT_DIR=/path/main/.git` the
command `git worktree add /path/other/test-next next` creates the linked
-working tree in `/path/other/test-next` and also creates a
+worktree in `/path/other/test-next` and also creates a
`$GIT_DIR/worktrees/test-next` directory (or `$GIT_DIR/worktrees/test-next1`
if `test-next` is already taken).
-Within a linked working tree, `$GIT_DIR` is set to point to this private
+Within a linked worktree, `$GIT_DIR` is set to point to this private
directory (e.g. `/path/main/.git/worktrees/test-next` in the example) and
-`$GIT_COMMON_DIR` is set to point back to the main working tree's `$GIT_DIR`
+`$GIT_COMMON_DIR` is set to point back to the main worktree's `$GIT_DIR`
(e.g. `/path/main/.git`). These settings are made in a `.git` file located at
-the top directory of the linked working tree.
+the top directory of the linked worktree.
Path resolution via `git rev-parse --git-path` uses either
`$GIT_DIR` or `$GIT_COMMON_DIR` depending on the path. For example, in the
-linked working tree `git rev-parse --git-path HEAD` returns
+linked worktree `git rev-parse --git-path HEAD` returns
`/path/main/.git/worktrees/test-next/HEAD` (not
`/path/other/test-next/.git/HEAD` or `/path/main/.git/HEAD`) while `git
rev-parse --git-path refs/heads/master` uses
`$GIT_COMMON_DIR` and returns `/path/main/.git/refs/heads/master`,
-since refs are shared across all working trees, except `refs/bisect` and
+since refs are shared across all worktrees, except `refs/bisect` and
`refs/worktree`.
See linkgit:gitrepository-layout[5] for more information. The rule of
@@ -344,8 +349,8 @@ thumb is do not make any assumption about whether a path belongs to
`$GIT_DIR` or `$GIT_COMMON_DIR` when you need to directly access something
inside `$GIT_DIR`. Use `git rev-parse --git-path` to get the final path.
-If you manually move a linked working tree, you need to update the `gitdir` file
-in the entry's directory. For example, if a linked working tree is moved
+If you manually move a linked worktree, you need to update the `gitdir` file
+in the entry's directory. For example, if a linked worktree is moved
to `/newpath/test-next` and its `.git` file points to
`/path/main/.git/worktrees/test-next`, then update
`/path/main/.git/worktrees/test-next/gitdir` to reference `/newpath/test-next`
@@ -354,10 +359,10 @@ automatically.
To prevent a `$GIT_DIR/worktrees` entry from being pruned (which
can be useful in some situations, such as when the
-entry's working tree is stored on a portable device), use the
+entry's worktree is stored on a portable device), use the
`git worktree lock` command, which adds a file named
`locked` to the entry's directory. The file contains the reason in
-plain text. For example, if a linked working tree's `.git` file points
+plain text. For example, if a linked worktree's `.git` file points
to `/path/main/.git/worktrees/test-next` then a file named
`/path/main/.git/worktrees/test-next/locked` will prevent the
`test-next` entry from being pruned. See
@@ -378,11 +383,11 @@ $ git worktree list
/path/to/other-linked-worktree 1234abc (detached HEAD)
------------
-The command also shows annotations for each working tree, according to its state.
+The command also shows annotations for each worktree, according to its state.
These annotations are:
- * `locked`, if the working tree is locked.
- * `prunable`, if the working tree can be pruned via `git worktree prune`.
+ * `locked`, if the worktree is locked.
+ * `prunable`, if the worktree can be pruned via `git worktree prune`.
------------
$ git worktree list
@@ -400,14 +405,14 @@ $ git worktree list --verbose
/path/to/linked-worktree abcd1234 [master]
/path/to/locked-worktree-no-reason abcd5678 (detached HEAD) locked
/path/to/locked-worktree-with-reason 1234abcd (brancha)
- locked: working tree path is mounted on a portable device
+ locked: worktree path is mounted on a portable device
/path/to/prunable-worktree 5678abc1 (detached HEAD)
prunable: gitdir file points to non-existent location
------------
Note that the annotation is moved to the next line if the additional
information is available, otherwise it stays on the same line as the
-working tree itself.
+worktree itself.
Porcelain Format
~~~~~~~~~~~~~~~~
@@ -416,7 +421,7 @@ label and value separated by a single space. Boolean attributes (like `bare`
and `detached`) are listed as a label only, and are present only
if the value is true. Some attributes (like `locked`) can be listed as a label
only or with a value depending upon whether a reason is available. The first
-attribute of a working tree is always `worktree`, an empty line indicates the
+attribute of a worktree is always `worktree`, an empty line indicates the
end of the record. For example:
------------
@@ -468,7 +473,7 @@ demands that you fix something immediately. You might typically use
linkgit:git-stash[1] to store your changes away temporarily, however, your
working tree is in such a state of disarray (with new, moved, and removed
files, and other bits and pieces strewn around) that you don't want to risk
-disturbing any of it. Instead, you create a temporary linked working tree to
+disturbing any of it. Instead, you create a temporary linked worktree to
make the emergency fix, remove it when done, and then resume your earlier
refactoring session.
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 60984a4682..a71dad2674 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -161,11 +161,12 @@ unspecified.
This attribute sets a specific line-ending style to be used in the
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.
+attribute is set or unspecified, or if it is set to `auto`, the file is
+detected as text, and it is stored with LF endings in the index. Note
+that setting this attribute on paths which are in the index with CRLF
+line endings may make the paths to be considered dirty unless
+`text=auto` is set. Adding the path to the index again will normalize
+the line endings in the index.
Set to string value "crlf"::
diff --git a/add-interactive.c b/add-interactive.c
index 6498ae196f..e1ab39cce3 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -797,14 +797,14 @@ static int run_revert(struct add_i_state *s, const struct pathspec *ps,
diffopt.flags.override_submodule_config = 1;
diffopt.repo = s->r;
- if (do_diff_cache(&oid, &diffopt))
+ if (do_diff_cache(&oid, &diffopt)) {
+ diff_free(&diffopt);
res = -1;
- else {
+ } else {
diffcore_std(&diffopt);
diff_flush(&diffopt);
}
free(paths);
- clear_pathspec(&diffopt.pathspec);
if (!res && write_locked_index(s->r->index, &index_lock,
COMMIT_LOCK) < 0)
diff --git a/archive-zip.c b/archive-zip.c
index 2961e01c75..8ea9d1a5da 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -9,6 +9,7 @@
#include "object-store.h"
#include "userdiff.h"
#include "xdiff-interface.h"
+#include "date.h"
static int zip_date;
static int zip_time;
diff --git a/archive.c b/archive.c
index d571249cf3..e29d0e00f6 100644
--- a/archive.c
+++ b/archive.c
@@ -12,7 +12,7 @@
static char const * const archive_usage[] = {
N_("git archive [<options>] <tree-ish> [<path>...]"),
- N_("git archive --list"),
+ "git archive --list",
N_("git archive --remote <repo> [--exec <cmd>] [<options>] <tree-ish> [<path>...]"),
N_("git archive --remote <repo> [--exec <cmd>] --list"),
NULL
diff --git a/bisect.c b/bisect.c
index 888949fba6..9e6a2b7f20 100644
--- a/bisect.c
+++ b/bisect.c
@@ -724,7 +724,8 @@ static int is_expected_rev(const struct object_id *oid)
return res;
}
-static enum bisect_error bisect_checkout(const struct object_id *bisect_rev, int no_checkout)
+enum bisect_error bisect_checkout(const struct object_id *bisect_rev,
+ int no_checkout)
{
char bisect_rev_hex[GIT_MAX_HEXSZ + 1];
struct commit *commit;
diff --git a/bisect.h b/bisect.h
index ec24ac2d7e..1015aeb8ea 100644
--- a/bisect.h
+++ b/bisect.h
@@ -3,6 +3,7 @@
struct commit_list;
struct repository;
+struct object_id;
/*
* Find bisection. If something is found, `reaches` will be the number of
@@ -69,4 +70,7 @@ void read_bisect_terms(const char **bad, const char **good);
int bisect_clean_state(void);
+enum bisect_error bisect_checkout(const struct object_id *bisect_rev,
+ int no_checkout);
+
#endif
diff --git a/blame.c b/blame.c
index 083d99fdbc..186ad96120 100644
--- a/blame.c
+++ b/blame.c
@@ -1403,7 +1403,6 @@ static struct blame_origin *find_origin(struct repository *r,
}
}
diff_flush(&diff_opts);
- clear_pathspec(&diff_opts.pathspec);
return porigin;
}
@@ -1447,7 +1446,6 @@ static struct blame_origin *find_rename(struct repository *r,
}
}
diff_flush(&diff_opts);
- clear_pathspec(&diff_opts.pathspec);
return porigin;
}
@@ -2328,7 +2326,6 @@ static void find_copy_in_parent(struct blame_scoreboard *sb,
} while (unblamed);
target->suspects = reverse_blame(leftover, NULL);
diff_flush(&diff_opts);
- clear_pathspec(&diff_opts.pathspec);
}
/*
diff --git a/builtin/am.c b/builtin/am.c
index 7de2c89ef2..0f4111bafa 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -34,6 +34,7 @@
#include "string-list.h"
#include "packfile.h"
#include "repository.h"
+#include "pretty.h"
/**
* Returns the length of the first line of msg.
@@ -199,7 +200,7 @@ static int am_option_parse_empty(const struct option *opt,
else if (!strcmp(arg, "keep"))
*opt_value = KEEP_EMPTY_COMMIT;
else
- return error(_("Invalid value for --empty: %s"), arg);
+ return error(_("invalid value for '%s': '%s'"), "--empty", arg);
return 0;
}
@@ -2239,7 +2240,8 @@ static int parse_opt_patchformat(const struct option *opt, const char *arg, int
* when you add new options
*/
else
- return error(_("Invalid value for --patch-format: %s"), arg);
+ return error(_("invalid value for '%s': '%s'"),
+ "--patch-format", arg);
return 0;
}
@@ -2282,7 +2284,8 @@ static int parse_opt_show_current_patch(const struct option *opt, const char *ar
break;
}
if (new_value >= ARRAY_SIZE(valid_modes))
- return error(_("Invalid value for --show-current-patch: %s"), arg);
+ return error(_("invalid value for '%s': '%s'"),
+ "--show-current-patch", arg);
}
if (resume->mode == RESUME_SHOW_PATCH && new_value != resume->sub_mode)
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 28a2e6a575..626de92098 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -22,15 +22,15 @@ static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
static const char * const git_bisect_helper_usage[] = {
N_("git bisect--helper --bisect-reset [<commit>]"),
- N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"),
+ "git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]",
N_("git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]"
" [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"),
- N_("git bisect--helper --bisect-next"),
+ "git bisect--helper --bisect-next",
N_("git bisect--helper --bisect-state (bad|new) [<rev>]"),
N_("git bisect--helper --bisect-state (good|old) [<rev>...]"),
N_("git bisect--helper --bisect-replay <filename>"),
N_("git bisect--helper --bisect-skip [(<rev>|<range>)...]"),
- N_("git bisect--helper --bisect-visualize"),
+ "git bisect--helper --bisect-visualize",
N_("git bisect--helper --bisect-run <cmd>..."),
NULL
};
@@ -1089,14 +1089,52 @@ static int bisect_visualize(struct bisect_terms *terms, const char **argv, int a
return res;
}
+static int get_first_good(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
+{
+ oidcpy(cb_data, oid);
+ return 1;
+}
+
+static int verify_good(const struct bisect_terms *terms,
+ const char **quoted_argv)
+{
+ int rc;
+ enum bisect_error res;
+ struct object_id good_rev;
+ struct object_id current_rev;
+ char *good_glob = xstrfmt("%s-*", terms->term_good);
+ int no_checkout = ref_exists("BISECT_HEAD");
+
+ for_each_glob_ref_in(get_first_good, good_glob, "refs/bisect/",
+ &good_rev);
+ free(good_glob);
+
+ if (read_ref(no_checkout ? "BISECT_HEAD" : "HEAD", &current_rev))
+ return -1;
+
+ res = bisect_checkout(&good_rev, no_checkout);
+ if (res != BISECT_OK)
+ return -1;
+
+ printf(_("running %s\n"), quoted_argv[0]);
+ rc = run_command_v_opt(quoted_argv, RUN_USING_SHELL);
+
+ res = bisect_checkout(&current_rev, no_checkout);
+ if (res != BISECT_OK)
+ return -1;
+
+ return rc;
+}
+
static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
{
int res = BISECT_OK;
struct strbuf command = STRBUF_INIT;
- struct strvec args = STRVEC_INIT;
struct strvec run_args = STRVEC_INIT;
const char *new_state;
int temporary_stdout_fd, saved_stdout;
+ int is_first_run = 1;
if (bisect_next_check(terms, NULL))
return BISECT_FAILED;
@@ -1111,16 +1149,37 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
strvec_push(&run_args, command.buf);
while (1) {
- strvec_clear(&args);
-
printf(_("running %s\n"), command.buf);
res = run_command_v_opt(run_args.v, RUN_USING_SHELL);
+ /*
+ * Exit code 126 and 127 can either come from the shell
+ * if it was unable to execute or even find the script,
+ * or from the script itself. Check with a known-good
+ * revision to avoid trashing the bisect run due to a
+ * missing or non-executable script.
+ */
+ if (is_first_run && (res == 126 || res == 127)) {
+ int rc = verify_good(terms, run_args.v);
+ is_first_run = 0;
+ if (rc < 0) {
+ error(_("unable to verify '%s' on good"
+ " revision"), command.buf);
+ res = BISECT_FAILED;
+ break;
+ }
+ if (rc == res) {
+ error(_("bogus exit code %d for good revision"),
+ rc);
+ res = BISECT_FAILED;
+ break;
+ }
+ }
+
if (res < 0 || 128 <= res) {
error(_("bisect run failed: exit code %d from"
" '%s' is < 0 or >= 128"), res, command.buf);
- strbuf_release(&command);
- return res;
+ break;
}
if (res == 125)
@@ -1132,8 +1191,10 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
temporary_stdout_fd = open(git_path_bisect_run(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
- if (temporary_stdout_fd < 0)
- return error_errno(_("cannot open file '%s' for writing"), git_path_bisect_run());
+ if (temporary_stdout_fd < 0) {
+ res = error_errno(_("cannot open file '%s' for writing"), git_path_bisect_run());
+ break;
+ }
fflush(stdout);
saved_stdout = dup(1);
@@ -1158,16 +1219,16 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
res = BISECT_OK;
} else if (res) {
error(_("bisect run failed: 'git bisect--helper --bisect-state"
- " %s' exited with error code %d"), args.v[0], res);
+ " %s' exited with error code %d"), new_state, res);
} else {
continue;
}
-
- strbuf_release(&command);
- strvec_clear(&args);
- strvec_clear(&run_args);
- return res;
+ break;
}
+
+ strbuf_release(&command);
+ strvec_clear(&run_args);
+ return res;
}
int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
diff --git a/builtin/blame.c b/builtin/blame.c
index 7fafeac408..8d15b68afc 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -721,8 +721,8 @@ static int git_blame_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, "color.blame.repeatedlines")) {
if (color_parse_mem(value, strlen(value), repeated_meta_color))
- warning(_("invalid color '%s' in color.blame.repeatedLines"),
- value);
+ warning(_("invalid value for '%s': '%s'"),
+ "color.blame.repeatedLines", value);
return 0;
}
if (!strcmp(var, "color.blame.highlightrecent")) {
@@ -739,7 +739,8 @@ static int git_blame_config(const char *var, const char *value, void *cb)
coloring_mode &= ~(OUTPUT_COLOR_LINE |
OUTPUT_SHOW_AGE_WITH_COLOR);
} else {
- warning(_("invalid value for blame.coloring"));
+ warning(_("invalid value for '%s': '%s'"),
+ "blame.coloring", value);
return 0;
}
}
@@ -934,6 +935,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
parse_revision_opt(&revs, &ctx, options, blame_opt_usage);
}
parse_done:
+ revision_opts_finish(&revs);
no_whole_file_rename = !revs.diffopt.flags.follow_renames;
xdl_opts |= revs.diffopt.xdl_opts & XDF_INDENT_HEURISTIC;
revs.diffopt.flags.follow_renames = 0;
diff --git a/builtin/clone.c b/builtin/clone.c
index 0d80b135c9..a572cda503 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -72,6 +72,8 @@ static int option_dissociate;
static int max_jobs = -1;
static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP;
static struct list_objects_filter_options filter_options;
+static int option_filter_submodules = -1; /* unspecified */
+static int config_filter_submodules = -1; /* unspecified */
static struct string_list server_options = STRING_LIST_INIT_NODUP;
static int option_remote_submodules;
@@ -151,6 +153,8 @@ static struct option builtin_clone_options[] = {
OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
TRANSPORT_FAMILY_IPV6),
OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
+ OPT_BOOL(0, "also-filter-submodules", &option_filter_submodules,
+ N_("apply partial clone filters to submodules")),
OPT_BOOL(0, "remote-submodules", &option_remote_submodules,
N_("any cloned submodules will use their remote-tracking branch")),
OPT_BOOL(0, "sparse", &option_sparse_checkout,
@@ -651,7 +655,7 @@ static int git_sparse_checkout_init(const char *repo)
return result;
}
-static int checkout(int submodule_progress)
+static int checkout(int submodule_progress, int filter_submodules)
{
struct object_id oid;
char *head;
@@ -730,6 +734,10 @@ static int checkout(int submodule_progress)
strvec_push(&args, "--no-fetch");
}
+ if (filter_submodules && filter_options.choice)
+ strvec_pushf(&args, "--filter=%s",
+ expand_list_objects_filter_spec(&filter_options));
+
if (option_single_branch >= 0)
strvec_push(&args, option_single_branch ?
"--single-branch" :
@@ -750,6 +758,8 @@ static int git_clone_config(const char *k, const char *v, void *cb)
}
if (!strcmp(k, "clone.rejectshallow"))
config_reject_shallow = git_config_bool(k, v);
+ if (!strcmp(k, "clone.filtersubmodules"))
+ config_filter_submodules = git_config_bool(k, v);
return git_default_config(k, v, cb);
}
@@ -872,6 +882,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
struct remote *remote;
int err = 0, complete_refs_before_fetch = 1;
int submodule_progress;
+ int filter_submodules = 0;
struct transport_ls_refs_options transport_ls_refs_options =
TRANSPORT_LS_REFS_OPTIONS_INIT;
@@ -1068,6 +1079,27 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
reject_shallow = option_reject_shallow;
/*
+ * If option_filter_submodules is specified from CLI option,
+ * ignore config_filter_submodules from git_clone_config.
+ */
+ if (config_filter_submodules != -1)
+ filter_submodules = config_filter_submodules;
+ if (option_filter_submodules != -1)
+ filter_submodules = option_filter_submodules;
+
+ /*
+ * Exit if the user seems to be doing something silly with submodule
+ * filter flags (but not with filter configs, as those should be
+ * set-and-forget).
+ */
+ if (option_filter_submodules > 0 && !filter_options.choice)
+ die(_("the option '%s' requires '%s'"),
+ "--also-filter-submodules", "--filter");
+ if (option_filter_submodules > 0 && !option_recurse_submodules.nr)
+ die(_("the option '%s' requires '%s'"),
+ "--also-filter-submodules", "--recurse-submodules");
+
+ /*
* apply the remote name provided by --origin only after this second
* call to git_config, to ensure it overrides all config-based values.
*/
@@ -1300,7 +1332,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
}
junk_mode = JUNK_LEAVE_REPO;
- err = checkout(submodule_progress);
+ err = checkout(submodule_progress, filter_submodules);
free(remote_name);
strbuf_release(&reflog_msg);
diff --git a/builtin/commit.c b/builtin/commit.c
index b9ed0374e3..8b8bdad395 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -37,6 +37,7 @@
#include "help.h"
#include "commit-reach.h"
#include "commit-graph.h"
+#include "pretty.h"
static const char * const builtin_commit_usage[] = {
N_("git commit [<options>] [--] <pathspec>..."),
@@ -1242,8 +1243,6 @@ static int parse_and_validate_options(int argc, const char *argv[],
struct commit *current_head,
struct wt_status *s)
{
- int f = 0;
-
argc = parse_options(argc, argv, prefix, options, usage, 0);
finalize_deferred_config(s);
@@ -1251,7 +1250,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
force_author = find_author_by_nickname(force_author);
if (force_author && renew_authorship)
- die(_("Using both --reset-author and --author does not make sense"));
+ die(_("options '%s' and '%s' cannot be used together"), "--reset-author", "--author");
if (logfile || have_option_m || use_message)
use_editor = 0;
@@ -1268,20 +1267,16 @@ static int parse_and_validate_options(int argc, const char *argv[],
die(_("You are in the middle of a rebase -- cannot amend."));
}
if (fixup_message && squash_message)
- die(_("Options --squash and --fixup cannot be used together"));
- if (use_message)
- f++;
- if (edit_message)
- f++;
- if (fixup_message)
- f++;
- if (logfile)
- f++;
- if (f > 1)
- die(_("Only one of -c/-C/-F/--fixup can be used."));
- if (have_option_m && (edit_message || use_message || logfile))
- die((_("Option -m cannot be combined with -c/-C/-F.")));
- if (f || have_option_m)
+ die(_("options '%s' and '%s' cannot be used together"), "--squash", "--fixup");
+ die_for_incompatible_opt4(!!use_message, "-C",
+ !!edit_message, "-c",
+ !!logfile, "-F",
+ !!fixup_message, "--fixup");
+ die_for_incompatible_opt4(have_option_m, "-m",
+ !!edit_message, "-c",
+ !!use_message, "-C",
+ !!logfile, "-F");
+ if (use_message || edit_message || logfile ||fixup_message || have_option_m)
template_file = NULL;
if (edit_message)
use_message = edit_message;
@@ -1306,9 +1301,10 @@ static int parse_and_validate_options(int argc, const char *argv[],
if (patch_interactive)
interactive = 1;
- if (also + only + all + interactive > 1)
- die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
-
+ die_for_incompatible_opt4(also, "-i/--include",
+ only, "-o/--only",
+ all, "-a/--all",
+ interactive, "--interactive/-p/--patch");
if (fixup_message) {
/*
* We limit --fixup's suboptions to only alpha characters.
diff --git a/builtin/count-objects.c b/builtin/count-objects.c
index 3fae474f6f..07b9419596 100644
--- a/builtin/count-objects.c
+++ b/builtin/count-objects.c
@@ -87,7 +87,7 @@ static int print_alternate(struct object_directory *odb, void *data)
}
static char const * const count_objects_usage[] = {
- N_("git count-objects [-v] [-H | --human-readable]"),
+ "git count-objects [-v] [-H | --human-readable]",
NULL
};
diff --git a/builtin/difftool.c b/builtin/difftool.c
index c79fbbf67e..faa3507369 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -732,8 +732,9 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
} else if (dir_diff)
die(_("options '%s' and '%s' cannot be used together"), "--dir-diff", "--no-index");
- if (use_gui_tool + !!difftool_cmd + !!extcmd > 1)
- die(_("options '%s', '%s', and '%s' cannot be used together"), "--gui", "--tool", "--extcmd");
+ die_for_incompatible_opt3(use_gui_tool, "--gui",
+ !!difftool_cmd, "--tool",
+ !!extcmd, "--extcmd");
if (use_gui_tool)
setenv("GIT_MERGETOOL_GUI", "true", 1);
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 9f1c730e58..510139e9b5 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -26,7 +26,7 @@
#include "commit-slab.h"
static const char *fast_export_usage[] = {
- N_("git fast-export [rev-list-opts]"),
+ N_("git fast-export [<rev-list-opts>]"),
NULL
};
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index 2b2e28bad7..28f2b9cc91 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -19,6 +19,7 @@
#include "mem-pool.h"
#include "commit-reach.h"
#include "khash.h"
+#include "date.h"
#define PACK_ID_BITS 16
#define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 5583f71ef3..95832ba1df 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -764,8 +764,8 @@ static void prepare_format_display(struct ref *ref_map)
else if (!strcasecmp(format, "compact"))
compact_format = 1;
else
- die(_("configuration fetch.output contains invalid value %s"),
- format);
+ die(_("invalid value for '%s': '%s'"),
+ "fetch.output", format);
for (rm = ref_map; rm; rm = rm->next) {
if (rm->status == REF_STATUS_REJECT_SHALLOW ||
@@ -1094,12 +1094,15 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
struct ref *rm;
char *url;
int want_status;
- int summary_width = transport_summary_width(ref_map);
+ int summary_width = 0;
rc = open_fetch_head(&fetch_head);
if (rc)
return -1;
+ if (verbosity >= 0)
+ summary_width = transport_summary_width(ref_map);
+
if (raw_url)
url = transport_anonymize_url(raw_url);
else
@@ -1345,7 +1348,6 @@ static int prune_refs(struct refspec *rs, struct ref *ref_map,
int url_len, i, result = 0;
struct ref *ref, *stale_refs = get_stale_heads(rs, ref_map);
char *url;
- int summary_width = transport_summary_width(stale_refs);
const char *dangling_msg = dry_run
? _(" (%s will become dangling)")
: _(" (%s has become dangling)");
@@ -1374,6 +1376,8 @@ static int prune_refs(struct refspec *rs, struct ref *ref_map,
}
if (verbosity >= 0) {
+ int summary_width = transport_summary_width(stale_refs);
+
for (ref = stale_refs; ref; ref = ref->next) {
struct strbuf sb = STRBUF_INIT;
if (!shown_url) {
diff --git a/builtin/grep.c b/builtin/grep.c
index 9e34a820ad..f1a924eade 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -26,6 +26,8 @@
#include "object-store.h"
#include "packfile.h"
+static const char *grep_prefix;
+
static char const * const grep_usage[] = {
N_("git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"),
NULL
@@ -284,7 +286,7 @@ static int wait_all(void)
static int grep_cmd_config(const char *var, const char *value, void *cb)
{
int st = grep_config(var, value, cb);
- if (git_color_default_config(var, value, cb) < 0)
+ if (git_color_default_config(var, value, NULL) < 0)
st = -1;
if (!strcmp(var, "grep.threads")) {
@@ -315,11 +317,11 @@ static void grep_source_name(struct grep_opt *opt, const char *filename,
strbuf_reset(out);
if (opt->null_following_name) {
- if (opt->relative && opt->prefix_length) {
+ if (opt->relative && grep_prefix) {
struct strbuf rel_buf = STRBUF_INIT;
const char *rel_name =
relative_path(filename + tree_name_len,
- opt->prefix, &rel_buf);
+ grep_prefix, &rel_buf);
if (tree_name_len)
strbuf_add(out, filename, tree_name_len);
@@ -332,8 +334,8 @@ static void grep_source_name(struct grep_opt *opt, const char *filename,
return;
}
- if (opt->relative && opt->prefix_length)
- quote_path(filename + tree_name_len, opt->prefix, out, 0);
+ if (opt->relative && grep_prefix)
+ quote_path(filename + tree_name_len, grep_prefix, out, 0);
else
quote_c_style(filename + tree_name_len, out, NULL, 0);
@@ -843,7 +845,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
int i;
int dummy;
int use_index = 1;
- int pattern_type_arg = GREP_PATTERN_TYPE_UNSPECIFIED;
int allow_revs;
struct option options[] = {
@@ -877,16 +878,16 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
N_("descend at most <depth> levels"), PARSE_OPT_NONEG,
NULL, 1 },
OPT_GROUP(""),
- OPT_SET_INT('E', "extended-regexp", &pattern_type_arg,
+ OPT_SET_INT('E', "extended-regexp", &opt.pattern_type_option,
N_("use extended POSIX regular expressions"),
GREP_PATTERN_TYPE_ERE),
- OPT_SET_INT('G', "basic-regexp", &pattern_type_arg,
+ OPT_SET_INT('G', "basic-regexp", &opt.pattern_type_option,
N_("use basic POSIX regular expressions (default)"),
GREP_PATTERN_TYPE_BRE),
- OPT_SET_INT('F', "fixed-strings", &pattern_type_arg,
+ OPT_SET_INT('F', "fixed-strings", &opt.pattern_type_option,
N_("interpret patterns as fixed strings"),
GREP_PATTERN_TYPE_FIXED),
- OPT_SET_INT('P', "perl-regexp", &pattern_type_arg,
+ OPT_SET_INT('P', "perl-regexp", &opt.pattern_type_option,
N_("use Perl-compatible regular expressions"),
GREP_PATTERN_TYPE_PCRE),
OPT_GROUP(""),
@@ -962,9 +963,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
PARSE_OPT_NOCOMPLETE),
OPT_END()
};
+ grep_prefix = prefix;
- git_config(grep_cmd_config, NULL);
- grep_init(&opt, the_repository, prefix);
+ grep_init(&opt, the_repository);
+ git_config(grep_cmd_config, &opt);
/*
* If there is no -- then the paths must exist in the working
@@ -979,7 +981,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, grep_usage,
PARSE_OPT_KEEP_DASHDASH |
PARSE_OPT_STOP_AT_NON_OPTION);
- grep_commit_pattern_type(pattern_type_arg, &opt);
if (use_index && !startup_info->have_repository) {
int fallback = 0;
@@ -1167,11 +1168,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
if (!show_in_pager && !opt.status_only)
setup_pager();
- if (!use_index && (untracked || cached))
- die(_("--cached or --untracked cannot be used with --no-index"));
-
- if (untracked && cached)
- die(_("--untracked cannot be used with --cached"));
+ die_for_incompatible_opt3(!use_index, "--no-index",
+ untracked, "--untracked",
+ cached, "--cached");
if (!use_index || untracked) {
int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index db9b253527..0837849288 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -81,7 +81,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
{
static const char * const hash_object_usage[] = {
N_("git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] [--] <file>..."),
- N_("git hash-object --stdin-paths"),
+ "git hash-object --stdin-paths",
NULL
};
const char *type = blob_type;
diff --git a/builtin/help.c b/builtin/help.c
index d387131dd8..b4f2ad3f94 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -77,8 +77,8 @@ static struct option builtin_help_options[] = {
static const char * const builtin_help_usage[] = {
N_("git help [-a|--all] [--[no-]verbose]]\n"
" [[-i|--info] [-m|--man] [-w|--web]] [<command>]"),
- N_("git help [-g|--guides]"),
- N_("git help [-c|--config]"),
+ "git help [-g|--guides]",
+ "git help [-c|--config]",
NULL
};
diff --git a/builtin/log.c b/builtin/log.c
index 093d0d2655..c211d66d1d 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -533,8 +533,6 @@ static int git_log_config(const char *var, const char *value, void *cb)
return 0;
}
- if (grep_config(var, value, cb) < 0)
- return -1;
if (git_gpg_config(var, value, cb) < 0)
return -1;
return git_diff_ui_config(var, value, cb);
@@ -549,6 +547,8 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
git_config(git_log_config, NULL);
repo_init_revisions(the_repository, &rev, prefix);
+ git_config(grep_config, &rev.grep_filter);
+
rev.diff = 1;
rev.simplify_history = 0;
memset(&opt, 0, sizeof(opt));
@@ -663,6 +663,8 @@ int cmd_show(int argc, const char **argv, const char *prefix)
memset(&match_all, 0, sizeof(match_all));
repo_init_revisions(the_repository, &rev, prefix);
+ git_config(grep_config, &rev.grep_filter);
+
rev.diff = 1;
rev.always_show_header = 1;
rev.no_walk = 1;
@@ -746,6 +748,8 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
repo_init_revisions(the_repository, &rev, prefix);
init_reflog_walk(&rev.reflog_info);
+ git_config(grep_config, &rev.grep_filter);
+
rev.verbose_header = 1;
memset(&opt, 0, sizeof(opt));
opt.def = "HEAD";
@@ -779,6 +783,8 @@ int cmd_log(int argc, const char **argv, const char *prefix)
git_config(git_log_config, NULL);
repo_init_revisions(the_repository, &rev, prefix);
+ git_config(grep_config, &rev.grep_filter);
+
rev.always_show_header = 1;
memset(&opt, 0, sizeof(opt));
opt.def = "HEAD";
@@ -1861,10 +1867,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
extra_hdr.strdup_strings = 1;
extra_to.strdup_strings = 1;
extra_cc.strdup_strings = 1;
+
init_log_defaults();
init_display_notes(&notes_opt);
git_config(git_format_config, NULL);
repo_init_revisions(the_repository, &rev, prefix);
+ git_config(grep_config, &rev.grep_filter);
+
rev.show_notes = show_notes;
memcpy(&rev.notes_opt, &notes_opt, sizeof(notes_opt));
rev.commit_format = CMIT_FMT_EMAIL;
@@ -1993,8 +2002,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (rev.show_notes)
load_display_notes(&rev.notes_opt);
- if (use_stdout + rev.diffopt.close_file + !!output_directory > 1)
- die(_("options '%s', '%s', and '%s' cannot be used together"), "--stdout", "--output", "--output-directory");
+ die_for_incompatible_opt3(use_stdout, "--stdout",
+ rev.diffopt.close_file, "--output",
+ !!output_directory, "--output-directory");
if (use_stdout) {
setup_pager();
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index 3a442631c7..6cb554cbb0 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -150,7 +150,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
git_config(git_default_config, NULL);
ls_tree_prefix = prefix;
- if (prefix && *prefix)
+ if (prefix)
chomp_prefix = strlen(prefix);
argc = parse_options(argc, argv, prefix, ls_tree_options,
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index 6719ac198d..26b84980db 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -159,12 +159,14 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)
if (argc < 2)
usage_with_options(merge_base_usage, options);
if (show_all)
- die("--is-ancestor cannot be used with --all");
+ die(_("options '%s' and '%s' cannot be used together"),
+ "--is-ancestor", "--all");
return handle_is_ancestor(argc, argv);
}
if (cmdmode == 'r' && show_all)
- die("--independent cannot be used with --all");
+ die(_("options '%s' and '%s' cannot be used together"),
+ "--independent", "--all");
if (cmdmode == 'o')
return handle_octopus(argc, argv, show_all);
diff --git a/builtin/mktag.c b/builtin/mktag.c
index 3b2dbbb37e..c7b905c614 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -7,7 +7,7 @@
#include "config.h"
static char const * const builtin_mktag_usage[] = {
- N_("git mktag"),
+ "git mktag",
NULL
};
static int option_strict = 1;
diff --git a/builtin/mktree.c b/builtin/mktree.c
index ae78ca1c02..8bdaada922 100644
--- a/builtin/mktree.c
+++ b/builtin/mktree.c
@@ -63,7 +63,7 @@ static void write_tree(struct object_id *oid)
}
static const char *mktree_usage[] = {
- N_("git mktree [-z] [--missing] [--batch]"),
+ "git mktree [-z] [--missing] [--batch]",
NULL
};
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 138e3c30a2..929591269d 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -473,7 +473,7 @@ static void show_name(const struct object *obj,
static char const * const name_rev_usage[] = {
N_("git name-rev [<options>] <commit>..."),
N_("git name-rev [<options>] --all"),
- N_("git name-rev [<options>] --stdin"),
+ N_("git name-rev [<options>] --annotate-stdin"),
NULL
};
diff --git a/builtin/notes.c b/builtin/notes.c
index 05d60483e8..f99593a185 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -32,8 +32,8 @@ static const char * const git_notes_usage[] = {
N_("git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"),
N_("git notes [--ref <notes-ref>] show [<object>]"),
N_("git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>"),
- N_("git notes merge --commit [-v | -q]"),
- N_("git notes merge --abort [-v | -q]"),
+ "git notes merge --commit [-v | -q]",
+ "git notes merge --abort [-v | -q]",
N_("git notes [--ref <notes-ref>] remove [<object>...]"),
N_("git notes [--ref <notes-ref>] prune [-n] [-v]"),
N_("git notes [--ref <notes-ref>] get-ref"),
@@ -89,7 +89,7 @@ static const char * const git_notes_prune_usage[] = {
};
static const char * const git_notes_get_ref_usage[] = {
- N_("git notes get-ref"),
+ "git notes get-ref",
NULL
};
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 87cb7b45c3..178e611f09 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -3504,7 +3504,7 @@ static int option_parse_missing_action(const struct option *opt,
return 0;
}
- die(_("invalid value for --missing"));
+ die(_("invalid value for '%s': '%s'"), "--missing", arg);
return 0;
}
diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c
index b7b9281a8c..da3273a268 100644
--- a/builtin/prune-packed.c
+++ b/builtin/prune-packed.c
@@ -3,7 +3,7 @@
#include "prune-packed.h"
static const char * const prune_packed_usage[] = {
- N_("git prune-packed [-n | --dry-run] [-q | --quiet]"),
+ "git prune-packed [-n | --dry-run] [-q | --quiet]",
NULL
};
diff --git a/builtin/pull.c b/builtin/pull.c
index 3768552e68..4d667abc19 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -42,9 +42,9 @@ static enum rebase_type parse_config_rebase(const char *key, const char *value,
return v;
if (fatal)
- die(_("Invalid value for %s: %s"), key, value);
+ die(_("invalid value for '%s': '%s'"), key, value);
else
- error(_("Invalid value for %s: %s"), key, value);
+ error(_("invalid value for '%s': '%s'"), key, value);
return REBASE_INVALID;
}
@@ -318,7 +318,7 @@ static const char *config_get_ff(void)
if (!strcmp(value, "only"))
return "--ff-only";
- die(_("Invalid value for pull.ff: %s"), value);
+ die(_("invalid value for '%s': '%s'"), "pull.ff", value);
}
/**
diff --git a/builtin/push.c b/builtin/push.c
index 359db90321..cad997965a 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -486,7 +486,7 @@ static int git_push_config(const char *k, const char *v, void *cb)
if (value && !strcasecmp(value, "if-asked"))
set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_IF_ASKED);
else
- return error("Invalid value for '%s'", k);
+ return error(_("invalid value for '%s'"), k);
}
}
} else if (!strcmp(k, "push.recursesubmodules")) {
diff --git a/builtin/rebase.c b/builtin/rebase.c
index d858add3fe..b29ad2b65e 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -37,7 +37,7 @@ static char const * const builtin_rebase_usage[] = {
"[--onto <newbase> | --keep-base] [<upstream> [<branch>]]"),
N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
"--root [<branch>]"),
- N_("git rebase --continue | --abort | --skip | --edit-todo"),
+ "git rebase --continue | --abort | --skip | --edit-todo",
NULL
};
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 85b838720c..c8f2b31873 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -818,7 +818,7 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix)
*/
static const char reflog_usage[] =
-N_("git reflog [ show | expire | delete | exists ]");
+"git reflog [ show | expire | delete | exists ]";
int cmd_reflog(int argc, const char **argv, const char *prefix)
{
diff --git a/builtin/remote.c b/builtin/remote.c
index 299c466116..6f27ddc47b 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -14,7 +14,7 @@
#include "commit-reach.h"
static const char * const builtin_remote_usage[] = {
- N_("git remote [-v | --verbose]"),
+ "git remote [-v | --verbose]",
N_("git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--mirror=<fetch|push>] <name> <url>"),
N_("git remote rename <old> <new>"),
N_("git remote remove <name>"),
diff --git a/builtin/replace.c b/builtin/replace.c
index 6ff1734d58..ac92337c0e 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -22,7 +22,7 @@ static const char * const git_replace_usage[] = {
N_("git replace [-f] <object> <replacement>"),
N_("git replace [-f] --edit <object>"),
N_("git replace [-f] --graft <commit> [<parent>...]"),
- N_("git replace [-f] --convert-graft-file"),
+ "git replace [-f] --convert-graft-file",
N_("git replace -d <object>..."),
N_("git replace [--format=<format>] [-l [<pattern>]]"),
NULL
diff --git a/builtin/reset.c b/builtin/reset.c
index 75b8d86481..6e65e90c5d 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -280,7 +280,6 @@ static int read_from_tree(const struct pathspec *pathspec,
return 1;
diffcore_std(&opt);
diff_flush(&opt);
- clear_pathspec(&opt.pathspec);
return 0;
}
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 777558e9b0..38528c7f15 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -20,7 +20,7 @@
#include "packfile.h"
static const char rev_list_usage[] =
-"git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
+"git rev-list [<options>] <commit-id>... [-- <path>...]\n"
" limiting output:\n"
" --max-count=<n>\n"
" --max-age=<epoch>\n"
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 69c432ef1a..64962be016 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -145,7 +145,7 @@ static int send_pack_config(const char *k, const char *v, void *cb)
if (value && !strcasecmp(value, "if-asked"))
args.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;
else
- return error("Invalid value for '%s'", k);
+ return error(_("invalid value for '%s'"), k);
}
}
}
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index e7f7af5de3..228d782754 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -388,6 +388,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
parse_revision_opt(&rev, &ctx, options, shortlog_usage);
}
parse_done:
+ revision_opts_finish(&rev);
argc = parse_options_end(&ctx);
if (nongit && argc > 1) {
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index e12c5e80e3..330b0553b9 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -8,6 +8,7 @@
#include "parse-options.h"
#include "dir.h"
#include "commit-slab.h"
+#include "date.h"
static const char* show_branch_usage[] = {
N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index a311483a7d..5518ed47f6 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -1,4 +1,5 @@
#include "builtin.h"
+#include "cache.h"
#include "config.h"
#include "dir.h"
#include "parse-options.h"
@@ -15,6 +16,7 @@
#include "wt-status.h"
#include "quote.h"
#include "sparse-index.h"
+#include "worktree.h"
static const char *empty_base = "";
@@ -43,7 +45,7 @@ static void write_patterns_to_file(FILE *fp, struct pattern_list *pl)
}
static char const * const builtin_sparse_checkout_list_usage[] = {
- N_("git sparse-checkout list"),
+ "git sparse-checkout list",
NULL
};
@@ -361,26 +363,23 @@ enum sparse_checkout_mode {
static int set_config(enum sparse_checkout_mode mode)
{
- const char *config_path;
-
- if (upgrade_repository_format(1) < 0)
- die(_("unable to upgrade repository format to enable worktreeConfig"));
- if (git_config_set_gently("extensions.worktreeConfig", "true")) {
- error(_("failed to set extensions.worktreeConfig setting"));
+ /* Update to use worktree config, if not already. */
+ if (init_worktree_config(the_repository)) {
+ error(_("failed to initialize worktree config"));
return 1;
}
- config_path = git_path("config.worktree");
- git_config_set_in_file_gently(config_path,
- "core.sparseCheckout",
- mode ? "true" : NULL);
-
- git_config_set_in_file_gently(config_path,
- "core.sparseCheckoutCone",
- mode == MODE_CONE_PATTERNS ? "true" : NULL);
+ if (repo_config_set_worktree_gently(the_repository,
+ "core.sparseCheckout",
+ mode ? "true" : "false") ||
+ repo_config_set_worktree_gently(the_repository,
+ "core.sparseCheckoutCone",
+ mode == MODE_CONE_PATTERNS ?
+ "true" : "false"))
+ return 1;
if (mode == MODE_NO_PATTERNS)
- set_sparse_index_config(the_repository, 0);
+ return set_sparse_index_config(the_repository, 0);
return 0;
}
@@ -403,6 +402,7 @@ static int update_modes(int *cone_mode, int *sparse_index)
core_sparse_checkout_cone = 1;
} else {
mode = MODE_ALL_PATTERNS;
+ core_sparse_checkout_cone = 0;
}
if (record_mode && set_config(mode))
return 1;
@@ -421,7 +421,7 @@ static int update_modes(int *cone_mode, int *sparse_index)
}
static char const * const builtin_sparse_checkout_init_usage[] = {
- N_("git sparse-checkout init [--cone] [--[no-]sparse-index]"),
+ "git sparse-checkout init [--cone] [--[no-]sparse-index]",
NULL
};
@@ -683,18 +683,76 @@ static int modify_pattern_list(int argc, const char **argv, int use_stdin,
return result;
}
+static void sanitize_paths(int argc, const char **argv,
+ const char *prefix, int skip_checks)
+{
+ int i;
+
+ if (!argc)
+ return;
+
+ if (prefix && *prefix && core_sparse_checkout_cone) {
+ /*
+ * The args are not pathspecs, so unfortunately we
+ * cannot imitate how cmd_add() uses parse_pathspec().
+ */
+ int prefix_len = strlen(prefix);
+
+ for (i = 0; i < argc; i++)
+ argv[i] = prefix_path(prefix, prefix_len, argv[i]);
+ }
+
+ if (skip_checks)
+ return;
+
+ if (prefix && *prefix && !core_sparse_checkout_cone)
+ die(_("please run from the toplevel directory in non-cone mode"));
+
+ if (core_sparse_checkout_cone) {
+ for (i = 0; i < argc; i++) {
+ if (argv[i][0] == '/')
+ die(_("specify directories rather than patterns (no leading slash)"));
+ if (argv[i][0] == '!')
+ die(_("specify directories rather than patterns. If your directory starts with a '!', pass --skip-checks"));
+ if (strpbrk(argv[i], "*?[]"))
+ die(_("specify directories rather than patterns. If your directory really has any of '*?[]\\' in it, pass --skip-checks"));
+ }
+ }
+
+ for (i = 0; i < argc; i++) {
+ struct cache_entry *ce;
+ struct index_state *index = the_repository->index;
+ int pos = index_name_pos(index, argv[i], strlen(argv[i]));
+
+ if (pos < 0)
+ continue;
+ ce = index->cache[pos];
+ if (S_ISSPARSEDIR(ce->ce_mode))
+ continue;
+
+ if (core_sparse_checkout_cone)
+ die(_("'%s' is not a directory; to treat it as a directory anyway, rerun with --skip-checks"), argv[i]);
+ else
+ warning(_("pass a leading slash before paths such as '%s' if you want a single file (see NON-CONE PROBLEMS in the git-sparse-checkout manual)."), argv[i]);
+ }
+}
+
static char const * const builtin_sparse_checkout_add_usage[] = {
- N_("git sparse-checkout add (--stdin | <patterns>)"),
+ N_("git sparse-checkout add [--skip-checks] (--stdin | <patterns>)"),
NULL
};
static struct sparse_checkout_add_opts {
+ int skip_checks;
int use_stdin;
} add_opts;
static int sparse_checkout_add(int argc, const char **argv, const char *prefix)
{
static struct option builtin_sparse_checkout_add_options[] = {
+ OPT_BOOL_F(0, "skip-checks", &add_opts.skip_checks,
+ N_("skip some sanity checks on the given paths that might give false positives"),
+ PARSE_OPT_NONEG),
OPT_BOOL(0, "stdin", &add_opts.use_stdin,
N_("read patterns from standard in")),
OPT_END(),
@@ -710,17 +768,20 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix)
builtin_sparse_checkout_add_usage,
PARSE_OPT_KEEP_UNKNOWN);
+ sanitize_paths(argc, argv, prefix, add_opts.skip_checks);
+
return modify_pattern_list(argc, argv, add_opts.use_stdin, ADD);
}
static char const * const builtin_sparse_checkout_set_usage[] = {
- N_("git sparse-checkout set [--[no-]cone] [--[no-]sparse-index] (--stdin | <patterns>)"),
+ N_("git sparse-checkout set [--[no-]cone] [--[no-]sparse-index] [--skip-checks] (--stdin | <patterns>)"),
NULL
};
static struct sparse_checkout_set_opts {
int cone_mode;
int sparse_index;
+ int skip_checks;
int use_stdin;
} set_opts;
@@ -734,6 +795,9 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
N_("initialize the sparse-checkout in cone mode")),
OPT_BOOL(0, "sparse-index", &set_opts.sparse_index,
N_("toggle the use of a sparse index")),
+ OPT_BOOL_F(0, "skip-checks", &set_opts.skip_checks,
+ N_("skip some sanity checks on the given paths that might give false positives"),
+ PARSE_OPT_NONEG),
OPT_BOOL_F(0, "stdin", &set_opts.use_stdin,
N_("read patterns from standard in"),
PARSE_OPT_NONEG),
@@ -761,13 +825,15 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
if (!core_sparse_checkout_cone && argc == 0) {
argv = default_patterns;
argc = default_patterns_nr;
+ } else {
+ sanitize_paths(argc, argv, prefix, set_opts.skip_checks);
}
return modify_pattern_list(argc, argv, set_opts.use_stdin, REPLACE);
}
static char const * const builtin_sparse_checkout_reapply_usage[] = {
- N_("git sparse-checkout reapply [--[no-]cone] [--[no-]sparse-index]"),
+ "git sparse-checkout reapply [--[no-]cone] [--[no-]sparse-index]",
NULL
};
@@ -789,15 +855,15 @@ static int sparse_checkout_reapply(int argc, const char **argv)
if (!core_apply_sparse_checkout)
die(_("must be in a sparse-checkout to reapply sparsity patterns"));
+ reapply_opts.cone_mode = -1;
+ reapply_opts.sparse_index = -1;
+
argc = parse_options(argc, argv, NULL,
builtin_sparse_checkout_reapply_options,
builtin_sparse_checkout_reapply_usage, 0);
repo_read_index(the_repository);
- reapply_opts.cone_mode = -1;
- reapply_opts.sparse_index = -1;
-
if (update_modes(&reapply_opts.cone_mode, &reapply_opts.sparse_index))
return 1;
@@ -805,7 +871,7 @@ static int sparse_checkout_reapply(int argc, const char **argv)
}
static char const * const builtin_sparse_checkout_disable_usage[] = {
- N_("git sparse-checkout disable"),
+ "git sparse-checkout disable",
NULL
};
diff --git a/builtin/stripspace.c b/builtin/stripspace.c
index be33eb83c1..1e34cf2beb 100644
--- a/builtin/stripspace.c
+++ b/builtin/stripspace.c
@@ -15,8 +15,8 @@ static void comment_lines(struct strbuf *buf)
}
static const char * const stripspace_usage[] = {
- N_("git stripspace [-s | --strip-comments]"),
- N_("git stripspace [-c | --comment-lines]"),
+ "git stripspace [-s | --strip-comments]",
+ "git stripspace [-c | --comment-lines]",
NULL
};
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 33c82c3ab9..eeacefcc38 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -21,6 +21,7 @@
#include "object-store.h"
#include "advice.h"
#include "branch.h"
+#include "list-objects-filter-options.h"
#define OPT_QUIET (1 << 0)
#define OPT_CACHED (1 << 1)
@@ -1631,6 +1632,7 @@ struct module_clone_data {
const char *name;
const char *url;
const char *depth;
+ struct list_objects_filter_options *filter_options;
struct string_list reference;
unsigned int quiet: 1;
unsigned int progress: 1;
@@ -1797,6 +1799,10 @@ static int clone_submodule(struct module_clone_data *clone_data)
strvec_push(&cp.args, "--dissociate");
if (sm_gitdir && *sm_gitdir)
strvec_pushl(&cp.args, "--separate-git-dir", sm_gitdir, NULL);
+ if (clone_data->filter_options && clone_data->filter_options->choice)
+ strvec_pushf(&cp.args, "--filter=%s",
+ expand_list_objects_filter_spec(
+ clone_data->filter_options));
if (clone_data->single_branch >= 0)
strvec_push(&cp.args, clone_data->single_branch ?
"--single-branch" :
@@ -1853,6 +1859,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
{
int dissociate = 0, quiet = 0, progress = 0, require_init = 0;
struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT;
+ struct list_objects_filter_options filter_options;
struct option module_clone_options[] = {
OPT_STRING(0, "prefix", &clone_data.prefix,
@@ -1882,17 +1889,19 @@ static int module_clone(int argc, const char **argv, const char *prefix)
N_("disallow cloning into non-empty directory")),
OPT_BOOL(0, "single-branch", &clone_data.single_branch,
N_("clone only one branch, HEAD or --branch")),
+ OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
OPT_END()
};
const char *const git_submodule_helper_usage[] = {
N_("git submodule--helper clone [--prefix=<path>] [--quiet] "
"[--reference <repository>] [--name <name>] [--depth <depth>] "
- "[--single-branch] "
+ "[--single-branch] [--filter <filter-spec>]"
"--url <url> --path <path>"),
NULL
};
+ memset(&filter_options, 0, sizeof(filter_options));
argc = parse_options(argc, argv, prefix, module_clone_options,
git_submodule_helper_usage, 0);
@@ -1900,12 +1909,14 @@ static int module_clone(int argc, const char **argv, const char *prefix)
clone_data.quiet = !!quiet;
clone_data.progress = !!progress;
clone_data.require_init = !!require_init;
+ clone_data.filter_options = &filter_options;
if (argc || !clone_data.url || !clone_data.path || !*(clone_data.path))
usage_with_options(git_submodule_helper_usage,
module_clone_options);
clone_submodule(&clone_data);
+ list_objects_filter_release(&filter_options);
return 0;
}
@@ -1995,6 +2006,7 @@ struct submodule_update_clone {
const char *recursive_prefix;
const char *prefix;
int single_branch;
+ struct list_objects_filter_options *filter_options;
/* to be consumed by git-submodule.sh */
struct update_clone_data *update_clone;
@@ -2155,6 +2167,9 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
strvec_pushl(&child->args, "--prefix", suc->prefix, NULL);
if (suc->recommend_shallow && sub->recommend_shallow == 1)
strvec_push(&child->args, "--depth=1");
+ if (suc->filter_options && suc->filter_options->choice)
+ strvec_pushf(&child->args, "--filter=%s",
+ expand_list_objects_filter_spec(suc->filter_options));
if (suc->require_init)
strvec_push(&child->args, "--require-init");
strvec_pushl(&child->args, "--path", sub->path, NULL);
@@ -2499,6 +2514,8 @@ static int update_clone(int argc, const char **argv, const char *prefix)
const char *update = NULL;
struct pathspec pathspec;
struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT;
+ struct list_objects_filter_options filter_options;
+ int ret;
struct option module_update_clone_options[] = {
OPT_STRING(0, "prefix", &prefix,
@@ -2529,6 +2546,7 @@ static int update_clone(int argc, const char **argv, const char *prefix)
N_("disallow cloning into non-empty directory")),
OPT_BOOL(0, "single-branch", &suc.single_branch,
N_("clone only one branch, HEAD or --branch")),
+ OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
OPT_END()
};
@@ -2541,20 +2559,26 @@ static int update_clone(int argc, const char **argv, const char *prefix)
update_clone_config_from_gitmodules(&suc.max_jobs);
git_config(git_update_clone_config, &suc.max_jobs);
+ memset(&filter_options, 0, sizeof(filter_options));
argc = parse_options(argc, argv, prefix, module_update_clone_options,
git_submodule_helper_usage, 0);
+ suc.filter_options = &filter_options;
if (update)
if (parse_submodule_update_strategy(update, &suc.update) < 0)
die(_("bad value for update parameter"));
- if (module_list_compute(argc, argv, prefix, &pathspec, &suc.list) < 0)
+ if (module_list_compute(argc, argv, prefix, &pathspec, &suc.list) < 0) {
+ list_objects_filter_release(&filter_options);
return 1;
+ }
if (pathspec.nr)
suc.warn_if_uninitialized = 1;
- return update_submodules(&suc);
+ ret = update_submodules(&suc);
+ list_objects_filter_release(&filter_options);
+ return ret;
}
static int run_update_procedure(int argc, const char **argv, const char *prefix)
@@ -2884,7 +2908,7 @@ static int module_config(int argc, const char **argv, const char *prefix)
const char *const git_submodule_helper_usage[] = {
N_("git submodule--helper config <name> [<value>]"),
N_("git submodule--helper config --unset <name>"),
- N_("git submodule--helper config --check-writeable"),
+ "git submodule--helper config --check-writeable",
NULL
};
diff --git a/builtin/tag.c b/builtin/tag.c
index 134b3f1edf..2479da0704 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -20,6 +20,7 @@
#include "oid-array.h"
#include "column.h"
#include "ref-filter.h"
+#include "date.h"
static const char * const git_tag_usage[] = {
N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
diff --git a/builtin/update-server-info.c b/builtin/update-server-info.c
index 4321a34456..880fffec58 100644
--- a/builtin/update-server-info.c
+++ b/builtin/update-server-info.c
@@ -4,7 +4,7 @@
#include "parse-options.h"
static const char * const update_server_info_usage[] = {
- N_("git update-server-info [--force]"),
+ "git update-server-info [--force]",
NULL
};
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 0d0809276f..4eaba2a8fd 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -236,6 +236,74 @@ static void check_candidate_path(const char *path,
die(_("'%s' is a missing but already registered worktree;\nuse '%s -f' to override, or 'prune' or 'remove' to clear"), path, cmd);
}
+static void copy_sparse_checkout(const char *worktree_git_dir)
+{
+ char *from_file = git_pathdup("info/sparse-checkout");
+ char *to_file = xstrfmt("%s/info/sparse-checkout", worktree_git_dir);
+
+ if (file_exists(from_file)) {
+ if (safe_create_leading_directories(to_file) ||
+ copy_file(to_file, from_file, 0666))
+ error(_("failed to copy '%s' to '%s'; sparse-checkout may not work correctly"),
+ from_file, to_file);
+ }
+
+ free(from_file);
+ free(to_file);
+}
+
+static void copy_filtered_worktree_config(const char *worktree_git_dir)
+{
+ char *from_file = git_pathdup("config.worktree");
+ char *to_file = xstrfmt("%s/config.worktree", worktree_git_dir);
+
+ if (file_exists(from_file)) {
+ struct config_set cs = { { 0 } };
+ const char *core_worktree;
+ int bare;
+
+ if (safe_create_leading_directories(to_file) ||
+ copy_file(to_file, from_file, 0666)) {
+ error(_("failed to copy worktree config from '%s' to '%s'"),
+ from_file, to_file);
+ goto worktree_copy_cleanup;
+ }
+
+ git_configset_init(&cs);
+ git_configset_add_file(&cs, from_file);
+
+ if (!git_configset_get_bool(&cs, "core.bare", &bare) &&
+ bare &&
+ git_config_set_multivar_in_file_gently(
+ to_file, "core.bare", NULL, "true", 0))
+ error(_("failed to unset '%s' in '%s'"),
+ "core.bare", to_file);
+ if (!git_configset_get_value(&cs, "core.worktree", &core_worktree) &&
+ git_config_set_in_file_gently(to_file,
+ "core.worktree", NULL))
+ error(_("failed to unset '%s' in '%s'"),
+ "core.worktree", to_file);
+
+ git_configset_clear(&cs);
+ }
+
+worktree_copy_cleanup:
+ free(from_file);
+ free(to_file);
+}
+
+static int checkout_worktree(const struct add_opts *opts,
+ struct strvec *child_env)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ cp.git_cmd = 1;
+ strvec_pushl(&cp.args, "reset", "--hard", "--no-recurse-submodules", NULL);
+ if (opts->quiet)
+ strvec_push(&cp.args, "--quiet");
+ strvec_pushv(&cp.env_array, child_env->v);
+ return run_command(&cp);
+}
+
static int add_worktree(const char *path, const char *refname,
const struct add_opts *opts)
{
@@ -335,6 +403,21 @@ static int add_worktree(const char *path, const char *refname,
strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
write_file(sb.buf, "../..");
+ /*
+ * If the current worktree has sparse-checkout enabled, then copy
+ * the sparse-checkout patterns from the current worktree.
+ */
+ if (core_apply_sparse_checkout)
+ copy_sparse_checkout(sb_repo.buf);
+
+ /*
+ * If we are using worktree config, then copy all current config
+ * values from the current worktree into the new one, that way the
+ * new worktree behaves the same as this one.
+ */
+ if (repository_format_worktree_config)
+ copy_filtered_worktree_config(sb_repo.buf);
+
strvec_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
strvec_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
cp.git_cmd = 1;
@@ -354,17 +437,9 @@ static int add_worktree(const char *path, const char *refname,
if (ret)
goto done;
- if (opts->checkout) {
- struct child_process cp = CHILD_PROCESS_INIT;
- cp.git_cmd = 1;
- strvec_pushl(&cp.args, "reset", "--hard", "--no-recurse-submodules", NULL);
- if (opts->quiet)
- strvec_push(&cp.args, "--quiet");
- strvec_pushv(&cp.env_array, child_env.v);
- ret = run_command(&cp);
- if (ret)
- goto done;
- }
+ if (opts->checkout &&
+ (ret = checkout_worktree(opts, &child_env)))
+ goto done;
is_junk = 0;
FREE_AND_NULL(junk_work_tree);
diff --git a/cache.h b/cache.h
index 825ec17198..04d4d2db25 100644
--- a/cache.h
+++ b/cache.h
@@ -1558,48 +1558,6 @@ struct object *repo_peel_to_type(struct repository *r,
#define peel_to_type(name, namelen, obj, type) \
repo_peel_to_type(the_repository, name, namelen, obj, type)
-enum date_mode_type {
- DATE_NORMAL = 0,
- DATE_HUMAN,
- DATE_RELATIVE,
- DATE_SHORT,
- DATE_ISO8601,
- DATE_ISO8601_STRICT,
- DATE_RFC2822,
- DATE_STRFTIME,
- DATE_RAW,
- DATE_UNIX
-};
-
-struct date_mode {
- enum date_mode_type type;
- const char *strftime_fmt;
- int local;
-};
-
-/*
- * Convenience helper for passing a constant type, like:
- *
- * show_date(t, tz, DATE_MODE(NORMAL));
- */
-#define DATE_MODE(t) date_mode_from_type(DATE_##t)
-struct date_mode *date_mode_from_type(enum date_mode_type type);
-
-const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode);
-void show_date_relative(timestamp_t time, struct strbuf *timebuf);
-void show_date_human(timestamp_t time, int tz, const struct timeval *now,
- struct strbuf *timebuf);
-int parse_date(const char *date, struct strbuf *out);
-int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset);
-int parse_expiry_date(const char *date, timestamp_t *timestamp);
-void datestamp(struct strbuf *out);
-#define approxidate(s) approxidate_careful((s), NULL)
-timestamp_t approxidate_careful(const char *, int *);
-timestamp_t approxidate_relative(const char *date);
-void parse_date_format(const char *format, struct date_mode *mode);
-int date_overflows(timestamp_t date);
-time_t tm_to_time_t(const struct tm *tm);
-
#define IDENT_STRICT 1
#define IDENT_NO_DATE 2
#define IDENT_NO_NAME 4
@@ -1646,14 +1604,6 @@ struct ident_split {
int split_ident_line(struct ident_split *, const char *, int);
/*
- * Like show_date, but pull the timestamp and tz parameters from
- * the ident_split. It will also sanity-check the values and produce
- * a well-known sentinel date if they appear bogus.
- */
-const char *show_ident_date(const struct ident_split *id,
- const struct date_mode *mode);
-
-/*
* Compare split idents for equality or strict ordering. Note that we
* compare only the ident part of the line, ignoring any timestamp.
*
diff --git a/config.c b/config.c
index e0c03d154c..383b1a4885 100644
--- a/config.c
+++ b/config.c
@@ -6,6 +6,7 @@
*
*/
#include "cache.h"
+#include "date.h"
#include "branch.h"
#include "config.h"
#include "environment.h"
@@ -21,6 +22,7 @@
#include "dir.h"
#include "color.h"
#include "refs.h"
+#include "worktree.h"
struct config_source {
struct config_source *prev;
@@ -2294,8 +2296,8 @@ int git_configset_get_string(struct config_set *cs, const char *key, char **dest
return 1;
}
-int git_configset_get_string_tmp(struct config_set *cs, const char *key,
- const char **dest)
+static int git_configset_get_string_tmp(struct config_set *cs, const char *key,
+ const char **dest)
{
const char *value;
if (!git_configset_get_value(cs, key, &value)) {
@@ -3000,6 +3002,20 @@ int git_config_set_gently(const char *key, const char *value)
return git_config_set_multivar_gently(key, value, NULL, 0);
}
+int repo_config_set_worktree_gently(struct repository *r,
+ const char *key, const char *value)
+{
+ /* Only use worktree-specific config if it is is already enabled. */
+ if (repository_format_worktree_config) {
+ char *file = repo_git_path(r, "config.worktree");
+ int ret = git_config_set_multivar_in_file_gently(
+ file, key, value, NULL, 0);
+ free(file);
+ return ret;
+ }
+ return repo_config_set_multivar_gently(r, key, value, NULL, 0);
+}
+
void git_config_set(const char *key, const char *value)
{
git_config_set_multivar(key, value, NULL, 0);
@@ -3297,14 +3313,28 @@ void git_config_set_multivar_in_file(const char *config_filename,
int git_config_set_multivar_gently(const char *key, const char *value,
const char *value_pattern, unsigned flags)
{
- return git_config_set_multivar_in_file_gently(NULL, key, value, value_pattern,
- flags);
+ return repo_config_set_multivar_gently(the_repository, key, value,
+ value_pattern, flags);
+}
+
+int repo_config_set_multivar_gently(struct repository *r, const char *key,
+ const char *value,
+ const char *value_pattern, unsigned flags)
+{
+ char *file = repo_git_path(r, "config");
+ int res = git_config_set_multivar_in_file_gently(file,
+ key, value,
+ value_pattern,
+ flags);
+ free(file);
+ return res;
}
void git_config_set_multivar(const char *key, const char *value,
const char *value_pattern, unsigned flags)
{
- git_config_set_multivar_in_file(NULL, key, value, value_pattern,
+ git_config_set_multivar_in_file(git_path("config"),
+ key, value, value_pattern,
flags);
}
diff --git a/config.h b/config.h
index ab0106d287..bb49baf1ee 100644
--- a/config.h
+++ b/config.h
@@ -267,6 +267,13 @@ void git_config_set_in_file(const char *, const char *, const char *);
int git_config_set_gently(const char *, const char *);
/**
+ * Write a config value that should apply to the current worktree. If
+ * extensions.worktreeConfig is enabled, then the write will happen in the
+ * current worktree's config. Otherwise, write to the common config file.
+ */
+int repo_config_set_worktree_gently(struct repository *, const char *, const char *);
+
+/**
* write config values to `.git/config`, takes a key/value pair as parameter.
*/
void git_config_set(const char *, const char *);
@@ -294,6 +301,7 @@ int git_config_parse_key(const char *, char **, size_t *);
int git_config_set_multivar_gently(const char *, const char *, const char *, unsigned);
void git_config_set_multivar(const char *, const char *, const char *, unsigned);
+int repo_config_set_multivar_gently(struct repository *, const char *, const char *, const char *, unsigned);
int git_config_set_multivar_in_file_gently(const char *, const char *, const char *, const char *, unsigned);
/**
@@ -466,7 +474,6 @@ void git_configset_clear(struct config_set *cs);
int git_configset_get_value(struct config_set *cs, const char *key, const char **dest);
int git_configset_get_string(struct config_set *cs, const char *key, char **dest);
-int git_configset_get_string_tmp(struct config_set *cs, const char *key, const char **dest);
int git_configset_get_int(struct config_set *cs, const char *key, int *dest);
int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest);
int git_configset_get_bool(struct config_set *cs, const char *key, int *dest);
diff --git a/contrib/rerere-train.sh b/contrib/rerere-train.sh
index 75125d6ae0..26b724c8c6 100755
--- a/contrib/rerere-train.sh
+++ b/contrib/rerere-train.sh
@@ -86,7 +86,7 @@ do
fi
if test -s "$GIT_DIR/MERGE_RR"
then
- git show -s --pretty=format:"Learning from %h %s" "$commit"
+ git --no-pager show -s --format="Learning from %h %s" "$commit"
git rerere
git checkout -q $commit -- .
git rerere
diff --git a/date.c b/date.c
index 84bb4451c1..68a260c214 100644
--- a/date.c
+++ b/date.c
@@ -5,6 +5,7 @@
*/
#include "cache.h"
+#include "date.h"
/*
* This is like mktime, but without normalization of tm_wday and tm_yday.
@@ -205,11 +206,10 @@ void show_date_relative(timestamp_t time, struct strbuf *timebuf)
struct date_mode *date_mode_from_type(enum date_mode_type type)
{
- static struct date_mode mode;
+ static struct date_mode mode = DATE_MODE_INIT;
if (type == DATE_STRFTIME)
BUG("cannot create anonymous strftime date_mode struct");
mode.type = type;
- mode.local = 0;
return &mode;
}
@@ -993,6 +993,11 @@ void parse_date_format(const char *format, struct date_mode *mode)
die("unknown date format %s", format);
}
+void date_mode_release(struct date_mode *mode)
+{
+ free((char *)mode->strftime_fmt);
+}
+
void datestamp(struct strbuf *out)
{
time_t now;
diff --git a/date.h b/date.h
new file mode 100644
index 0000000000..5d4eaba0a9
--- /dev/null
+++ b/date.h
@@ -0,0 +1,74 @@
+#ifndef DATE_H
+#define DATE_H
+
+/**
+ * The date mode type. This has DATE_NORMAL at an explicit "= 0" to
+ * accommodate a memset([...], 0, [...]) initialization when "struct
+ * date_mode" is used as an embedded struct member, as in the case of
+ * e.g. "struct pretty_print_context" and "struct rev_info".
+ */
+enum date_mode_type {
+ DATE_NORMAL = 0,
+ DATE_HUMAN,
+ DATE_RELATIVE,
+ DATE_SHORT,
+ DATE_ISO8601,
+ DATE_ISO8601_STRICT,
+ DATE_RFC2822,
+ DATE_STRFTIME,
+ DATE_RAW,
+ DATE_UNIX
+};
+
+struct date_mode {
+ enum date_mode_type type;
+ const char *strftime_fmt;
+ int local;
+};
+
+#define DATE_MODE_INIT { \
+ .type = DATE_NORMAL, \
+}
+
+/**
+ * Convenience helper for passing a constant type, like:
+ *
+ * show_date(t, tz, DATE_MODE(NORMAL));
+ */
+#define DATE_MODE(t) date_mode_from_type(DATE_##t)
+struct date_mode *date_mode_from_type(enum date_mode_type type);
+
+/**
+ * Format <'time', 'timezone'> into static memory according to 'mode'
+ * and return it. The mode is an initialized "struct date_mode"
+ * (usually from the DATE_MODE() macro).
+ */
+const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode);
+
+/**
+ * Parse a date format for later use with show_date().
+ *
+ * When the "date_mode_type" is DATE_STRFTIME the "strftime_fmt"
+ * member of "struct date_mode" will be a malloc()'d format string to
+ * be used with strbuf_addftime(), in which case you'll need to call
+ * date_mode_release() later.
+ */
+void parse_date_format(const char *format, struct date_mode *mode);
+
+/**
+ * Release a "struct date_mode", currently only required if
+ * parse_date_format() has parsed a "DATE_STRFTIME" format.
+ */
+void date_mode_release(struct date_mode *mode);
+
+void show_date_relative(timestamp_t time, struct strbuf *timebuf);
+int parse_date(const char *date, struct strbuf *out);
+int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset);
+int parse_expiry_date(const char *date, timestamp_t *timestamp);
+void datestamp(struct strbuf *out);
+#define approxidate(s) approxidate_careful((s), NULL)
+timestamp_t approxidate_careful(const char *, int *);
+timestamp_t approxidate_relative(const char *date);
+int date_overflows(timestamp_t date);
+time_t tm_to_time_t(const struct tm *tm);
+#endif
diff --git a/diff-merges.c b/diff-merges.c
index a833fd747a..7f64156b8b 100644
--- a/diff-merges.c
+++ b/diff-merges.c
@@ -78,7 +78,7 @@ static void set_diff_merges(struct rev_info *revs, const char *optarg)
diff_merges_setup_func_t func = func_by_opt(optarg);
if (!func)
- die(_("unknown value for --diff-merges: %s"), optarg);
+ die(_("invalid value for '%s': '%s'"), "--diff-merges", optarg);
func(revs);
diff --git a/diff.c b/diff.c
index 7d5cfd325e..c4ccb6b1a3 100644
--- a/diff.c
+++ b/diff.c
@@ -6452,6 +6452,8 @@ void diff_free(struct diff_options *options)
diff_free_file(options);
diff_free_ignore_regex(options);
+ clear_pathspec(&options->pathspec);
+ FREE_AND_NULL(options->parseopts);
}
void diff_flush(struct diff_options *options)
diff --git a/dir.c b/dir.c
index d91295f2bc..79a5f6918c 100644
--- a/dir.c
+++ b/dir.c
@@ -2936,7 +2936,9 @@ int read_directory(struct dir_struct *dir, struct index_state *istate,
if (force_untracked_cache < 0)
force_untracked_cache =
- git_env_bool("GIT_FORCE_UNTRACKED_CACHE", 0);
+ git_env_bool("GIT_FORCE_UNTRACKED_CACHE", -1);
+ if (force_untracked_cache < 0)
+ force_untracked_cache = (istate->repo->settings.core_untracked_cache == UNTRACKED_CACHE_WRITE);
if (force_untracked_cache &&
dir->untracked == istate->untracked &&
(dir->untracked->dir_opened ||
diff --git a/fetch-pack.c b/fetch-pack.c
index dd6ec449f2..87657907e7 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -696,26 +696,30 @@ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator,
trace2_region_enter("fetch-pack", "parse_remote_refs_and_find_cutoff", NULL);
for (ref = *refs; ref; ref = ref->next) {
- struct object *o;
+ struct commit *commit;
+
+ commit = lookup_commit_in_graph(the_repository, &ref->old_oid);
+ if (!commit) {
+ struct object *o;
- if (!has_object_file_with_flags(&ref->old_oid,
+ if (!has_object_file_with_flags(&ref->old_oid,
OBJECT_INFO_QUICK |
- OBJECT_INFO_SKIP_FETCH_OBJECT))
- continue;
- o = parse_object(the_repository, &ref->old_oid);
- if (!o)
- continue;
+ OBJECT_INFO_SKIP_FETCH_OBJECT))
+ continue;
+ o = parse_object(the_repository, &ref->old_oid);
+ if (!o || o->type != OBJ_COMMIT)
+ continue;
+
+ commit = (struct commit *)o;
+ }
/*
* We already have it -- which may mean that we were
* in sync with the other side at some time after
* that (it is OK if we guess wrong here).
*/
- if (o->type == OBJ_COMMIT) {
- struct commit *commit = (struct commit *)o;
- if (!cutoff || cutoff < commit->date)
- cutoff = commit->date;
- }
+ if (!cutoff || cutoff < commit->date)
+ cutoff = commit->date;
}
trace2_region_leave("fetch-pack", "parse_remote_refs_and_find_cutoff", NULL);
@@ -1415,9 +1419,17 @@ static int process_ack(struct fetch_negotiator *negotiator,
* otherwise.
*/
if (*received_ready && reader->status != PACKET_READ_DELIM)
- die(_("expected packfile to be sent after 'ready'"));
+ /*
+ * TRANSLATORS: The parameter will be 'ready', a protocol
+ * keyword.
+ */
+ die(_("expected packfile to be sent after '%s'"), "ready");
if (!*received_ready && reader->status != PACKET_READ_FLUSH)
- die(_("expected no other sections to be sent after no 'ready'"));
+ /*
+ * TRANSLATORS: The parameter will be 'ready', a protocol
+ * keyword.
+ */
+ die(_("expected no other sections to be sent after no '%s'"), "ready");
return 0;
}
diff --git a/git-submodule.sh b/git-submodule.sh
index 652861aa66..87772ac891 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -10,7 +10,7 @@ USAGE="[--quiet] [--cached]
or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
or: $dashless [--quiet] init [--] [<path>...]
or: $dashless [--quiet] deinit [-f|--force] (--all| [--] <path>...)
- or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-shallow] [--reference <repository>] [--recursive] [--[no-]single-branch] [--] [<path>...]
+ or: $dashless [--quiet] update [--init [--filter=<filter-spec>]] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-shallow] [--reference <repository>] [--recursive] [--[no-]single-branch] [--] [<path>...]
or: $dashless [--quiet] set-branch (--default|--branch <branch>) [--] <path>
or: $dashless [--quiet] set-url [--] <path> <newurl>
or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
@@ -49,6 +49,7 @@ dissociate=
single_branch=
jobs=
recommend_shallow=
+filter=
die_if_unmatched ()
{
@@ -347,6 +348,14 @@ cmd_update()
--no-single-branch)
single_branch="--no-single-branch"
;;
+ --filter)
+ case "$2" in '') usage ;; esac
+ filter="--filter=$2"
+ shift
+ ;;
+ --filter=*)
+ filter="$1"
+ ;;
--)
shift
break
@@ -361,6 +370,11 @@ cmd_update()
shift
done
+ if test -n "$filter" && test "$init" != "1"
+ then
+ usage
+ fi
+
if test -n "$init"
then
cmd_init "--" "$@" || return
@@ -379,6 +393,7 @@ cmd_update()
$single_branch \
$recommend_shallow \
$jobs \
+ $filter \
-- \
"$@" || echo "#unmatched" $?
} | {
diff --git a/git.c b/git.c
index 340665d4a0..a25940d72e 100644
--- a/git.c
+++ b/git.c
@@ -436,6 +436,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
} else {
prefix = NULL;
}
+ assert(!prefix || *prefix);
precompose_argv_prefix(argc, argv, NULL);
if (use_pager == -1 && run_setup &&
!(p->option & DELAY_PAGER_CONFIG))
diff --git a/gpg-interface.c b/gpg-interface.c
index 17b1e44baa..aa50224e67 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -716,7 +716,7 @@ int git_gpg_config(const char *var, const char *value, void *cb)
return config_error_nonbool(var);
fmt = get_format_by_name(value);
if (!fmt)
- return error("unsupported value for %s: %s",
+ return error(_("invalid value for '%s': '%s'"),
var, value);
use_format = fmt;
return 0;
@@ -731,8 +731,8 @@ int git_gpg_config(const char *var, const char *value, void *cb)
free(trust);
if (ret)
- return error("unsupported value for %s: %s", var,
- value);
+ return error(_("invalid value for '%s': '%s'"),
+ var, value);
return 0;
}
diff --git a/graph.c b/graph.c
index e3828eb8f2..568b6e7cd4 100644
--- a/graph.c
+++ b/graph.c
@@ -401,6 +401,18 @@ struct git_graph *graph_init(struct rev_info *opt)
return graph;
}
+void graph_clear(struct git_graph *graph)
+{
+ if (!graph)
+ return;
+
+ free(graph->columns);
+ free(graph->new_columns);
+ free(graph->mapping);
+ free(graph->old_mapping);
+ free(graph);
+}
+
static void graph_update_state(struct git_graph *graph, enum graph_state s)
{
graph->prev_state = graph->state;
diff --git a/graph.h b/graph.h
index 8313e293c7..e88632a014 100644
--- a/graph.h
+++ b/graph.h
@@ -140,6 +140,11 @@ void graph_set_column_colors(const char **colors, unsigned short colors_max);
struct git_graph *graph_init(struct rev_info *opt);
/*
+ * Free a struct git_graph.
+ */
+void graph_clear(struct git_graph *graph);
+
+/*
* Update a git_graph with a new commit.
* This will cause the graph to begin outputting lines for the new commit
* the next time graph_next_line() is called.
diff --git a/grep.c b/grep.c
index 5bec7fd793..82eb7da102 100644
--- a/grep.c
+++ b/grep.c
@@ -19,27 +19,6 @@ static void std_output(struct grep_opt *opt, const void *buf, size_t size)
fwrite(buf, size, 1, stdout);
}
-static struct grep_opt grep_defaults = {
- .relative = 1,
- .pathname = 1,
- .max_depth = -1,
- .pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED,
- .colors = {
- [GREP_COLOR_CONTEXT] = "",
- [GREP_COLOR_FILENAME] = GIT_COLOR_MAGENTA,
- [GREP_COLOR_FUNCTION] = "",
- [GREP_COLOR_LINENO] = GIT_COLOR_GREEN,
- [GREP_COLOR_COLUMNNO] = GIT_COLOR_GREEN,
- [GREP_COLOR_MATCH_CONTEXT] = GIT_COLOR_BOLD_RED,
- [GREP_COLOR_MATCH_SELECTED] = GIT_COLOR_BOLD_RED,
- [GREP_COLOR_SELECTED] = "",
- [GREP_COLOR_SEP] = GIT_COLOR_CYAN,
- },
- .only_matching = 0,
- .color = -1,
- .output = std_output,
-};
-
static const char *color_grep_slots[] = {
[GREP_COLOR_CONTEXT] = "context",
[GREP_COLOR_FILENAME] = "filename",
@@ -75,20 +54,12 @@ define_list_config_array_extra(color_grep_slots, {"match"});
*/
int grep_config(const char *var, const char *value, void *cb)
{
- struct grep_opt *opt = &grep_defaults;
+ struct grep_opt *opt = cb;
const char *slot;
if (userdiff_config(var, value) < 0)
return -1;
- /*
- * The instance of grep_opt that we set up here is copied by
- * grep_init() to be used by each individual invocation.
- * When populating a new field of this structure here, be
- * sure to think about ownership -- e.g., you might need to
- * override the shallow copy in grep_init() with a deep copy.
- */
-
if (!strcmp(var, "grep.extendedregexp")) {
opt->extended_regexp_option = git_config_bool(var, value);
return 0;
@@ -134,78 +105,16 @@ int grep_config(const char *var, const char *value, void *cb)
return 0;
}
-/*
- * Initialize one instance of grep_opt and copy the
- * default values from the template we read the configuration
- * information in an earlier call to git_config(grep_config).
- */
-void grep_init(struct grep_opt *opt, struct repository *repo, const char *prefix)
+void grep_init(struct grep_opt *opt, struct repository *repo)
{
- *opt = grep_defaults;
+ struct grep_opt blank = GREP_OPT_INIT;
+ memcpy(opt, &blank, sizeof(*opt));
opt->repo = repo;
- opt->prefix = prefix;
- opt->prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;
opt->pattern_tail = &opt->pattern_list;
opt->header_tail = &opt->header_list;
}
-static void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct grep_opt *opt)
-{
- /*
- * When committing to the pattern type by setting the relevant
- * fields in grep_opt it's generally not necessary to zero out
- * the fields we're not choosing, since they won't have been
- * set by anything. The extended_regexp_option field is the
- * only exception to this.
- *
- * This is because in the process of parsing grep.patternType
- * & grep.extendedRegexp we set opt->pattern_type_option and
- * opt->extended_regexp_option, respectively. We then
- * internally use opt->extended_regexp_option to see if we're
- * compiling an ERE. It must be unset if that's not actually
- * the case.
- */
- if (pattern_type != GREP_PATTERN_TYPE_ERE &&
- opt->extended_regexp_option)
- opt->extended_regexp_option = 0;
-
- switch (pattern_type) {
- case GREP_PATTERN_TYPE_UNSPECIFIED:
- /* fall through */
-
- case GREP_PATTERN_TYPE_BRE:
- break;
-
- case GREP_PATTERN_TYPE_ERE:
- opt->extended_regexp_option = 1;
- break;
-
- case GREP_PATTERN_TYPE_FIXED:
- opt->fixed = 1;
- break;
-
- case GREP_PATTERN_TYPE_PCRE:
- opt->pcre2 = 1;
- break;
- }
-}
-
-void grep_commit_pattern_type(enum grep_pattern_type pattern_type, struct grep_opt *opt)
-{
- if (pattern_type != GREP_PATTERN_TYPE_UNSPECIFIED)
- grep_set_pattern_type_option(pattern_type, opt);
- else if (opt->pattern_type_option != GREP_PATTERN_TYPE_UNSPECIFIED)
- grep_set_pattern_type_option(opt->pattern_type_option, opt);
- else if (opt->extended_regexp_option)
- /*
- * This branch *must* happen after setting from the
- * opt->pattern_type_option above, we don't want
- * grep.extendedRegexp to override grep.patternType!
- */
- grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, opt);
-}
-
static struct grep_pat *create_grep_pat(const char *pat, size_t patlen,
const char *origin, int no,
enum grep_pat_token t,
@@ -386,7 +295,7 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt
if (!opt->ignore_locale && is_utf8_locale() && !literal)
options |= (PCRE2_UTF | PCRE2_MATCH_INVALID_UTF);
-#ifdef GIT_PCRE2_VERSION_10_36_OR_HIGHER
+#ifndef GIT_PCRE2_VERSION_10_36_OR_HIGHER
/* Work around https://bugs.exim.org/show_bug.cgi?id=2642 fixed in 10.36 */
if (PCRE2_MATCH_INVALID_UTF && options & (PCRE2_UTF | PCRE2_CASELESS))
options |= PCRE2_NO_START_OPTIMIZE;
@@ -523,11 +432,17 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
int err;
int regflags = REG_NEWLINE;
+ if (opt->pattern_type_option == GREP_PATTERN_TYPE_UNSPECIFIED)
+ opt->pattern_type_option = (opt->extended_regexp_option
+ ? GREP_PATTERN_TYPE_ERE
+ : GREP_PATTERN_TYPE_BRE);
+
p->word_regexp = opt->word_regexp;
p->ignore_case = opt->ignore_case;
- p->fixed = opt->fixed;
+ p->fixed = opt->pattern_type_option == GREP_PATTERN_TYPE_FIXED;
- if (memchr(p->pattern, 0, p->patternlen) && !opt->pcre2)
+ if (opt->pattern_type_option != GREP_PATTERN_TYPE_PCRE &&
+ memchr(p->pattern, 0, p->patternlen))
die(_("given pattern contains NULL byte (via -f <file>). This is only supported with -P under PCRE v2"));
p->is_fixed = is_fixed(p->pattern, p->patternlen);
@@ -578,14 +493,14 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
return;
}
- if (opt->pcre2) {
+ if (opt->pattern_type_option == GREP_PATTERN_TYPE_PCRE) {
compile_pcre2_pattern(p, opt);
return;
}
if (p->ignore_case)
regflags |= REG_ICASE;
- if (opt->extended_regexp_option)
+ if (opt->pattern_type_option == GREP_PATTERN_TYPE_ERE)
regflags |= REG_EXTENDED;
err = regcomp(&p->regexp, p->pattern, regflags);
if (err) {
diff --git a/grep.h b/grep.h
index 6a1f0ab017..c722d25ed9 100644
--- a/grep.h
+++ b/grep.h
@@ -134,9 +134,6 @@ struct grep_opt {
*/
struct repository *repo;
- const char *prefix;
- int prefix_length;
- regex_t regexp;
int linenum;
int columnnum;
int invert;
@@ -146,7 +143,6 @@ struct grep_opt {
int unmatch_name_only;
int count;
int word_regexp;
- int fixed;
int all_match;
int no_body_match;
int body_hit;
@@ -157,7 +153,6 @@ struct grep_opt {
int allow_textconv;
int extended;
int use_reflog_filter;
- int pcre2;
int relative;
int pathname;
int null_following_name;
@@ -167,7 +162,7 @@ struct grep_opt {
int funcname;
int funcbody;
int extended_regexp_option;
- int pattern_type_option;
+ enum grep_pattern_type pattern_type_option;
int ignore_locale;
char colors[NR_GREP_COLORS][COLOR_MAXLEN];
unsigned pre_context;
@@ -182,9 +177,29 @@ struct grep_opt {
void *output_priv;
};
+#define GREP_OPT_INIT { \
+ .relative = 1, \
+ .pathname = 1, \
+ .max_depth = -1, \
+ .pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED, \
+ .colors = { \
+ [GREP_COLOR_CONTEXT] = "", \
+ [GREP_COLOR_FILENAME] = GIT_COLOR_MAGENTA, \
+ [GREP_COLOR_FUNCTION] = "", \
+ [GREP_COLOR_LINENO] = GIT_COLOR_GREEN, \
+ [GREP_COLOR_COLUMNNO] = GIT_COLOR_GREEN, \
+ [GREP_COLOR_MATCH_CONTEXT] = GIT_COLOR_BOLD_RED, \
+ [GREP_COLOR_MATCH_SELECTED] = GIT_COLOR_BOLD_RED, \
+ [GREP_COLOR_SELECTED] = "", \
+ [GREP_COLOR_SEP] = GIT_COLOR_CYAN, \
+ }, \
+ .only_matching = 0, \
+ .color = -1, \
+ .output = std_output, \
+}
+
int grep_config(const char *var, const char *value, void *);
-void grep_init(struct grep_opt *, struct repository *repo, const char *prefix);
-void grep_commit_pattern_type(enum grep_pattern_type, struct grep_opt *opt);
+void grep_init(struct grep_opt *, struct repository *repo);
void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t);
void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
diff --git a/http-backend.c b/http-backend.c
index 807fb8839e..81a7229ece 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -13,6 +13,7 @@
#include "packfile.h"
#include "object-store.h"
#include "protocol.h"
+#include "date.h"
static const char content_type[] = "Content-Type";
static const char content_length[] = "Content-Length";
diff --git a/ident.c b/ident.c
index 6aba4b5cb6..89ca5b4700 100644
--- a/ident.c
+++ b/ident.c
@@ -7,6 +7,7 @@
*/
#include "cache.h"
#include "config.h"
+#include "date.h"
static struct strbuf git_default_name = STRBUF_INIT;
static struct strbuf git_default_email = STRBUF_INIT;
diff --git a/ls-refs.c b/ls-refs.c
index 54078323dc..98e69373c8 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -34,7 +34,8 @@ static void ensure_config_read(void)
} else if (!strcmp(str, "ignore")) {
/* do nothing */
} else {
- die(_("invalid value '%s' for lsrefs.unborn"), str);
+ die(_("invalid value for '%s': '%s'"),
+ "lsrefs.unborn", str);
}
}
config_read = 1;
diff --git a/merge-ort.c b/merge-ort.c
index d85b1cd99e..ff739d4b36 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -651,6 +651,11 @@ static void path_msg(struct merge_options *opt,
dest = (opt->record_conflict_msgs_as_headers ? &tmp : sb);
va_start(ap, fmt);
+ if (opt->priv->call_depth) {
+ strbuf_addchars(dest, ' ', 2);
+ strbuf_addstr(dest, "From inner merge:");
+ strbuf_addchars(dest, ' ', opt->priv->call_depth * 2);
+ }
strbuf_vaddf(dest, fmt, ap);
va_end(ap);
@@ -722,13 +727,15 @@ static void add_flattened_path(struct strbuf *out, const char *s)
out->buf[i] = '_';
}
-static char *unique_path(struct strmap *existing_paths,
+static char *unique_path(struct merge_options *opt,
const char *path,
const char *branch)
{
+ char *ret = NULL;
struct strbuf newpath = STRBUF_INIT;
int suffix = 0;
size_t base_len;
+ struct strmap *existing_paths = &opt->priv->paths;
strbuf_addf(&newpath, "%s~", path);
add_flattened_path(&newpath, branch);
@@ -739,7 +746,11 @@ static char *unique_path(struct strmap *existing_paths,
strbuf_addf(&newpath, "_%d", suffix++);
}
- return strbuf_detach(&newpath, NULL);
+ /* Track the new path in our memory pool */
+ ret = mem_pool_alloc(&opt->priv->pool, newpath.len + 1);
+ memcpy(ret, newpath.buf, newpath.len + 1);
+ strbuf_release(&newpath);
+ return ret;
}
/*** Function Grouping: functions related to collect_merge_info() ***/
@@ -3086,12 +3097,11 @@ static int detect_and_process_renames(struct merge_options *opt,
struct tree *side1,
struct tree *side2)
{
- struct diff_queue_struct combined;
+ struct diff_queue_struct combined = { 0 };
struct rename_info *renames = &opt->priv->renames;
- int need_dir_renames, s, clean = 1;
+ int need_dir_renames, s, i, clean = 1;
unsigned detection_run = 0;
- memset(&combined, 0, sizeof(combined));
if (!possible_renames(renames))
goto cleanup;
@@ -3175,13 +3185,9 @@ simple_cleanup:
free(renames->pairs[s].queue);
DIFF_QUEUE_CLEAR(&renames->pairs[s]);
}
- if (combined.nr) {
- int i;
- for (i = 0; i < combined.nr; i++)
- pool_diff_free_filepair(&opt->priv->pool,
- combined.queue[i]);
- free(combined.queue);
- }
+ for (i = 0; i < combined.nr; i++)
+ pool_diff_free_filepair(&opt->priv->pool, combined.queue[i]);
+ free(combined.queue);
return clean;
}
@@ -3679,7 +3685,7 @@ static void process_entry(struct merge_options *opt,
*/
df_file_index = (ci->dirmask & (1 << 1)) ? 2 : 1;
branch = (df_file_index == 1) ? opt->branch1 : opt->branch2;
- path = unique_path(&opt->priv->paths, path, branch);
+ path = unique_path(opt, path, branch);
strmap_put(&opt->priv->paths, path, new_ci);
path_msg(opt, path, 0,
@@ -3804,14 +3810,12 @@ static void process_entry(struct merge_options *opt,
/* Insert entries into opt->priv_paths */
assert(rename_a || rename_b);
if (rename_a) {
- a_path = unique_path(&opt->priv->paths,
- path, opt->branch1);
+ a_path = unique_path(opt, path, opt->branch1);
strmap_put(&opt->priv->paths, a_path, ci);
}
if (rename_b)
- b_path = unique_path(&opt->priv->paths,
- path, opt->branch2);
+ b_path = unique_path(opt, path, opt->branch2);
else
b_path = path;
strmap_put(&opt->priv->paths, b_path, new_ci);
@@ -4199,7 +4203,7 @@ static int record_conflicted_index_entries(struct merge_options *opt)
struct stat st;
if (!lstat(path, &st)) {
- char *new_name = unique_path(&opt->priv->paths,
+ char *new_name = unique_path(opt,
path,
"cruft");
@@ -4207,7 +4211,6 @@ static int record_conflicted_index_entries(struct merge_options *opt)
_("Note: %s not up to date and in way of checking out conflicted version; old copy renamed to %s"),
path, new_name);
errs |= rename(path, new_name);
- free(new_name);
}
errs |= checkout_entry(ce, &state, NULL, NULL);
}
diff --git a/notes-merge.c b/notes-merge.c
index 01d596920e..878b6c571b 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -175,7 +175,6 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o,
oid_to_hex(&mp->remote));
}
diff_flush(&opt);
- clear_pathspec(&opt.pathspec);
*num_changes = len;
return changes;
@@ -261,7 +260,6 @@ static void diff_tree_local(struct notes_merge_options *o,
oid_to_hex(&mp->local));
}
diff_flush(&opt);
- clear_pathspec(&opt.pathspec);
}
static void check_notes_merge_worktree(struct notes_merge_options *o)
diff --git a/object-name.c b/object-name.c
index 92862eeb1a..f0e327f91f 100644
--- a/object-name.c
+++ b/object-name.c
@@ -15,6 +15,7 @@
#include "submodule.h"
#include "midx.h"
#include "commit-reach.h"
+#include "date.h"
static int get_oid_oneline(struct repository *r, const char *, struct object_id *, struct commit_list *);
@@ -351,35 +352,118 @@ static int init_object_disambiguation(struct repository *r,
return 0;
}
+struct ambiguous_output {
+ const struct disambiguate_state *ds;
+ struct strbuf advice;
+ struct strbuf sb;
+};
+
static int show_ambiguous_object(const struct object_id *oid, void *data)
{
- const struct disambiguate_state *ds = data;
- struct strbuf desc = STRBUF_INIT;
+ struct ambiguous_output *state = data;
+ const struct disambiguate_state *ds = state->ds;
+ struct strbuf *advice = &state->advice;
+ struct strbuf *sb = &state->sb;
int type;
+ const char *hash;
if (ds->fn && !ds->fn(ds->repo, oid, ds->cb_data))
return 0;
+ hash = repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV);
type = oid_object_info(ds->repo, oid, NULL);
+
+ if (type < 0) {
+ /*
+ * TRANSLATORS: This is a line of ambiguous object
+ * output shown when we cannot look up or parse the
+ * object in question. E.g. "deadbeef [bad object]".
+ */
+ strbuf_addf(sb, _("%s [bad object]"), hash);
+ goto out;
+ }
+
+ assert(type == OBJ_TREE || type == OBJ_COMMIT ||
+ type == OBJ_BLOB || type == OBJ_TAG);
+
if (type == OBJ_COMMIT) {
+ struct strbuf date = STRBUF_INIT;
+ struct strbuf msg = STRBUF_INIT;
struct commit *commit = lookup_commit(ds->repo, oid);
+
if (commit) {
struct pretty_print_context pp = {0};
pp.date_mode.type = DATE_SHORT;
- format_commit_message(commit, " %ad - %s", &desc, &pp);
+ format_commit_message(commit, "%ad", &date, &pp);
+ format_commit_message(commit, "%s", &msg, &pp);
}
+
+ /*
+ * TRANSLATORS: This is a line of ambiguous commit
+ * object output. E.g.:
+ *
+ * "deadbeef commit 2021-01-01 - Some Commit Message"
+ */
+ strbuf_addf(sb, _("%s commit %s - %s"), hash, date.buf,
+ msg.buf);
+
+ strbuf_release(&date);
+ strbuf_release(&msg);
} else if (type == OBJ_TAG) {
struct tag *tag = lookup_tag(ds->repo, oid);
- if (!parse_tag(tag) && tag->tag)
- strbuf_addf(&desc, " %s", tag->tag);
+
+ if (!parse_tag(tag) && tag->tag) {
+ /*
+ * TRANSLATORS: This is a line of ambiguous
+ * tag object output. E.g.:
+ *
+ * "deadbeef tag 2022-01-01 - Some Tag Message"
+ *
+ * The second argument is the YYYY-MM-DD found
+ * in the tag.
+ *
+ * The third argument is the "tag" string
+ * from object.c.
+ */
+ strbuf_addf(sb, _("%s tag %s - %s"), hash,
+ show_date(tag->date, 0, DATE_MODE(SHORT)),
+ tag->tag);
+ } else {
+ /*
+ * TRANSLATORS: This is a line of ambiguous
+ * tag object output where we couldn't parse
+ * the tag itself. E.g.:
+ *
+ * "deadbeef [bad tag, could not parse it]"
+ */
+ strbuf_addf(sb, _("%s [bad tag, could not parse it]"),
+ hash);
+ }
+ } else if (type == OBJ_TREE) {
+ /*
+ * TRANSLATORS: This is a line of ambiguous <type>
+ * object output. E.g. "deadbeef tree".
+ */
+ strbuf_addf(sb, _("%s tree"), hash);
+ } else if (type == OBJ_BLOB) {
+ /*
+ * TRANSLATORS: This is a line of ambiguous <type>
+ * object output. E.g. "deadbeef blob".
+ */
+ strbuf_addf(sb, _("%s blob"), hash);
}
- advise(" %s %s%s",
- repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV),
- type_name(type) ? type_name(type) : "unknown type",
- desc.buf);
- strbuf_release(&desc);
+out:
+ /*
+ * TRANSLATORS: This is line item of ambiguous object output
+ * from describe_ambiguous_object() above. For RTL languages
+ * you'll probably want to swap the "%s" and leading " " space
+ * around.
+ */
+ strbuf_addf(advice, _(" %s\n"), sb->buf);
+
+ strbuf_reset(sb);
return 0;
}
@@ -476,6 +560,11 @@ static enum get_oid_result get_short_oid(struct repository *r,
if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {
struct oid_array collect = OID_ARRAY_INIT;
+ struct ambiguous_output out = {
+ .ds = &ds,
+ .sb = STRBUF_INIT,
+ .advice = STRBUF_INIT,
+ };
error(_("short object ID %s is ambiguous"), ds.hex_pfx);
@@ -488,13 +577,22 @@ static enum get_oid_result get_short_oid(struct repository *r,
if (!ds.ambiguous)
ds.fn = NULL;
- advise(_("The candidates are:"));
repo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect);
sort_ambiguous_oid_array(r, &collect);
- if (oid_array_for_each(&collect, show_ambiguous_object, &ds))
+ if (oid_array_for_each(&collect, show_ambiguous_object, &out))
BUG("show_ambiguous_object shouldn't return non-zero");
+
+ /*
+ * TRANSLATORS: The argument is the list of ambiguous
+ * objects composed in show_ambiguous_object(). See
+ * its "TRANSLATORS" comments for details.
+ */
+ advise(_("The candidates are:\n%s"), out.advice.buf);
+
oid_array_clear(&collect);
+ strbuf_release(&out.advice);
+ strbuf_release(&out.sb);
}
return status;
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index 9c55c1531e..cab3eaa2ac 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -575,15 +575,15 @@ void bitmap_writer_select_commits(struct commit **indexed_commits,
QSORT(indexed_commits, indexed_commits_nr, date_compare);
- if (writer.show_progress)
- writer.progress = start_progress("Selecting bitmap commits", 0);
-
if (indexed_commits_nr < 100) {
for (i = 0; i < indexed_commits_nr; ++i)
push_bitmapped_commit(indexed_commits[i]);
return;
}
+ if (writer.show_progress)
+ writer.progress = start_progress("Selecting bitmap commits", 0);
+
for (;;) {
struct commit *chosen = NULL;
diff --git a/parallel-checkout.c b/parallel-checkout.c
index 8dd7e7bad4..31a3d0ee1b 100644
--- a/parallel-checkout.c
+++ b/parallel-checkout.c
@@ -39,8 +39,8 @@ void get_parallel_checkout_configs(int *num_workers, int *threshold)
if (env_workers && *env_workers) {
if (strtol_i(env_workers, 10, num_workers)) {
- die("invalid value for GIT_TEST_CHECKOUT_WORKERS: '%s'",
- env_workers);
+ die(_("invalid value for '%s': '%s'"),
+ "GIT_TEST_CHECKOUT_WORKERS", env_workers);
}
if (*num_workers < 1)
*num_workers = online_cpus();
diff --git a/parse-options.c b/parse-options.c
index 2437ad3bcd..6e57744fd2 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -1092,3 +1092,37 @@ void NORETURN usage_msg_optf(const char * const fmt,
usage_msg_opt(msg.buf, usagestr, options);
}
+
+void die_for_incompatible_opt4(int opt1, const char *opt1_name,
+ int opt2, const char *opt2_name,
+ int opt3, const char *opt3_name,
+ int opt4, const char *opt4_name)
+{
+ int count = 0;
+ const char *options[4];
+
+ if (opt1)
+ options[count++] = opt1_name;
+ if (opt2)
+ options[count++] = opt2_name;
+ if (opt3)
+ options[count++] = opt3_name;
+ if (opt4)
+ options[count++] = opt4_name;
+ switch (count) {
+ case 4:
+ die(_("options '%s', '%s', '%s', and '%s' cannot be used together"),
+ opt1_name, opt2_name, opt3_name, opt4_name);
+ break;
+ case 3:
+ die(_("options '%s', '%s', and '%s' cannot be used together"),
+ options[0], options[1], options[2]);
+ break;
+ case 2:
+ die(_("options '%s' and '%s' cannot be used together"),
+ options[0], options[1]);
+ break;
+ default:
+ break;
+ }
+}
diff --git a/parse-options.h b/parse-options.h
index 659a4c28b2..685fccac13 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -240,6 +240,22 @@ void NORETURN usage_msg_optf(const char *fmt,
const char * const *usagestr,
const struct option *options, ...);
+void die_for_incompatible_opt4(int opt1, const char *opt1_name,
+ int opt2, const char *opt2_name,
+ int opt3, const char *opt3_name,
+ int opt4, const char *opt4_name);
+
+
+static inline void die_for_incompatible_opt3(int opt1, const char *opt1_name,
+ int opt2, const char *opt2_name,
+ int opt3, const char *opt3_name)
+{
+ die_for_incompatible_opt4(opt1, opt1_name,
+ opt2, opt2_name,
+ opt3, opt3_name,
+ 0, "");
+}
+
/*
* 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/pretty.h b/pretty.h
index 2f16acd213..f34e24c53a 100644
--- a/pretty.h
+++ b/pretty.h
@@ -2,6 +2,7 @@
#define PRETTY_H
#include "cache.h"
+#include "date.h"
#include "string-list.h"
struct commit;
@@ -163,4 +164,13 @@ int format_set_trailers_options(struct process_trailer_options *opts,
const char **arg,
char **invalid_arg);
+/*
+ * Like show_date, but pull the timestamp and tz parameters from
+ * the ident_split. It will also sanity-check the values and produce
+ * a well-known sentinel date if they appear bogus.
+ */
+const char *show_ident_date(const struct ident_split *id,
+ const struct date_mode *mode);
+
+
#endif /* PRETTY_H */
diff --git a/progress.c b/progress.c
index 680c6a8bf9..0cdd875d37 100644
--- a/progress.c
+++ b/progress.c
@@ -311,32 +311,39 @@ struct progress *start_delayed_sparse_progress(const char *title,
static void finish_if_sparse(struct progress *progress)
{
- if (progress &&
- progress->sparse &&
+ if (progress->sparse &&
progress->last_value != progress->total)
display_progress(progress, progress->total);
}
-void stop_progress(struct progress **p_progress)
+static void force_last_update(struct progress *progress, const char *msg)
{
- if (!p_progress)
- BUG("don't provide NULL to stop_progress");
-
- finish_if_sparse(*p_progress);
-
- if (*p_progress) {
- trace2_data_intmax("progress", the_repository, "total_objects",
- (*p_progress)->total);
+ char *buf;
+ struct throughput *tp = progress->throughput;
+
+ if (tp) {
+ uint64_t now_ns = progress_getnanotime(progress);
+ unsigned int misecs, rate;
+ misecs = ((now_ns - progress->start_ns) * 4398) >> 32;
+ rate = tp->curr_total / (misecs ? misecs : 1);
+ throughput_string(&tp->display, tp->curr_total, rate);
+ }
+ progress_update = 1;
+ buf = xstrfmt(", %s.\n", msg);
+ display(progress, progress->last_value, buf);
+ free(buf);
+}
- if ((*p_progress)->throughput)
- trace2_data_intmax("progress", the_repository,
- "total_bytes",
- (*p_progress)->throughput->curr_total);
+static void log_trace2(struct progress *progress)
+{
+ trace2_data_intmax("progress", the_repository, "total_objects",
+ progress->total);
- trace2_region_leave("progress", (*p_progress)->title, the_repository);
- }
+ if (progress->throughput)
+ trace2_data_intmax("progress", the_repository, "total_bytes",
+ progress->throughput->curr_total);
- stop_progress_msg(p_progress, _("done"));
+ trace2_region_leave("progress", progress->title, the_repository);
}
void stop_progress_msg(struct progress **p_progress, const char *msg)
@@ -350,23 +357,12 @@ void stop_progress_msg(struct progress **p_progress, const char *msg)
if (!progress)
return;
*p_progress = NULL;
- if (progress->last_value != -1) {
- /* Force the last update */
- char *buf;
- struct throughput *tp = progress->throughput;
-
- if (tp) {
- uint64_t now_ns = progress_getnanotime(progress);
- unsigned int misecs, rate;
- misecs = ((now_ns - progress->start_ns) * 4398) >> 32;
- rate = tp->curr_total / (misecs ? misecs : 1);
- throughput_string(&tp->display, tp->curr_total, rate);
- }
- progress_update = 1;
- buf = xstrfmt(", %s.\n", msg);
- display(progress, progress->last_value, buf);
- free(buf);
- }
+
+ finish_if_sparse(progress);
+ if (progress->last_value != -1)
+ force_last_update(progress, msg);
+ log_trace2(progress);
+
clear_progress_signal();
strbuf_release(&progress->counters_sb);
if (progress->throughput)
diff --git a/progress.h b/progress.h
index f1913acf73..3a945637c8 100644
--- a/progress.h
+++ b/progress.h
@@ -1,5 +1,6 @@
#ifndef PROGRESS_H
#define PROGRESS_H
+#include "gettext.h"
struct progress;
@@ -18,7 +19,9 @@ struct progress *start_sparse_progress(const char *title, uint64_t total);
struct progress *start_delayed_progress(const char *title, uint64_t total);
struct progress *start_delayed_sparse_progress(const char *title,
uint64_t total);
-void stop_progress(struct progress **progress);
-void stop_progress_msg(struct progress **progress, const char *msg);
-
+void stop_progress_msg(struct progress **p_progress, const char *msg);
+static inline void stop_progress(struct progress **p_progress)
+{
+ stop_progress_msg(p_progress, _("done"));
+}
#endif
diff --git a/ref-filter.c b/ref-filter.c
index f7a2f17bfd..7838bd22b8 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -1251,7 +1251,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
char *zone;
timestamp_t timestamp;
long tz;
- struct date_mode date_mode = { DATE_NORMAL };
+ struct date_mode date_mode = DATE_MODE_INIT;
const char *formatp;
/*
@@ -1276,6 +1276,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
goto bad;
v->s = xstrdup(show_date(timestamp, tz, &date_mode));
v->value = timestamp;
+ date_mode_release(&date_mode);
return;
bad:
v->s = xstrdup("");
diff --git a/reflog-walk.h b/reflog-walk.h
index f26408f6cc..e9e00ffd47 100644
--- a/reflog-walk.h
+++ b/reflog-walk.h
@@ -5,6 +5,7 @@
struct commit;
struct reflog_walk_info;
+struct date_mode;
void init_reflog_walk(struct reflog_walk_info **info);
int add_reflog_for_walk(struct reflog_walk_info *info,
diff --git a/refs.c b/refs.c
index d680de3bc0..183e9ce3a2 100644
--- a/refs.c
+++ b/refs.c
@@ -19,6 +19,7 @@
#include "strvec.h"
#include "repository.h"
#include "sigchain.h"
+#include "date.h"
/*
* List of all available backends
diff --git a/revision.c b/revision.c
index d8d326d6b0..2bb913f2f3 100644
--- a/revision.c
+++ b/revision.c
@@ -1846,7 +1846,7 @@ void repo_init_revisions(struct repository *r,
revs->commit_format = CMIT_FMT_DEFAULT;
revs->expand_tabs_in_log_default = 8;
- grep_init(&revs->grep_filter, revs->repo, prefix);
+ grep_init(&revs->grep_filter, revs->repo);
revs->grep_filter.status_only = 1;
repo_diff_setup(revs->repo, &revs->diffopt);
@@ -2434,9 +2434,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->pretty_given = 1;
revs->abbrev_commit = 1;
} else if (!strcmp(arg, "--graph")) {
- revs->topo_order = 1;
- revs->rewrite_parents = 1;
+ graph_clear(revs->graph);
revs->graph = graph_init(revs);
+ } else if (!strcmp(arg, "--no-graph")) {
+ graph_clear(revs->graph);
+ revs->graph = NULL;
} else if (!strcmp(arg, "--encode-email-headers")) {
revs->encode_email_headers = 1;
} else if (!strcmp(arg, "--no-encode-email-headers")) {
@@ -2533,8 +2535,6 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
unkv[(*unkc)++] = arg;
return opts;
}
- if (revs->graph && revs->track_linear)
- die(_("options '%s' and '%s' cannot be used together"), "--show-linear-break", "--graph");
return 1;
}
@@ -2553,6 +2553,17 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
ctx->argc -= n;
}
+void revision_opts_finish(struct rev_info *revs)
+{
+ if (revs->graph && revs->track_linear)
+ die(_("options '%s' and '%s' cannot be used together"), "--show-linear-break", "--graph");
+
+ if (revs->graph) {
+ revs->topo_order = 1;
+ revs->rewrite_parents = 1;
+ }
+}
+
static int for_each_bisect_ref(struct ref_store *refs, each_ref_fn fn,
void *cb_data, const char *term)
{
@@ -2795,6 +2806,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
break;
}
}
+ revision_opts_finish(revs);
if (prune_data.nr) {
/*
@@ -2870,8 +2882,6 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
diff_setup_done(&revs->diffopt);
- grep_commit_pattern_type(GREP_PATTERN_TYPE_UNSPECIFIED,
- &revs->grep_filter);
if (!is_encoding_utf8(get_log_output_encoding()))
revs->grep_filter.ignore_locale = 1;
compile_grep_patterns(&revs->grep_filter);
diff --git a/revision.h b/revision.h
index e16d06f29d..b9c2421687 100644
--- a/revision.h
+++ b/revision.h
@@ -377,6 +377,7 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
#define REVARG_COMMITTISH 02
int handle_revision_arg(const char *arg, struct rev_info *revs,
int flags, unsigned revarg_opt);
+void revision_opts_finish(struct rev_info *revs);
/**
* Reset the flags used by the revision walking api. You can use this to do
diff --git a/sequencer.c b/sequencer.c
index 9b697158c5..35006c0cea 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2802,7 +2802,7 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
return error(_("invalid key: %s"), key);
if (!error_flag)
- return error(_("invalid value for %s: %s"), key, value);
+ return error(_("invalid value for '%s': '%s'"), key, value);
return 0;
}
diff --git a/setup.c b/setup.c
index af3b8c09ab..04ce33cdcd 100644
--- a/setup.c
+++ b/setup.c
@@ -559,7 +559,8 @@ static enum extension_result handle_extension(const char *var,
return config_error_nonbool(var);
format = hash_algo_by_name(value);
if (format == GIT_HASH_UNKNOWN)
- return error("invalid value for 'extensions.objectformat'");
+ return error(_("invalid value for '%s': '%s'"),
+ "extensions.objectformat", value);
data->hash_algo = format;
return EXTENSION_OK;
}
diff --git a/sparse-index.c b/sparse-index.c
index 08f54747bb..fdbe97b976 100644
--- a/sparse-index.c
+++ b/sparse-index.c
@@ -99,13 +99,9 @@ static int convert_to_sparse_rec(struct index_state *istate,
int set_sparse_index_config(struct repository *repo, int enable)
{
- int res;
- char *config_path = repo_git_path(repo, "config.worktree");
- res = git_config_set_in_file_gently(config_path,
- "index.sparse",
- enable ? "true" : NULL);
- free(config_path);
-
+ int res = repo_config_set_worktree_gently(repo,
+ "index.sparse",
+ enable ? "true" : "false");
prepare_repo_settings(repo);
repo->settings.sparse_index = enable;
return res;
diff --git a/strbuf.c b/strbuf.c
index 613fee8c82..00abeb55af 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -2,6 +2,7 @@
#include "refs.h"
#include "string-list.h"
#include "utf8.h"
+#include "date.h"
int starts_with(const char *str, const char *prefix)
{
diff --git a/submodule-config.c b/submodule-config.c
index c9f54bc72d..29668b0620 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -497,7 +497,7 @@ static int parse_config(const char *var, const char *value, void *data)
else if (parse_submodule_update_strategy(value,
&submodule->update_strategy) < 0 ||
submodule->update_strategy.type == SM_UPDATE_COMMAND)
- die(_("invalid value for %s"), var);
+ die(_("invalid value for '%s'"), var);
} else if (!strcmp(item.buf, "shallow")) {
if (!me->overwrite && submodule->recommend_shallow != -1)
warn_multiple_config(me->treeish_name, submodule->name,
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 099eff4f0f..45951b1df8 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -1,5 +1,6 @@
#include "test-tool.h"
#include "cache.h"
+#include "date.h"
static const char *usage_msg = "\n"
" test-tool date relative [time_t]...\n"
@@ -34,7 +35,7 @@ static void show_human_dates(const char **argv)
static void show_dates(const char **argv, const char *format)
{
- struct date_mode mode;
+ struct date_mode mode = DATE_MODE_INIT;
parse_date_format(format, &mode);
for (; *argv; argv++) {
@@ -53,6 +54,8 @@ static void show_dates(const char **argv, const char *format)
printf("%s -> %s\n", *argv, show_date(t, tz, &mode));
}
+
+ date_mode_release(&mode);
}
static void parse_dates(const char **argv)
diff --git a/t/helper/test-progress.c b/t/helper/test-progress.c
index 5d05cbe789..6cc9735b60 100644
--- a/t/helper/test-progress.c
+++ b/t/helper/test-progress.c
@@ -3,6 +3,9 @@
*
* Reads instructions from standard input, one instruction per line:
*
+ * "start <total>[ <title>]" - Call start_progress(title, total),
+ * Uses the default title of "Working hard"
+ * if the " <title>" is omitted.
* "progress <items>" - Call display_progress() with the given item count
* as parameter.
* "throughput <bytes> <millis> - Call display_throughput() with the given
@@ -10,6 +13,7 @@
* specify the time elapsed since the
* start_progress() call.
* "update" - Set the 'progress_update' flag.
+ * "stop" - Call stop_progress().
*
* See 't0500-progress-display.sh' for examples.
*/
@@ -19,34 +23,50 @@
#include "parse-options.h"
#include "progress.h"
#include "strbuf.h"
+#include "string-list.h"
int cmd__progress(int argc, const char **argv)
{
- int total = 0;
- const char *title;
+ const char *const default_title = "Working hard";
+ struct string_list titles = STRING_LIST_INIT_DUP;
struct strbuf line = STRBUF_INIT;
- struct progress *progress;
+ struct progress *progress = NULL;
const char *usage[] = {
- "test-tool progress [--total=<n>] <progress-title>",
+ "test-tool progress <stdin",
NULL
};
struct option options[] = {
- OPT_INTEGER(0, "total", &total, "total number of items"),
OPT_END(),
};
argc = parse_options(argc, argv, NULL, options, usage, 0);
- if (argc != 1)
- die("need a title for the progress output");
- title = argv[0];
+ if (argc)
+ usage_with_options(usage, options);
progress_testing = 1;
- progress = start_progress(title, total);
while (strbuf_getline(&line, stdin) != EOF) {
char *end;
- if (skip_prefix(line.buf, "progress ", (const char **) &end)) {
+ if (skip_prefix(line.buf, "start ", (const char **) &end)) {
+ uint64_t total = strtoull(end, &end, 10);
+ const char *title;
+
+ /*
+ * We can't use "end + 1" as an argument to
+ * start_progress(), it doesn't xstrdup() its
+ * "title" argument. We need to hold onto a
+ * valid "char *" for it until the end.
+ */
+ if (!*end)
+ title = default_title;
+ else if (*end == ' ')
+ title = string_list_insert(&titles, end + 1)->string;
+ else
+ die("invalid input: '%s'\n", line.buf);
+
+ progress = start_progress(title, total);
+ } else if (skip_prefix(line.buf, "progress ", (const char **) &end)) {
uint64_t item_count = strtoull(end, &end, 10);
if (*end != '\0')
die("invalid input: '%s'\n", line.buf);
@@ -63,12 +83,16 @@ int cmd__progress(int argc, const char **argv)
die("invalid input: '%s'\n", line.buf);
progress_test_ns = test_ms * 1000 * 1000;
display_throughput(progress, byte_count);
- } else if (!strcmp(line.buf, "update"))
+ } else if (!strcmp(line.buf, "update")) {
progress_test_force_update();
- else
+ } else if (!strcmp(line.buf, "stop")) {
+ stop_progress(&progress);
+ } else {
die("invalid input: '%s'\n", line.buf);
+ }
}
- stop_progress(&progress);
+ strbuf_release(&line);
+ string_list_clear(&titles, 0);
return 0;
}
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index 3235ab4d53..d479303efa 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -6,7 +6,8 @@ TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check_config () {
- if test -d "$1" && test -f "$1/config" && test -d "$1/refs"
+ if test_path_is_dir "$1" &&
+ test_path_is_file "$1/config" && test_path_is_dir "$1/refs"
then
: happy
else
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 794186961e..2490162071 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='test date parsing and printing'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# arbitrary reference time: 2009-08-30 19:20:00
diff --git a/t/t0500-progress-display.sh b/t/t0500-progress-display.sh
index 22058b503a..1eb3a8306b 100755
--- a/t/t0500-progress-display.sh
+++ b/t/t0500-progress-display.sh
@@ -2,6 +2,7 @@
test_description='progress display'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
show_cr () {
@@ -17,6 +18,7 @@ test_expect_success 'simple progress display' '
EOF
cat >in <<-\EOF &&
+ start 0
update
progress 1
update
@@ -25,8 +27,9 @@ test_expect_success 'simple progress display' '
progress 4
update
progress 5
+ stop
EOF
- test-tool progress "Working hard" <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -41,11 +44,13 @@ test_expect_success 'progress display with total' '
EOF
cat >in <<-\EOF &&
+ start 3
progress 1
progress 2
progress 3
+ stop
EOF
- test-tool progress --total=3 "Working hard" <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -62,14 +67,14 @@ Working hard.......2.........3.........4.........5.........6:
EOF
cat >in <<-\EOF &&
+ start 100000 Working hard.......2.........3.........4.........5.........6
progress 100
progress 1000
progress 10000
progress 100000
+ stop
EOF
- test-tool progress --total=100000 \
- "Working hard.......2.........3.........4.........5.........6" \
- <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -88,16 +93,16 @@ Working hard.......2.........3.........4.........5.........6:
EOF
cat >in <<-\EOF &&
+ start 100000 Working hard.......2.........3.........4.........5.........6
update
progress 1
update
progress 2
progress 10000
progress 100000
+ stop
EOF
- test-tool progress --total=100000 \
- "Working hard.......2.........3.........4.........5.........6" \
- <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -116,14 +121,14 @@ Working hard.......2.........3.........4.........5.........6:
EOF
cat >in <<-\EOF &&
+ start 100000 Working hard.......2.........3.........4.........5.........6
progress 25000
progress 50000
progress 75000
progress 100000
+ stop
EOF
- test-tool progress --total=100000 \
- "Working hard.......2.........3.........4.........5.........6" \
- <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -140,14 +145,14 @@ Working hard.......2.........3.........4.........5.........6.........7.........:
EOF
cat >in <<-\EOF &&
+ start 100000 Working hard.......2.........3.........4.........5.........6.........7.........
progress 25000
progress 50000
progress 75000
progress 100000
+ stop
EOF
- test-tool progress --total=100000 \
- "Working hard.......2.........3.........4.........5.........6.........7........." \
- <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -164,12 +169,14 @@ test_expect_success 'progress shortens - crazy caller' '
EOF
cat >in <<-\EOF &&
+ start 1000
progress 100
progress 200
progress 1
progress 1000
+ stop
EOF
- test-tool progress --total=1000 "Working hard" <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -185,6 +192,7 @@ test_expect_success 'progress display with throughput' '
EOF
cat >in <<-\EOF &&
+ start 0
throughput 102400 1000
update
progress 10
@@ -197,8 +205,9 @@ test_expect_success 'progress display with throughput' '
throughput 409600 4000
update
progress 40
+ stop
EOF
- test-tool progress "Working hard" <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -214,6 +223,7 @@ test_expect_success 'progress display with throughput and total' '
EOF
cat >in <<-\EOF &&
+ start 40
throughput 102400 1000
progress 10
throughput 204800 2000
@@ -222,8 +232,9 @@ test_expect_success 'progress display with throughput and total' '
progress 30
throughput 409600 4000
progress 40
+ stop
EOF
- test-tool progress --total=40 "Working hard" <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -239,6 +250,7 @@ test_expect_success 'cover up after throughput shortens' '
EOF
cat >in <<-\EOF &&
+ start 0
throughput 409600 1000
update
progress 1
@@ -251,8 +263,9 @@ test_expect_success 'cover up after throughput shortens' '
throughput 1638400 4000
update
progress 4
+ stop
EOF
- test-tool progress "Working hard" <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -267,6 +280,7 @@ test_expect_success 'cover up after throughput shortens a lot' '
EOF
cat >in <<-\EOF &&
+ start 0
throughput 1 1000
update
progress 1
@@ -276,8 +290,9 @@ test_expect_success 'cover up after throughput shortens a lot' '
throughput 3145728 3000
update
progress 3
+ stop
EOF
- test-tool progress "Working hard" <in 2>stderr &&
+ test-tool progress <in 2>stderr &&
show_cr <stderr >out &&
test_cmp expect out
@@ -285,6 +300,7 @@ test_expect_success 'cover up after throughput shortens a lot' '
test_expect_success 'progress generates traces' '
cat >in <<-\EOF &&
+ start 40
throughput 102400 1000
update
progress 10
@@ -297,10 +313,11 @@ test_expect_success 'progress generates traces' '
throughput 409600 4000
update
progress 40
+ stop
EOF
- GIT_TRACE2_EVENT="$(pwd)/trace.event" test-tool progress --total=40 \
- "Working hard" <in 2>stderr &&
+ GIT_TRACE2_EVENT="$(pwd)/trace.event" test-tool progress \
+ <in 2>stderr &&
# t0212/parse_events.perl intentionally omits regions and data.
test_region progress "Working hard" trace.event &&
@@ -308,4 +325,54 @@ test_expect_success 'progress generates traces' '
grep "\"key\":\"total_bytes\",\"value\":\"409600\"" trace.event
'
+test_expect_success 'progress generates traces: stop / start' '
+ cat >in <<-\EOF &&
+ start 0
+ stop
+ EOF
+
+ GIT_TRACE2_EVENT="$PWD/trace-startstop.event" test-tool progress \
+ <in 2>stderr &&
+ test_region progress "Working hard" trace-startstop.event
+'
+
+test_expect_success 'progress generates traces: start without stop' '
+ cat >in <<-\EOF &&
+ start 0
+ EOF
+
+ GIT_TRACE2_EVENT="$PWD/trace-start.event" \
+ LSAN_OPTIONS=detect_leaks=0 \
+ test-tool progress \
+ <in 2>stderr &&
+ grep region_enter.*progress trace-start.event &&
+ ! grep region_leave.*progress trace-start.event
+'
+
+test_expect_success 'progress generates traces: stop without start' '
+ cat >in <<-\EOF &&
+ stop
+ EOF
+
+ GIT_TRACE2_EVENT="$PWD/trace-stop.event" test-tool progress \
+ <in 2>stderr &&
+ ! grep region_enter.*progress trace-stop.event &&
+ ! grep region_leave.*progress trace-stop.event
+'
+
+test_expect_success 'progress generates traces: start with active progress bar (no stops)' '
+ cat >in <<-\EOF &&
+ start 0 One
+ start 0 Two
+ EOF
+
+ GIT_TRACE2_EVENT="$PWD/trace-2start.event" \
+ LSAN_OPTIONS=detect_leaks=0 \
+ test-tool progress \
+ <in 2>stderr &&
+ grep region_enter.*progress.*One trace-2start.event &&
+ grep region_enter.*progress.*Two trace-2start.event &&
+ ! grep region_leave trace-2start.event
+'
+
test_done
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index 3592d12442..9a90031018 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -126,7 +126,7 @@ test_expect_success 'switching to cone mode with non-cone mode patterns' '
cd bad-patterns &&
git sparse-checkout init &&
git sparse-checkout add dir &&
- git config core.sparseCheckoutCone true &&
+ git config --worktree core.sparseCheckoutCone true &&
test_must_fail git sparse-checkout add dir 2>err &&
grep "existing sparse-checkout patterns do not use cone mode" err
)
@@ -155,9 +155,9 @@ test_expect_success 'interaction with clone --no-checkout (unborn index)' '
'
test_expect_success 'set enables config' '
- git init empty-config &&
+ git init worktree-config &&
(
- cd empty-config &&
+ cd worktree-config &&
test_commit test file &&
test_path_is_missing .git/config.worktree &&
git sparse-checkout set nothing &&
@@ -210,6 +210,21 @@ test_expect_success 'add to sparse-checkout' '
check_files repo "a folder1 folder2"
'
+test_expect_success 'worktree: add copies sparse-checkout patterns' '
+ cat repo/.git/info/sparse-checkout >old &&
+ test_when_finished cp old repo/.git/info/sparse-checkout &&
+ test_when_finished git -C repo worktree remove ../worktree &&
+ git -C repo sparse-checkout set --no-cone "/*" &&
+ git -C repo worktree add --quiet ../worktree 2>err &&
+ test_must_be_empty err &&
+ new="$(git -C worktree rev-parse --git-path info/sparse-checkout)" &&
+ test_path_is_file "$new" &&
+ test_cmp repo/.git/info/sparse-checkout "$new" &&
+ git -C worktree sparse-checkout set --cone &&
+ test_cmp_config -C worktree true core.sparseCheckoutCone &&
+ test_must_fail git -C repo core.sparseCheckoutCone
+'
+
test_expect_success 'cone mode: match patterns' '
git -C repo config --worktree core.sparseCheckoutCone true &&
rm -rf repo/a repo/folder1 repo/folder2 &&
@@ -261,7 +276,7 @@ test_expect_success 'sparse-index enabled and disabled' '
test_cmp expect actual &&
git -C repo config --list >config &&
- ! grep index.sparse config
+ test_cmp_config -C repo false index.sparse
'
test_expect_success 'cone mode: init and set' '
@@ -495,6 +510,37 @@ test_expect_failure 'sparse-checkout reapply' '
git -C tweak sparse-checkout disable
'
+test_expect_success 'reapply can handle config options' '
+ git -C repo sparse-checkout init --cone --no-sparse-index &&
+ git -C repo config --worktree --list >actual &&
+ cat >expect <<-\EOF &&
+ core.sparsecheckout=true
+ core.sparsecheckoutcone=true
+ index.sparse=false
+ EOF
+ test_cmp expect actual &&
+
+ git -C repo sparse-checkout reapply --no-cone --no-sparse-index &&
+ git -C repo config --worktree --list >actual &&
+ cat >expect <<-\EOF &&
+ core.sparsecheckout=true
+ core.sparsecheckoutcone=false
+ index.sparse=false
+ EOF
+ test_cmp expect actual &&
+
+ git -C repo sparse-checkout reapply --cone --sparse-index &&
+ git -C repo config --worktree --list >actual &&
+ cat >expect <<-\EOF &&
+ core.sparsecheckout=true
+ core.sparsecheckoutcone=true
+ index.sparse=true
+ EOF
+ test_cmp expect actual &&
+
+ git -C repo sparse-checkout disable
+'
+
test_expect_success 'cone mode: set with core.ignoreCase=true' '
rm repo/.git/info/sparse-checkout &&
git -C repo sparse-checkout init --cone &&
@@ -524,17 +570,17 @@ test_expect_success 'interaction with submodules' '
'
test_expect_success 'different sparse-checkouts with worktrees' '
+ git -C repo sparse-checkout set --cone deep folder1 &&
git -C repo worktree add --detach ../worktree &&
- check_files worktree "a deep folder1 folder2" &&
- git -C worktree sparse-checkout init --cone &&
- git -C repo sparse-checkout set folder1 &&
- git -C worktree sparse-checkout set deep/deeper1 &&
- check_files repo a folder1 &&
- check_files worktree a deep
+ check_files worktree "a deep folder1" &&
+ git -C repo sparse-checkout set --cone folder1 &&
+ git -C worktree sparse-checkout set --cone deep/deeper1 &&
+ check_files repo "a folder1" &&
+ check_files worktree "a deep"
'
test_expect_success 'set using filename keeps file on-disk' '
- git -C repo sparse-checkout set a deep &&
+ git -C repo sparse-checkout set --skip-checks a deep &&
cat >expect <<-\EOF &&
/*
!/*/
@@ -645,7 +691,7 @@ test_expect_success BSLASHPSPEC 'pattern-checks: escaped characters' '
git -C escaped reset --hard $COMMIT &&
check_files escaped "a deep folder1 folder2 zbad\\dir zdoes*exist" zglob[!a]? &&
git -C escaped sparse-checkout init --cone &&
- git -C escaped sparse-checkout set zbad\\dir/bogus "zdoes*not*exist" "zdoes*exist" "zglob[!a]?" &&
+ git -C escaped sparse-checkout set --skip-checks zbad\\dir/bogus "zdoes*not*exist" "zdoes*exist" "zglob[!a]?" &&
cat >expect <<-\EOF &&
/*
!/*/
@@ -770,4 +816,59 @@ test_expect_success 'malformed cone-mode patterns' '
grep "warning: disabling cone pattern matching" err
'
+test_expect_success 'set from subdir pays attention to prefix' '
+ git -C repo sparse-checkout disable &&
+ git -C repo/deep sparse-checkout set --cone deeper2 ../folder1 &&
+
+ git -C repo sparse-checkout list >actual &&
+
+ cat >expect <<-\EOF &&
+ deep/deeper2
+ folder1
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'add from subdir pays attention to prefix' '
+ git -C repo sparse-checkout set --cone deep/deeper2 &&
+ git -C repo/deep sparse-checkout add deeper1/deepest ../folder1 &&
+
+ git -C repo sparse-checkout list >actual &&
+
+ cat >expect <<-\EOF &&
+ deep/deeper1/deepest
+ deep/deeper2
+ folder1
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'set from subdir in non-cone mode throws an error' '
+ git -C repo sparse-checkout disable &&
+ test_must_fail git -C repo/deep sparse-checkout set --no-cone deeper2 ../folder1 2>error &&
+
+ grep "run from the toplevel directory in non-cone mode" error
+'
+
+test_expect_success 'set from subdir in non-cone mode throws an error' '
+ git -C repo sparse-checkout set --no-cone deep/deeper2 &&
+ test_must_fail git -C repo/deep sparse-checkout add deeper1/deepest ../folder1 2>error &&
+
+ grep "run from the toplevel directory in non-cone mode" error
+'
+
+test_expect_success 'by default, cone mode will error out when passed files' '
+ git -C repo sparse-checkout reapply --cone &&
+ test_must_fail git -C repo sparse-checkout add .gitignore 2>error &&
+
+ grep ".gitignore.*is not a directory" error
+'
+
+test_expect_success 'by default, non-cone mode will warn on individual files' '
+ git -C repo sparse-checkout reapply --no-cone &&
+ git -C repo sparse-checkout add .gitignore 2>warning &&
+
+ grep "pass a leading slash before paths.*if you want a single file" warning
+'
+
test_done
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index d7ddf7612d..68f69bb543 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -341,7 +341,7 @@ test_expect_success 'stale dirs do not cause d/f conflicts (reflogs off)' '
# Each line is 114 characters, so we need 75 to still have a few before the
# last 8K. The 89-character padding on the final entry lines up our
# newline exactly.
-test_expect_success SHA1 'parsing reverse reflogs at BUFSIZ boundaries' '
+test_expect_success REFFILES,SHA1 'parsing reverse reflogs at BUFSIZ boundaries' '
git checkout -b reflogskip &&
zf=$(test_oid zero_2) &&
ident="abc <xyz> 0000000001 +0000" &&
@@ -418,7 +418,8 @@ test_expect_success 'expire with multiple worktrees' '
test_commit -C link-wt foobar &&
test_tick &&
git reflog expire --verbose --all --expire=$test_tick &&
- test_must_be_empty .git/worktrees/link-wt/logs/HEAD
+ test-tool ref-store worktree:link-wt for-each-reflog-ent HEAD >actual &&
+ test_must_be_empty actual
)
'
diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh
index b0119bf8bc..98cefe3b70 100755
--- a/t/t1512-rev-parse-disambiguation.sh
+++ b/t/t1512-rev-parse-disambiguation.sh
@@ -25,6 +25,87 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+test_cmp_failed_rev_parse () {
+ dir=$1
+ rev=$2
+
+ cat >expect &&
+ test_must_fail git -C "$dir" rev-parse "$rev" 2>actual.raw &&
+ sed "s/\($rev\)[0-9a-f]*/\1.../" <actual.raw >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'ambiguous blob output' '
+ git init --bare blob.prefix &&
+ (
+ cd blob.prefix &&
+
+ # Both start with "dead..", under both SHA-1 and SHA-256
+ echo brocdnra | git hash-object -w --stdin &&
+ echo brigddsv | git hash-object -w --stdin &&
+
+ # Both start with "beef.."
+ echo 1agllotbh | git hash-object -w --stdin &&
+ echo 1bbfctrkc | git hash-object -w --stdin
+ ) &&
+
+ test_must_fail git -C blob.prefix rev-parse dead &&
+ test_cmp_failed_rev_parse blob.prefix beef <<-\EOF
+ error: short object ID beef... is ambiguous
+ hint: The candidates are:
+ hint: beef... blob
+ hint: beef... blob
+ fatal: ambiguous argument '\''beef...'\'': unknown revision or path not in the working tree.
+ Use '\''--'\'' to separate paths from revisions, like this:
+ '\''git <command> [<revision>...] -- [<file>...]'\''
+ EOF
+'
+
+test_expect_success 'ambiguous loose bad object parsed as OBJ_BAD' '
+ git init --bare blob.bad &&
+ (
+ cd blob.bad &&
+
+ # Both have the prefix "bad0"
+ echo xyzfaowcoh | git hash-object -t bad -w --stdin --literally &&
+ echo xyzhjpyvwl | git hash-object -t bad -w --stdin --literally
+ ) &&
+
+ test_cmp_failed_rev_parse blob.bad bad0 <<-\EOF
+ error: short object ID bad0... is ambiguous
+ fatal: invalid object type
+ EOF
+'
+
+test_expect_success POSIXPERM 'ambigous zlib corrupt loose blob' '
+ git init --bare blob.corrupt &&
+ (
+ cd blob.corrupt &&
+
+ # Both have the prefix "cafe"
+ echo bnkxmdwz | git hash-object -w --stdin &&
+ oid=$(echo bmwsjxzi | git hash-object -w --stdin) &&
+
+ oidf=objects/$(test_oid_to_path "$oid") &&
+ chmod 755 $oidf &&
+ echo broken >$oidf
+ ) &&
+
+ test_cmp_failed_rev_parse blob.corrupt cafe <<-\EOF
+ error: short object ID cafe... is ambiguous
+ error: inflate: data stream error (incorrect header check)
+ error: unable to unpack cafe... header
+ error: inflate: data stream error (incorrect header check)
+ error: unable to unpack cafe... header
+ hint: The candidates are:
+ hint: cafe... [bad object]
+ hint: cafe... blob
+ fatal: ambiguous argument '\''cafe...'\'': unknown revision or path not in the working tree.
+ Use '\''--'\'' to separate paths from revisions, like this:
+ '\''git <command> [<revision>...] -- [<file>...]'\''
+ EOF
+'
+
if ! test_have_prereq SHA1
then
skip_all='not using SHA-1 for objects'
diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh
index 37ad79470f..43139af08f 100755
--- a/t/t2400-worktree-add.sh
+++ b/t/t2400-worktree-add.sh
@@ -165,8 +165,62 @@ test_expect_success '"add" default branch of a bare repo' '
(
git clone --bare . bare2 &&
cd bare2 &&
- git worktree add ../there3 main
- )
+ git worktree add ../there3 main &&
+ cd ../there3 &&
+ # Simple check that a Git command does not
+ # immediately fail with the current setup
+ git status
+ ) &&
+ cat >expect <<-EOF &&
+ init.t
+ EOF
+ ls there3 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '"add" to bare repo with worktree config' '
+ (
+ git clone --bare . bare3 &&
+ cd bare3 &&
+ git config extensions.worktreeconfig true &&
+
+ # Add config values that are erroneous to have in
+ # a config.worktree file outside of the main
+ # working tree, to check that Git filters them out
+ # when copying config during "git worktree add".
+ git config --worktree core.bare true &&
+ git config --worktree core.worktree "$(pwd)" &&
+
+ # We want to check that bogus.key is copied
+ git config --worktree bogus.key value &&
+ git config --unset core.bare &&
+ git worktree add ../there4 main &&
+ cd ../there4 &&
+
+ # Simple check that a Git command does not
+ # immediately fail with the current setup
+ git status &&
+ git worktree add --detach ../there5 &&
+ cd ../there5 &&
+ git status
+ ) &&
+
+ # the worktree has the arbitrary value copied.
+ test_cmp_config -C there4 value bogus.key &&
+ test_cmp_config -C there5 value bogus.key &&
+
+ # however, core.bare and core.worktree were removed.
+ test_must_fail git -C there4 config core.bare &&
+ test_must_fail git -C there4 config core.worktree &&
+
+ cat >expect <<-EOF &&
+ init.t
+ EOF
+
+ ls there4 >actual &&
+ test_cmp expect actual &&
+ ls there5 >actual &&
+ test_cmp expect actual
'
test_expect_success 'checkout with grafts' '
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index b149e2af44..d5ecee4fcc 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -390,10 +390,11 @@ test_expect_success SYMLINKS 'stash file to symlink' '
rm file &&
ln -s file2 file &&
git stash save "file to symlink" &&
- test -f file &&
+ test_path_is_file_not_symlink file &&
test bar = "$(cat file)" &&
git stash apply &&
- case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
+ test_path_is_symlink file &&
+ test "$(test_readlink file)" = file2
'
test_expect_success SYMLINKS 'stash file to symlink (stage rm)' '
@@ -401,10 +402,11 @@ test_expect_success SYMLINKS 'stash file to symlink (stage rm)' '
git rm file &&
ln -s file2 file &&
git stash save "file to symlink (stage rm)" &&
- test -f file &&
+ test_path_is_file_not_symlink file &&
test bar = "$(cat file)" &&
git stash apply &&
- case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
+ test_path_is_symlink file &&
+ test "$(test_readlink file)" = file2
'
test_expect_success SYMLINKS 'stash file to symlink (full stage)' '
@@ -413,10 +415,11 @@ test_expect_success SYMLINKS 'stash file to symlink (full stage)' '
ln -s file2 file &&
git add file &&
git stash save "file to symlink (full stage)" &&
- test -f file &&
+ test_path_is_file_not_symlink file &&
test bar = "$(cat file)" &&
git stash apply &&
- case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
+ test_path_is_symlink file &&
+ test "$(test_readlink file)" = file2
'
# This test creates a commit with a symlink used for the following tests
@@ -487,7 +490,7 @@ test_expect_failure 'stash directory to file' '
rm -fr dir &&
echo bar >dir &&
git stash save "directory to file" &&
- test -d dir &&
+ test_path_is_dir dir &&
test foo = "$(cat dir/file)" &&
test_must_fail git stash apply &&
test bar = "$(cat dir)" &&
@@ -500,10 +503,10 @@ test_expect_failure 'stash file to directory' '
mkdir file &&
echo foo >file/file &&
git stash save "file to directory" &&
- test -f file &&
+ test_path_is_file file &&
test bar = "$(cat file)" &&
git stash apply &&
- test -f file/file &&
+ test_path_is_file file/file &&
test foo = "$(cat file/file)"
'
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 6caff0ca39..159fae8d01 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -1169,7 +1169,7 @@ test_expect_success 'invalid when passing the --empty option alone' '
test_when_finished "git am --abort || :" &&
git checkout empty-commit^ &&
test_must_fail git am --empty empty-commit.patch 2>err &&
- echo "error: Invalid value for --empty: empty-commit.patch" >expected &&
+ echo "error: invalid value for '\''--empty'\'': '\''empty-commit.patch'\''" >expected &&
test_cmp expected err
'
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index a20d578349..55fac64446 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -462,6 +462,30 @@ test_expect_success !FAIL_PREREQS 'log with various grep.patternType configurati
)
'
+for cmd in show whatchanged reflog format-patch
+do
+ case "$cmd" in
+ format-patch) myarg="HEAD~.." ;;
+ *) myarg= ;;
+ esac
+
+ test_expect_success "$cmd: understands grep.patternType, like 'log'" '
+ git init "pattern-type-$cmd" &&
+ (
+ cd "pattern-type-$cmd" &&
+ test_commit 1 file A &&
+ test_commit "(1|2)" file B 2 &&
+
+ git -c grep.patternType=fixed $cmd --grep="..." $myarg >actual &&
+ test_must_be_empty actual &&
+
+ git -c grep.patternType=basic $cmd --grep="..." $myarg >actual &&
+ test_file_not_empty actual
+ )
+ '
+done
+test_done
+
test_expect_success 'log --author' '
cat >expect <<-\EOF &&
Author: <BOLD;RED>A U<RESET> Thor <author@example.com>
@@ -1684,6 +1708,75 @@ test_expect_success 'log --graph with --name-only' '
test_cmp_graph --name-only tangle..reach
'
+test_expect_success '--no-graph countermands --graph' '
+ git log >expect &&
+ git log --graph --no-graph >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--graph countermands --no-graph' '
+ git log --graph >expect &&
+ git log --no-graph --graph >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--no-graph does not unset --topo-order' '
+ git log --topo-order >expect &&
+ git log --topo-order --no-graph >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--no-graph does not unset --parents' '
+ git log --parents >expect &&
+ git log --parents --no-graph >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--reverse and --graph conflict' '
+ test_must_fail git log --reverse --graph 2>stderr &&
+ test_i18ngrep "cannot be used together" stderr
+'
+
+test_expect_success '--reverse --graph --no-graph works' '
+ git log --reverse >expect &&
+ git log --reverse --graph --no-graph >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--show-linear-break and --graph conflict' '
+ test_must_fail git log --show-linear-break --graph 2>stderr &&
+ test_i18ngrep "cannot be used together" stderr
+'
+
+test_expect_success '--show-linear-break --graph --no-graph works' '
+ git log --show-linear-break >expect &&
+ git log --show-linear-break --graph --no-graph >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--no-walk and --graph conflict' '
+ test_must_fail git log --no-walk --graph 2>stderr &&
+ test_i18ngrep "cannot be used together" stderr
+'
+
+test_expect_success '--no-walk --graph --no-graph works' '
+ git log --no-walk >expect &&
+ git log --no-walk --graph --no-graph >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--walk-reflogs and --graph conflict' '
+ test_must_fail git log --walk-reflogs --graph 2>stderr &&
+ (test_i18ngrep "cannot combine" stderr ||
+ test_i18ngrep "cannot be used together" stderr)
+'
+
+test_expect_success '--walk-reflogs --graph --no-graph works' '
+ git log --walk-reflogs >expect &&
+ git log --walk-reflogs --graph --no-graph >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'dotdot is a parent directory' '
mkdir -p a/b &&
( echo sixth && echo fifth ) >expect &&
diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh
index df524f7b6d..e9045009a1 100755
--- a/t/t5316-pack-delta-depth.sh
+++ b/t/t5316-pack-delta-depth.sh
@@ -64,7 +64,11 @@ test_expect_success 'create series of packs' '
echo $cur &&
echo "$(git rev-parse :file) file"
} | git pack-objects --stdout >tmp &&
- git index-pack --stdin --fix-thin <tmp || return 1
+ GIT_TRACE2_EVENT=$PWD/trace \
+ git index-pack -v --stdin --fix-thin <tmp || return 1 &&
+ grep -c region_enter.*progress trace >enter &&
+ grep -c region_leave.*progress trace >leave &&
+ test_cmp enter leave &&
prev=$cur
done
'
diff --git a/t/t5571-pre-push-hook.sh b/t/t5571-pre-push-hook.sh
index 660f876eec..96d6ecc0af 100755
--- a/t/t5571-pre-push-hook.sh
+++ b/t/t5571-pre-push-hook.sh
@@ -11,7 +11,7 @@ HOOKDIR="$(git rev-parse --git-dir)/hooks"
HOOK="$HOOKDIR/pre-push"
mkdir -p "$HOOKDIR"
write_script "$HOOK" <<EOF
-cat >/dev/null
+cat >actual
exit 0
EOF
@@ -20,10 +20,16 @@ test_expect_success 'setup' '
git init --bare repo1 &&
git remote add parent1 repo1 &&
test_commit one &&
- git push parent1 HEAD:foreign
+ cat >expect <<-EOF &&
+ HEAD $(git rev-parse HEAD) refs/heads/foreign $(test_oid zero)
+ EOF
+
+ test_when_finished "rm actual" &&
+ git push parent1 HEAD:foreign &&
+ test_cmp expect actual
'
write_script "$HOOK" <<EOF
-cat >/dev/null
+cat >actual
exit 1
EOF
@@ -32,11 +38,18 @@ export COMMIT1
test_expect_success 'push with failing hook' '
test_commit two &&
- test_must_fail git push parent1 HEAD
+ cat >expect <<-EOF &&
+ HEAD $(git rev-parse HEAD) refs/heads/main $(test_oid zero)
+ EOF
+
+ test_when_finished "rm actual" &&
+ test_must_fail git push parent1 HEAD &&
+ test_cmp expect actual
'
test_expect_success '--no-verify bypasses hook' '
- git push --no-verify parent1 HEAD
+ git push --no-verify parent1 HEAD &&
+ test_path_is_missing actual
'
COMMIT2="$(git rev-parse HEAD)"
@@ -48,15 +61,15 @@ echo "$2" >>actual
cat >>actual
EOF
-cat >expected <<EOF
-parent1
-repo1
-refs/heads/main $COMMIT2 refs/heads/foreign $COMMIT1
-EOF
-
test_expect_success 'push with hook' '
+ cat >expect <<-EOF &&
+ parent1
+ repo1
+ refs/heads/main $COMMIT2 refs/heads/foreign $COMMIT1
+ EOF
+
git push parent1 main:foreign &&
- diff expected actual
+ test_cmp expect actual
'
test_expect_success 'add a branch' '
@@ -67,49 +80,48 @@ test_expect_success 'add a branch' '
COMMIT3="$(git rev-parse HEAD)"
export COMMIT3
-cat >expected <<EOF
-parent1
-repo1
-refs/heads/other $COMMIT3 refs/heads/foreign $COMMIT2
-EOF
-
test_expect_success 'push to default' '
+ cat >expect <<-EOF &&
+ parent1
+ repo1
+ refs/heads/other $COMMIT3 refs/heads/foreign $COMMIT2
+ EOF
git push &&
- diff expected actual
+ test_cmp expect actual
'
-cat >expected <<EOF
-parent1
-repo1
-refs/tags/one $COMMIT1 refs/tags/tag1 $ZERO_OID
-HEAD~ $COMMIT2 refs/heads/prev $ZERO_OID
-EOF
-
test_expect_success 'push non-branches' '
+ cat >expect <<-EOF &&
+ parent1
+ repo1
+ refs/tags/one $COMMIT1 refs/tags/tag1 $ZERO_OID
+ HEAD~ $COMMIT2 refs/heads/prev $ZERO_OID
+ EOF
+
git push parent1 one:tag1 HEAD~:refs/heads/prev &&
- diff expected actual
+ test_cmp expect actual
'
-cat >expected <<EOF
-parent1
-repo1
-(delete) $ZERO_OID refs/heads/prev $COMMIT2
-EOF
-
test_expect_success 'push delete' '
+ cat >expect <<-EOF &&
+ parent1
+ repo1
+ (delete) $ZERO_OID refs/heads/prev $COMMIT2
+ EOF
+
git push parent1 :prev &&
- diff expected actual
+ test_cmp expect actual
'
-cat >expected <<EOF
-repo1
-repo1
-HEAD $COMMIT3 refs/heads/other $ZERO_OID
-EOF
-
test_expect_success 'push to URL' '
+ cat >expect <<-EOF &&
+ repo1
+ repo1
+ HEAD $COMMIT3 refs/heads/other $ZERO_OID
+ EOF
+
git push repo1 HEAD &&
- diff expected actual
+ test_cmp expect actual
'
test_expect_success 'set up many-ref tests' '
diff --git a/t/t5617-clone-submodules-remote.sh b/t/t5617-clone-submodules-remote.sh
index e2dbb4eaba..ca8f80083a 100755
--- a/t/t5617-clone-submodules-remote.sh
+++ b/t/t5617-clone-submodules-remote.sh
@@ -28,6 +28,13 @@ test_expect_success 'setup' '
)
'
+# bare clone giving "srv.bare" for use as our server.
+test_expect_success 'setup bare clone for server' '
+ git clone --bare "file://$(pwd)/." srv.bare &&
+ git -C srv.bare config --local uploadpack.allowfilter 1 &&
+ git -C srv.bare config --local uploadpack.allowanysha1inwant 1
+'
+
test_expect_success 'clone with --no-remote-submodules' '
test_when_finished "rm -rf super_clone" &&
git clone --recurse-submodules --no-remote-submodules "file://$pwd/." super_clone &&
@@ -65,4 +72,38 @@ test_expect_success 'clone with --single-branch' '
)
'
+# do basic partial clone from "srv.bare"
+# confirm partial clone was registered in the local config for super and sub.
+test_expect_success 'clone with --filter' '
+ git clone --recurse-submodules \
+ --filter blob:none --also-filter-submodules \
+ "file://$pwd/srv.bare" super_clone &&
+ test_cmp_config -C super_clone true remote.origin.promisor &&
+ test_cmp_config -C super_clone blob:none remote.origin.partialclonefilter &&
+ test_cmp_config -C super_clone/sub true remote.origin.promisor &&
+ test_cmp_config -C super_clone/sub blob:none remote.origin.partialclonefilter
+'
+
+# check that clone.filterSubmodules works (--also-filter-submodules can be
+# omitted)
+test_expect_success 'filters applied with clone.filterSubmodules' '
+ test_config_global clone.filterSubmodules true &&
+ git clone --recurse-submodules --filter blob:none \
+ "file://$pwd/srv.bare" super_clone2 &&
+ test_cmp_config -C super_clone2 true remote.origin.promisor &&
+ test_cmp_config -C super_clone2 blob:none remote.origin.partialclonefilter &&
+ test_cmp_config -C super_clone2/sub true remote.origin.promisor &&
+ test_cmp_config -C super_clone2/sub blob:none remote.origin.partialclonefilter
+'
+
+test_expect_success '--no-also-filter-submodules overrides clone.filterSubmodules=true' '
+ test_config_global clone.filterSubmodules true &&
+ git clone --recurse-submodules --filter blob:none \
+ --no-also-filter-submodules \
+ "file://$pwd/srv.bare" super_clone3 &&
+ test_cmp_config -C super_clone3 true remote.origin.promisor &&
+ test_cmp_config -C super_clone3 blob:none remote.origin.partialclonefilter &&
+ test_cmp_config -C super_clone3/sub false --default false remote.origin.promisor
+'
+
test_done
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 1be85d064e..5382e5d216 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -278,6 +278,51 @@ test_expect_success '"git bisect run" with more complex "git bisect start"' '
git bisect reset
'
+test_expect_success 'bisect run accepts exit code 126 as bad' '
+ test_when_finished "git bisect reset" &&
+ write_script test_script.sh <<-\EOF &&
+ ! grep Another hello || exit 126 >/dev/null
+ EOF
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect run ./test_script.sh >my_bisect_log.txt &&
+ grep "$HASH3 is the first bad commit" my_bisect_log.txt
+'
+
+test_expect_success POSIXPERM 'bisect run fails with non-executable test script' '
+ test_when_finished "git bisect reset" &&
+ >not-executable.sh &&
+ chmod -x not-executable.sh &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ test_must_fail git bisect run ./not-executable.sh >my_bisect_log.txt &&
+ ! grep "is the first bad commit" my_bisect_log.txt
+'
+
+test_expect_success 'bisect run accepts exit code 127 as bad' '
+ test_when_finished "git bisect reset" &&
+ write_script test_script.sh <<-\EOF &&
+ ! grep Another hello || exit 127 >/dev/null
+ EOF
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect run ./test_script.sh >my_bisect_log.txt &&
+ grep "$HASH3 is the first bad commit" my_bisect_log.txt
+'
+
+test_expect_success 'bisect run fails with missing test script' '
+ test_when_finished "git bisect reset" &&
+ rm -f does-not-exist.sh &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ test_must_fail git bisect run ./does-not-exist.sh >my_bisect_log.txt &&
+ ! grep "is the first bad commit" my_bisect_log.txt
+'
+
# $HASH1 is good, $HASH5 is bad, we skip $HASH3
# but $HASH4 is good,
# so we should find $HASH5 as the first bad commit
diff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh
index 91964653a0..5fcaa0b4f2 100755
--- a/t/t7500-commit-template-squash-signoff.sh
+++ b/t/t7500-commit-template-squash-signoff.sh
@@ -442,7 +442,7 @@ test_expect_success '--fixup=reword: give error with pathsec' '
'
test_expect_success '--fixup=reword: -F give error message' '
- echo "fatal: Only one of -c/-C/-F/--fixup can be used." >expect &&
+ echo "fatal: options '\''-F'\'' and '\''--fixup'\'' cannot be used together" >expect &&
test_must_fail git commit --fixup=reword:HEAD~ -F msg 2>actual &&
test_cmp expect actual
'
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
index 424c31c328..6935601171 100755
--- a/t/t7810-grep.sh
+++ b/t/t7810-grep.sh
@@ -98,6 +98,37 @@ test_expect_success 'grep should not segfault with a bad input' '
test_invalid_grep_expression --and -e A
+test_pattern_type () {
+ H=$1 &&
+ HC=$2 &&
+ L=$3 &&
+ type=$4 &&
+ shift 4 &&
+
+ expected_str= &&
+ case "$type" in
+ BRE)
+ expected_str="${HC}ab:a+bc"
+ ;;
+ ERE)
+ expected_str="${HC}ab:abc"
+ ;;
+ FIX)
+ expected_str="${HC}ab:a+b*c"
+ ;;
+ *)
+ BUG "unknown pattern type '$type'"
+ ;;
+ esac &&
+ config_str="$@" &&
+
+ test_expect_success "grep $L with '$config_str' interpreted as $type" '
+ echo $expected_str >expected &&
+ git $config_str grep "a+b*c" $H ab >actual &&
+ test_cmp expected actual
+ '
+}
+
for H in HEAD ''
do
case "$H" in
@@ -393,35 +424,13 @@ do
git grep --no-recursive -n -e vvv $H -- t . >actual &&
test_cmp expected actual
'
- test_expect_success "grep $L with grep.extendedRegexp=false" '
- echo "${HC}ab:a+bc" >expected &&
- git -c grep.extendedRegexp=false grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
- test_expect_success "grep $L with grep.extendedRegexp=true" '
- echo "${HC}ab:abc" >expected &&
- git -c grep.extendedRegexp=true grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
- test_expect_success "grep $L with grep.patterntype=basic" '
- echo "${HC}ab:a+bc" >expected &&
- git -c grep.patterntype=basic grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
-
- test_expect_success "grep $L with grep.patterntype=extended" '
- echo "${HC}ab:abc" >expected &&
- git -c grep.patterntype=extended grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
-
- test_expect_success "grep $L with grep.patterntype=fixed" '
- echo "${HC}ab:a+b*c" >expected &&
- git -c grep.patterntype=fixed grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
+ test_pattern_type "$H" "$HC" "$L" BRE -c grep.extendedRegexp=false
+ test_pattern_type "$H" "$HC" "$L" ERE -c grep.extendedRegexp=true
+ test_pattern_type "$H" "$HC" "$L" BRE -c grep.patternType=basic
+ test_pattern_type "$H" "$HC" "$L" ERE -c grep.patternType=extended
+ test_pattern_type "$H" "$HC" "$L" FIX -c grep.patternType=fixed
test_expect_success PCRE "grep $L with grep.patterntype=perl" '
echo "${HC}ab:a+b*c" >expected &&
@@ -433,59 +442,76 @@ do
test_must_fail git -c grep.patterntype=perl grep "foo.*bar"
'
- test_expect_success "grep $L with grep.patternType=default and grep.extendedRegexp=true" '
- echo "${HC}ab:abc" >expected &&
- git \
- -c grep.patternType=default \
- -c grep.extendedRegexp=true \
- grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
-
- test_expect_success "grep $L with grep.extendedRegexp=true and grep.patternType=default" '
- echo "${HC}ab:abc" >expected &&
- git \
- -c grep.extendedRegexp=true \
- -c grep.patternType=default \
- grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
-
- test_expect_success "grep $L with grep.patternType=extended and grep.extendedRegexp=false" '
- echo "${HC}ab:abc" >expected &&
- git \
- -c grep.patternType=extended \
- -c grep.extendedRegexp=false \
- grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
-
- test_expect_success "grep $L with grep.patternType=basic and grep.extendedRegexp=true" '
- echo "${HC}ab:a+bc" >expected &&
- git \
- -c grep.patternType=basic \
- -c grep.extendedRegexp=true \
- grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
-
- test_expect_success "grep $L with grep.extendedRegexp=false and grep.patternType=extended" '
- echo "${HC}ab:abc" >expected &&
- git \
- -c grep.extendedRegexp=false \
- -c grep.patternType=extended \
- grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
-
- test_expect_success "grep $L with grep.extendedRegexp=true and grep.patternType=basic" '
- echo "${HC}ab:a+bc" >expected &&
- git \
- -c grep.extendedRegexp=true \
- -c grep.patternType=basic \
- grep "a+b*c" $H ab >actual &&
- test_cmp expected actual
- '
+ test_pattern_type "$H" "$HC" "$L" ERE \
+ -c grep.patternType=default \
+ -c grep.extendedRegexp=true
+ test_pattern_type "$H" "$HC" "$L" ERE \
+ -c grep.extendedRegexp=true \
+ -c grep.patternType=default
+ test_pattern_type "$H" "$HC" "$L" ERE \
+ -c grep.patternType=extended \
+ -c grep.extendedRegexp=false
+ test_pattern_type "$H" "$HC" "$L" BRE \
+ -c grep.patternType=basic \
+ -c grep.extendedRegexp=true
+ test_pattern_type "$H" "$HC" "$L" ERE \
+ -c grep.extendedRegexp=false \
+ -c grep.patternType=extended
+ test_pattern_type "$H" "$HC" "$L" BRE \
+ -c grep.extendedRegexp=true \
+ -c grep.patternType=basic
+
+ # grep.extendedRegexp is last-one-wins
+ test_pattern_type "$H" "$HC" "$L" BRE \
+ -c grep.extendedRegexp=true \
+ -c grep.extendedRegexp=false
+
+ # grep.patternType=basic pays no attention to grep.extendedRegexp
+ test_pattern_type "$H" "$HC" "$L" BRE \
+ -c grep.extendedRegexp=true \
+ -c grep.patternType=basic \
+ -c grep.extendedRegexp=false
+
+ # grep.patternType=extended pays no attention to grep.extendedRegexp
+ test_pattern_type "$H" "$HC" "$L" ERE \
+ -c grep.extendedRegexp=true \
+ -c grep.patternType=extended \
+ -c grep.extendedRegexp=false
+
+ # grep.extendedRegexp is used with a last-one-wins grep.patternType=default
+ test_pattern_type "$H" "$HC" "$L" ERE \
+ -c grep.patternType=fixed \
+ -c grep.extendedRegexp=true \
+ -c grep.patternType=default
+
+ # grep.extendedRegexp is used with earlier grep.patternType=default
+ test_pattern_type "$H" "$HC" "$L" ERE \
+ -c grep.extendedRegexp=false \
+ -c grep.patternType=default \
+ -c grep.extendedRegexp=true
+
+ # grep.extendedRegexp is used with a last-one-loses grep.patternType=default
+ test_pattern_type "$H" "$HC" "$L" ERE \
+ -c grep.extendedRegexp=false \
+ -c grep.extendedRegexp=true \
+ -c grep.patternType=default
+
+ # grep.extendedRegexp and grep.patternType are both last-one-wins independently
+ test_pattern_type "$H" "$HC" "$L" BRE \
+ -c grep.patternType=default \
+ -c grep.extendedRegexp=true \
+ -c grep.patternType=basic
+
+ # grep.patternType=extended and grep.patternType=default
+ test_pattern_type "$H" "$HC" "$L" BRE \
+ -c grep.patternType=extended \
+ -c grep.patternType=default
+
+ # grep.patternType=[extended -> default -> fixed] (BRE)" '
+ test_pattern_type "$H" "$HC" "$L" FIX \
+ -c grep.patternType=extended \
+ -c grep.patternType=default \
+ -c grep.patternType=fixed
test_expect_success "grep --count $L" '
echo ${HC}ab:3 >expected &&
diff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh
index 058e5d0c96..a4476dc492 100755
--- a/t/t7814-grep-recurse-submodules.sh
+++ b/t/t7814-grep-recurse-submodules.sh
@@ -544,4 +544,45 @@ test_expect_failure 'grep saves textconv cache in the appropriate repository' '
test_path_is_file "$sub_textconv_cache"
'
+test_expect_success 'grep partially-cloned submodule' '
+ # Set up clean superproject and submodule for partial cloning.
+ git init super &&
+ git init super/sub &&
+ (
+ cd super &&
+ test_commit --no-tag "Add file in superproject" \
+ super-file "Some content for super-file" &&
+ test_commit -C sub --no-tag "Add file in submodule" \
+ sub-file "Some content for sub-file" &&
+ git submodule add ./sub &&
+ git commit -m "Add other as submodule sub" &&
+ test_tick &&
+ test_commit -C sub --no-tag --append "Update file in submodule" \
+ sub-file "Some more content for sub-file" &&
+ git add sub &&
+ git commit -m "Update submodule" &&
+ test_tick &&
+ git config --local uploadpack.allowfilter 1 &&
+ git config --local uploadpack.allowanysha1inwant 1 &&
+ git -C sub config --local uploadpack.allowfilter 1 &&
+ git -C sub config --local uploadpack.allowanysha1inwant 1
+ ) &&
+ # Clone the superproject & submodule, then make sure we can lazy-fetch submodule objects.
+ git clone --filter=blob:none --also-filter-submodules \
+ --recurse-submodules "file://$(pwd)/super" partial &&
+ (
+ cd partial &&
+ cat >expect <<-\EOF &&
+ HEAD^:sub/sub-file:Some content for sub-file
+ HEAD^:super-file:Some content for super-file
+ EOF
+
+ GIT_TRACE2_EVENT="$(pwd)/trace2.log" git grep -e content \
+ --recurse-submodules HEAD^ >actual &&
+ test_cmp expect actual &&
+ # Verify that we actually fetched data from the promisor remote:
+ grep \"category\":\"promisor\",\"key\":\"fetch_count\",\"value\":\"1\" trace2.log
+ )
+'
+
test_done
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 85385d2ede..0f439c99d6 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -856,6 +856,16 @@ test_path_is_file () {
fi
}
+test_path_is_file_not_symlink () {
+ test "$#" -ne 1 && BUG "1 param"
+ test_path_is_file "$1" &&
+ if test -h "$1"
+ then
+ echo "$1 shouldn't be a symbolic link"
+ false
+ fi
+}
+
test_path_is_dir () {
test "$#" -ne 1 && BUG "1 param"
if ! test -d "$1"
@@ -865,6 +875,16 @@ test_path_is_dir () {
fi
}
+test_path_is_dir_not_symlink () {
+ test "$#" -ne 1 && BUG "1 param"
+ test_path_is_dir "$1" &&
+ if test -h "$1"
+ then
+ echo "$1 shouldn't be a symbolic link"
+ false
+ fi
+}
+
test_path_exists () {
test "$#" -ne 1 && BUG "1 param"
if ! test -e "$1"
@@ -874,6 +894,15 @@ test_path_exists () {
fi
}
+test_path_is_symlink () {
+ test "$#" -ne 1 && BUG "1 param"
+ if ! test -h "$1"
+ then
+ echo "Symbolic link $1 doesn't exist"
+ false
+ fi
+}
+
# Check if the directory exists and is empty as expected, barf otherwise.
test_dir_is_empty () {
test "$#" -ne 1 && BUG "1 param"
diff --git a/worktree.c b/worktree.c
index e8f6f6ae6f..90fc085f76 100644
--- a/worktree.c
+++ b/worktree.c
@@ -5,6 +5,7 @@
#include "worktree.h"
#include "dir.h"
#include "wt-status.h"
+#include "config.h"
void free_worktrees(struct worktree **worktrees)
{
@@ -821,3 +822,75 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,
*wtpath = path;
return 0;
}
+
+static int move_config_setting(const char *key, const char *value,
+ const char *from_file, const char *to_file)
+{
+ if (git_config_set_in_file_gently(to_file, key, value))
+ return error(_("unable to set %s in '%s'"), key, to_file);
+ if (git_config_set_in_file_gently(from_file, key, NULL))
+ return error(_("unable to unset %s in '%s'"), key, from_file);
+ return 0;
+}
+
+int init_worktree_config(struct repository *r)
+{
+ int res = 0;
+ int bare = 0;
+ struct config_set cs = { { 0 } };
+ const char *core_worktree;
+ char *common_config_file;
+ char *main_worktree_file;
+
+ /*
+ * If the extension is already enabled, then we can skip the
+ * upgrade process.
+ */
+ if (repository_format_worktree_config)
+ return 0;
+ if ((res = git_config_set_gently("extensions.worktreeConfig", "true")))
+ return error(_("failed to set extensions.worktreeConfig setting"));
+
+ common_config_file = xstrfmt("%s/config", r->commondir);
+ main_worktree_file = xstrfmt("%s/config.worktree", r->commondir);
+
+ git_configset_init(&cs);
+ git_configset_add_file(&cs, common_config_file);
+
+ /*
+ * If core.bare is true in the common config file, then we need to
+ * move it to the main worktree's config file or it will break all
+ * worktrees. If it is false, then leave it in place because it
+ * _could_ be negating a global core.bare=true.
+ */
+ if (!git_configset_get_bool(&cs, "core.bare", &bare) && bare) {
+ if ((res = move_config_setting("core.bare", "true",
+ common_config_file,
+ main_worktree_file)))
+ goto cleanup;
+ }
+ /*
+ * If core.worktree is set, then the main worktree is located
+ * somewhere different than the parent of the common Git dir.
+ * Relocate that value to avoid breaking all worktrees with this
+ * upgrade to worktree config.
+ */
+ if (!git_configset_get_value(&cs, "core.worktree", &core_worktree)) {
+ if ((res = move_config_setting("core.worktree", core_worktree,
+ common_config_file,
+ main_worktree_file)))
+ goto cleanup;
+ }
+
+ /*
+ * Ensure that we use worktree config for the remaining lifetime
+ * of the current process.
+ */
+ repository_format_worktree_config = 1;
+
+cleanup:
+ git_configset_clear(&cs);
+ free(common_config_file);
+ free(main_worktree_file);
+ return res;
+}
diff --git a/worktree.h b/worktree.h
index 9e06fcbdf3..e9e839926b 100644
--- a/worktree.h
+++ b/worktree.h
@@ -183,4 +183,25 @@ void strbuf_worktree_ref(const struct worktree *wt,
struct strbuf *sb,
const char *refname);
+/**
+ * Enable worktree config for the first time. This will make the following
+ * adjustments:
+ *
+ * 1. Add extensions.worktreeConfig=true in the common config file.
+ *
+ * 2. If the common config file has a core.worktree value, then that value
+ * is moved to the main worktree's config.worktree file.
+ *
+ * 3. If the common config file has a core.bare enabled, then that value
+ * is moved to the main worktree's config.worktree file.
+ *
+ * If extensions.worktreeConfig is already true, then this method
+ * terminates early without any of the above steps. The existing config
+ * arrangement is assumed to be intentional.
+ *
+ * Returns 0 on success. Reports an error message and returns non-zero
+ * if any of these steps fail.
+ */
+int init_worktree_config(struct repository *r);
+
#endif