]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'ab/coding-guidelines-c99'
authorJunio C Hamano <gitster@pobox.com>
Wed, 19 Oct 2022 22:38:05 +0000 (15:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 19 Oct 2022 22:38:05 +0000 (15:38 -0700)
Update CodingGuidelines to clarify what features to use and avoid
in C99.

* ab/coding-guidelines-c99:
  CodingGuidelines: recommend against unportable C99 struct syntax
  CodingGuidelines: mention C99 features we can't use
  CodingGuidelines: allow declaring variables in for loops
  CodingGuidelines: mention dynamic C99 initializer elements
  CodingGuidelines: update for C99

189 files changed:
.gitignore
Documentation/CodingGuidelines
Documentation/RelNotes/2.30.6.txt [new file with mode: 0644]
Documentation/RelNotes/2.31.5.txt [new file with mode: 0644]
Documentation/RelNotes/2.32.4.txt [new file with mode: 0644]
Documentation/RelNotes/2.33.5.txt [new file with mode: 0644]
Documentation/RelNotes/2.34.5.txt [new file with mode: 0644]
Documentation/RelNotes/2.35.5.txt [new file with mode: 0644]
Documentation/RelNotes/2.36.3.txt [new file with mode: 0644]
Documentation/RelNotes/2.37.4.txt
Documentation/RelNotes/2.38.1.txt [new file with mode: 0644]
Documentation/RelNotes/2.39.0.txt [new file with mode: 0644]
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/config/fsmonitor--daemon.txt [new file with mode: 0644]
Documentation/config/log.txt
Documentation/config/mergetool.txt
Documentation/config/protocol.txt
Documentation/git-fsmonitor--daemon.txt
Documentation/git-maintenance.txt
Documentation/git-multi-pack-index.txt
Documentation/git.txt
Documentation/technical/bundle-uri.txt
GIT-VERSION-GEN
Makefile
RelNotes
alias.c
attr.c
builtin/bisect--helper.c
builtin/branch.c
builtin/clone.c
builtin/commit.c
builtin/fsck.c
builtin/fsmonitor--daemon.c
builtin/gc.c
builtin/grep.c
builtin/multi-pack-index.c
builtin/push.c
builtin/reflog.c
builtin/remote.c
common-main.c
compat/fsmonitor/fsm-ipc-darwin.c [new file with mode: 0644]
compat/fsmonitor/fsm-ipc-win32.c [new file with mode: 0644]
compat/fsmonitor/fsm-listen-darwin.c
compat/fsmonitor/fsm-path-utils-darwin.c [new file with mode: 0644]
compat/fsmonitor/fsm-path-utils-win32.c [new file with mode: 0644]
compat/fsmonitor/fsm-settings-darwin.c
compat/fsmonitor/fsm-settings-win32.c
contrib/buildsystems/CMakeLists.txt
contrib/credential/netrc/git-credential-netrc.perl
contrib/credential/osxkeychain/git-credential-osxkeychain.c
contrib/credential/wincred/git-credential-wincred.c
diff-merges.c
dir.c
fsmonitor--daemon.h
fsmonitor-ipc.c
fsmonitor-ipc.h
fsmonitor-path-utils.h [new file with mode: 0644]
fsmonitor-settings.c
fsmonitor-settings.h
fsmonitor.c
gettext.c
git-compat-util.h
gpg-interface.c
mailinfo.c
merge-ort.c
midx.c
object-file.c
object.c
oss-fuzz/.gitignore [new file with mode: 0644]
oss-fuzz/fuzz-commit-graph.c [moved from fuzz-commit-graph.c with 100% similarity]
oss-fuzz/fuzz-pack-headers.c [moved from fuzz-pack-headers.c with 100% similarity]
oss-fuzz/fuzz-pack-idx.c [moved from fuzz-pack-idx.c with 100% similarity]
promisor-remote.c
promisor-remote.h
read-cache.c
ref-filter.c
reflog.c
refs.c
refs.h
refs/files-backend.c
refs/packed-backend.c
scalar.c
sequencer.c
shell.c
string-list.h
t/check-non-portable-shell.pl
t/helper/test-submodule.c
t/lib-httpd/apache.conf
t/lib-submodule-update.sh
t/perf/p2000-sparse-operations.sh
t/perf/run
t/t0410-partial-clone.sh
t/t1091-sparse-checkout-builtin.sh
t/t1092-sparse-checkout-compatibility.sh
t/t1304-default-acl.sh
t/t1500-rev-parse.sh
t/t2080-parallel-checkout-basics.sh
t/t2400-worktree-add.sh
t/t2403-worktree-move.sh
t/t2405-worktree-submodule.sh
t/t3200-branch.sh
t/t3202-show-branch.sh
t/t3206-range-diff.sh
t/t3207-branch-submodule.sh
t/t3305-notes-fanout.sh
t/t3404-rebase-interactive.sh
t/t3415-rebase-autosquash.sh
t/t3420-rebase-autostash.sh
t/t3426-rebase-submodule.sh
t/t3435-rebase-gpg-sign.sh
t/t3438-rebase-broken-files.sh [new file with mode: 0755]
t/t3512-cherry-pick-submodule.sh
t/t3600-rm.sh
t/t3700-add.sh
t/t3702-add-edit.sh
t/t3906-stash-submodule.sh
t/t4014-format-patch.sh
t/t4059-diff-submodule-not-initialized.sh
t/t4060-diff-submodule-option-diff-format.sh
t/t4067-diff-partial-clone.sh
t/t4208-log-magic-pathspec.sh
t/t4301-merge-tree-write-tree.sh
t/t5100-mailinfo.sh
t/t5319-multi-pack-index.sh
t/t5320-delta-islands.sh
t/t5505-remote.sh
t/t5510-fetch.sh
t/t5516-fetch-push.sh
t/t5526-fetch-submodules.sh
t/t5537-fetch-shallow.sh
t/t5545-push-options.sh
t/t5550-http-fetch-dumb.sh
t/t5572-pull-submodule.sh
t/t5601-clone.sh
t/t5604-clone-reference.sh
t/t5606-clone-options.sh
t/t5614-clone-submodules-shallow.sh
t/t5616-partial-clone.sh
t/t5617-clone-submodules-remote.sh
t/t5702-protocol-v2.sh
t/t6008-rev-list-submodule.sh
t/t6134-pathspec-in-submodule.sh
t/t7001-mv.sh
t/t7003-filter-branch.sh
t/t7064-wtstatus-pv2.sh
t/t7300-clean.sh
t/t7400-submodule-basic.sh
t/t7403-submodule-sync.sh
t/t7406-submodule-update.sh
t/t7407-submodule-foreach.sh
t/t7408-submodule-reference.sh
t/t7409-submodule-detached-work-tree.sh
t/t7411-submodule-config.sh
t/t7413-submodule-is-active.sh
t/t7414-submodule-mistakes.sh
t/t7416-submodule-dash-url.sh
t/t7417-submodule-path-url.sh
t/t7418-submodule-sparse-gitmodules.sh
t/t7419-submodule-set-branch.sh
t/t7420-submodule-set-url.sh
t/t7421-submodule-summary-add.sh
t/t7450-bad-git-dotfiles.sh
t/t7506-status-submodule.sh
t/t7507-commit-verbose.sh
t/t7527-builtin-fsmonitor.sh
t/t7701-repack-unpack-unreachable.sh
t/t7800-difftool.sh
t/t7810-grep.sh
t/t7814-grep-recurse-submodules.sh
t/t7900-maintenance.sh
t/t9001-send-email.sh
t/t9133-git-svn-nested-git-repo.sh
t/t9134-git-svn-ignore-paths.sh
t/t9140-git-svn-reset.sh
t/t9147-git-svn-include-paths.sh
t/t9210-scalar.sh
t/t9304-fast-import-marks.sh
t/t9350-fast-export.sh
t/t9814-git-p4-rename.sh
t/t9815-git-p4-submit-fail.sh
t/t9850-shell.sh [new file with mode: 0755]
t/test-lib-functions.sh
t/test-lib.sh
tmp-objdir.c
transport.c
worktree.c
worktree.h
write-or-die.c

index b3dcafcb3310e9f0bf6c03cd51741b8cb48831b9..62720c6135d02fb5f1f533ce79249ebd7b8a3454 100644 (file)
@@ -1,7 +1,4 @@
-/fuzz-commit-graph
 /fuzz_corpora
-/fuzz-pack-headers
-/fuzz-pack-idx
 /GIT-BUILD-OPTIONS
 /GIT-CFLAGS
 /GIT-LDFLAGS
index 1d95a142b2780ad27636d3657d37f2608e9ac968..fc7de00aefc032c3caf6214a27abb6ed5b6097ef 100644 (file)
@@ -162,8 +162,6 @@ For shell scripts specifically (not exhaustive):
 
    - We do not use \{m,n\};
 
-   - We do not use -E;
-
    - We do not use ? or + (which are \{0,1\} and \{1,\}
      respectively in BRE) but that goes without saying as these
      are ERE elements not BRE (note that \? and \+ are not even part
diff --git a/Documentation/RelNotes/2.30.6.txt b/Documentation/RelNotes/2.30.6.txt
new file mode 100644 (file)
index 0000000..d649071
--- /dev/null
@@ -0,0 +1,60 @@
+Git v2.30.6 Release Notes
+=========================
+
+This release addresses the security issues CVE-2022-39253 and
+CVE-2022-39260.
+
+Fixes since v2.30.5
+-------------------
+
+ * CVE-2022-39253:
+   When relying on the `--local` clone optimization, Git dereferences
+   symbolic links in the source repository before creating hardlinks
+   (or copies) of the dereferenced link in the destination repository.
+   This can lead to surprising behavior where arbitrary files are
+   present in a repository's `$GIT_DIR` when cloning from a malicious
+   repository.
+
+   Git will no longer dereference symbolic links via the `--local`
+   clone mechanism, and will instead refuse to clone repositories that
+   have symbolic links present in the `$GIT_DIR/objects` directory.
+
+   Additionally, the value of `protocol.file.allow` is changed to be
+   "user" by default.
+
+ * CVE-2022-39260:
+   An overly-long command string given to `git shell` can result in
+   overflow in `split_cmdline()`, leading to arbitrary heap writes and
+   remote code execution when `git shell` is exposed and the directory
+   `$HOME/git-shell-commands` exists.
+
+   `git shell` is taught to refuse interactive commands that are
+   longer than 4MiB in size. `split_cmdline()` is hardened to reject
+   inputs larger than 2GiB.
+
+Credit for finding CVE-2022-39253 goes to Cory Snider of Mirantis. The
+fix was authored by Taylor Blau, with help from Johannes Schindelin.
+
+Credit for finding CVE-2022-39260 goes to Kevin Backhouse of GitHub.
+The fix was authored by Kevin Backhouse, Jeff King, and Taylor Blau.
+
+
+Jeff King (2):
+      shell: add basic tests
+      shell: limit size of interactive commands
+
+Kevin Backhouse (1):
+      alias.c: reject too-long cmdline strings in split_cmdline()
+
+Taylor Blau (11):
+      builtin/clone.c: disallow `--local` clones with symlinks
+      t/lib-submodule-update.sh: allow local submodules
+      t/t1NNN: allow local submodules
+      t/2NNNN: allow local submodules
+      t/t3NNN: allow local submodules
+      t/t4NNN: allow local submodules
+      t/t5NNN: allow local submodules
+      t/t6NNN: allow local submodules
+      t/t7NNN: allow local submodules
+      t/t9NNN: allow local submodules
+      transport: make `protocol.file.allow` be "user" by default
diff --git a/Documentation/RelNotes/2.31.5.txt b/Documentation/RelNotes/2.31.5.txt
new file mode 100644 (file)
index 0000000..0d87e6e
--- /dev/null
@@ -0,0 +1,5 @@
+Git v2.31.5 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.30.6; see
+the release notes for that version for details.
diff --git a/Documentation/RelNotes/2.32.4.txt b/Documentation/RelNotes/2.32.4.txt
new file mode 100644 (file)
index 0000000..76c67b2
--- /dev/null
@@ -0,0 +1,5 @@
+Git v2.32.4 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.30.6; see
+the release notes for that version for details.
diff --git a/Documentation/RelNotes/2.33.5.txt b/Documentation/RelNotes/2.33.5.txt
new file mode 100644 (file)
index 0000000..a636526
--- /dev/null
@@ -0,0 +1,5 @@
+Git v2.33.5 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.30.6; see
+the release notes for that version for details.
diff --git a/Documentation/RelNotes/2.34.5.txt b/Documentation/RelNotes/2.34.5.txt
new file mode 100644 (file)
index 0000000..0e89992
--- /dev/null
@@ -0,0 +1,5 @@
+Git v2.34.5 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.30.6; see
+the release notes for that version for details.
diff --git a/Documentation/RelNotes/2.35.5.txt b/Documentation/RelNotes/2.35.5.txt
new file mode 100644 (file)
index 0000000..e19cc48
--- /dev/null
@@ -0,0 +1,5 @@
+Git v2.35.5 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.30.6; see
+the release notes for that version for details.
diff --git a/Documentation/RelNotes/2.36.3.txt b/Documentation/RelNotes/2.36.3.txt
new file mode 100644 (file)
index 0000000..56db77b
--- /dev/null
@@ -0,0 +1,5 @@
+Git v2.36.3 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.30.6; see
+the release notes for that version for details.
index 732176376fb8b88d655d950a7972bffdecbdeb36..e42a5c1620961c95145f08f7b1d643eb2efb7dfe 100644 (file)
@@ -2,11 +2,45 @@ Git 2.37.4 Release Notes
 ========================
 
 This primarily is to backport various fixes accumulated on the 'master'
-front since 2.37.3.
+front since 2.37.3, and also includes the same security fixes as in
+v2.30.6.
 
 Fixes since v2.37.3
 -------------------
 
+ * CVE-2022-39253:
+   When relying on the `--local` clone optimization, Git dereferences
+   symbolic links in the source repository before creating hardlinks
+   (or copies) of the dereferenced link in the destination repository.
+   This can lead to surprising behavior where arbitrary files are
+   present in a repository's `$GIT_DIR` when cloning from a malicious
+   repository.
+
+   Git will no longer dereference symbolic links via the `--local`
+   clone mechanism, and will instead refuse to clone repositories that
+   have symbolic links present in the `$GIT_DIR/objects` directory.
+
+   Additionally, the value of `protocol.file.allow` is changed to be
+   "user" by default.
+
+   Credit for finding CVE-2022-39253 goes to Cory Snider of Mirantis.
+   The fix was authored by Taylor Blau, with help from Johannes
+   Schindelin.
+
+ * CVE-2022-39260:
+   An overly-long command string given to `git shell` can result in
+   overflow in `split_cmdline()`, leading to arbitrary heap writes and
+   remote code execution when `git shell` is exposed and the directory
+   `$HOME/git-shell-commands` exists.
+
+   `git shell` is taught to refuse interactive commands that are
+   longer than 4MiB in size. `split_cmdline()` is hardened to reject
+   inputs larger than 2GiB.
+
+   Credit for finding CVE-2022-39260 goes to Kevin Backhouse of
+   GitHub. The fix was authored by Kevin Backhouse, Jeff King, and
+   Taylor Blau.
+
  * An earlier optimization discarded a tree-object buffer that is
    still in use, which has been corrected.
 
diff --git a/Documentation/RelNotes/2.38.1.txt b/Documentation/RelNotes/2.38.1.txt
new file mode 100644 (file)
index 0000000..b2b5854
--- /dev/null
@@ -0,0 +1,5 @@
+Git v2.38.1 Release Notes
+=========================
+
+This release merges the security fix that appears in v2.30.6; see
+the release notes for that version for details.
diff --git a/Documentation/RelNotes/2.39.0.txt b/Documentation/RelNotes/2.39.0.txt
new file mode 100644 (file)
index 0000000..c8e333e
--- /dev/null
@@ -0,0 +1,124 @@
+Git v2.39 Release Notes
+=======================
+
+UI, Workflows & Features
+------------------------
+
+ * "git grep" learned to expand the sparse-index more lazily and on
+   demand in a sparse checkout.
+
+ * By default, use of fsmonitor on a repository on networked
+   filesystem is disabled. Add knobs to make it workable on macOS.
+
+
+Performance, Internal Implementation, Development Support etc.
+--------------------------------------------------------------
+
+ * With a bit of header twiddling, use the native regexp library on
+   macOS instead of the compat/ one.
+
+ * Prepare for GNU [ef]grep that throw warning of their uses.
+
+ * Sources related to fuzz testing have been moved down to their own
+   directory.
+
+ * Most credential helpers ignored unknown entries in a credential
+   description, but a few died upon seeing them.  The latter were
+   taught to ignore them, too
+
+ * "scalar unregister" in a repository that is already been
+   unregistered reported an error.
+
+ * Remove error detection from a function that fetches from promisor
+   remotes, and make it die when such a fetch fails to bring all the
+   requested objects, to give an early failure to various operations.
+
+
+Fixes since v2.38
+-----------------
+
+ * The codepath that reads from the index v4 had unaligned memory
+   accesses, which has been corrected.
+   (merge 4a6ed30f96 vd/fix-unaligned-read-index-v4 later to maint).
+
+ * Fix messages incorrectly marked for translation.
+   (merge 02cb8b9ee3 ah/fsmonitor-daemon-usage-non-l10n later to maint).
+
+ * "git fsck" failed to release contents of tree objects already used
+   from the memory, which has been fixed.
+   (merge 51b27747e5 jk/fsck-on-diet later to maint).
+
+ * "git clone" did not like to see the "--bare" and the "--origin"
+   options used together without a good reason.
+   (merge 3b910d6e29 jk/clone-allow-bare-and-o-together later to maint).
+
+ * "git remote rename" failed to rename a remote without fetch
+   refspec, which has been corrected.
+   (merge 5a97b38109 jk/remote-rename-without-fetch-refspec later to maint).
+
+ * Documentation on various Boolean GIT_* environment variables have
+   been clarified.
+   (merge 819fb68222 jc/environ-docs later to maint).
+
+ * "git rebase -i" can mistakenly attempt to apply a fixup to a commit
+   itself, which has been corrected.
+   (merge 3e367a5f2f ja/rebase-i-avoid-amending-self later to maint).
+
+ * "git multi-pack-index repack/expire" used to repack unreachable
+   cruft into a new pack, which have been corrected.
+   (merge b62ad5681f tb/midx-repack-ignore-cruft-packs later to maint).
+
+ * In read-only repositories, "git merge-tree" tried to come up with a
+   merge result tree object, which it failed (which is not wrong) and
+   led to a segfault (which is bad), which has been corrected.
+   (merge 92481d1b26 js/merge-ort-in-read-only-repo later to maint).
+
+ * Force C locale while running tests around httpd to make sure we can
+   find expected error messages in the log.
+   (merge 7a2d8ea47e rs/test-httpd-in-C-locale later to maint).
+
+ * Fix a logic in "mailinfo -b" that miscomputed the length of a
+   substring, which lead to an out-of-bounds access.
+   (merge 3ef1494685 pw/mailinfo-b-fix later to maint).
+
+ * The codepath to sign learned to report errors when it fails to read
+   from "ssh-keygen".
+   (merge 36fb0d07d8 pw/ssh-sign-report-errors later to maint).
+
+ * Code clean-up that results in plugging a leak.
+   (merge 246526d019 rs/bisect-start-leakfix later to maint).
+
+ * "GIT_EDITOR=: git branch --edit-description" resulted in failure,
+   which has been corrected.
+   (merge e288b3de35 jc/branch-description-unset later to maint).
+
+ * The code to clean temporary object directories (used for
+   quarantine) tried to remove them inside its signal handler, which
+   was a no-no.
+   (merge 22613b25ec jc/tmp-objdir later to maint).
+
+ * Update comment in the Makefile about the RUNTIME_PREFIX config knob.
+   (merge ebb6c16607 dd/document-runtime-prefix-better later to maint).
+
+ * Clarify that "the sentence after <area>: prefix does not begin with
+   a capital letter" rule applies only to the commit title.
+   (merge 3991bb73dd jc/use-of-uc-in-log-messages later to maint).
+
+ * "git branch --edit-description" on an unborh branch misleadingly
+   said that no such branch exists, which has been corrected.
+   (merge bcfc82bd48 rj/branch-edit-desc-unborn later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge c34a6bd291 so/diff-merges-cleanup later to maint).
+   (merge 5e7c8b75e7 ab/test-malloc-with-sanitize-leak later to maint).
+   (merge 2a905f8fa8 ah/branch-autosetupmerge-grammofix later to maint).
+   (merge abcac2e19f rj/ref-filter-get-head-description-leakfix later to maint).
+   (merge 71e5473493 hn/parse-worktree-ref later to maint).
+   (merge 7190b7ebf9 ds/bundle-uri-docfix later to maint).
+   (merge 45350aeb11 jk/sequencer-missing-author-name-check later to maint).
+   (merge edbf9a2e20 nb/doc-mergetool-typofix later to maint).
+   (merge b004c90282 rs/gc-pack-refs-simplify later to maint).
+   (merge 69c5f17f11 jk/cleanup-callback-parameters later to maint).
+   (merge 7c07f36ad2 ab/unused-annotation later to maint).
+   (merge f7669676d0 rs/use-fspathncmp later to maint).
+   (merge a677d3c416 pw/remove-rebase-p-test later to maint).
index 5bd795e5dbf90daff92fcf33a10f7c318e9d0689..927f7329a557bab6c316fa8fa1e478fa54b7e6a7 100644 (file)
@@ -153,7 +153,9 @@ files you are modifying to see the current conventions.
 
 [[summary-section]]
 The title sentence after the "area:" prefix omits the full stop at the
-end, and its first word is not capitalized unless there is a reason to
+end, and its first word is not capitalized (the omission
+of capitalization applies only to the word after the "area:"
+prefix of the title) unless there is a reason to
 capitalize it other than because it is the first word in the sentence.
 E.g. "doc: clarify...", not "doc: Clarify...", or "githooks.txt:
 improve...", not "githooks.txt: Improve...".  But "refs: HEAD is also
index 5b5b9765699933c406af47f6c1b4ee9ed885f0ba..1e205831656711e898dac1c33da753066e621088 100644 (file)
@@ -423,6 +423,8 @@ include::config/filter.txt[]
 
 include::config/fsck.txt[]
 
+include::config/fsmonitor--daemon.txt[]
+
 include::config/gc.txt[]
 
 include::config/gitcvs.txt[]
diff --git a/Documentation/config/fsmonitor--daemon.txt b/Documentation/config/fsmonitor--daemon.txt
new file mode 100644 (file)
index 0000000..c225c6c
--- /dev/null
@@ -0,0 +1,11 @@
+fsmonitor.allowRemote::
+    By default, the fsmonitor daemon refuses to work against network-mounted
+    repositories. Setting `fsmonitor.allowRemote` to `true` overrides this
+    behavior.  Only respected when `core.fsmonitor` is set to `true`.
+
+fsmonitor.socketDir::
+    This Mac OS-specific option, if set, specifies the directory in
+    which to create the Unix domain socket used for communication
+    between the fsmonitor daemon and various Git commands. The directory must
+    reside on a native Mac OS filesystem.  Only respected when `core.fsmonitor`
+    is set to `true`.
index bc63bc3939c27d40969564657321866fe8236f21..5f96cf87fb96ce7366f11f80b48f9c9bf9fd13f6 100644 (file)
@@ -34,9 +34,9 @@ log.excludeDecoration::
        option.
 
 log.diffMerges::
-       Set default diff format to be used for merge commits. See
-       `--diff-merges` in linkgit:git-log[1] for details.
-       Defaults to `separate`.
+       Set diff format to be used when `--diff-merges=on` is
+       specified, see `--diff-merges` in linkgit:git-log[1] for
+       details. Defaults to `separate`.
 
 log.follow::
        If `true`, `git log` will act as if the `--follow` option was used when
index 90b380970023a76799206a30789d4f8af7793991..e779a122d8a78f10adb6381a9d00e9041b2c5b70 100644 (file)
@@ -59,7 +59,7 @@ mergetool.hideResolved::
        possible and write the 'MERGED' file containing conflict markers around
        any conflicts that it cannot resolve; 'LOCAL' and 'REMOTE' normally
        represent the versions of the file from before Git's conflict
-       resolution. This flag causes 'LOCAL' and 'REMOTE' to be overwriten so
+       resolution. This flag causes 'LOCAL' and 'REMOTE' to be overwritten so
        that only the unresolved conflicts are presented to the merge tool. Can
        be configured per-tool via the `mergetool.<tool>.hideResolved`
        configuration variable. Defaults to `false`.
index 576038185148d8cce30170b3fe4888b6cf3aa2b6..a9bf187a933a243a6a0b8b0e38f730f8f50bed2d 100644 (file)
@@ -1,10 +1,10 @@
 protocol.allow::
        If set, provide a user defined default policy for all protocols which
        don't explicitly have a policy (`protocol.<name>.allow`).  By default,
-       if unset, known-safe protocols (http, https, git, ssh, file) have a
+       if unset, known-safe protocols (http, https, git, ssh) have a
        default policy of `always`, known-dangerous protocols (ext) have a
-       default policy of `never`, and all other protocols have a default
-       policy of `user`.  Supported policies:
+       default policy of `never`, and all other protocols (including file)
+       have a default policy of `user`.  Supported policies:
 +
 --
 
index cc142fb8612c7279d7b74eaf8bea5edc10252d88..8238eadb0e166a97537fdfd28296df6c31e7e342 100644 (file)
@@ -3,7 +3,7 @@ git-fsmonitor{litdd}daemon(1)
 
 NAME
 ----
-git-fsmonitor--daemon - A Built-in File System Monitor
+git-fsmonitor--daemon - A Built-in Filesystem Monitor
 
 SYNOPSIS
 --------
@@ -17,7 +17,7 @@ DESCRIPTION
 -----------
 
 A daemon to watch the working directory for file and directory
-changes using platform-specific file system notification facilities.
+changes using platform-specific filesystem notification facilities.
 
 This daemon communicates directly with commands like `git status`
 using the link:technical/api-simple-ipc.html[simple IPC] interface
@@ -63,13 +63,44 @@ CAVEATS
 -------
 
 The fsmonitor daemon does not currently know about submodules and does
-not know to filter out file system events that happen within a
+not know to filter out filesystem 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.
 
+By default, the fsmonitor daemon refuses to work against network-mounted
+repositories; this may be overridden by setting `fsmonitor.allowRemote` to
+`true`. Note, however, that the fsmonitor daemon is not guaranteed to work
+correctly with all network-mounted repositories and such use is considered
+experimental.
+
+On Mac OS, the inter-process communication (IPC) between various Git
+commands and the fsmonitor daemon is done via a Unix domain socket (UDS) -- a
+special type of file -- which is supported by native Mac OS filesystems,
+but not on network-mounted filesystems, NTFS, or FAT32.  Other filesystems
+may or may not have the needed support; the fsmonitor daemon is not guaranteed
+to work with these filesystems and such use is considered experimental.
+
+By default, the socket is created in the `.git` directory, however, if the
+`.git` directory is on a network-mounted filesystem, it will be instead be
+created at `$HOME/.git-fsmonitor-*` unless `$HOME` itself is on a
+network-mounted filesystem in which case you must set the configuration
+variable `fsmonitor.socketDir` to the path of a directory on a Mac OS native
+filesystem in which to create the socket file.
+
+If none of the above directories (`.git`, `$HOME`, or `fsmonitor.socketDir`)
+is on a native Mac OS file filesystem the fsmonitor daemon will report an
+error that will cause the daemon and the currently running command to exit.
+
+CONFIGURATION
+-------------
+
+include::includes/cmd-config-section-all.txt[]
+
+include::config/fsmonitor--daemon.txt[]
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index 9c630efe19c68ec4b25fee1cf716c5a2073e2f51..bb888690e4dbd2642fc46d3c8775fa1638741cf4 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 [verse]
 'git maintenance' run [<options>]
 'git maintenance' start [--scheduler=<scheduler>]
-'git maintenance' (stop|register|unregister)
+'git maintenance' (stop|register|unregister) [<options>]
 
 
 DESCRIPTION
@@ -79,6 +79,10 @@ unregister::
        Remove the current repository from background maintenance. This
        only removes the repository from the configured list. It does not
        stop the background maintenance processes from running.
++
+The `unregister` subcommand will report an error if the current repository
+is not already registered. Use the `--force` option to return success even
+when the current repository is not registered.
 
 TASKS
 -----
index a48c3d5ea6301abcdda5df021ac8442ed149f23c..3696506eb355e1b11a447a7014dcca370b760555 100644 (file)
@@ -70,9 +70,10 @@ verify::
        Verify the contents of the MIDX file.
 
 expire::
-       Delete the pack-files that are tracked  by the MIDX file, but
-       have no objects referenced by the MIDX. Rewrite the MIDX file
-       afterward to remove all references to these pack-files.
+       Delete the pack-files that are tracked by the MIDX file, but
+       have no objects referenced by the MIDX (with the exception of
+       `.keep` packs and cruft packs). Rewrite the MIDX file afterward
+       to remove all references to these pack-files.
 
 repack::
        Create a new pack-file containing objects in small pack-files
index 0c15ef3a8e6a51d3bbab8957f7487022f80bc170..1d33e083ab8ba104641001cd6e85b49505c3c374 100644 (file)
@@ -458,7 +458,12 @@ Please see linkgit:gitglossary[7].
 
 Environment Variables
 ---------------------
-Various Git commands use the following environment variables:
+Various Git commands pay attention to environment variables and change
+their behavior.  The environment variables marked as "Boolean" take
+their values the same way as Boolean valued configuration variables, e.g.
+"true", "yes", "on" and positive numbers are taken as "yes".
+
+Here are the variables:
 
 The Git Repository
 ~~~~~~~~~~~~~~~~~~
@@ -467,13 +472,13 @@ is worth noting that they may be used/overridden by SCMS sitting above
 Git so take care if using a foreign front-end.
 
 `GIT_INDEX_FILE`::
-       This environment allows the specification of an alternate
+       This environment variable specifies an alternate
        index file. If not specified, the default of `$GIT_DIR/index`
        is used.
 
 `GIT_INDEX_VERSION`::
-       This environment variable allows the specification of an index
-       version for new repositories.  It won't affect existing index
+       This environment variable specifies what index version is used
+       when writing the index file out.  It won't affect existing index
        files.  By default index file version 2 or 3 is used. See
        linkgit:git-update-index[1] for more information.
 
@@ -530,7 +535,7 @@ double-quotes and respecting backslash escapes. E.g., the value
        When run in a directory that does not have ".git" repository
        directory, Git tries to find such a directory in the parent
        directories to find the top of the working tree, but by default it
-       does not cross filesystem boundaries.  This environment variable
+       does not cross filesystem boundaries.  This Boolean environment variable
        can be set to true to tell Git not to stop at filesystem
        boundaries.  Like `GIT_CEILING_DIRECTORIES`, this will not affect
        an explicit repository directory set via `GIT_DIR` or on the
@@ -682,6 +687,11 @@ for further details.
        plink or tortoiseplink. This variable overrides the config setting
        `ssh.variant` that serves the same purpose.
 
+`GIT_SSL_NO_VERIFY`::
+       Setting and exporting this environment variable to any value
+       tells Git not to verify the SSL certificate when fetching or
+       pushing over HTTPS.
+
 `GIT_ASKPASS`::
        If this environment variable is set, then Git commands which need to
        acquire passwords or passphrases (e.g. for HTTP or IMAP authentication)
@@ -690,7 +700,7 @@ for further details.
        option in linkgit:git-config[1].
 
 `GIT_TERMINAL_PROMPT`::
-       If this environment variable is set to `0`, git will not prompt
+       If this Boolean environment variable is set to false, git will not prompt
        on the terminal (e.g., when asking for HTTP authentication).
 
 `GIT_CONFIG_GLOBAL`::
@@ -705,13 +715,14 @@ for further details.
 
 `GIT_CONFIG_NOSYSTEM`::
        Whether to skip reading settings from the system-wide
-       `$(prefix)/etc/gitconfig` file.  This environment variable can
+       `$(prefix)/etc/gitconfig` file.  This Boolean environment variable can
        be used along with `$HOME` and `$XDG_CONFIG_HOME` to create a
        predictable environment for a picky script, or you can set it
-       temporarily to avoid using a buggy `/etc/gitconfig` file while
+       to true to temporarily avoid using a buggy `/etc/gitconfig` file while
        waiting for someone with sufficient permissions to fix it.
 
 `GIT_FLUSH`::
+// NEEDSWORK: make it into a usual Boolean environment variable
        If this environment variable is set to "1", then commands such
        as 'git blame' (in incremental mode), 'git rev-list', 'git log',
        'git check-attr' and 'git check-ignore' will
@@ -852,11 +863,11 @@ for full details.
 `GIT_TRACE_REDACT`::
        By default, when tracing is activated, Git redacts the values of
        cookies, the "Authorization:" header, the "Proxy-Authorization:"
-       header and packfile URIs. Set this variable to `0` to prevent this
+       header and packfile URIs. Set this Boolean environment variable to false to prevent this
        redaction.
 
 `GIT_LITERAL_PATHSPECS`::
-       Setting this variable to `1` will cause Git to treat all
+       Setting this Boolean environment variable to true will cause Git to treat all
        pathspecs literally, rather than as glob patterns. For example,
        running `GIT_LITERAL_PATHSPECS=1 git log -- '*.c'` will search
        for commits that touch the path `*.c`, not any paths that the
@@ -865,15 +876,15 @@ for full details.
        `git ls-tree`, `--raw` diff output, etc).
 
 `GIT_GLOB_PATHSPECS`::
-       Setting this variable to `1` will cause Git to treat all
+       Setting this Boolean environment variable to true will cause Git to treat all
        pathspecs as glob patterns (aka "glob" magic).
 
 `GIT_NOGLOB_PATHSPECS`::
-       Setting this variable to `1` will cause Git to treat all
+       Setting this Boolean environment variable to true will cause Git to treat all
        pathspecs as literal (aka "literal" magic).
 
 `GIT_ICASE_PATHSPECS`::
-       Setting this variable to `1` will cause Git to treat all
+       Setting this Boolean environment variable to true will cause Git to treat all
        pathspecs as case-insensitive.
 
 `GIT_REFLOG_ACTION`::
@@ -887,7 +898,7 @@ for full details.
        end user, to be recorded in the body of the reflog.
 
 `GIT_REF_PARANOIA`::
-       If set to `0`, ignore broken or badly named refs when iterating
+       If this Boolean environment variable is set to false, ignore broken or badly named refs when iterating
        over lists of refs. Normally Git will try to include any such
        refs, which may cause some operations to fail. This is usually
        preferable, as potentially destructive operations (e.g.,
@@ -906,7 +917,7 @@ for full details.
        `protocol.allow` in linkgit:git-config[1] for more details.
 
 `GIT_PROTOCOL_FROM_USER`::
-       Set to 0 to prevent protocols used by fetch/push/clone which are
+       Set this Boolean environment variable to false to prevent protocols used by fetch/push/clone which are
        configured to the `user` state.  This is useful to restrict recursive
        submodule initialization from an untrusted repository or for programs
        which feed potentially-untrusted URLS to git commands.  See
@@ -934,7 +945,7 @@ only affects clones and fetches; it is not yet used for pushes (but may
 be in the future).
 
 `GIT_OPTIONAL_LOCKS`::
-       If set to `0`, Git will complete any requested operation without
+       If this Boolean environment variable is set to false, Git will complete any requested operation without
        performing any optional sub-operations that require taking a lock.
        For example, this will prevent `git status` from refreshing the
        index as a side effect. This is useful for processes running in
index 18f2dedd400aaf588e4af8fd6bf186b6c9ffc5a5..b78d01d9adfc72ff7598cda0e836d8c9f3c99e0b 100644 (file)
@@ -153,8 +153,8 @@ bundle.<id>.filter::
 
 bundle.<id>.creationToken::
        This value is a nonnegative 64-bit integer used for sorting the bundles
-       the list. This is used to download a subset of bundles during a fetch
-       when `bundle.heuristic=creationToken`.
+       list. This is used to download a subset of bundles during a fetch when
+       `bundle.heuristic=creationToken`.
 
 bundle.<id>.location::
        This string value advertises a real-world location from where the bundle
@@ -234,8 +234,8 @@ will interact with bundle URIs according to the following flow:
    making those OIDs present. When all required OIDs are present, the
    client unbundles that data using a refspec. The default refspec is
    `+refs/heads/*:refs/bundles/*`, but this can be configured. These refs
-   are stored so that later `git fetch` negotiations can communicate the
-   bundled refs as `have`s, reducing the size of the fetch over the Git
+   are stored so that later `git fetch` negotiations can communicate each
+   bundled ref as a `have`, reducing the size of the fetch over the Git
    protocol. To allow pruning refs from this ref namespace, Git may
    introduce a numbered namespace (such as `refs/bundles/<i>/*`) such that
    stale bundle refs can be deleted.
index a3eb6eca7abfb5d4176251f820a6680d7aa83ce9..e3eaeb49264eea1619d10dc6b17b7dbfbd42ec4b 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.38.0
+DEF_VER=v2.38.GIT
 
 LF='
 '
index cac3452edb90b473782ec0bda5782d133848d6ce..d93ad956e589bd3a4c4800c97c98f36d040b323e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -529,8 +529,9 @@ GIT-VERSION-FILE: FORCE
 #   template_dir
 #   sysconfdir
 # can be specified as a relative path some/where/else;
-# this is interpreted as relative to $(prefix) and "git" at
-# runtime figures out where they are based on the path to the executable.
+# this is interpreted as relative to $(prefix) and "git" built with
+# RUNTIME_PREFIX flag will figure out (at runtime) where they are
+# based on the path to the executable.
 # Additionally, the following will be treated as relative by "git" if they
 # begin with "$(prefix)/":
 #   mandir
@@ -688,9 +689,9 @@ SCRIPTS = $(SCRIPT_SH_GEN) \
 
 ETAGS_TARGET = TAGS
 
-FUZZ_OBJS += fuzz-commit-graph.o
-FUZZ_OBJS += fuzz-pack-headers.o
-FUZZ_OBJS += fuzz-pack-idx.o
+FUZZ_OBJS += oss-fuzz/fuzz-commit-graph.o
+FUZZ_OBJS += oss-fuzz/fuzz-pack-headers.o
+FUZZ_OBJS += oss-fuzz/fuzz-pack-idx.o
 .PHONY: fuzz-objs
 fuzz-objs: $(FUZZ_OBJS)
 
@@ -1442,7 +1443,6 @@ ifeq ($(uname_S),Darwin)
                APPLE_COMMON_CRYPTO = YesPlease
                COMPAT_CFLAGS += -DAPPLE_COMMON_CRYPTO
        endif
-       NO_REGEX = YesPlease
        PTHREAD_LIBS =
 endif
 
@@ -2039,11 +2039,13 @@ ifdef FSMONITOR_DAEMON_BACKEND
        COMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND
        COMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o
        COMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o
+       COMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_DAEMON_BACKEND).o
 endif
 
 ifdef FSMONITOR_OS_SETTINGS
        COMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS
        COMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o
+       COMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o
 endif
 
 ifeq ($(TCLTK_PATH),)
@@ -2980,6 +2982,7 @@ GIT-BUILD-OPTIONS: FORCE
        @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@+
        @echo NO_PTHREADS=\''$(subst ','\'',$(subst ','\'',$(NO_PTHREADS)))'\' >>$@+
        @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+
+       @echo NO_REGEX=\''$(subst ','\'',$(subst ','\'',$(NO_REGEX)))'\' >>$@+
        @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+
        @echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+
        @echo DC_SHA1=\''$(subst ','\'',$(subst ','\'',$(DC_SHA1)))'\' >>$@+
index d505db645bec7e01446fc88472acd84b83bdf34f..758368388a4641dd46cf221bea607f885d487e84 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.38.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.39.0.txt
\ No newline at end of file
diff --git a/alias.c b/alias.c
index c4715380205b5f8dd032b16ade846842fa3c23b9..00abde081739436236aa077412c3b5b686144f42 100644 (file)
--- a/alias.c
+++ b/alias.c
@@ -46,14 +46,16 @@ void list_aliases(struct string_list *list)
 
 #define SPLIT_CMDLINE_BAD_ENDING 1
 #define SPLIT_CMDLINE_UNCLOSED_QUOTE 2
+#define SPLIT_CMDLINE_ARGC_OVERFLOW 3
 static const char *split_cmdline_errors[] = {
        N_("cmdline ends with \\"),
-       N_("unclosed quote")
+       N_("unclosed quote"),
+       N_("too many arguments"),
 };
 
 int split_cmdline(char *cmdline, const char ***argv)
 {
-       int src, dst, count = 0, size = 16;
+       size_t src, dst, count = 0, size = 16;
        char quoted = 0;
 
        ALLOC_ARRAY(*argv, size);
@@ -96,6 +98,11 @@ int split_cmdline(char *cmdline, const char ***argv)
                return -SPLIT_CMDLINE_UNCLOSED_QUOTE;
        }
 
+       if (count >= INT_MAX) {
+               FREE_AND_NULL(*argv);
+               return -SPLIT_CMDLINE_ARGC_OVERFLOW;
+       }
+
        ALLOC_GROW(*argv, count + 1, size);
        (*argv)[count] = NULL;
 
diff --git a/attr.c b/attr.c
index 8250b0695321a804be3d06777570702aee1e5f2e..42ad6de8c7c61432e4a3c950d940eee433680a74 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -23,10 +23,6 @@ static const char git_attr__unknown[] = "(builtin)unknown";
 #define ATTR__UNSET NULL
 #define ATTR__UNKNOWN git_attr__unknown
 
-#ifndef DEBUG_ATTR
-#define DEBUG_ATTR 0
-#endif
-
 struct git_attr {
        int attr_nr; /* unique attribute number */
        char name[FLEX_ARRAY]; /* attribute name */
@@ -807,33 +803,6 @@ static struct attr_stack *read_attr(struct index_state *istate,
        return res;
 }
 
-#if DEBUG_ATTR
-static void debug_info(const char *what, struct attr_stack *elem)
-{
-       fprintf(stderr, "%s: %s\n", what, elem->origin ? elem->origin : "()");
-}
-static void debug_set(const char *what, const char *match, struct git_attr *attr, const void *v)
-{
-       const char *value = v;
-
-       if (ATTR_TRUE(value))
-               value = "set";
-       else if (ATTR_FALSE(value))
-               value = "unset";
-       else if (ATTR_UNSET(value))
-               value = "unspecified";
-
-       fprintf(stderr, "%s: %s => %s (%s)\n",
-               what, attr->name, (char *) value, match);
-}
-#define debug_push(a) debug_info("push", (a))
-#define debug_pop(a) debug_info("pop", (a))
-#else
-#define debug_push(a) do { ; } while (0)
-#define debug_pop(a) do { ; } while (0)
-#define debug_set(a,b,c,d) do { ; } while (0)
-#endif /* DEBUG_ATTR */
-
 static const char *git_etc_gitattributes(void)
 {
        static const char *system_wide;
@@ -954,7 +923,6 @@ static void prepare_attr_stack(struct index_state *istate,
                    (!namelen || path[namelen] == '/'))
                        break;
 
-               debug_pop(elem);
                *stack = elem->prev;
                attr_stack_free(elem);
        }
@@ -1028,7 +996,7 @@ static int path_matches(const char *pathname, int pathlen,
 
 static int macroexpand_one(struct all_attrs_item *all_attrs, int nr, int rem);
 
-static int fill_one(const char *what, struct all_attrs_item *all_attrs,
+static int fill_one(struct all_attrs_item *all_attrs,
                    const struct match_attr *a, int rem)
 {
        int i;
@@ -1039,9 +1007,6 @@ static int fill_one(const char *what, struct all_attrs_item *all_attrs,
                const char *v = a->state[i].setto;
 
                if (*n == ATTR__UNKNOWN) {
-                       debug_set(what,
-                                 a->is_macro ? a->u.attr->name : a->u.pat.pattern,
-                                 attr, v);
                        *n = v;
                        rem--;
                        rem = macroexpand_one(all_attrs, attr->attr_nr, rem);
@@ -1064,7 +1029,7 @@ static int fill(const char *path, int pathlen, int basename_offset,
                                continue;
                        if (path_matches(path, pathlen, basename_offset,
                                         &a->u.pat, base, stack->originlen))
-                               rem = fill_one("fill", all_attrs, a, rem);
+                               rem = fill_one(all_attrs, a, rem);
                }
        }
 
@@ -1076,7 +1041,7 @@ static int macroexpand_one(struct all_attrs_item *all_attrs, int nr, int rem)
        const struct all_attrs_item *item = &all_attrs[nr];
 
        if (item->macro && item->value == ATTR__TRUE)
-               return fill_one("expand", all_attrs, item->macro, rem);
+               return fill_one(all_attrs, item->macro, rem);
        else
                return rem;
 }
index 501245fac9521717d4ca83341ab5528686fb5777..28ef7ec2a48856b1c13f1cdd31546985148752f0 100644 (file)
@@ -765,11 +765,10 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a
                strbuf_read_file(&start_head, git_path_bisect_start(), 0);
                strbuf_trim(&start_head);
                if (!no_checkout) {
-                       struct strvec argv = STRVEC_INIT;
+                       const char *argv[] = { "checkout", start_head.buf,
+                                              "--", NULL };
 
-                       strvec_pushl(&argv, "checkout", start_head.buf,
-                                    "--", NULL);
-                       if (run_command_v_opt(argv.v, RUN_GIT_CMD)) {
+                       if (run_command_v_opt(argv, RUN_GIT_CMD)) {
                                res = error(_("checking out '%s' failed."
                                                 " Try 'git bisect start "
                                                 "<valid-branch>'."),
index 55cd9a6e9984fc66334a7e78bd65294c628e34fb..e0e0af432024bb6db1802dec92fe48bab9da3157 100644 (file)
@@ -538,6 +538,13 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
                        die(_("Invalid branch name: '%s'"), oldname);
        }
 
+       if ((copy || strcmp(head, oldname)) && !ref_exists(oldref.buf)) {
+               if (copy && !strcmp(head, oldname))
+                       die(_("No commit on branch '%s' yet."), oldname);
+               else
+                       die(_("No branch named '%s'."), oldname);
+       }
+
        /*
         * A command like "git branch -M currentbranch currentbranch" cannot
         * cause the worktree to become inconsistent with HEAD, so allow it.
@@ -599,10 +606,11 @@ static GIT_PATH_FUNC(edit_description, "EDIT_DESCRIPTION")
 
 static int edit_branch_description(const char *branch_name)
 {
+       int exists;
        struct strbuf buf = STRBUF_INIT;
        struct strbuf name = STRBUF_INIT;
 
-       read_branch_desc(&buf, branch_name);
+       exists = !read_branch_desc(&buf, branch_name);
        if (!buf.len || buf.buf[buf.len-1] != '\n')
                strbuf_addch(&buf, '\n');
        strbuf_commented_addf(&buf,
@@ -619,7 +627,8 @@ static int edit_branch_description(const char *branch_name)
        strbuf_stripspace(&buf, 1);
 
        strbuf_addf(&name, "branch.%s.description", branch_name);
-       git_config_set(name.buf, buf.len ? buf.buf : NULL);
+       if (buf.len || exists)
+               git_config_set(name.buf, buf.len ? buf.buf : NULL);
        strbuf_release(&name);
        strbuf_release(&buf);
 
@@ -805,7 +814,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                if (!ref_exists(branch_ref.buf)) {
                        strbuf_release(&branch_ref);
 
-                       if (!argc)
+                       if (!argc || !strcmp(head, branch_name))
                                return error(_("No commit on branch '%s' yet."),
                                             branch_name);
                        else
@@ -848,8 +857,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                        die(_("no such branch '%s'"), argv[0]);
                }
 
-               if (!ref_exists(branch->refname))
+               if (!ref_exists(branch->refname)) {
+                       if (!argc || !strcmp(head, branch->name))
+                               die(_("No commit on branch '%s' yet."), branch->name);
                        die(_("branch '%s' does not exist"), branch->name);
+               }
 
                dwim_and_setup_tracking(the_repository, branch->name,
                                        new_upstream, BRANCH_TRACK_OVERRIDE,
index d269d6fec68ce409cac6e659d8e78e602b1ce4b8..547d6464b3c04b83cb1376d325eeefb79aca079c 100644 (file)
@@ -320,13 +320,11 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
        int src_len, dest_len;
        struct dir_iterator *iter;
        int iter_status;
-       unsigned int flags;
        struct strbuf realpath = STRBUF_INIT;
 
        mkdir_if_missing(dest->buf, 0777);
 
-       flags = DIR_ITERATOR_PEDANTIC | DIR_ITERATOR_FOLLOW_SYMLINKS;
-       iter = dir_iterator_begin(src->buf, flags);
+       iter = dir_iterator_begin(src->buf, DIR_ITERATOR_PEDANTIC);
 
        if (!iter)
                die_errno(_("failed to start iterator over '%s'"), src->buf);
@@ -342,6 +340,10 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
                strbuf_setlen(dest, dest_len);
                strbuf_addstr(dest, iter->relative_path);
 
+               if (S_ISLNK(iter->st.st_mode))
+                       die(_("symlink '%s' exists, refusing to clone with --local"),
+                           iter->relative_path);
+
                if (S_ISDIR(iter->st.st_mode)) {
                        mkdir_if_missing(dest->buf, 0777);
                        continue;
@@ -929,9 +931,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                option_bare = 1;
 
        if (option_bare) {
-               if (option_origin)
-                       die(_("options '%s' and '%s %s' cannot be used together"),
-                           "--bare", "--origin", option_origin);
                if (real_git_dir)
                        die(_("options '%s' and '%s' cannot be used together"), "--bare", "--separate-git-dir");
                option_no_checkout = 1;
index fcf9c85947e6a1fbe2e96baee2d1cdd3f4764e89..d9de4ef008b5add9fed39008589167c508c67d64 100644 (file)
@@ -139,7 +139,7 @@ static int opt_pass_trailer(const struct option *opt, const char *arg, int unset
 {
        BUG_ON_OPT_NEG(unset);
 
-       strvec_pushl(&trailer_args, "--trailer", arg, NULL);
+       strvec_pushl(opt->value, "--trailer", arg, NULL);
        return 0;
 }
 
@@ -1633,7 +1633,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                OPT_STRING(0, "fixup", &fixup_message, N_("[(amend|reword):]commit"), N_("use autosquash formatted message to fixup or amend/reword specified commit")),
                OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")),
                OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
-               OPT_CALLBACK_F(0, "trailer", NULL, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
+               OPT_CALLBACK_F(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG, opt_pass_trailer),
                OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
                OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
                OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
index f7916f06ed51119576035c9381c4dace2dbbecf2..41acbc229e468f68092a5b6e3f2df95ece62fd7c 100644 (file)
@@ -228,6 +228,8 @@ static void mark_unreachable_referents(const struct object_id *oid)
 
        options.walk = mark_used;
        fsck_walk(obj, NULL, &options);
+       if (obj->type == OBJ_TREE)
+               free_tree_buffer((struct tree *)obj);
 }
 
 static int mark_loose_unreachable_referents(const struct object_id *oid,
@@ -437,9 +439,6 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
 out:
        if (obj->type == OBJ_TREE)
                free_tree_buffer((struct tree *)obj);
-       if (obj->type == OBJ_COMMIT)
-               free_commit_buffer(the_repository->parsed_objects,
-                                  (struct commit *)obj);
        return err;
 }
 
@@ -853,6 +852,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
 
        errors_found = 0;
        read_replace_refs = 0;
+       save_commit_buffer = 0;
 
        argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
 
index 2c109cf8b377712a1bb2849ee83724d9c3473656..6f30a4f93a7fa93873e40312a0fcb569056daf88 100644 (file)
@@ -3,6 +3,7 @@
 #include "parse-options.h"
 #include "fsmonitor.h"
 #include "fsmonitor-ipc.h"
+#include "fsmonitor-path-utils.h"
 #include "compat/fsmonitor/fsm-health.h"
 #include "compat/fsmonitor/fsm-listen.h"
 #include "fsmonitor--daemon.h"
@@ -13,8 +14,8 @@
 static const char * const builtin_fsmonitor__daemon_usage[] = {
        N_("git fsmonitor--daemon start [<options>]"),
        N_("git fsmonitor--daemon run [<options>]"),
-       N_("git fsmonitor--daemon stop"),
-       N_("git fsmonitor--daemon status"),
+       "git fsmonitor--daemon stop",
+       "git fsmonitor--daemon status",
        NULL
 };
 
@@ -1282,6 +1283,11 @@ static int fsmonitor_run_daemon(void)
        strbuf_addstr(&state.path_worktree_watch, absolute_path(get_git_work_tree()));
        state.nr_paths_watching = 1;
 
+       strbuf_init(&state.alias.alias, 0);
+       strbuf_init(&state.alias.points_to, 0);
+       if ((err = fsmonitor__get_alias(state.path_worktree_watch.buf, &state.alias)))
+               goto done;
+
        /*
         * We create and delete cookie files somewhere inside the .git
         * directory to help us keep sync with the file system.  If
@@ -1343,7 +1349,8 @@ static int fsmonitor_run_daemon(void)
         * directory.)
         */
        strbuf_init(&state.path_ipc, 0);
-       strbuf_addstr(&state.path_ipc, absolute_path(fsmonitor_ipc__get_path()));
+       strbuf_addstr(&state.path_ipc,
+               absolute_path(fsmonitor_ipc__get_path(the_repository)));
 
        /*
         * Confirm that we can create platform-specific resources for the
@@ -1390,6 +1397,8 @@ done:
        strbuf_release(&state.path_gitdir_watch);
        strbuf_release(&state.path_cookie_prefix);
        strbuf_release(&state.path_ipc);
+       strbuf_release(&state.alias.alias);
+       strbuf_release(&state.alias.points_to);
 
        return err;
 }
index 2753bd15a5e85f584a0e73943828ce8cd1d0fe5c..243ee85d283297a7e08b81c24155a8a58abebf17 100644 (file)
@@ -167,16 +167,9 @@ static void gc_config(void)
 struct maintenance_run_opts;
 static int maintenance_task_pack_refs(MAYBE_UNUSED struct maintenance_run_opts *opts)
 {
-       struct strvec pack_refs_cmd = STRVEC_INIT;
-       int ret;
+       const char *argv[] = { "pack-refs", "--all", "--prune", NULL };
 
-       strvec_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL);
-
-       ret = run_command_v_opt(pack_refs_cmd.v, RUN_GIT_CMD);
-
-       strvec_clear(&pack_refs_cmd);
-
-       return ret;
+       return run_command_v_opt(argv, RUN_GIT_CMD);
 }
 
 static int too_many_loose_objects(void)
@@ -1470,11 +1463,12 @@ static int maintenance_register(int argc, const char **argv, const char *prefix)
        struct option options[] = {
                OPT_END(),
        };
-       int rc;
+       int found = 0;
+       const char *key = "maintenance.repo";
        char *config_value;
-       struct child_process config_set = CHILD_PROCESS_INIT;
-       struct child_process config_get = CHILD_PROCESS_INIT;
        char *maintpath = get_maintpath();
+       struct string_list_item *item;
+       const struct string_list *list;
 
        argc = parse_options(argc, argv, prefix, options,
                             builtin_maintenance_register_usage, 0);
@@ -1491,46 +1485,56 @@ static int maintenance_register(int argc, const char **argv, const char *prefix)
        else
                git_config_set("maintenance.strategy", "incremental");
 
-       config_get.git_cmd = 1;
-       strvec_pushl(&config_get.args, "config", "--global", "--get",
-                    "--fixed-value", "maintenance.repo", maintpath, NULL);
-       config_get.out = -1;
-
-       if (start_command(&config_get)) {
-               rc = error(_("failed to run 'git config'"));
-               goto done;
+       list = git_config_get_value_multi(key);
+       if (list) {
+               for_each_string_list_item(item, list) {
+                       if (!strcmp(maintpath, item->string)) {
+                               found = 1;
+                               break;
+                       }
+               }
        }
 
-       /* We already have this value in our config! */
-       if (!finish_command(&config_get)) {
-               rc = 0;
-               goto done;
+       if (!found) {
+               int rc;
+               char *user_config, *xdg_config;
+               git_global_config(&user_config, &xdg_config);
+               if (!user_config)
+                       die(_("$HOME not set"));
+               rc = git_config_set_multivar_in_file_gently(
+                       user_config, "maintenance.repo", maintpath,
+                       CONFIG_REGEX_NONE, 0);
+               free(user_config);
+               free(xdg_config);
+
+               if (rc)
+                       die(_("unable to add '%s' value of '%s'"),
+                           key, maintpath);
        }
 
-       config_set.git_cmd = 1;
-       strvec_pushl(&config_set.args, "config", "--add", "--global", "maintenance.repo",
-                    maintpath, NULL);
-
-       rc = run_command(&config_set);
-
-done:
        free(maintpath);
-       return rc;
+       return 0;
 }
 
 static char const * const builtin_maintenance_unregister_usage[] = {
-       "git maintenance unregister",
+       "git maintenance unregister [--force]",
        NULL
 };
 
 static int maintenance_unregister(int argc, const char **argv, const char *prefix)
 {
+       int force = 0;
        struct option options[] = {
+               OPT__FORCE(&force,
+                          N_("return success even if repository was not registered"),
+                          PARSE_OPT_NOCOMPLETE),
                OPT_END(),
        };
-       int rc;
-       struct child_process config_unset = CHILD_PROCESS_INIT;
+       const char *key = "maintenance.repo";
        char *maintpath = get_maintpath();
+       int found = 0;
+       struct string_list_item *item;
+       const struct string_list *list;
 
        argc = parse_options(argc, argv, prefix, options,
                             builtin_maintenance_unregister_usage, 0);
@@ -1538,13 +1542,38 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi
                usage_with_options(builtin_maintenance_unregister_usage,
                                   options);
 
-       config_unset.git_cmd = 1;
-       strvec_pushl(&config_unset.args, "config", "--global", "--unset",
-                    "--fixed-value", "maintenance.repo", maintpath, NULL);
+       list = git_config_get_value_multi(key);
+       if (list) {
+               for_each_string_list_item(item, list) {
+                       if (!strcmp(maintpath, item->string)) {
+                               found = 1;
+                               break;
+                       }
+               }
+       }
+
+       if (found) {
+               int rc;
+               char *user_config, *xdg_config;
+               git_global_config(&user_config, &xdg_config);
+               if (!user_config)
+                       die(_("$HOME not set"));
+               rc = git_config_set_multivar_in_file_gently(
+                       user_config, key, NULL, maintpath,
+                       CONFIG_FLAGS_MULTI_REPLACE | CONFIG_FLAGS_FIXED_VALUE);
+               free(user_config);
+               free(xdg_config);
+
+               if (rc &&
+                   (!force || rc == CONFIG_NOTHING_SET))
+                       die(_("unable to unset '%s' value of '%s'"),
+                           key, maintpath);
+       } else if (!force) {
+               die(_("repository '%s' is not registered"), maintpath);
+       }
 
-       rc = run_command(&config_unset);
        free(maintpath);
-       return rc;
+       return 0;
 }
 
 static const char *get_frequency(enum schedule_priority schedule)
index e6bcdf860cc96af2e70975eea2f66ac8ec8606eb..5fa927d4e22bde2d2282230a98a1bfe744c628d1 100644 (file)
@@ -458,6 +458,33 @@ static int grep_submodule(struct grep_opt *opt,
         * subrepo's odbs to the in-memory alternates list.
         */
        obj_read_lock();
+
+       /*
+        * NEEDSWORK: when reading a submodule, the sparsity settings in the
+        * superproject are incorrectly forgotten or misused. For example:
+        *
+        * 1. "command_requires_full_index"
+        *      When this setting is turned on for `grep`, only the superproject
+        *      knows it. All the submodules are read with their own configs
+        *      and get prepare_repo_settings()'d. Therefore, these submodules
+        *      "forget" the sparse-index feature switch. As a result, the index
+        *      of these submodules are expanded unexpectedly.
+        *
+        * 2. "core_apply_sparse_checkout"
+        *      When running `grep` in the superproject, this setting is
+        *      populated using the superproject's configs. However, once
+        *      initialized, this config is globally accessible and is read by
+        *      prepare_repo_settings() for the submodules. For instance, if a
+        *      submodule is using a sparse-checkout, however, the superproject
+        *      is not, the result is that the config from the superproject will
+        *      dictate the behavior for the submodule, making it "forget" its
+        *      sparse-checkout state.
+        *
+        * 3. "core_sparse_checkout_cone"
+        *      ditto.
+        *
+        * Note that this list is not exhaustive.
+        */
        repo_read_gitmodules(subrepo, 0);
 
        /*
@@ -520,8 +547,6 @@ static int grep_cache(struct grep_opt *opt,
        if (repo_read_index(repo) < 0)
                die(_("index file corrupt"));
 
-       /* TODO: audit for interaction with sparse-index. */
-       ensure_full_index(repo->index);
        for (nr = 0; nr < repo->index->cache_nr; nr++) {
                const struct cache_entry *ce = repo->index->cache[nr];
 
@@ -530,8 +555,20 @@ static int grep_cache(struct grep_opt *opt,
 
                strbuf_setlen(&name, name_base_len);
                strbuf_addstr(&name, ce->name);
+               if (S_ISSPARSEDIR(ce->ce_mode)) {
+                       enum object_type type;
+                       struct tree_desc tree;
+                       void *data;
+                       unsigned long size;
 
-               if (S_ISREG(ce->ce_mode) &&
+                       data = read_object_file(&ce->oid, &type, &size);
+                       init_tree_desc(&tree, data, size);
+
+                       hit |= grep_tree(opt, pathspec, &tree, &name, 0, 0);
+                       strbuf_setlen(&name, name_base_len);
+                       strbuf_addstr(&name, ce->name);
+                       free(data);
+               } else if (S_ISREG(ce->ce_mode) &&
                    match_pathspec(repo->index, pathspec, name.buf, name.len, 0, NULL,
                                   S_ISDIR(ce->ce_mode) ||
                                   S_ISGITLINK(ce->ce_mode))) {
@@ -984,6 +1021,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                             PARSE_OPT_KEEP_DASHDASH |
                             PARSE_OPT_STOP_AT_NON_OPTION);
 
+       if (the_repository->gitdir) {
+               prepare_repo_settings(the_repository);
+               the_repository->settings.command_requires_full_index = 0;
+       }
+
        if (use_index && !startup_info->have_repository) {
                int fallback = 0;
                git_config_get_bool("grep.fallbacktonoindex", &fallback);
index 9b126d6ce0e7f5fbaa4a5616d45c0c9bfa41d5b5..9a18a82b0575efdff2c98707179d827fd73eacb9 100644 (file)
@@ -56,11 +56,12 @@ static struct opts_multi_pack_index {
 static int parse_object_dir(const struct option *opt, const char *arg,
                            int unset)
 {
-       free(opts.object_dir);
+       char **value = opt->value;
+       free(*value);
        if (unset)
-               opts.object_dir = xstrdup(get_object_directory());
+               *value = xstrdup(get_object_directory());
        else
-               opts.object_dir = real_pathdup(arg, 1);
+               *value = real_pathdup(arg, 1);
        return 0;
 }
 
index df0d68e599857c74a4edf0a85ecdfb037516c47d..f0329c62a2dc04d0422a8de7bd27f813a19115cf 100644 (file)
@@ -169,8 +169,8 @@ static NORETURN void die_push_simple(struct branch *branch,
        if (git_branch_track != BRANCH_TRACK_SIMPLE)
                advice_automergesimple_maybe = _("\n"
                                 "To avoid automatically configuring "
-                                "upstream branches when their name\n"
-                                "doesn't match the local branch, see option "
+                                "an upstream branch when its name\n"
+                                "won't match the local branch, see option "
                                 "'simple' of branch.autoSetupMerge\n"
                                 "in 'git help config'.\n");
        die(_("The upstream branch of your current branch does not match\n"
index 57c5c0d061c449a0cf782c2cc41688cc042e3c47..270681dcdf40e3c7ff0bf2969340eb1b981cf8e8 100644 (file)
@@ -67,7 +67,8 @@ static int collect_reflog(const char *ref, const struct object_id *oid UNUSED,
         * Avoid collecting the same shared ref multiple times because
         * they are available via all worktrees.
         */
-       if (!worktree->is_current && ref_type(ref) == REF_TYPE_NORMAL)
+       if (!worktree->is_current &&
+           parse_worktree_ref(ref, NULL, NULL, NULL) == REF_WORKTREE_SHARED)
                return 0;
 
        strbuf_worktree_ref(worktree, &newref, ref);
index 985b845a18bae82bfa90d63da78fca8445563d51..910f7b9316a4bdd036bd4203434c0bd6be0cd6cf 100644 (file)
@@ -733,29 +733,31 @@ static int mv(int argc, const char **argv, const char *prefix)
                return error(_("Could not rename config section '%s' to '%s'"),
                                buf.buf, buf2.buf);
 
-       strbuf_reset(&buf);
-       strbuf_addf(&buf, "remote.%s.fetch", rename.new_name);
-       git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);
-       strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name);
-       for (i = 0; i < oldremote->fetch.raw_nr; i++) {
-               char *ptr;
-
-               strbuf_reset(&buf2);
-               strbuf_addstr(&buf2, oldremote->fetch.raw[i]);
-               ptr = strstr(buf2.buf, old_remote_context.buf);
-               if (ptr) {
-                       refspec_updated = 1;
-                       strbuf_splice(&buf2,
-                                     ptr-buf2.buf + strlen(":refs/remotes/"),
-                                     strlen(rename.old_name), rename.new_name,
-                                     strlen(rename.new_name));
-               } else
-                       warning(_("Not updating non-default fetch refspec\n"
-                                 "\t%s\n"
-                                 "\tPlease update the configuration manually if necessary."),
-                               buf2.buf);
-
-               git_config_set_multivar(buf.buf, buf2.buf, "^$", 0);
+       if (oldremote->fetch.raw_nr) {
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "remote.%s.fetch", rename.new_name);
+               git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);
+               strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name);
+               for (i = 0; i < oldremote->fetch.raw_nr; i++) {
+                       char *ptr;
+
+                       strbuf_reset(&buf2);
+                       strbuf_addstr(&buf2, oldremote->fetch.raw[i]);
+                       ptr = strstr(buf2.buf, old_remote_context.buf);
+                       if (ptr) {
+                               refspec_updated = 1;
+                               strbuf_splice(&buf2,
+                                             ptr-buf2.buf + strlen(":refs/remotes/"),
+                                             strlen(rename.old_name), rename.new_name,
+                                             strlen(rename.new_name));
+                       } else
+                               warning(_("Not updating non-default fetch refspec\n"
+                                         "\t%s\n"
+                                         "\tPlease update the configuration manually if necessary."),
+                                       buf2.buf);
+
+                       git_config_set_multivar(buf.buf, buf2.buf, "^$", 0);
+               }
        }
 
        read_branches();
index c531372f3ff0f13839a2056568cbaa7fd37f98a8..0a22861f1ceb28547002a237e7201eb920eb9d68 100644 (file)
@@ -40,6 +40,7 @@ int main(int argc, const char **argv)
 
        git_resolve_executable_dir(argv[0]);
 
+       setlocale(LC_CTYPE, "");
        git_setup_gettext();
 
        initialize_the_repository();
diff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c
new file mode 100644 (file)
index 0000000..ce843d6
--- /dev/null
@@ -0,0 +1,52 @@
+#include "cache.h"
+#include "config.h"
+#include "strbuf.h"
+#include "fsmonitor.h"
+#include "fsmonitor-ipc.h"
+#include "fsmonitor-path-utils.h"
+
+static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, "fsmonitor--daemon.ipc")
+
+const char *fsmonitor_ipc__get_path(struct repository *r)
+{
+       static const char *ipc_path = NULL;
+       SHA_CTX sha1ctx;
+       char *sock_dir = NULL;
+       struct strbuf ipc_file = STRBUF_INIT;
+       unsigned char hash[SHA_DIGEST_LENGTH];
+
+       if (!r)
+               BUG("No repository passed into fsmonitor_ipc__get_path");
+
+       if (ipc_path)
+               return ipc_path;
+
+
+       /* By default the socket file is created in the .git directory */
+       if (fsmonitor__is_fs_remote(r->gitdir) < 1) {
+               ipc_path = fsmonitor_ipc__get_default_path();
+               return ipc_path;
+       }
+
+       SHA1_Init(&sha1ctx);
+       SHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));
+       SHA1_Final(hash, &sha1ctx);
+
+       repo_config_get_string(r, "fsmonitor.socketdir", &sock_dir);
+
+       /* Create the socket file in either socketDir or $HOME */
+       if (sock_dir && *sock_dir) {
+               strbuf_addf(&ipc_file, "%s/.git-fsmonitor-%s",
+                                       sock_dir, hash_to_hex(hash));
+       } else {
+               strbuf_addf(&ipc_file, "~/.git-fsmonitor-%s", hash_to_hex(hash));
+       }
+       free(sock_dir);
+
+       ipc_path = interpolate_path(ipc_file.buf, 1);
+       if (!ipc_path)
+               die(_("Invalid path: %s"), ipc_file.buf);
+
+       strbuf_release(&ipc_file);
+       return ipc_path;
+}
diff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c
new file mode 100644 (file)
index 0000000..e08c505
--- /dev/null
@@ -0,0 +1,9 @@
+#include "config.h"
+#include "fsmonitor-ipc.h"
+
+const char *fsmonitor_ipc__get_path(struct repository *r) {
+       static char *ret;
+       if (!ret)
+               ret = git_pathdup("fsmonitor--daemon.ipc");
+       return ret;
+}
index 8e208e8289e0caef1d12425bc913b7069fe63a60..daeee4e465c1246df1ca254403b81d8c0d2220ac 100644 (file)
@@ -26,6 +26,7 @@
 #include "fsmonitor.h"
 #include "fsm-listen.h"
 #include "fsmonitor--daemon.h"
+#include "fsmonitor-path-utils.h"
 
 struct fsm_listen_data
 {
@@ -198,8 +199,9 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,
        struct string_list cookie_list = STRING_LIST_INIT_DUP;
        const char *path_k;
        const char *slash;
-       int k;
+       char *resolved = NULL;
        struct strbuf tmp = STRBUF_INIT;
+       int k;
 
        /*
         * Build a list of all filesystem changes into a private/local
@@ -209,7 +211,12 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,
                /*
                 * On Mac, we receive an array of absolute paths.
                 */
-               path_k = paths[k];
+               free(resolved);
+               resolved = fsmonitor__resolve_alias(paths[k], &state->alias);
+               if (resolved)
+                       path_k = resolved;
+               else
+                       path_k = paths[k];
 
                /*
                 * If you want to debug FSEvents, log them to GIT_TRACE_FSMONITOR.
@@ -238,6 +245,7 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,
                        fsmonitor_force_resync(state);
                        fsmonitor_batch__free_list(batch);
                        string_list_clear(&cookie_list, 0);
+                       batch = NULL;
 
                        /*
                         * We assume that any events that we received
@@ -360,12 +368,14 @@ static void fsevent_callback(ConstFSEventStreamRef streamRef,
                }
        }
 
+       free(resolved);
        fsmonitor_publish(state, batch, &cookie_list);
        string_list_clear(&cookie_list, 0);
        strbuf_release(&tmp);
        return;
 
 force_shutdown:
+       free(resolved);
        fsmonitor_batch__free_list(batch);
        string_list_clear(&cookie_list, 0);
 
diff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c
new file mode 100644 (file)
index 0000000..ce5a8fe
--- /dev/null
@@ -0,0 +1,135 @@
+#include "fsmonitor.h"
+#include "fsmonitor-path-utils.h"
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+
+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)
+{
+       struct statfs fs;
+       if (statfs(path, &fs) == -1) {
+               int saved_errno = errno;
+               trace_printf_key(&trace_fsmonitor, "statfs('%s') failed: %s",
+                                path, strerror(saved_errno));
+               errno = saved_errno;
+               return -1;
+       }
+
+       trace_printf_key(&trace_fsmonitor,
+                        "statfs('%s') [type 0x%08x][flags 0x%08x] '%s'",
+                        path, fs.f_type, fs.f_flags, fs.f_fstypename);
+
+       if (!(fs.f_flags & MNT_LOCAL))
+               fs_info->is_remote = 1;
+       else
+               fs_info->is_remote = 0;
+
+       fs_info->typename = xstrdup(fs.f_fstypename);
+
+       trace_printf_key(&trace_fsmonitor,
+                               "'%s' is_remote: %d",
+                               path, fs_info->is_remote);
+       return 0;
+}
+
+int fsmonitor__is_fs_remote(const char *path)
+{
+       struct fs_info fs;
+       if (fsmonitor__get_fs_info(path, &fs))
+               return -1;
+
+       free(fs.typename);
+
+       return fs.is_remote;
+}
+
+/*
+ * Scan the root directory for synthetic firmlinks that when resolved
+ * are a prefix of the path, stopping at the first one found.
+ *
+ * Some information about firmlinks and synthetic firmlinks:
+ * https://eclecticlight.co/2020/01/23/catalina-boot-volumes/
+ *
+ * macOS no longer allows symlinks in the root directory; any link found
+ * there is therefore a synthetic firmlink.
+ *
+ * If this function gets called often, will want to cache all the firmlink
+ * information, but for now there is only one caller of this function.
+ *
+ * If there is more than one alias for the path, that is another
+ * matter altogether.
+ */
+int fsmonitor__get_alias(const char *path, struct alias_info *info)
+{
+       DIR *dir;
+       int retval = -1;
+       const char *const root = "/";
+       struct stat st;
+       struct dirent *de;
+       struct strbuf alias;
+       struct strbuf points_to = STRBUF_INIT;
+
+       dir = opendir(root);
+       if (!dir)
+               return error_errno(_("opendir('%s') failed"), root);
+
+       strbuf_init(&alias, 256);
+
+       while ((de = readdir(dir)) != NULL) {
+               strbuf_reset(&alias);
+               strbuf_addf(&alias, "%s%s", root, de->d_name);
+
+               if (lstat(alias.buf, &st) < 0) {
+                       error_errno(_("lstat('%s') failed"), alias.buf);
+                       goto done;
+               }
+
+               if (!S_ISLNK(st.st_mode))
+                       continue;
+
+               if (strbuf_readlink(&points_to, alias.buf, st.st_size) < 0) {
+                       error_errno(_("strbuf_readlink('%s') failed"), alias.buf);
+                       goto done;
+               }
+
+               if (!strncmp(points_to.buf, path, points_to.len) &&
+                       (path[points_to.len] == '/')) {
+                       strbuf_addbuf(&info->alias, &alias);
+                       strbuf_addbuf(&info->points_to, &points_to);
+                       trace_printf_key(&trace_fsmonitor,
+                               "Found alias for '%s' : '%s' -> '%s'",
+                               path, info->alias.buf, info->points_to.buf);
+                       retval = 0;
+                       goto done;
+               }
+       }
+       retval = 0; /* no alias */
+
+done:
+       strbuf_release(&alias);
+       strbuf_release(&points_to);
+       if (closedir(dir) < 0)
+               return error_errno(_("closedir('%s') failed"), root);
+       return retval;
+}
+
+char *fsmonitor__resolve_alias(const char *path,
+       const struct alias_info *info)
+{
+       if (!info->alias.len)
+               return NULL;
+
+       if ((!strncmp(info->alias.buf, path, info->alias.len))
+               && path[info->alias.len] == '/') {
+               struct strbuf tmp = STRBUF_INIT;
+               const char *remainder = path + info->alias.len;
+
+               strbuf_addbuf(&tmp, &info->points_to);
+               strbuf_add(&tmp, remainder, strlen(remainder));
+               return strbuf_detach(&tmp, NULL);
+       }
+
+       return NULL;
+}
diff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c
new file mode 100644 (file)
index 0000000..0d95bbb
--- /dev/null
@@ -0,0 +1,145 @@
+#include "cache.h"
+#include "fsmonitor.h"
+#include "fsmonitor-path-utils.h"
+
+/*
+ * Check remote working directory protocol.
+ *
+ * Return -1 if client machine cannot get remote protocol information.
+ */
+static int check_remote_protocol(wchar_t *wpath)
+{
+       HANDLE h;
+       FILE_REMOTE_PROTOCOL_INFO proto_info;
+
+       h = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+                       FILE_FLAG_BACKUP_SEMANTICS, NULL);
+
+       if (h == INVALID_HANDLE_VALUE) {
+               error(_("[GLE %ld] unable to open for read '%ls'"),
+                     GetLastError(), wpath);
+               return -1;
+       }
+
+       if (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,
+               &proto_info, sizeof(proto_info))) {
+               error(_("[GLE %ld] unable to get protocol information for '%ls'"),
+                     GetLastError(), wpath);
+               CloseHandle(h);
+               return -1;
+       }
+
+       CloseHandle(h);
+
+       trace_printf_key(&trace_fsmonitor,
+                               "check_remote_protocol('%ls') remote protocol %#8.8lx",
+                               wpath, proto_info.Protocol);
+
+       return 0;
+}
+
+/*
+ * Notes for testing:
+ *
+ * (a) Windows allows a network share to be mapped to a drive letter.
+ *     (This is the normal method to access it.)
+ *
+ *     $ NET USE Z: \\server\share
+ *     $ git -C Z:/repo status
+ *
+ * (b) Windows allows a network share to be referenced WITHOUT mapping
+ *     it to drive letter.
+ *
+ *     $ NET USE \\server\share\dir
+ *     $ git -C //server/share/repo status
+ *
+ * (c) Windows allows "SUBST" to create a fake drive mapping to an
+ *     arbitrary path (which may be remote)
+ *
+ *     $ SUBST Q: Z:\repo
+ *     $ git -C Q:/ status
+ *
+ * (d) Windows allows a directory symlink to be created on a local
+ *     file system that points to a remote repo.
+ *
+ *     $ mklink /d ./link //server/share/repo
+ *     $ git -C ./link status
+ */
+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)
+{
+       wchar_t wpath[MAX_PATH];
+       wchar_t wfullpath[MAX_PATH];
+       size_t wlen;
+       UINT driveType;
+
+       /*
+        * Do everything in wide chars because the drive letter might be
+        * a multi-byte sequence.  See win32_has_dos_drive_prefix().
+        */
+       if (xutftowcs_path(wpath, path) < 0) {
+               return -1;
+       }
+
+       /*
+        * GetDriveTypeW() requires a final slash.  We assume that the
+        * worktree pathname points to an actual directory.
+        */
+       wlen = wcslen(wpath);
+       if (wpath[wlen - 1] != L'\\' && wpath[wlen - 1] != L'/') {
+               wpath[wlen++] = L'\\';
+               wpath[wlen] = 0;
+       }
+
+       /*
+        * Normalize the path.  If nothing else, this converts forward
+        * slashes to backslashes.  This is essential to get GetDriveTypeW()
+        * correctly handle some UNC "\\server\share\..." paths.
+        */
+       if (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL)) {
+               return -1;
+       }
+
+       driveType = GetDriveTypeW(wfullpath);
+       trace_printf_key(&trace_fsmonitor,
+                        "DriveType '%s' L'%ls' (%u)",
+                        path, wfullpath, driveType);
+
+       if (driveType == DRIVE_REMOTE) {
+               fs_info->is_remote = 1;
+               if (check_remote_protocol(wfullpath) < 0)
+                       return -1;
+       } else {
+               fs_info->is_remote = 0;
+       }
+
+       trace_printf_key(&trace_fsmonitor,
+                               "'%s' is_remote: %d",
+                               path, fs_info->is_remote);
+
+       return 0;
+}
+
+int fsmonitor__is_fs_remote(const char *path)
+{
+       struct fs_info fs;
+       if (fsmonitor__get_fs_info(path, &fs))
+               return -1;
+       return fs.is_remote;
+}
+
+/*
+ * No-op for now.
+ */
+int fsmonitor__get_alias(const char *path, struct alias_info *info)
+{
+       return 0;
+}
+
+/*
+ * No-op for now.
+ */
+char *fsmonitor__resolve_alias(const char *path,
+       const struct alias_info *info)
+{
+       return NULL;
+}
index efc732c0f317fc63c75d5878c82695748bcc1aa4..6abbc7af3ab53cd4911edf93d91ebf18e2c5c152 100644 (file)
@@ -1,32 +1,10 @@
-#include "cache.h"
 #include "config.h"
-#include "repository.h"
-#include "fsmonitor-settings.h"
 #include "fsmonitor.h"
-#include <sys/param.h>
-#include <sys/mount.h>
+#include "fsmonitor-ipc.h"
+#include "fsmonitor-settings.h"
+#include "fsmonitor-path-utils.h"
 
-/*
- * [1] Remote working directories are problematic for FSMonitor.
- *
- * The underlying file system on the server machine and/or the remote
- * mount type (NFS, SAMBA, etc.) dictates whether notification events
- * are available at all to remote client machines.
- *
- * Kernel differences between the server and client machines also
- * dictate the how (buffering, frequency, de-dup) the events are
- * delivered to client machine processes.
- *
- * A client machine (such as a laptop) may choose to suspend/resume
- * and it is unclear (without lots of testing) whether the watcher can
- * resync after a resume.  We might be able to treat this as a normal
- * "events were dropped by the kernel" event and do our normal "flush
- * and resync" --or-- we might need to close the existing (zombie?)
- * notification fd and create a new one.
- *
- * In theory, the above issues need to be addressed whether we are
- * using the Hook or IPC API.
- *
+ /*
  * For the builtin FSMonitor, we create the Unix domain socket for the
  * IPC in the .git directory.  If the working directory is remote,
  * then the socket will be created on the remote file system.  This
  * be taken to ensure that $HOME is actually local and not a managed
  * file share.)
  *
- * So (for now at least), mark remote working directories as
- * incompatible.
- *
- *
- * [2] FAT32 and NTFS working directories are problematic too.
+ * FAT32 and NTFS working directories are problematic too.
  *
  * The builtin FSMonitor uses a Unix domain socket in the .git
  * directory for IPC.  These Windows drive formats do not support
  * Unix domain sockets, so mark them as incompatible for the daemon.
  *
  */
-static enum fsmonitor_reason check_volume(struct repository *r)
+static enum fsmonitor_reason check_uds_volume(struct repository *r)
 {
-       struct statfs fs;
+       struct fs_info fs;
+       const char *ipc_path = fsmonitor_ipc__get_path(r);
+       struct strbuf path = STRBUF_INIT;
+       strbuf_add(&path, ipc_path, strlen(ipc_path));
 
-       if (statfs(r->worktree, &fs) == -1) {
-               int saved_errno = errno;
-               trace_printf_key(&trace_fsmonitor, "statfs('%s') failed: %s",
-                                r->worktree, strerror(saved_errno));
-               errno = saved_errno;
+       if (fsmonitor__get_fs_info(dirname(path.buf), &fs) == -1) {
+               strbuf_release(&path);
                return FSMONITOR_REASON_ERROR;
        }
 
-       trace_printf_key(&trace_fsmonitor,
-                        "statfs('%s') [type 0x%08x][flags 0x%08x] '%s'",
-                        r->worktree, fs.f_type, fs.f_flags, fs.f_fstypename);
+       strbuf_release(&path);
 
-       if (!(fs.f_flags & MNT_LOCAL))
-               return FSMONITOR_REASON_REMOTE;
-
-       if (!strcmp(fs.f_fstypename, "msdos")) /* aka FAT32 */
-               return FSMONITOR_REASON_NOSOCKETS;
-
-       if (!strcmp(fs.f_fstypename, "ntfs"))
+       if (fs.is_remote ||
+               !strcmp(fs.typename, "msdos") ||
+               !strcmp(fs.typename, "ntfs")) {
+               free(fs.typename);
                return FSMONITOR_REASON_NOSOCKETS;
+       }
 
+       free(fs.typename);
        return FSMONITOR_REASON_OK;
 }
 
-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)
+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)
 {
        enum fsmonitor_reason reason;
 
-       reason = check_volume(r);
-       if (reason != FSMONITOR_REASON_OK)
-               return reason;
+       if (ipc) {
+               reason = check_uds_volume(r);
+               if (reason != FSMONITOR_REASON_OK)
+                       return reason;
+       }
 
        return FSMONITOR_REASON_OK;
 }
index e5ec5b0a9f73bcf04085b5d62d7b123690ce937e..a8af31b71de05cab939c1d8223194fa91d0cbd6a 100644 (file)
@@ -1,8 +1,9 @@
 #include "cache.h"
 #include "config.h"
 #include "repository.h"
-#include "fsmonitor-settings.h"
 #include "fsmonitor.h"
+#include "fsmonitor-settings.h"
+#include "fsmonitor-path-utils.h"
 
 /*
  * VFS for Git is incompatible with FSMonitor.
@@ -24,172 +25,7 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r)
        return FSMONITOR_REASON_OK;
 }
 
-/*
- * Check if monitoring remote working directories is allowed.
- *
- * By default, monitoring remote working directories is
- * disabled.  Users may override this behavior in enviroments where
- * they have proper support.
- */
-static int check_config_allowremote(struct repository *r)
-{
-       int allow;
-
-       if (!repo_config_get_bool(r, "fsmonitor.allowremote", &allow))
-               return allow;
-
-       return -1; /* fsmonitor.allowremote not set */
-}
-
-/*
- * Check remote working directory protocol.
- *
- * Error if client machine cannot get remote protocol information.
- */
-static int check_remote_protocol(wchar_t *wpath)
-{
-       HANDLE h;
-       FILE_REMOTE_PROTOCOL_INFO proto_info;
-
-       h = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
-                       FILE_FLAG_BACKUP_SEMANTICS, NULL);
-
-       if (h == INVALID_HANDLE_VALUE) {
-               error(_("[GLE %ld] unable to open for read '%ls'"),
-                     GetLastError(), wpath);
-               return -1;
-       }
-
-       if (!GetFileInformationByHandleEx(h, FileRemoteProtocolInfo,
-               &proto_info, sizeof(proto_info))) {
-               error(_("[GLE %ld] unable to get protocol information for '%ls'"),
-                     GetLastError(), wpath);
-               CloseHandle(h);
-               return -1;
-       }
-
-       CloseHandle(h);
-
-       trace_printf_key(&trace_fsmonitor,
-                               "check_remote_protocol('%ls') remote protocol %#8.8lx",
-                               wpath, proto_info.Protocol);
-
-       return 0;
-}
-
-/*
- * Remote working directories are problematic for FSMonitor.
- *
- * The underlying file system on the server machine and/or the remote
- * mount type dictates whether notification events are available at
- * all to remote client machines.
- *
- * Kernel differences between the server and client machines also
- * dictate the how (buffering, frequency, de-dup) the events are
- * delivered to client machine processes.
- *
- * A client machine (such as a laptop) may choose to suspend/resume
- * and it is unclear (without lots of testing) whether the watcher can
- * resync after a resume.  We might be able to treat this as a normal
- * "events were dropped by the kernel" event and do our normal "flush
- * and resync" --or-- we might need to close the existing (zombie?)
- * notification fd and create a new one.
- *
- * In theory, the above issues need to be addressed whether we are
- * using the Hook or IPC API.
- *
- * So (for now at least), mark remote working directories as
- * incompatible.
- *
- * Notes for testing:
- *
- * (a) Windows allows a network share to be mapped to a drive letter.
- *     (This is the normal method to access it.)
- *
- *     $ NET USE Z: \\server\share
- *     $ git -C Z:/repo status
- *
- * (b) Windows allows a network share to be referenced WITHOUT mapping
- *     it to drive letter.
- *
- *     $ NET USE \\server\share\dir
- *     $ git -C //server/share/repo status
- *
- * (c) Windows allows "SUBST" to create a fake drive mapping to an
- *     arbitrary path (which may be remote)
- *
- *     $ SUBST Q: Z:\repo
- *     $ git -C Q:/ status
- *
- * (d) Windows allows a directory symlink to be created on a local
- *     file system that points to a remote repo.
- *
- *     $ mklink /d ./link //server/share/repo
- *     $ git -C ./link status
- */
-static enum fsmonitor_reason check_remote(struct repository *r)
-{
-       int ret;
-       wchar_t wpath[MAX_PATH];
-       wchar_t wfullpath[MAX_PATH];
-       size_t wlen;
-       UINT driveType;
-
-       /*
-        * Do everything in wide chars because the drive letter might be
-        * a multi-byte sequence.  See win32_has_dos_drive_prefix().
-        */
-       if (xutftowcs_path(wpath, r->worktree) < 0)
-               return FSMONITOR_REASON_ERROR;
-
-       /*
-        * GetDriveTypeW() requires a final slash.  We assume that the
-        * worktree pathname points to an actual directory.
-        */
-       wlen = wcslen(wpath);
-       if (wpath[wlen - 1] != L'\\' && wpath[wlen - 1] != L'/') {
-               wpath[wlen++] = L'\\';
-               wpath[wlen] = 0;
-       }
-
-       /*
-        * Normalize the path.  If nothing else, this converts forward
-        * slashes to backslashes.  This is essential to get GetDriveTypeW()
-        * correctly handle some UNC "\\server\share\..." paths.
-        */
-       if (!GetFullPathNameW(wpath, MAX_PATH, wfullpath, NULL))
-               return FSMONITOR_REASON_ERROR;
-
-       driveType = GetDriveTypeW(wfullpath);
-       trace_printf_key(&trace_fsmonitor,
-                        "DriveType '%s' L'%ls' (%u)",
-                        r->worktree, wfullpath, driveType);
-
-       if (driveType == DRIVE_REMOTE) {
-               trace_printf_key(&trace_fsmonitor,
-                                "check_remote('%s') true",
-                                r->worktree);
-
-               ret = check_remote_protocol(wfullpath);
-               if (ret < 0)
-                       return FSMONITOR_REASON_ERROR;
-
-               switch (check_config_allowremote(r)) {
-               case 0: /* config overrides and disables */
-                       return FSMONITOR_REASON_REMOTE;
-               case 1: /* config overrides and enables */
-                       return FSMONITOR_REASON_OK;
-               default:
-                       break; /* config has no opinion */
-               }
-
-               return FSMONITOR_REASON_REMOTE;
-       }
-
-       return FSMONITOR_REASON_OK;
-}
-
-enum fsmonitor_reason fsm_os__incompatible(struct repository *r)
+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)
 {
        enum fsmonitor_reason reason;
 
@@ -197,9 +33,5 @@ enum fsmonitor_reason fsm_os__incompatible(struct repository *r)
        if (reason != FSMONITOR_REASON_OK)
                return reason;
 
-       reason = check_remote(r);
-       if (reason != FSMONITOR_REASON_OK)
-               return reason;
-
        return FSMONITOR_REASON_OK;
 }
index ea2a531be87494d9db48fc5dd72aa5c6ffb606b2..787738e6fa3377297e83deea54507e9e05b944b6 100644 (file)
@@ -308,6 +308,8 @@ if(SUPPORTS_SIMPLE_IPC)
                add_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)
                list(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)
                list(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)
+               list(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-win32.c)
+               list(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)
 
                add_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)
                list(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-win32.c)
@@ -315,6 +317,8 @@ if(SUPPORTS_SIMPLE_IPC)
                add_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)
                list(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)
                list(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)
+               list(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-darwin.c)
+               list(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)
 
                add_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)
                list(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-darwin.c)
index bc57cc65884b97dc6a0d33a067f00dac35ee19a8..9fb998ae090325ba0e0080d31798b7b01870b30e 100755 (executable)
@@ -356,7 +356,10 @@ sub read_credential_data_from_stdin {
                next unless m/^([^=]+)=(.+)/;
 
                my ($token, $value) = ($1, $2);
-               die "Unknown search token $token" unless exists $q{$token};
+
+               # skip any unknown tokens
+               next unless exists $q{$token};
+
                $q{$token} = $value;
                log_debug("We were given search token $token and value $value");
        }
index bf77748d602fec651661075cbd6dd32951d065d2..e29cc28779dbb846de8446d4dce3a86dfe0ece5f 100644 (file)
@@ -159,6 +159,11 @@ static void read_credential(void)
                        username = xstrdup(v);
                else if (!strcmp(buf, "password"))
                        password = xstrdup(v);
+               /*
+                * Ignore other lines; we don't know what they mean, but
+                * this future-proofs us when later versions of git do
+                * learn new lines, and the helpers are updated to match.
+                */
        }
 }
 
index 5091048f9c649f31f0c11c49f33a7e757043023a..ead6e267c78120f06bfd2e74def8bbe2f4b66e50 100644 (file)
@@ -278,8 +278,11 @@ static void read_credential(void)
                        wusername = utf8_to_utf16_dup(v);
                } else if (!strcmp(buf, "password"))
                        password = utf8_to_utf16_dup(v);
-               else
-                       die("unrecognized input");
+               /*
+                * Ignore other lines; we don't know what they mean, but
+                * this future-proofs us when later versions of git do
+                * learn new lines, and the helpers are updated to match.
+                */
        }
 }
 
index 7f64156b8bfec6bb5ec17038eee0d73c68487988..85cbefa5afd7c00e903232011ef3d2ff7aca4a8f 100644 (file)
@@ -20,9 +20,20 @@ static void suppress(struct rev_info *revs)
        revs->remerge_diff = 0;
 }
 
-static void set_separate(struct rev_info *revs)
+static void common_setup(struct rev_info *revs)
 {
        suppress(revs);
+       revs->merges_need_diff = 1;
+}
+
+static void set_none(struct rev_info *revs)
+{
+       suppress(revs);
+}
+
+static void set_separate(struct rev_info *revs)
+{
+       common_setup(revs);
        revs->separate_merges = 1;
        revs->simplify_history = 0;
 }
@@ -35,21 +46,21 @@ static void set_first_parent(struct rev_info *revs)
 
 static void set_combined(struct rev_info *revs)
 {
-       suppress(revs);
+       common_setup(revs);
        revs->combine_merges = 1;
        revs->dense_combined_merges = 0;
 }
 
 static void set_dense_combined(struct rev_info *revs)
 {
-       suppress(revs);
+       common_setup(revs);
        revs->combine_merges = 1;
        revs->dense_combined_merges = 1;
 }
 
 static void set_remerge_diff(struct rev_info *revs)
 {
-       suppress(revs);
+       common_setup(revs);
        revs->remerge_diff = 1;
        revs->simplify_history = 0;
 }
@@ -57,18 +68,18 @@ static void set_remerge_diff(struct rev_info *revs)
 static diff_merges_setup_func_t func_by_opt(const char *optarg)
 {
        if (!strcmp(optarg, "off") || !strcmp(optarg, "none"))
-               return suppress;
+               return set_none;
        if (!strcmp(optarg, "1") || !strcmp(optarg, "first-parent"))
                return set_first_parent;
-       else if (!strcmp(optarg, "separate"))
+       if (!strcmp(optarg, "separate"))
                return set_separate;
-       else if (!strcmp(optarg, "c") || !strcmp(optarg, "combined"))
+       if (!strcmp(optarg, "c") || !strcmp(optarg, "combined"))
                return set_combined;
-       else if (!strcmp(optarg, "cc") || !strcmp(optarg, "dense-combined"))
+       if (!strcmp(optarg, "cc") || !strcmp(optarg, "dense-combined"))
                return set_dense_combined;
-       else if (!strcmp(optarg, "r") || !strcmp(optarg, "remerge"))
+       if (!strcmp(optarg, "r") || !strcmp(optarg, "remerge"))
                return set_remerge_diff;
-       else if (!strcmp(optarg, "m") || !strcmp(optarg, "on"))
+       if (!strcmp(optarg, "m") || !strcmp(optarg, "on"))
                return set_to_default;
        return NULL;
 }
@@ -81,10 +92,6 @@ static void set_diff_merges(struct rev_info *revs, const char *optarg)
                die(_("invalid value for '%s': '%s'"), "--diff-merges", optarg);
 
        func(revs);
-
-       /* NOTE: the merges_need_diff flag is cleared by func() call */
-       if (func != suppress)
-               revs->merges_need_diff = 1;
 }
 
 /*
@@ -115,6 +122,7 @@ int diff_merges_parse_opts(struct rev_info *revs, const char **argv)
 
        if (!suppress_m_parsing && !strcmp(arg, "-m")) {
                set_to_default(revs);
+               revs->merges_need_diff = 0;
        } else if (!strcmp(arg, "-c")) {
                set_combined(revs);
                revs->merges_imply_patch = 1;
@@ -125,7 +133,7 @@ int diff_merges_parse_opts(struct rev_info *revs, const char **argv)
                set_remerge_diff(revs);
                revs->merges_imply_patch = 1;
        } else if (!strcmp(arg, "--no-diff-merges")) {
-               suppress(revs);
+               set_none(revs);
        } else if (!strcmp(arg, "--combined-all-paths")) {
                revs->combined_all_paths = 1;
        } else if ((argcount = parse_long_opt("diff-merges", argv, &optarg))) {
@@ -139,7 +147,7 @@ int diff_merges_parse_opts(struct rev_info *revs, const char **argv)
 
 void diff_merges_suppress(struct rev_info *revs)
 {
-       suppress(revs);
+       set_none(revs);
 }
 
 void diff_merges_default_to_first_parent(struct rev_info *revs)
diff --git a/dir.c b/dir.c
index 75429508200a9d8b591d23586ecab69fe6011a0d..d604d1bab982980fd5506a0fae072bac3510a5ae 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -669,9 +669,7 @@ int pl_hashmap_cmp(const void *cmp_data UNUSED,
                         ? ee1->patternlen
                         : ee2->patternlen;
 
-       if (ignore_case)
-               return strncasecmp(ee1->pattern, ee2->pattern, min_len);
-       return strncmp(ee1->pattern, ee2->pattern, min_len);
+       return fspathncmp(ee1->pattern, ee2->pattern, min_len);
 }
 
 static char *dup_and_filter_pattern(const char *pattern)
index 2102a5c9ff5bb07345064386b8ef9b618ebf86d8..e24838f9a86acc5de6980ac1c68d9d20421c3d42 100644 (file)
@@ -8,6 +8,7 @@
 #include "run-command.h"
 #include "simple-ipc.h"
 #include "thread-utils.h"
+#include "fsmonitor-path-utils.h"
 
 struct fsmonitor_batch;
 struct fsmonitor_token_data;
@@ -43,6 +44,7 @@ struct fsmonitor_daemon_state {
 
        struct strbuf path_worktree_watch;
        struct strbuf path_gitdir_watch;
+       struct alias_info alias;
        int nr_paths_watching;
 
        struct fsmonitor_token_data *current_token_data;
@@ -59,6 +61,7 @@ struct fsmonitor_daemon_state {
 
        struct ipc_server_data *ipc_server_data;
        struct strbuf path_ipc;
+
 };
 
 /*
index 789e7397baa48d9172213b5319618d81ff6d60d1..c0f42301c849c7d697816a0e56577283dba32949 100644 (file)
@@ -18,7 +18,7 @@ int fsmonitor_ipc__is_supported(void)
        return 0;
 }
 
-const char *fsmonitor_ipc__get_path(void)
+const char *fsmonitor_ipc__get_path(struct repository *r)
 {
        return NULL;
 }
@@ -47,11 +47,9 @@ int fsmonitor_ipc__is_supported(void)
        return 1;
 }
 
-GIT_PATH_FUNC(fsmonitor_ipc__get_path, "fsmonitor--daemon.ipc")
-
 enum ipc_active_state fsmonitor_ipc__get_state(void)
 {
-       return ipc_get_active_state(fsmonitor_ipc__get_path());
+       return ipc_get_active_state(fsmonitor_ipc__get_path(the_repository));
 }
 
 static int spawn_daemon(void)
@@ -81,8 +79,8 @@ int fsmonitor_ipc__send_query(const char *since_token,
        trace2_data_string("fsm_client", NULL, "query/command", tok);
 
 try_again:
-       state = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,
-                                      &connection);
+       state = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),
+                                               &options, &connection);
 
        switch (state) {
        case IPC_STATE__LISTENING:
@@ -117,13 +115,13 @@ try_again:
 
        case IPC_STATE__INVALID_PATH:
                ret = error(_("fsmonitor_ipc__send_query: invalid path '%s'"),
-                           fsmonitor_ipc__get_path());
+                           fsmonitor_ipc__get_path(the_repository));
                goto done;
 
        case IPC_STATE__OTHER_ERROR:
        default:
                ret = error(_("fsmonitor_ipc__send_query: unspecified error on '%s'"),
-                           fsmonitor_ipc__get_path());
+                           fsmonitor_ipc__get_path(the_repository));
                goto done;
        }
 
@@ -149,8 +147,8 @@ int fsmonitor_ipc__send_command(const char *command,
        options.wait_if_busy = 1;
        options.wait_if_not_found = 0;
 
-       state = ipc_client_try_connect(fsmonitor_ipc__get_path(), &options,
-                                      &connection);
+       state = ipc_client_try_connect(fsmonitor_ipc__get_path(the_repository),
+                                               &options, &connection);
        if (state != IPC_STATE__LISTENING) {
                die(_("fsmonitor--daemon is not running"));
                return -1;
index b6a7067c3af511bda2e3c2f4d4a8af436b8f5ad2..8b489da762b04756b2373332eb72634889d96b7f 100644 (file)
@@ -3,6 +3,8 @@
 
 #include "simple-ipc.h"
 
+struct repository;
+
 /*
  * Returns true if built-in file system monitor daemon is defined
  * for this platform.
@@ -16,7 +18,7 @@ int fsmonitor_ipc__is_supported(void);
  *
  * Returns NULL if the daemon is not supported on this platform.
  */
-const char *fsmonitor_ipc__get_path(void);
+const char *fsmonitor_ipc__get_path(struct repository *r);
 
 /*
  * Try to determine whether there is a `git-fsmonitor--daemon` process
diff --git a/fsmonitor-path-utils.h b/fsmonitor-path-utils.h
new file mode 100644 (file)
index 0000000..5bfdfb8
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef FSM_PATH_UTILS_H
+#define FSM_PATH_UTILS_H
+
+#include "strbuf.h"
+
+struct alias_info
+{
+       struct strbuf alias;
+       struct strbuf points_to;
+};
+
+struct fs_info {
+       int is_remote;
+       char *typename;
+};
+
+/*
+ * Get some basic filesystem information for the given path
+ *
+ * The caller owns the storage that is occupied by fs_info and
+ * is responsible for releasing it.
+ *
+ * Returns -1 on error, zero otherwise.
+ */
+int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info);
+
+/*
+ * Determines if the filesystem that path resides on is remote.
+ *
+ * Returns -1 on error, 0 if not remote, 1 if remote.
+ */
+int fsmonitor__is_fs_remote(const char *path);
+
+/*
+ * Get the alias in given path, if any.
+ *
+ * Sets alias to the first alias that matches any part of the path.
+ *
+ * If an alias is found, info.alias and info.points_to are set to the
+ * found mapping.
+ *
+ * Returns -1 on error, 0 otherwise.
+ *
+ * The caller owns the storage that is occupied by info.alias and
+ * info.points_to and is responsible for releasing it.
+ */
+int fsmonitor__get_alias(const char *path, struct alias_info *info);
+
+/*
+ * Resolve the path against the given alias.
+ *
+ * Returns the resolved path if there is one, NULL otherwise.
+ *
+ * The caller owns the storage that the returned string occupies and
+ * is responsible for releasing it.
+ */
+char *fsmonitor__resolve_alias(const char *path,
+       const struct alias_info *info);
+
+#endif
index 464424a1e924c63073a501fc0b9c030904074314..ee63a97dc51081c5b600cf14c2c2279334c1faa9 100644 (file)
@@ -1,7 +1,9 @@
 #include "cache.h"
 #include "config.h"
 #include "repository.h"
+#include "fsmonitor-ipc.h"
 #include "fsmonitor-settings.h"
+#include "fsmonitor-path-utils.h"
 
 /*
  * We keep this structure defintion private and have getters
@@ -13,7 +15,53 @@ struct fsmonitor_settings {
        char *hook_path;
 };
 
-static enum fsmonitor_reason check_for_incompatible(struct repository *r)
+/*
+ * Remote working directories are problematic for FSMonitor.
+ *
+ * The underlying file system on the server machine and/or the remote
+ * mount type dictates whether notification events are available at
+ * all to remote client machines.
+ *
+ * Kernel differences between the server and client machines also
+ * dictate the how (buffering, frequency, de-dup) the events are
+ * delivered to client machine processes.
+ *
+ * A client machine (such as a laptop) may choose to suspend/resume
+ * and it is unclear (without lots of testing) whether the watcher can
+ * resync after a resume.  We might be able to treat this as a normal
+ * "events were dropped by the kernel" event and do our normal "flush
+ * and resync" --or-- we might need to close the existing (zombie?)
+ * notification fd and create a new one.
+ *
+ * In theory, the above issues need to be addressed whether we are
+ * using the Hook or IPC API.
+ *
+ * So (for now at least), mark remote working directories as
+ * incompatible unless 'fsmonitor.allowRemote' is true.
+ *
+ */
+#ifdef HAVE_FSMONITOR_OS_SETTINGS
+static enum fsmonitor_reason check_remote(struct repository *r)
+{
+       int allow_remote = -1; /* -1 unset, 0 not allowed, 1 allowed */
+       int is_remote = fsmonitor__is_fs_remote(r->worktree);
+
+       switch (is_remote) {
+               case 0:
+                       return FSMONITOR_REASON_OK;
+               case 1:
+                       repo_config_get_bool(r, "fsmonitor.allowremote", &allow_remote);
+                       if (allow_remote < 1)
+                               return FSMONITOR_REASON_REMOTE;
+                       else
+                               return FSMONITOR_REASON_OK;
+               default:
+                       return FSMONITOR_REASON_ERROR;
+       }
+}
+#endif
+
+static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc)
 {
        if (!r->worktree) {
                /*
@@ -27,7 +75,10 @@ static enum fsmonitor_reason check_for_incompatible(struct repository *r)
        {
                enum fsmonitor_reason reason;
 
-               reason = fsm_os__incompatible(r);
+               reason = check_remote(r);
+               if (reason != FSMONITOR_REASON_OK)
+                       return reason;
+               reason = fsm_os__incompatible(r, ipc);
                if (reason != FSMONITOR_REASON_OK)
                        return reason;
        }
@@ -112,7 +163,7 @@ const char *fsm_settings__get_hook_path(struct repository *r)
 
 void fsm_settings__set_ipc(struct repository *r)
 {
-       enum fsmonitor_reason reason = check_for_incompatible(r);
+       enum fsmonitor_reason reason = check_for_incompatible(r, 1);
 
        if (reason != FSMONITOR_REASON_OK) {
                fsm_settings__set_incompatible(r, reason);
@@ -135,7 +186,7 @@ void fsm_settings__set_ipc(struct repository *r)
 
 void fsm_settings__set_hook(struct repository *r, const char *path)
 {
-       enum fsmonitor_reason reason = check_for_incompatible(r);
+       enum fsmonitor_reason reason = check_for_incompatible(r, 0);
 
        if (reason != FSMONITOR_REASON_OK) {
                fsm_settings__set_incompatible(r, reason);
@@ -192,10 +243,11 @@ enum fsmonitor_reason fsm_settings__get_reason(struct repository *r)
        return r->settings.fsmonitor->reason;
 }
 
-char *fsm_settings__get_incompatible_msg(const struct repository *r,
+char *fsm_settings__get_incompatible_msg(struct repository *r,
                                         enum fsmonitor_reason reason)
 {
        struct strbuf msg = STRBUF_INIT;
+       const char *socket_dir;
 
        switch (reason) {
        case FSMONITOR_REASON_UNTESTED:
@@ -231,9 +283,11 @@ char *fsm_settings__get_incompatible_msg(const struct repository *r,
                goto done;
 
        case FSMONITOR_REASON_NOSOCKETS:
+               socket_dir = dirname((char *)fsmonitor_ipc__get_path(r));
                strbuf_addf(&msg,
-                           _("repository '%s' is incompatible with fsmonitor due to lack of Unix sockets"),
-                           r->worktree);
+                           _("socket directory '%s' is incompatible with fsmonitor due"
+                             " to lack of Unix sockets support"),
+                           socket_dir);
                goto done;
        }
 
index d9c2605197ff8e957ffb0346dba78d85d3e1dab7..ab02e3995ee8f4eb3739359ea4b31f2a9bf697c9 100644 (file)
@@ -33,7 +33,7 @@ enum fsmonitor_mode fsm_settings__get_mode(struct repository *r);
 const char *fsm_settings__get_hook_path(struct repository *r);
 
 enum fsmonitor_reason fsm_settings__get_reason(struct repository *r);
-char *fsm_settings__get_incompatible_msg(const struct repository *r,
+char *fsm_settings__get_incompatible_msg(struct repository *r,
                                         enum fsmonitor_reason reason);
 
 struct fsmonitor_settings;
@@ -48,7 +48,7 @@ struct fsmonitor_settings;
  * fsm_os__* routines should considered private to fsm_settings__
  * routines.
  */
-enum fsmonitor_reason fsm_os__incompatible(struct repository *r);
+enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc);
 #endif /* HAVE_FSMONITOR_OS_SETTINGS */
 
 #endif /* FSMONITOR_SETTINGS_H */
index 57d6a483beede8e3b254634cdb944b19445875d1..08af00c73872d2c3516d6bb419c099e058fd6345 100644 (file)
@@ -295,6 +295,7 @@ static int fsmonitor_force_update_threshold = 100;
 
 void refresh_fsmonitor(struct index_state *istate)
 {
+       static int warn_once = 0;
        struct strbuf query_result = STRBUF_INIT;
        int query_success = 0, hook_version = -1;
        size_t bol = 0; /* beginning of line */
@@ -305,6 +306,14 @@ void refresh_fsmonitor(struct index_state *istate)
        int is_trivial = 0;
        struct repository *r = istate->repo ? istate->repo : the_repository;
        enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(r);
+       enum fsmonitor_reason reason = fsm_settings__get_reason(r);
+
+       if (!warn_once && reason > FSMONITOR_REASON_OK) {
+               char *msg = fsm_settings__get_incompatible_msg(r, reason);
+               warn_once = 1;
+               warning("%s", msg);
+               free(msg);
+       }
 
        if (fsm_mode <= FSMONITOR_MODE_DISABLED ||
            istate->fsmonitor_has_run_once)
index bb5ba1fe7cc5979255fe79fd8dfc0085fc8df8f1..f139008d0a385140f354e6d2b36bd520c1b555b4 100644 (file)
--- a/gettext.c
+++ b/gettext.c
@@ -10,7 +10,6 @@
 #include "config.h"
 
 #ifndef NO_GETTEXT
-#      include <locale.h>
 #      include <libintl.h>
 #      ifdef GIT_WINDOWS_NATIVE
 
@@ -80,7 +79,6 @@ static int test_vsnprintf(const char *fmt, ...)
 
 static void init_gettext_charset(const char *domain)
 {
-       setlocale(LC_CTYPE, "");
        charset = locale_charset();
        bind_textdomain_codeset(domain, charset);
 
index b90b64718eb610f9303fde76b0f207e22a4c9be6..ea53ea4a786a67419a49058750b0947a4e96838d 100644 (file)
@@ -189,9 +189,12 @@ struct strbuf;
 #define _NETBSD_SOURCE 1
 #define _SGI_SOURCE 1
 
-#if defined(__GNUC__)
+#if GIT_GNUC_PREREQ(4, 5)
 #define UNUSED __attribute__((unused)) \
        __attribute__((deprecated ("parameter declared as UNUSED")))
+#elif defined(__GNUC__)
+#define UNUSED __attribute__((unused)) \
+       __attribute__((deprecated))
 #else
 #define UNUSED
 #endif
@@ -222,6 +225,7 @@ struct strbuf;
 #endif
 #include <errno.h>
 #include <limits.h>
+#include <locale.h>
 #ifdef NEEDS_SYS_PARAM_H
 #include <sys/param.h>
 #endif
index 9aa714bdeea81e3c781cd8413b9299263030d85d..f877a1ea56460de8e7c831b58ab4cfbda837a304 100644 (file)
@@ -1059,12 +1059,11 @@ static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
        strbuf_addbuf(&ssh_signature_filename, &buffer_file->filename);
        strbuf_addstr(&ssh_signature_filename, ".sig");
        if (strbuf_read_file(signature, ssh_signature_filename.buf, 0) < 0) {
-               error_errno(
+               ret = error_errno(
                        _("failed reading ssh signing data buffer from '%s'"),
                        ssh_signature_filename.buf);
+               goto out;
        }
-       unlink_or_warn(ssh_signature_filename.buf);
-
        /* Strip CR from the line endings, in case we are on Windows. */
        remove_cr_after(signature, bottom);
 
@@ -1073,6 +1072,8 @@ out:
                delete_tempfile(&key_file);
        if (buffer_file)
                delete_tempfile(&buffer_file);
+       if (ssh_signature_filename.len)
+               unlink_or_warn(ssh_signature_filename.buf);
        strbuf_release(&signer_stderr);
        strbuf_release(&ssh_signature_filename);
        FREE_AND_NULL(ssh_signing_key_file);
index 9621ba62a394348a95f6e5e181aea286c6c37b56..833d28612f77473e9a50d6329ba55bd9706ba81d 100644 (file)
@@ -317,7 +317,7 @@ static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)
                        pos = strchr(subject->buf + at, ']');
                        if (!pos)
                                break;
-                       remove = pos - subject->buf + at + 1;
+                       remove = pos - (subject->buf + at) + 1;
                        if (!mi->keep_non_patch_brackets_in_subject ||
                            (7 <= remove &&
                             memmem(subject->buf + at, remove, "PATCH", 5)))
index 99dcee2db8afa4c06f88739601e98bc3cf3dd64c..e5f41cce48143db0526676ebc4949a4431cb8d9f 100644 (file)
@@ -2807,6 +2807,8 @@ static int process_renames(struct merge_options *opt,
                                                           pathnames,
                                                           1 + 2 * opt->priv->call_depth,
                                                           &merged);
+                       if (clean_merge < 0)
+                               return -1;
                        if (!clean_merge &&
                            merged.mode == side1->stages[1].mode &&
                            oideq(&merged.oid, &side1->stages[1].oid))
@@ -2916,7 +2918,7 @@ static int process_renames(struct merge_options *opt,
                        struct version_info merged;
 
                        struct conflict_info *base, *side1, *side2;
-                       unsigned clean;
+                       int clean;
 
                        pathnames[0] = oldpath;
                        pathnames[other_source_index] = oldpath;
@@ -2937,6 +2939,8 @@ static int process_renames(struct merge_options *opt,
                                                     pathnames,
                                                     1 + 2 * opt->priv->call_depth,
                                                     &merged);
+                       if (clean < 0)
+                               return -1;
 
                        memcpy(&newinfo->stages[target_index], &merged,
                               sizeof(merged));
@@ -3571,15 +3575,15 @@ static int tree_entry_order(const void *a_, const void *b_)
                                 b->string, strlen(b->string), bmi->result.mode);
 }
 
-static void write_tree(struct object_id *result_oid,
-                      struct string_list *versions,
-                      unsigned int offset,
-                      size_t hash_size)
+static int write_tree(struct object_id *result_oid,
+                     struct string_list *versions,
+                     unsigned int offset,
+                     size_t hash_size)
 {
        size_t maxlen = 0, extra;
        unsigned int nr;
        struct strbuf buf = STRBUF_INIT;
-       int i;
+       int i, ret = 0;
 
        assert(offset <= versions->nr);
        nr = versions->nr - offset;
@@ -3605,8 +3609,10 @@ static void write_tree(struct object_id *result_oid,
        }
 
        /* Write this object file out, and record in result_oid */
-       write_object_file(buf.buf, buf.len, OBJ_TREE, result_oid);
+       if (write_object_file(buf.buf, buf.len, OBJ_TREE, result_oid))
+               ret = -1;
        strbuf_release(&buf);
+       return ret;
 }
 
 static void record_entry_for_tree(struct directory_versions *dir_metadata,
@@ -3625,13 +3631,13 @@ static void record_entry_for_tree(struct directory_versions *dir_metadata,
                           basename)->util = &mi->result;
 }
 
-static void write_completed_directory(struct merge_options *opt,
-                                     const char *new_directory_name,
-                                     struct directory_versions *info)
+static int write_completed_directory(struct merge_options *opt,
+                                    const char *new_directory_name,
+                                    struct directory_versions *info)
 {
        const char *prev_dir;
        struct merged_info *dir_info = NULL;
-       unsigned int offset;
+       unsigned int offset, ret = 0;
 
        /*
         * Some explanation of info->versions and info->offsets...
@@ -3717,7 +3723,7 @@ static void write_completed_directory(struct merge_options *opt,
         * strcmp here.)
         */
        if (new_directory_name == info->last_directory)
-               return;
+               return 0;
 
        /*
         * If we are just starting (last_directory is NULL), or last_directory
@@ -3739,7 +3745,7 @@ static void write_completed_directory(struct merge_options *opt,
                 */
                string_list_append(&info->offsets,
                                   info->last_directory)->util = (void*)offset;
-               return;
+               return 0;
        }
 
        /*
@@ -3769,8 +3775,9 @@ static void write_completed_directory(struct merge_options *opt,
                 */
                dir_info->is_null = 0;
                dir_info->result.mode = S_IFDIR;
-               write_tree(&dir_info->result.oid, &info->versions, offset,
-                          opt->repo->hash_algo->rawsz);
+               if (write_tree(&dir_info->result.oid, &info->versions, offset,
+                              opt->repo->hash_algo->rawsz) < 0)
+                       ret = -1;
        }
 
        /*
@@ -3798,13 +3805,15 @@ static void write_completed_directory(struct merge_options *opt,
        /* And, of course, we need to update last_directory to match. */
        info->last_directory = new_directory_name;
        info->last_directory_len = strlen(info->last_directory);
+
+       return ret;
 }
 
 /* Per entry merge function */
-static void process_entry(struct merge_options *opt,
-                         const char *path,
-                         struct conflict_info *ci,
-                         struct directory_versions *dir_metadata)
+static int process_entry(struct merge_options *opt,
+                        const char *path,
+                        struct conflict_info *ci,
+                        struct directory_versions *dir_metadata)
 {
        int df_file_index = 0;
 
@@ -3818,7 +3827,7 @@ static void process_entry(struct merge_options *opt,
                record_entry_for_tree(dir_metadata, path, &ci->merged);
                if (ci->filemask == 0)
                        /* nothing else to handle */
-                       return;
+                       return 0;
                assert(ci->df_conflict);
        }
 
@@ -3865,7 +3874,7 @@ static void process_entry(struct merge_options *opt,
                 */
                if (ci->filemask == 1) {
                        ci->filemask = 0;
-                       return;
+                       return 0;
                }
 
                /*
@@ -4060,7 +4069,7 @@ static void process_entry(struct merge_options *opt,
        } else if (ci->filemask >= 6) {
                /* Need a two-way or three-way content merge */
                struct version_info merged_file;
-               unsigned clean_merge;
+               int clean_merge;
                struct version_info *o = &ci->stages[0];
                struct version_info *a = &ci->stages[1];
                struct version_info *b = &ci->stages[2];
@@ -4069,6 +4078,8 @@ static void process_entry(struct merge_options *opt,
                                                   ci->pathnames,
                                                   opt->priv->call_depth * 2,
                                                   &merged_file);
+               if (clean_merge < 0)
+                       return -1;
                ci->merged.clean = clean_merge &&
                                   !ci->df_conflict && !ci->path_conflict;
                ci->merged.result.mode = merged_file.mode;
@@ -4164,6 +4175,7 @@ static void process_entry(struct merge_options *opt,
 
        /* Record metadata for ci->merged in dir_metadata */
        record_entry_for_tree(dir_metadata, path, &ci->merged);
+       return 0;
 }
 
 static void prefetch_for_content_merges(struct merge_options *opt,
@@ -4214,8 +4226,8 @@ static void prefetch_for_content_merges(struct merge_options *opt,
        oid_array_clear(&to_fetch);
 }
 
-static void process_entries(struct merge_options *opt,
-                           struct object_id *result_oid)
+static int process_entries(struct merge_options *opt,
+                          struct object_id *result_oid)
 {
        struct hashmap_iter iter;
        struct strmap_entry *e;
@@ -4224,11 +4236,12 @@ static void process_entries(struct merge_options *opt,
        struct directory_versions dir_metadata = { STRING_LIST_INIT_NODUP,
                                                   STRING_LIST_INIT_NODUP,
                                                   NULL, 0 };
+       int ret = 0;
 
        trace2_region_enter("merge", "process_entries setup", opt->repo);
        if (strmap_empty(&opt->priv->paths)) {
                oidcpy(result_oid, opt->repo->hash_algo->empty_tree);
-               return;
+               return 0;
        }
 
        /* Hack to pre-allocate plist to the desired size */
@@ -4270,13 +4283,19 @@ static void process_entries(struct merge_options *opt,
                 */
                struct merged_info *mi = entry->util;
 
-               write_completed_directory(opt, mi->directory_name,
-                                         &dir_metadata);
+               if (write_completed_directory(opt, mi->directory_name,
+                                             &dir_metadata) < 0) {
+                       ret = -1;
+                       goto cleanup;
+               }
                if (mi->clean)
                        record_entry_for_tree(&dir_metadata, path, mi);
                else {
                        struct conflict_info *ci = (struct conflict_info *)mi;
-                       process_entry(opt, path, ci, &dir_metadata);
+                       if (process_entry(opt, path, ci, &dir_metadata) < 0) {
+                               ret = -1;
+                               goto cleanup;
+                       };
                }
        }
        trace2_region_leave("merge", "processing", opt->repo);
@@ -4291,12 +4310,16 @@ static void process_entries(struct merge_options *opt,
                fflush(stdout);
                BUG("dir_metadata accounting completely off; shouldn't happen");
        }
-       write_tree(result_oid, &dir_metadata.versions, 0,
-                  opt->repo->hash_algo->rawsz);
+       if (write_tree(result_oid, &dir_metadata.versions, 0,
+                      opt->repo->hash_algo->rawsz) < 0)
+               ret = -1;
+cleanup:
        string_list_clear(&plist, 0);
        string_list_clear(&dir_metadata.versions, 0);
        string_list_clear(&dir_metadata.offsets, 0);
        trace2_region_leave("merge", "process_entries cleanup", opt->repo);
+
+       return ret;
 }
 
 /*** Function Grouping: functions related to merge_switch_to_result() ***/
@@ -4928,15 +4951,18 @@ redo:
        }
 
        trace2_region_enter("merge", "process_entries", opt->repo);
-       process_entries(opt, &working_tree_oid);
+       if (process_entries(opt, &working_tree_oid) < 0)
+               result->clean = -1;
        trace2_region_leave("merge", "process_entries", opt->repo);
 
        /* Set return values */
        result->path_messages = &opt->priv->conflicts;
 
-       result->tree = parse_tree_indirect(&working_tree_oid);
-       /* existence of conflicted entries implies unclean */
-       result->clean &= strmap_empty(&opt->priv->conflicted);
+       if (result->clean >= 0) {
+               result->tree = parse_tree_indirect(&working_tree_oid);
+               /* existence of conflicted entries implies unclean */
+               result->clean &= strmap_empty(&opt->priv->conflicted);
+       }
        if (!opt->priv->call_depth) {
                result->priv = opt->priv;
                result->_properly_initialized = RESULT_INITIALIZED;
diff --git a/midx.c b/midx.c
index c27d0e5f1510c32ad7cbf0fca8dc1d05ec4917a3..3a8dcfe98e2e937c23d80db3e27fa89ccd36308b 100644 (file)
--- a/midx.c
+++ b/midx.c
@@ -1839,7 +1839,7 @@ int expire_midx_packs(struct repository *r, const char *object_dir, unsigned fla
                if (prepare_midx_pack(r, m, i))
                        continue;
 
-               if (m->packs[i]->pack_keep)
+               if (m->packs[i]->pack_keep || m->packs[i]->is_cruft)
                        continue;
 
                pack_name = xstrdup(m->packs[i]->pack_name);
@@ -1895,6 +1895,8 @@ static int fill_included_packs_all(struct repository *r,
                        continue;
                if (!pack_kept_objects && m->packs[i]->pack_keep)
                        continue;
+               if (m->packs[i]->is_cruft)
+                       continue;
 
                include_pack[i] = 1;
                count++;
@@ -1910,9 +1912,11 @@ static int fill_included_packs_batch(struct repository *r,
 {
        uint32_t i, packs_to_repack;
        size_t total_size;
-       struct repack_info *pack_info = xcalloc(m->num_packs, sizeof(struct repack_info));
+       struct repack_info *pack_info;
        int pack_kept_objects = 0;
 
+       CALLOC_ARRAY(pack_info, m->num_packs);
+
        repo_config_get_bool(r, "repack.packkeptobjects", &pack_kept_objects);
 
        for (i = 0; i < m->num_packs; i++) {
@@ -1924,7 +1928,7 @@ static int fill_included_packs_batch(struct repository *r,
                pack_info[i].mtime = m->packs[i]->mtime;
        }
 
-       for (i = 0; batch_size && i < m->num_objects; i++) {
+       for (i = 0; i < m->num_objects; i++) {
                uint32_t pack_int_id = nth_midxed_pack_int_id(m, i);
                pack_info[pack_int_id].referenced_objects++;
        }
@@ -1942,6 +1946,8 @@ static int fill_included_packs_batch(struct repository *r,
                        continue;
                if (!pack_kept_objects && p->pack_keep)
                        continue;
+               if (p->is_cruft)
+                       continue;
                if (open_pack_index(p) || !p->num_objects)
                        continue;
 
index 5b270f046dda1d4234af6174cd80c07f64f9dff6..5e3096023465277f194dc5cb0b3cfeb5f7eaf52c 100644 (file)
@@ -1599,10 +1599,6 @@ static int do_oid_object_info_extended(struct repository *r,
                if (fetch_if_missing && repo_has_promisor_remote(r) &&
                    !already_retried &&
                    !(flags & OBJECT_INFO_SKIP_FETCH_OBJECT)) {
-                       /*
-                        * TODO Investigate checking promisor_remote_get_direct()
-                        * TODO return value and stopping on error here.
-                        */
                        promisor_remote_get_direct(r, real, 1);
                        already_retried = 1;
                        continue;
index 2e4589bae50105da847ad5b861d1752fb26dec77..8a74eb85e94603458e0fe7eafbc58a40a3380b10 100644 (file)
--- a/object.c
+++ b/object.c
@@ -233,7 +233,8 @@ struct object *parse_object_buffer(struct repository *r, const struct object_id
                if (commit) {
                        if (parse_commit_buffer(r, commit, buffer, size, 1))
                                return NULL;
-                       if (!get_cached_commit_buffer(r, commit, NULL)) {
+                       if (save_commit_buffer &&
+                           !get_cached_commit_buffer(r, commit, NULL)) {
                                set_commit_buffer(r, commit, buffer, size);
                                *eaten_p = 1;
                        }
diff --git a/oss-fuzz/.gitignore b/oss-fuzz/.gitignore
new file mode 100644 (file)
index 0000000..9acb744
--- /dev/null
@@ -0,0 +1,3 @@
+fuzz-commit-graph
+fuzz-pack-headers
+fuzz-pack-idx
similarity index 100%
rename from fuzz-pack-idx.c
rename to oss-fuzz/fuzz-pack-idx.c
index 68f46f5ec70b93b28fa7c75988d671a4046e8fee..faa7612941cc2a70bf24f9dff2c02f2c5a2c0428 100644 (file)
@@ -4,6 +4,7 @@
 #include "config.h"
 #include "transport.h"
 #include "strvec.h"
+#include "packfile.h"
 
 struct promisor_remote_config {
        struct promisor_remote *promisors;
@@ -230,18 +231,18 @@ static int remove_fetched_oids(struct repository *repo,
        return remaining_nr;
 }
 
-int promisor_remote_get_direct(struct repository *repo,
-                              const struct object_id *oids,
-                              int oid_nr)
+void promisor_remote_get_direct(struct repository *repo,
+                               const struct object_id *oids,
+                               int oid_nr)
 {
        struct promisor_remote *r;
        struct object_id *remaining_oids = (struct object_id *)oids;
        int remaining_nr = oid_nr;
        int to_free = 0;
-       int res = -1;
+       int i;
 
        if (oid_nr == 0)
-               return 0;
+               return;
 
        promisor_remote_init(repo);
 
@@ -256,12 +257,16 @@ int promisor_remote_get_direct(struct repository *repo,
                                continue;
                        }
                }
-               res = 0;
-               break;
+               goto all_fetched;
        }
 
+       for (i = 0; i < remaining_nr; i++) {
+               if (is_promisor_object(&remaining_oids[i]))
+                       die(_("could not fetch %s from promisor remote"),
+                           oid_to_hex(&remaining_oids[i]));
+       }
+
+all_fetched:
        if (to_free)
                free(remaining_oids);
-
-       return res;
 }
index edc45ab0f5fdcd8c4145aa1c81eb3346f2eeeda9..df36eb08efc2e194b6fc4f770235fb9707770de6 100644 (file)
@@ -39,13 +39,12 @@ static inline int has_promisor_remote(void)
 
 /*
  * Fetches all requested objects from all promisor remotes, trying them one at
- * a time until all objects are fetched. Returns 0 upon success, and non-zero
- * otherwise.
+ * a time until all objects are fetched.
  *
- * If oid_nr is 0, this function returns 0 (success) immediately.
+ * If oid_nr is 0, this function returns immediately.
  */
-int promisor_remote_get_direct(struct repository *repo,
-                              const struct object_id *oids,
-                              int oid_nr);
+void promisor_remote_get_direct(struct repository *repo,
+                               const struct object_id *oids,
+                               int oid_nr);
 
 #endif /* PROMISOR_REMOTE_H */
index b09128b188431857450336481898f27f8894a241..32024029274828c12654e3c256de3d8d08f37734 100644 (file)
@@ -1873,9 +1873,20 @@ static int read_index_extension(struct index_state *istate,
        return 0;
 }
 
+/*
+ * Parses the contents of the cache entry contained within the 'ondisk' buffer
+ * into a new incore 'cache_entry'.
+ *
+ * Note that 'char *ondisk' may not be aligned to a 4-byte address interval in
+ * index v4, so we cannot cast it to 'struct ondisk_cache_entry *' and access
+ * its members. Instead, we use the byte offsets of members within the struct to
+ * identify where 'get_be16()', 'get_be32()', and 'oidread()' (which can all
+ * read from an unaligned memory buffer) should read from the 'ondisk' buffer
+ * into the corresponding incore 'cache_entry' members.
+ */
 static struct cache_entry *create_from_disk(struct mem_pool *ce_mem_pool,
                                            unsigned int version,
-                                           struct ondisk_cache_entry *ondisk,
+                                           const char *ondisk,
                                            unsigned long *ent_size,
                                            const struct cache_entry *previous_ce)
 {
@@ -1883,7 +1894,7 @@ static struct cache_entry *create_from_disk(struct mem_pool *ce_mem_pool,
        size_t len;
        const char *name;
        const unsigned hashsz = the_hash_algo->rawsz;
-       const uint16_t *flagsp = (const uint16_t *)(ondisk->data + hashsz);
+       const char *flagsp = ondisk + offsetof(struct ondisk_cache_entry, data) + hashsz;
        unsigned int flags;
        size_t copy_len = 0;
        /*
@@ -1901,15 +1912,15 @@ static struct cache_entry *create_from_disk(struct mem_pool *ce_mem_pool,
 
        if (flags & CE_EXTENDED) {
                int extended_flags;
-               extended_flags = get_be16(flagsp + 1) << 16;
+               extended_flags = get_be16(flagsp + sizeof(uint16_t)) << 16;
                /* We do not yet understand any bit out of CE_EXTENDED_FLAGS */
                if (extended_flags & ~CE_EXTENDED_FLAGS)
                        die(_("unknown index entry format 0x%08x"), extended_flags);
                flags |= extended_flags;
-               name = (const char *)(flagsp + 2);
+               name = (const char *)(flagsp + 2 * sizeof(uint16_t));
        }
        else
-               name = (const char *)(flagsp + 1);
+               name = (const char *)(flagsp + sizeof(uint16_t));
 
        if (expand_name_field) {
                const unsigned char *cp = (const unsigned char *)name;
@@ -1935,20 +1946,32 @@ static struct cache_entry *create_from_disk(struct mem_pool *ce_mem_pool,
 
        ce = mem_pool__ce_alloc(ce_mem_pool, len);
 
-       ce->ce_stat_data.sd_ctime.sec = get_be32(&ondisk->ctime.sec);
-       ce->ce_stat_data.sd_mtime.sec = get_be32(&ondisk->mtime.sec);
-       ce->ce_stat_data.sd_ctime.nsec = get_be32(&ondisk->ctime.nsec);
-       ce->ce_stat_data.sd_mtime.nsec = get_be32(&ondisk->mtime.nsec);
-       ce->ce_stat_data.sd_dev   = get_be32(&ondisk->dev);
-       ce->ce_stat_data.sd_ino   = get_be32(&ondisk->ino);
-       ce->ce_mode  = get_be32(&ondisk->mode);
-       ce->ce_stat_data.sd_uid   = get_be32(&ondisk->uid);
-       ce->ce_stat_data.sd_gid   = get_be32(&ondisk->gid);
-       ce->ce_stat_data.sd_size  = get_be32(&ondisk->size);
+       /*
+        * NEEDSWORK: using 'offsetof()' is cumbersome and should be replaced
+        * with something more akin to 'load_bitmap_entries_v1()'s use of
+        * 'read_be16'/'read_be32'. For consistency with the corresponding
+        * ondisk entry write function ('copy_cache_entry_to_ondisk()'), this
+        * should be done at the same time as removing references to
+        * 'ondisk_cache_entry' there.
+        */
+       ce->ce_stat_data.sd_ctime.sec = get_be32(ondisk + offsetof(struct ondisk_cache_entry, ctime)
+                                                       + offsetof(struct cache_time, sec));
+       ce->ce_stat_data.sd_mtime.sec = get_be32(ondisk + offsetof(struct ondisk_cache_entry, mtime)
+                                                       + offsetof(struct cache_time, sec));
+       ce->ce_stat_data.sd_ctime.nsec = get_be32(ondisk + offsetof(struct ondisk_cache_entry, ctime)
+                                                        + offsetof(struct cache_time, nsec));
+       ce->ce_stat_data.sd_mtime.nsec = get_be32(ondisk + offsetof(struct ondisk_cache_entry, mtime)
+                                                        + offsetof(struct cache_time, nsec));
+       ce->ce_stat_data.sd_dev   = get_be32(ondisk + offsetof(struct ondisk_cache_entry, dev));
+       ce->ce_stat_data.sd_ino   = get_be32(ondisk + offsetof(struct ondisk_cache_entry, ino));
+       ce->ce_mode  = get_be32(ondisk + offsetof(struct ondisk_cache_entry, mode));
+       ce->ce_stat_data.sd_uid   = get_be32(ondisk + offsetof(struct ondisk_cache_entry, uid));
+       ce->ce_stat_data.sd_gid   = get_be32(ondisk + offsetof(struct ondisk_cache_entry, gid));
+       ce->ce_stat_data.sd_size  = get_be32(ondisk + offsetof(struct ondisk_cache_entry, size));
        ce->ce_flags = flags & ~CE_NAMEMASK;
        ce->ce_namelen = len;
        ce->index = 0;
-       oidread(&ce->oid, ondisk->data);
+       oidread(&ce->oid, (const unsigned char *)ondisk + offsetof(struct ondisk_cache_entry, data));
 
        if (expand_name_field) {
                if (copy_len)
@@ -2117,12 +2140,12 @@ static unsigned long load_cache_entry_block(struct index_state *istate,
        unsigned long src_offset = start_offset;
 
        for (i = offset; i < offset + nr; i++) {
-               struct ondisk_cache_entry *disk_ce;
                struct cache_entry *ce;
                unsigned long consumed;
 
-               disk_ce = (struct ondisk_cache_entry *)(mmap + src_offset);
-               ce = create_from_disk(ce_mem_pool, istate->version, disk_ce, &consumed, previous_ce);
+               ce = create_from_disk(ce_mem_pool, istate->version,
+                                     mmap + src_offset,
+                                     &consumed, previous_ce);
                set_index_entry(istate, i, ce);
 
                src_offset += consumed;
index fd1cb14b0f131de522cd60c71127c9369fe84648..914908fac524e788cb362511deb0592e4948f62e 100644 (file)
@@ -1722,6 +1722,8 @@ char *get_head_description(void)
        } else
                strbuf_addstr(&desc, _("(no branch)"));
 
+       wt_status_state_free_buffers(&state);
+
        return strbuf_detach(&desc, NULL);
 }
 
index d258fd31995fbb4f8a198ba0bd275564182fe43c..78e9350e20526a279fd5f5cedfb3d5a726edd481 100644 (file)
--- a/reflog.c
+++ b/reflog.c
@@ -312,16 +312,9 @@ static int push_tip_to_list(const char *refname UNUSED,
 
 static int is_head(const char *refname)
 {
-       switch (ref_type(refname)) {
-       case REF_TYPE_OTHER_PSEUDOREF:
-       case REF_TYPE_MAIN_PSEUDOREF:
-               if (parse_worktree_ref(refname, NULL, NULL, &refname))
-                       BUG("not a worktree ref: %s", refname);
-               break;
-       default:
-               break;
-       }
-       return !strcmp(refname, "HEAD");
+       const char *stripped_refname;
+       parse_worktree_ref(refname, NULL, NULL, &stripped_refname);
+       return !strcmp(stripped_refname, "HEAD");
 }
 
 void reflog_expiry_prepare(const char *refname,
diff --git a/refs.c b/refs.c
index c89d558892569b6326d092eb61a7bba28ef4b3d4..1491ae937eb8c4d5be5dbe9678911fe39be390cd 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -811,7 +811,7 @@ int dwim_log(const char *str, int len, struct object_id *oid, char **log)
        return repo_dwim_log(the_repository, str, len, oid, log);
 }
 
-static int is_per_worktree_ref(const char *refname)
+int is_per_worktree_ref(const char *refname)
 {
        return starts_with(refname, "refs/worktree/") ||
               starts_with(refname, "refs/bisect/") ||
@@ -827,37 +827,63 @@ static int is_pseudoref_syntax(const char *refname)
                        return 0;
        }
 
+       /*
+        * HEAD is not a pseudoref, but it certainly uses the
+        * pseudoref syntax.
+        */
        return 1;
 }
 
-static int is_main_pseudoref_syntax(const char *refname)
-{
-       return skip_prefix(refname, "main-worktree/", &refname) &&
-               *refname &&
-               is_pseudoref_syntax(refname);
+static int is_current_worktree_ref(const char *ref) {
+       return is_pseudoref_syntax(ref) || is_per_worktree_ref(ref);
 }
 
-static int is_other_pseudoref_syntax(const char *refname)
+enum ref_worktree_type parse_worktree_ref(const char *maybe_worktree_ref,
+                                         const char **worktree_name, int *worktree_name_length,
+                                         const char **bare_refname)
 {
-       if (!skip_prefix(refname, "worktrees/", &refname))
-               return 0;
-       refname = strchr(refname, '/');
-       if (!refname || !refname[1])
-               return 0;
-       return is_pseudoref_syntax(refname + 1);
-}
+       const char *name_dummy;
+       int name_length_dummy;
+       const char *ref_dummy;
 
-enum ref_type ref_type(const char *refname)
-{
-       if (is_per_worktree_ref(refname))
-               return REF_TYPE_PER_WORKTREE;
-       if (is_pseudoref_syntax(refname))
-               return REF_TYPE_PSEUDOREF;
-       if (is_main_pseudoref_syntax(refname))
-               return REF_TYPE_MAIN_PSEUDOREF;
-       if (is_other_pseudoref_syntax(refname))
-               return REF_TYPE_OTHER_PSEUDOREF;
-       return REF_TYPE_NORMAL;
+       if (!worktree_name)
+               worktree_name = &name_dummy;
+       if (!worktree_name_length)
+               worktree_name_length = &name_length_dummy;
+       if (!bare_refname)
+               bare_refname = &ref_dummy;
+
+       if (skip_prefix(maybe_worktree_ref, "worktrees/", bare_refname)) {
+               const char *slash = strchr(*bare_refname, '/');
+
+               *worktree_name = *bare_refname;
+               if (!slash) {
+                       *worktree_name_length = strlen(*worktree_name);
+
+                       /* This is an error condition, and the caller tell because the bare_refname is "" */
+                       *bare_refname = *worktree_name + *worktree_name_length;
+                       return REF_WORKTREE_OTHER;
+               }
+
+               *worktree_name_length = slash - *bare_refname;
+               *bare_refname = slash + 1;
+
+               if (is_current_worktree_ref(*bare_refname))
+                       return REF_WORKTREE_OTHER;
+       }
+
+       *worktree_name = NULL;
+       *worktree_name_length = 0;
+
+       if (skip_prefix(maybe_worktree_ref, "main-worktree/", bare_refname)
+           && is_current_worktree_ref(*bare_refname))
+               return REF_WORKTREE_MAIN;
+
+       *bare_refname = maybe_worktree_ref;
+       if (is_current_worktree_ref(maybe_worktree_ref))
+               return REF_WORKTREE_CURRENT;
+
+       return REF_WORKTREE_SHARED;
 }
 
 long get_files_ref_lock_timeout_ms(void)
diff --git a/refs.h b/refs.h
index d6575b8c2bdf0d3e9021f7000b42308ff33203af..8958717a17dd161618a89034f95d72064000290f 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -820,15 +820,34 @@ int parse_hide_refs_config(const char *var, const char *value, const char *);
  */
 int ref_is_hidden(const char *, const char *);
 
-enum ref_type {
-       REF_TYPE_PER_WORKTREE,    /* refs inside refs/ but not shared       */
-       REF_TYPE_PSEUDOREF,       /* refs outside refs/ in current worktree */
-       REF_TYPE_MAIN_PSEUDOREF,  /* pseudo refs from the main worktree     */
-       REF_TYPE_OTHER_PSEUDOREF, /* pseudo refs from other worktrees       */
-       REF_TYPE_NORMAL,          /* normal/shared refs inside refs/        */
+/* Is this a per-worktree ref living in the refs/ namespace? */
+int is_per_worktree_ref(const char *refname);
+
+/* Describes how a refname relates to worktrees */
+enum ref_worktree_type {
+       REF_WORKTREE_CURRENT, /* implicitly per worktree, eg. HEAD or
+                                refs/bisect/SOMETHING */
+       REF_WORKTREE_MAIN, /* explicitly in main worktree, eg.
+                             main-worktree/HEAD */
+       REF_WORKTREE_OTHER, /* explicitly in named worktree, eg.
+                              worktrees/bla/HEAD */
+       REF_WORKTREE_SHARED, /* the default, eg. refs/heads/main */
 };
 
-enum ref_type ref_type(const char *refname);
+/*
+ * Parse a `maybe_worktree_ref` as a ref that possibly refers to a worktree ref
+ * (ie. either REFNAME, main-worktree/REFNAME or worktree/WORKTREE/REFNAME). It
+ * returns what kind of ref was found, and in case of REF_WORKTREE_OTHER, the
+ * worktree name is returned in `worktree_name` (pointing into
+ * `maybe_worktree_ref`) and `worktree_name_length`. The bare refname (the
+ * refname stripped of prefixes) is returned in `bare_refname`. The
+ * `worktree_name`, `worktree_name_length` and `bare_refname` arguments may be
+ * NULL.
+ */
+enum ref_worktree_type parse_worktree_ref(const char *maybe_worktree_ref,
+                                         const char **worktree_name,
+                                         int *worktree_name_length,
+                                         const char **bare_refname);
 
 enum expire_reflog_flags {
        EXPIRE_REFLOGS_DRY_RUN = 1 << 0,
index e4009b3c421f5bddf9b3480a9fc881cee0d2307b..b89954355debd64056e045a5f433647ca8ba0160 100644 (file)
@@ -138,44 +138,30 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store,
        return refs;
 }
 
-static void files_reflog_path_other_worktrees(struct files_ref_store *refs,
-                                             struct strbuf *sb,
-                                             const char *refname)
-{
-       const char *real_ref;
-       const char *worktree_name;
-       int length;
-
-       if (parse_worktree_ref(refname, &worktree_name, &length, &real_ref))
-               BUG("refname %s is not a other-worktree ref", refname);
-
-       if (worktree_name)
-               strbuf_addf(sb, "%s/worktrees/%.*s/logs/%s", refs->gitcommondir,
-                           length, worktree_name, real_ref);
-       else
-               strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir,
-                           real_ref);
-}
-
 static void files_reflog_path(struct files_ref_store *refs,
                              struct strbuf *sb,
                              const char *refname)
 {
-       switch (ref_type(refname)) {
-       case REF_TYPE_PER_WORKTREE:
-       case REF_TYPE_PSEUDOREF:
+       const char *bare_refname;
+       const char *wtname;
+       int wtname_len;
+       enum ref_worktree_type wt_type = parse_worktree_ref(
+               refname, &wtname, &wtname_len, &bare_refname);
+
+       switch (wt_type) {
+       case REF_WORKTREE_CURRENT:
                strbuf_addf(sb, "%s/logs/%s", refs->base.gitdir, refname);
                break;
-       case REF_TYPE_OTHER_PSEUDOREF:
-       case REF_TYPE_MAIN_PSEUDOREF:
-               files_reflog_path_other_worktrees(refs, sb, refname);
+       case REF_WORKTREE_SHARED:
+       case REF_WORKTREE_MAIN:
+               strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, bare_refname);
                break;
-       case REF_TYPE_NORMAL:
-               strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, refname);
+       case REF_WORKTREE_OTHER:
+               strbuf_addf(sb, "%s/worktrees/%.*s/logs/%s", refs->gitcommondir,
+                           wtname_len, wtname, bare_refname);
                break;
        default:
-               BUG("unknown ref type %d of ref %s",
-                   ref_type(refname), refname);
+               BUG("unknown ref type %d of ref %s", wt_type, refname);
        }
 }
 
@@ -183,22 +169,25 @@ static void files_ref_path(struct files_ref_store *refs,
                           struct strbuf *sb,
                           const char *refname)
 {
-       switch (ref_type(refname)) {
-       case REF_TYPE_PER_WORKTREE:
-       case REF_TYPE_PSEUDOREF:
+       const char *bare_refname;
+       const char *wtname;
+       int wtname_len;
+       enum ref_worktree_type wt_type = parse_worktree_ref(
+               refname, &wtname, &wtname_len, &bare_refname);
+       switch (wt_type) {
+       case REF_WORKTREE_CURRENT:
                strbuf_addf(sb, "%s/%s", refs->base.gitdir, refname);
                break;
-       case REF_TYPE_MAIN_PSEUDOREF:
-               if (!skip_prefix(refname, "main-worktree/", &refname))
-                       BUG("ref %s is not a main pseudoref", refname);
-               /* fallthrough */
-       case REF_TYPE_OTHER_PSEUDOREF:
-       case REF_TYPE_NORMAL:
-               strbuf_addf(sb, "%s/%s", refs->gitcommondir, refname);
+       case REF_WORKTREE_OTHER:
+               strbuf_addf(sb, "%s/worktrees/%.*s/%s", refs->gitcommondir,
+                           wtname_len, wtname, bare_refname);
+               break;
+       case REF_WORKTREE_SHARED:
+       case REF_WORKTREE_MAIN:
+               strbuf_addf(sb, "%s/%s", refs->gitcommondir, bare_refname);
                break;
        default:
-               BUG("unknown ref type %d of ref %s",
-                   ref_type(refname), refname);
+               BUG("unknown ref type %d of ref %s", wt_type, refname);
        }
 }
 
@@ -771,7 +760,8 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
 
        while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
                if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
-                   ref_type(iter->iter0->refname) != REF_TYPE_PER_WORKTREE)
+                   parse_worktree_ref(iter->iter0->refname, NULL, NULL,
+                                      NULL) != REF_WORKTREE_CURRENT)
                        continue;
 
                if ((iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS) &&
@@ -1178,7 +1168,8 @@ static int should_pack_ref(const char *refname,
                           unsigned int pack_flags)
 {
        /* Do not pack per-worktree refs: */
-       if (ref_type(refname) != REF_TYPE_NORMAL)
+       if (parse_worktree_ref(refname, NULL, NULL, NULL) !=
+           REF_WORKTREE_SHARED)
                return 0;
 
        /* Do not pack non-tags unless PACK_REFS_ALL is set: */
@@ -2267,7 +2258,8 @@ static enum iterator_selection reflog_iterator_select(
                 */
                return ITER_SELECT_0;
        } else if (iter_common) {
-               if (ref_type(iter_common->refname) == REF_TYPE_NORMAL)
+               if (parse_worktree_ref(iter_common->refname, NULL, NULL,
+                                      NULL) == REF_WORKTREE_SHARED)
                        return ITER_SELECT_1;
 
                /*
index 43cdb97f8b37756ee2f62fcf2dc285ec3116f5b4..c1c71d183ea3c0baf082b16cf18bc373a7615fe3 100644 (file)
@@ -862,7 +862,7 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
 
        while ((ok = next_record(iter)) == ITER_OK) {
                if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
-                   ref_type(iter->base.refname) != REF_TYPE_PER_WORKTREE)
+                   !is_per_worktree_ref(iter->base.refname))
                        continue;
 
                if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
index c5c1ce689199083a9eb47ff6161ce6b1bcd7be64..6de9c0ee523abe56881d9d0158b8ff6a7e07c866 100644 (file)
--- a/scalar.c
+++ b/scalar.c
@@ -207,7 +207,10 @@ static int set_recommended_config(int reconfigure)
 
 static int toggle_maintenance(int enable)
 {
-       return run_git("maintenance", enable ? "start" : "unregister", NULL);
+       return run_git("maintenance",
+                      enable ? "start" : "unregister",
+                      enable ? NULL : "--force",
+                      NULL);
 }
 
 static int add_or_remove_enlistment(int add)
index d26ede83c4b2beb1f14ee368bfc30958c4db6d07..debb2ecbafe2efdff334042b00c85badf7b88dfe 100644 (file)
@@ -915,7 +915,7 @@ int read_author_script(const char *path, char **name, char **email, char **date,
                error(_("missing 'GIT_AUTHOR_EMAIL'"));
        if (date_i == -2)
                error(_("missing 'GIT_AUTHOR_DATE'"));
-       if (date_i < 0 || email_i < 0 || date_i < 0 || err)
+       if (name_i < 0 || email_i < 0 || date_i < 0 || err)
                goto finish;
        *name = kv.items[name_i].util;
        *email = kv.items[email_i].util;
@@ -6203,8 +6203,6 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
                        return error(_("the script was already rearranged."));
                }
 
-               *commit_todo_item_at(&commit_todo, item->commit) = item;
-
                parse_commit(item->commit);
                commit_buffer = logmsg_reencode(item->commit, NULL, "UTF-8");
                find_commit_subject(commit_buffer, &subject);
@@ -6271,6 +6269,8 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
                                        strhash(entry->subject));
                        hashmap_put(&subject2item, &entry->entry);
                }
+
+               *commit_todo_item_at(&commit_todo, item->commit) = item;
        }
 
        if (rearranged) {
diff --git a/shell.c b/shell.c
index 811e13b9c9597e9c89bf773a1065819d48a2277c..7ff4109db7058b5677be2fa8775f699b54e4d4f1 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -47,6 +47,8 @@ static void cd_to_homedir(void)
                die("could not chdir to user's home directory");
 }
 
+#define MAX_INTERACTIVE_COMMAND (4*1024*1024)
+
 static void run_shell(void)
 {
        int done = 0;
@@ -67,22 +69,46 @@ static void run_shell(void)
        run_command_v_opt(help_argv, RUN_SILENT_EXEC_FAILURE);
 
        do {
-               struct strbuf line = STRBUF_INIT;
                const char *prog;
                char *full_cmd;
                char *rawargs;
+               size_t len;
                char *split_args;
                const char **argv;
                int code;
                int count;
 
                fprintf(stderr, "git> ");
-               if (git_read_line_interactively(&line) == EOF) {
+
+               /*
+                * Avoid using a strbuf or git_read_line_interactively() here.
+                * We don't want to allocate arbitrary amounts of memory on
+                * behalf of a possibly untrusted client, and we're subject to
+                * OS limits on command length anyway.
+                */
+               fflush(stdout);
+               rawargs = xmalloc(MAX_INTERACTIVE_COMMAND);
+               if (!fgets(rawargs, MAX_INTERACTIVE_COMMAND, stdin)) {
                        fprintf(stderr, "\n");
-                       strbuf_release(&line);
+                       free(rawargs);
                        break;
                }
-               rawargs = strbuf_detach(&line, NULL);
+               len = strlen(rawargs);
+
+               /*
+                * If we truncated due to our input buffer size, reject the
+                * command. That's better than running bogus input, and
+                * there's a good chance it's just malicious garbage anyway.
+                */
+               if (len >= MAX_INTERACTIVE_COMMAND - 1)
+                       die("invalid command format: input too long");
+
+               if (len > 0 && rawargs[len - 1] == '\n') {
+                       if (--len > 0 && rawargs[len - 1] == '\r')
+                               --len;
+                       rawargs[len] = '\0';
+               }
+
                split_args = xstrdup(rawargs);
                count = split_cmdline(split_args, &argv);
                if (count < 0) {
index d5a744e1438600080b9970e3f7cdedbe7f2a3708..c7b0d5d0008efb906ba034c632adb69b53c4f545 100644 (file)
@@ -141,7 +141,12 @@ void string_list_clear_func(struct string_list *list, string_list_clear_func_t c
 int for_each_string_list(struct string_list *list,
                         string_list_each_func_t func, void *cb_data);
 
-/** Iterate over each item, as a macro. */
+/**
+ * Iterate over each item, as a macro.
+ *
+ * Be sure that 'list' is non-NULL. The macro cannot perform NULL
+ * checks due to -Werror=address errors.
+ */
 #define for_each_string_list_item(item,list)            \
        for (item = (list)->items;                      \
             item && item < (list)->items + (list)->nr; \
index fd3303552bec0d3ca243845634179eb35890866a..dd8107cd7da24b01541f0bccf4cff332940f31e9 100755 (executable)
@@ -45,6 +45,7 @@ while (<>) {
        /\bhead\s+-c\b/ and err 'head -c is not portable (use test_copy_bytes BYTES <file >out)';
        /(?:\$\(seq|^\s*seq\b)/ and err 'seq is not portable (use test_seq)';
        /\bgrep\b.*--file\b/ and err 'grep --file FILE is not portable (use grep -f FILE)';
+       /\b[ef]grep\b/ and err 'egrep/fgrep obsolescent (use grep -E/-F)';
        /\bexport\s+[A-Za-z0-9_]*=/ and err '"export FOO=bar" is not portable (use FOO=bar && export FOO)';
        /^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
                err '"FOO=bar shell_func" assignment extends beyond "shell_func"';
index e0e0c53d38619ded93c916b5f11685cf616a1de2..b7d117cd557a84eabcdc25df9ae874e0192e3dd7 100644 (file)
@@ -85,10 +85,17 @@ static int cmd__submodule_is_active(int argc, const char **argv)
        return !is_submodule_active(the_repository, argv[0]);
 }
 
-static int resolve_relative_url(int argc, const char **argv)
+static int cmd__submodule_resolve_relative_url(int argc, const char **argv)
 {
        char *remoteurl, *res;
        const char *up_path, *url;
+       struct option options[] = {
+               OPT_END()
+       };
+       argc = parse_options(argc, argv, "test-tools", options,
+                            submodule_resolve_relative_url_usage, 0);
+       if (argc != 3)
+               usage_with_options(submodule_resolve_relative_url_usage, options);
 
        up_path = argv[0];
        remoteurl = xstrdup(argv[1]);
@@ -104,19 +111,6 @@ static int resolve_relative_url(int argc, const char **argv)
        return 0;
 }
 
-static int cmd__submodule_resolve_relative_url(int argc, const char **argv)
-{
-       struct option options[] = {
-               OPT_END()
-       };
-       argc = parse_options(argc, argv, "test-tools", options,
-                            submodule_resolve_relative_url_usage, 0);
-       if (argc != 3)
-               usage_with_options(submodule_resolve_relative_url_usage, options);
-
-       return resolve_relative_url(argc, argv);
-}
-
 static struct test_cmd cmds[] = {
        { "check-name", cmd__submodule_check_name },
        { "is-active", cmd__submodule_is_active },
index 497b9b9d9272ea72796aa644e67f0ab9f4392d8c..706799391bd4047a1c15f8a32f77875cb8dcb44d 100644 (file)
@@ -80,6 +80,8 @@ PassEnv LSAN_OPTIONS
 PassEnv GIT_TRACE
 PassEnv GIT_CONFIG_NOSYSTEM
 PassEnv GIT_TEST_SIDEBAND_ALL
+PassEnv LANG
+PassEnv LC_ALL
 
 Alias /dumb/ www/
 Alias /auth/dumb/ www/auth/dumb/
index 03e0abbdb83f260f3c688734d5e0d3914c460581..2d31fcfda1f338973cfc868b7f01ae925b39d326 100644 (file)
@@ -197,6 +197,7 @@ test_git_directory_exists () {
 # the submodule repo if it doesn't exist and configures the most problematic
 # settings for diff.ignoreSubmodules.
 prolog () {
+       test_config_global protocol.file.allow always &&
        (test -d submodule_update_repo || create_lib_submodule_repo) &&
        test_config_global diff.ignoreSubmodules all &&
        test_config diff.ignoreSubmodules all
index fce8151d41cbbd9eadd098b26c864d61b5b74bcc..3242cfe91a096fef06d11cc589bb1ab415135e96 100755 (executable)
@@ -124,5 +124,6 @@ test_perf_on_all git read-tree -mu HEAD
 test_perf_on_all git checkout-index -f --all
 test_perf_on_all git update-index --add --remove $SPARSE_CONE/a
 test_perf_on_all "git rm -f $SPARSE_CONE/a && git checkout HEAD -- $SPARSE_CONE/a"
+test_perf_on_all git grep --cached --sparse bogus -- "f2/f1/f1/*"
 
 test_done
index 33da4d2aba2587b4591b0cd1c3785e6ff8119996..34115edec356831d4863a3ca891be0d885bf46fc 100755 (executable)
@@ -232,10 +232,10 @@ then
        )
 elif test -n "$GIT_PERF_SUBSECTION"
 then
-       egrep "^$GIT_PERF_SUBSECTION\$" "$TEST_RESULTS_DIR"/run_subsections.names >/dev/null ||
+       grep -E "^$GIT_PERF_SUBSECTION\$" "$TEST_RESULTS_DIR"/run_subsections.names >/dev/null ||
                die "subsection '$GIT_PERF_SUBSECTION' not found in '$GIT_PERF_CONFIG_FILE'"
 
-       egrep "^$GIT_PERF_SUBSECTION\$" "$TEST_RESULTS_DIR"/run_subsections.names | while read -r subsec
+       grep -E "^$GIT_PERF_SUBSECTION\$" "$TEST_RESULTS_DIR"/run_subsections.names | while read -r subsec
        do
                (
                        GIT_PERF_SUBSECTION="$subsec"
index 1e864cf3172bec45e949a840d026e507a117fec8..5b7bee888d567b5b55e2d4e7c53849db8945fdcb 100755 (executable)
@@ -215,6 +215,20 @@ test_expect_success 'fetching of missing objects' '
        grep "$HASH" out
 '
 
+test_expect_success 'fetching of a promised object that promisor remote no longer has' '
+       rm -f err &&
+       test_create_repo unreliable-server &&
+       git -C unreliable-server config uploadpack.allowanysha1inwant 1 &&
+       git -C unreliable-server config uploadpack.allowfilter 1 &&
+       test_commit -C unreliable-server foo &&
+
+       git clone --filter=blob:none --no-checkout "file://$(pwd)/unreliable-server" unreliable-client &&
+
+       rm -rf unreliable-server/.git/objects/* &&
+       test_must_fail git -C unreliable-client checkout HEAD 2>err &&
+       grep "could not fetch.*from promisor remote" err
+'
+
 test_expect_success 'fetching of missing objects works with ref-in-want enabled' '
        # ref-in-want requires protocol version 2
        git -C server config protocol.version 2 &&
index de1ec89007d787ff828405b1f6225477efe68235..b563d6c263ec1cee0d4bfc93cba1e85e4833f4e6 100755 (executable)
@@ -560,7 +560,8 @@ test_expect_success 'interaction with submodules' '
        (
                cd super &&
                mkdir modules &&
-               git submodule add ../repo modules/child &&
+               git -c protocol.file.allow=always \
+                       submodule add ../repo modules/child &&
                git add . &&
                git commit -m "add submodule" &&
                git sparse-checkout init --cone &&
index b9350c075c2a02db00c154bb3522a087208cdc84..801919009e1d073735ac785a49b0aa353e565c55 100755 (executable)
@@ -162,6 +162,19 @@ init_repos () {
        git -C sparse-index sparse-checkout set deep
 }
 
+init_repos_as_submodules () {
+       git reset --hard &&
+       init_repos &&
+       git submodule add ./full-checkout &&
+       git submodule add ./sparse-checkout &&
+       git submodule add ./sparse-index &&
+
+       git submodule status >actual &&
+       grep full-checkout actual &&
+       grep sparse-checkout actual &&
+       grep sparse-index actual
+}
+
 run_on_sparse () {
        (
                cd sparse-checkout &&
@@ -1302,6 +1315,8 @@ test_expect_success 'submodule handling' '
        test_all_match git add modules &&
        test_all_match git commit -m "add modules directory" &&
 
+       test_config_global protocol.file.allow always &&
+
        run_on_all git submodule add "$(pwd)/initial-repo" modules/sub &&
        test_all_match git commit -m "add submodule" &&
 
@@ -1981,4 +1996,63 @@ test_expect_success 'sparse index is not expanded: rm' '
        ensure_not_expanded rm -r deep
 '
 
+test_expect_success 'grep with and --cached' '
+       init_repos &&
+
+       test_all_match git grep --cached a &&
+       test_all_match git grep --cached a -- "folder1/*"
+'
+
+test_expect_success 'grep is not expanded' '
+       init_repos &&
+
+       ensure_not_expanded grep a &&
+       ensure_not_expanded grep a -- deep/* &&
+
+       # All files within the folder1/* pathspec are sparse,
+       # so this command does not find any matches
+       ensure_not_expanded ! grep a -- folder1/* &&
+
+       # test out-of-cone pathspec with or without wildcard
+       ensure_not_expanded grep --cached a -- "folder1/a" &&
+       ensure_not_expanded grep --cached a -- "folder1/*" &&
+
+       # test in-cone pathspec with or without wildcard
+       ensure_not_expanded grep --cached a -- "deep/a" &&
+       ensure_not_expanded grep --cached a -- "deep/*"
+'
+
+# NEEDSWORK: when running `grep` in the superproject with --recurse-submodules,
+# Git expands the index of the submodules unexpectedly. Even though `grep`
+# builtin is marked as "command_requires_full_index = 0", this config is only
+# useful for the superproject. Namely, the submodules have their own configs,
+# which are _not_ populated by the one-time sparse-index feature switch.
+test_expect_failure 'grep within submodules is not expanded' '
+       init_repos_as_submodules &&
+
+       # do not use ensure_not_expanded() here, becasue `grep` should be
+       # run in the superproject, not in "./sparse-index"
+       GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+       git grep --cached --recurse-submodules a -- "*/folder1/*" &&
+       test_region ! index ensure_full_index trace2.txt
+'
+
+# NEEDSWORK: this test is not actually testing the code. The design purpose
+# of this test is to verify the grep result when the submodules are using a
+# sparse-index. Namely, we want "folder1/" as a tree (a sparse directory); but
+# because of the index expansion, we are now grepping the "folder1/a" blob.
+# Because of the problem stated above 'grep within submodules is not expanded',
+# we don't have the ideal test environment yet.
+test_expect_success 'grep sparse directory within submodules' '
+       init_repos_as_submodules &&
+
+       cat >expect <<-\EOF &&
+       full-checkout/folder1/a:a
+       sparse-checkout/folder1/a:a
+       sparse-index/folder1/a:a
+       EOF
+       git grep --cached --recurse-submodules a -- "*/folder1/*" >actual &&
+       test_cmp actual expect
+'
+
 test_done
index 335d3f3211aa874fd3a8e0d0006dd9fc53a4e589..c69ae41306c90d33b2ac253a25961ae76fec81e7 100755 (executable)
@@ -18,7 +18,7 @@ test_expect_success 'checking for a working acl setup' '
        if setfacl -m d:m:rwx -m u:root:rwx . &&
           getfacl . | grep user:root:rwx &&
           touch should-have-readable-acl &&
-          getfacl should-have-readable-acl | egrep "mask::?rw-"
+          getfacl should-have-readable-acl | grep -E "mask::?rw-"
        then
                test_set_prereq SETFACL
        fi
@@ -34,7 +34,7 @@ check_perms_and_acl () {
        getfacl "$1" > actual &&
        grep -q "user:root:rwx" actual &&
        grep -q "user:${LOGNAME}:rwx" actual &&
-       egrep "mask::?r--" actual > /dev/null 2>&1 &&
+       grep -E "mask::?r--" actual > /dev/null 2>&1 &&
        grep -q "group::---" actual || false
 }
 
index 0e13bcb4ebbf703337bda31e5714620dcc2cf636..81de584ea29bca8cfcbe20769c69072202a53efb 100755 (executable)
@@ -226,7 +226,8 @@ test_expect_success 'showing the superproject correctly' '
        test_commit -C super test_commit &&
        test_create_repo sub &&
        test_commit -C sub test_commit &&
-       git -C super submodule add ../sub dir/sub &&
+       git -c protocol.file.allow=always \
+               -C super submodule add ../sub dir/sub &&
        echo $(pwd)/super >expect  &&
        git -C super/dir/sub rev-parse --show-superproject-working-tree >out &&
        test_cmp expect out &&
index 00ce3033d3489285b8580d6e23f3eeb24074862f..5ffe1a41e2cd726e00b242a9f289c28c183ef5f0 100755 (executable)
@@ -41,6 +41,8 @@ TEST_NO_CREATE_REPO=1
 #  -                  m/m (file)
 #
 test_expect_success 'setup repo for checkout with various types of changes' '
+       test_config_global protocol.file.allow always &&
+
        git init sub &&
        (
                cd sub &&
@@ -140,6 +142,7 @@ do
        esac
 
        test_expect_success "$mode checkout on clone" '
+               test_config_global protocol.file.allow always &&
                repo=various_${mode}_clone &&
                set_checkout_config $workers $threshold &&
                test_checkout_workers $expected_workers \
index f3242fef6b65772ea973a1906210950cc0411f73..d587e0b20db22423ef1a1d204076c50b769e0375 100755 (executable)
@@ -669,6 +669,7 @@ test_expect_success '"add" should not fail because of another bad worktree' '
 '
 
 test_expect_success '"add" with uninitialized submodule, with submodule.recurse unset' '
+       test_config_global protocol.file.allow always &&
        test_create_repo submodule &&
        test_commit -C submodule first &&
        test_create_repo project &&
@@ -684,6 +685,7 @@ test_expect_success '"add" with uninitialized submodule, with submodule.recurse
 '
 
 test_expect_success '"add" with initialized submodule, with submodule.recurse unset' '
+       test_config_global protocol.file.allow always &&
        git -C project-clone submodule update --init &&
        git -C project-clone worktree add ../project-4
 '
index 1168e9f998232ccfa3d4c7c2e875b95299f23ead..230a55e99af85f9f0b60d13df1b15372012e0f89 100755 (executable)
@@ -139,7 +139,8 @@ test_expect_success 'move a repo with uninitialized submodule' '
        (
                cd withsub &&
                test_commit initial &&
-               git submodule add "$PWD"/.git sub &&
+               git -c protocol.file.allow=always \
+                       submodule add "$PWD"/.git sub &&
                git commit -m withsub &&
                git worktree add second HEAD &&
                git worktree move second third
@@ -149,7 +150,7 @@ test_expect_success 'move a repo with uninitialized submodule' '
 test_expect_success 'not move a repo with initialized submodule' '
        (
                cd withsub &&
-               git -C third submodule update &&
+               git -c protocol.file.allow=always -C third submodule update &&
                test_must_fail git worktree move third forth
        )
 '
@@ -228,6 +229,7 @@ test_expect_success 'remove cleans up .git/worktrees when empty' '
 '
 
 test_expect_success 'remove a repo with uninitialized submodule' '
+       test_config_global protocol.file.allow always &&
        (
                cd withsub &&
                git worktree add to-remove HEAD &&
@@ -236,6 +238,7 @@ test_expect_success 'remove a repo with uninitialized submodule' '
 '
 
 test_expect_success 'not remove a repo with initialized submodule' '
+       test_config_global protocol.file.allow always &&
        (
                cd withsub &&
                git worktree add to-remove HEAD &&
index b172c26ca43291e61ed2d38f720047fdc8a9d785..11018f37c70c02e9052b36be9edb8b05211bcf8d 100755 (executable)
@@ -10,6 +10,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 base_path=$(pwd -P)
 
 test_expect_success 'setup: create origin repos'  '
+       git config --global protocol.file.allow always &&
        git init origin/sub &&
        test_commit -C origin/sub file1 &&
        git init origin/main &&
index 9723c2827ccddc84824035e600d90d28aed0e1eb..7d8edff9c320f04aed15932cde1ad16dbec30f22 100755 (executable)
@@ -201,8 +201,8 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou
 
 test_expect_success 'git branch -M baz bam should add entries to .git/logs/HEAD' '
        msg="Branch: renamed refs/heads/baz to refs/heads/bam" &&
-       grep " 0\{40\}.*$msg$" .git/logs/HEAD &&
-       grep "^0\{40\}.*$msg$" .git/logs/HEAD
+       grep " $ZERO_OID.*$msg$" .git/logs/HEAD &&
+       grep "^$ZERO_OID.*$msg$" .git/logs/HEAD
 '
 
 test_expect_success 'git branch -M should leave orphaned HEAD alone' '
@@ -306,6 +306,7 @@ test_expect_success 'deleting checked-out branch from repo that is a submodule'
        git init repo1 &&
        git init repo1/sub &&
        test_commit -C repo1/sub x &&
+       test_config_global protocol.file.allow always &&
        git -C repo1 submodule add ./sub &&
        git -C repo1 commit -m "adding sub" &&
 
@@ -1381,6 +1382,9 @@ test_expect_success 'branch --delete --force removes dangling branch' '
 '
 
 test_expect_success 'use --edit-description' '
+       EDITOR=: git branch --edit-description &&
+       test_must_fail git config branch.main.description &&
+
        write_script editor <<-\EOF &&
                echo "New contents" >"$1"
        EOF
index f2b9199007752eded932a8da541e1a86e350d10a..ea7cfd1951d8b2173c8fe13f1bc307ff9e7cfa1c 100755 (executable)
@@ -7,6 +7,28 @@ test_description='test show-branch'
 # arbitrary reference time: 2009-08-30 19:20:00
 GIT_TEST_DATE_NOW=1251660000; export GIT_TEST_DATE_NOW
 
+test_expect_success 'error descriptions on empty repository' '
+       current=$(git branch --show-current) &&
+       cat >expect <<-EOF &&
+       error: No commit on branch '\''$current'\'' yet.
+       EOF
+       test_must_fail git branch --edit-description 2>actual &&
+       test_cmp expect actual &&
+       test_must_fail git branch --edit-description $current 2>actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'fatal descriptions on empty repository' '
+       current=$(git branch --show-current) &&
+       cat >expect <<-EOF &&
+       fatal: No commit on branch '\''$current'\'' yet.
+       EOF
+       test_must_fail git branch --set-upstream-to=non-existent 2>actual &&
+       test_cmp expect actual &&
+       test_must_fail git branch -c new-branch 2>actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'setup' '
        test_commit initial &&
        for i in $(test_seq 1 10)
@@ -175,4 +197,28 @@ done <<\EOF
 --reflog --current
 EOF
 
+test_expect_success 'error descriptions on non-existent branch' '
+       cat >expect <<-EOF &&
+       error: No branch named '\''non-existent'\'.'
+       EOF
+       test_must_fail git branch --edit-description non-existent 2>actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'fatal descriptions on non-existent branch' '
+       cat >expect <<-EOF &&
+       fatal: branch '\''non-existent'\'' does not exist
+       EOF
+       test_must_fail git branch --set-upstream-to=non-existent non-existent 2>actual &&
+       test_cmp expect actual &&
+
+       cat >expect <<-EOF &&
+       fatal: No branch named '\''non-existent'\''.
+       EOF
+       test_must_fail git branch -c non-existent new-branch 2>actual &&
+       test_cmp expect actual &&
+       test_must_fail git branch -m non-existent new-branch 2>actual &&
+       test_cmp expect actual
+'
+
 test_done
index 459beaf7d9cec09cfbc617d6461eacd1d5917dff..84dd0cd26d02cb3a3151f1529544ef1f70e052ae 100755 (executable)
@@ -793,7 +793,7 @@ test_expect_success 'submodule changes are shown irrespective of diff.submodule'
        sub_oid3=$(git -C sub-repo rev-parse HEAD) &&
 
        git checkout -b main-sub topic &&
-       git submodule add ./sub-repo sub &&
+       git -c protocol.file.allow=always submodule add ./sub-repo sub &&
        git -C sub checkout --detach sub-first &&
        git commit -m "add sub" sub &&
        sup_oid1=$(git rev-parse --short HEAD) &&
index cfde6b237f5dc40d79beca20c0f26cfd8f80e53c..fe72b247164e56fc9a7af134583e2b9f8cc9bbdc 100755 (executable)
@@ -28,6 +28,7 @@ test_no_branch () {
 }
 
 test_expect_success 'setup superproject and submodule' '
+       git config --global protocol.file.allow always &&
        mkdir test_dirs &&
        (
                cd test_dirs &&
index 22ffe5bcb9908d914585dbc90e59194023bd9ff5..1ec1fb6715efda8a0ec546956a0deece76efefff 100755 (executable)
@@ -9,7 +9,7 @@ path_has_fanout() {
        path=$1 &&
        fanout=$2 &&
        after_last_slash=$(($(test_oid hexsz) - $fanout * 2)) &&
-       echo $path | grep -q "^\([0-9a-f]\{2\}/\)\{$fanout\}[0-9a-f]\{$after_last_slash\}$"
+       echo $path | grep -q -E "^([0-9a-f]{2}/){$fanout}[0-9a-f]{$after_last_slash}$"
 }
 
 touched_one_note_with_fanout() {
index 688b01e3eb6a387c0d9ad36031dd8beab46d9419..4f5abb5ad25e9d39fc85d4d20a4ab1d4905b61e2 100755 (executable)
@@ -1244,9 +1244,9 @@ test_expect_success 'short commit ID collide' '
                test $colliding_id = "$(git rev-parse HEAD | cut -c 1-4)" &&
                grep "^pick $colliding_id " \
                        .git/rebase-merge/git-rebase-todo.tmp &&
-               grep "^pick [0-9a-f]\{$hexsz\}" \
+               grep -E "^pick [0-9a-f]{$hexsz}" \
                        .git/rebase-merge/git-rebase-todo &&
-               grep "^pick [0-9a-f]\{$hexsz\}" \
+               grep -E "^pick [0-9a-f]{$hexsz}" \
                        .git/rebase-merge/git-rebase-todo.backup &&
                git rebase --continue
        ) &&
@@ -1261,7 +1261,7 @@ test_expect_success 'respect core.abbrev' '
                set_cat_todo_editor &&
                test_must_fail git rebase -i HEAD~4 >todo-list
        ) &&
-       test 4 = $(grep -c "pick [0-9a-f]\{12,\}" todo-list)
+       test 4 = $(grep -c -E "pick [0-9a-f]{12,}" todo-list)
 '
 
 test_expect_success 'todo count' '
index 78c27496d6a715414e3a531ffe37fd790d5833c8..a364530d7629fc0bb47c30211935b878e1aa9805 100755 (executable)
@@ -232,6 +232,19 @@ test_expect_success 'auto squash that matches longer sha1' '
        test_line_count = 1 actual
 '
 
+test_expect_success 'auto squash of fixup commit that matches branch name which points back to fixup commit' '
+       git reset --hard base &&
+       git commit --allow-empty -m "fixup! self-cycle" &&
+       git branch self-cycle &&
+       GIT_SEQUENCE_EDITOR="cat >tmp" git rebase --autosquash -i HEAD^^ &&
+       sed -ne "/^[^#]/{s/[0-9a-f]\{7,\}/HASH/g;p;}" tmp >actual &&
+       cat <<-EOF >expect &&
+       pick HASH second commit
+       pick HASH fixup! self-cycle # empty
+       EOF
+       test_cmp expect actual
+'
+
 test_auto_commit_flags () {
        git reset --hard base &&
        echo 1 >file1 &&
index 43fcb68f27e439b5542e82a85bc77e401adc6e98..693934ee8be9600e6edda9e691b5415f8c153b34 100755 (executable)
@@ -310,7 +310,7 @@ test_expect_success 'autostash is saved on editor failure with conflict' '
 test_expect_success 'autostash with dirty submodules' '
        test_when_finished "git reset --hard && git checkout main" &&
        git checkout -b with-submodule &&
-       git submodule add ./ sub &&
+       git -c protocol.file.allow=always submodule add ./ sub &&
        test_tick &&
        git commit -m add-submodule &&
        echo changed >sub/file0 &&
index 7a9f1127a4b974aea5c7390447b44f1798ba20ab..ba069dccbdf56197b0e93831fdb1316853d54ef4 100755 (executable)
@@ -48,7 +48,8 @@ test_expect_success 'rebase interactive ignores modified submodules' '
        git init sub &&
        git -C sub commit --allow-empty -m "Initial commit" &&
        git init super &&
-       git -C super submodule add ../sub &&
+       git -c protocol.file.allow=always \
+               -C super submodule add ../sub &&
        git -C super config submodule.sub.ignore dirty &&
        >super/foo &&
        git -C super add foo &&
index 5f8ba2c7399dc3d1cb5661097a3a3f0990624e77..6aa2aeb628d0eda991ee16760985f9017dceebb1 100755 (executable)
@@ -64,14 +64,6 @@ test_rebase_gpg_sign ! true  -i --no-gpg-sign
 test_rebase_gpg_sign ! true  -i --gpg-sign --no-gpg-sign
 test_rebase_gpg_sign   false -i --no-gpg-sign --gpg-sign
 
-test_expect_failure 'rebase -p --no-gpg-sign override commit.gpgsign' '
-       test_when_finished "git clean -f" &&
-       git reset --hard merged &&
-       git config commit.gpgsign true &&
-       git rebase -p --no-gpg-sign --onto=one fork-point main &&
-       test_must_fail git verify-commit HEAD
-'
-
 test_expect_success 'rebase -r, merge strategy, --gpg-sign will sign commit' '
        git reset --hard merged &&
        test_unconfig commit.gpgsign &&
diff --git a/t/t3438-rebase-broken-files.sh b/t/t3438-rebase-broken-files.sh
new file mode 100755 (executable)
index 0000000..b92a3ce
--- /dev/null
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+test_description='rebase behavior when on-disk files are broken'
+. ./test-lib.sh
+
+test_expect_success 'set up conflicting branches' '
+       test_commit base file &&
+       git checkout -b branch1 &&
+       test_commit one file &&
+       git checkout -b branch2 HEAD^ &&
+       test_commit two file
+'
+
+create_conflict () {
+       test_when_finished "git rebase --abort" &&
+       git checkout -B tmp branch2 &&
+       test_must_fail git rebase branch1
+}
+
+check_resolve_fails () {
+       echo resolved >file &&
+       git add file &&
+       test_must_fail git rebase --continue
+}
+
+for item in NAME EMAIL DATE
+do
+       test_expect_success "detect missing GIT_AUTHOR_$item" '
+               create_conflict &&
+
+               grep -v $item .git/rebase-merge/author-script >tmp &&
+               mv tmp .git/rebase-merge/author-script &&
+
+               check_resolve_fails
+       '
+done
+
+for item in NAME EMAIL DATE
+do
+       test_expect_success "detect duplicate GIT_AUTHOR_$item" '
+               create_conflict &&
+
+               grep -i $item .git/rebase-merge/author-script >tmp &&
+               cat tmp >>.git/rebase-merge/author-script &&
+
+               check_resolve_fails
+       '
+done
+
+test_expect_success 'unknown key in author-script' '
+       create_conflict &&
+
+       echo "GIT_AUTHOR_BOGUS=${SQ}whatever${SQ}" \
+               >>.git/rebase-merge/author-script &&
+
+       check_resolve_fails
+'
+
+test_done
index c657840db33b6e8d4c47ece5c9eb0feba495825a..f22d1ddead1ac94de714f61fc10b87cc4bae3aec 100755 (executable)
@@ -16,6 +16,8 @@ fi
 test_submodule_switch "cherry-pick"
 
 test_expect_success 'unrelated submodule/file conflict is ignored' '
+       test_config_global protocol.file.allow always &&
+
        test_create_repo sub &&
 
        touch sub/file &&
index e74a318ac33ac00d10e6517b10b838ac8e0c899a..0e8afe49ed100cb3c1b89f8c33b639ab8ac1cabb 100755 (executable)
@@ -333,7 +333,7 @@ test_expect_success 'rm removes empty submodules from work tree' '
 
 test_expect_success 'rm removes removed submodule from index and .gitmodules' '
        git reset --hard &&
-       git submodule update &&
+       git -c protocol.file.allow=always submodule update &&
        rm -rf submod &&
        git rm submod &&
        git status -s -uno --ignore-submodules=none >actual &&
@@ -639,6 +639,7 @@ cat >expect.deepmodified <<EOF
 EOF
 
 test_expect_success 'setup subsubmodule' '
+       test_config_global protocol.file.allow always &&
        git reset --hard &&
        git submodule update &&
        (
index 8689b48589c0d515b9d15b86e4e00807fd26f566..51afbd7b24a10c247520ba2f9be702da57f83afd 100755 (executable)
@@ -291,7 +291,7 @@ test_expect_success BSLASHPSPEC "git add 'fo\\[ou\\]bar' ignores foobar" '
        git reset --hard &&
        touch fo\[ou\]bar foobar &&
        git add '\''fo\[ou\]bar'\'' &&
-       git ls-files fo\[ou\]bar | fgrep fo\[ou\]bar &&
+       git ls-files fo\[ou\]bar | grep -F fo\[ou\]bar &&
        ! ( git ls-files foobar | grep foobar )
 '
 
index a1801a8cbd4185f80253b019eb40a2c6a378d7ab..82bfb2fd2aca9eae78af33fa67620e76458b3ac6 100755 (executable)
@@ -100,7 +100,7 @@ EOF
 
 echo "#!$SHELL_PATH" >fake-editor.sh
 cat >> fake-editor.sh <<\EOF
-egrep -v '^index' "$1" >orig-patch &&
+grep -E -v '^index' "$1" >orig-patch &&
 mv -f patch "$1"
 EOF
 
index a52e53dd2da481e3c24a82d9599a58c0d1c5ab43..0f7348ec21b8821cb6b7594919bbc637ec0a7236 100755 (executable)
@@ -36,7 +36,7 @@ setup_basic () {
        git init main &&
        (
                cd main &&
-               git submodule add ../sub &&
+               git -c protocol.file.allow=always submodule add ../sub &&
                test_commit main_file
        )
 }
index ad5c02927943ab44a6fb986a646cd9b829786ef3..de1da4673da9626f0c155fd4c67597a403870ece 100755 (executable)
@@ -1457,7 +1457,7 @@ append_signoff()
        C=$(git commit-tree HEAD^^{tree} -p HEAD) &&
        git format-patch --stdout --signoff $C^..$C >append_signoff.patch &&
        sed -n -e "1,/^---$/p" append_signoff.patch |
-               egrep -n "^Subject|Sign|^$"
+               grep -E -n "^Subject|Sign|^$"
 }
 
 test_expect_success 'signoff: commit with no body' '
@@ -2274,10 +2274,10 @@ test_expect_success 'format-patch --base with --attach' '
 test_expect_success 'format-patch --attach cover-letter only is non-multipart' '
        test_when_finished "rm -fr patches" &&
        git format-patch -o patches --cover-letter --attach=mimemime --base=HEAD~ -1 &&
-       ! egrep "^--+mimemime" patches/0000*.patch &&
-       egrep "^--+mimemime$" patches/0001*.patch >output &&
+       ! grep -E "^--+mimemime" patches/0000*.patch &&
+       grep -E "^--+mimemime$" patches/0001*.patch >output &&
        test_line_count = 2 output &&
-       egrep "^--+mimemime--$" patches/0001*.patch >output &&
+       grep -E "^--+mimemime--$" patches/0001*.patch >output &&
        test_line_count = 1 output
 '
 
index 49bca7b48d910fa1128435f121c849e45a7eefa6..d489230df89663620fda4a11b538fc740720a8aa 100755 (executable)
@@ -49,7 +49,7 @@ test_expect_success 'setup - submodules' '
 '
 
 test_expect_success 'setup - git submodule add' '
-       git submodule add ./sm2 sm1 &&
+       git -c protocol.file.allow=always submodule add ./sm2 sm1 &&
        commit_file sm1 .gitmodules &&
        git diff-tree -p --no-commit-id --submodule=log HEAD -- sm1 >actual &&
        cat >expected <<-EOF &&
index d86e38abd882220ec60d45b2a351d2947f7dcfcb..97c6424cd51714eeadffd5899c709a409b1cedd9 100755 (executable)
@@ -840,7 +840,7 @@ rm sm2
 mv sm2-bak sm2
 
 test_expect_success 'setup nested submodule' '
-       git -C sm2 submodule add ../sm2 nested &&
+       git -c protocol.file.allow=always -C sm2 submodule add ../sm2 nested &&
        git -C sm2 commit -a -m "nested sub" &&
        head10=$(git -C sm2 rev-parse --short --verify HEAD)
 '
index 804f2a82e8315d4f5936894adf7846769d2e201d..28f42a4046e08ed61fcf2f300b826c33136e9e98 100755 (executable)
@@ -77,6 +77,7 @@ test_expect_success 'diff skips same-OID blobs' '
 
 test_expect_success 'when fetching missing objects, diff skips GITLINKs' '
        test_when_finished "rm -rf sub server client trace" &&
+       test_config_global protocol.file.allow always &&
 
        test_create_repo sub &&
        test_commit -C sub first &&
index 7f0c1dcc0f0bfbfd746648f4fee1b7588a8f31b8..2e8f5ad7b822b2c33d1262c5576706f40de52692 100755 (executable)
@@ -124,6 +124,7 @@ test_expect_success 'command line pathspec parsing for "git log"' '
 
 test_expect_success 'tree_entry_interesting does not match past submodule boundaries' '
        test_when_finished "rm -rf repo submodule" &&
+       test_config_global protocol.file.allow always &&
        git init submodule &&
        test_commit -C submodule initial &&
        git init repo &&
index 28ca5c38bb52819394ba0b2b7d81e86d9d94378b..013b77144bdf1a59d4577a57cace9ce0e7408742 100755 (executable)
@@ -810,4 +810,13 @@ test_expect_success 'can override merge of unrelated histories' '
        test_cmp expect actual
 '
 
+test_expect_success SANITY 'merge-ort fails gracefully in a read-only repository' '
+       git init --bare read-only &&
+       git push read-only side1 side2 side3 &&
+       test_when_finished "chmod -R u+w read-only" &&
+       chmod -R a-w read-only &&
+       test_must_fail git -C read-only merge-tree side1 side3 &&
+       test_must_fail git -C read-only merge-tree side1 side2
+'
+
 test_done
index cebad1048cfca3a142bcaef361dfab69fe889ceb..db11cababd310f9773dcd0a3bb7039a4df8d4f59 100755 (executable)
@@ -201,13 +201,13 @@ test_expect_success 'mailinfo -b double [PATCH]' '
        test z"$subj" = z"Subject: message"
 '
 
-test_expect_failure 'mailinfo -b trailing [PATCH]' '
+test_expect_success 'mailinfo -b trailing [PATCH]' '
        subj="$(echo "Subject: [other] [PATCH] message" |
                git mailinfo -b /dev/null /dev/null)" &&
        test z"$subj" = z"Subject: [other] message"
 '
 
-test_expect_failure 'mailinfo -b separated double [PATCH]' '
+test_expect_success 'mailinfo -b separated double [PATCH]' '
        subj="$(echo "Subject: [PATCH] [other] [PATCH] message" |
                git mailinfo -b /dev/null /dev/null)" &&
        test z"$subj" = z"Subject: [other] message"
index afbe93f162e854b462774cd5242ddfe40a64bf6c..b5f9b1092229d3d8c1879d41c5c3f02f4ad04923 100755 (executable)
@@ -784,6 +784,70 @@ test_expect_success 'repack creates a new pack' '
        )
 '
 
+test_expect_success 'repack (all) ignores cruft pack' '
+       git init repo &&
+       test_when_finished "rm -fr repo" &&
+       (
+               cd repo &&
+
+               test_commit base &&
+               test_commit --no-tag unreachable &&
+
+               git reset --hard base &&
+               git reflog expire --all --expire=all &&
+               git repack --cruft -d &&
+
+               git multi-pack-index write &&
+
+               find $objdir/pack | sort >before &&
+               git multi-pack-index repack --batch-size=0 &&
+               find $objdir/pack | sort >after &&
+
+               test_cmp before after
+       )
+'
+
+test_expect_success 'repack (--batch-size) ignores cruft pack' '
+       git init repo &&
+       test_when_finished "rm -fr repo" &&
+       (
+               cd repo &&
+
+               test_commit_bulk 5 &&
+               test_commit --no-tag unreachable &&
+
+               git reset --hard HEAD^ &&
+               git reflog expire --all --expire=all &&
+               git repack --cruft -d &&
+
+               test_commit four &&
+
+               find $objdir/pack -type f -name "*.pack" | sort >before &&
+               git repack -d &&
+               find $objdir/pack -type f -name "*.pack" | sort >after &&
+
+               pack="$(comm -13 before after)" &&
+               test_file_size "$pack" >sz &&
+               # Set --batch-size to twice the size of the pack created
+               # in the previous step, since this is enough to
+               # accommodate it and the cruft pack.
+               #
+               # This means that the MIDX machinery *could* combine the
+               # new and cruft packs together.
+               #
+               # We ensure that it does not below.
+               batch="$((($(cat sz) * 2)))" &&
+
+               git multi-pack-index write &&
+
+               find $objdir/pack | sort >before &&
+               git multi-pack-index repack --batch-size=$batch &&
+               find $objdir/pack | sort >after &&
+
+               test_cmp before after
+       )
+'
+
 test_expect_success 'expire removes repacked packs' '
        (
                cd dup &&
@@ -847,6 +911,36 @@ test_expect_success 'expire respects .keep files' '
        )
 '
 
+test_expect_success 'expiring unreferenced cruft pack retains pack' '
+       git init repo &&
+       test_when_finished "rm -fr repo" &&
+       (
+               cd repo &&
+
+               test_commit base &&
+               test_commit --no-tag unreachable &&
+               unreachable=$(git rev-parse HEAD) &&
+
+               git reset --hard base &&
+               git reflog expire --all --expire=all &&
+               git repack --cruft -d &&
+               mtimes="$(ls $objdir/pack/pack-*.mtimes)" &&
+
+               echo "base..$unreachable" >in &&
+               pack="$(git pack-objects --revs --delta-base-offset \
+                       $objdir/pack/pack <in)" &&
+
+               # Preferring the contents of "$pack" will leave the
+               # cruft pack unreferenced (ie., none of the objects
+               # contained in the cruft pack will have their MIDX copy
+               # selected from the cruft pack).
+               git multi-pack-index write --preferred-pack="pack-$pack.pack" &&
+               git multi-pack-index expire &&
+
+               test_path_is_file "$mtimes"
+       )
+'
+
 test_expect_success 'repack --batch-size=0 repacks everything' '
        cp -r dup dup2 &&
        (
index 124d47603df4ae71a9a1eeee9059752855f6860b..406363381f10bcc6ee4f8040afadcc362d85fc29 100755 (executable)
@@ -134,7 +134,7 @@ test_expect_success 'island core places core objects first' '
            repack -adfi &&
        git verify-pack -v .git/objects/pack/*.pack |
        cut -d" " -f1 |
-       egrep "$root|$two" >actual &&
+       grep -E "$root|$two" >actual &&
        test_cmp expect actual
 '
 
index 9006196ac601e8f93f046b54eafe11aa9df58563..43b7bcd7159c252ff7a2e68dc28fc617649bb658 100755 (executable)
@@ -902,6 +902,17 @@ test_expect_success 'rename a remote renames repo remote.pushDefault but keeps g
        )
 '
 
+test_expect_success 'rename handles remote without fetch refspec' '
+       git clone --bare one no-refspec.git &&
+       # confirm assumption that bare clone does not create refspec
+       test_expect_code 5 \
+               git -C no-refspec.git config --unset-all remote.origin.fetch &&
+       git -C no-refspec.git config remote.origin.url >expect &&
+       git -C no-refspec.git remote rename origin foo &&
+       git -C no-refspec.git config remote.foo.url >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'rename does not update a non-default fetch refspec' '
        git clone one four.one &&
        (
index b45879a760b829270c3460cb3abd32996cad1c9a..c0b745e33b8a52b6c2defda1fa04ef1b8c202841 100755 (executable)
@@ -790,6 +790,7 @@ test_expect_success 'fetch.writeCommitGraph' '
 '
 
 test_expect_success 'fetch.writeCommitGraph with submodules' '
+       test_config_global protocol.file.allow always &&
        git clone dups super &&
        (
                cd super &&
index 3211002d466867fb2abb99eeff897029a7fdfa71..79dc470c014a15224fef5f05e648cc178ef24910 100755 (executable)
@@ -222,6 +222,7 @@ test_expect_success 'push with negotiation proceeds anyway even if negotiation f
 test_expect_success 'push with negotiation does not attempt to fetch submodules' '
        mk_empty submodule_upstream &&
        test_commit -C submodule_upstream submodule_commit &&
+       test_config_global protocol.file.allow always &&
        git submodule add ./submodule_upstream submodule &&
        mk_empty testrepo &&
        git push testrepo $the_first_commit:refs/remotes/origin/first_commit &&
index a301b56db894253829a0934647396ad854ec11b7..3c44f1961220b376a0156f020588788ab986e131 100755 (executable)
@@ -127,6 +127,7 @@ verify_fetch_result () {
 }
 
 test_expect_success setup '
+       git config --global protocol.file.allow always &&
        mkdir deepsubmodule &&
        (
                cd deepsubmodule &&
index 10e9a7ff2641897b6a666b1e78eefb81e1c5b7d4..37f7547a4cadb22929d7f88d9d8e11e5059b9d32 100755 (executable)
@@ -162,6 +162,8 @@ test_expect_success 'fetch --update-shallow' '
 '
 
 test_expect_success 'fetch --update-shallow into a repo with submodules' '
+       test_config_global protocol.file.allow always &&
+
        git init a-submodule &&
        test_commit -C a-submodule foo &&
 
@@ -175,7 +177,8 @@ test_expect_success 'fetch --update-shallow into a repo with submodules' '
 test_expect_success 'fetch --update-shallow a commit that is also a shallow point into a repo with submodules' '
        test_when_finished "rm -rf repo-with-sub" &&
        git init repo-with-sub &&
-       git -C repo-with-sub submodule add ../a-submodule a-submodule &&
+       git -c protocol.file.allow=always -C repo-with-sub \
+               submodule add ../a-submodule a-submodule &&
        git -C repo-with-sub commit -m "added submodule" &&
 
        SHALLOW=$(cat shallow/.git/shallow) &&
index 214228349ad3995e49815850412e9c24bfc58065..a158e7d2c011f464c90ff69e7eda1c6ae0669ece 100755 (executable)
@@ -119,6 +119,7 @@ test_expect_success 'push options and submodules' '
        test_commit -C parent one &&
        git -C parent push --mirror up &&
 
+       test_config_global protocol.file.allow always &&
        git -C parent submodule add ../upstream workbench &&
        git -C parent/workbench remote add up ../../upstream &&
        git -C parent commit -m "add submodule" &&
index d7cf85ffeadefcc62e051cc61f9a0fbeae58cd53..8f182a3cbfe73cbcf1d61cbf96c14b78cf25a168 100755 (executable)
@@ -234,7 +234,7 @@ test_expect_success 'http-fetch --packfile' '
                --index-pack-arg=--keep \
                "$HTTPD_URL"/dumb/repo_pack.git/$p >out &&
 
-       grep "^keep.[0-9a-f]\{16,\}$" out &&
+       grep -E "^keep.[0-9a-f]{16,}$" out &&
        cut -c6- out >packhash &&
 
        # Ensure that the expected files are generated
index a35396fadf5ec2acea618be47c9cec5b5a108fa3..09097eff3f46033a26c9b60c94bb8cb79f3fad2c 100755 (executable)
@@ -52,6 +52,10 @@ then
 fi
 test_submodule_switch_func "git_pull_noff"
 
+test_expect_success 'setup' '
+       git config --global protocol.file.allow always
+'
+
 test_expect_success 'pull --recurse-submodule setup' '
        test_create_repo child &&
        test_commit -C child bar &&
index 2e57de9c12a39a63b870079c17b014ad24fb93c3..45f0803ed4dccd528df61060dfb4c82ff4c6fc34 100755 (executable)
@@ -767,6 +767,7 @@ test_expect_success 'batch missing blob request does not inadvertently try to fe
        echo aa >server/a &&
        echo bb >server/b &&
        # Also add a gitlink pointing to an arbitrary repository
+       test_config_global protocol.file.allow always &&
        git -C server submodule add "$(pwd)/repo_for_submodule" c &&
        git -C server add a b c &&
        git -C server commit -m x &&
index 24340e6d56e18d6a2a9082654c95ef58ac7ed196..2734e37e8804cd6944301e1d836af517fb1172b5 100755 (executable)
@@ -303,8 +303,6 @@ test_expect_success SYMLINKS 'setup repo with manually symlinked or unknown file
                ln -s ../an-object $obj &&
 
                cd ../ &&
-               find . -type f | sort >../../../T.objects-files.raw &&
-               find . -type l | sort >../../../T.objects-symlinks.raw &&
                echo unknown_content >unknown_file
        ) &&
        git -C T fsck &&
@@ -313,19 +311,27 @@ test_expect_success SYMLINKS 'setup repo with manually symlinked or unknown file
 
 
 test_expect_success SYMLINKS 'clone repo with symlinked or unknown files at objects/' '
-       for option in --local --no-hardlinks --shared --dissociate
+       # None of these options work when cloning locally, since T has
+       # symlinks in its `$GIT_DIR/objects` directory
+       for option in --local --no-hardlinks --dissociate
        do
-               git clone $option T T$option || return 1 &&
-               git -C T$option fsck || return 1 &&
-               git -C T$option rev-list --all --objects >T$option.objects &&
-               test_cmp T.objects T$option.objects &&
-               (
-                       cd T$option/.git/objects &&
-                       find . -type f | sort >../../../T$option.objects-files.raw &&
-                       find . -type l | sort >../../../T$option.objects-symlinks.raw
-               )
+               test_must_fail git clone $option T T$option 2>err || return 1 &&
+               test_i18ngrep "symlink.*exists" err || return 1
        done &&
 
+       # But `--shared` clones should still work, even when specifying
+       # a local path *and* that repository has symlinks present in its
+       # `$GIT_DIR/objects` directory.
+       git clone --shared T T--shared &&
+       git -C T--shared fsck &&
+       git -C T--shared rev-list --all --objects >T--shared.objects &&
+       test_cmp T.objects T--shared.objects &&
+       (
+               cd T--shared/.git/objects &&
+               find . -type f | sort >../../../T--shared.objects-files.raw &&
+               find . -type l | sort >../../../T--shared.objects-symlinks.raw
+       ) &&
+
        for raw in $(ls T*.raw)
        do
                sed -e "s!/../!/Y/!; s![0-9a-f]\{38,\}!Z!" -e "/commit-graph/d" \
@@ -333,26 +339,6 @@ test_expect_success SYMLINKS 'clone repo with symlinked or unknown files at obje
                sort $raw.de-sha-1 >$raw.de-sha || return 1
        done &&
 
-       cat >expected-files <<-EOF &&
-       ./Y/Z
-       ./Y/Z
-       ./Y/Z
-       ./a-loose-dir/Z
-       ./an-object
-       ./info/packs
-       ./pack/pack-Z.idx
-       ./pack/pack-Z.pack
-       ./packs/pack-Z.idx
-       ./packs/pack-Z.pack
-       ./unknown_file
-       EOF
-
-       for option in --local --no-hardlinks --dissociate
-       do
-               test_cmp expected-files T$option.objects-files.raw.de-sha || return 1 &&
-               test_must_be_empty T$option.objects-symlinks.raw.de-sha || return 1
-       done &&
-
        echo ./info/alternates >expected-files &&
        test_cmp expected-files T--shared.objects-files.raw &&
        test_must_be_empty T--shared.objects-symlinks.raw
index f6bb02ab947c1de61484e95e0f4a6228a16da103..cf221e92c4d97e852f1bdefbf61f6caded87aff2 100755 (executable)
@@ -42,11 +42,12 @@ test_expect_success 'rejects invalid -o/--origin' '
 
 '
 
-test_expect_success 'disallows --bare with --origin' '
+test_expect_success 'clone --bare -o' '
 
-       test_must_fail git clone -o foo --bare parent clone-bare-o 2>err &&
-       test_debug "cat err" &&
-       test_i18ngrep -e "options .--bare. and .--origin foo. cannot be used together" err
+       git clone -o foo --bare parent clone-bare-o &&
+       (cd parent && pwd) >expect &&
+       git -C clone-bare-o config remote.foo.url >actual &&
+       test_cmp expect actual
 
 '
 
index 5504b519c79c9b2b9d0ab7d036da1b8b56975a21..0c85ef834ab90e6fddec2ec510929df1d62acb1d 100755 (executable)
@@ -24,6 +24,7 @@ test_expect_success 'setup' '
 
 test_expect_success 'nonshallow clone implies nonshallow submodule' '
        test_when_finished "rm -rf super_clone" &&
+       test_config_global protocol.file.allow always &&
        git clone --recurse-submodules "file://$pwd/." super_clone &&
        git -C super_clone log --oneline >lines &&
        test_line_count = 3 lines &&
@@ -33,6 +34,7 @@ test_expect_success 'nonshallow clone implies nonshallow submodule' '
 
 test_expect_success 'shallow clone with shallow submodule' '
        test_when_finished "rm -rf super_clone" &&
+       test_config_global protocol.file.allow always &&
        git clone --recurse-submodules --depth 2 --shallow-submodules "file://$pwd/." super_clone &&
        git -C super_clone log --oneline >lines &&
        test_line_count = 2 lines &&
@@ -42,6 +44,7 @@ test_expect_success 'shallow clone with shallow submodule' '
 
 test_expect_success 'shallow clone does not imply shallow submodule' '
        test_when_finished "rm -rf super_clone" &&
+       test_config_global protocol.file.allow always &&
        git clone --recurse-submodules --depth 2 "file://$pwd/." super_clone &&
        git -C super_clone log --oneline >lines &&
        test_line_count = 2 lines &&
@@ -51,6 +54,7 @@ test_expect_success 'shallow clone does not imply shallow submodule' '
 
 test_expect_success 'shallow clone with non shallow submodule' '
        test_when_finished "rm -rf super_clone" &&
+       test_config_global protocol.file.allow always &&
        git clone --recurse-submodules --depth 2 --no-shallow-submodules "file://$pwd/." super_clone &&
        git -C super_clone log --oneline >lines &&
        test_line_count = 2 lines &&
@@ -60,6 +64,7 @@ test_expect_success 'shallow clone with non shallow submodule' '
 
 test_expect_success 'non shallow clone with shallow submodule' '
        test_when_finished "rm -rf super_clone" &&
+       test_config_global protocol.file.allow always &&
        git clone --recurse-submodules --no-local --shallow-submodules "file://$pwd/." super_clone &&
        git -C super_clone log --oneline >lines &&
        test_line_count = 3 lines &&
@@ -69,6 +74,7 @@ test_expect_success 'non shallow clone with shallow submodule' '
 
 test_expect_success 'clone follows shallow recommendation' '
        test_when_finished "rm -rf super_clone" &&
+       test_config_global protocol.file.allow always &&
        git config -f .gitmodules submodule.sub.shallow true &&
        git add .gitmodules &&
        git commit -m "recommend shallow for sub" &&
@@ -87,6 +93,7 @@ test_expect_success 'clone follows shallow recommendation' '
 
 test_expect_success 'get unshallow recommended shallow submodule' '
        test_when_finished "rm -rf super_clone" &&
+       test_config_global protocol.file.allow always &&
        git clone --no-local "file://$pwd/." super_clone &&
        (
                cd super_clone &&
@@ -103,6 +110,7 @@ test_expect_success 'get unshallow recommended shallow submodule' '
 
 test_expect_success 'clone follows non shallow recommendation' '
        test_when_finished "rm -rf super_clone" &&
+       test_config_global protocol.file.allow always &&
        git config -f .gitmodules submodule.sub.shallow false &&
        git add .gitmodules &&
        git commit -m "recommend non shallow for sub" &&
index 9aeacc2f6a5267cfdf8a05f2c924f4ecd04fe319..037941b95d2019f3c65b1b7fa39c03bfc343cd33 100755 (executable)
@@ -260,6 +260,8 @@ test_expect_success 'partial clone with transfer.fsckobjects=1 works with submod
        test_config -C src_with_sub uploadpack.allowfilter 1 &&
        test_config -C src_with_sub uploadpack.allowanysha1inwant 1 &&
 
+       test_config_global protocol.file.allow always &&
+
        git -C src_with_sub submodule add "file://$(pwd)/submodule" mysub &&
        git -C src_with_sub commit -m "commit with submodule" &&
 
index ca8f80083a2f93bcef46a4a4e32c7a26150ef22e..688433824934d89354669d920acd1fb06a5da725 100755 (executable)
@@ -10,6 +10,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 pwd=$(pwd)
 
 test_expect_success 'setup' '
+       git config --global protocol.file.allow always &&
        git checkout -b main &&
        test_commit commit1 &&
        mkdir sub &&
index 5d42a355a8b81b0f2087eeb8302eb7859c04553c..b33cd4afca3d021f1f8f38393521589243ae95fb 100755 (executable)
@@ -1001,7 +1001,7 @@ test_expect_success 'part of packfile response provided as URI' '
        do
                git verify-pack --object-format=$(test_oid algo) --verbose $idx >out &&
                {
-                       grep "^[0-9a-f]\{16,\} " out || :
+                       grep -E "^[0-9a-f]{16,} " out || :
                } >out.objectlist &&
                if test_line_count = 1 out.objectlist
                then
index 12e67e187ef21456f79a2ebdcc19c641d8ec81a6..2cdef6fdf985c1ec3836ed1a642da783f902a63e 100755 (executable)
@@ -27,7 +27,7 @@ test_expect_success 'setup' '
 
        : > super-file &&
        git add super-file &&
-       git submodule add "$(pwd)" sub &&
+       git -c protocol.file.allow=always submodule add "$(pwd)" sub &&
        git symbolic-ref HEAD refs/heads/super &&
        test_tick &&
        git commit -m super-initial &&
index 3a241f259de157185ba2733504e5ed1de552c7c5..3214d9db97d9bcedf86de0cfed611517cfbf58f5 100755 (executable)
@@ -10,7 +10,7 @@ test_expect_success 'setup a submodule' '
        : >pretzel/a &&
        git -C pretzel add a &&
        git -C pretzel commit -m "add a file" -- a &&
-       git submodule add ./pretzel sub &&
+       git -c protocol.file.allow=always submodule add ./pretzel sub &&
        git commit -a -m "add submodule" &&
        git submodule deinit --all
 '
index a402908142d0d34a7145df03b6ea863d513a7200..8c37bceb336344f9b10878faf9cabf933dcb8ea2 100755 (executable)
@@ -324,6 +324,7 @@ test_expect_success SYMLINKS 'check moved symlink' '
 rm -f moved symlink
 
 test_expect_success 'setup submodule' '
+       test_config_global protocol.file.allow always &&
        git commit -m initial &&
        git reset --hard &&
        git submodule add ./. sub &&
@@ -509,6 +510,7 @@ test_expect_success 'moving a submodule in nested directories' '
 '
 
 test_expect_success 'moving nested submodules' '
+       test_config_global protocol.file.allow always &&
        git commit -am "cleanup commit" &&
        mkdir sub_nested_nested &&
        (
index e18a218952385984944eaaab369af051873d0363..f6aebe92ff9d94b20670f7436c57f8107bb3404a 100755 (executable)
@@ -49,7 +49,7 @@ test_expect_success 'result is really identical' '
 test_expect_success 'rewrite bare repository identically' '
        (git config core.bare true && cd .git &&
         git filter-branch branch > filter-output 2>&1 &&
-       ! fgrep fatal filter-output)
+       ! grep fatal filter-output)
 '
 git config core.bare false
 test_expect_success 'result is really identical' '
@@ -506,7 +506,7 @@ test_expect_success 'rewrite repository including refs that point at non-commit
        git tag -a -m "tag to a tree" treetag $new_tree &&
        git reset --hard HEAD &&
        git filter-branch -f -- --all >filter-output 2>&1 &&
-       ! fgrep fatal filter-output
+       ! grep fatal filter-output
 '
 
 test_expect_success 'filter-branch handles ref deletion' '
index 20a0d2afc2a54d9cd446d2a70168641b7167a15a..11884d2fc36911cea7f82a1257e35434de61500c 100755 (executable)
@@ -473,6 +473,7 @@ test_expect_success 'create and add submodule, submodule appears clean (A. S...)
        git checkout initial-branch &&
        git clone . sub_repo &&
        git clone . super_repo &&
+       test_config_global protocol.file.allow always &&
        (       cd super_repo &&
                git submodule add ../sub_repo sub1 &&
 
index 0399701e6276d67ac8736128f24e8f8bf94a6b12..c975eb54d234d77b2f13df2a96acd4cbefb93865 100755 (executable)
@@ -480,6 +480,7 @@ test_expect_success 'should not clean submodules' '
                git init &&
                test_commit msg hello.world
        ) &&
+       test_config_global protocol.file.allow always &&
        git submodule add ./repo/.git sub1 &&
        git commit -m "sub1" &&
        git branch before_sub2 &&
index b50db3f1031339b62071bd28b52094f90c644cb5..a989aafaf57aa8710ff7889318abb8d3c9cadd51 100755 (executable)
@@ -14,6 +14,10 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
 
+test_expect_success 'setup - enable local submodules' '
+       git config --global protocol.file.allow always
+'
+
 test_expect_success 'submodule usage: -h' '
        git submodule -h >out 2>err &&
        grep "^usage: git submodule" out &&
index 7d2ac3322b50cc580d2212f40658cdda66204a16..ea92ef52a5eb9c593039614a6e42fa308a68b852 100755 (executable)
@@ -14,6 +14,8 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 . ./test-lib.sh
 
 test_expect_success setup '
+       git config --global protocol.file.allow always &&
+
        echo file >file &&
        git add file &&
        test_tick &&
index c5f5dbe55e0a91e24589c8c9d85ba53995091b36..f094e3d7f3642de61a69b3d9131e31707654539c 100755 (executable)
@@ -25,6 +25,7 @@ compare_head()
 
 
 test_expect_success 'setup a submodule tree' '
+       git config --global protocol.file.allow always &&
        echo file > file &&
        git add file &&
        test_tick &&
index e2f110b786387035f6cc4fa31a21f9c380edb2f4..59bd150166793000ed60e70fc9e570519f53f055 100755 (executable)
@@ -16,6 +16,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 
 test_expect_success 'setup a submodule tree' '
+       git config --global protocol.file.allow always &&
        echo file > file &&
        git add file &&
        test_tick &&
index c3a45455106f0961d7d80813cff55988caedd9bc..d6040e0a33703310d52d588607e4f62912f0c049 100755 (executable)
@@ -17,6 +17,10 @@ test_alternate_is_used () {
        test_cmp expect actual
 }
 
+test_expect_success 'setup' '
+       git config --global protocol.file.allow always
+'
+
 test_expect_success 'preparing first repository' '
        test_create_repo A &&
        (
index e17ac81a893e329828c5bfe1458f4db1a2fb4841..374ed481e9c64b9813a11b57c80f7b6f22c791e1 100755 (executable)
@@ -15,6 +15,10 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
 
+test_expect_success 'setup' '
+       git config --global protocol.file.allow always
+'
+
 test_expect_success 'submodule on detached working tree' '
        git init --bare remote &&
        test_create_repo bundle1 &&
index ad28e9388053c4009f4af3121788951f9e79d848..c583c4e373ad0de2a0b51080010da995646dff3a 100755 (executable)
@@ -12,6 +12,9 @@ from the database and from the worktree works.
 TEST_NO_CREATE_REPO=1
 . ./test-lib.sh
 
+test_expect_success 'setup' '
+       git config --global protocol.file.allow always
+'
 test_expect_success 'submodule config cache setup' '
        mkdir submodule &&
        (cd submodule &&
index 4dc7d089423be4acd3c83ceb4055d77fbc11ba70..7cdc26376490e80274c4f699ddd0b00c25e4d9cd 100755 (executable)
@@ -13,6 +13,7 @@ TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
+       git config --global protocol.file.allow always &&
        git init sub &&
        test_commit -C sub initial &&
        git init super &&
index 3269298197c2c2dcf92a780d401d212101c8475f..101afff30f67683ec03e92e4a89cead6badac521 100755 (executable)
@@ -32,7 +32,8 @@ test_expect_success 'no warning when updating entry' '
 
 test_expect_success 'submodule add does not warn' '
        test_when_finished "git rm -rf submodule .gitmodules" &&
-       git submodule add ./embed submodule 2>stderr &&
+       git -c protocol.file.allow=always \
+               submodule add ./embed submodule 2>stderr &&
        test_i18ngrep ! warning stderr
 '
 
index d21dc8b009f6d0d8e75368d01a984ac350155ba3..3ebd98598144c95165cc75870e6d586407add1c8 100755 (executable)
@@ -3,6 +3,10 @@
 test_description='check handling of disallowed .gitmodule urls'
 . ./test-lib.sh
 
+test_expect_success 'setup' '
+       git config --global protocol.file.allow always
+'
+
 test_expect_success 'create submodule with protected dash in url' '
        git init upstream &&
        git -C upstream commit --allow-empty -m base &&
index f0f6b9fa9e9a029bd8a17337beb402d1618ca948..2f4b25dfd7e3860e47c0acdd3a9eeabff7f5504e 100755 (executable)
@@ -6,6 +6,10 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
 
+test_expect_success 'setup' '
+       git config --global protocol.file.allow always
+'
+
 test_expect_success 'create submodule with dash in path' '
        git init upstream &&
        git -C upstream commit --allow-empty -m base &&
index 57d7ab3eced2970791021caa9855e1ed0c1d2712..d5874200fdcc44c3812f0b702fe4f8c3c2a6dbdc 100755 (executable)
@@ -17,6 +17,10 @@ export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
 
 . ./test-lib.sh
 
+test_expect_success 'setup' '
+       git config --global protocol.file.allow always
+'
+
 test_expect_success 'sparse checkout setup which hides .gitmodules' '
        git init upstream &&
        git init submodule &&
index 96e984232144c60743d59150c68461e5b1e779eb..232065504cbfdcba60d3f5f69ffb4536b5ae99e8 100755 (executable)
@@ -13,6 +13,10 @@ TEST_PASSES_SANITIZE_LEAK=true
 TEST_NO_CREATE_REPO=1
 . ./test-lib.sh
 
+test_expect_success 'setup' '
+       git config --global protocol.file.allow always
+'
+
 test_expect_success 'submodule config cache setup' '
        mkdir submodule &&
        (cd submodule &&
index ef0cb6e8e185379dc853e16dcd7d2ea84ac8c9ff..d6bf62b3ac670fc0be8167150cdf79cbd9000a45 100755 (executable)
@@ -12,6 +12,10 @@ as expected.
 TEST_NO_CREATE_REPO=1
 . ./test-lib.sh
 
+test_expect_success 'setup' '
+       git config --global protocol.file.allow always
+'
+
 test_expect_success 'submodule config cache setup' '
        mkdir submodule &&
        (
index b070f13714a7ee194e513cffd143ef2b0039afe3..ce64d8b13721732a30d8739c72dfdba670060454 100755 (executable)
@@ -12,6 +12,10 @@ while making sure to add submodules using `git submodule add` instead of
 
 . ./test-lib.sh
 
+test_expect_success 'setup' '
+       git config --global protocol.file.allow always
+'
+
 test_expect_success 'summary test environment setup' '
        git init sm &&
        test_commit -C sm "add file" file file-content file-tag &&
index 2c24f120da3591305b073d247e21e3c07cff9386..ba1f569bcbbdc613c768bd34e7de6e560b809c9d 100755 (executable)
@@ -15,6 +15,10 @@ Such as:
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-pack.sh
 
+test_expect_success 'setup' '
+       git config --global protocol.file.allow always
+'
+
 test_expect_success 'check names' '
        cat >expect <<-\EOF &&
        valid
index f5426a8e589fb6a0beadd9ef793ed6be66b69355..d050091345b3929dc8e578409d45069b932c9733 100755 (executable)
@@ -252,6 +252,7 @@ test_expect_success 'status with merge conflict in .gitmodules' '
        test_create_repo_with_commit sub1 &&
        test_tick &&
        test_create_repo_with_commit sub2 &&
+       test_config_global protocol.file.allow always &&
        (
                cd super &&
                prev=$(git rev-parse HEAD) &&
@@ -327,6 +328,7 @@ test_expect_success 'diff --submodule with merge conflict in .gitmodules' '
 # sub2 will have an untracked file
 # sub3 will have an untracked repository
 test_expect_success 'setup superproject with untracked file in nested submodule' '
+       test_config_global protocol.file.allow always &&
        (
                cd super &&
                git clean -dfx &&
index 92462a22374042603a885c4b52fb49b3d16e0373..916470c48bd69cb245eaf0e9045cd3f1519b7ba6 100755 (executable)
@@ -76,6 +76,7 @@ test_expect_success 'diff in message is retained with -v' '
 
 test_expect_success 'submodule log is stripped out too with -v' '
        git config diff.submodule log &&
+       test_config_global protocol.file.allow always &&
        git submodule add ./. sub &&
        git commit -m "sub added" &&
        (
index 56c0dfffea6e73e54120e61cd752dda78ae15ad3..4abc74db2bb8ede3879ab32ea771d688b8211eac 100755 (executable)
@@ -813,6 +813,10 @@ my_match_and_clean () {
        git -C super/dir_1/dir_2/sub clean -d -f
 }
 
+test_expect_success 'submodule setup' '
+       git config --global protocol.file.allow always
+'
+
 test_expect_success 'submodule always visited' '
        test_when_finished "git -C super fsmonitor--daemon stop; \
                            rm -rf super; \
@@ -939,9 +943,9 @@ test_expect_success CASE_INSENSITIVE_FS 'case insensitive+preserving' '
        # directories and files that we touched.  We may or may not get a
        # trailing slash on modified directories.
        #
-       egrep "^event: abc/?$"       ./insensitive.trace &&
-       egrep "^event: abc/def/?$"   ./insensitive.trace &&
-       egrep "^event: abc/def/xyz$" ./insensitive.trace
+       grep -E "^event: abc/?$"       ./insensitive.trace &&
+       grep -E "^event: abc/def/?$"   ./insensitive.trace &&
+       grep -E "^event: abc/def/xyz$" ./insensitive.trace
 '
 
 # The variable "unicode_debug" is defined in the following library
@@ -983,20 +987,20 @@ test_expect_success !UNICODE_COMPOSITION_SENSITIVE 'Unicode nfc/nfd' '
        then
                # We should have seen NFC event from OS.
                # We should not have synthesized an NFD event.
-               egrep    "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace &&
-               egrep -v "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace
+               grep -E    "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace &&
+               grep -E -v "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace
        else
                # We should have seen NFD event from OS.
                # We should have synthesized an NFC event.
-               egrep "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace &&
-               egrep "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace
+               grep -E "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace &&
+               grep -E "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace
        fi &&
 
        # We assume UNICODE_NFD_PRESERVED.
        # We should have seen explicit NFD from OS.
        # We should have synthesized an NFC event.
-       egrep "^event: nfd/d_${utf8_nfd}/?$" ./unicode.trace &&
-       egrep "^event: nfd/d_${utf8_nfc}/?$" ./unicode.trace
+       grep -E "^event: nfd/d_${utf8_nfd}/?$" ./unicode.trace &&
+       grep -E "^event: nfd/d_${utf8_nfc}/?$" ./unicode.trace
 '
 
 test_done
index 937f89ee8c8aa0afd4db98fe4bbe9b63b333d77a..b7ac4f598a85c47b360b50726ca3d7b04840ff96 100755 (executable)
@@ -35,7 +35,7 @@ test_expect_success '-A with -d option leaves unreachable objects unpacked' '
        git repack -A -d -l &&
        # verify objects are packed in repository
        test 3 = $(git verify-pack -v -- .git/objects/pack/*.idx |
-                  egrep "^($fsha1|$csha1|$tsha1) " |
+                  grep -E "^($fsha1|$csha1|$tsha1) " |
                   sort | uniq | wc -l) &&
        git show $fsha1 &&
        git show $csha1 &&
@@ -49,7 +49,7 @@ test_expect_success '-A with -d option leaves unreachable objects unpacked' '
        git repack -A -d -l &&
        # verify objects are retained unpacked
        test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx |
-                  egrep "^($fsha1|$csha1|$tsha1) " |
+                  grep -E "^($fsha1|$csha1|$tsha1) " |
                   sort | uniq | wc -l) &&
        git show $fsha1 &&
        git show $csha1 &&
index 096456292c0ad604d90c1d4d2aebb8aacf883d99..24297e26ca028bd0b36aa29803a51c6ddde73f0c 100755 (executable)
@@ -636,6 +636,7 @@ test_expect_success 'difftool --no-symlinks detects conflict ' '
 
 test_expect_success 'difftool properly honors gitlink and core.worktree' '
        test_when_finished rm -rf submod/ule &&
+       test_config_global protocol.file.allow always &&
        git submodule add ./. submod/ule &&
        test_config -C submod/ule diff.tool checktrees &&
        test_config -C submod/ule difftool.checktrees.cmd '\''
index 0f937990a062e30b5f11baa296c88472019ac338..8eded6ab274fafe636151b7903bae3351a27df5c 100755 (executable)
@@ -18,6 +18,9 @@ test_invalid_grep_expression() {
        '
 }
 
+LC_ALL=en_US.UTF-8 test-tool regex '^.$' '¿' &&
+  test_set_prereq MB_REGEX
+
 cat >hello.c <<EOF
 #include <assert.h>
 #include <stdio.h>
@@ -88,6 +91,10 @@ test_expect_success setup '
                echo unusual >"\"unusual\" pathname" &&
                echo unusual >"t/nested \"unusual\" pathname"
        fi &&
+       if test_have_prereq MB_REGEX
+       then
+               echo "¿" >reverse-question-mark
+       fi &&
        git add . &&
        test_tick &&
        git commit -m initial
@@ -569,6 +576,14 @@ do
        '
 done
 
+test_expect_success MB_REGEX 'grep exactly one char in single-char multibyte file' '
+       LC_ALL=en_US.UTF-8 git grep "^.$" reverse-question-mark
+'
+
+test_expect_success MB_REGEX 'grep two chars in single-char multibyte file' '
+       LC_ALL=en_US.UTF-8 test_expect_code 1 git grep ".." reverse-question-mark
+'
+
 cat >expected <<EOF
 file
 EOF
index 3ad80526c4c426b1ebeb94ee0730ff9ec79a4ef2..8143817b19e7962460387b58b7c807e0c995090e 100755 (executable)
@@ -197,6 +197,7 @@ test_expect_success !MINGW 'grep recurse submodule colon in name' '
        git -C "su:b" commit -m "add fi:le" &&
        test_tick &&
 
+       test_config_global protocol.file.allow always &&
        git -C parent submodule add "../su:b" "su:b" &&
        git -C parent commit -m "add submodule" &&
        test_tick &&
@@ -231,6 +232,7 @@ test_expect_success 'grep history with moved submoules' '
        git -C sub commit -m "add file" &&
        test_tick &&
 
+       test_config_global protocol.file.allow always &&
        git -C parent submodule add ../sub dir/sub &&
        git -C parent commit -m "add submodule" &&
        test_tick &&
@@ -275,6 +277,7 @@ test_expect_success 'grep using relative path' '
        mkdir parent/src &&
        echo "(1|2)d(3|4)" >parent/src/file2 &&
        git -C parent add src/file2 &&
+       test_config_global protocol.file.allow always &&
        git -C parent submodule add ../sub &&
        git -C parent commit -m "add files and submodule" &&
        test_tick &&
@@ -317,6 +320,7 @@ test_expect_success 'grep from a subdir' '
        mkdir parent/src &&
        echo "(1|2)d(3|4)" >parent/src/file &&
        git -C parent add src/file &&
+       test_config_global protocol.file.allow always &&
        git -C parent submodule add ../sub src/sub &&
        git -C parent submodule add ../sub sub &&
        git -C parent commit -m "add files and submodules" &&
@@ -550,6 +554,7 @@ test_expect_failure 'grep saves textconv cache in the appropriate repository' '
 
 test_expect_success 'grep partially-cloned submodule' '
        # Set up clean superproject and submodule for partial cloning.
+       test_config_global protocol.file.allow always &&
        git init super &&
        git init super/sub &&
        (
index 2724a44fe3ef240aa09077f770706ed022a99b7e..96bdd4204560c58ec2fe97f9898e698a2379549f 100755 (executable)
@@ -480,6 +480,11 @@ test_expect_success 'maintenance.strategy inheritance' '
 
 test_expect_success 'register and unregister' '
        test_when_finished git config --global --unset-all maintenance.repo &&
+
+       test_must_fail git maintenance unregister 2>err &&
+       grep "is not registered" err &&
+       git maintenance unregister --force &&
+
        git config --global --add maintenance.repo /existing1 &&
        git config --global --add maintenance.repo /existing2 &&
        git config --global --get-all maintenance.repo >before &&
@@ -493,7 +498,11 @@ test_expect_success 'register and unregister' '
 
        git maintenance unregister &&
        git config --global --get-all maintenance.repo >actual &&
-       test_cmp before actual
+       test_cmp before actual &&
+
+       test_must_fail git maintenance unregister 2>err &&
+       grep "is not registered" err &&
+       git maintenance unregister --force
 '
 
 test_expect_success !MINGW 'register and unregister with regex metacharacters' '
index 01c74b8b0756caf01483f8cafbdda55eeb7d0ddb..1130ef21b342a83f02f7d5865fa726b80fd4ce01 100755 (executable)
@@ -1519,7 +1519,7 @@ test_expect_success $PREREQ 'asks about and fixes 8bit encodings' '
        grep "do not declare a Content-Transfer-Encoding" stdout &&
        grep email-using-8bit stdout &&
        grep "Which 8bit encoding" stdout &&
-       egrep "Content|MIME" msgtxt1 >actual &&
+       grep -E "Content|MIME" msgtxt1 >actual &&
        test_cmp content-type-decl actual
 '
 
@@ -1530,7 +1530,7 @@ test_expect_success $PREREQ 'sendemail.8bitEncoding works' '
        git send-email --from=author@example.com --to=nobody@example.com \
                        --smtp-server="$(pwd)/fake.sendmail" \
                        email-using-8bit >stdout &&
-       egrep "Content|MIME" msgtxt1 >actual &&
+       grep -E "Content|MIME" msgtxt1 >actual &&
        test_cmp content-type-decl actual
 '
 
@@ -1545,7 +1545,7 @@ test_expect_success $PREREQ 'sendemail.8bitEncoding in .git/config overrides --g
        git send-email --from=author@example.com --to=nobody@example.com \
                        --smtp-server="$(pwd)/fake.sendmail" \
                        email-using-8bit >stdout &&
-       egrep "Content|MIME" msgtxt1 >actual &&
+       grep -E "Content|MIME" msgtxt1 >actual &&
        test_cmp content-type-decl actual
 '
 
@@ -1557,7 +1557,7 @@ test_expect_success $PREREQ '--8bit-encoding overrides sendemail.8bitEncoding' '
                        --smtp-server="$(pwd)/fake.sendmail" \
                        --8bit-encoding=UTF-8 \
                        email-using-8bit >stdout &&
-       egrep "Content|MIME" msgtxt1 >actual &&
+       grep -E "Content|MIME" msgtxt1 >actual &&
        test_cmp content-type-decl actual
 '
 
index f894860867a1398555876ce0586479157189a376..d8d536269cf73d64d9d2df593667c17c826479c5 100755 (executable)
@@ -35,7 +35,7 @@ test_expect_success 'SVN-side change outside of .git' '
                echo b >> a &&
                svn_cmd commit -m "SVN-side change outside of .git" &&
                svn_cmd up &&
-               svn_cmd log -v | fgrep "SVN-side change outside of .git"
+               svn_cmd log -v | grep -F "SVN-side change outside of .git"
        )
 '
 
@@ -59,7 +59,7 @@ test_expect_success 'SVN-side change inside of .git' '
                svn_cmd add --force .git &&
                svn_cmd commit -m "SVN-side change inside of .git" &&
                svn_cmd up &&
-               svn_cmd log -v | fgrep "SVN-side change inside of .git"
+               svn_cmd log -v | grep -F "SVN-side change inside of .git"
        )
 '
 
@@ -82,7 +82,7 @@ test_expect_success 'SVN-side change in and out of .git' '
                git commit -m "add a inside an SVN repo" &&
                svn_cmd commit -m "SVN-side change in and out of .git" &&
                svn_cmd up &&
-               svn_cmd log -v | fgrep "SVN-side change in and out of .git"
+               svn_cmd log -v | grep -F "SVN-side change in and out of .git"
        )
 '
 
index 4a77eb9f60da3ade87ed701ecb0dca7f7ad13d87..31884002263fcf0b245bcf5231d65554911b698c 100755 (executable)
@@ -43,7 +43,7 @@ test_expect_success 'init+fetch an SVN repository with ignored www directory' '
 test_expect_success 'verify ignore-paths config saved by clone' '
        (
            cd g &&
-           git config --get svn-remote.svn.ignore-paths | fgrep "www"
+           git config --get svn-remote.svn.ignore-paths | grep www
        )
 '
 
@@ -53,7 +53,7 @@ test_expect_success 'SVN-side change outside of www' '
                echo b >> qqq/test_qqq.txt &&
                svn_cmd commit -m "SVN-side change outside of www" &&
                svn_cmd up &&
-               svn_cmd log -v | fgrep "SVN-side change outside of www"
+               svn_cmd log -v | grep "SVN-side change outside of www"
        )
 '
 
@@ -85,7 +85,7 @@ test_expect_success 'SVN-side change inside of ignored www' '
                echo zaq >> www/test_www.txt &&
                svn_cmd commit -m "SVN-side change inside of www/test_www.txt" &&
                svn_cmd up &&
-               svn_cmd log -v | fgrep "SVN-side change inside of www/test_www.txt"
+               svn_cmd log -v | grep -F "SVN-side change inside of www/test_www.txt"
        )
 '
 
@@ -118,7 +118,7 @@ test_expect_success 'SVN-side change in and out of ignored www' '
                echo ygg >> qqq/test_qqq.txt &&
                svn_cmd commit -m "SVN-side change in and out of ignored www" &&
                svn_cmd up &&
-               svn_cmd log -v | fgrep "SVN-side change in and out of ignored www"
+               svn_cmd log -v | grep "SVN-side change in and out of ignored www"
        )
 '
 
index e8559046296ce5570ec46b2b3a9280f0a493bbc4..a420b2a87af10a9a3e6f6136d0ff53d5a466804f 100755 (executable)
@@ -43,7 +43,7 @@ test_expect_success 'fetch fails on modified hidden file' '
          git svn find-rev refs/remotes/git-svn > ../expect &&
          test_must_fail git svn fetch 2> ../errors &&
          git svn find-rev refs/remotes/git-svn > ../expect2 ) &&
-       fgrep "not found in commit" errors &&
+       grep "not found in commit" errors &&
        test_cmp expect expect2
 '
 
@@ -59,7 +59,7 @@ test_expect_success 'refetch succeeds not ignoring any files' '
        ( cd g &&
          git svn fetch &&
          git svn rebase &&
-         fgrep "mod hidden" hid/hid.txt
+         grep "mod hidden" hid/hid.txt
        )
 '
 
index 257fc8f2f8d194bbf1a0bb151da1b80d85874e28..63fa0b6732e33e393d0c47b258239b841a8a4bb8 100755 (executable)
@@ -45,7 +45,7 @@ test_expect_success 'init+fetch an SVN repository with included qqq directory' '
 test_expect_success 'verify include-paths config saved by clone' '
        (
            cd g &&
-           git config --get svn-remote.svn.include-paths | fgrep "qqq"
+           git config --get svn-remote.svn.include-paths | grep qqq
        )
 '
 
@@ -55,7 +55,7 @@ test_expect_success 'SVN-side change outside of www' '
                echo b >> qqq/test_qqq.txt &&
                svn_cmd commit -m "SVN-side change outside of www" &&
                svn_cmd up &&
-               svn_cmd log -v | fgrep "SVN-side change outside of www"
+               svn_cmd log -v | grep "SVN-side change outside of www"
        )
 '
 
@@ -87,7 +87,7 @@ test_expect_success 'SVN-side change inside of ignored www' '
                echo zaq >> www/test_www.txt &&
                svn_cmd commit -m "SVN-side change inside of www/test_www.txt" &&
                svn_cmd up &&
-               svn_cmd log -v | fgrep "SVN-side change inside of www/test_www.txt"
+               svn_cmd log -v | grep "SVN-side change inside of www/test_www.txt"
        )
 '
 
@@ -120,7 +120,7 @@ test_expect_success 'SVN-side change in and out of included qqq' '
                echo ygg >> qqq/test_qqq.txt &&
                svn_cmd commit -m "SVN-side change in and out of ignored www" &&
                svn_cmd up &&
-               svn_cmd log -v | fgrep "SVN-side change in and out of ignored www"
+               svn_cmd log -v | grep "SVN-side change in and out of ignored www"
        )
 '
 
index 14ca575a214f3b74409f89ada717277aeabe5e82..be51a8bb7a4e38e2c53c675850265c6433e905ed 100755 (executable)
@@ -116,7 +116,10 @@ test_expect_success 'scalar unregister' '
        test_must_fail git config --get --global --fixed-value \
                maintenance.repo "$(pwd)/vanish/src" &&
        scalar list >scalar.repos &&
-       ! grep -F "$(pwd)/vanish/src" scalar.repos
+       ! grep -F "$(pwd)/vanish/src" scalar.repos &&
+
+       # scalar unregister should be idempotent
+       scalar unregister vanish
 '
 
 test_expect_success 'set up repository to clone' '
index bed01c99ea704244ace8c189dc428cd432eb6205..a98ef032d94c106c8f2ee604b60b771e11880f2d 100755 (executable)
@@ -25,6 +25,7 @@ test_expect_success 'import with large marks file' '
 '
 
 test_expect_success 'setup dump with submodule' '
+       test_config_global protocol.file.allow always &&
        git submodule add "$PWD" sub &&
        git commit -m "add submodule" &&
        git fast-export HEAD >dump
index fc99703fc5181041e521ab5bb5f783c4ed484b64..ff21a12ee6e4eb1557eca22bb60ab2e01aa6b909 100755 (executable)
@@ -268,6 +268,7 @@ test_expect_success 'signed-tags=warn-strip' '
 
 test_expect_success 'setup submodule' '
 
+       test_config_global protocol.file.allow always &&
        git checkout -f main &&
        mkdir sub &&
        (
@@ -293,6 +294,7 @@ test_expect_success 'setup submodule' '
 
 test_expect_success 'submodule fast-export | fast-import' '
 
+       test_config_global protocol.file.allow always &&
        SUBENT1=$(git ls-tree main^ sub) &&
        SUBENT2=$(git ls-tree main sub) &&
        rm -rf new &&
index 468767cbf4b93eb20f209fc25b46cc0f53d5b907..2a9838f37fe293738a75e626041531456c274f72 100755 (executable)
@@ -216,7 +216,7 @@ test_expect_success 'detect copies' '
 # variable exists, which allows admins to disable the "p4 move" command.
 test_lazy_prereq P4D_HAVE_CONFIGURABLE_RUN_MOVE_ALLOW '
        p4 configure show run.move.allow >out &&
-       egrep ^run.move.allow: out
+       grep -E ^run.move.allow: out
 '
 
 # If move can be disabled, turn it off and test p4 move handling
index 9779dc0d11f33b6d8a6c5b8d8ffded834eddd8b2..0ca9937de6cfcee5c762ab47dffdc6bd5e2195f5 100755 (executable)
@@ -417,8 +417,8 @@ test_expect_success 'cleanup chmod after submit cancel' '
                ! p4 fstat -T action text &&
                test_path_is_file text+x &&
                ! p4 fstat -T action text+x &&
-               ls -l text | egrep ^-r-- &&
-               ls -l text+x | egrep ^-r-x
+               ls -l text | grep -E ^-r-- &&
+               ls -l text+x | grep -E ^-r-x
        )
 '
 
diff --git a/t/t9850-shell.sh b/t/t9850-shell.sh
new file mode 100755 (executable)
index 0000000..cfc71c3
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+test_description='git shell tests'
+. ./test-lib.sh
+
+test_expect_success 'shell allows upload-pack' '
+       printf 0000 >input &&
+       git upload-pack . <input >expect &&
+       git shell -c "git-upload-pack $SQ.$SQ" <input >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'shell forbids other commands' '
+       test_must_fail git shell -c "git config foo.bar baz"
+'
+
+test_expect_success 'shell forbids interactive use by default' '
+       test_must_fail git shell
+'
+
+test_expect_success 'shell allows interactive command' '
+       mkdir git-shell-commands &&
+       write_script git-shell-commands/ping <<-\EOF &&
+       echo pong
+       EOF
+       echo pong >expect &&
+       echo ping | git shell >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'shell complains of overlong commands' '
+       perl -e "print \"a\" x 2**12 for (0..2**19)" |
+       test_must_fail git shell 2>err &&
+       grep "too long" err
+'
+
+test_done
index c6479f24eb5ac291a29664903b3b6393d04748c6..527a7145000f6414d7663f6ee6a1d7c1396d8fe4 100644 (file)
@@ -897,7 +897,7 @@ test_path_is_symlink () {
 test_dir_is_empty () {
        test "$#" -ne 1 && BUG "1 param"
        test_path_is_dir "$1" &&
-       if test -n "$(ls -a1 "$1" | egrep -v '^\.\.?$')"
+       if test -n "$(ls -a1 "$1" | grep -E -v '^\.\.?$')"
        then
                echo "Directory '$1' is not empty, it contains:"
                ls -la "$1"
index a65df2fd22046586a714e2aa40482f022e8740f8..6ca68311eb9ea3db2671dabd794b0229e270edd2 100644 (file)
@@ -563,9 +563,11 @@ case $GIT_TEST_FSYNC in
 esac
 
 # Add libc MALLOC and MALLOC_PERTURB test only if we are not executing
-# the test with valgrind and have not compiled with SANITIZE=address.
+# the test with valgrind and have not compiled with conflict SANITIZE
+# options.
 if test -n "$valgrind" ||
    test -n "$SANITIZE_ADDRESS" ||
+   test -n "$SANITIZE_LEAK" ||
    test -n "$TEST_NO_MALLOC_CHECK"
 then
        setup_malloc_check () {
index adf6033549ee7369c370e333a57db18ac8bdf0d6..2a2012eb6d0d4b38c047b570ca06697ffead6dbd 100644 (file)
@@ -18,7 +18,7 @@ struct tmp_objdir {
 
 /*
  * Allow only one tmp_objdir at a time in a running process, which simplifies
- * our signal/atexit cleanup routines.  It's doubtful callers will ever need
+ * our atexit cleanup routines.  It's doubtful callers will ever need
  * more than one, and we can expand later if so.  You can have many such
  * tmp_objdirs simultaneously in many processes, of course.
  */
@@ -31,7 +31,7 @@ static void tmp_objdir_free(struct tmp_objdir *t)
        free(t);
 }
 
-static int tmp_objdir_destroy_1(struct tmp_objdir *t, int on_signal)
+int tmp_objdir_destroy(struct tmp_objdir *t)
 {
        int err;
 
@@ -41,44 +41,21 @@ static int tmp_objdir_destroy_1(struct tmp_objdir *t, int on_signal)
        if (t == the_tmp_objdir)
                the_tmp_objdir = NULL;
 
-       if (!on_signal && t->prev_odb)
+       if (t->prev_odb)
                restore_primary_odb(t->prev_odb, t->path.buf);
 
-       /*
-        * This may use malloc via strbuf_grow(), but we should
-        * have pre-grown t->path sufficiently so that this
-        * doesn't happen in practice.
-        */
        err = remove_dir_recursively(&t->path, 0);
 
-       /*
-        * When we are cleaning up due to a signal, we won't bother
-        * freeing memory; it may cause a deadlock if the signal
-        * arrived while libc's allocator lock is held.
-        */
-       if (!on_signal)
-               tmp_objdir_free(t);
+       tmp_objdir_free(t);
 
        return err;
 }
 
-int tmp_objdir_destroy(struct tmp_objdir *t)
-{
-       return tmp_objdir_destroy_1(t, 0);
-}
-
 static void remove_tmp_objdir(void)
 {
        tmp_objdir_destroy(the_tmp_objdir);
 }
 
-static void remove_tmp_objdir_on_signal(int signo)
-{
-       tmp_objdir_destroy_1(the_tmp_objdir, 1);
-       sigchain_pop(signo);
-       raise(signo);
-}
-
 void tmp_objdir_discard_objects(struct tmp_objdir *t)
 {
        remove_dir_recursively(&t->path, REMOVE_DIR_KEEP_TOPLEVEL);
@@ -152,14 +129,6 @@ struct tmp_objdir *tmp_objdir_create(const char *prefix)
         */
        strbuf_addf(&t->path, "%s/tmp_objdir-%s-XXXXXX", get_object_directory(), prefix);
 
-       /*
-        * Grow the strbuf beyond any filename we expect to be placed in it.
-        * If tmp_objdir_destroy() is called by a signal handler, then
-        * we should be able to use the strbuf to remove files without
-        * having to call malloc.
-        */
-       strbuf_grow(&t->path, 1024);
-
        if (!mkdtemp(t->path.buf)) {
                /* free, not destroy, as we never touched the filesystem */
                tmp_objdir_free(t);
@@ -169,7 +138,6 @@ struct tmp_objdir *tmp_objdir_create(const char *prefix)
        the_tmp_objdir = t;
        if (!installed_handlers) {
                atexit(remove_tmp_objdir);
-               sigchain_push_common(remove_tmp_objdir_on_signal);
                installed_handlers++;
        }
 
index f78e29058fc272d2345c9d4c7d4026da9e1b1bc2..70e9c188a398db37d316becb9f8393a46ab350c5 100644 (file)
@@ -1009,8 +1009,7 @@ static enum protocol_allow_config get_protocol_config(const char *type)
        if (!strcmp(type, "http") ||
            !strcmp(type, "https") ||
            !strcmp(type, "git") ||
-           !strcmp(type, "ssh") ||
-           !strcmp(type, "file"))
+           !strcmp(type, "ssh"))
                return PROTOCOL_ALLOW_ALWAYS;
 
        /* known scary; err on the side of caution */
index 257ba4cf1ee5b71de8be0bff85511415f6600347..aa43c6411914308798a58e4e3a0a5c55d1eede8f 100644 (file)
@@ -489,62 +489,17 @@ int submodule_uses_worktrees(const char *path)
        return ret;
 }
 
-int parse_worktree_ref(const char *worktree_ref, const char **name,
-                      int *name_length, const char **ref)
-{
-       if (skip_prefix(worktree_ref, "main-worktree/", &worktree_ref)) {
-               if (!*worktree_ref)
-                       return -1;
-               if (name)
-                       *name = NULL;
-               if (name_length)
-                       *name_length = 0;
-               if (ref)
-                       *ref = worktree_ref;
-               return 0;
-       }
-       if (skip_prefix(worktree_ref, "worktrees/", &worktree_ref)) {
-               const char *slash = strchr(worktree_ref, '/');
-
-               if (!slash || slash == worktree_ref || !slash[1])
-                       return -1;
-               if (name)
-                       *name = worktree_ref;
-               if (name_length)
-                       *name_length = slash - worktree_ref;
-               if (ref)
-                       *ref = slash + 1;
-               return 0;
-       }
-       return -1;
-}
-
 void strbuf_worktree_ref(const struct worktree *wt,
                         struct strbuf *sb,
                         const char *refname)
 {
-       switch (ref_type(refname)) {
-       case REF_TYPE_PSEUDOREF:
-       case REF_TYPE_PER_WORKTREE:
-               if (wt && !wt->is_current) {
-                       if (is_main_worktree(wt))
-                               strbuf_addstr(sb, "main-worktree/");
-                       else
-                               strbuf_addf(sb, "worktrees/%s/", wt->id);
-               }
-               break;
-
-       case REF_TYPE_MAIN_PSEUDOREF:
-       case REF_TYPE_OTHER_PSEUDOREF:
-               break;
-
-       case REF_TYPE_NORMAL:
-               /*
-                * For shared refs, don't prefix worktrees/ or
-                * main-worktree/. It's not necessary and
-                * files-backend.c can't handle it anyway.
-                */
-               break;
+       if (parse_worktree_ref(refname, NULL, NULL, NULL) ==
+                   REF_WORKTREE_CURRENT &&
+           wt && !wt->is_current) {
+               if (is_main_worktree(wt))
+                       strbuf_addstr(sb, "main-worktree/");
+               else
+                       strbuf_addf(sb, "worktrees/%s/", wt->id);
        }
        strbuf_addstr(sb, refname);
 }
index e9e839926b0b83a605acdeb5b82c2e01f7104f3f..9dcea6fc8c1b4fcc387818967d2de99713bfb50b 100644 (file)
@@ -166,16 +166,6 @@ const char *worktree_git_path(const struct worktree *wt,
                              const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 
-/*
- * Parse a worktree ref (i.e. with prefix main-worktree/ or
- * worktrees/) and return the position of the worktree's name and
- * length (or NULL and zero if it's main worktree), and ref.
- *
- * All name, name_length and ref arguments could be NULL.
- */
-int parse_worktree_ref(const char *worktree_ref, const char **name,
-                      int *name_length, const char **ref);
-
 /*
  * Return a refname suitable for access from the current ref store.
  */
index c4fd91b5b4308aa13cc8e2407df1e826470f54dd..aaa0318e8248116af4d14143eace6c34e59d730b 100644 (file)
@@ -23,6 +23,7 @@ void maybe_flush_or_die(FILE *f, const char *desc)
 
        if (f == stdout) {
                if (skip_stdout_flush < 0) {
+                       /* NEEDSWORK: make this a normal Boolean */
                        cp = getenv("GIT_FLUSH");
                        if (cp)
                                skip_stdout_flush = (atoi(cp) == 0);