Merge branch 'jh/builtin-fsmonitor-part2' into jh/builtin-fsmonitor-part3
* jh/builtin-fsmonitor-part2: (150 commits) t7527: test status with untracked-cache and fsmonitor--daemon fsmonitor: force update index after large responses fsmonitor--daemon: use a cookie file to sync with file system fsmonitor--daemon: periodically truncate list of modified files t/perf/p7519: add fsmonitor--daemon test cases t/perf/p7519: speed up test on Windows t/perf/p7519: fix coding style t/helper/test-chmtime: skip directories on Windows t/perf: avoid copying builtin fsmonitor files into test repo t7527: create test for fsmonitor--daemon t/helper/fsmonitor-client: create IPC client to talk to FSMonitor Daemon help: include fsmonitor--daemon feature flag in version info fsmonitor--daemon: implement handle_client callback compat/fsmonitor/fsm-listen-darwin: implement FSEvent listener on MacOS compat/fsmonitor/fsm-listen-darwin: add MacOS header files for FSEvent compat/fsmonitor/fsm-listen-win32: implement FSMonitor backend on Windows fsmonitor--daemon: create token-based changed path cache fsmonitor--daemon: define token-ids fsmonitor--daemon: add pathname classification fsmonitor--daemon: implement 'start' command ...maint
commit
852e2c84f8
|
@ -72,6 +72,7 @@
|
|||
/git-format-patch
|
||||
/git-fsck
|
||||
/git-fsck-objects
|
||||
/git-fsmonitor--daemon
|
||||
/git-gc
|
||||
/git-get-tar-commit-id
|
||||
/git-grep
|
||||
|
|
5
.mailmap
5
.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>
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -25,6 +29,17 @@ UI, Workflows & Features
|
|||
* "git log --remerge-diff" shows the difference from mechanical merge
|
||||
result and the result that is actually recorded in a merge commit.
|
||||
|
||||
* "git log" and friends learned an option --exclude-first-parent-only
|
||||
to propagate UNINTERESTING bit down only along the first-parent
|
||||
chain, just like --first-parent option shows commits that lack the
|
||||
UNINTERESTING bit only along the first-parent chain.
|
||||
|
||||
* The command line completion script (in contrib/) learned to
|
||||
complete all Git subcommands, including the ones that are normally
|
||||
hidden, when GIT_COMPLETION_SHOW_ALL_COMMANDS is used.
|
||||
|
||||
* "git branch" learned the "--recurse-submodules" option.
|
||||
|
||||
|
||||
Performance, Internal Implementation, Development Support etc.
|
||||
|
||||
|
@ -47,6 +62,17 @@ Performance, Internal Implementation, Development Support etc.
|
|||
all. Start the process of renaming it to "--annotate-stdin".
|
||||
(merge a2585719b3 jc/name-rev-stdin later to maint).
|
||||
|
||||
* "git update-index", "git checkout-index", and "git clean" are
|
||||
taught to work better with the sparse checkout feature.
|
||||
|
||||
* Use an internal call to reset_head() helper function instead of
|
||||
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
|
||||
-----------------
|
||||
|
@ -140,6 +166,72 @@ Fixes since v2.35
|
|||
* "git diff --diff-filter=aR" is now parsed correctly.
|
||||
(merge 75408ca949 js/diff-filter-negation-fix later to maint).
|
||||
|
||||
* When "git subtree" wants to create a merge, it used "git merge" and
|
||||
let it be affected by end-user's "merge.ff" configuration, which
|
||||
has been corrected.
|
||||
(merge 9158a3564a tk/subtree-merge-not-ff-only later to maint).
|
||||
|
||||
* Unlike "git apply", "git patch-id" did not handle patches with
|
||||
hunks that has only 1 line in either preimage or postimage, which
|
||||
has been corrected.
|
||||
(merge 757e75c81e jz/patch-id-hunk-header-parsing-fix later to maint).
|
||||
|
||||
* "receive-pack" checks if it will do any ref updates (various
|
||||
conditions could reject a push) before received objects are taken
|
||||
out of the temporary directory used for quarantine purposes, so
|
||||
that a push that is known-to-fail will not leave crufts that a
|
||||
future "gc" needs to clean up.
|
||||
(merge 5407764069 cb/clear-quarantine-early-on-all-ref-update-errors later to maint).
|
||||
|
||||
* Because a deletion of ref would need to remove it from both the
|
||||
loose ref store and the packed ref store, a delete-ref operation
|
||||
that logically removes one ref may end up invoking ref-transaction
|
||||
hook twice, which has been corrected.
|
||||
(merge 2ed1b64ebd ps/avoid-unnecessary-hook-invocation-with-packed-refs later to maint).
|
||||
|
||||
* When there is no object to write .bitmap file for, "git
|
||||
multi-pack-index" triggered an error, instead of just skipping,
|
||||
which has been corrected.
|
||||
(merge eb57277ba3 tb/midx-no-bitmap-for-no-objects later to maint).
|
||||
|
||||
* "git cmd -h" outside a repository should error out cleanly for many
|
||||
commands, but instead it hit a BUG(), which has been corrected.
|
||||
(merge 87ad07d735 js/short-help-outside-repo-fix later to maint).
|
||||
|
||||
* "working tree" and "per-worktree ref" were in glossary, but
|
||||
"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).
|
||||
|
@ -157,3 +249,10 @@ Fixes since v2.35
|
|||
(merge 45d0212a71 ll/doc-mktree-typofix later to maint).
|
||||
(merge e9b272e4c1 js/no-more-legacy-stash later to maint).
|
||||
(merge 6798b08e84 ab/do-not-hide-failures-in-git-dot-pm later to maint).
|
||||
(merge 9325285df4 po/doc-check-ignore-markup-fix later to maint).
|
||||
(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).
|
||||
|
|
|
@ -116,6 +116,9 @@ advice.*::
|
|||
submoduleAlternateErrorStrategyDie::
|
||||
Advice shown when a submodule.alternateErrorStrategy option
|
||||
configured to "die" causes a fatal error.
|
||||
submodulesNotUpdated::
|
||||
Advice shown when a user runs a submodule command that fails
|
||||
because `git submodule update --init` was not run.
|
||||
addIgnoredFile::
|
||||
Advice shown if a user attempts to add an ignored file to
|
||||
the index.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -62,22 +62,54 @@ core.protectNTFS::
|
|||
Defaults to `true` on Windows, and `false` elsewhere.
|
||||
|
||||
core.fsmonitor::
|
||||
If set, the value of this variable is used as a command which
|
||||
will identify all files that may have changed since the
|
||||
requested date/time. This information is used to speed up git by
|
||||
avoiding unnecessary processing of files that have not changed.
|
||||
See the "fsmonitor-watchman" section of linkgit:githooks[5].
|
||||
If set to true, enable the built-in file system monitor
|
||||
daemon for this working directory (linkgit:git-fsmonitor--daemon[1]).
|
||||
+
|
||||
Like hook-based file system monitors, the built-in file system monitor
|
||||
can speed up Git commands that need to refresh the Git index
|
||||
(e.g. `git status`) in a working directory with many files. The
|
||||
built-in monitor eliminates the need to install and maintain an
|
||||
external third-party tool.
|
||||
+
|
||||
The built-in file system monitor is currently available only on a
|
||||
limited set of supported platforms. Currently, this includes Windows
|
||||
and MacOS.
|
||||
+
|
||||
Otherwise, this variable contains the pathname of the "fsmonitor"
|
||||
hook command.
|
||||
+
|
||||
This hook command is used to identify all files that may have changed
|
||||
since the requested date/time. This information is used to speed up
|
||||
git by avoiding unnecessary scanning of files that have not changed.
|
||||
+
|
||||
See the "fsmonitor-watchman" section of linkgit:githooks[5].
|
||||
+
|
||||
Note that if you concurrently use multiple versions of Git, such
|
||||
as one version on the command line and another version in an IDE
|
||||
tool, that the definition of `core.fsmonitor` was extended to
|
||||
allow boolean values in addition to hook pathnames. Git versions
|
||||
2.35.1 and prior will not understand the boolean values and will
|
||||
consider the "true" or "false" values as hook pathnames to be
|
||||
invoked. Git versions 2.26 thru 2.35.1 default to hook protocol
|
||||
V2 and will fall back to no fsmonitor (full scan). Git versions
|
||||
prior to 2.26 default to hook protocol V1 and will silently
|
||||
assume there were no changes to report (no scan), so status
|
||||
commands may report incomplete results. For this reason, it is
|
||||
best to upgrade all of your Git versions before using the built-in
|
||||
file system monitor.
|
||||
|
||||
core.fsmonitorHookVersion::
|
||||
Sets the version of hook that is to be used when calling fsmonitor.
|
||||
There are currently versions 1 and 2. When this is not set,
|
||||
version 2 will be tried first and if it fails then version 1
|
||||
will be tried. Version 1 uses a timestamp as input to determine
|
||||
which files have changes since that time but some monitors
|
||||
like watchman have race conditions when used with a timestamp.
|
||||
Version 2 uses an opaque string so that the monitor can return
|
||||
something that can be used to determine what files have changed
|
||||
without race conditions.
|
||||
Sets the protocol version to be used when invoking the
|
||||
"fsmonitor" hook.
|
||||
+
|
||||
There are currently versions 1 and 2. When this is not set,
|
||||
version 2 will be tried first and if it fails then version 1
|
||||
will be tried. Version 1 uses a timestamp as input to determine
|
||||
which files have changes since that time but some monitors
|
||||
like Watchman have race conditions when used with a timestamp.
|
||||
Version 2 uses an opaque string so that the monitor can return
|
||||
something that can be used to determine what files have changed
|
||||
without race conditions.
|
||||
|
||||
core.trustctime::
|
||||
If false, the ctime differences between the index and the
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -59,18 +59,33 @@ submodule.active::
|
|||
|
||||
submodule.recurse::
|
||||
A boolean indicating if commands should enable the `--recurse-submodules`
|
||||
option by default.
|
||||
Applies to all commands that support this option
|
||||
(`checkout`, `fetch`, `grep`, `pull`, `push`, `read-tree`, `reset`,
|
||||
`restore` and `switch`) except `clone` and `ls-files`.
|
||||
option by default. Defaults to false.
|
||||
+
|
||||
When set to true, it can be deactivated via the
|
||||
`--no-recurse-submodules` option. Note that some Git commands
|
||||
lacking this option may call some of the above commands affected by
|
||||
`submodule.recurse`; for instance `git remote update` will call
|
||||
`git fetch` but does not have a `--no-recurse-submodules` option.
|
||||
For these commands a workaround is to temporarily change the
|
||||
configuration value by using `git -c submodule.recurse=0`.
|
||||
+
|
||||
The following list shows the commands that accept
|
||||
`--recurse-submodules` and whether they are supported by this
|
||||
setting.
|
||||
|
||||
* `checkout`, `fetch`, `grep`, `pull`, `push`, `read-tree`,
|
||||
`reset`, `restore` and `switch` are always supported.
|
||||
* `clone` and `ls-files` are not supported.
|
||||
* `branch` is supported only if `submodule.propagateBranches` is
|
||||
enabled
|
||||
|
||||
submodule.propagateBranches::
|
||||
[EXPERIMENTAL] A boolean that enables branching support when
|
||||
using `--recurse-submodules` or `submodule.recurse=true`.
|
||||
Enabling this will allow certain commands to accept
|
||||
`--recurse-submodules` and certain commands that already accept
|
||||
`--recurse-submodules` will now consider branches.
|
||||
Defaults to false.
|
||||
When set to true, it can be deactivated via the
|
||||
`--no-recurse-submodules` option. Note that some Git commands
|
||||
lacking this option may call some of the above commands affected by
|
||||
`submodule.recurse`; for instance `git remote update` will call
|
||||
`git fetch` but does not have a `--no-recurse-submodules` option.
|
||||
For these commands a workaround is to temporarily change the
|
||||
configuration value by using `git -c submodule.recurse=0`.
|
||||
|
||||
submodule.fetchJobs::
|
||||
Specifies how many submodules are fetched/cloned at the same time.
|
||||
|
|
|
@ -16,7 +16,8 @@ SYNOPSIS
|
|||
[--points-at <object>] [--format=<format>]
|
||||
[(-r | --remotes) | (-a | --all)]
|
||||
[--list] [<pattern>...]
|
||||
'git branch' [--track[=(direct|inherit)] | --no-track] [-f] <branchname> [<start-point>]
|
||||
'git branch' [--track[=(direct|inherit)] | --no-track] [-f]
|
||||
[--recurse-submodules] <branchname> [<start-point>]
|
||||
'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
|
||||
'git branch' --unset-upstream [<branchname>]
|
||||
'git branch' (-m | -M) [<oldbranch>] <newbranch>
|
||||
|
@ -235,6 +236,22 @@ how the `branch.<name>.remote` and `branch.<name>.merge` options are used.
|
|||
Do not set up "upstream" configuration, even if the
|
||||
branch.autoSetupMerge configuration variable is set.
|
||||
|
||||
--recurse-submodules::
|
||||
THIS OPTION IS EXPERIMENTAL! Causes the current command to
|
||||
recurse into submodules if `submodule.propagateBranches` is
|
||||
enabled. See `submodule.propagateBranches` in
|
||||
linkgit:git-config[1]. Currently, only branch creation is
|
||||
supported.
|
||||
+
|
||||
When used in branch creation, a new branch <branchname> will be created
|
||||
in the superproject and all of the submodules in the superproject's
|
||||
<start-point>. In submodules, the branch will point to the submodule
|
||||
commit in the superproject's <start-point> but the branch's tracking
|
||||
information will be set up based on the submodule's branches and remotes
|
||||
e.g. `git branch --recurse-submodules topic origin/main` will create the
|
||||
submodule branch "topic" that points to the submodule commit in the
|
||||
superproject's "origin/main", but tracks the submodule's "origin/main".
|
||||
|
||||
--set-upstream::
|
||||
As this option had confusing syntax, it is no longer supported.
|
||||
Please use `--track` or `--set-upstream-to` instead.
|
||||
|
|
|
@ -33,7 +33,7 @@ OPTIONS
|
|||
Instead of printing the paths that are excluded, for each path
|
||||
that matches an exclude pattern, print the exclude pattern
|
||||
together with the path. (Matching an exclude pattern usually
|
||||
means the path is excluded, but if the pattern begins with '!'
|
||||
means the path is excluded, but if the pattern begins with "`!`"
|
||||
then it is a negated pattern and matching it means the path is
|
||||
NOT excluded.)
|
||||
+
|
||||
|
@ -77,7 +77,7 @@ If `--verbose` is specified, the output is a series of lines of the form:
|
|||
<pathname> is the path of a file being queried, <pattern> is the
|
||||
matching pattern, <source> is the pattern's source file, and <linenum>
|
||||
is the line number of the pattern within that source. If the pattern
|
||||
contained a `!` prefix or `/` suffix, it will be preserved in the
|
||||
contained a "`!`" prefix or "`/`" suffix, it will be preserved in the
|
||||
output. <source> will be an absolute path when referring to the file
|
||||
configured by `core.excludesFile`, or relative to the repository root
|
||||
when referring to `.git/info/exclude` or a per-directory exclude file.
|
||||
|
|
|
@ -12,6 +12,7 @@ SYNOPSIS
|
|||
'git checkout-index' [-u] [-q] [-a] [-f] [-n] [--prefix=<string>]
|
||||
[--stage=<number>|all]
|
||||
[--temp]
|
||||
[--ignore-skip-worktree-bits]
|
||||
[-z] [--stdin]
|
||||
[--] [<file>...]
|
||||
|
||||
|
@ -37,8 +38,9 @@ OPTIONS
|
|||
|
||||
-a::
|
||||
--all::
|
||||
checks out all files in the index. Cannot be used
|
||||
together with explicit filenames.
|
||||
checks out all files in the index except for those with the
|
||||
skip-worktree bit set (see `--ignore-skip-worktree-bits`).
|
||||
Cannot be used together with explicit filenames.
|
||||
|
||||
-n::
|
||||
--no-create::
|
||||
|
@ -59,6 +61,10 @@ OPTIONS
|
|||
write the content to temporary files. The temporary name
|
||||
associations will be written to stdout.
|
||||
|
||||
--ignore-skip-worktree-bits::
|
||||
Check out all files, including those with the skip-worktree bit
|
||||
set.
|
||||
|
||||
--stdin::
|
||||
Instead of taking list of paths from the command line,
|
||||
read list of paths from the standard input. Paths are
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>::
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
git-fsmonitor--daemon(1)
|
||||
========================
|
||||
|
||||
NAME
|
||||
----
|
||||
git-fsmonitor--daemon - A Built-in File System Monitor
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git fsmonitor--daemon' start
|
||||
'git fsmonitor--daemon' run
|
||||
'git fsmonitor--daemon' stop
|
||||
'git fsmonitor--daemon' status
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
A daemon to watch the working directory for file and directory
|
||||
changes using platform-specific file system notification facilities.
|
||||
|
||||
This daemon communicates directly with commands like `git status`
|
||||
using the link:technical/api-simple-ipc.html[simple IPC] interface
|
||||
instead of the slower linkgit:githooks[5] interface.
|
||||
|
||||
This daemon is built into Git so that no third-party tools are
|
||||
required.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
start::
|
||||
Starts a daemon in the background.
|
||||
|
||||
run::
|
||||
Runs a daemon in the foreground.
|
||||
|
||||
stop::
|
||||
Stops the daemon running in the current working
|
||||
directory, if present.
|
||||
|
||||
status::
|
||||
Exits with zero status if a daemon is watching the
|
||||
current working directory.
|
||||
|
||||
REMARKS
|
||||
-------
|
||||
|
||||
This daemon is a long running process used to watch a single working
|
||||
directory and maintain a list of the recently changed files and
|
||||
directories. Performance of commands such as `git status` can be
|
||||
increased if they just ask for a summary of changes to the working
|
||||
directory and can avoid scanning the disk.
|
||||
|
||||
When `core.fsmonitor` is set to `true` (see linkgit:git-config[1])
|
||||
commands, such as `git status`, will ask the daemon for changes and
|
||||
automatically start it (if necessary).
|
||||
|
||||
For more information see the "File System Monitor" section in
|
||||
linkgit:git-update-index[1].
|
||||
|
||||
CAVEATS
|
||||
-------
|
||||
|
||||
The fsmonitor daemon does not currently know about submodules and does
|
||||
not know to filter out file system events that happen within a
|
||||
submodule. If fsmonitor daemon is watching a super repo and a file is
|
||||
modified within the working directory of a submodule, it will report
|
||||
the change (as happening against the super repo). However, the client
|
||||
will properly ignore these extra events, so performance may be affected
|
||||
but it will not cause an incorrect result.
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the linkgit:git[1] suite
|
|
@ -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.
|
||||
+
|
||||
|
|
|
@ -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>::
|
||||
|
|
|
@ -498,7 +498,9 @@ FILE SYSTEM MONITOR
|
|||
This feature is intended to speed up git operations for repos that have
|
||||
large working directories.
|
||||
|
||||
It enables git to work together with a file system monitor (see the
|
||||
It enables git to work together with a file system monitor (see
|
||||
linkgit:git-fsmonitor--daemon[1]
|
||||
and the
|
||||
"fsmonitor-watchman" section of linkgit:githooks[5]) that can
|
||||
inform it as to what files have been modified. This enables git to avoid
|
||||
having to lstat() every file to find modified files.
|
||||
|
@ -509,8 +511,8 @@ looking for new files.
|
|||
|
||||
If you want to enable (or disable) this feature, it is easier to use
|
||||
the `core.fsmonitor` configuration variable (see
|
||||
linkgit:git-config[1]) than using the `--fsmonitor` option to
|
||||
`git update-index` in each repository, especially if you want to do so
|
||||
linkgit:git-config[1]) than using the `--fsmonitor` option to `git
|
||||
update-index` in each repository, especially if you want to do so
|
||||
across all repositories you use, because you can set the configuration
|
||||
variable in your `$HOME/.gitconfig` just once and have it affect all
|
||||
repositories you touch.
|
||||
|
|
|
@ -286,8 +286,8 @@ 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.
|
||||
present in the common config file and `extensions.worktreeConfig` is
|
||||
disabled, then they will be applied to the main working tree only.
|
||||
|
||||
In order to have configuration specific to working trees, you can turn
|
||||
on the `worktreeConfig` extension, e.g.:
|
||||
|
@ -307,11 +307,16 @@ 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:
|
||||
|
||||
- `core.worktree` and `core.bare` should never be shared
|
||||
- `core.worktree` should never be shared.
|
||||
|
||||
- `core.bare` should not be shared if the value is `core.bare=true`.
|
||||
|
||||
- `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
|
||||
|
|
|
@ -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"::
|
||||
|
||||
|
|
|
@ -312,7 +312,7 @@ Pathspecs are used on the command line of "git ls-files", "git
|
|||
ls-tree", "git add", "git grep", "git diff", "git checkout",
|
||||
and many other commands to
|
||||
limit the scope of operations to some subset of the tree or
|
||||
worktree. See the documentation of each command for whether
|
||||
working tree. See the documentation of each command for whether
|
||||
paths are relative to the current directory or toplevel. The
|
||||
pathspec syntax is as follows:
|
||||
+
|
||||
|
@ -446,7 +446,7 @@ exclude;;
|
|||
interface than the <<def_plumbing,plumbing>>.
|
||||
|
||||
[[def_per_worktree_ref]]per-worktree ref::
|
||||
Refs that are per-<<def_working_tree,worktree>>, rather than
|
||||
Refs that are per-<<def_worktree,worktree>>, rather than
|
||||
global. This is presently only <<def_HEAD,HEAD>> and any refs
|
||||
that start with `refs/bisect/`, but might later include other
|
||||
unusual refs.
|
||||
|
@ -669,3 +669,12 @@ The most notable example is `HEAD`.
|
|||
The tree of actual checked out files. The working tree normally
|
||||
contains the contents of the <<def_HEAD,HEAD>> commit's tree,
|
||||
plus any local changes that you have made but not yet committed.
|
||||
|
||||
[[def_worktree]]worktree::
|
||||
A repository can have zero (i.e. bare repository) or one or
|
||||
more worktrees attached to it. One "worktree" consists of a
|
||||
"working tree" and repository metadata, most of which are
|
||||
shared among other worktrees of a single repository, and
|
||||
some of which are maintained separately per worktree
|
||||
(e.g. the index, HEAD and pseudorefs like MERGE_HEAD,
|
||||
per-worktree refs and per-worktree configuration file).
|
||||
|
|
|
@ -122,19 +122,27 @@ again. Equivalent forms are `--min-parents=0` (any commit has 0 or more
|
|||
parents) and `--max-parents=-1` (negative numbers denote no upper limit).
|
||||
|
||||
--first-parent::
|
||||
Follow only the first parent commit upon seeing a merge
|
||||
commit. This option can give a better overview when
|
||||
viewing the evolution of a particular topic branch,
|
||||
because merges into a topic branch tend to be only about
|
||||
adjusting to updated upstream from time to time, and
|
||||
this option allows you to ignore the individual commits
|
||||
brought in to your history by such a merge.
|
||||
When finding commits to include, follow only the first
|
||||
parent commit upon seeing a merge commit. This option
|
||||
can give a better overview when viewing the evolution of
|
||||
a particular topic branch, because merges into a topic
|
||||
branch tend to be only about adjusting to updated upstream
|
||||
from time to time, and this option allows you to ignore
|
||||
the individual commits brought in to your history by such
|
||||
a merge.
|
||||
ifdef::git-log[]
|
||||
+
|
||||
This option also changes default diff format for merge commits
|
||||
to `first-parent`, see `--diff-merges=first-parent` for details.
|
||||
endif::git-log[]
|
||||
|
||||
--exclude-first-parent-only::
|
||||
When finding commits to exclude (with a '{caret}'), follow only
|
||||
the first parent commit upon seeing a merge commit.
|
||||
This can be used to find the set of changes in a topic branch
|
||||
from the point where it diverged from the remote branch, given
|
||||
that arbitrary merges can be valid topic branch changes.
|
||||
|
||||
--not::
|
||||
Reverses the meaning of the '{caret}' prefix (or lack thereof)
|
||||
for all following revision specifiers, up to the next `--not`.
|
||||
|
|
17
Makefile
17
Makefile
|
@ -470,6 +470,11 @@ all::
|
|||
# directory, and the JSON compilation database 'compile_commands.json' will be
|
||||
# created at the root of the repository.
|
||||
#
|
||||
# If your platform supports a built-in fsmonitor backend, set
|
||||
# FSMONITOR_DAEMON_BACKEND to the "<name>" of the corresponding
|
||||
# `compat/fsmonitor/fsm-listen-<name>.c` that implements the
|
||||
# `fsm_listen__*()` routines.
|
||||
#
|
||||
# Define DEVELOPER to enable more compiler warnings. Compiler version
|
||||
# and family are auto detected, but could be overridden by defining
|
||||
# COMPILER_FEATURES (see config.mak.dev). You can still set
|
||||
|
@ -711,6 +716,7 @@ TEST_BUILTINS_OBJS += test-dump-split-index.o
|
|||
TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
|
||||
TEST_BUILTINS_OBJS += test-example-decorate.o
|
||||
TEST_BUILTINS_OBJS += test-fast-rebase.o
|
||||
TEST_BUILTINS_OBJS += test-fsmonitor-client.o
|
||||
TEST_BUILTINS_OBJS += test-genrandom.o
|
||||
TEST_BUILTINS_OBJS += test-genzeros.o
|
||||
TEST_BUILTINS_OBJS += test-getcwd.o
|
||||
|
@ -907,6 +913,8 @@ LIB_OBJS += fetch-pack.o
|
|||
LIB_OBJS += fmt-merge-msg.o
|
||||
LIB_OBJS += fsck.o
|
||||
LIB_OBJS += fsmonitor.o
|
||||
LIB_OBJS += fsmonitor-ipc.o
|
||||
LIB_OBJS += fsmonitor-settings.o
|
||||
LIB_OBJS += gettext.o
|
||||
LIB_OBJS += gpg-interface.o
|
||||
LIB_OBJS += graph.o
|
||||
|
@ -1112,6 +1120,7 @@ BUILTIN_OBJS += builtin/fmt-merge-msg.o
|
|||
BUILTIN_OBJS += builtin/for-each-ref.o
|
||||
BUILTIN_OBJS += builtin/for-each-repo.o
|
||||
BUILTIN_OBJS += builtin/fsck.o
|
||||
BUILTIN_OBJS += builtin/fsmonitor--daemon.o
|
||||
BUILTIN_OBJS += builtin/gc.o
|
||||
BUILTIN_OBJS += builtin/get-tar-commit-id.o
|
||||
BUILTIN_OBJS += builtin/grep.o
|
||||
|
@ -1965,6 +1974,11 @@ ifdef NEED_ACCESS_ROOT_HANDLER
|
|||
COMPAT_OBJS += compat/access.o
|
||||
endif
|
||||
|
||||
ifdef FSMONITOR_DAEMON_BACKEND
|
||||
COMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND
|
||||
COMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o
|
||||
endif
|
||||
|
||||
ifeq ($(TCLTK_PATH),)
|
||||
NO_TCLTK = NoThanks
|
||||
endif
|
||||
|
@ -2884,6 +2898,9 @@ GIT-BUILD-OPTIONS: FORCE
|
|||
@echo DC_SHA1=\''$(subst ','\'',$(subst ','\'',$(DC_SHA1)))'\' >>$@+
|
||||
@echo SANITIZE_LEAK=\''$(subst ','\'',$(subst ','\'',$(SANITIZE_LEAK)))'\' >>$@+
|
||||
@echo X=\'$(X)\' >>$@+
|
||||
ifdef FSMONITOR_DAEMON_BACKEND
|
||||
@echo FSMONITOR_DAEMON_BACKEND=\''$(subst ','\'',$(subst ','\'',$(FSMONITOR_DAEMON_BACKEND)))'\' >>$@+
|
||||
endif
|
||||
ifdef TEST_OUTPUT_DIRECTORY
|
||||
@echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+
|
||||
endif
|
||||
|
|
|
@ -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)
|
||||
|
|
1
advice.c
1
advice.c
|
@ -70,6 +70,7 @@ static struct {
|
|||
[ADVICE_STATUS_HINTS] = { "statusHints", 1 },
|
||||
[ADVICE_STATUS_U_OPTION] = { "statusUoption", 1 },
|
||||
[ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE] = { "submoduleAlternateErrorStrategyDie", 1 },
|
||||
[ADVICE_SUBMODULES_NOT_UPDATED] = { "submodulesNotUpdated", 1 },
|
||||
[ADVICE_UPDATE_SPARSE_PATH] = { "updateSparsePath", 1 },
|
||||
[ADVICE_WAITING_FOR_EDITOR] = { "waitingForEditor", 1 },
|
||||
};
|
||||
|
|
1
advice.h
1
advice.h
|
@ -44,6 +44,7 @@ struct string_list;
|
|||
ADVICE_STATUS_HINTS,
|
||||
ADVICE_STATUS_U_OPTION,
|
||||
ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE,
|
||||
ADVICE_SUBMODULES_NOT_UPDATED,
|
||||
ADVICE_UPDATE_SPARSE_PATH,
|
||||
ADVICE_WAITING_FOR_EDITOR,
|
||||
ADVICE_SKIPPED_CHERRY_PICKS,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
5
blame.c
5
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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2615,7 +2612,7 @@ void assign_blame(struct blame_scoreboard *sb, int opt)
|
|||
else {
|
||||
commit->object.flags |= UNINTERESTING;
|
||||
if (commit->object.parsed)
|
||||
mark_parents_uninteresting(commit);
|
||||
mark_parents_uninteresting(sb->revs, commit);
|
||||
}
|
||||
/* treat root commit as boundary */
|
||||
if (!commit->parents && !sb->show_root)
|
||||
|
|
277
branch.c
277
branch.c
|
@ -8,6 +8,8 @@
|
|||
#include "sequencer.h"
|
||||
#include "commit.h"
|
||||
#include "worktree.h"
|
||||
#include "submodule-config.h"
|
||||
#include "run-command.h"
|
||||
|
||||
struct tracking {
|
||||
struct refspec_item spec;
|
||||
|
@ -218,9 +220,11 @@ static int inherit_tracking(struct tracking *tracking, const char *orig_ref)
|
|||
}
|
||||
|
||||
/*
|
||||
* This is called when new_ref is branched off of orig_ref, and tries
|
||||
* to infer the settings for branch.<new_ref>.{remote,merge} from the
|
||||
* config.
|
||||
* Used internally to set the branch.<new_ref>.{remote,merge} config
|
||||
* settings so that branch 'new_ref' tracks 'orig_ref'. Unlike
|
||||
* dwim_and_setup_tracking(), this does not do DWIM, i.e. "origin/main"
|
||||
* will not be expanded to "refs/remotes/origin/main", so it is not safe
|
||||
* for 'orig_ref' to be raw user input.
|
||||
*/
|
||||
static void setup_tracking(const char *new_ref, const char *orig_ref,
|
||||
enum branch_track track, int quiet)
|
||||
|
@ -235,7 +239,7 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,
|
|||
if (track != BRANCH_TRACK_INHERIT)
|
||||
for_each_remote(find_tracked_branch, &tracking);
|
||||
else if (inherit_tracking(&tracking, orig_ref))
|
||||
return;
|
||||
goto cleanup;
|
||||
|
||||
if (!tracking.matches)
|
||||
switch (track) {
|
||||
|
@ -245,7 +249,7 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,
|
|||
case BRANCH_TRACK_INHERIT:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (tracking.matches > 1)
|
||||
|
@ -258,7 +262,8 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,
|
|||
tracking.remote, tracking.srcs) < 0)
|
||||
exit(-1);
|
||||
|
||||
string_list_clear(tracking.srcs, 0);
|
||||
cleanup:
|
||||
string_list_clear(&tracking_srcs, 0);
|
||||
}
|
||||
|
||||
int read_branch_desc(struct strbuf *buf, const char *branch_name)
|
||||
|
@ -346,31 +351,37 @@ N_("\n"
|
|||
"will track its remote counterpart, you may want to use\n"
|
||||
"\"git push -u\" to set the upstream config as you push.");
|
||||
|
||||
void create_branch(struct repository *r,
|
||||
const char *name, const char *start_name,
|
||||
int force, int clobber_head_ok, int reflog,
|
||||
int quiet, enum branch_track track)
|
||||
/**
|
||||
* DWIMs a user-provided ref to determine the starting point for a
|
||||
* branch and validates it, where:
|
||||
*
|
||||
* - r is the repository to validate the branch for
|
||||
*
|
||||
* - start_name is the ref that we would like to test. This is
|
||||
* expanded with DWIM and assigned to out_real_ref.
|
||||
*
|
||||
* - track is the tracking mode of the new branch. If tracking is
|
||||
* explicitly requested, start_name must be a branch (because
|
||||
* otherwise start_name cannot be tracked)
|
||||
*
|
||||
* - out_oid is an out parameter containing the object_id of start_name
|
||||
*
|
||||
* - out_real_ref is an out parameter containing the full, 'real' form
|
||||
* of start_name e.g. refs/heads/main instead of main
|
||||
*
|
||||
*/
|
||||
static void dwim_branch_start(struct repository *r, const char *start_name,
|
||||
enum branch_track track, char **out_real_ref,
|
||||
struct object_id *out_oid)
|
||||
{
|
||||
struct commit *commit;
|
||||
struct object_id oid;
|
||||
char *real_ref;
|
||||
struct strbuf ref = STRBUF_INIT;
|
||||
int forcing = 0;
|
||||
int dont_change_ref = 0;
|
||||
int explicit_tracking = 0;
|
||||
|
||||
if (track == BRANCH_TRACK_EXPLICIT || track == BRANCH_TRACK_OVERRIDE)
|
||||
explicit_tracking = 1;
|
||||
|
||||
if ((track == BRANCH_TRACK_OVERRIDE || clobber_head_ok)
|
||||
? validate_branchname(name, &ref)
|
||||
: validate_new_branchname(name, &ref, force)) {
|
||||
if (!force)
|
||||
dont_change_ref = 1;
|
||||
else
|
||||
forcing = 1;
|
||||
}
|
||||
|
||||
real_ref = NULL;
|
||||
if (get_oid_mb(start_name, &oid)) {
|
||||
if (explicit_tracking) {
|
||||
|
@ -407,40 +418,218 @@ void create_branch(struct repository *r,
|
|||
|
||||
if ((commit = lookup_commit_reference(r, &oid)) == NULL)
|
||||
die(_("not a valid branch point: '%s'"), start_name);
|
||||
oidcpy(&oid, &commit->object.oid);
|
||||
if (out_real_ref) {
|
||||
*out_real_ref = real_ref;
|
||||
real_ref = NULL;
|
||||
}
|
||||
if (out_oid)
|
||||
oidcpy(out_oid, &commit->object.oid);
|
||||
|
||||
FREE_AND_NULL(real_ref);
|
||||
}
|
||||
|
||||
void create_branch(struct repository *r,
|
||||
const char *name, const char *start_name,
|
||||
int force, int clobber_head_ok, int reflog,
|
||||
int quiet, enum branch_track track, int dry_run)
|
||||
{
|
||||
struct object_id oid;
|
||||
char *real_ref;
|
||||
struct strbuf ref = STRBUF_INIT;
|
||||
int forcing = 0;
|
||||
struct ref_transaction *transaction;
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
char *msg;
|
||||
|
||||
if (track == BRANCH_TRACK_OVERRIDE)
|
||||
BUG("'track' cannot be BRANCH_TRACK_OVERRIDE. Did you mean to call dwim_and_setup_tracking()?");
|
||||
if (clobber_head_ok && !force)
|
||||
BUG("'clobber_head_ok' can only be used with 'force'");
|
||||
|
||||
if (clobber_head_ok ?
|
||||
validate_branchname(name, &ref) :
|
||||
validate_new_branchname(name, &ref, force)) {
|
||||
forcing = 1;
|
||||
}
|
||||
|
||||
dwim_branch_start(r, start_name, track, &real_ref, &oid);
|
||||
if (dry_run)
|
||||
goto cleanup;
|
||||
|
||||
if (reflog)
|
||||
log_all_ref_updates = LOG_REFS_NORMAL;
|
||||
|
||||
if (!dont_change_ref) {
|
||||
struct ref_transaction *transaction;
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
char *msg;
|
||||
|
||||
if (forcing)
|
||||
msg = xstrfmt("branch: Reset to %s", start_name);
|
||||
else
|
||||
msg = xstrfmt("branch: Created from %s", start_name);
|
||||
|
||||
transaction = ref_transaction_begin(&err);
|
||||
if (!transaction ||
|
||||
ref_transaction_update(transaction, ref.buf,
|
||||
&oid, forcing ? NULL : null_oid(),
|
||||
0, msg, &err) ||
|
||||
ref_transaction_commit(transaction, &err))
|
||||
die("%s", err.buf);
|
||||
ref_transaction_free(transaction);
|
||||
strbuf_release(&err);
|
||||
free(msg);
|
||||
}
|
||||
if (forcing)
|
||||
msg = xstrfmt("branch: Reset to %s", start_name);
|
||||
else
|
||||
msg = xstrfmt("branch: Created from %s", start_name);
|
||||
transaction = ref_transaction_begin(&err);
|
||||
if (!transaction ||
|
||||
ref_transaction_update(transaction, ref.buf,
|
||||
&oid, forcing ? NULL : null_oid(),
|
||||
0, msg, &err) ||
|
||||
ref_transaction_commit(transaction, &err))
|
||||
die("%s", err.buf);
|
||||
ref_transaction_free(transaction);
|
||||
strbuf_release(&err);
|
||||
free(msg);
|
||||
|
||||
if (real_ref && track)
|
||||
setup_tracking(ref.buf + 11, real_ref, track, quiet);
|
||||
|
||||
cleanup:
|
||||
strbuf_release(&ref);
|
||||
free(real_ref);
|
||||
}
|
||||
|
||||
void dwim_and_setup_tracking(struct repository *r, const char *new_ref,
|
||||
const char *orig_ref, enum branch_track track,
|
||||
int quiet)
|
||||
{
|
||||
char *real_orig_ref;
|
||||
dwim_branch_start(r, orig_ref, track, &real_orig_ref, NULL);
|
||||
setup_tracking(new_ref, real_orig_ref, track, quiet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a branch in a submodule by calling
|
||||
* create_branches_recursively() in a child process. The child process
|
||||
* is necessary because install_branch_config_multiple_remotes() (which
|
||||
* is called by setup_tracking()) does not support writing configs to
|
||||
* submodules.
|
||||
*/
|
||||
static int submodule_create_branch(struct repository *r,
|
||||
const struct submodule *submodule,
|
||||
const char *name, const char *start_oid,
|
||||
const char *tracking_name, int force,
|
||||
int reflog, int quiet,
|
||||
enum branch_track track, int dry_run)
|
||||
{
|
||||
int ret = 0;
|
||||
struct child_process child = CHILD_PROCESS_INIT;
|
||||
struct strbuf child_err = STRBUF_INIT;
|
||||
struct strbuf out_buf = STRBUF_INIT;
|
||||
char *out_prefix = xstrfmt("submodule '%s': ", submodule->name);
|
||||
child.git_cmd = 1;
|
||||
child.err = -1;
|
||||
child.stdout_to_stderr = 1;
|
||||
|
||||
prepare_other_repo_env(&child.env_array, r->gitdir);
|
||||
/*
|
||||
* submodule_create_branch() is indirectly invoked by "git
|
||||
* branch", but we cannot invoke "git branch" in the child
|
||||
* process. "git branch" accepts a branch name and start point,
|
||||
* where the start point is assumed to provide both the OID
|
||||
* (start_oid) and the branch to use for tracking
|
||||
* (tracking_name). But when recursing through submodules,
|
||||
* start_oid and tracking name need to be specified separately
|
||||
* (see create_branches_recursively()).
|
||||
*/
|
||||
strvec_pushl(&child.args, "submodule--helper", "create-branch", NULL);
|
||||
if (dry_run)
|
||||
strvec_push(&child.args, "--dry-run");
|
||||
if (force)
|
||||
strvec_push(&child.args, "--force");
|
||||
if (quiet)
|
||||
strvec_push(&child.args, "--quiet");
|
||||
if (reflog)
|
||||
strvec_push(&child.args, "--create-reflog");
|
||||
if (track == BRANCH_TRACK_ALWAYS || track == BRANCH_TRACK_EXPLICIT)
|
||||
strvec_push(&child.args, "--track");
|
||||
|
||||
strvec_pushl(&child.args, name, start_oid, tracking_name, NULL);
|
||||
|
||||
if ((ret = start_command(&child)))
|
||||
return ret;
|
||||
ret = finish_command(&child);
|
||||
strbuf_read(&child_err, child.err, 0);
|
||||
strbuf_add_lines(&out_buf, out_prefix, child_err.buf, child_err.len);
|
||||
|
||||
if (ret)
|
||||
fprintf(stderr, "%s", out_buf.buf);
|
||||
else
|
||||
printf("%s", out_buf.buf);
|
||||
|
||||
strbuf_release(&child_err);
|
||||
strbuf_release(&out_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void create_branches_recursively(struct repository *r, const char *name,
|
||||
const char *start_commitish,
|
||||
const char *tracking_name, int force,
|
||||
int reflog, int quiet, enum branch_track track,
|
||||
int dry_run)
|
||||
{
|
||||
int i = 0;
|
||||
char *branch_point = NULL;
|
||||
struct object_id super_oid;
|
||||
struct submodule_entry_list submodule_entry_list;
|
||||
|
||||
/* Perform dwim on start_commitish to get super_oid and branch_point. */
|
||||
dwim_branch_start(r, start_commitish, BRANCH_TRACK_NEVER,
|
||||
&branch_point, &super_oid);
|
||||
|
||||
/*
|
||||
* If we were not given an explicit name to track, then assume we are at
|
||||
* the top level and, just like the non-recursive case, the tracking
|
||||
* name is the branch point.
|
||||
*/
|
||||
if (!tracking_name)
|
||||
tracking_name = branch_point;
|
||||
|
||||
submodules_of_tree(r, &super_oid, &submodule_entry_list);
|
||||
/*
|
||||
* Before creating any branches, first check that the branch can
|
||||
* be created in every submodule.
|
||||
*/
|
||||
for (i = 0; i < submodule_entry_list.entry_nr; i++) {
|
||||
if (submodule_entry_list.entries[i].repo == NULL) {
|
||||
if (advice_enabled(ADVICE_SUBMODULES_NOT_UPDATED))
|
||||
advise(_("You may try updating the submodules using 'git checkout %s && git submodule update --init'"),
|
||||
start_commitish);
|
||||
die(_("submodule '%s': unable to find submodule"),
|
||||
submodule_entry_list.entries[i].submodule->name);
|
||||
}
|
||||
|
||||
if (submodule_create_branch(
|
||||
submodule_entry_list.entries[i].repo,
|
||||
submodule_entry_list.entries[i].submodule, name,
|
||||
oid_to_hex(&submodule_entry_list.entries[i]
|
||||
.name_entry->oid),
|
||||
tracking_name, force, reflog, quiet, track, 1))
|
||||
die(_("submodule '%s': cannot create branch '%s'"),
|
||||
submodule_entry_list.entries[i].submodule->name,
|
||||
name);
|
||||
}
|
||||
|
||||
create_branch(the_repository, name, start_commitish, force, 0, reflog, quiet,
|
||||
BRANCH_TRACK_NEVER, dry_run);
|
||||
if (dry_run)
|
||||
return;
|
||||
/*
|
||||
* NEEDSWORK If tracking was set up in the superproject but not the
|
||||
* submodule, users might expect "git branch --recurse-submodules" to
|
||||
* fail or give a warning, but this is not yet implemented because it is
|
||||
* tedious to determine whether or not tracking was set up in the
|
||||
* superproject.
|
||||
*/
|
||||
setup_tracking(name, tracking_name, track, quiet);
|
||||
|
||||
for (i = 0; i < submodule_entry_list.entry_nr; i++) {
|
||||
if (submodule_create_branch(
|
||||
submodule_entry_list.entries[i].repo,
|
||||
submodule_entry_list.entries[i].submodule, name,
|
||||
oid_to_hex(&submodule_entry_list.entries[i]
|
||||
.name_entry->oid),
|
||||
tracking_name, force, reflog, quiet, track, 0))
|
||||
die(_("submodule '%s': cannot create branch '%s'"),
|
||||
submodule_entry_list.entries[i].submodule->name,
|
||||
name);
|
||||
repo_clear(submodule_entry_list.entries[i].repo);
|
||||
}
|
||||
}
|
||||
|
||||
void remove_merge_branch_state(struct repository *r)
|
||||
{
|
||||
unlink(git_path_merge_head(r));
|
||||
|
|
60
branch.h
60
branch.h
|
@ -18,6 +18,28 @@ extern enum branch_track git_branch_track;
|
|||
|
||||
/* Functions for acting on the information about branches. */
|
||||
|
||||
/**
|
||||
* Sets branch.<new_ref>.{remote,merge} config settings such that
|
||||
* new_ref tracks orig_ref according to the specified tracking mode.
|
||||
*
|
||||
* - new_ref is the name of the branch that we are setting tracking
|
||||
* for.
|
||||
*
|
||||
* - orig_ref is the name of the ref that is 'upstream' of new_ref.
|
||||
* orig_ref will be expanded with DWIM so that the config settings
|
||||
* are in the correct format e.g. "refs/remotes/origin/main" instead
|
||||
* of "origin/main".
|
||||
*
|
||||
* - track is the tracking mode e.g. BRANCH_TRACK_REMOTE causes
|
||||
* new_ref to track orig_ref directly, whereas BRANCH_TRACK_INHERIT
|
||||
* causes new_ref to track whatever orig_ref tracks.
|
||||
*
|
||||
* - quiet suppresses tracking information.
|
||||
*/
|
||||
void dwim_and_setup_tracking(struct repository *r, const char *new_ref,
|
||||
const char *orig_ref, enum branch_track track,
|
||||
int quiet);
|
||||
|
||||
/*
|
||||
* Creates a new branch, where:
|
||||
*
|
||||
|
@ -30,8 +52,8 @@ extern enum branch_track git_branch_track;
|
|||
*
|
||||
* - force enables overwriting an existing (non-head) branch
|
||||
*
|
||||
* - clobber_head_ok allows the currently checked out (hence existing)
|
||||
* branch to be overwritten; without 'force', it has no effect.
|
||||
* - clobber_head_ok, when enabled with 'force', allows the currently
|
||||
* checked out (head) branch to be overwritten
|
||||
*
|
||||
* - reflog creates a reflog for the branch
|
||||
*
|
||||
|
@ -40,12 +62,44 @@ extern enum branch_track git_branch_track;
|
|||
* - track causes the new branch to be configured to merge the remote branch
|
||||
* that start_name is a tracking branch for (if any).
|
||||
*
|
||||
* - dry_run causes the branch to be validated but not created.
|
||||
*
|
||||
*/
|
||||
void create_branch(struct repository *r,
|
||||
const char *name, const char *start_name,
|
||||
int force, int clobber_head_ok,
|
||||
int reflog, int quiet, enum branch_track track);
|
||||
int reflog, int quiet, enum branch_track track,
|
||||
int dry_run);
|
||||
|
||||
/*
|
||||
* Creates a new branch in a repository and its submodules (and its
|
||||
* submodules, recursively). The parameters are mostly analogous to
|
||||
* those of create_branch() except for start_name, which is represented
|
||||
* by two different parameters:
|
||||
*
|
||||
* - start_commitish is the commit-ish, in repository r, that determines
|
||||
* which commits the branches will point to. The superproject branch
|
||||
* will point to the commit of start_commitish and the submodule
|
||||
* branches will point to the gitlink commit oids in start_commitish's
|
||||
* tree.
|
||||
*
|
||||
* - tracking_name is the name of the ref, in repository r, that will be
|
||||
* used to set up tracking information. This value is propagated to
|
||||
* all submodules, which will evaluate the ref using their own ref
|
||||
* stores. If NULL, this defaults to start_commitish.
|
||||
*
|
||||
* When this function is called on the superproject, start_commitish
|
||||
* can be any user-provided ref and tracking_name can be NULL (similar
|
||||
* to create_branches()). But when recursing through submodules,
|
||||
* start_commitish is the plain gitlink commit oid. Since the oid cannot
|
||||
* be used for tracking information, tracking_name is propagated and
|
||||
* used for tracking instead.
|
||||
*/
|
||||
void create_branches_recursively(struct repository *r, const char *name,
|
||||
const char *start_commitish,
|
||||
const char *tracking_name, int force,
|
||||
int reflog, int quiet, enum branch_track track,
|
||||
int dry_run);
|
||||
/*
|
||||
* Check if 'name' can be a valid name for a branch; die otherwise.
|
||||
* Return 1 if the named branch already exists; return 0 otherwise.
|
||||
|
|
|
@ -159,6 +159,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix);
|
|||
int cmd_for_each_repo(int argc, const char **argv, const char *prefix);
|
||||
int cmd_format_patch(int argc, const char **argv, const char *prefix);
|
||||
int cmd_fsck(int argc, const char **argv, const char *prefix);
|
||||
int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix);
|
||||
int cmd_gc(int argc, const char **argv, const char *prefix);
|
||||
int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
|
||||
int cmd_grep(int argc, const char **argv, const char *prefix);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -27,7 +27,8 @@
|
|||
|
||||
static const char * const builtin_branch_usage[] = {
|
||||
N_("git branch [<options>] [-r | -a] [--merged] [--no-merged]"),
|
||||
N_("git branch [<options>] [-l] [-f] <branch-name> [<start-point>]"),
|
||||
N_("git branch [<options>] [-f] [--recurse-submodules] <branch-name> [<start-point>]"),
|
||||
N_("git branch [<options>] [-l] [<pattern>...]"),
|
||||
N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."),
|
||||
N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"),
|
||||
N_("git branch [<options>] (-c | -C) [<old-branch>] <new-branch>"),
|
||||
|
@ -38,6 +39,8 @@ static const char * const builtin_branch_usage[] = {
|
|||
|
||||
static const char *head;
|
||||
static struct object_id head_oid;
|
||||
static int recurse_submodules = 0;
|
||||
static int submodule_propagate_branches = 0;
|
||||
|
||||
static int branch_use_color = -1;
|
||||
static char branch_colors[][COLOR_MAXLEN] = {
|
||||
|
@ -99,6 +102,15 @@ static int git_branch_config(const char *var, const char *value, void *cb)
|
|||
return config_error_nonbool(var);
|
||||
return color_parse(value, branch_colors[slot]);
|
||||
}
|
||||
if (!strcmp(var, "submodule.recurse")) {
|
||||
recurse_submodules = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
if (!strcasecmp(var, "submodule.propagateBranches")) {
|
||||
submodule_propagate_branches = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return git_color_default_config(var, value, cb);
|
||||
}
|
||||
|
||||
|
@ -621,14 +633,16 @@ static int edit_branch_description(const char *branch_name)
|
|||
|
||||
int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int delete = 0, rename = 0, copy = 0, force = 0, list = 0;
|
||||
int show_current = 0;
|
||||
int reflog = 0, edit_description = 0;
|
||||
int quiet = 0, unset_upstream = 0;
|
||||
/* possible actions */
|
||||
int delete = 0, rename = 0, copy = 0, list = 0,
|
||||
unset_upstream = 0, show_current = 0, edit_description = 0;
|
||||
const char *new_upstream = NULL;
|
||||
int noncreate_actions = 0;
|
||||
/* possible options */
|
||||
int reflog = 0, quiet = 0, icase = 0, force = 0,
|
||||
recurse_submodules_explicit = 0;
|
||||
enum branch_track track;
|
||||
struct ref_filter filter;
|
||||
int icase = 0;
|
||||
static struct ref_sorting *sorting;
|
||||
struct string_list sorting_options = STRING_LIST_INIT_DUP;
|
||||
struct ref_format format = REF_FORMAT_INIT;
|
||||
|
@ -677,6 +691,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
|||
OPT_CALLBACK(0, "points-at", &filter.points_at, N_("object"),
|
||||
N_("print only branches of the object"), parse_opt_object_name),
|
||||
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
|
||||
OPT_BOOL(0, "recurse-submodules", &recurse_submodules_explicit, N_("recurse through submodules")),
|
||||
OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
|
||||
OPT_END(),
|
||||
};
|
||||
|
@ -713,10 +728,23 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
|||
filter.reachable_from || filter.unreachable_from || filter.points_at.nr)
|
||||
list = 1;
|
||||
|
||||
if (!!delete + !!rename + !!copy + !!new_upstream + !!show_current +
|
||||
list + edit_description + unset_upstream > 1)
|
||||
noncreate_actions = !!delete + !!rename + !!copy + !!new_upstream +
|
||||
!!show_current + !!list + !!edit_description +
|
||||
!!unset_upstream;
|
||||
if (noncreate_actions > 1)
|
||||
usage_with_options(builtin_branch_usage, options);
|
||||
|
||||
if (recurse_submodules_explicit) {
|
||||
if (!submodule_propagate_branches)
|
||||
die(_("branch with --recurse-submodules can only be used if submodule.propagateBranches is enabled"));
|
||||
if (noncreate_actions)
|
||||
die(_("--recurse-submodules can only be used to create branches"));
|
||||
}
|
||||
|
||||
recurse_submodules =
|
||||
(recurse_submodules || recurse_submodules_explicit) &&
|
||||
submodule_propagate_branches;
|
||||
|
||||
if (filter.abbrev == -1)
|
||||
filter.abbrev = DEFAULT_ABBREV;
|
||||
filter.ignore_case = icase;
|
||||
|
@ -828,12 +856,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
|||
if (!ref_exists(branch->refname))
|
||||
die(_("branch '%s' does not exist"), branch->name);
|
||||
|
||||
/*
|
||||
* create_branch takes care of setting up the tracking
|
||||
* info and making sure new_upstream is correct
|
||||
*/
|
||||
create_branch(the_repository, branch->name, new_upstream,
|
||||
0, 0, 0, quiet, BRANCH_TRACK_OVERRIDE);
|
||||
dwim_and_setup_tracking(the_repository, branch->name,
|
||||
new_upstream, BRANCH_TRACK_OVERRIDE,
|
||||
quiet);
|
||||
} else if (unset_upstream) {
|
||||
struct branch *branch = branch_get(argv[0]);
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
|
@ -857,7 +882,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
|||
strbuf_addf(&buf, "branch.%s.merge", branch->name);
|
||||
git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);
|
||||
strbuf_release(&buf);
|
||||
} else if (argc > 0 && argc <= 2) {
|
||||
} else if (!noncreate_actions && argc > 0 && argc <= 2) {
|
||||
const char *branch_name = argv[0];
|
||||
const char *start_name = argc == 2 ? argv[1] : head;
|
||||
|
||||
if (filter.kind != FILTER_REFS_BRANCHES)
|
||||
die(_("The -a, and -r, options to 'git branch' do not take a branch name.\n"
|
||||
"Did you mean to use: -a|-r --list <pattern>?"));
|
||||
|
@ -865,10 +893,14 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
|||
if (track == BRANCH_TRACK_OVERRIDE)
|
||||
die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead."));
|
||||
|
||||
create_branch(the_repository,
|
||||
argv[0], (argc == 2) ? argv[1] : head,
|
||||
force, 0, reflog, quiet, track);
|
||||
|
||||
if (recurse_submodules) {
|
||||
create_branches_recursively(the_repository, branch_name,
|
||||
start_name, NULL, force,
|
||||
reflog, quiet, track, 0);
|
||||
return 0;
|
||||
}
|
||||
create_branch(the_repository, branch_name, start_name, force, 0,
|
||||
reflog, quiet, track, 0);
|
||||
} else
|
||||
usage_with_options(builtin_branch_usage, options);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#define USE_THE_INDEX_COMPATIBILITY_MACROS
|
||||
#include "builtin.h"
|
||||
#include "config.h"
|
||||
#include "dir.h"
|
||||
#include "lockfile.h"
|
||||
#include "quote.h"
|
||||
#include "cache-tree.h"
|
||||
|
@ -17,6 +18,7 @@
|
|||
#define CHECKOUT_ALL 4
|
||||
static int nul_term_line;
|
||||
static int checkout_stage; /* default to checkout stage0 */
|
||||
static int ignore_skip_worktree; /* default to 0 */
|
||||
static int to_tempfile;
|
||||
static char topath[4][TEMPORARY_FILENAME_LENGTH + 1];
|
||||
|
||||
|
@ -65,6 +67,8 @@ static int checkout_file(const char *name, const char *prefix)
|
|||
int namelen = strlen(name);
|
||||
int pos = cache_name_pos(name, namelen);
|
||||
int has_same_name = 0;
|
||||
int is_file = 0;
|
||||
int is_skipped = 1;
|
||||
int did_checkout = 0;
|
||||
int errs = 0;
|
||||
|
||||
|
@ -78,6 +82,12 @@ static int checkout_file(const char *name, const char *prefix)
|
|||
break;
|
||||
has_same_name = 1;
|
||||
pos++;
|
||||
if (S_ISSPARSEDIR(ce->ce_mode))
|
||||
break;
|
||||
is_file = 1;
|
||||
if (!ignore_skip_worktree && ce_skip_worktree(ce))
|
||||
break;
|
||||
is_skipped = 0;
|
||||
if (ce_stage(ce) != checkout_stage
|
||||
&& (CHECKOUT_ALL != checkout_stage || !ce_stage(ce)))
|
||||
continue;
|
||||
|
@ -106,6 +116,11 @@ static int checkout_file(const char *name, const char *prefix)
|
|||
fprintf(stderr, "git checkout-index: %s ", name);
|
||||
if (!has_same_name)
|
||||
fprintf(stderr, "is not in the cache");
|
||||
else if (!is_file)
|
||||
fprintf(stderr, "is a sparse directory");
|
||||
else if (is_skipped)
|
||||
fprintf(stderr, "has skip-worktree enabled; "
|
||||
"use '--ignore-skip-worktree-bits' to checkout");
|
||||
else if (checkout_stage)
|
||||
fprintf(stderr, "does not exist at stage %d",
|
||||
checkout_stage);
|
||||
|
@ -121,10 +136,27 @@ static int checkout_all(const char *prefix, int prefix_length)
|
|||
int i, errs = 0;
|
||||
struct cache_entry *last_ce = NULL;
|
||||
|
||||
/* TODO: audit for interaction with sparse-index. */
|
||||
ensure_full_index(&the_index);
|
||||
for (i = 0; i < active_nr ; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
|
||||
if (S_ISSPARSEDIR(ce->ce_mode)) {
|
||||
if (!ce_skip_worktree(ce))
|
||||
BUG("sparse directory '%s' does not have skip-worktree set", ce->name);
|
||||
|
||||
/*
|
||||
* If the current entry is a sparse directory and skip-worktree
|
||||
* entries are being checked out, expand the index and continue
|
||||
* the loop on the current index position (now pointing to the
|
||||
* first entry inside the expanded sparse directory).
|
||||
*/
|
||||
if (ignore_skip_worktree) {
|
||||
ensure_full_index(&the_index);
|
||||
ce = active_cache[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (!ignore_skip_worktree && ce_skip_worktree(ce))
|
||||
continue;
|
||||
if (ce_stage(ce) != checkout_stage
|
||||
&& (CHECKOUT_ALL != checkout_stage || !ce_stage(ce)))
|
||||
continue;
|
||||
|
@ -185,6 +217,8 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
|
|||
struct option builtin_checkout_index_options[] = {
|
||||
OPT_BOOL('a', "all", &all,
|
||||
N_("check out all files in the index")),
|
||||
OPT_BOOL(0, "ignore-skip-worktree-bits", &ignore_skip_worktree,
|
||||
N_("do not skip files with skip-worktree set")),
|
||||
OPT__FORCE(&force, N_("force overwrite of existing files"), 0),
|
||||
OPT__QUIET(&quiet,
|
||||
N_("no warning for existing files and files not in index")),
|
||||
|
@ -212,6 +246,9 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
|
|||
git_config(git_default_config, NULL);
|
||||
prefix_length = prefix ? strlen(prefix) : 0;
|
||||
|
||||
prepare_repo_settings(the_repository);
|
||||
the_repository->settings.command_requires_full_index = 0;
|
||||
|
||||
if (read_cache() < 0) {
|
||||
die("invalid cache");
|
||||
}
|
||||
|
|
|
@ -909,7 +909,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
|
|||
opts->new_branch_force ? 1 : 0,
|
||||
opts->new_branch_log,
|
||||
opts->quiet,
|
||||
opts->track);
|
||||
opts->track,
|
||||
0);
|
||||
free(new_branch_info->name);
|
||||
free(new_branch_info->refname);
|
||||
new_branch_info->name = xstrdup(opts->new_branch);
|
||||
|
@ -1607,9 +1608,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
|
|||
opts->show_progress = -1;
|
||||
|
||||
git_config(git_checkout_config, opts);
|
||||
|
||||
prepare_repo_settings(the_repository);
|
||||
the_repository->settings.command_requires_full_index = 0;
|
||||
if (the_repository->gitdir) {
|
||||
prepare_repo_settings(the_repository);
|
||||
the_repository->settings.command_requires_full_index = 0;
|
||||
}
|
||||
|
||||
opts->track = BRANCH_TRACK_UNSPECIFIED;
|
||||
|
||||
|
|
|
@ -1009,6 +1009,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
|
|||
dir.flags |= DIR_KEEP_UNTRACKED_CONTENTS;
|
||||
}
|
||||
|
||||
prepare_repo_settings(the_repository);
|
||||
the_repository->settings.command_requires_full_index = 0;
|
||||
|
||||
if (read_cache() < 0)
|
||||
die(_("index file corrupt"));
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -1067,6 +1078,27 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
|||
if (option_reject_shallow != -1)
|
||||
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.
|
||||
|
@ -1235,7 +1267,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
|||
}
|
||||
else {
|
||||
const char *branch;
|
||||
char *ref;
|
||||
const char *ref;
|
||||
char *ref_free = NULL;
|
||||
|
||||
if (option_branch)
|
||||
die(_("Remote branch %s not found in upstream %s"),
|
||||
|
@ -1251,17 +1284,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
|||
skip_prefix(transport_ls_refs_options.unborn_head_target,
|
||||
"refs/heads/", &branch)) {
|
||||
ref = transport_ls_refs_options.unborn_head_target;
|
||||
transport_ls_refs_options.unborn_head_target = NULL;
|
||||
create_symref("HEAD", ref, reflog_msg.buf);
|
||||
} else {
|
||||
branch = git_default_branch_name(0);
|
||||
ref = xstrfmt("refs/heads/%s", branch);
|
||||
ref_free = xstrfmt("refs/heads/%s", branch);
|
||||
ref = ref_free;
|
||||
}
|
||||
|
||||
if (!option_bare)
|
||||
install_branch_config(0, branch, remote_name, ref);
|
||||
|
||||
free(ref);
|
||||
free(ref_free);
|
||||
}
|
||||
|
||||
write_refspec_config(src_ref_prefix, our_head_points_at,
|
||||
|
@ -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);
|
||||
|
@ -1313,7 +1345,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
|||
UNLEAK(repo);
|
||||
junk_mode = JUNK_LEAVE_ALL;
|
||||
|
||||
strvec_clear(&transport_ls_refs_options.ref_prefixes);
|
||||
free(transport_ls_refs_options.unborn_head_target);
|
||||
transport_ls_refs_options_release(&transport_ls_refs_options);
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
@ -1594,7 +1598,7 @@ static int do_fetch(struct transport *transport,
|
|||
} else
|
||||
remote_refs = NULL;
|
||||
|
||||
strvec_clear(&transport_ls_refs_options.ref_prefixes);
|
||||
transport_ls_refs_options_release(&transport_ls_refs_options);
|
||||
|
||||
ref_map = get_ref_map(transport->remote, remote_refs, rs,
|
||||
tags, &autotags);
|
||||
|
@ -2017,8 +2021,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
|
|||
}
|
||||
|
||||
git_config(git_fetch_config, NULL);
|
||||
prepare_repo_settings(the_repository);
|
||||
the_repository->settings.command_requires_full_index = 0;
|
||||
if (the_repository->gitdir) {
|
||||
prepare_repo_settings(the_repository);
|
||||
the_repository->settings.command_requires_full_index = 0;
|
||||
}
|
||||
|
||||
argc = parse_options(argc, argv, prefix,
|
||||
builtin_fetch_options, builtin_fetch_usage, 0);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
@ -92,6 +92,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
|
|||
int nongit = 0;
|
||||
unsigned flags = HASH_FORMAT_CHECK;
|
||||
const char *vpath = NULL;
|
||||
char *vpath_free = NULL;
|
||||
const struct option hash_object_options[] = {
|
||||
OPT_STRING('t', NULL, &type, N_("type"), N_("object type")),
|
||||
OPT_BIT('w', NULL, &flags, N_("write the object into the object database"),
|
||||
|
@ -114,8 +115,10 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
|
|||
else
|
||||
prefix = setup_git_directory_gently(&nongit);
|
||||
|
||||
if (vpath && prefix)
|
||||
vpath = prefix_filename(prefix, vpath);
|
||||
if (vpath && prefix) {
|
||||
vpath_free = prefix_filename(prefix, vpath);
|
||||
vpath = vpath_free;
|
||||
}
|
||||
|
||||
git_config(git_default_config, NULL);
|
||||
|
||||
|
@ -156,5 +159,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
|
|||
if (stdin_paths)
|
||||
hash_stdin_paths(type, no_filters, flags, literally);
|
||||
|
||||
free(vpath_free);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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(¬es_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, ¬es_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();
|
||||
|
|
|
@ -155,6 +155,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
|
|||
|
||||
ref_array_clear(&ref_array);
|
||||
if (transport_disconnect(transport))
|
||||
return 1;
|
||||
status = 1;
|
||||
transport_ls_refs_options_release(&transport_options);
|
||||
return status;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1568,8 +1568,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
|
|||
|
||||
if (autostash)
|
||||
create_autostash(the_repository,
|
||||
git_path_merge_autostash(the_repository),
|
||||
"merge");
|
||||
git_path_merge_autostash(the_repository));
|
||||
if (checkout_fast_forward(the_repository,
|
||||
&head_commit->object.oid,
|
||||
&commit->object.oid,
|
||||
|
@ -1640,8 +1639,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
|
|||
|
||||
if (autostash)
|
||||
create_autostash(the_repository,
|
||||
git_path_merge_autostash(the_repository),
|
||||
"merge");
|
||||
git_path_merge_autostash(the_repository));
|
||||
|
||||
/* We are going to make a new commit. */
|
||||
git_committer_info(IDENT_STRICT);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -3976,9 +3976,11 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
|
|||
read_replace_refs = 0;
|
||||
|
||||
sparse = git_env_bool("GIT_TEST_PACK_SPARSE", -1);
|
||||
prepare_repo_settings(the_repository);
|
||||
if (sparse < 0)
|
||||
sparse = the_repository->settings.pack_use_sparse;
|
||||
if (the_repository->gitdir) {
|
||||
prepare_repo_settings(the_repository);
|
||||
if (sparse < 0)
|
||||
sparse = the_repository->settings.pack_use_sparse;
|
||||
}
|
||||
|
||||
reset_pack_idx_option(&pack_idx_opts);
|
||||
git_config(git_pack_config, NULL);
|
||||
|
|
|
@ -32,8 +32,12 @@ static int scan_hunk_header(const char *p, int *p_before, int *p_after)
|
|||
n = strspn(q, digits);
|
||||
if (q[n] == ',') {
|
||||
q += n + 1;
|
||||
*p_before = atoi(q);
|
||||
n = strspn(q, digits);
|
||||
} else {
|
||||
*p_before = 1;
|
||||
}
|
||||
|
||||
if (n == 0 || q[n] != ' ' || q[n+1] != '+')
|
||||
return 0;
|
||||
|
||||
|
@ -41,13 +45,14 @@ static int scan_hunk_header(const char *p, int *p_before, int *p_after)
|
|||
n = strspn(r, digits);
|
||||
if (r[n] == ',') {
|
||||
r += n + 1;
|
||||
*p_after = atoi(r);
|
||||
n = strspn(r, digits);
|
||||
} else {
|
||||
*p_after = 1;
|
||||
}
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
*p_before = atoi(q);
|
||||
*p_after = atoi(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -994,8 +994,10 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
|
|||
set_reflog_message(argc, argv);
|
||||
|
||||
git_config(git_pull_config, NULL);
|
||||
prepare_repo_settings(the_repository);
|
||||
the_repository->settings.command_requires_full_index = 0;
|
||||
if (the_repository->gitdir) {
|
||||
prepare_repo_settings(the_repository);
|
||||
the_repository->settings.command_requires_full_index = 0;
|
||||
}
|
||||
|
||||
argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);
|
||||
|
||||
|
|
|
@ -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")) {
|
||||
|
|
103
builtin/rebase.c
103
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
|
||||
};
|
||||
|
||||
|
@ -571,7 +571,8 @@ static int finish_rebase(struct rebase_options *opts)
|
|||
|
||||
static int move_to_original_branch(struct rebase_options *opts)
|
||||
{
|
||||
struct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;
|
||||
struct strbuf branch_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;
|
||||
struct reset_head_opts ropts = { 0 };
|
||||
int ret;
|
||||
|
||||
if (!opts->head_name)
|
||||
|
@ -580,16 +581,17 @@ static int move_to_original_branch(struct rebase_options *opts)
|
|||
if (!opts->onto)
|
||||
BUG("move_to_original_branch without onto");
|
||||
|
||||
strbuf_addf(&orig_head_reflog, "rebase finished: %s onto %s",
|
||||
strbuf_addf(&branch_reflog, "rebase finished: %s onto %s",
|
||||
opts->head_name, oid_to_hex(&opts->onto->object.oid));
|
||||
strbuf_addf(&head_reflog, "rebase finished: returning to %s",
|
||||
opts->head_name);
|
||||
ret = reset_head(the_repository, NULL, "", opts->head_name,
|
||||
RESET_HEAD_REFS_ONLY,
|
||||
orig_head_reflog.buf, head_reflog.buf,
|
||||
DEFAULT_REFLOG_ACTION);
|
||||
ropts.branch = opts->head_name;
|
||||
ropts.flags = RESET_HEAD_REFS_ONLY;
|
||||
ropts.branch_msg = branch_reflog.buf;
|
||||
ropts.head_msg = head_reflog.buf;
|
||||
ret = reset_head(the_repository, &ropts);
|
||||
|
||||
strbuf_release(&orig_head_reflog);
|
||||
strbuf_release(&branch_reflog);
|
||||
strbuf_release(&head_reflog);
|
||||
return ret;
|
||||
}
|
||||
|
@ -671,13 +673,15 @@ static int run_am(struct rebase_options *opts)
|
|||
|
||||
status = run_command(&format_patch);
|
||||
if (status) {
|
||||
struct reset_head_opts ropts = { 0 };
|
||||
unlink(rebased_patches);
|
||||
free(rebased_patches);
|
||||
strvec_clear(&am.args);
|
||||
|
||||
reset_head(the_repository, &opts->orig_head, "checkout",
|
||||
opts->head_name, 0,
|
||||
"HEAD", NULL, DEFAULT_REFLOG_ACTION);
|
||||
ropts.oid = &opts->orig_head;
|
||||
ropts.branch = opts->head_name;
|
||||
ropts.default_reflog_action = DEFAULT_REFLOG_ACTION;
|
||||
reset_head(the_repository, &ropts);
|
||||
error(_("\ngit encountered an error while preparing the "
|
||||
"patches to replay\n"
|
||||
"these revisions:\n"
|
||||
|
@ -813,6 +817,26 @@ static int rebase_config(const char *var, const char *value, void *data)
|
|||
return git_default_config(var, value, data);
|
||||
}
|
||||
|
||||
static int checkout_up_to_date(struct rebase_options *options)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct reset_head_opts ropts = { 0 };
|
||||
int ret = 0;
|
||||
|
||||
strbuf_addf(&buf, "%s: checkout %s",
|
||||
getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
|
||||
options->switch_to);
|
||||
ropts.oid = &options->orig_head;
|
||||
ropts.branch = options->head_name;
|
||||
ropts.flags = RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
|
||||
ropts.head_msg = buf.buf;
|
||||
if (reset_head(the_repository, &ropts) < 0)
|
||||
ret = error(_("could not switch to %s"), options->switch_to);
|
||||
strbuf_release(&buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines whether the commits in from..to are linear, i.e. contain
|
||||
* no merge commits. This function *expects* `from` to be an ancestor of
|
||||
|
@ -1018,6 +1042,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||
int reschedule_failed_exec = -1;
|
||||
int allow_preemptive_ff = 1;
|
||||
int preserve_merges_selected = 0;
|
||||
struct reset_head_opts ropts = { 0 };
|
||||
struct option builtin_rebase_options[] = {
|
||||
OPT_STRING(0, "onto", &options.onto_name,
|
||||
N_("revision"),
|
||||
|
@ -1255,9 +1280,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||
|
||||
rerere_clear(the_repository, &merge_rr);
|
||||
string_list_clear(&merge_rr, 1);
|
||||
|
||||
if (reset_head(the_repository, NULL, "reset", NULL, RESET_HEAD_HARD,
|
||||
NULL, NULL, DEFAULT_REFLOG_ACTION) < 0)
|
||||
ropts.flags = RESET_HEAD_HARD;
|
||||
if (reset_head(the_repository, &ropts) < 0)
|
||||
die(_("could not discard worktree changes"));
|
||||
remove_branch_state(the_repository, 0);
|
||||
if (read_basic_state(&options))
|
||||
|
@ -1274,9 +1298,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||
|
||||
if (read_basic_state(&options))
|
||||
exit(1);
|
||||
if (reset_head(the_repository, &options.orig_head, "reset",
|
||||
options.head_name, RESET_HEAD_HARD,
|
||||
NULL, NULL, DEFAULT_REFLOG_ACTION) < 0)
|
||||
ropts.oid = &options.orig_head;
|
||||
ropts.branch = options.head_name;
|
||||
ropts.flags = RESET_HEAD_HARD;
|
||||
ropts.default_reflog_action = DEFAULT_REFLOG_ACTION;
|
||||
if (reset_head(the_repository, &ropts) < 0)
|
||||
die(_("could not move back to %s"),
|
||||
oid_to_hex(&options.orig_head));
|
||||
remove_branch_state(the_repository, 0);
|
||||
|
@ -1642,10 +1668,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||
if (repo_read_index(the_repository) < 0)
|
||||
die(_("could not read index"));
|
||||
|
||||
if (options.autostash) {
|
||||
create_autostash(the_repository, state_dir_path("autostash", &options),
|
||||
DEFAULT_REFLOG_ACTION);
|
||||
}
|
||||
if (options.autostash)
|
||||
create_autostash(the_repository,
|
||||
state_dir_path("autostash", &options));
|
||||
|
||||
|
||||
if (require_clean_work_tree(the_repository, "rebase",
|
||||
_("Please commit or stash them."), 1, 1)) {
|
||||
|
@ -1674,21 +1700,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||
if (!(options.flags & REBASE_FORCE)) {
|
||||
/* Lazily switch to the target branch if needed... */
|
||||
if (options.switch_to) {
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addf(&buf, "%s: checkout %s",
|
||||
getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
|
||||
options.switch_to);
|
||||
if (reset_head(the_repository,
|
||||
&options.orig_head, "checkout",
|
||||
options.head_name,
|
||||
RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
|
||||
NULL, buf.buf,
|
||||
DEFAULT_REFLOG_ACTION) < 0) {
|
||||
ret = error(_("could not switch to "
|
||||
"%s"),
|
||||
options.switch_to);
|
||||
ret = checkout_up_to_date(&options);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(options.flags & REBASE_NO_QUIET))
|
||||
|
@ -1755,10 +1769,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||
|
||||
strbuf_addf(&msg, "%s: checkout %s",
|
||||
getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
|
||||
if (reset_head(the_repository, &options.onto->object.oid, "checkout", NULL,
|
||||
RESET_HEAD_DETACH | RESET_ORIG_HEAD |
|
||||
RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
|
||||
NULL, msg.buf, DEFAULT_REFLOG_ACTION))
|
||||
ropts.oid = &options.onto->object.oid;
|
||||
ropts.orig_head = &options.orig_head,
|
||||
ropts.flags = RESET_HEAD_DETACH | RESET_ORIG_HEAD |
|
||||
RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
|
||||
ropts.head_msg = msg.buf;
|
||||
ropts.default_reflog_action = DEFAULT_REFLOG_ACTION;
|
||||
if (reset_head(the_repository, &ropts))
|
||||
die(_("Could not detach HEAD"));
|
||||
strbuf_release(&msg);
|
||||
|
||||
|
@ -1773,9 +1790,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||
strbuf_addf(&msg, "rebase finished: %s onto %s",
|
||||
options.head_name ? options.head_name : "detached HEAD",
|
||||
oid_to_hex(&options.onto->object.oid));
|
||||
reset_head(the_repository, NULL, "Fast-forwarded", options.head_name,
|
||||
RESET_HEAD_REFS_ONLY, "HEAD", msg.buf,
|
||||
DEFAULT_REFLOG_ACTION);
|
||||
memset(&ropts, 0, sizeof(ropts));
|
||||
ropts.branch = options.head_name;
|
||||
ropts.flags = RESET_HEAD_REFS_ONLY;
|
||||
ropts.head_msg = msg.buf;
|
||||
reset_head(the_repository, &ropts);
|
||||
strbuf_release(&msg);
|
||||
ret = finish_rebase(&options);
|
||||
goto cleanup;
|
||||
|
|
|
@ -1961,6 +1961,15 @@ static void execute_commands(struct command *commands,
|
|||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is no command ready to run, should return directly to destroy
|
||||
* temporary data in the quarantine area.
|
||||
*/
|
||||
for (cmd = commands; cmd && cmd->error_string; cmd = cmd->next)
|
||||
; /* nothing */
|
||||
if (!cmd)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Now we'll start writing out refs, which means the objects need
|
||||
* to be in their final positions so that other processes can see them.
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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>"),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -204,10 +204,16 @@ static int pathspec_needs_expanded_index(const struct pathspec *pathspec)
|
|||
/*
|
||||
* Special case: if the pattern is a path inside the cone
|
||||
* followed by only wildcards, the pattern cannot match
|
||||
* partial sparse directories, so we don't expand the index.
|
||||
* partial sparse directories, so we know we don't need to
|
||||
* expand the index.
|
||||
*
|
||||
* Examples:
|
||||
* - in-cone/foo***: doesn't need expanded index
|
||||
* - not-in-cone/bar*: may need expanded index
|
||||
* - **.c: may need expanded index
|
||||
*/
|
||||
if (path_in_cone_mode_sparse_checkout(item.original, &the_index) &&
|
||||
strspn(item.original + item.nowildcard_len, "*") == item.len - item.nowildcard_len)
|
||||
if (strspn(item.original + item.nowildcard_len, "*") == item.len - item.nowildcard_len &&
|
||||
path_in_cone_mode_sparse_checkout(item.original, &the_index))
|
||||
continue;
|
||||
|
||||
for (pos = 0; pos < active_nr; pos++) {
|
||||
|
@ -274,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;
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "wt-status.h"
|
||||
#include "quote.h"
|
||||
#include "sparse-index.h"
|
||||
#include "worktree.h"
|
||||
|
||||
static const char *empty_base = "";
|
||||
|
||||
|
@ -43,7 +44,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 +362,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;
|
||||
}
|
||||
|
@ -421,7 +419,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
|
||||
};
|
||||
|
||||
|
@ -767,7 +765,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
|
|||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
|
@ -805,7 +803,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
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include "diff.h"
|
||||
#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)
|
||||
|
@ -1630,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;
|
||||
|
@ -1796,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" :
|
||||
|
@ -1852,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,
|
||||
|
@ -1881,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);
|
||||
|
||||
|
@ -1899,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;
|
||||
}
|
||||
|
||||
|
@ -1994,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;
|
||||
|
@ -2154,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);
|
||||
|
@ -2498,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,
|
||||
|
@ -2528,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()
|
||||
};
|
||||
|
||||
|
@ -2540,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)
|
||||
|
@ -2883,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
|
||||
};
|
||||
|
||||
|
@ -2984,6 +3009,42 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)
|
|||
return !!ret;
|
||||
}
|
||||
|
||||
static int module_create_branch(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
enum branch_track track;
|
||||
int quiet = 0, force = 0, reflog = 0, dry_run = 0;
|
||||
|
||||
struct option options[] = {
|
||||
OPT__QUIET(&quiet, N_("print only error messages")),
|
||||
OPT__FORCE(&force, N_("force creation"), 0),
|
||||
OPT_BOOL(0, "create-reflog", &reflog,
|
||||
N_("create the branch's reflog")),
|
||||
OPT_SET_INT('t', "track", &track,
|
||||
N_("set up tracking mode (see git-pull(1))"),
|
||||
BRANCH_TRACK_EXPLICIT),
|
||||
OPT__DRY_RUN(&dry_run,
|
||||
N_("show whether the branch would be created")),
|
||||
OPT_END()
|
||||
};
|
||||
const char *const usage[] = {
|
||||
N_("git submodule--helper create-branch [-f|--force] [--create-reflog] [-q|--quiet] [-t|--track] [-n|--dry-run] <name> <start_oid> <start_name>"),
|
||||
NULL
|
||||
};
|
||||
|
||||
git_config(git_default_config, NULL);
|
||||
track = git_branch_track;
|
||||
argc = parse_options(argc, argv, prefix, options, usage, 0);
|
||||
|
||||
if (argc != 3)
|
||||
usage_with_options(usage, options);
|
||||
|
||||
if (!quiet && !dry_run)
|
||||
printf_ln(_("creating branch '%s'"), argv[0]);
|
||||
|
||||
create_branches_recursively(the_repository, argv[0], argv[1], argv[2],
|
||||
force, reflog, quiet, track, dry_run);
|
||||
return 0;
|
||||
}
|
||||
struct add_data {
|
||||
const char *prefix;
|
||||
const char *branch;
|
||||
|
@ -3390,6 +3451,7 @@ static struct cmd_struct commands[] = {
|
|||
{"config", module_config, 0},
|
||||
{"set-url", module_set_url, 0},
|
||||
{"set-branch", module_set_branch, 0},
|
||||
{"create-branch", module_create_branch, 0},
|
||||
};
|
||||
|
||||
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -606,7 +606,7 @@ static struct cache_entry *read_one_ent(const char *which,
|
|||
error("%s: not in %s branch.", path, which);
|
||||
return NULL;
|
||||
}
|
||||
if (mode == S_IFDIR) {
|
||||
if (!the_index.sparse_index && mode == S_IFDIR) {
|
||||
if (which)
|
||||
error("%s: not a blob in %s branch.", path, which);
|
||||
return NULL;
|
||||
|
@ -743,8 +743,6 @@ static int do_reupdate(int ac, const char **av,
|
|||
*/
|
||||
has_head = 0;
|
||||
redo:
|
||||
/* TODO: audit for interaction with sparse-index. */
|
||||
ensure_full_index(&the_index);
|
||||
for (pos = 0; pos < active_nr; pos++) {
|
||||
const struct cache_entry *ce = active_cache[pos];
|
||||
struct cache_entry *old = NULL;
|
||||
|
@ -761,6 +759,16 @@ static int do_reupdate(int ac, const char **av,
|
|||
discard_cache_entry(old);
|
||||
continue; /* unchanged */
|
||||
}
|
||||
|
||||
/* At this point, we know the contents of the sparse directory are
|
||||
* modified with respect to HEAD, so we expand the index and restart
|
||||
* to process each path individually
|
||||
*/
|
||||
if (S_ISSPARSEDIR(ce->ce_mode)) {
|
||||
ensure_full_index(&the_index);
|
||||
goto redo;
|
||||
}
|
||||
|
||||
/* Be careful. The working tree may not have the
|
||||
* path anymore, in which case, under 'allow_remove',
|
||||
* or worse yet 'allow_replace', active_nr may decrease.
|
||||
|
@ -1088,6 +1096,9 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
|
|||
|
||||
git_config(git_default_config, NULL);
|
||||
|
||||
prepare_repo_settings(r);
|
||||
the_repository->settings.command_requires_full_index = 0;
|
||||
|
||||
/* we will diagnose later if it turns out that we need to update it */
|
||||
newfd = hold_locked_index(&lock_file, 0);
|
||||
if (newfd < 0)
|
||||
|
@ -1225,14 +1236,17 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
|
|||
}
|
||||
|
||||
if (fsmonitor > 0) {
|
||||
if (git_config_get_fsmonitor() == 0)
|
||||
enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);
|
||||
if (fsm_mode == FSMONITOR_MODE_DISABLED) {
|
||||
warning(_("core.fsmonitor is unset; "
|
||||
"set it if you really want to "
|
||||
"enable fsmonitor"));
|
||||
}
|
||||
add_fsmonitor(&the_index);
|
||||
report(_("fsmonitor enabled"));
|
||||
} else if (!fsmonitor) {
|
||||
if (git_config_get_fsmonitor() == 1)
|
||||
enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);
|
||||
if (fsm_mode > FSMONITOR_MODE_DISABLED)
|
||||
warning(_("core.fsmonitor is set; "
|
||||
"remove it if you really want to "
|
||||
"disable fsmonitor"));
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -335,6 +335,69 @@ 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) {
|
||||
char *from_file = git_pathdup("info/sparse-checkout");
|
||||
char *to_file = xstrfmt("%s/info/sparse-checkout",
|
||||
sb_repo.buf);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
char *from_file = git_pathdup("config.worktree");
|
||||
char *to_file = xstrfmt("%s/config.worktree",
|
||||
sb_repo.buf);
|
||||
|
||||
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 'core.bare' in '%s'"), 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 'core.worktree' in '%s'"), to_file);
|
||||
|
||||
git_configset_clear(&cs);
|
||||
}
|
||||
|
||||
worktree_copy_cleanup:
|
||||
free(from_file);
|
||||
free(to_file);
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
51
cache.h
51
cache.h
|
@ -999,7 +999,6 @@ extern int core_preload_index;
|
|||
extern int precomposed_unicode;
|
||||
extern int protect_hfs;
|
||||
extern int protect_ntfs;
|
||||
extern const char *core_fsmonitor;
|
||||
|
||||
extern int core_apply_sparse_checkout;
|
||||
extern int core_sparse_checkout_cone;
|
||||
|
@ -1558,48 +1557,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
|
||||
|
@ -1645,14 +1602,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.
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
#ifndef FSM_DARWIN_GCC_H
|
||||
#define FSM_DARWIN_GCC_H
|
||||
|
||||
#ifndef __clang__
|
||||
/*
|
||||
* It is possible to #include CoreFoundation/CoreFoundation.h when compiling
|
||||
* with clang, but not with GCC as of time of writing.
|
||||
*
|
||||
* See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93082 for details.
|
||||
*/
|
||||
typedef unsigned int FSEventStreamCreateFlags;
|
||||
#define kFSEventStreamEventFlagNone 0x00000000
|
||||
#define kFSEventStreamEventFlagMustScanSubDirs 0x00000001
|
||||
#define kFSEventStreamEventFlagUserDropped 0x00000002
|
||||
#define kFSEventStreamEventFlagKernelDropped 0x00000004
|
||||
#define kFSEventStreamEventFlagEventIdsWrapped 0x00000008
|
||||
#define kFSEventStreamEventFlagHistoryDone 0x00000010
|
||||
#define kFSEventStreamEventFlagRootChanged 0x00000020
|
||||
#define kFSEventStreamEventFlagMount 0x00000040
|
||||
#define kFSEventStreamEventFlagUnmount 0x00000080
|
||||
#define kFSEventStreamEventFlagItemCreated 0x00000100
|
||||
#define kFSEventStreamEventFlagItemRemoved 0x00000200
|
||||
#define kFSEventStreamEventFlagItemInodeMetaMod 0x00000400
|
||||
#define kFSEventStreamEventFlagItemRenamed 0x00000800
|
||||
#define kFSEventStreamEventFlagItemModified 0x00001000
|
||||
#define kFSEventStreamEventFlagItemFinderInfoMod 0x00002000
|
||||
#define kFSEventStreamEventFlagItemChangeOwner 0x00004000
|
||||
#define kFSEventStreamEventFlagItemXattrMod 0x00008000
|
||||
#define kFSEventStreamEventFlagItemIsFile 0x00010000
|
||||
#define kFSEventStreamEventFlagItemIsDir 0x00020000
|
||||
#define kFSEventStreamEventFlagItemIsSymlink 0x00040000
|
||||
#define kFSEventStreamEventFlagOwnEvent 0x00080000
|
||||
#define kFSEventStreamEventFlagItemIsHardlink 0x00100000
|
||||
#define kFSEventStreamEventFlagItemIsLastHardlink 0x00200000
|
||||
#define kFSEventStreamEventFlagItemCloned 0x00400000
|
||||
|
||||
typedef struct __FSEventStream *FSEventStreamRef;
|
||||
typedef const FSEventStreamRef ConstFSEventStreamRef;
|
||||
|
||||
typedef unsigned int CFStringEncoding;
|
||||
#define kCFStringEncodingUTF8 0x08000100
|
||||
|
||||
typedef const struct __CFString *CFStringRef;
|
||||
typedef const struct __CFArray *CFArrayRef;
|
||||
typedef const struct __CFRunLoop *CFRunLoopRef;
|
||||
|
||||
struct FSEventStreamContext {
|
||||
long long version;
|
||||
void *cb_data, *retain, *release, *copy_description;
|
||||
};
|
||||
|
||||
typedef struct FSEventStreamContext FSEventStreamContext;
|
||||
typedef unsigned int FSEventStreamEventFlags;
|
||||
#define kFSEventStreamCreateFlagNoDefer 0x02
|
||||
#define kFSEventStreamCreateFlagWatchRoot 0x04
|
||||
#define kFSEventStreamCreateFlagFileEvents 0x10
|
||||
|
||||
typedef unsigned long long FSEventStreamEventId;
|
||||
#define kFSEventStreamEventIdSinceNow 0xFFFFFFFFFFFFFFFFULL
|
||||
|
||||
typedef void (*FSEventStreamCallback)(ConstFSEventStreamRef streamRef,
|
||||
void *context,
|
||||
__SIZE_TYPE__ num_of_events,
|
||||
void *event_paths,
|
||||
const FSEventStreamEventFlags event_flags[],
|
||||
const FSEventStreamEventId event_ids[]);
|
||||
typedef double CFTimeInterval;
|
||||
FSEventStreamRef FSEventStreamCreate(void *allocator,
|
||||
FSEventStreamCallback callback,
|
||||
FSEventStreamContext *context,
|
||||
CFArrayRef paths_to_watch,
|
||||
FSEventStreamEventId since_when,
|
||||
CFTimeInterval latency,
|
||||
FSEventStreamCreateFlags flags);
|
||||
CFStringRef CFStringCreateWithCString(void *allocator, const char *string,
|
||||
CFStringEncoding encoding);
|
||||
CFArrayRef CFArrayCreate(void *allocator, const void **items, long long count,
|
||||
void *callbacks);
|
||||
void CFRunLoopRun(void);
|
||||
void CFRunLoopStop(CFRunLoopRef run_loop);
|
||||
CFRunLoopRef CFRunLoopGetCurrent(void);
|
||||
extern CFStringRef kCFRunLoopDefaultMode;
|
||||
void FSEventStreamScheduleWithRunLoop(FSEventStreamRef stream,
|
||||
CFRunLoopRef run_loop,
|
||||
CFStringRef run_loop_mode);
|
||||
unsigned char FSEventStreamStart(FSEventStreamRef stream);
|
||||
void FSEventStreamStop(FSEventStreamRef stream);
|
||||
void FSEventStreamInvalidate(FSEventStreamRef stream);
|
||||
void FSEventStreamRelease(FSEventStreamRef stream);
|
||||
|
||||
#endif /* !clang */
|
||||
#endif /* FSM_DARWIN_GCC_H */
|
|
@ -0,0 +1,427 @@
|
|||
#ifndef __clang__
|
||||
#include "fsm-darwin-gcc.h"
|
||||
#else
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <CoreServices/CoreServices.h>
|
||||
|
||||
#ifndef AVAILABLE_MAC_OS_X_VERSION_10_13_AND_LATER
|
||||
/*
|
||||
* This enum value was added in 10.13 to:
|
||||
*
|
||||
* /Applications/Xcode.app/Contents/Developer/Platforms/ \
|
||||
* MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/ \
|
||||
* Library/Frameworks/CoreServices.framework/Frameworks/ \
|
||||
* FSEvents.framework/Versions/Current/Headers/FSEvents.h
|
||||
*
|
||||
* If we're compiling against an older SDK, this symbol won't be
|
||||
* present. Silently define it here so that we don't have to ifdef
|
||||
* the logging or masking below. This should be harmless since older
|
||||
* versions of macOS won't ever emit this FS event anyway.
|
||||
*/
|
||||
#define kFSEventStreamEventFlagItemCloned 0x00400000
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "cache.h"
|
||||
#include "fsmonitor.h"
|
||||
#include "fsm-listen.h"
|
||||
#include "fsmonitor--daemon.h"
|
||||
|
||||
struct fsmonitor_daemon_backend_data
|
||||
{
|
||||
CFStringRef cfsr_worktree_path;
|
||||
CFStringRef cfsr_gitdir_path;
|
||||
|
||||
CFArrayRef cfar_paths_to_watch;
|
||||
int nr_paths_watching;
|
||||
|
||||
FSEventStreamRef stream;
|
||||
|
||||
CFRunLoopRef rl;
|
||||
|
||||
enum shutdown_style {
|
||||
SHUTDOWN_EVENT = 0,
|
||||
FORCE_SHUTDOWN,
|
||||
FORCE_ERROR_STOP,
|
||||
} shutdown_style;
|
||||
|
||||
unsigned int stream_scheduled:1;
|
||||
unsigned int stream_started:1;
|
||||
};
|
||||
|
||||
static void log_flags_set(const char *path, const FSEventStreamEventFlags flag)
|
||||
{
|
||||
struct strbuf msg = STRBUF_INIT;
|
||||
|
||||
if (flag & kFSEventStreamEventFlagMustScanSubDirs)
|
||||
strbuf_addstr(&msg, "MustScanSubDirs|");
|
||||
if (flag & kFSEventStreamEventFlagUserDropped)
|
||||
strbuf_addstr(&msg, "UserDropped|");
|
||||
if (flag & kFSEventStreamEventFlagKernelDropped)
|
||||
strbuf_addstr(&msg, "KernelDropped|");
|
||||
if (flag & kFSEventStreamEventFlagEventIdsWrapped)
|
||||
strbuf_addstr(&msg, "EventIdsWrapped|");
|
||||
if (flag & kFSEventStreamEventFlagHistoryDone)
|
||||
strbuf_addstr(&msg, "HistoryDone|");
|
||||
if (flag & kFSEventStreamEventFlagRootChanged)
|
||||
strbuf_addstr(&msg, "RootChanged|");
|
||||
if (flag & kFSEventStreamEventFlagMount)
|
||||
strbuf_addstr(&msg, "Mount|");
|
||||
if (flag & kFSEventStreamEventFlagUnmount)
|
||||
strbuf_addstr(&msg, "Unmount|");
|
||||
if (flag & kFSEventStreamEventFlagItemChangeOwner)
|
||||
strbuf_addstr(&msg, "ItemChangeOwner|");
|
||||
if (flag & kFSEventStreamEventFlagItemCreated)
|
||||
strbuf_addstr(&msg, "ItemCreated|");
|
||||
if (flag & kFSEventStreamEventFlagItemFinderInfoMod)
|
||||
strbuf_addstr(&msg, "ItemFinderInfoMod|");
|
||||
if (flag & kFSEventStreamEventFlagItemInodeMetaMod)
|
||||
strbuf_addstr(&msg, "ItemInodeMetaMod|");
|
||||
if (flag & kFSEventStreamEventFlagItemIsDir)
|
||||
strbuf_addstr(&msg, "ItemIsDir|");
|
||||
if (flag & kFSEventStreamEventFlagItemIsFile)
|
||||
strbuf_addstr(&msg, "ItemIsFile|");
|
||||
if (flag & kFSEventStreamEventFlagItemIsHardlink)
|
||||
strbuf_addstr(&msg, "ItemIsHardlink|");
|
||||
if (flag & kFSEventStreamEventFlagItemIsLastHardlink)
|
||||
strbuf_addstr(&msg, "ItemIsLastHardlink|");
|
||||
if (flag & kFSEventStreamEventFlagItemIsSymlink)
|
||||
strbuf_addstr(&msg, "ItemIsSymlink|");
|
||||
if (flag & kFSEventStreamEventFlagItemModified)
|
||||
strbuf_addstr(&msg, "ItemModified|");
|
||||
if (flag & kFSEventStreamEventFlagItemRemoved)
|
||||
strbuf_addstr(&msg, "ItemRemoved|");
|
||||
if (flag & kFSEventStreamEventFlagItemRenamed)
|
||||
strbuf_addstr(&msg, "ItemRenamed|");
|
||||
if (flag & kFSEventStreamEventFlagItemXattrMod)
|
||||
strbuf_addstr(&msg, "ItemXattrMod|");
|
||||
if (flag & kFSEventStreamEventFlagOwnEvent)
|
||||
strbuf_addstr(&msg, "OwnEvent|");
|
||||
if (flag & kFSEventStreamEventFlagItemCloned)
|
||||
strbuf_addstr(&msg, "ItemCloned|");
|
||||
|
||||
trace_printf_key(&trace_fsmonitor, "fsevent: '%s', flags=%u %s",
|
||||
path, flag, msg.buf);
|
||||
|
||||
strbuf_release(&msg);
|
||||
}
|
||||
|
||||
static int ef_is_root_delete(const FSEventStreamEventFlags ef)
|
||||
{
|
||||
return (ef & kFSEventStreamEventFlagItemIsDir &&
|
||||
ef & kFSEventStreamEventFlagItemRemoved);
|
||||
}
|
||||
|
||||
static int ef_is_root_renamed(const FSEventStreamEventFlags ef)
|
||||
{
|
||||
return (ef & kFSEventStreamEventFlagItemIsDir &&
|
||||
ef & kFSEventStreamEventFlagItemRenamed);
|
||||
}
|
||||
|
||||
static int ef_is_dropped(const FSEventStreamEventFlags ef)
|
||||
{
|
||||
return (ef & kFSEventStreamEventFlagMustScanSubDirs ||
|
||||
ef & kFSEventStreamEventFlagKernelDropped ||
|
||||
ef & kFSEventStreamEventFlagUserDropped);
|
||||
}
|
||||
|
||||
static void fsevent_callback(ConstFSEventStreamRef streamRef,
|
||||
void *ctx,
|
||||
size_t num_of_events,
|
||||
void *event_paths,
|
||||
const FSEventStreamEventFlags event_flags[],
|
||||
const FSEventStreamEventId event_ids[])
|
||||
{
|
||||
struct fsmonitor_daemon_state *state = ctx;
|
||||
struct fsmonitor_daemon_backend_data *data = state->backend_data;
|
||||
char **paths = (char **)event_paths;
|
||||
struct fsmonitor_batch *batch = NULL;
|
||||
struct string_list cookie_list = STRING_LIST_INIT_DUP;
|
||||
const char *path_k;
|
||||
const char *slash;
|
||||
int k;
|
||||
struct strbuf tmp = STRBUF_INIT;
|
||||
|
||||
/*
|
||||
* Build a list of all filesystem changes into a private/local
|
||||
* list and without holding any locks.
|
||||
*/
|
||||
for (k = 0; k < num_of_events; k++) {
|
||||
/*
|
||||
* On Mac, we receive an array of absolute paths.
|
||||
*/
|
||||
path_k = paths[k];
|
||||
|
||||
/*
|
||||
* If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.
|
||||
* Please don't log them to Trace2.
|
||||
*
|
||||
* trace_printf_key(&trace_fsmonitor, "Path: '%s'", path_k);
|
||||
*/
|
||||
|
||||
/*
|
||||
* If event[k] is marked as dropped, we assume that we have
|
||||
* lost sync with the filesystem and should flush our cached
|
||||
* data. We need to:
|
||||
*
|
||||
* [1] Abort/wake any client threads waiting for a cookie and
|
||||
* flush the cached state data (the current token), and
|
||||
* create a new token.
|
||||
*
|
||||
* [2] Discard the batch that we were locally building (since
|
||||
* they are conceptually relative to the just flushed
|
||||
* token).
|
||||
*/
|
||||
if (ef_is_dropped(event_flags[k])) {
|
||||
if (trace_pass_fl(&trace_fsmonitor))
|
||||
log_flags_set(path_k, event_flags[k]);
|
||||
|
||||
fsmonitor_force_resync(state);
|
||||
fsmonitor_batch__free_list(batch);
|
||||
string_list_clear(&cookie_list, 0);
|
||||
|
||||
/*
|
||||
* We assume that any events that we received
|
||||
* in this callback after this dropped event
|
||||
* may still be valid, so we continue rather
|
||||
* than break. (And just in case there is a
|
||||
* delete of ".git" hiding in there.)
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (fsmonitor_classify_path_absolute(state, path_k)) {
|
||||
|
||||
case IS_INSIDE_DOT_GIT_WITH_COOKIE_PREFIX:
|
||||
case IS_INSIDE_GITDIR_WITH_COOKIE_PREFIX:
|
||||
/* special case cookie files within .git or gitdir */
|
||||
|
||||
/* Use just the filename of the cookie file. */
|
||||
slash = find_last_dir_sep(path_k);
|
||||
string_list_append(&cookie_list,
|
||||
slash ? slash + 1 : path_k);
|
||||
break;
|
||||
|
||||
case IS_INSIDE_DOT_GIT:
|
||||
case IS_INSIDE_GITDIR:
|
||||
/* ignore all other paths inside of .git or gitdir */
|
||||
break;
|
||||
|
||||
case IS_DOT_GIT:
|
||||
case IS_GITDIR:
|
||||
/*
|
||||
* If .git directory is deleted or renamed away,
|
||||
* we have to quit.
|
||||
*/
|
||||
if (ef_is_root_delete(event_flags[k])) {
|
||||
trace_printf_key(&trace_fsmonitor,
|
||||
"event: gitdir removed");
|
||||
goto force_shutdown;
|
||||
}
|
||||
if (ef_is_root_renamed(event_flags[k])) {
|
||||
trace_printf_key(&trace_fsmonitor,
|
||||
"event: gitdir renamed");
|
||||
goto force_shutdown;
|
||||
}
|
||||
break;
|
||||
|
||||
case IS_WORKDIR_PATH:
|
||||
/* try to queue normal pathnames */
|
||||
|
||||
if (trace_pass_fl(&trace_fsmonitor))
|
||||
log_flags_set(path_k, event_flags[k]);
|
||||
|
||||
/*
|
||||
* Because of the implicit "binning" (the
|
||||
* kernel calls us at a given frequency) and
|
||||
* de-duping (the kernel is free to combine
|
||||
* multiple events for a given pathname), an
|
||||
* individual fsevent could be marked as both
|
||||
* a file and directory. Add it to the queue
|
||||
* with both spellings so that the client will
|
||||
* know how much to invalidate/refresh.
|
||||
*/
|
||||
|
||||
if (event_flags[k] & kFSEventStreamEventFlagItemIsFile) {
|
||||
const char *rel = path_k +
|
||||
state->path_worktree_watch.len + 1;
|
||||
|
||||
if (!batch)
|
||||
batch = fsmonitor_batch__new();
|
||||
fsmonitor_batch__add_path(batch, rel);
|
||||
}
|
||||
|
||||
if (event_flags[k] & kFSEventStreamEventFlagItemIsDir) {
|
||||
const char *rel = path_k +
|
||||
state->path_worktree_watch.len + 1;
|
||||
|
||||
strbuf_reset(&tmp);
|
||||
strbuf_addstr(&tmp, rel);
|
||||
strbuf_addch(&tmp, '/');
|
||||
|
||||
if (!batch)
|
||||
batch = fsmonitor_batch__new();
|
||||
fsmonitor_batch__add_path(batch, tmp.buf);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IS_OUTSIDE_CONE:
|
||||
default:
|
||||
trace_printf_key(&trace_fsmonitor,
|
||||
"ignoring '%s'", path_k);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fsmonitor_publish(state, batch, &cookie_list);
|
||||
string_list_clear(&cookie_list, 0);
|
||||
strbuf_release(&tmp);
|
||||
return;
|
||||
|
||||
force_shutdown:
|
||||
fsmonitor_batch__free_list(batch);
|
||||
string_list_clear(&cookie_list, 0);
|
||||
|
||||
data->shutdown_style = FORCE_SHUTDOWN;
|
||||
CFRunLoopStop(data->rl);
|
||||
strbuf_release(&tmp);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* In the call to `FSEventStreamCreate()` to setup our watch, the
|
||||
* `latency` argument determines the frequency of calls to our callback
|
||||
* with new FS events. Too slow and events get dropped; too fast and
|
||||
* we burn CPU unnecessarily. Since it is rather obscure, I don't
|
||||
* think this needs to be a config setting. I've done extensive
|
||||
* testing on my systems and chosen the value below. It gives good
|
||||
* results and I've not seen any dropped events.
|
||||
*
|
||||
* With a latency of 0.1, I was seeing lots of dropped events during
|
||||
* the "touch 100000" files test within t/perf/p7519, but with a
|
||||
* latency of 0.001 I did not see any dropped events. So I'm going
|
||||
* to assume that this is the "correct" value.
|
||||
*
|
||||
* https://developer.apple.com/documentation/coreservices/1443980-fseventstreamcreate
|
||||
*/
|
||||
|
||||
int fsm_listen__ctor(struct fsmonitor_daemon_state *state)
|
||||
{
|
||||
FSEventStreamCreateFlags flags = kFSEventStreamCreateFlagNoDefer |
|
||||
kFSEventStreamCreateFlagWatchRoot |
|
||||
kFSEventStreamCreateFlagFileEvents;
|
||||
FSEventStreamContext ctx = {
|
||||
0,
|
||||
state,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
struct fsmonitor_daemon_backend_data *data;
|
||||
const void *dir_array[2];
|
||||
|
||||
CALLOC_ARRAY(data, 1);
|
||||
state->backend_data = data;
|
||||
|
||||
data->cfsr_worktree_path = CFStringCreateWithCString(
|
||||
NULL, state->path_worktree_watch.buf, kCFStringEncodingUTF8);
|
||||
dir_array[data->nr_paths_watching++] = data->cfsr_worktree_path;
|
||||
|
||||
if (state->nr_paths_watching > 1) {
|
||||
data->cfsr_gitdir_path = CFStringCreateWithCString(
|
||||
NULL, state->path_gitdir_watch.buf,
|
||||
kCFStringEncodingUTF8);
|
||||
dir_array[data->nr_paths_watching++] = data->cfsr_gitdir_path;
|
||||
}
|
||||
|
||||
data->cfar_paths_to_watch = CFArrayCreate(NULL, dir_array,
|
||||
data->nr_paths_watching,
|
||||
NULL);
|
||||
data->stream = FSEventStreamCreate(NULL, fsevent_callback, &ctx,
|
||||
data->cfar_paths_to_watch,
|
||||
kFSEventStreamEventIdSinceNow,
|
||||
0.001, flags);
|
||||
if (data->stream == NULL)
|
||||
goto failed;
|
||||
|
||||
/*
|
||||
* `data->rl` needs to be set inside the listener thread.
|
||||
*/
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
error(_("Unable to create FSEventStream."));
|
||||
|
||||
FREE_AND_NULL(state->backend_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void fsm_listen__dtor(struct fsmonitor_daemon_state *state)
|
||||
{
|
||||
struct fsmonitor_daemon_backend_data *data;
|
||||
|
||||
if (!state || !state->backend_data)
|
||||
return;
|
||||
|
||||
data = state->backend_data;
|
||||
|
||||
if (data->stream) {
|
||||
if (data->stream_started)
|
||||
FSEventStreamStop(data->stream);
|
||||
if (data->stream_scheduled)
|
||||
FSEventStreamInvalidate(data->stream);
|
||||
FSEventStreamRelease(data->stream);
|
||||
}
|
||||
|
||||
FREE_AND_NULL(state->backend_data);
|
||||
}
|
||||
|
||||
void fsm_listen__stop_async(struct fsmonitor_daemon_state *state)
|
||||
{
|
||||
struct fsmonitor_daemon_backend_data *data;
|
||||
|
||||
data = state->backend_data;
|
||||
data->shutdown_style = SHUTDOWN_EVENT;
|
||||
|
||||
CFRunLoopStop(data->rl);
|
||||
}
|
||||
|
||||
void fsm_listen__loop(struct fsmonitor_daemon_state *state)
|
||||
{
|
||||
struct fsmonitor_daemon_backend_data *data;
|
||||
|
||||
data = state->backend_data;
|
||||
|
||||
data->rl = CFRunLoopGetCurrent();
|
||||
|
||||
FSEventStreamScheduleWithRunLoop(data->stream, data->rl, kCFRunLoopDefaultMode);
|
||||
data->stream_scheduled = 1;
|
||||
|
||||
if (!FSEventStreamStart(data->stream)) {
|
||||
error(_("Failed to start the FSEventStream"));
|
||||
goto force_error_stop_without_loop;
|
||||
}
|
||||
data->stream_started = 1;
|
||||
|
||||
CFRunLoopRun();
|
||||
|
||||
switch (data->shutdown_style) {
|
||||
case FORCE_ERROR_STOP:
|
||||
state->error_code = -1;
|
||||
/* fall thru */
|
||||
case FORCE_SHUTDOWN:
|
||||
ipc_server_stop_async(state->ipc_server_data);
|
||||
/* fall thru */
|
||||
case SHUTDOWN_EVENT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
||||
force_error_stop_without_loop:
|
||||
state->error_code = -1;
|
||||
ipc_server_stop_async(state->ipc_server_data);
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,586 @@
|
|||
#include "cache.h"
|
||||
#include "config.h"
|
||||
#include "fsmonitor.h"
|
||||
#include "fsm-listen.h"
|
||||
#include "fsmonitor--daemon.h"
|
||||
|
||||
/*
|
||||
* The documentation of ReadDirectoryChangesW() states that the maximum
|
||||
* buffer size is 64K when the monitored directory is remote.
|
||||
*
|
||||
* Larger buffers may be used when the monitored directory is local and
|
||||
* will help us receive events faster from the kernel and avoid dropped
|
||||
* events.
|
||||
*
|
||||
* So we try to use a very large buffer and silently fallback to 64K if
|
||||
* we get an error.
|
||||
*/
|
||||
#define MAX_RDCW_BUF_FALLBACK (65536)
|
||||
#define MAX_RDCW_BUF (65536 * 8)
|
||||
|
||||
struct one_watch
|
||||
{
|
||||
char buffer[MAX_RDCW_BUF];
|
||||
DWORD buf_len;
|
||||
DWORD count;
|
||||
|
||||
struct strbuf path;
|
||||
HANDLE hDir;
|
||||
HANDLE hEvent;
|
||||
OVERLAPPED overlapped;
|
||||
|
||||
/*
|
||||
* Is there an active ReadDirectoryChangesW() call pending. If so, we
|
||||
* need to later call GetOverlappedResult() and possibly CancelIoEx().
|
||||
*/
|
||||
BOOL is_active;
|
||||
};
|
||||
|
||||
struct fsmonitor_daemon_backend_data
|
||||
{
|
||||
struct one_watch *watch_worktree;
|
||||
struct one_watch *watch_gitdir;
|
||||
|
||||
HANDLE hEventShutdown;
|
||||
|
||||
HANDLE hListener[3]; /* we don't own these handles */
|
||||
#define LISTENER_SHUTDOWN 0
|
||||
#define LISTENER_HAVE_DATA_WORKTREE 1
|
||||
#define LISTENER_HAVE_DATA_GITDIR 2
|
||||
int nr_listener_handles;
|
||||
};
|
||||
|
||||
/*
|
||||
* Convert the WCHAR path from the notification into UTF8 and
|
||||
* then normalize it.
|
||||
*/
|
||||
static int normalize_path_in_utf8(FILE_NOTIFY_INFORMATION *info,
|
||||
struct strbuf *normalized_path)
|
||||
{
|
||||
int reserve;
|
||||
int len = 0;
|
||||
|
||||
strbuf_reset(normalized_path);
|
||||
if (!info->FileNameLength)
|
||||
goto normalize;
|
||||
|
||||
/*
|
||||
* Pre-reserve enough space in the UTF8 buffer for
|
||||
* each Unicode WCHAR character to be mapped into a
|
||||
* sequence of 2 UTF8 characters. That should let us
|
||||
* avoid ERROR_INSUFFICIENT_BUFFER 99.9+% of the time.
|
||||
*/
|
||||
reserve = info->FileNameLength + 1;
|
||||
strbuf_grow(normalized_path, reserve);
|
||||
|
||||
for (;;) {
|
||||
len = WideCharToMultiByte(CP_UTF8, 0, info->FileName,
|
||||
info->FileNameLength / sizeof(WCHAR),
|
||||
normalized_path->buf,
|
||||
strbuf_avail(normalized_path) - 1,
|
||||
NULL, NULL);
|
||||
if (len > 0)
|
||||
goto normalize;
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||
error(_("[GLE %ld] could not convert path to UTF-8: '%.*ls'"),
|
||||
GetLastError(),
|
||||
(int)(info->FileNameLength / sizeof(WCHAR)),
|
||||
info->FileName);
|
||||
return -1;
|
||||
}
|
||||
|
||||
strbuf_grow(normalized_path,
|
||||
strbuf_avail(normalized_path) + reserve);
|
||||
}
|
||||
|
||||
normalize:
|
||||
strbuf_setlen(normalized_path, len);
|
||||
return strbuf_normalize_path(normalized_path);
|
||||
}
|
||||
|
||||
void fsm_listen__stop_async(struct fsmonitor_daemon_state *state)
|
||||
{
|
||||
SetEvent(state->backend_data->hListener[LISTENER_SHUTDOWN]);
|
||||
}
|
||||
|
||||
static struct one_watch *create_watch(struct fsmonitor_daemon_state *state,
|
||||
const char *path)
|
||||
{
|
||||
struct one_watch *watch = NULL;
|
||||
DWORD desired_access = FILE_LIST_DIRECTORY;
|
||||
DWORD share_mode =
|
||||
FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE;
|
||||
HANDLE hDir;
|
||||
wchar_t wpath[MAX_PATH];
|
||||
|
||||
if (xutftowcs_path(wpath, path) < 0) {
|
||||
error(_("could not convert to wide characters: '%s'"), path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hDir = CreateFileW(wpath,
|
||||
desired_access, share_mode, NULL, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
|
||||
NULL);
|
||||
if (hDir == INVALID_HANDLE_VALUE) {
|
||||
error(_("[GLE %ld] could not watch '%s'"),
|
||||
GetLastError(), path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CALLOC_ARRAY(watch, 1);
|
||||
|
||||
watch->buf_len = sizeof(watch->buffer); /* assume full MAX_RDCW_BUF */
|
||||
|
||||
strbuf_init(&watch->path, 0);
|
||||
strbuf_addstr(&watch->path, path);
|
||||
|
||||
watch->hDir = hDir;
|
||||
watch->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
return watch;
|
||||
}
|
||||
|
||||
static void destroy_watch(struct one_watch *watch)
|
||||
{
|
||||
if (!watch)
|
||||
return;
|
||||
|
||||
strbuf_release(&watch->path);
|
||||
if (watch->hDir != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(watch->hDir);
|
||||
if (watch->hEvent != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(watch->hEvent);
|
||||
|
||||
free(watch);
|
||||
}
|
||||
|
||||
static int start_rdcw_watch(struct fsmonitor_daemon_backend_data *data,
|
||||
struct one_watch *watch)
|
||||
{
|
||||
DWORD dwNotifyFilter =
|
||||
FILE_NOTIFY_CHANGE_FILE_NAME |
|
||||
FILE_NOTIFY_CHANGE_DIR_NAME |
|
||||
FILE_NOTIFY_CHANGE_ATTRIBUTES |
|
||||
FILE_NOTIFY_CHANGE_SIZE |
|
||||
FILE_NOTIFY_CHANGE_LAST_WRITE |
|
||||
FILE_NOTIFY_CHANGE_CREATION;
|
||||
|
||||
ResetEvent(watch->hEvent);
|
||||
|
||||
memset(&watch->overlapped, 0, sizeof(watch->overlapped));
|
||||
watch->overlapped.hEvent = watch->hEvent;
|
||||
|
||||
/*
|
||||
* Queue an async call using Overlapped IO. This returns immediately.
|
||||
* Our event handle will be signalled when the real result is available.
|
||||
*
|
||||
* The return value here just means that we successfully queued it.
|
||||
* We won't know if the Read...() actually produces data until later.
|
||||
*/
|
||||
watch->is_active = ReadDirectoryChangesW(
|
||||
watch->hDir, watch->buffer, watch->buf_len, TRUE,
|
||||
dwNotifyFilter, &watch->count, &watch->overlapped, NULL);
|
||||
|
||||
if (watch->is_active)
|
||||
return 0;
|
||||
|
||||
error(_("ReadDirectoryChangedW failed on '%s' [GLE %ld]"),
|
||||
watch->path.buf, GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int recv_rdcw_watch(struct one_watch *watch)
|
||||
{
|
||||
DWORD gle;
|
||||
|
||||
watch->is_active = FALSE;
|
||||
|
||||
/*
|
||||
* The overlapped result is ready. If the Read...() was successful
|
||||
* we finally receive the actual result into our buffer.
|
||||
*/
|
||||
if (GetOverlappedResult(watch->hDir, &watch->overlapped, &watch->count,
|
||||
TRUE))
|
||||
return 0;
|
||||
|
||||
gle = GetLastError();
|
||||
if (gle == ERROR_INVALID_PARAMETER &&
|
||||
/*
|
||||
* The kernel throws an invalid parameter error when our
|
||||
* buffer is too big and we are pointed at a remote
|
||||
* directory (and possibly for other reasons). Quietly
|
||||
* set it down and try again.
|
||||
*
|
||||
* See note about MAX_RDCW_BUF at the top.
|
||||
*/
|
||||
watch->buf_len > MAX_RDCW_BUF_FALLBACK) {
|
||||
watch->buf_len = MAX_RDCW_BUF_FALLBACK;
|
||||
return -2;
|
||||
}
|
||||
|
||||
/*
|
||||
* NEEDSWORK: If an external <gitdir> is deleted, the above
|
||||
* returns an error. I'm not sure that there's anything that
|
||||
* we can do here other than failing -- the <worktree>/.git
|
||||
* link file would be broken anyway. We might try to check
|
||||
* for that and return a better error message, but I'm not
|
||||
* sure it is worth it.
|
||||
*/
|
||||
|
||||
error(_("GetOverlappedResult failed on '%s' [GLE %ld]"),
|
||||
watch->path.buf, gle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void cancel_rdcw_watch(struct one_watch *watch)
|
||||
{
|
||||
DWORD count;
|
||||
|
||||
if (!watch || !watch->is_active)
|
||||
return;
|
||||
|
||||
/*
|
||||
* The calls to ReadDirectoryChangesW() and GetOverlappedResult()
|
||||
* form a "pair" (my term) where we queue an IO and promise to
|
||||
* hang around and wait for the kernel to give us the result.
|
||||
*
|
||||
* If for some reason after we queue the IO, we have to quit
|
||||
* or otherwise not stick around for the second half, we must
|
||||
* tell the kernel to abort the IO. This prevents the kernel
|
||||
* from writing to our buffer and/or signalling our event
|
||||
* after we free them.
|
||||
*
|
||||
* (Ask me how much fun it was to track that one down).
|
||||
*/
|
||||
CancelIoEx(watch->hDir, &watch->overlapped);
|
||||
GetOverlappedResult(watch->hDir, &watch->overlapped, &count, TRUE);
|
||||
watch->is_active = FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process filesystem events that happen anywhere (recursively) under the
|
||||
* <worktree> root directory. For a normal working directory, this includes
|
||||
* both version controlled files and the contents of the .git/ directory.
|
||||
*
|
||||
* If <worktree>/.git is a file, then we only see events for the file
|
||||
* itself.
|
||||
*/
|
||||
static int process_worktree_events(struct fsmonitor_daemon_state *state)
|
||||
{
|
||||
struct fsmonitor_daemon_backend_data *data = state->backend_data;
|
||||
struct one_watch *watch = data->watch_worktree;
|
||||
struct strbuf path = STRBUF_INIT;
|
||||
struct string_list cookie_list = STRING_LIST_INIT_DUP;
|
||||
struct fsmonitor_batch *batch = NULL;
|
||||
const char *p = watch->buffer;
|
||||
|
||||
/*
|
||||
* If the kernel gets more events than will fit in the kernel
|
||||
* buffer associated with our RDCW handle, it drops them and
|
||||
* returns a count of zero.
|
||||
*
|
||||
* Yes, the call returns WITHOUT error and with length zero.
|
||||
* This is the documented behavior. (My testing has confirmed
|
||||
* that it also sets the last error to ERROR_NOTIFY_ENUM_DIR,
|
||||
* but we do not rely on that since the function did not
|
||||
* return an error and it is not documented.)
|
||||
*
|
||||
* (The "overflow" case is not ambiguous with the "no data" case
|
||||
* because we did an INFINITE wait.)
|
||||
*
|
||||
* This means we have a gap in coverage. Tell the daemon layer
|
||||
* to resync.
|
||||
*/
|
||||
if (!watch->count) {
|
||||
trace2_data_string("fsmonitor", NULL, "fsm-listen/kernel",
|
||||
"overflow");
|
||||
fsmonitor_force_resync(state);
|
||||
return LISTENER_HAVE_DATA_WORKTREE;
|
||||
}
|
||||
|
||||
/*
|
||||
* On Windows, `info` contains an "array" of paths that are
|
||||
* relative to the root of whichever directory handle received
|
||||
* the event.
|
||||
*/
|
||||
for (;;) {
|
||||
FILE_NOTIFY_INFORMATION *info = (void *)p;
|
||||
const char *slash;
|
||||
enum fsmonitor_path_type t;
|
||||
|
||||
strbuf_reset(&path);
|
||||
if (normalize_path_in_utf8(info, &path) == -1)
|
||||
goto skip_this_path;
|
||||
|
||||
t = fsmonitor_classify_path_workdir_relative(path.buf);
|
||||
|
||||
switch (t) {
|
||||
case IS_INSIDE_DOT_GIT_WITH_COOKIE_PREFIX:
|
||||
/* special case cookie files within .git */
|
||||
|
||||
/* Use just the filename of the cookie file. */
|
||||
slash = find_last_dir_sep(path.buf);
|
||||
string_list_append(&cookie_list,
|
||||
slash ? slash + 1 : path.buf);
|
||||
break;
|
||||
|
||||
case IS_INSIDE_DOT_GIT:
|
||||
/* ignore everything inside of "<worktree>/.git/" */
|
||||
break;
|
||||
|
||||
case IS_DOT_GIT:
|
||||
/* "<worktree>/.git" was deleted (or renamed away) */
|
||||
if ((info->Action == FILE_ACTION_REMOVED) ||
|
||||
(info->Action == FILE_ACTION_RENAMED_OLD_NAME)) {
|
||||
trace2_data_string("fsmonitor", NULL,
|
||||
"fsm-listen/dotgit",
|
||||
"removed");
|
||||
goto force_shutdown;
|
||||
}
|
||||
break;
|
||||
|
||||
case IS_WORKDIR_PATH:
|
||||
/* queue normal pathname */
|
||||
if (!batch)
|
||||
batch = fsmonitor_batch__new();
|
||||
fsmonitor_batch__add_path(batch, path.buf);
|
||||
break;
|
||||
|
||||
case IS_GITDIR:
|
||||
case IS_INSIDE_GITDIR:
|
||||
case IS_INSIDE_GITDIR_WITH_COOKIE_PREFIX:
|
||||
default:
|
||||
BUG("unexpected path classification '%d' for '%s'",
|
||||
t, path.buf);
|
||||
}
|
||||
|
||||
skip_this_path:
|
||||
if (!info->NextEntryOffset)
|
||||
break;
|
||||
p += info->NextEntryOffset;
|
||||
}
|
||||
|
||||
fsmonitor_publish(state, batch, &cookie_list);
|
||||
batch = NULL;
|
||||
string_list_clear(&cookie_list, 0);
|
||||
strbuf_release(&path);
|
||||
return LISTENER_HAVE_DATA_WORKTREE;
|
||||
|
||||
force_shutdown:
|
||||
fsmonitor_batch__free_list(batch);
|
||||
string_list_clear(&cookie_list, 0);
|
||||
strbuf_release(&path);
|
||||
return LISTENER_SHUTDOWN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process filesystem events that happened anywhere (recursively) under the
|
||||
* external <gitdir> (such as non-primary worktrees or submodules).
|
||||
* We only care about cookie files that our client threads created here.
|
||||
*
|
||||
* Note that we DO NOT get filesystem events on the external <gitdir>
|
||||
* itself (it is not inside something that we are watching). In particular,
|
||||
* we do not get an event if the external <gitdir> is deleted.
|
||||
*/
|
||||
static int process_gitdir_events(struct fsmonitor_daemon_state *state)
|
||||
{
|
||||
struct fsmonitor_daemon_backend_data *data = state->backend_data;
|
||||
struct one_watch *watch = data->watch_gitdir;
|
||||
struct strbuf path = STRBUF_INIT;
|
||||
struct string_list cookie_list = STRING_LIST_INIT_DUP;
|
||||
const char *p = watch->buffer;
|
||||
|
||||
if (!watch->count) {
|
||||
trace2_data_string("fsmonitor", NULL, "fsm-listen/kernel",
|
||||
"overflow");
|
||||
fsmonitor_force_resync(state);
|
||||
return LISTENER_HAVE_DATA_GITDIR;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
FILE_NOTIFY_INFORMATION *info = (void *)p;
|
||||
const char *slash;
|
||||
enum fsmonitor_path_type t;
|
||||
|
||||
strbuf_reset(&path);
|
||||
if (normalize_path_in_utf8(info, &path) == -1)
|
||||
goto skip_this_path;
|
||||
|
||||
t = fsmonitor_classify_path_gitdir_relative(path.buf);
|
||||
|
||||
switch (t) {
|
||||
case IS_INSIDE_GITDIR_WITH_COOKIE_PREFIX:
|
||||
/* special case cookie files within gitdir */
|
||||
|
||||
/* Use just the filename of the cookie file. */
|
||||
slash = find_last_dir_sep(path.buf);
|
||||
string_list_append(&cookie_list,
|
||||
slash ? slash + 1 : path.buf);
|
||||
break;
|
||||
|
||||
case IS_INSIDE_GITDIR:
|
||||
goto skip_this_path;
|
||||
|
||||
default:
|
||||
BUG("unexpected path classification '%d' for '%s'",
|
||||
t, path.buf);
|
||||
}
|
||||
|
||||
skip_this_path:
|
||||
if (!info->NextEntryOffset)
|
||||
break;
|
||||
p += info->NextEntryOffset;
|
||||
}
|
||||
|
||||
fsmonitor_publish(state, NULL, &cookie_list);
|
||||
string_list_clear(&cookie_list, 0);
|
||||
strbuf_release(&path);
|
||||
return LISTENER_HAVE_DATA_GITDIR;
|
||||
}
|
||||
|
||||
void fsm_listen__loop(struct fsmonitor_daemon_state *state)
|
||||
{
|
||||
struct fsmonitor_daemon_backend_data *data = state->backend_data;
|
||||
DWORD dwWait;
|
||||
int result;
|
||||
|
||||
state->error_code = 0;
|
||||
|
||||
if (start_rdcw_watch(data, data->watch_worktree) == -1)
|
||||
goto force_error_stop;
|
||||
|
||||
if (data->watch_gitdir &&
|
||||
start_rdcw_watch(data, data->watch_gitdir) == -1)
|
||||
goto force_error_stop;
|
||||
|
||||
for (;;) {
|
||||
dwWait = WaitForMultipleObjects(data->nr_listener_handles,
|
||||
data->hListener,
|
||||
FALSE, INFINITE);
|
||||
|
||||
if (dwWait == WAIT_OBJECT_0 + LISTENER_HAVE_DATA_WORKTREE) {
|
||||
result = recv_rdcw_watch(data->watch_worktree);
|
||||
if (result == -1) {
|
||||
/* hard error */
|
||||
goto force_error_stop;
|
||||
}
|
||||
if (result == -2) {
|
||||
/* retryable error */
|
||||
if (start_rdcw_watch(data, data->watch_worktree) == -1)
|
||||
goto force_error_stop;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* have data */
|
||||
if (process_worktree_events(state) == LISTENER_SHUTDOWN)
|
||||
goto force_shutdown;
|
||||
if (start_rdcw_watch(data, data->watch_worktree) == -1)
|
||||
goto force_error_stop;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dwWait == WAIT_OBJECT_0 + LISTENER_HAVE_DATA_GITDIR) {
|
||||
result = recv_rdcw_watch(data->watch_gitdir);
|
||||
if (result == -1) {
|
||||
/* hard error */
|
||||
goto force_error_stop;
|
||||
}
|
||||
if (result == -2) {
|
||||
/* retryable error */
|
||||
if (start_rdcw_watch(data, data->watch_gitdir) == -1)
|
||||
goto force_error_stop;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* have data */
|
||||
if (process_gitdir_events(state) == LISTENER_SHUTDOWN)
|
||||
goto force_shutdown;
|
||||
if (start_rdcw_watch(data, data->watch_gitdir) == -1)
|
||||
goto force_error_stop;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dwWait == WAIT_OBJECT_0 + LISTENER_SHUTDOWN)
|
||||
goto clean_shutdown;
|
||||
|
||||
error(_("could not read directory changes [GLE %ld]"),
|
||||
GetLastError());
|
||||
goto force_error_stop;
|
||||
}
|
||||
|
||||
force_error_stop:
|
||||
state->error_code = -1;
|
||||
|
||||
force_shutdown:
|
||||
/*
|
||||
* Tell the IPC thead pool to stop (which completes the await
|
||||
* in the main thread (which will also signal this thread (if
|
||||
* we are still alive))).
|
||||
*/
|
||||
ipc_server_stop_async(state->ipc_server_data);
|
||||
|
||||
clean_shutdown:
|
||||
cancel_rdcw_watch(data->watch_worktree);
|
||||
cancel_rdcw_watch(data->watch_gitdir);
|
||||
}
|
||||
|
||||
int fsm_listen__ctor(struct fsmonitor_daemon_state *state)
|
||||
{
|
||||
struct fsmonitor_daemon_backend_data *data;
|
||||
|
||||
CALLOC_ARRAY(data, 1);
|
||||
|
||||
data->hEventShutdown = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
data->watch_worktree = create_watch(state,
|
||||
state->path_worktree_watch.buf);
|
||||
if (!data->watch_worktree)
|
||||
goto failed;
|
||||
|
||||
if (state->nr_paths_watching > 1) {
|
||||
data->watch_gitdir = create_watch(state,
|
||||
state->path_gitdir_watch.buf);
|
||||
if (!data->watch_gitdir)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
data->hListener[LISTENER_SHUTDOWN] = data->hEventShutdown;
|
||||
data->nr_listener_handles++;
|
||||
|
||||
data->hListener[LISTENER_HAVE_DATA_WORKTREE] =
|
||||
data->watch_worktree->hEvent;
|
||||
data->nr_listener_handles++;
|
||||
|
||||
if (data->watch_gitdir) {
|
||||
data->hListener[LISTENER_HAVE_DATA_GITDIR] =
|
||||
data->watch_gitdir->hEvent;
|
||||
data->nr_listener_handles++;
|
||||
}
|
||||
|
||||
state->backend_data = data;
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
CloseHandle(data->hEventShutdown);
|
||||
destroy_watch(data->watch_worktree);
|
||||
destroy_watch(data->watch_gitdir);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void fsm_listen__dtor(struct fsmonitor_daemon_state *state)
|
||||
{
|
||||
struct fsmonitor_daemon_backend_data *data;
|
||||
|
||||
if (!state || !state->backend_data)
|
||||
return;
|
||||
|
||||
data = state->backend_data;
|
||||
|
||||
CloseHandle(data->hEventShutdown);
|
||||
destroy_watch(data->watch_worktree);
|
||||
destroy_watch(data->watch_gitdir);
|
||||
|
||||
FREE_AND_NULL(state->backend_data);
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
#ifndef FSM_LISTEN_H
|
||||
#define FSM_LISTEN_H
|
||||
|
||||
/* This needs to be implemented by each backend */
|
||||
|
||||
#ifdef HAVE_FSMONITOR_DAEMON_BACKEND
|
||||
|
||||
struct fsmonitor_daemon_state;
|
||||
|
||||
/*
|
||||
* Initialize platform-specific data for the fsmonitor listener thread.
|
||||
* This will be called from the main thread PRIOR to staring the
|
||||
* fsmonitor_fs_listener thread.
|
||||
*
|
||||
* Returns 0 if successful.
|
||||
* Returns -1 otherwise.
|
||||
*/
|
||||
int fsm_listen__ctor(struct fsmonitor_daemon_state *state);
|
||||
|
||||
/*
|
||||
* Cleanup platform-specific data for the fsmonitor listener thread.
|
||||
* This will be called from the main thread AFTER joining the listener.
|
||||
*/
|
||||
void fsm_listen__dtor(struct fsmonitor_daemon_state *state);
|
||||
|
||||
/*
|
||||
* The main body of the platform-specific event loop to watch for
|
||||
* filesystem events. This will run in the fsmonitor_fs_listen thread.
|
||||
*
|
||||
* It should call `ipc_server_stop_async()` if the listener thread
|
||||
* prematurely terminates (because of a filesystem error or if it
|
||||
* detects that the .git directory has been deleted). (It should NOT
|
||||
* do so if the listener thread receives a normal shutdown signal from
|
||||
* the IPC layer.)
|
||||
*
|
||||
* It should set `state->error_code` to -1 if the daemon should exit
|
||||
* with an error.
|
||||
*/
|
||||
void fsm_listen__loop(struct fsmonitor_daemon_state *state);
|
||||
|
||||
/*
|
||||
* Gently request that the fsmonitor listener thread shutdown.
|
||||
* It does not wait for it to stop. The caller should do a JOIN
|
||||
* to wait for it.
|
||||
*/
|
||||
void fsm_listen__stop_async(struct fsmonitor_daemon_state *state);
|
||||
|
||||
#endif /* HAVE_FSMONITOR_DAEMON_BACKEND */
|
||||
#endif /* FSM_LISTEN_H */
|
54
config.c
54
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)) {
|
||||
|
@ -2624,20 +2626,6 @@ int git_config_get_max_percent_split_change(void)
|
|||
return -1; /* default value */
|
||||
}
|
||||
|
||||
int git_config_get_fsmonitor(void)
|
||||
{
|
||||
if (git_config_get_pathname("core.fsmonitor", &core_fsmonitor))
|
||||
core_fsmonitor = getenv("GIT_TEST_FSMONITOR");
|
||||
|
||||
if (core_fsmonitor && !*core_fsmonitor)
|
||||
core_fsmonitor = NULL;
|
||||
|
||||
if (core_fsmonitor)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_config_get_index_threads(int *dest)
|
||||
{
|
||||
int is_bool, val;
|
||||
|
@ -3000,6 +2988,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 +3299,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);
|
||||
}
|
||||
|
||||
|
|
10
config.h
10
config.h
|
@ -266,6 +266,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.
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -590,7 +597,6 @@ int git_config_get_pathname(const char *key, const char **dest);
|
|||
int git_config_get_index_threads(int *dest);
|
||||
int git_config_get_split_index(void);
|
||||
int git_config_get_max_percent_split_change(void);
|
||||
int git_config_get_fsmonitor(void);
|
||||
|
||||
/* This dies if the configured or default date is in the future */
|
||||
int git_config_get_expiry(const char *key, const char **output);
|
||||
|
|
|
@ -157,6 +157,16 @@ ifeq ($(uname_S),Darwin)
|
|||
MSGFMT = /usr/local/opt/gettext/bin/msgfmt
|
||||
endif
|
||||
endif
|
||||
|
||||
# The builtin FSMonitor on MacOS builds upon Simple-IPC. Both require
|
||||
# Unix domain sockets and PThreads.
|
||||
ifndef NO_PTHREADS
|
||||
ifndef NO_UNIX_SOCKETS
|
||||
FSMONITOR_DAEMON_BACKEND = darwin
|
||||
endif
|
||||
endif
|
||||
|
||||
BASIC_LDFLAGS += -framework CoreServices
|
||||
endif
|
||||
ifeq ($(uname_S),SunOS)
|
||||
NEEDS_SOCKET = YesPlease
|
||||
|
@ -435,6 +445,11 @@ ifeq ($(uname_S),Windows)
|
|||
# so we don't need this:
|
||||
#
|
||||
# SNPRINTF_RETURNS_BOGUS = YesPlease
|
||||
|
||||
# The builtin FSMonitor requires Named Pipes and Threads on Windows.
|
||||
# These are always available, so we do not have to conditionally
|
||||
# support it.
|
||||
FSMONITOR_DAEMON_BACKEND = win32
|
||||
NO_SVN_TESTS = YesPlease
|
||||
RUNTIME_PREFIX = YesPlease
|
||||
HAVE_WPGMPTR = YesWeDo
|
||||
|
@ -619,6 +634,11 @@ ifeq ($(uname_S),MINGW)
|
|||
NO_STRTOUMAX = YesPlease
|
||||
NO_MKDTEMP = YesPlease
|
||||
NO_SVN_TESTS = YesPlease
|
||||
|
||||
# The builtin FSMonitor requires Named Pipes and Threads on Windows.
|
||||
# These are always available, so we do not have to conditionally
|
||||
# support it.
|
||||
FSMONITOR_DAEMON_BACKEND = win32
|
||||
RUNTIME_PREFIX = YesPlease
|
||||
HAVE_WPGMPTR = YesWeDo
|
||||
NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
|
||||
|
|
|
@ -379,7 +379,7 @@ struct ref **get_remote_heads(struct packet_reader *reader,
|
|||
|
||||
/* Returns 1 when a valid ref has been added to `list`, 0 otherwise */
|
||||
static int process_ref_v2(struct packet_reader *reader, struct ref ***list,
|
||||
char **unborn_head_target)
|
||||
const char **unborn_head_target)
|
||||
{
|
||||
int ret = 1;
|
||||
int i = 0;
|
||||
|
@ -483,7 +483,7 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
|
|||
const char *hash_name;
|
||||
struct strvec *ref_prefixes = transport_options ?
|
||||
&transport_options->ref_prefixes : NULL;
|
||||
char **unborn_head_target = transport_options ?
|
||||
const char **unborn_head_target = transport_options ?
|
||||
&transport_options->unborn_head_target : NULL;
|
||||
*list = NULL;
|
||||
|
||||
|
|
|
@ -285,6 +285,16 @@ else()
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(SUPPORTS_SIMPLE_IPC)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
add_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)
|
||||
list(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
add_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)
|
||||
list(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(EXE_EXTENSION ${CMAKE_EXECUTABLE_SUFFIX})
|
||||
|
||||
#header checks
|
||||
|
|
|
@ -49,6 +49,11 @@
|
|||
# and git-switch completion (e.g., completing "foo" when "origin/foo"
|
||||
# exists).
|
||||
#
|
||||
# GIT_COMPLETION_SHOW_ALL_COMMANDS
|
||||
#
|
||||
# When set to "1" suggest all commands, including plumbing commands
|
||||
# which are hidden by default (e.g. "cat-file" on "git ca<TAB>").
|
||||
#
|
||||
# GIT_COMPLETION_SHOW_ALL
|
||||
#
|
||||
# When set to "1" suggest all options, including options which are
|
||||
|
@ -3483,7 +3488,13 @@ __git_main ()
|
|||
then
|
||||
__gitcomp "$GIT_TESTING_PORCELAIN_COMMAND_LIST"
|
||||
else
|
||||
__gitcomp "$(__git --list-cmds=list-mainporcelain,others,nohelpers,alias,list-complete,config)"
|
||||
local list_cmds=list-mainporcelain,others,nohelpers,alias,list-complete,config
|
||||
|
||||
if test "${GIT_COMPLETION_SHOW_ALL_COMMANDS-}" = "1"
|
||||
then
|
||||
list_cmds=builtins,$list_cmds
|
||||
fi
|
||||
__gitcomp "$(__git --list-cmds=$list_cmds)"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
|
|
@ -808,6 +808,25 @@ int cmd_main(int argc, const char **argv)
|
|||
struct strbuf scalar_usage = STRBUF_INIT;
|
||||
int i;
|
||||
|
||||
while (argc > 1 && *argv[1] == '-') {
|
||||
if (!strcmp(argv[1], "-C")) {
|
||||
if (argc < 3)
|
||||
die(_("-C requires a <directory>"));
|
||||
if (chdir(argv[2]) < 0)
|
||||
die_errno(_("could not change to '%s'"),
|
||||
argv[2]);
|
||||
argc -= 2;
|
||||
argv += 2;
|
||||
} else if (!strcmp(argv[1], "-c")) {
|
||||
if (argc < 3)
|
||||
die(_("-c requires a <key>=<value> argument"));
|
||||
git_config_push_parameter(argv[2]);
|
||||
argc -= 2;
|
||||
argv += 2;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (argc > 1) {
|
||||
argv++;
|
||||
argc--;
|
||||
|
@ -818,7 +837,8 @@ int cmd_main(int argc, const char **argv)
|
|||
}
|
||||
|
||||
strbuf_addstr(&scalar_usage,
|
||||
N_("scalar <command> [<options>]\n\nCommands:\n"));
|
||||
N_("scalar [-C <directory>] [-c <key>=<value>] "
|
||||
"<command> [<options>]\n\nCommands:\n"));
|
||||
for (i = 0; builtins[i].name; i++)
|
||||
strbuf_addf(&scalar_usage, "\t%s\n", builtins[i].name);
|
||||
|
||||
|
|
|
@ -36,6 +36,16 @@ The `scalar` command implements various subcommands, and different options
|
|||
depending on the subcommand. With the exception of `clone`, `list` and
|
||||
`reconfigure --all`, all subcommands expect to be run in an enlistment.
|
||||
|
||||
The following options can be specified _before_ the subcommand:
|
||||
|
||||
-C <directory>::
|
||||
Before running the subcommand, change the working directory. This
|
||||
option imitates the same option of linkgit:git[1].
|
||||
|
||||
-c <key>=<value>::
|
||||
For the duration of running the specified subcommand, configure this
|
||||
setting. This option imitates the same option of linkgit:git[1].
|
||||
|
||||
COMMANDS
|
||||
--------
|
||||
|
||||
|
|
|
@ -85,4 +85,12 @@ test_expect_success 'scalar delete with enlistment' '
|
|||
test_path_is_missing cloned
|
||||
'
|
||||
|
||||
test_expect_success 'scalar supports -c/-C' '
|
||||
test_when_finished "scalar delete sub" &&
|
||||
git init sub &&
|
||||
scalar -C sub -c status.aheadBehind=bogus register &&
|
||||
test -z "$(git -C sub config --local status.aheadBehind)" &&
|
||||
test true = "$(git -C sub config core.preloadIndex)"
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
|
@ -975,10 +975,10 @@ cmd_merge () {
|
|||
|
||||
if test -n "$arg_addmerge_message"
|
||||
then
|
||||
git merge -Xsubtree="$arg_prefix" \
|
||||
git merge --no-ff -Xsubtree="$arg_prefix" \
|
||||
--message="$arg_addmerge_message" "$rev"
|
||||
else
|
||||
git merge -Xsubtree="$arg_prefix" $rev
|
||||
git merge --no-ff -Xsubtree="$arg_prefix" $rev
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
9
date.c
9
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;
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
|
||||
|
|
2
diff.c
2
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)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue