Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.kernel.org/pub/scm/git/git.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.mailmap1
-rw-r--r--Documentation/.gitignore1
-rw-r--r--Documentation/Makefile20
-rw-r--r--Documentation/MyFirstContribution.txt16
-rw-r--r--Documentation/RelNotes/2.30.9.txt43
-rw-r--r--Documentation/RelNotes/2.31.8.txt6
-rw-r--r--Documentation/RelNotes/2.32.7.txt7
-rw-r--r--Documentation/RelNotes/2.33.8.txt7
-rw-r--r--Documentation/RelNotes/2.34.8.txt7
-rw-r--r--Documentation/RelNotes/2.35.8.txt7
-rw-r--r--Documentation/RelNotes/2.36.6.txt7
-rw-r--r--Documentation/RelNotes/2.37.7.txt7
-rw-r--r--Documentation/RelNotes/2.38.5.txt8
-rw-r--r--Documentation/RelNotes/2.39.3.txt12
-rw-r--r--Documentation/RelNotes/2.40.1.txt8
-rw-r--r--Documentation/RelNotes/2.41.0.txt170
-rw-r--r--Documentation/SubmittingPatches2
-rw-r--r--Documentation/asciidoc.conf19
-rw-r--r--Documentation/blame-options.txt10
-rw-r--r--Documentation/config/difftool.txt7
-rw-r--r--Documentation/config/format.txt7
-rw-r--r--Documentation/config/mergetool.txt7
-rw-r--r--Documentation/config/rebase.txt10
-rw-r--r--Documentation/diff-options.txt5
-rw-r--r--Documentation/git-am.txt7
-rw-r--r--Documentation/git-blame.txt2
-rw-r--r--Documentation/git-branch.txt4
-rw-r--r--Documentation/git-clone.txt5
-rw-r--r--Documentation/git-difftool.txt10
-rw-r--r--Documentation/git-for-each-ref.txt16
-rw-r--r--Documentation/git-format-patch.txt7
-rw-r--r--Documentation/git-mergetool.txt9
-rw-r--r--Documentation/git-pack-redundant.txt14
-rw-r--r--Documentation/git-rebase.txt19
-rw-r--r--Documentation/git-send-email.txt2
-rw-r--r--Documentation/git-sparse-checkout.txt25
-rw-r--r--Documentation/git-tag.txt4
-rw-r--r--Documentation/manpage-base-url.xsl.in10
-rw-r--r--Documentation/manpage-quote-apos.xsl16
-rw-r--r--Documentation/rev-list-options.txt9
-rw-r--r--Makefile4
-rw-r--r--alias.c18
-rw-r--r--alias.h3
-rw-r--r--apply.c14
-rw-r--r--archive.c75
-rw-r--r--blame.c40
-rw-r--r--builtin.h10
-rw-r--r--builtin/branch.c32
-rw-r--r--builtin/check-ref-format.c2
-rw-r--r--builtin/clone.c23
-rw-r--r--builtin/credential.c2
-rw-r--r--builtin/describe.c2
-rw-r--r--builtin/diff.c6
-rw-r--r--builtin/difftool.c16
-rw-r--r--builtin/fast-export.c79
-rw-r--r--builtin/fast-import.c10
-rw-r--r--builtin/fetch-pack.c6
-rw-r--r--builtin/fetch.c274
-rw-r--r--builtin/for-each-ref.c33
-rw-r--r--builtin/for-each-repo.c14
-rw-r--r--builtin/fsmonitor--daemon.c2
-rw-r--r--builtin/gc.c15
-rw-r--r--builtin/get-tar-commit-id.c6
-rw-r--r--builtin/log.c23
-rw-r--r--builtin/ls-files.c5
-rw-r--r--builtin/ls-remote.c1
-rw-r--r--builtin/mailsplit.c2
-rw-r--r--builtin/merge-index.c2
-rw-r--r--builtin/merge-ours.c2
-rw-r--r--builtin/merge-recursive.c2
-rw-r--r--builtin/mktag.c2
-rw-r--r--builtin/pack-objects.c1
-rw-r--r--builtin/pack-redundant.c3
-rw-r--r--builtin/rebase.c126
-rw-r--r--builtin/receive-pack.c15
-rw-r--r--builtin/reflog.c1
-rw-r--r--builtin/remote-ext.c2
-rw-r--r--builtin/remote-fd.c2
-rw-r--r--builtin/revert.c29
-rw-r--r--builtin/send-pack.c3
-rw-r--r--builtin/sparse-checkout.c137
-rw-r--r--builtin/stash.c2
-rw-r--r--builtin/submodule--helper.c9
-rw-r--r--builtin/tag.c7
-rw-r--r--builtin/unpack-file.c2
-rw-r--r--builtin/unpack-objects.c2
-rw-r--r--builtin/upload-archive.c2
-rw-r--r--builtin/var.c2
-rw-r--r--builtin/worktree.c3
-rw-r--r--builtin/write-tree.c3
-rw-r--r--bundle-uri.c9
-rw-r--r--commit-graph.c207
-rw-r--r--commit-graph.h8
-rw-r--r--commit-reach.c216
-rw-r--r--commit-reach.h40
-rw-r--r--config.c721
-rw-r--r--config.h73
-rw-r--r--connect.c8
-rw-r--r--connect.h2
-rw-r--r--contrib/completion/git-prompt.sh19
-rw-r--r--contrib/subtree/t/Makefile4
-rw-r--r--date.c14
-rw-r--r--date.h1
-rw-r--r--diagnose.c1
-rw-r--r--diagnose.h3
-rw-r--r--diff.c33
-rw-r--r--diff.h2
-rw-r--r--fsmonitor.h2
-rw-r--r--gettext.c4
-rw-r--r--gettext.h6
-rw-r--r--git-compat-util.h19
-rwxr-xr-xgit-difftool--helper.sh5
-rw-r--r--git-mergetool--lib.sh40
-rwxr-xr-xgit-mergetool.sh9
-rwxr-xr-xgit-send-email.perl4
-rw-r--r--git.c2
-rw-r--r--grep.c9
-rw-r--r--grep.h3
-rw-r--r--hashmap.h2
-rw-r--r--http-push.c2
-rw-r--r--http-walker.c11
-rw-r--r--http.c12
-rw-r--r--list-objects-filter-options.c1
-rw-r--r--list-objects-filter-options.h3
-rw-r--r--log-tree.c2
-rw-r--r--mailinfo.c4
-rw-r--r--mailmap.c42
-rw-r--r--object-name.c5
-rw-r--r--pack-bitmap.c6
-rw-r--r--parse-options-cb.c27
-rw-r--r--parse-options.h328
-rw-r--r--protocol-caps.c2
-rw-r--r--read-cache.c49
-rw-r--r--ref-filter.c94
-rw-r--r--ref-filter.h29
-rw-r--r--remote-curl.c7
-rw-r--r--remote.c1
-rw-r--r--remote.h2
-rw-r--r--revision.c1
-rw-r--r--revision.h3
-rw-r--r--scalar.c2
-rw-r--r--send-pack.c1
-rw-r--r--sequencer.c79
-rw-r--r--sequencer.h12
-rw-r--r--submodule.c3
-rw-r--r--t/Makefile8
-rwxr-xr-xt/aggregate-results.sh2
-rw-r--r--t/annotate-tests.sh14
-rwxr-xr-xt/chainlint.pl15
-rw-r--r--t/chainlint/unclosed-here-doc-indent.expect4
-rw-r--r--t/chainlint/unclosed-here-doc-indent.test4
-rw-r--r--t/chainlint/unclosed-here-doc.expect7
-rw-r--r--t/chainlint/unclosed-here-doc.test7
-rw-r--r--t/helper/test-config.c45
-rw-r--r--t/helper/test-ctype.c2
-rw-r--r--t/helper/test-date.c6
-rw-r--r--t/helper/test-drop-caches.c2
-rw-r--r--t/helper/test-dump-cache-tree.c2
-rw-r--r--t/helper/test-dump-fsmonitor.c2
-rw-r--r--t/helper/test-dump-split-index.c2
-rw-r--r--t/helper/test-dump-untracked-cache.c2
-rw-r--r--t/helper/test-example-decorate.c2
-rw-r--r--t/helper/test-fsmonitor-client.c2
-rw-r--r--t/helper/test-hexdump.c2
-rw-r--r--t/helper/test-index-version.c2
-rw-r--r--t/helper/test-match-trees.c2
-rw-r--r--t/helper/test-oid-array.c2
-rw-r--r--t/helper/test-oidmap.c2
-rw-r--r--t/helper/test-oidtree.c4
-rw-r--r--t/helper/test-online-cpus.c2
-rw-r--r--t/helper/test-parse-options.c4
-rw-r--r--t/helper/test-prio-queue.c2
-rw-r--r--t/helper/test-read-graph.c2
-rw-r--r--t/helper/test-ref-store.c5
-rw-r--r--t/helper/test-scrap-cache-tree.c2
-rw-r--r--t/helper/test-sigchain.c2
-rw-r--r--t/helper/test-strcmp-offset.c2
-rw-r--r--t/helper/test-submodule-config.c2
-rw-r--r--t/helper/test-submodule.c2
-rw-r--r--t/helper/test-trace2.c6
-rw-r--r--t/helper/test-xml-encode.c2
-rw-r--r--t/lib-httpd/apache.conf2
-rw-r--r--t/lib-httpd/apply-one-time-perl.sh2
-rwxr-xr-xt/perf/p1500-graph-walks.sh50
-rwxr-xr-xt/perf/p2000-sparse-operations.sh6
-rwxr-xr-xt/t0068-for-each-repo.sh19
-rwxr-xr-xt/t1005-read-tree-reset.sh15
-rwxr-xr-xt/t1006-cat-file.sh3
-rwxr-xr-xt/t1010-mktree.sh4
-rwxr-xr-xt/t1091-sparse-checkout-builtin.sh167
-rwxr-xr-xt/t1092-sparse-checkout-compatibility.sh53
-rwxr-xr-xt/t1300-config.sh30
-rwxr-xr-xt/t1302-repo-version.sh2
-rwxr-xr-xt/t1308-config-set.sh117
-rwxr-xr-xt/t1400-update-ref.sh3
-rwxr-xr-xt/t1404-update-ref-errors.sh1
-rwxr-xr-xt/t1507-rev-parse-upstream.sh11
-rwxr-xr-xt/t2024-checkout-dwim.sh7
-rwxr-xr-xt/t2107-update-index-basic.sh2
-rwxr-xr-xt/t3013-ls-files-format.sh16
-rwxr-xr-xt/t3060-ls-files-with-tree.sh4
-rwxr-xr-xt/t3070-wildmatch.sh11
-rwxr-xr-xt/t3203-branch-output.sh38
-rwxr-xr-xt/t3309-notes-merge-auto-resolve.sh7
-rwxr-xr-xt/t3402-rebase-merge.sh21
-rwxr-xr-xt/t3418-rebase-continue.sh62
-rwxr-xr-xt/t3422-rebase-incompatible-options.sh17
-rwxr-xr-xt/t3430-rebase-merges.sh44
-rwxr-xr-xt/t3436-rebase-more-options.sh18
-rwxr-xr-xt/t3701-add-interactive.sh6
-rwxr-xr-xt/t4013-diff-various.sh42
-rwxr-xr-xt/t4014-format-patch.sh77
-rwxr-xr-xt/t4034-diff-words.sh4
-rwxr-xr-xt/t4115-apply-symlink.sh15
-rwxr-xr-xt/t4150-am.sh8
-rwxr-xr-xt/t4202-log.sh15
-rw-r--r--t/t4258/mbox2
-rwxr-xr-xt/t5000-tar-tree.sh22
-rwxr-xr-xt/t5001-archive-attr.sh16
-rw-r--r--t/t5100/msg00022
-rw-r--r--t/t5100/msg00032
-rw-r--r--t/t5100/msg0012--message-id2
-rw-r--r--t/t5100/quoted-cr.mbox4
-rw-r--r--t/t5100/sample.mbox6
-rwxr-xr-xt/t5304-prune.sh12
-rwxr-xr-xt/t5310-pack-bitmaps.sh20
-rwxr-xr-xt/t5318-commit-graph.sh2
-rwxr-xr-xt/t5328-commit-graph-64bit-time.sh9
-rwxr-xr-xt/t5552-skipping-fetch-negotiator.sh16
-rwxr-xr-xt/t5558-clone-bundle-uri.sh34
-rwxr-xr-xt/t5563-simple-http-auth.sh26
-rwxr-xr-xt/t5604-clone-reference.sh2
-rwxr-xr-xt/t5702-protocol-v2.sh38
-rwxr-xr-xt/t6300-for-each-ref.sh58
-rwxr-xr-xt/t6301-for-each-ref-errors.sh14
-rwxr-xr-xt/t6600-test-reach.sh169
-rwxr-xr-xt/t7004-tag.sh61
-rwxr-xr-xt/t7413-submodule-is-active.sh16
-rwxr-xr-xt/t7527-builtin-fsmonitor.sh37
-rwxr-xr-xt/t7610-mergetool.sh38
-rwxr-xr-xt/t7800-difftool.sh52
-rwxr-xr-xt/t7900-maintenance.sh38
-rwxr-xr-xt/t9001-send-email.sh38
-rwxr-xr-xt/t9304-fast-import-marks.sh29
-rwxr-xr-xt/t9351-fast-export-anonymize.sh2
-rwxr-xr-xt/t9903-bash-prompt.sh8
-rw-r--r--t/test-lib.sh27
-rw-r--r--transport.c13
-rw-r--r--unicode-width.h33
-rw-r--r--unpack-trees.c2
-rw-r--r--userdiff.c31
-rw-r--r--userdiff.h1
-rw-r--r--versioncmp.c22
-rw-r--r--wildmatch.c23
-rw-r--r--wildmatch.h2
255 files changed, 4409 insertions, 1417 deletions
diff --git a/.mailmap b/.mailmap
index 95aaa1c833..733e047aa8 100644
--- a/.mailmap
+++ b/.mailmap
@@ -65,6 +65,7 @@ Derrick Stolee <derrickstolee@github.com> <dstolee@microsoft.com>
Deskin Miller <deskinm@umich.edu>
Đoàn Trần Công Danh <congdanhqx@gmail.com> Doan Tran Cong Danh
Dirk Süsserott <newsletter@dirk.my1.cc>
+Emily Shaffer <nasamuffin@google.com> <emilyshaffer@google.com>
Eric Blake <eblake@redhat.com> <ebb9@byu.net>
Eric Hanchrow <eric.hanchrow@gmail.com> <offby1@blarg.net>
Eric S. Raymond <esr@thyrsus.com>
diff --git a/Documentation/.gitignore b/Documentation/.gitignore
index 1c3771e7d7..a48448de32 100644
--- a/Documentation/.gitignore
+++ b/Documentation/.gitignore
@@ -10,7 +10,6 @@ howto-index.txt
doc.dep
cmds-*.txt
mergetools-*.txt
-manpage-base-url.xsl
SubmittingPatches.txt
tmp-doc-diff/
GIT-ASCIIDOCFLAGS
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 9c67c3a1c5..3133ea3182 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -150,8 +150,7 @@ ASCIIDOC_HTML = xhtml11
ASCIIDOC_DOCBOOK = docbook
ASCIIDOC_CONF = -f asciidoc.conf
ASCIIDOC_COMMON = $(ASCIIDOC) $(ASCIIDOC_EXTRA) $(ASCIIDOC_CONF) \
- -amanversion=$(GIT_VERSION) \
- -amanmanual='Git Manual' -amansource='Git'
+ -amanmanual='Git Manual' -amansource='Git $(GIT_VERSION)'
ASCIIDOC_DEPS = asciidoc.conf GIT-ASCIIDOCFLAGS
TXT_TO_HTML = $(ASCIIDOC_COMMON) -b $(ASCIIDOC_HTML)
TXT_TO_XML = $(ASCIIDOC_COMMON) -b $(ASCIIDOC_DOCBOOK)
@@ -189,15 +188,7 @@ endif
ifndef MAN_BASE_URL
MAN_BASE_URL = file://$(htmldir)/
endif
-XMLTO_EXTRA += -m manpage-base-url.xsl
-
-# If your target system uses GNU groff, it may try to render
-# apostrophes as a "pretty" apostrophe using unicode. This breaks
-# cut&paste, so you should set GNU_ROFF to force them to be ASCII
-# apostrophes. Unfortunately does not work with non-GNU roff.
-ifdef GNU_ROFF
-XMLTO_EXTRA += -m manpage-quote-apos.xsl
-endif
+XMLTO_EXTRA += --stringparam man.base.url.for.relative.links='$(MAN_BASE_URL)'
ifdef USE_ASCIIDOCTOR
ASCIIDOC = asciidoctor
@@ -339,7 +330,6 @@ clean:
$(RM) technical/*.html technical/api-index.txt
$(RM) SubmittingPatches.txt
$(RM) $(cmds_txt) $(mergetools_txt) *.made
- $(RM) manpage-base-url.xsl
$(RM) GIT-ASCIIDOCFLAGS
$(MAN_HTML): %.html : %.txt $(ASCIIDOC_DEPS)
@@ -348,11 +338,7 @@ $(MAN_HTML): %.html : %.txt $(ASCIIDOC_DEPS)
$(OBSOLETE_HTML): %.html : %.txto $(ASCIIDOC_DEPS)
$(QUIET_ASCIIDOC)$(TXT_TO_HTML) -o $@ $<
-manpage-base-url.xsl: manpage-base-url.xsl.in
- $(QUIET_GEN)sed "s|@@MAN_BASE_URL@@|$(MAN_BASE_URL)|" $< > $@
-
-
-manpage-prereqs := manpage-base-url.xsl $(wildcard manpage*.xsl)
+manpage-prereqs := $(wildcard manpage*.xsl)
manpage-cmd = $(QUIET_XMLTO)$(XMLTO) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
%.1 : %.xml $(manpage-prereqs)
diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
index ccfd0cb5f3..56130e4bec 100644
--- a/Documentation/MyFirstContribution.txt
+++ b/Documentation/MyFirstContribution.txt
@@ -1164,28 +1164,28 @@ After you run this command, `format-patch` will output the patches to the `psuh/
directory, alongside the v1 patches. Using a single directory makes it easy to
refer to the old v1 patches while proofreading the v2 patches, but you will need
to be careful to send out only the v2 patches. We will use a pattern like
-"psuh/v2-*.patch" (not "psuh/*.patch", which would match v1 and v2 patches).
+`psuh/v2-*.patch` (not `psuh/*.patch`, which would match v1 and v2 patches).
Edit your cover letter again. Now is a good time to mention what's different
between your last version and now, if it's something significant. You do not
need the exact same body in your second cover letter; focus on explaining to
reviewers the changes you've made that may not be as visible.
-You will also need to go and find the Message-Id of your previous cover letter.
+You will also need to go and find the Message-ID of your previous cover letter.
You can either note it when you send the first series, from the output of `git
send-email`, or you can look it up on the
https://lore.kernel.org/git[mailing list]. Find your cover letter in the
-archives, click on it, then click "permalink" or "raw" to reveal the Message-Id
+archives, click on it, then click "permalink" or "raw" to reveal the Message-ID
header. It should match:
----
-Message-Id: <foo.12345.author@example.com>
+Message-ID: <foo.12345.author@example.com>
----
-Your Message-Id is `<foo.12345.author@example.com>`. This example will be used
-below as well; make sure to replace it with the correct Message-Id for your
-**previous cover letter** - that is, if you're sending v2, use the Message-Id
-from v1; if you're sending v3, use the Message-Id from v2.
+Your Message-ID is `<foo.12345.author@example.com>`. This example will be used
+below as well; make sure to replace it with the correct Message-ID for your
+**previous cover letter** - that is, if you're sending v2, use the Message-ID
+from v1; if you're sending v3, use the Message-ID from v2.
While you're looking at the email, you should also note who is CC'd, as it's
common practice in the mailing list to keep all CCs on a thread. You can add
diff --git a/Documentation/RelNotes/2.30.9.txt b/Documentation/RelNotes/2.30.9.txt
new file mode 100644
index 0000000000..708d626ce6
--- /dev/null
+++ b/Documentation/RelNotes/2.30.9.txt
@@ -0,0 +1,43 @@
+Git v2.30.9 Release Notes
+=========================
+
+This release addresses the security issues CVE-2023-25652,
+CVE-2023-25815, and CVE-2023-29007.
+
+
+Fixes since v2.30.8
+-------------------
+
+ * CVE-2023-25652:
+
+ By feeding specially crafted input to `git apply --reject`, a
+ path outside the working tree can be overwritten with partially
+ controlled contents (corresponding to the rejected hunk(s) from
+ the given patch).
+
+ * CVE-2023-25815:
+
+ When Git is compiled with runtime prefix support and runs without
+ translated messages, it still used the gettext machinery to
+ display messages, which subsequently potentially looked for
+ translated messages in unexpected places. This allowed for
+ malicious placement of crafted messages.
+
+ * CVE-2023-29007:
+
+ When renaming or deleting a section from a configuration file,
+ certain malicious configuration values may be misinterpreted as
+ the beginning of a new configuration section, leading to arbitrary
+ configuration injection.
+
+Credit for finding CVE-2023-25652 goes to Ry0taK, and the fix was
+developed by Taylor Blau, Junio C Hamano and Johannes Schindelin,
+with the help of Linus Torvalds.
+
+Credit for finding CVE-2023-25815 goes to Maxime Escourbiac and
+Yassine BENGANA of Michelin, and the fix was developed by Johannes
+Schindelin.
+
+Credit for finding CVE-2023-29007 goes to André Baptista and Vítor Pinho
+of Ethiack, and the fix was developed by Taylor Blau, and Johannes
+Schindelin, with help from Jeff King, and Patrick Steinhardt.
diff --git a/Documentation/RelNotes/2.31.8.txt b/Documentation/RelNotes/2.31.8.txt
new file mode 100644
index 0000000000..0aa3080780
--- /dev/null
+++ b/Documentation/RelNotes/2.31.8.txt
@@ -0,0 +1,6 @@
+Git v2.31.8 Release Notes
+=========================
+
+This release merges the fixes that appear in v2.30.9 to address the
+security issues CVE-2023-25652, CVE-2023-25815, and CVE-2023-29007;
+see the release notes for that version for details.
diff --git a/Documentation/RelNotes/2.32.7.txt b/Documentation/RelNotes/2.32.7.txt
new file mode 100644
index 0000000000..7bb35388b5
--- /dev/null
+++ b/Documentation/RelNotes/2.32.7.txt
@@ -0,0 +1,7 @@
+Git v2.32.7 Release Notes
+=========================
+
+This release merges the fixes that appear in v2.30.9 and v2.31.8 to
+address the security issues CVE-2023-25652, CVE-2023-25815, and
+CVE-2023-29007; see the release notes for these versions for
+details.
diff --git a/Documentation/RelNotes/2.33.8.txt b/Documentation/RelNotes/2.33.8.txt
new file mode 100644
index 0000000000..d8cf4c7f3a
--- /dev/null
+++ b/Documentation/RelNotes/2.33.8.txt
@@ -0,0 +1,7 @@
+Git v2.33.8 Release Notes
+=========================
+
+This release merges the fixes that appear in v2.30.9, v2.31.8 and
+v2.32.7 to address the security issues CVE-2023-25652,
+CVE-2023-25815, and CVE-2023-29007; see the release notes for these
+versions for details.
diff --git a/Documentation/RelNotes/2.34.8.txt b/Documentation/RelNotes/2.34.8.txt
new file mode 100644
index 0000000000..2b5bd7d9a3
--- /dev/null
+++ b/Documentation/RelNotes/2.34.8.txt
@@ -0,0 +1,7 @@
+Git v2.34.8 Release Notes
+=========================
+
+This release merges the fixes that appear in v2.30.9, v2.31.8,
+v2.32.7 and v2.33.8 to address the security issues CVE-2023-25652,
+CVE-2023-25815, and CVE-2023-29007; see the release notes for these
+versions for details.
diff --git a/Documentation/RelNotes/2.35.8.txt b/Documentation/RelNotes/2.35.8.txt
new file mode 100644
index 0000000000..3c9c094c2b
--- /dev/null
+++ b/Documentation/RelNotes/2.35.8.txt
@@ -0,0 +1,7 @@
+Git v2.35.8 Release Notes
+=========================
+
+This release merges the fixes that appear in v2.30.9, v2.31.8,
+v2.32.7, v2.33.8 and v2.34.8 to address the security issues
+CVE-2023-25652, CVE-2023-25815, and CVE-2023-29007; see the release
+notes for these versions for details.
diff --git a/Documentation/RelNotes/2.36.6.txt b/Documentation/RelNotes/2.36.6.txt
new file mode 100644
index 0000000000..e1edebcc43
--- /dev/null
+++ b/Documentation/RelNotes/2.36.6.txt
@@ -0,0 +1,7 @@
+Git v2.36.6 Release Notes
+=========================
+
+This release merges the fixes that appear in v2.30.9, v2.31.8,
+v2.32.7, v2.33.8, v2.34.8 and v2.35.8 to address the security issues
+CVE-2023-25652, CVS-2023-25815, and CVE-2023-29007; see the release
+notes for these versions for details.
diff --git a/Documentation/RelNotes/2.37.7.txt b/Documentation/RelNotes/2.37.7.txt
new file mode 100644
index 0000000000..4b8165f4b5
--- /dev/null
+++ b/Documentation/RelNotes/2.37.7.txt
@@ -0,0 +1,7 @@
+Git v2.37.7 Release Notes
+=========================
+
+This release merges up the fix that appears in v2.30.9, v2.31.8,
+v2.32.7, v2.33.8, v2.34.8, v2.35.8 and v2.36.6 to address the
+security issues CVE-2023-25652, CVE-2023-25815, and CVE-2023-29007;
+see the release notes for these versions for details.
diff --git a/Documentation/RelNotes/2.38.5.txt b/Documentation/RelNotes/2.38.5.txt
new file mode 100644
index 0000000000..2d1f3b1249
--- /dev/null
+++ b/Documentation/RelNotes/2.38.5.txt
@@ -0,0 +1,8 @@
+Git v2.38.5 Release Notes
+=========================
+
+This release merges up the fix that appears in v2.30.9, v2.31.8,
+v2.32.7, v2.33.8, v2.34.8, v2.35.8, v2.36.6 and v2.37.7 to address
+the security issues CVE-2023-25652, CVE-2023-25815, and
+CVE-2023-29007; see the release notes for these versions for
+details.
diff --git a/Documentation/RelNotes/2.39.3.txt b/Documentation/RelNotes/2.39.3.txt
index dddff53627..66351b65c2 100644
--- a/Documentation/RelNotes/2.39.3.txt
+++ b/Documentation/RelNotes/2.39.3.txt
@@ -1,9 +1,15 @@
Git v2.39.3 Release Notes
=========================
-This release is primarily to merge fixes accumulated on the 'master'
-front to prepare for 2.40 release that are still relevant to 2.39.x
-maintenance track.
+This release merges up the fix that appears in v2.30.9, v2.31.8,
+v2.32.7, v2.33.8, v2.34.8, v2.35.8, v2.36.6, v2.37.7 and v2.38.5 to
+address the security issues CVE-2023-25652, CVE-2023-25815, and
+CVE-2023-29007; see the release notes for these versions for
+details.
+
+This release also merges fixes that have accumulated on the 'master'
+front to prepare for the 2.40 release that are still relevant to
+2.39.x maintenance track.
Fixes since v2.39.2
-------------------
diff --git a/Documentation/RelNotes/2.40.1.txt b/Documentation/RelNotes/2.40.1.txt
new file mode 100644
index 0000000000..e72f6b1b25
--- /dev/null
+++ b/Documentation/RelNotes/2.40.1.txt
@@ -0,0 +1,8 @@
+Git v2.40.1 Release Notes
+=========================
+
+This release merges up the fix that appears in v2.30.9, v2.31.8,
+v2.32.7, v2.33.8, v2.34.8, v2.35.8, v2.36.6, v2.37.7, v2.38.5
+and v2.39.3 to address the security issues CVE-2023-25652,
+CVE-2023-25815, and CVE-2023-29007; see the release notes for these
+versions for details.
diff --git a/Documentation/RelNotes/2.41.0.txt b/Documentation/RelNotes/2.41.0.txt
index d6095a5df3..abbaff3095 100644
--- a/Documentation/RelNotes/2.41.0.txt
+++ b/Documentation/RelNotes/2.41.0.txt
@@ -27,6 +27,48 @@ UI, Workflows & Features
* "git format-patch" learned to write a log-message only output file
for empty commits.
+ * "git format-patch" honors the src/dst prefixes set to nonstandard
+ values with configuration variables like "diff.noprefix", causing
+ receiving end of the patch that expects the standard -p1 format to
+ break. "format-patch" has been taught to ignore end-user configuration
+ and always use the standard prefixes.
+
+ This is a backward compatibility breaking change.
+
+ * Lift the limitation that colored prompts can only be used with
+ PROMPT_COMMAND mode.
+
+ * "git blame --contents=<file> <rev> -- <path>" used to be forbidden,
+ but now it finds the origins of lines starting at <file> contents
+ through the history that leads to <rev>.
+
+ * "git pack-redundant" gave a warning when run, as the command has
+ outlived its usefulness long ago and is nominated for future
+ removal. Now we escalate to give an error.
+
+ * "git clone" from an empty repository learned to propagate the
+ choice of the hash algorithm from the source repository to the
+ newly created repository.
+
+ * "git mergetool" and "git difftool" learns a new configuration
+ guiDefault to optionally favor configured guitool over non-gui-tool
+ automatically when $DISPLAY is set.
+
+ * "git branch -d origin/master" would say "no such branch", but it is
+ likely a missed "-r" if refs/remotes/origin/master exists. The
+ command has been taught to give such a hint in its error message.
+
+ * Clean-up of the code path that deals with merge strategy option
+ handling in "git rebase".
+
+ * "git clone --local" stops copying from an original repository that
+ has symbolic links inside its $GIT_DIR; an error message when that
+ happens has been updated.
+
+ * "git branch --format=..." and "git format-patch --format=..."
+ learns "--omit-empty" to hide refs that whose formatting result
+ becomes an empty string from the output.
+
Performance, Internal Implementation, Development Support etc.
@@ -41,6 +83,33 @@ Performance, Internal Implementation, Development Support etc.
configuration variables, make the subsystem lazily initialize
itself.
+ * Remove workaround for ancient versions of DocBook to make it work
+ correctly with groff, which has not been necessary since docbook
+ 1.76 from 2010.
+
+ * Code clean-up to include and/or uninclude parse-options.h file as
+ needed.
+
+ * The code path that reports what "git fetch" did to each ref has
+ been cleaned up.
+
+ * Assorted config API updates.
+
+ * A few configuration variables to tell the cURL library that
+ different types of ssl-cert and ssl-key are in use have been added.
+
+ * Split key function and data structure definitions out of cache.h to
+ new header files and adjust the users.
+
+ * "git fetch --all" does not have to download and handle the same
+ bundleURI over and over, which has been corrected.
+
+ * "git sparse-checkout" command learns a debugging aid for the sparse
+ rule definitions.
+
+ * "git write-tree" learns to work better with sparse-index.
+
+
Fixes since v2.40
-----------------
@@ -84,5 +153,106 @@ Fixes since v2.40
corrected.
(merge 28d1122f9c jk/add-p-unmerged-fix later to maint).
+ * Fix for a "ls-files --format="%(path)" that produced nonsense
+ output, which was a bug in 2.38.
+ (merge cfb62dd006 aj/ls-files-format-fix later to maint).
+
+ * "git receive-pack" that responds to "git push" requests failed to
+ clean a stale lockfile when killed in the middle, which has been
+ corrected.
+ (merge c55c30669c ps/receive-pack-unlock-before-die later to maint).
+
+ * "git rev-parse --quiet foo@{u}", or anything that asks @{u} to be
+ parsed with GET_OID_QUIETLY option, did not quietly fail, which has
+ been corrected.
+ (merge dfbfdc521d fc/oid-quietly-parse-upstream later to maint).
+
+ * Transports that do not support protocol v2 did not correctly fall
+ back to protocol v0 under certain conditions, which has been
+ corrected.
+ (merge eaa0fd6584 jk/fix-proto-downgrade-to-v0 later to maint).
+
+ * time(2) on glib 2.31+, especially on Linux, goes out of sync with
+ higher resolution timers used for gettimeofday(2) and by the
+ filesystem. Replace all calls to it with a git_time() wrapper and
+ (merge 370ddcbc89 pe/time-use-gettimeofday later to maint).
+
+ * Code clean-up to use designated initializers in parse-options API.
+ (merge 353e6d4554 sg/parse-options-h-initializers later to maint).
+
+ * A recent-ish change to allow unicode character classes to be used
+ with "grep -P" triggered a JIT bug in older pcre2 libraries.
+ The problematic change in Git built with these older libraries has
+ been disabled to work around the bug.
+ (merge 14b9a04479 mk/workaround-pcre-jit-ucp-bug later to maint).
+
+ * The wildmatch library code unlearns exponential behaviour it
+ acquired some time ago since it was borrowed from rsync.
+ (merge 3dc0b7f0dc pw/wildmatch-fixes later to maint).
+
+ * The index files can become corrupt under certain conditions when
+ the split-index feature is in use, especially together with
+ fsmonitor, which have been corrected.
+ (merge 061dd722dc js/split-index-fixes later to maint).
+
+ * Document what the pathname-looking strings in "rev-list --object"
+ output are for and what they mean.
+ (merge 15364d2a3c jk/document-rev-list-object-name later to maint).
+
+ * Fix unnecessary truncation of generation numbers used in-core.
+ (merge d3af1c193d ps/ahead-behind-truncation-fix later to maint).
+
+ * Code clean-up around the use of the_repository.
+ (merge 4a93b899c1 ab/remove-implicit-use-of-the-repository later to maint).
+
+ * Consistently spell "Message-ID" as such, not "Message-Id".
+ (merge ba4324c4e1 jc/spell-id-in-both-caps-in-message-id later to maint).
+
+ * Correct use of an uninitialized structure member.
+ (merge dc12ee77ab jx/cap-object-info-uninitialized-fix later to maint).
+
+ * Tests had a few places where we ignored PERL_PATH and blindly used
+ /usr/bin/perl, which have been corrected.
+ (merge c1917156a0 jk/use-perl-path-consistently later to maint).
+
+ * Documentation mark-up fix.
+ (merge 78b6369e67 la/mfc-markup-fix later to maint).
+
+ * Doc toolchain update to remove old workaround for AsciiDoc.
+ (merge 8806120de6 fc/remove-header-workarounds-for-asciidoc later to maint).
+
+ * The userdiff regexp patterns for various filetypes that are built
+ into the system have been updated to avoid triggering regexp errors
+ from UTF-8 aware regex engines.
+ (merge be39144954 rs/userdiff-multibyte-regex later to maint).
+
+ * The approxidate() API has been simplified by losing an extra
+ function that did the same thing as another one.
+ (merge 8a7f0b666f rs/remove-approxidate-relative later to maint).
+
+ * Code clean-up to replace a hardcoded constant with a CPP macro.
+ (merge c870de6502 rs/get-tar-commit-id-use-defined-const later to maint).
+
+ * Doc build simplification.
+ (merge 9a09ed3229 fc/doc-stop-using-manversion later to maint).
+
+ * "git archive" run from a subdirectory mishandled attributes and
+ paths outside the current directory.
+ (merge 92b1dd1b9e rs/archive-from-subdirectory-fixes later to maint).
+
* Other code cleanup, docfix, build fix, etc.
(merge f7111175df as/doc-markup-fix later to maint).
+ (merge 90ff7c9898 fc/test-aggregation-clean-up later to maint).
+ (merge 9b0c7f308a jc/am-doc-refer-to-format-patch later to maint).
+ (merge b10cbdac4c bb/unicode-width-table-15 later to maint).
+ (merge 3457b50e8c ab/retire-scripted-add-p later to maint).
+ (merge d52fcf493b ds/p2000-fix-grep-sparse later to maint).
+ (merge ec063d2591 ss/hashmap-typofix later to maint).
+ (merge 1aaed69d11 rs/archive-mtime later to maint).
+ (merge 2da2cc9b28 ob/rollback-after-commit-lock-failure later to maint).
+ (merge 54dbd0933b ob/sequencer-save-head-simplify later to maint).
+ (merge a93cbe8d78 ar/test-cleanup-unused-file-creation later to maint).
+ (merge cc48ddd937 jk/chainlint-fixes later to maint).
+ (merge 4833b08426 ow/ref-format-remove-unused-member later to maint).
+ (merge d0ea2ca1cf dw/doc-submittingpatches-grammofix later to maint).
+ (merge fd72637423 ar/t2024-checkout-output-fix later to maint).
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index 927f7329a5..b218e27357 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -543,7 +543,7 @@ trigger a new CI build to ensure all tests pass.
[[mua]]
== MUA specific hints
-Some of patches I receive or pick up from the list share common
+Some of the patches I receive or pick up from the list share common
patterns of breakage. Please make sure your MUA is set up
properly not to corrupt whitespaces.
diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf
index 3e4c13971b..60f76f43ed 100644
--- a/Documentation/asciidoc.conf
+++ b/Documentation/asciidoc.conf
@@ -51,25 +51,6 @@ ifdef::doctype-manpage[]
endif::doctype-manpage[]
endif::backend-docbook[]
-ifdef::doctype-manpage[]
-ifdef::backend-docbook[]
-[header]
-template::[header-declarations]
-<refentry>
-<refmeta>
-<refentrytitle>{mantitle}</refentrytitle>
-<manvolnum>{manvolnum}</manvolnum>
-<refmiscinfo class="source">{mansource}</refmiscinfo>
-<refmiscinfo class="version">{manversion}</refmiscinfo>
-<refmiscinfo class="manual">{manmanual}</refmiscinfo>
-</refmeta>
-<refnamediv>
- <refname>{manname}</refname>
- <refpurpose>{manpurpose}</refpurpose>
-</refnamediv>
-endif::backend-docbook[]
-endif::doctype-manpage[]
-
ifdef::backend-xhtml11[]
[attributes]
git-relative-html-prefix=
diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index 9a663535f4..95599bd6e5 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -64,11 +64,11 @@ include::line-range-format.txt[]
manual page.
--contents <file>::
- When <rev> is not specified, the command annotates the
- changes starting backwards from the working tree copy.
- This flag makes the command pretend as if the working
- tree copy has the contents of the named file (specify
- `-` to make the command read from the standard input).
+ Pretend the file being annotated has a commit with the
+ contents from the named file and a parent of <rev>,
+ defaulting to HEAD when no <rev> is specified. You may
+ specify '-' to make the command read from the standard
+ input for the file contents.
--date <format>::
Specifies the format used to output dates. If --date is not
diff --git a/Documentation/config/difftool.txt b/Documentation/config/difftool.txt
index a3f8211210..447c40d85a 100644
--- a/Documentation/config/difftool.txt
+++ b/Documentation/config/difftool.txt
@@ -34,3 +34,10 @@ See the `--trust-exit-code` option in linkgit:git-difftool[1] for more details.
difftool.prompt::
Prompt before each invocation of the diff tool.
+
+difftool.guiDefault::
+ Set `true` to use the `diff.guitool` by default (equivalent to specifying
+ the `--gui` argument), or `auto` to select `diff.guitool` or `diff.tool`
+ depending on the presence of a `DISPLAY` environment variable value. The
+ default is `false`, where the `--gui` argument must be provided
+ explicitly for the `diff.guitool` to be used.
diff --git a/Documentation/config/format.txt b/Documentation/config/format.txt
index 73678d88a1..8cf6f00d93 100644
--- a/Documentation/config/format.txt
+++ b/Documentation/config/format.txt
@@ -144,3 +144,10 @@ will only show notes from `refs/notes/bar`.
format.mboxrd::
A boolean value which enables the robust "mboxrd" format when
`--stdout` is in use to escape "^>+From " lines.
+
+format.noprefix::
+ If set, do not show any source or destination prefix in patches.
+ This is equivalent to the `diff.noprefix` option used by `git
+ diff` (but which is not respected by `format-patch`). Note that
+ by setting this, the receiver of any patches you generate will
+ have to apply them using the `-p0` option.
diff --git a/Documentation/config/mergetool.txt b/Documentation/config/mergetool.txt
index e779a122d8..56a7eeeffb 100644
--- a/Documentation/config/mergetool.txt
+++ b/Documentation/config/mergetool.txt
@@ -85,3 +85,10 @@ mergetool.writeToTemp::
mergetool.prompt::
Prompt before each invocation of the merge resolution program.
+
+mergetool.guiDefault::
+ Set `true` to use the `merge.guitool` by default (equivalent to
+ specifying the `--gui` argument), or `auto` to select `merge.guitool`
+ or `merge.tool` depending on the presence of a `DISPLAY` environment
+ variable value. The default is `false`, where the `--gui` argument
+ must be provided explicitly for the `merge.guitool` to be used.
diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt
index f19bd0e040..afaf6dad99 100644
--- a/Documentation/config/rebase.txt
+++ b/Documentation/config/rebase.txt
@@ -67,3 +67,13 @@ rebase.rescheduleFailedExec::
rebase.forkPoint::
If set to false set `--no-fork-point` option by default.
+
+rebase.rebaseMerges::
+ Whether and how to set the `--rebase-merges` option by default. Can
+ be `rebase-cousins`, `no-rebase-cousins`, or a boolean. Setting to
+ true or to `no-rebase-cousins` is equivalent to
+ `--rebase-merges=no-rebase-cousins`, setting to `rebase-cousins` is
+ equivalent to `--rebase-merges=rebase-cousins`, and setting to false is
+ equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the
+ command line, with or without an argument, overrides any
+ `rebase.rebaseMerges` configuration.
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 7d73e976d9..08ab86189a 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -852,6 +852,11 @@ endif::git-format-patch[]
--no-prefix::
Do not show any source or destination prefix.
+--default-prefix::
+ Use the default source and destination prefixes ("a/" and "b/").
+ This is usually the default already, but may be used to override
+ config such as `diff.noprefix`.
+
--line-prefix=<prefix>::
Prepend an additional prefix to every line of output.
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0c1dfb3c98..900be198b1 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -24,7 +24,9 @@ DESCRIPTION
-----------
Splits mail messages in a mailbox into commit log message,
authorship information and patches, and applies them to the
-current branch.
+current branch. You could think of it as a reverse operation
+of linkgit:git-format-patch[1] run on a branch with a straight
+history without merges.
OPTIONS
-------
@@ -273,7 +275,8 @@ include::config/am.txt[]
SEE ALSO
--------
-linkgit:git-apply[1].
+linkgit:git-apply[1],
+linkgit:git-format-patch[1].
GIT
---
diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt
index 4400a17330..f69a871a96 100644
--- a/Documentation/git-blame.txt
+++ b/Documentation/git-blame.txt
@@ -12,7 +12,7 @@ SYNOPSIS
[-L <range>] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
[--ignore-rev <rev>] [--ignore-revs-file <file>]
[--color-lines] [--color-by-age] [--progress] [--abbrev=<n>]
- [<rev> | --contents <file> | --reverse <rev>..<rev>] [--] <file>
+ [ --contents <file> ] [<rev> | --reverse <rev>..<rev>] [--] <file>
DESCRIPTION
-----------
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index d382ac69f7..d207da9101 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -156,6 +156,10 @@ in another worktree linked to the same repository.
--ignore-case::
Sorting and filtering branches are case insensitive.
+--omit-empty::
+ Do not print a newline after formatted refs where the format expands
+ to the empty string.
+
--column[=<options>]::
--no-column::
Display branch listing in columns. See configuration variable
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index d6434d262d..c37c4a37f7 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -58,6 +58,11 @@ never use the local optimizations). Specifying `--no-local` will
override the default when `/path/to/repo` is given, using the regular
Git transport instead.
+
+If the repository's `$GIT_DIR/objects` has symbolic links or is a
+symbolic link, the clone will fail. This is a security measure to
+prevent the unintentional copying of files by dereferencing the symbolic
+links.
++
*NOTE*: this operation can race with concurrent modification to the
source repository, similar to running `cp -r src dst` while modifying
`src`.
diff --git a/Documentation/git-difftool.txt b/Documentation/git-difftool.txt
index 9d14c3c9f0..ac0ac6fa02 100644
--- a/Documentation/git-difftool.txt
+++ b/Documentation/git-difftool.txt
@@ -97,10 +97,12 @@ instead. `--no-symlinks` is the default on Windows.
--[no-]gui::
When 'git-difftool' is invoked with the `-g` or `--gui` option
the default diff tool will be read from the configured
- `diff.guitool` variable instead of `diff.tool`. The `--no-gui`
- option can be used to override this setting. If `diff.guitool`
- is not set, we will fallback in the order of `merge.guitool`,
- `diff.tool`, `merge.tool` until a tool is found.
+ `diff.guitool` variable instead of `diff.tool`. This may be
+ selected automatically using the configuration variable
+ `difftool.guiDefault`. The `--no-gui` option can be used to
+ override these settings. If `diff.guitool` is not set, we will
+ fallback in the order of `merge.guitool`, `diff.tool`,
+ `merge.tool` until a tool is found.
--[no-]trust-exit-code::
'git-difftool' invokes a diff tool individually on each file.
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 6da899c629..1e215d4e73 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -9,7 +9,8 @@ SYNOPSIS
--------
[verse]
'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
- [(--sort=<key>)...] [--format=<format>] [<pattern>...]
+ [(--sort=<key>)...] [--format=<format>]
+ [ --stdin | <pattern>... ]
[--points-at=<object>]
[--merged[=<object>]] [--no-merged[=<object>]]
[--contains[=<object>]] [--no-contains[=<object>]]
@@ -32,6 +33,10 @@ OPTIONS
literally, in the latter case matching completely or from the
beginning up to a slash.
+--stdin::
+ If `--stdin` is supplied, then the list of patterns is read from
+ standard input instead of from the argument list.
+
--count=<count>::
By default the command shows all refs that match
`<pattern>`. This option makes it stop after showing
@@ -93,6 +98,10 @@ OPTIONS
--ignore-case::
Sorting and filtering refs are case insensitive.
+--omit-empty::
+ Do not print a newline after formatted refs where the format expands
+ to the empty string.
+
FIELD NAMES
-----------
@@ -217,6 +226,11 @@ worktreepath::
out, if it is checked out in any linked worktree. Empty string
otherwise.
+ahead-behind:<committish>::
+ Two integers, separated by a space, demonstrating the number of
+ commits ahead and behind, respectively, when comparing the output
+ ref to the `<committish>` specified in the format.
+
In addition to the above, for commit and tag objects, the header
field names (`tree`, `parent`, `object`, `type`, and `tag`) can
be used to specify the value in the header field.
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index dfcc7da4c2..b1c13fb39a 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -99,7 +99,7 @@ To omit patch numbers from the subject, use `-N`.
If given `--thread`, `git-format-patch` will generate `In-Reply-To` and
`References` headers to make the second and subsequent patch mails appear
-as replies to the first mail; this also generates a `Message-Id` header to
+as replies to the first mail; this also generates a `Message-ID` header to
reference.
OPTIONS
@@ -163,7 +163,7 @@ include::diff-options.txt[]
--no-thread::
Controls addition of `In-Reply-To` and `References` headers to
make the second and subsequent mails appear as replies to the
- first. Also controls generation of the `Message-Id` header to
+ first. Also controls generation of the `Message-ID` header to
reference.
+
The optional <style> argument can be either `shallow` or `deep`.
@@ -173,8 +173,7 @@ series, where the head is chosen from the cover letter, the
threading makes every mail a reply to the previous one.
+
The default is `--no-thread`, unless the `format.thread` configuration
-is set. If `--thread` is specified without a style, it defaults to the
-style specified by `format.thread` if any, or else `shallow`.
+is set. `--thread` without an argument is equivalent to `--thread=shallow`.
+
Beware that the default for 'git send-email' is to thread emails
itself. If you want `git format-patch` to take care of threading, you
diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt
index c44e205629..07535f6576 100644
--- a/Documentation/git-mergetool.txt
+++ b/Documentation/git-mergetool.txt
@@ -85,12 +85,13 @@ success of the resolution after the custom tool has exited.
the default merge tool will be read from the configured
`merge.guitool` variable instead of `merge.tool`. If
`merge.guitool` is not set, we will fallback to the tool
- configured under `merge.tool`.
+ configured under `merge.tool`. This may be autoselected using
+ the configuration variable `mergetool.guiDefault`.
--no-gui::
- This overrides a previous `-g` or `--gui` setting and reads the
- default merge tool will be read from the configured `merge.tool`
- variable.
+ This overrides a previous `-g` or `--gui` setting or
+ `mergetool.guiDefault` configuration and reads the default merge
+ tool from the configured `merge.tool` variable.
-O<orderfile>::
Process files in the order specified in the
diff --git a/Documentation/git-pack-redundant.txt b/Documentation/git-pack-redundant.txt
index 99ef13839d..13c3eb5ec9 100644
--- a/Documentation/git-pack-redundant.txt
+++ b/Documentation/git-pack-redundant.txt
@@ -11,6 +11,20 @@ SYNOPSIS
[verse]
'git pack-redundant' [--verbose] [--alt-odb] (--all | <pack-filename>...)
+WARNING
+-------
+`git pack-redundant` has been deprecated and is scheduled for removal in
+a future version of Git. Because it can only remove entire duplicate
+packs and not individual duplicate objects, it is generally not a useful
+tool for reducing repository size. You are better off using `git gc` to
+do so, which will put objects into a new pack, removing duplicates.
+
+Running `pack-redundant` without the `--i-still-use-this` flag will fail
+in this release. If you believe you have a use case for which
+`pack-redundant` is better suited and oppose this removal, please
+contact the Git mailing list at git@vger.kernel.org. More information
+about the list is available at https://git-scm.com/community.
+
DESCRIPTION
-----------
This program computes which packs in your repository
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 9a295bcee4..e7b39ad244 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -529,20 +529,25 @@ See also INCOMPATIBLE OPTIONS below.
-r::
--rebase-merges[=(rebase-cousins|no-rebase-cousins)]::
+--no-rebase-merges::
By default, a rebase will simply drop merge commits from the todo
list, and put the rebased commits into a single, linear branch.
With `--rebase-merges`, the rebase will instead try to preserve
the branching structure within the commits that are to be rebased,
by recreating the merge commits. Any resolved merge conflicts or
manual amendments in these merge commits will have to be
- resolved/re-applied manually.
+ resolved/re-applied manually. `--no-rebase-merges` can be used to
+ countermand both the `rebase.rebaseMerges` config option and a previous
+ `--rebase-merges`.
+
-By default, or when `no-rebase-cousins` was specified, commits which do not
-have `<upstream>` as direct ancestor will keep their original branch point,
-i.e. commits that would be excluded by linkgit:git-log[1]'s
-`--ancestry-path` option will keep their original ancestry by default. If
-the `rebase-cousins` mode is turned on, such commits are instead rebased
-onto `<upstream>` (or `<onto>`, if specified).
+When rebasing merges, there are two modes: `rebase-cousins` and
+`no-rebase-cousins`. If the mode is not specified, it defaults to
+`no-rebase-cousins`. In `no-rebase-cousins` mode, commits which do not have
+`<upstream>` as direct ancestor will keep their original branch point, i.e.
+commits that would be excluded by linkgit:git-log[1]'s `--ancestry-path`
+option will keep their original ancestry by default. In `rebase-cousins` mode,
+such commits are instead rebased onto `<upstream>` (or `<onto>`, if
+specified).
+
It is currently only possible to recreate the merge commits using the
`ort` merge strategy; different merge strategies can be used only via
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 765b2df853..b0f438ec99 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -93,7 +93,7 @@ See the CONFIGURATION section for `sendemail.multiEdit`.
--in-reply-to=<identifier>::
Make the first mail (or all the mails with `--no-thread`) appear as a
- reply to the given Message-Id, which avoids breaking threads to
+ reply to the given Message-ID, which avoids breaking threads to
provide a new patch series.
The second and subsequent emails will be sent as replies according to
the `--[no-]chain-reply-to` setting.
diff --git a/Documentation/git-sparse-checkout.txt b/Documentation/git-sparse-checkout.txt
index 68392d2a56..53dc17aa77 100644
--- a/Documentation/git-sparse-checkout.txt
+++ b/Documentation/git-sparse-checkout.txt
@@ -9,7 +9,7 @@ git-sparse-checkout - Reduce your working tree to a subset of tracked files
SYNOPSIS
--------
[verse]
-'git sparse-checkout' (init | list | set | add | reapply | disable) [<options>]
+'git sparse-checkout' (init | list | set | add | reapply | disable | check-rules) [<options>]
DESCRIPTION
@@ -135,6 +135,29 @@ paths to pass to a subsequent 'set' or 'add' command. However,
the disable command, so the easy restore of calling a plain `init`
decreased in utility.
+'check-rules'::
+ Check whether sparsity rules match one or more paths.
++
+By default `check-rules` reads a list of paths from stdin and outputs only
+the ones that match the current sparsity rules. The input is expected to consist
+of one path per line, matching the output of `git ls-tree --name-only` including
+that pathnames that begin with a double quote (") are interpreted as C-style
+quoted strings.
++
+When called with the `--rules-file <file>` flag the input files are matched
+against the sparse checkout rules found in `<file>` instead of the current ones.
+The rules in the files are expected to be in the same form as accepted by `git
+sparse-checkout set --stdin` (in particular, they must be newline-delimited).
++
+By default, the rules passed to the `--rules-file` option are interpreted as
+cone mode directories. To pass non-cone mode patterns with `--rules-file`,
+combine the option with the `--no-cone` option.
++
+When called with the `-z` flag, the format of the paths input on stdin as well
+as the output paths are \0 terminated and not quoted. Note that this does not
+apply to the format of the rules passed with the `--rules-file` option.
+
+
EXAMPLES
--------
`git sparse-checkout set MY/DIR1 SUB/DIR2`::
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index fdc72b5875..7f61c1edb3 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -131,6 +131,10 @@ options for details.
--ignore-case::
Sorting and filtering tags are case insensitive.
+--omit-empty::
+ Do not print a newline after formatted refs where the format expands
+ to the empty string.
+
--column[=<options>]::
--no-column::
Display tag listing in columns. See configuration variable
diff --git a/Documentation/manpage-base-url.xsl.in b/Documentation/manpage-base-url.xsl.in
deleted file mode 100644
index e800904df3..0000000000
--- a/Documentation/manpage-base-url.xsl.in
+++ /dev/null
@@ -1,10 +0,0 @@
-<!-- manpage-base-url.xsl:
- special settings for manpages rendered from newer docbook -->
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- version="1.0">
-
-<!-- set a base URL for relative links -->
-<xsl:param name="man.base.url.for.relative.links"
- >@@MAN_BASE_URL@@</xsl:param>
-
-</xsl:stylesheet>
diff --git a/Documentation/manpage-quote-apos.xsl b/Documentation/manpage-quote-apos.xsl
deleted file mode 100644
index aeb8839f33..0000000000
--- a/Documentation/manpage-quote-apos.xsl
+++ /dev/null
@@ -1,16 +0,0 @@
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- version="1.0">
-
-<!-- work around newer groff/man setups using a prettier apostrophe
- that unfortunately does not quote anything when cut&pasting
- examples to the shell -->
-<xsl:template name="escape.apostrophe">
- <xsl:param name="content"/>
- <xsl:call-template name="string.subst">
- <xsl:with-param name="string" select="$content"/>
- <xsl:with-param name="target">'</xsl:with-param>
- <xsl:with-param name="replacement">\(aq</xsl:with-param>
- </xsl:call-template>
-</xsl:template>
-
-</xsl:stylesheet>
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index 90c73d6708..3000888a90 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -890,7 +890,7 @@ ifdef::git-rev-list[]
Print the object IDs of any object referenced by the listed
commits. `--objects foo ^bar` thus means ``send me
all object IDs which I need to download if I have the commit
- object _bar_ but not _foo_''.
+ object _bar_ but not _foo_''. See also `--object-names` below.
--in-commit-order::
Print tree and blob ids in order of the commits. The tree
@@ -920,7 +920,12 @@ ifdef::git-rev-list[]
--object-names::
Only useful with `--objects`; print the names of the object IDs
- that are found. This is the default behavior.
+ that are found. This is the default behavior. Note that the
+ "name" of each object is ambiguous, and mostly intended as a
+ hint for packing objects. In particular: no distinction is made between
+ the names of tags, trees, and blobs; path names may be modified
+ to remove newlines; and if an object would appear multiple times
+ with different names, only one name is shown.
--no-object-names::
Only useful with `--objects`; does not print the names of the object
diff --git a/Makefile b/Makefile
index 0c367881bf..e440728c24 100644
--- a/Makefile
+++ b/Makefile
@@ -207,10 +207,6 @@ include shared.mak
# Define NO_ST_BLOCKS_IN_STRUCT_STAT if your platform does not have st_blocks
# field that counts the on-disk footprint in 512-byte blocks.
#
-# Define GNU_ROFF if your target system uses GNU groff. This forces
-# apostrophes to be ASCII so that cut&pasting examples to the shell
-# will work.
-#
# Define USE_ASCIIDOCTOR to use Asciidoctor instead of AsciiDoc to build the
# documentation.
#
diff --git a/alias.c b/alias.c
index e814948ced..54a1a23d2c 100644
--- a/alias.c
+++ b/alias.c
@@ -3,6 +3,7 @@
#include "alloc.h"
#include "config.h"
#include "gettext.h"
+#include "strbuf.h"
#include "string-list.h"
struct config_alias_data {
@@ -46,6 +47,23 @@ void list_aliases(struct string_list *list)
read_early_config(config_alias_cb, &data);
}
+void quote_cmdline(struct strbuf *buf, const char **argv)
+{
+ for (const char **argp = argv; *argp; argp++) {
+ if (argp != argv)
+ strbuf_addch(buf, ' ');
+ strbuf_addch(buf, '"');
+ for (const char *p = *argp; *p; p++) {
+ const char c = *p;
+
+ if (c == '"' || c =='\\')
+ strbuf_addch(buf, '\\');
+ strbuf_addch(buf, c);
+ }
+ strbuf_addch(buf, '"');
+ }
+}
+
#define SPLIT_CMDLINE_BAD_ENDING 1
#define SPLIT_CMDLINE_UNCLOSED_QUOTE 2
#define SPLIT_CMDLINE_ARGC_OVERFLOW 3
diff --git a/alias.h b/alias.h
index aef4843bb7..43db736484 100644
--- a/alias.h
+++ b/alias.h
@@ -1,9 +1,12 @@
#ifndef ALIAS_H
#define ALIAS_H
+struct strbuf;
struct string_list;
char *alias_lookup(const char *alias);
+/* Quote argv so buf can be parsed by split_cmdline() */
+void quote_cmdline(struct strbuf *buf, const char **argv);
int split_cmdline(char *cmdline, const char ***argv);
/* Takes a negative value returned by split_cmdline */
const char *split_cmdline_strerror(int cmdline_errno);
diff --git a/apply.c b/apply.c
index 2868cef5dd..3636bc14c2 100644
--- a/apply.c
+++ b/apply.c
@@ -4588,7 +4588,7 @@ static int write_out_one_reject(struct apply_state *state, struct patch *patch)
FILE *rej;
char namebuf[PATH_MAX];
struct fragment *frag;
- int cnt = 0;
+ int fd, cnt = 0;
struct strbuf sb = STRBUF_INIT;
for (cnt = 0, frag = patch->fragments; frag; frag = frag->next) {
@@ -4628,7 +4628,17 @@ static int write_out_one_reject(struct apply_state *state, struct patch *patch)
memcpy(namebuf, patch->new_name, cnt);
memcpy(namebuf + cnt, ".rej", 5);
- rej = fopen(namebuf, "w");
+ fd = open(namebuf, O_CREAT | O_EXCL | O_WRONLY, 0666);
+ if (fd < 0) {
+ if (errno != EEXIST)
+ return error_errno(_("cannot open %s"), namebuf);
+ if (unlink(namebuf))
+ return error_errno(_("cannot unlink '%s'"), namebuf);
+ fd = open(namebuf, O_CREAT | O_EXCL | O_WRONLY, 0666);
+ if (fd < 0)
+ return error_errno(_("cannot open %s"), namebuf);
+ }
+ rej = fdopen(fd, "w");
if (!rej)
return error_errno(_("cannot open %s"), namebuf);
diff --git a/archive.c b/archive.c
index ab8966d73a..8570cf37ff 100644
--- a/archive.c
+++ b/archive.c
@@ -174,6 +174,29 @@ static int write_archive_entry(const struct object_id *oid, const char *base,
args->convert = check_attr_export_subst(check);
}
+ if (args->prefix) {
+ static struct strbuf new_path = STRBUF_INIT;
+ static struct strbuf buf = STRBUF_INIT;
+ const char *rel;
+
+ rel = relative_path(path_without_prefix, args->prefix, &buf);
+
+ /*
+ * We don't add an entry for the current working
+ * directory when we are at the root; skip it also when
+ * we're in a subdirectory or submodule. Skip entries
+ * higher up as well.
+ */
+ if (!strcmp(rel, "./") || starts_with(rel, "../"))
+ return S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0;
+
+ /* rel can refer to path, so don't edit it in place */
+ strbuf_reset(&new_path);
+ strbuf_add(&new_path, args->base, args->baselen);
+ strbuf_addstr(&new_path, rel);
+ strbuf_swap(&path, &new_path);
+ }
+
if (args->verbose)
fprintf(stderr, "%.*s\n", (int)path.len, path.buf);
@@ -409,6 +432,27 @@ static int reject_entry(const struct object_id *oid UNUSED,
return ret;
}
+static int reject_outside(const struct object_id *oid UNUSED,
+ struct strbuf *base, const char *filename,
+ unsigned mode, void *context)
+{
+ struct archiver_args *args = context;
+ struct strbuf buf = STRBUF_INIT;
+ struct strbuf path = STRBUF_INIT;
+ int ret = 0;
+
+ if (S_ISDIR(mode))
+ return READ_TREE_RECURSIVE;
+
+ strbuf_addbuf(&path, base);
+ strbuf_addstr(&path, filename);
+ if (starts_with(relative_path(path.buf, args->prefix, &buf), "../"))
+ ret = -1;
+ strbuf_release(&buf);
+ strbuf_release(&path);
+ return ret;
+}
+
static int path_exists(struct archiver_args *args, const char *path)
{
const char *paths[] = { path, NULL };
@@ -416,8 +460,13 @@ static int path_exists(struct archiver_args *args, const char *path)
int ret;
ctx.args = args;
- parse_pathspec(&ctx.pathspec, 0, 0, "", paths);
+ parse_pathspec(&ctx.pathspec, 0, PATHSPEC_PREFER_CWD,
+ args->prefix, paths);
ctx.pathspec.recursive = 1;
+ if (args->prefix && read_tree(args->repo, args->tree, &ctx.pathspec,
+ reject_outside, args))
+ die(_("pathspec '%s' matches files outside the "
+ "current directory"), path);
ret = read_tree(args->repo, args->tree,
&ctx.pathspec,
reject_entry, &ctx);
@@ -433,9 +482,8 @@ static void parse_pathspec_arg(const char **pathspec,
* Also if pathspec patterns are dependent, we're in big
* trouble as we test each one separately
*/
- parse_pathspec(&ar_args->pathspec, 0,
- PATHSPEC_PREFER_FULL,
- "", pathspec);
+ parse_pathspec(&ar_args->pathspec, 0, PATHSPEC_PREFER_CWD,
+ ar_args->prefix, pathspec);
ar_args->pathspec.recursive = 1;
if (pathspec) {
while (*pathspec) {
@@ -447,8 +495,7 @@ static void parse_pathspec_arg(const char **pathspec,
}
static void parse_treeish_arg(const char **argv,
- struct archiver_args *ar_args, const char *prefix,
- int remote)
+ struct archiver_args *ar_args, int remote)
{
const char *name = argv[0];
const struct object_id *commit_oid;
@@ -488,20 +535,6 @@ static void parse_treeish_arg(const char **argv,
if (!tree)
die(_("not a tree object: %s"), oid_to_hex(&oid));
- if (prefix) {
- struct object_id tree_oid;
- unsigned short mode;
- int err;
-
- err = get_tree_entry(ar_args->repo,
- &tree->object.oid,
- prefix, &tree_oid,
- &mode);
- if (err || !S_ISDIR(mode))
- die(_("current working directory is untracked"));
-
- tree = parse_tree_indirect(&tree_oid);
- }
ar_args->refname = ref;
ar_args->tree = tree;
ar_args->commit_oid = commit_oid;
@@ -719,7 +752,7 @@ int write_archive(int argc, const char **argv, const char *prefix,
setup_git_directory();
}
- parse_treeish_arg(argv, &args, prefix, remote);
+ parse_treeish_arg(argv, &args, remote);
parse_pathspec_arg(argv + 1, &args);
rc = ar->write_archive(ar, &args);
diff --git a/blame.c b/blame.c
index ff2a6c4912..58dd58b6c9 100644
--- a/blame.c
+++ b/blame.c
@@ -181,12 +181,12 @@ static void set_commit_buffer_from_strbuf(struct repository *r,
static struct commit *fake_working_tree_commit(struct repository *r,
struct diff_options *opt,
const char *path,
- const char *contents_from)
+ const char *contents_from,
+ struct object_id *oid)
{
struct commit *commit;
struct blame_origin *origin;
struct commit_list **parent_tail, *parent;
- struct object_id head_oid;
struct strbuf buf = STRBUF_INIT;
const char *ident;
time_t now;
@@ -202,10 +202,7 @@ static struct commit *fake_working_tree_commit(struct repository *r,
commit->date = now;
parent_tail = &commit->parents;
- if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
- die("no such ref: HEAD");
-
- parent_tail = append_parent(r, parent_tail, &head_oid);
+ parent_tail = append_parent(r, parent_tail, oid);
append_merge_parents(r, parent_tail);
verify_working_tree_path(r, commit, path);
@@ -2777,22 +2774,37 @@ void setup_scoreboard(struct blame_scoreboard *sb,
sb->commits.compare = compare_commits_by_reverse_commit_date;
}
- if (sb->final && sb->contents_from)
- die(_("cannot use --contents with final commit object name"));
-
if (sb->reverse && sb->revs->first_parent_only)
sb->revs->children.name = NULL;
- if (!sb->final) {
+ if (sb->contents_from || !sb->final) {
+ struct object_id head_oid, *parent_oid;
+
/*
- * "--not A B -- path" without anything positive;
- * do not default to HEAD, but use the working tree
- * or "--contents".
+ * Build a fake commit at the top of the history, when
+ * (1) "git blame [^A] --path", i.e. with no positive end
+ * of the history range, in which case we build such
+ * a fake commit on top of the HEAD to blame in-tree
+ * modifications.
+ * (2) "git blame --contents=file [A] -- path", with or
+ * without positive end of the history range but with
+ * --contents, in which case we pretend that there is
+ * a fake commit on top of the positive end (defaulting to
+ * HEAD) that has the given contents in the path.
*/
+ if (sb->final) {
+ parent_oid = &sb->final->object.oid;
+ } else {
+ if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
+ die("no such ref: HEAD");
+ parent_oid = &head_oid;
+ }
+
setup_work_tree();
sb->final = fake_working_tree_commit(sb->repo,
&sb->revs->diffopt,
- sb->path, sb->contents_from);
+ sb->path, sb->contents_from,
+ parent_oid);
add_pending_object(sb->revs, &(sb->final->object), ":");
}
diff --git a/builtin.h b/builtin.h
index 46cc789789..cb0db67681 100644
--- a/builtin.h
+++ b/builtin.h
@@ -107,6 +107,16 @@ void setup_auto_pager(const char *cmd, int def);
int is_builtin(const char *s);
+/*
+ * Builtins which do not use RUN_SETUP should never see
+ * a prefix that is not empty; use this to protect downstream
+ * code which is not prepared to call prefix_filename(), etc.
+ */
+#define BUG_ON_NON_EMPTY_PREFIX(prefix) do { \
+ if ((prefix)) \
+ BUG("unexpected prefix in builtin: %s", (prefix)); \
+} while (0)
+
int cmd_add(int argc, const char **argv, const char *prefix);
int cmd_am(int argc, const char **argv, const char *prefix);
int cmd_annotate(int argc, const char **argv, const char *prefix);
diff --git a/builtin/branch.c b/builtin/branch.c
index 1fb11d55e6..501c47657c 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -46,6 +46,7 @@ static const char *head;
static struct object_id head_oid;
static int recurse_submodules = 0;
static int submodule_propagate_branches = 0;
+static int omit_empty = 0;
static int branch_use_color = -1;
static char branch_colors[][COLOR_MAXLEN] = {
@@ -222,10 +223,11 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
struct string_list_item *item;
int branch_name_pos;
+ const char *fmt_remotes = "refs/remotes/%s";
switch (kinds) {
case FILTER_REFS_REMOTES:
- fmt = "refs/remotes/%s";
+ fmt = fmt_remotes;
/* For subsequent UI messages */
remote_branch = 1;
allowed_interpret = INTERPRET_BRANCH_REMOTE;
@@ -269,9 +271,25 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
| RESOLVE_REF_ALLOW_BAD_NAME,
&oid, &flags);
if (!target) {
- error(remote_branch
- ? _("remote-tracking branch '%s' not found.")
- : _("branch '%s' not found."), bname.buf);
+ if (remote_branch) {
+ error(_("remote-tracking branch '%s' not found."), bname.buf);
+ } else {
+ char *virtual_name = mkpathdup(fmt_remotes, bname.buf);
+ char *virtual_target = resolve_refdup(virtual_name,
+ RESOLVE_REF_READING
+ | RESOLVE_REF_NO_RECURSE
+ | RESOLVE_REF_ALLOW_BAD_NAME,
+ &oid, &flags);
+ FREE_AND_NULL(virtual_name);
+
+ if (virtual_target)
+ error(_("branch '%s' not found.\n"
+ "Did you forget --remote?"),
+ bname.buf);
+ else
+ error(_("branch '%s' not found."), bname.buf);
+ FREE_AND_NULL(virtual_target);
+ }
ret = 1;
continue;
}
@@ -454,6 +472,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
if (verify_ref_format(format))
die(_("unable to parse format string"));
+ filter_ahead_behind(the_repository, format, &array);
ref_array_sort(sorting, &array);
for (i = 0; i < array.nr; i++) {
@@ -467,7 +486,8 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
string_list_append(output, out.buf);
} else {
fwrite(out.buf, 1, out.len, stdout);
- putchar('\n');
+ if (out.len || !omit_empty)
+ putchar('\n');
}
}
@@ -676,6 +696,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2),
OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1),
OPT_BIT('M', NULL, &rename, N_("move/rename a branch, even if target exists"), 2),
+ OPT_BOOL(0, "omit-empty", &omit_empty,
+ N_("do not output a newline after empty formatted refs")),
OPT_BIT('c', "copy", &copy, N_("copy a branch and its reflog"), 1),
OPT_BIT('C', NULL, &copy, N_("copy a branch, even if target exists"), 2),
OPT_BOOL('l', "list", &list, N_("list branch names")),
diff --git a/builtin/check-ref-format.c b/builtin/check-ref-format.c
index b026346742..57f0505070 100644
--- a/builtin/check-ref-format.c
+++ b/builtin/check-ref-format.c
@@ -61,6 +61,8 @@ int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
char *to_free = NULL;
int ret = 1;
+ BUG_ON_NON_EMPTY_PREFIX(prefix);
+
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(builtin_check_ref_format_usage);
diff --git a/builtin/clone.c b/builtin/clone.c
index 4ed0a1d5aa..186845ef0b 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -334,8 +334,18 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
iter = dir_iterator_begin(src->buf, DIR_ITERATOR_PEDANTIC);
- if (!iter)
+ if (!iter) {
+ if (errno == ENOTDIR) {
+ int saved_errno = errno;
+ struct stat st;
+
+ if (!lstat(src->buf, &st) && S_ISLNK(st.st_mode))
+ die(_("'%s' is a symlink, refusing to clone with --local"),
+ src->buf);
+ errno = saved_errno;
+ }
die_errno(_("failed to start iterator over '%s'"), src->buf);
+ }
strbuf_addch(src, '/');
src_len = src->len;
@@ -917,6 +927,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
int err = 0, complete_refs_before_fetch = 1;
int submodule_progress;
int filter_submodules = 0;
+ int hash_algo;
struct transport_ls_refs_options transport_ls_refs_options =
TRANSPORT_LS_REFS_OPTIONS_INIT;
@@ -1305,15 +1316,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
}
}
- if (mapped_refs) {
- int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
-
/*
* Now that we know what algorithm the remote side is using,
* let's set ours to the same thing.
*/
- initialize_repository_version(hash_algo, 1);
- repo_set_hash_algo(the_repository, hash_algo);
+ hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
+ initialize_repository_version(hash_algo, 1);
+ repo_set_hash_algo(the_repository, hash_algo);
+
+ if (mapped_refs) {
/*
* transport_get_remote_refs() may return refs with null sha-1
* in mapped_refs (see struct transport->get_refs_list
diff --git a/builtin/credential.c b/builtin/credential.c
index d7b304fa08..7010752987 100644
--- a/builtin/credential.c
+++ b/builtin/credential.c
@@ -6,7 +6,7 @@
static const char usage_msg[] =
"git credential (fill|approve|reject)";
-int cmd_credential(int argc, const char **argv, const char *prefix)
+int cmd_credential(int argc, const char **argv, const char *prefix UNUSED)
{
const char *op;
struct credential c = CREDENTIAL_INIT;
diff --git a/builtin/describe.c b/builtin/describe.c
index d360b1d12a..55b4baaa22 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -659,6 +659,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
int fd, result;
setup_work_tree();
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
repo_read_index(the_repository);
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED,
NULL, NULL, NULL);
diff --git a/builtin/diff.c b/builtin/diff.c
index 8f386535d6..5a6a5d7f4b 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -76,7 +76,7 @@ static void stuff_change(struct diff_options *opt,
}
static int builtin_diff_b_f(struct rev_info *revs,
- int argc, const char **argv,
+ int argc, const char **argv UNUSED,
struct object_array_entry **blob)
{
/* Blob vs file in the working tree*/
@@ -111,7 +111,7 @@ static int builtin_diff_b_f(struct rev_info *revs,
}
static int builtin_diff_blobs(struct rev_info *revs,
- int argc, const char **argv,
+ int argc, const char **argv UNUSED,
struct object_array_entry **blob)
{
const unsigned mode = canon_mode(S_IFREG | 0644);
@@ -211,7 +211,7 @@ static int builtin_diff_tree(struct rev_info *revs,
}
static int builtin_diff_combined(struct rev_info *revs,
- int argc, const char **argv,
+ int argc, const char **argv UNUSED,
struct object_array_entry *ent,
int ents, int first_non_parent)
{
diff --git a/builtin/difftool.c b/builtin/difftool.c
index 3ffb0524be..f09d24d37f 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -692,7 +692,7 @@ static int run_file_diff(int prompt, const char *prefix,
int cmd_difftool(int argc, const char **argv, const char *prefix)
{
- int use_gui_tool = 0, dir_diff = 0, prompt = -1, symlinks = 0,
+ int use_gui_tool = -1, dir_diff = 0, prompt = -1, symlinks = 0,
tool_help = 0, no_index = 0;
static char *difftool_cmd = NULL, *extcmd = NULL;
struct option builtin_difftool_options[] = {
@@ -742,13 +742,21 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
} else if (dir_diff)
die(_("options '%s' and '%s' cannot be used together"), "--dir-diff", "--no-index");
- die_for_incompatible_opt3(use_gui_tool, "--gui",
+ die_for_incompatible_opt3(use_gui_tool == 1, "--gui",
!!difftool_cmd, "--tool",
!!extcmd, "--extcmd");
- if (use_gui_tool)
+ /*
+ * Explicitly specified GUI option is forwarded to git-mergetool--lib.sh;
+ * empty or unset means "use the difftool.guiDefault config or default to
+ * false".
+ */
+ if (use_gui_tool == 1)
setenv("GIT_MERGETOOL_GUI", "true", 1);
- else if (difftool_cmd) {
+ else if (use_gui_tool == 0)
+ setenv("GIT_MERGETOOL_GUI", "false", 1);
+
+ if (difftool_cmd) {
if (*difftool_cmd)
setenv("GIT_DIFF_TOOL", difftool_cmd, 1);
else
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index c7835ea079..9a95f6a1a8 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -112,7 +112,7 @@ static struct decoration idnums;
static uint32_t last_idnum;
struct anonymized_entry {
struct hashmap_entry hash;
- const char *anon;
+ char *anon;
const char orig[FLEX_ARRAY];
};
@@ -141,43 +141,56 @@ static int anonymized_entry_cmp(const void *cmp_data UNUSED,
return strcmp(a->orig, b->orig);
}
+static struct anonymized_entry *add_anonymized_entry(struct hashmap *map,
+ unsigned hash,
+ const char *orig, size_t len,
+ char *anon)
+{
+ struct anonymized_entry *ret, *old;
+
+ if (!map->cmpfn)
+ hashmap_init(map, anonymized_entry_cmp, NULL, 0);
+
+ FLEX_ALLOC_MEM(ret, orig, orig, len);
+ hashmap_entry_init(&ret->hash, hash);
+ ret->anon = anon;
+ old = hashmap_put_entry(map, ret, hash);
+
+ if (old) {
+ free(old->anon);
+ free(old);
+ }
+
+ return ret;
+}
+
/*
* Basically keep a cache of X->Y so that we can repeatedly replace
* the same anonymized string with another. The actual generation
* is farmed out to the generate function.
*/
static const char *anonymize_str(struct hashmap *map,
- char *(*generate)(void *),
- const char *orig, size_t len,
- void *data)
+ char *(*generate)(void),
+ const char *orig, size_t len)
{
struct anonymized_entry_key key;
struct anonymized_entry *ret;
- if (!map->cmpfn)
- hashmap_init(map, anonymized_entry_cmp, NULL, 0);
-
hashmap_entry_init(&key.hash, memhash(orig, len));
key.orig = orig;
key.orig_len = len;
/* First check if it's a token the user configured manually... */
- if (anonymized_seeds.cmpfn)
- ret = hashmap_get_entry(&anonymized_seeds, &key, hash, &key);
- else
- ret = NULL;
+ ret = hashmap_get_entry(&anonymized_seeds, &key, hash, &key);
/* ...otherwise check if we've already seen it in this context... */
if (!ret)
ret = hashmap_get_entry(map, &key, hash, &key);
/* ...and finally generate a new mapping if necessary */
- if (!ret) {
- FLEX_ALLOC_MEM(ret, orig, orig, len);
- hashmap_entry_init(&ret->hash, key.hash.hash);
- ret->anon = generate(data);
- hashmap_put(map, &ret->hash);
- }
+ if (!ret)
+ ret = add_anonymized_entry(map, key.hash.hash,
+ orig, len, generate());
return ret->anon;
}
@@ -190,12 +203,12 @@ static const char *anonymize_str(struct hashmap *map,
*/
static void anonymize_path(struct strbuf *out, const char *path,
struct hashmap *map,
- char *(*generate)(void *))
+ char *(*generate)(void))
{
while (*path) {
const char *end_of_component = strchrnul(path, '/');
size_t len = end_of_component - path;
- const char *c = anonymize_str(map, generate, path, len, NULL);
+ const char *c = anonymize_str(map, generate, path, len);
strbuf_addstr(out, c);
path = end_of_component;
if (*path)
@@ -370,7 +383,7 @@ static void print_path_1(const char *path)
printf("%s", path);
}
-static char *anonymize_path_component(void *data)
+static char *anonymize_path_component(void)
{
static int counter;
struct strbuf out = STRBUF_INIT;
@@ -392,7 +405,7 @@ static void print_path(const char *path)
}
}
-static char *generate_fake_oid(void *data)
+static char *generate_fake_oid(void)
{
static uint32_t counter = 1; /* avoid null oid */
const unsigned hashsz = the_hash_algo->rawsz;
@@ -408,7 +421,7 @@ static const char *anonymize_oid(const char *oid_hex)
{
static struct hashmap objs;
size_t len = strlen(oid_hex);
- return anonymize_str(&objs, generate_fake_oid, oid_hex, len, NULL);
+ return anonymize_str(&objs, generate_fake_oid, oid_hex, len);
}
static void show_filemodify(struct diff_queue_struct *q,
@@ -505,7 +518,7 @@ static const char *find_encoding(const char *begin, const char *end)
return bol;
}
-static char *anonymize_ref_component(void *data)
+static char *anonymize_ref_component(void)
{
static int counter;
struct strbuf out = STRBUF_INIT;
@@ -545,13 +558,13 @@ static const char *anonymize_refname(const char *refname)
* We do not even bother to cache commit messages, as they are unlikely
* to be repeated verbatim, and it is not that interesting when they are.
*/
-static char *anonymize_commit_message(const char *old)
+static char *anonymize_commit_message(void)
{
static int counter;
return xstrfmt("subject %d\n\nbody\n", counter++);
}
-static char *anonymize_ident(void *data)
+static char *anonymize_ident(void)
{
static int counter;
struct strbuf out = STRBUF_INIT;
@@ -594,7 +607,7 @@ static void anonymize_ident_line(const char **beg, const char **end)
len = split.mail_end - split.name_begin;
ident = anonymize_str(&idents, anonymize_ident,
- split.name_begin, len, NULL);
+ split.name_begin, len);
strbuf_addstr(out, ident);
strbuf_addch(out, ' ');
strbuf_add(out, split.date_begin, split.tz_end - split.date_begin);
@@ -672,7 +685,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
mark_next_object(&commit->object);
if (anonymize) {
- reencoded = anonymize_commit_message(message);
+ reencoded = anonymize_commit_message();
} else if (encoding) {
switch(reencode_mode) {
case REENCODE_YES:
@@ -735,7 +748,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
show_progress();
}
-static char *anonymize_tag(void *data)
+static char *anonymize_tag(void)
{
static int counter;
struct strbuf out = STRBUF_INIT;
@@ -798,7 +811,7 @@ static void handle_tag(const char *name, struct tag *tag)
if (message) {
static struct hashmap tags;
message = anonymize_str(&tags, anonymize_tag,
- message, message_size, NULL);
+ message, message_size);
message_size = strlen(message);
}
}
@@ -1130,11 +1143,6 @@ static void handle_deletes(void)
}
}
-static char *anonymize_seed(void *data)
-{
- return xstrdup(data);
-}
-
static int parse_opt_anonymize_map(const struct option *opt,
const char *arg, int unset)
{
@@ -1156,7 +1164,8 @@ static int parse_opt_anonymize_map(const struct option *opt,
if (!keylen || !*value)
return error(_("--anonymize-map token cannot be empty"));
- anonymize_str(map, anonymize_seed, arg, keylen, (void *)value);
+ add_anonymized_entry(map, memhash(arg, keylen), arg, keylen,
+ xstrdup(value));
return 0;
}
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index 9f90f5b9e4..bbd9b2b3e7 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -182,6 +182,7 @@ static FILE *pack_edges;
static unsigned int show_stats = 1;
static int global_argc;
static const char **global_argv;
+static const char *global_prefix;
/* Memory pools */
static struct mem_pool fi_mem_pool = {
@@ -3253,7 +3254,7 @@ static void parse_alias(void)
static char* make_fast_import_path(const char *path)
{
if (!relative_marks_paths || is_absolute_path(path))
- return xstrdup(path);
+ return prefix_filename(global_prefix, path);
return git_pathdup("info/fast-import/%s", path);
}
@@ -3324,9 +3325,11 @@ static void option_cat_blob_fd(const char *fd)
static void option_export_pack_edges(const char *edges)
{
+ char *fn = prefix_filename(global_prefix, edges);
if (pack_edges)
fclose(pack_edges);
- pack_edges = xfopen(edges, "a");
+ pack_edges = xfopen(fn, "a");
+ free(fn);
}
static void option_rewrite_submodules(const char *arg, struct string_list *list)
@@ -3341,11 +3344,13 @@ static void option_rewrite_submodules(const char *arg, struct string_list *list)
f++;
CALLOC_ARRAY(ms, 1);
+ f = prefix_filename(global_prefix, f);
fp = fopen(f, "r");
if (!fp)
die_errno("cannot read '%s'", f);
read_mark_file(&ms, fp, insert_oid_entry);
fclose(fp);
+ free(f);
string_list_insert(list, s)->util = ms;
}
@@ -3559,6 +3564,7 @@ int cmd_fast_import(int argc, const char **argv, const char *prefix)
global_argc = argc;
global_argv = argv;
+ global_prefix = prefix;
rc_free = mem_pool_alloc(&fi_mem_pool, cmd_save * sizeof(*rc_free));
for (i = 0; i < (cmd_save - 1); i++)
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 664ac1ec0e..3ba0fe5a39 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -44,7 +44,7 @@ static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
(*sought)[*nr - 1] = ref;
}
-int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
+int cmd_fetch_pack(int argc, const char **argv, const char *prefix UNUSED)
{
int i, ret;
struct ref *ref = NULL;
@@ -215,8 +215,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
int flags = args.verbose ? CONNECT_VERBOSE : 0;
if (args.diag_url)
flags |= CONNECT_DIAG_URL;
- conn = git_connect(fd, dest, args.uploadpack,
- flags);
+ conn = git_connect(fd, dest, "git-upload-pack",
+ args.uploadpack, flags);
if (!conn)
return args.diag_url ? 0 : 1;
}
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 61e8ac113b..ab623f41b4 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -56,6 +56,16 @@ enum {
TAGS_SET = 2
};
+struct display_state {
+ struct strbuf buf;
+
+ int refcol_width;
+ int compact_format;
+
+ char *url;
+ int url_len, shown_url;
+};
+
static int fetch_prune_config = -1; /* unspecified */
static int fetch_show_forced_updates = 1;
static uint64_t forced_updates_ms = 0;
@@ -88,7 +98,6 @@ static const char *submodule_prefix = "";
static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
-static int shown_url = 0;
static struct refspec refmap = REFSPEC_INIT_FETCH;
static struct list_objects_filter_options filter_options = LIST_OBJECTS_FILTER_INIT;
static struct string_list server_options = STRING_LIST_INIT_DUP;
@@ -750,16 +759,13 @@ out:
return ret;
}
-static int refcol_width = 10;
-static int compact_format;
-
-static void adjust_refcol_width(const struct ref *ref)
+static int refcol_width(const struct ref *ref, int compact_format)
{
int max, rlen, llen, len;
/* uptodate lines are only shown on high verbosity level */
if (verbosity <= 0 && oideq(&ref->peer_ref->old_oid, &ref->old_oid))
- return;
+ return 0;
max = term_columns();
rlen = utf8_strwidth(prettify_refname(ref->name));
@@ -778,48 +784,78 @@ static void adjust_refcol_width(const struct ref *ref)
}
len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;
if (len >= max)
- return;
+ return 0;
- /*
- * Not precise calculation for compact mode because '*' can
- * appear on the left hand side of '->' and shrink the column
- * back.
- */
- if (refcol_width < rlen)
- refcol_width = rlen;
+ return rlen;
}
-static void prepare_format_display(struct ref *ref_map)
+static void display_state_init(struct display_state *display_state, struct ref *ref_map,
+ const char *raw_url)
{
struct ref *rm;
const char *format = "full";
+ int i;
+
+ memset(display_state, 0, sizeof(*display_state));
+
+ strbuf_init(&display_state->buf, 0);
+
+ if (raw_url)
+ display_state->url = transport_anonymize_url(raw_url);
+ else
+ display_state->url = xstrdup("foreign");
+
+ display_state->url_len = strlen(display_state->url);
+ for (i = display_state->url_len - 1; display_state->url[i] == '/' && 0 <= i; i--)
+ ;
+ display_state->url_len = i + 1;
+ if (4 < i && !strncmp(".git", display_state->url + i - 3, 4))
+ display_state->url_len = i - 3;
if (verbosity < 0)
return;
git_config_get_string_tmp("fetch.output", &format);
if (!strcasecmp(format, "full"))
- compact_format = 0;
+ display_state->compact_format = 0;
else if (!strcasecmp(format, "compact"))
- compact_format = 1;
+ display_state->compact_format = 1;
else
die(_("invalid value for '%s': '%s'"),
"fetch.output", format);
+ display_state->refcol_width = 10;
for (rm = ref_map; rm; rm = rm->next) {
+ int width;
+
if (rm->status == REF_STATUS_REJECT_SHALLOW ||
!rm->peer_ref ||
!strcmp(rm->name, "HEAD"))
continue;
- adjust_refcol_width(rm);
+ width = refcol_width(rm, display_state->compact_format);
+
+ /*
+ * Not precise calculation for compact mode because '*' can
+ * appear on the left hand side of '->' and shrink the column
+ * back.
+ */
+ if (display_state->refcol_width < width)
+ display_state->refcol_width = width;
}
}
-static void print_remote_to_local(struct strbuf *display,
+static void display_state_release(struct display_state *display_state)
+{
+ strbuf_release(&display_state->buf);
+ free(display_state->url);
+}
+
+static void print_remote_to_local(struct display_state *display_state,
const char *remote, const char *local)
{
- strbuf_addf(display, "%-*s -> %s", refcol_width, remote, local);
+ strbuf_addf(&display_state->buf, "%-*s -> %s",
+ display_state->refcol_width, remote, local);
}
static int find_and_replace(struct strbuf *haystack,
@@ -849,14 +885,14 @@ static int find_and_replace(struct strbuf *haystack,
return 1;
}
-static void print_compact(struct strbuf *display,
+static void print_compact(struct display_state *display_state,
const char *remote, const char *local)
{
struct strbuf r = STRBUF_INIT;
struct strbuf l = STRBUF_INIT;
if (!strcmp(remote, local)) {
- strbuf_addf(display, "%-*s -> *", refcol_width, remote);
+ strbuf_addf(&display_state->buf, "%-*s -> *", display_state->refcol_width, remote);
return;
}
@@ -865,40 +901,51 @@ static void print_compact(struct strbuf *display,
if (!find_and_replace(&r, local, "*"))
find_and_replace(&l, remote, "*");
- print_remote_to_local(display, r.buf, l.buf);
+ print_remote_to_local(display_state, r.buf, l.buf);
strbuf_release(&r);
strbuf_release(&l);
}
-static void format_display(struct strbuf *display, char code,
- const char *summary, const char *error,
- const char *remote, const char *local,
- int summary_width)
+static void display_ref_update(struct display_state *display_state, char code,
+ const char *summary, const char *error,
+ const char *remote, const char *local,
+ int summary_width)
{
int width;
if (verbosity < 0)
return;
+ strbuf_reset(&display_state->buf);
+
+ if (!display_state->shown_url) {
+ strbuf_addf(&display_state->buf, _("From %.*s\n"),
+ display_state->url_len, display_state->url);
+ display_state->shown_url = 1;
+ }
+
width = (summary_width + strlen(summary) - gettext_width(summary));
- strbuf_addf(display, "%c %-*s ", code, width, summary);
- if (!compact_format)
- print_remote_to_local(display, remote, local);
+ strbuf_addf(&display_state->buf, " %c %-*s ", code, width, summary);
+ if (!display_state->compact_format)
+ print_remote_to_local(display_state, remote, prettify_refname(local));
else
- print_compact(display, remote, local);
+ print_compact(display_state, remote, prettify_refname(local));
if (error)
- strbuf_addf(display, " (%s)", error);
+ strbuf_addf(&display_state->buf, " (%s)", error);
+ strbuf_addch(&display_state->buf, '\n');
+
+ fputs(display_state->buf.buf, stderr);
}
static int update_local_ref(struct ref *ref,
struct ref_transaction *transaction,
+ struct display_state *display_state,
const char *remote, const struct ref *remote_ref,
- struct strbuf *display, int summary_width)
+ int summary_width)
{
struct commit *current = NULL, *updated;
- const char *pretty_ref = prettify_refname(ref->name);
int fast_forward = 0;
if (!repo_has_object_file(the_repository, &ref->new_oid))
@@ -906,8 +953,8 @@ static int update_local_ref(struct ref *ref,
if (oideq(&ref->old_oid, &ref->new_oid)) {
if (verbosity > 0)
- format_display(display, '=', _("[up to date]"), NULL,
- remote, pretty_ref, summary_width);
+ display_ref_update(display_state, '=', _("[up to date]"), NULL,
+ remote, ref->name, summary_width);
return 0;
}
@@ -918,9 +965,9 @@ static int update_local_ref(struct ref *ref,
* If this is the head, and it's not okay to update
* the head, and the old value of the head isn't empty...
*/
- format_display(display, '!', _("[rejected]"),
- _("can't fetch into checked-out branch"),
- remote, pretty_ref, summary_width);
+ display_ref_update(display_state, '!', _("[rejected]"),
+ _("can't fetch into checked-out branch"),
+ remote, ref->name, summary_width);
return 1;
}
@@ -929,13 +976,14 @@ static int update_local_ref(struct ref *ref,
if (force || ref->force) {
int r;
r = s_update_ref("updating tag", ref, transaction, 0);
- format_display(display, r ? '!' : 't', _("[tag update]"),
- r ? _("unable to update local ref") : NULL,
- remote, pretty_ref, summary_width);
+ display_ref_update(display_state, r ? '!' : 't', _("[tag update]"),
+ r ? _("unable to update local ref") : NULL,
+ remote, ref->name, summary_width);
return r;
} else {
- format_display(display, '!', _("[rejected]"), _("would clobber existing tag"),
- remote, pretty_ref, summary_width);
+ display_ref_update(display_state, '!', _("[rejected]"),
+ _("would clobber existing tag"),
+ remote, ref->name, summary_width);
return 1;
}
}
@@ -966,9 +1014,9 @@ static int update_local_ref(struct ref *ref,
}
r = s_update_ref(msg, ref, transaction, 0);
- format_display(display, r ? '!' : '*', what,
- r ? _("unable to update local ref") : NULL,
- remote, pretty_ref, summary_width);
+ display_ref_update(display_state, r ? '!' : '*', what,
+ r ? _("unable to update local ref") : NULL,
+ remote, ref->name, summary_width);
return r;
}
@@ -989,9 +1037,9 @@ static int update_local_ref(struct ref *ref,
strbuf_addstr(&quickref, "..");
strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
r = s_update_ref("fast-forward", ref, transaction, 1);
- format_display(display, r ? '!' : ' ', quickref.buf,
- r ? _("unable to update local ref") : NULL,
- remote, pretty_ref, summary_width);
+ display_ref_update(display_state, r ? '!' : ' ', quickref.buf,
+ r ? _("unable to update local ref") : NULL,
+ remote, ref->name, summary_width);
strbuf_release(&quickref);
return r;
} else if (force || ref->force) {
@@ -1001,14 +1049,14 @@ static int update_local_ref(struct ref *ref,
strbuf_addstr(&quickref, "...");
strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);
r = s_update_ref("forced-update", ref, transaction, 1);
- format_display(display, r ? '!' : '+', quickref.buf,
- r ? _("unable to update local ref") : _("forced update"),
- remote, pretty_ref, summary_width);
+ display_ref_update(display_state, r ? '!' : '+', quickref.buf,
+ r ? _("unable to update local ref") : _("forced update"),
+ remote, ref->name, summary_width);
strbuf_release(&quickref);
return r;
} else {
- format_display(display, '!', _("[rejected]"), _("non-fast-forward"),
- remote, pretty_ref, summary_width);
+ display_ref_update(display_state, '!', _("[rejected]"), _("non-fast-forward"),
+ remote, ref->name, summary_width);
return 1;
}
}
@@ -1118,40 +1166,34 @@ N_("it took %.2f seconds to check forced updates; you can use\n"
"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates false'\n"
"to avoid this check\n");
-static int store_updated_refs(const char *raw_url, const char *remote_name,
+static int store_updated_refs(struct display_state *display_state,
+ const char *remote_name,
int connectivity_checked,
struct ref_transaction *transaction, struct ref *ref_map,
struct fetch_head *fetch_head)
{
- int url_len, i, rc = 0;
+ int rc = 0;
struct strbuf note = STRBUF_INIT;
const char *what, *kind;
struct ref *rm;
- char *url;
int want_status;
int summary_width = 0;
if (verbosity >= 0)
summary_width = transport_summary_width(ref_map);
- if (raw_url)
- url = transport_anonymize_url(raw_url);
- else
- url = xstrdup("foreign");
-
if (!connectivity_checked) {
struct check_connected_options opt = CHECK_CONNECTED_INIT;
opt.exclude_hidden_refs_section = "fetch";
rm = ref_map;
if (check_connected(iterate_ref_map, &rm, &opt)) {
- rc = error(_("%s did not send all necessary objects\n"), url);
+ rc = error(_("%s did not send all necessary objects\n"),
+ display_state->url);
goto abort;
}
}
- prepare_format_display(ref_map);
-
/*
* We do a pass for each fetch_head_status type in their enum order, so
* merged entries are written before not-for-merge. That lets readers
@@ -1231,13 +1273,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
what = rm->name;
}
- url_len = strlen(url);
- for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
- ;
- url_len = i + 1;
- if (4 < i && !strncmp(".git", url + i - 3, 4))
- url_len = i - 3;
-
strbuf_reset(&note);
if (*what) {
if (*kind)
@@ -1247,12 +1282,12 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
append_fetch_head(fetch_head, &rm->old_oid,
rm->fetch_head_status,
- note.buf, url, url_len);
+ note.buf, display_state->url,
+ display_state->url_len);
- strbuf_reset(&note);
if (ref) {
- rc |= update_local_ref(ref, transaction, what,
- rm, &note, summary_width);
+ rc |= update_local_ref(ref, transaction, display_state, what,
+ rm, summary_width);
free(ref);
} else if (write_fetch_head || dry_run) {
/*
@@ -1260,18 +1295,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
* would be written to FETCH_HEAD, if --dry-run
* is set).
*/
- format_display(&note, '*',
- *kind ? kind : "branch", NULL,
- *what ? what : "HEAD",
- "FETCH_HEAD", summary_width);
- }
- if (note.len) {
- if (!shown_url) {
- fprintf(stderr, _("From %.*s\n"),
- url_len, url);
- shown_url = 1;
- }
- fprintf(stderr, " %s\n", note.buf);
+ display_ref_update(display_state, '*',
+ *kind ? kind : "branch", NULL,
+ *what ? what : "HEAD",
+ "FETCH_HEAD", summary_width);
}
}
}
@@ -1292,7 +1319,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
abort:
strbuf_release(&note);
- free(url);
return rc;
}
@@ -1340,7 +1366,8 @@ static int check_exist_and_connected(struct ref *ref_map)
return check_connected(iterate_ref_map, &rm, &opt);
}
-static int fetch_and_consume_refs(struct transport *transport,
+static int fetch_and_consume_refs(struct display_state *display_state,
+ struct transport *transport,
struct ref_transaction *transaction,
struct ref *ref_map,
struct fetch_head *fetch_head)
@@ -1364,7 +1391,7 @@ static int fetch_and_consume_refs(struct transport *transport,
}
trace2_region_enter("fetch", "consume_refs", the_repository);
- ret = store_updated_refs(transport->url, transport->remote->name,
+ ret = store_updated_refs(display_state, transport->remote->name,
connectivity_checked, transaction, ref_map,
fetch_head);
trace2_region_leave("fetch", "consume_refs", the_repository);
@@ -1374,32 +1401,18 @@ out:
return ret;
}
-static int prune_refs(struct refspec *rs,
+static int prune_refs(struct display_state *display_state,
+ struct refspec *rs,
struct ref_transaction *transaction,
- struct ref *ref_map,
- const char *raw_url)
+ struct ref *ref_map)
{
- int url_len, i, result = 0;
+ int result = 0;
struct ref *ref, *stale_refs = get_stale_heads(rs, ref_map);
struct strbuf err = STRBUF_INIT;
- char *url;
const char *dangling_msg = dry_run
? _(" (%s will become dangling)")
: _(" (%s has become dangling)");
- if (raw_url)
- url = transport_anonymize_url(raw_url);
- else
- url = xstrdup("foreign");
-
- url_len = strlen(url);
- for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
- ;
-
- url_len = i + 1;
- if (4 < i && !strncmp(".git", url + i - 3, 4))
- url_len = i - 3;
-
if (!dry_run) {
if (transaction) {
for (ref = stale_refs; ref; ref = ref->next) {
@@ -1423,23 +1436,15 @@ static int prune_refs(struct refspec *rs,
int summary_width = transport_summary_width(stale_refs);
for (ref = stale_refs; ref; ref = ref->next) {
- struct strbuf sb = STRBUF_INIT;
- if (!shown_url) {
- fprintf(stderr, _("From %.*s\n"), url_len, url);
- shown_url = 1;
- }
- format_display(&sb, '-', _("[deleted]"), NULL,
- _("(none)"), prettify_refname(ref->name),
- summary_width);
- fprintf(stderr, " %s\n",sb.buf);
- strbuf_release(&sb);
+ display_ref_update(display_state, '-', _("[deleted]"), NULL,
+ _("(none)"), ref->name,
+ summary_width);
warn_dangling_symref(stderr, dangling_msg, ref->name);
}
}
cleanup:
strbuf_release(&err);
- free(url);
free_refs(stale_refs);
return result;
}
@@ -1554,7 +1559,8 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
return transport;
}
-static int backfill_tags(struct transport *transport,
+static int backfill_tags(struct display_state *display_state,
+ struct transport *transport,
struct ref_transaction *transaction,
struct ref *ref_map,
struct fetch_head *fetch_head)
@@ -1578,7 +1584,7 @@ static int backfill_tags(struct transport *transport,
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
- retcode = fetch_and_consume_refs(transport, transaction, ref_map, fetch_head);
+ retcode = fetch_and_consume_refs(display_state, transport, transaction, ref_map, fetch_head);
if (gsecondary) {
transport_disconnect(gsecondary);
@@ -1593,6 +1599,7 @@ static int do_fetch(struct transport *transport,
{
struct ref_transaction *transaction = NULL;
struct ref *ref_map = NULL;
+ struct display_state display_state = { 0 };
int autotags = (transport->remote->fetch_tags == 1);
int retcode = 0;
const struct ref *remote_refs;
@@ -1674,6 +1681,8 @@ static int do_fetch(struct transport *transport,
if (retcode)
goto cleanup;
+ display_state_init(&display_state, ref_map, transport->url);
+
if (atomic_fetch) {
transaction = ref_transaction_begin(&err);
if (!transaction) {
@@ -1691,17 +1700,16 @@ static int do_fetch(struct transport *transport,
* don't care whether --tags was specified.
*/
if (rs->nr) {
- retcode = prune_refs(rs, transaction, ref_map, transport->url);
+ retcode = prune_refs(&display_state, rs, transaction, ref_map);
} else {
- retcode = prune_refs(&transport->remote->fetch,
- transaction, ref_map,
- transport->url);
+ retcode = prune_refs(&display_state, &transport->remote->fetch,
+ transaction, ref_map);
}
if (retcode != 0)
retcode = 1;
}
- if (fetch_and_consume_refs(transport, transaction, ref_map, &fetch_head)) {
+ if (fetch_and_consume_refs(&display_state, transport, transaction, ref_map, &fetch_head)) {
retcode = 1;
goto cleanup;
}
@@ -1723,7 +1731,7 @@ static int do_fetch(struct transport *transport,
* when `--atomic` is passed: in that case we'll abort
* the transaction and don't commit anything.
*/
- if (backfill_tags(transport, transaction, tags_ref_map,
+ if (backfill_tags(&display_state, transport, transaction, tags_ref_map,
&fetch_head))
retcode = 1;
}
@@ -1806,6 +1814,7 @@ cleanup:
error("%s", err.buf);
}
+ display_state_release(&display_state);
close_fetch_head(&fetch_head);
strbuf_release(&err);
free_refs(ref_map);
@@ -1964,7 +1973,12 @@ static int fetch_multiple(struct string_list *list, int max_children)
return errcode;
}
- strvec_pushl(&argv, "fetch", "--append", "--no-auto-gc",
+ /*
+ * Cancel out the fetch.bundleURI config when running subprocesses,
+ * to avoid fetching from the same bundle list multiple times.
+ */
+ strvec_pushl(&argv, "-c", "fetch.bundleURI=",
+ "fetch", "--append", "--no-auto-gc",
"--no-write-commit-graph", NULL);
add_options_to_argv(&argv);
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 7a8ff5902c..695fc8f4a5 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -6,6 +6,8 @@
#include "object.h"
#include "parse-options.h"
#include "ref-filter.h"
+#include "strvec.h"
+#include "commit-reach.h"
static char const * const for_each_ref_usage[] = {
N_("git for-each-ref [<options>] [<pattern>]"),
@@ -20,12 +22,14 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
int i;
struct ref_sorting *sorting;
struct string_list sorting_options = STRING_LIST_INIT_DUP;
- int maxcount = 0, icase = 0;
+ int maxcount = 0, icase = 0, omit_empty = 0;
struct ref_array array;
struct ref_filter filter;
struct ref_format format = REF_FORMAT_INIT;
struct strbuf output = STRBUF_INIT;
struct strbuf err = STRBUF_INIT;
+ int from_stdin = 0;
+ struct strvec vec = STRVEC_INIT;
struct option opts[] = {
OPT_BIT('s', "shell", &format.quote_style,
@@ -36,6 +40,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
N_("quote placeholders suitably for python"), QUOTE_PYTHON),
OPT_BIT(0 , "tcl", &format.quote_style,
N_("quote placeholders suitably for Tcl"), QUOTE_TCL),
+ OPT_BOOL(0, "omit-empty", &omit_empty,
+ N_("do not output a newline after empty formatted refs")),
OPT_GROUP(""),
OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
@@ -50,6 +56,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
OPT_CONTAINS(&filter.with_commit, N_("print only refs which contain the commit")),
OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")),
OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
+ OPT_BOOL(0, "stdin", &from_stdin, N_("read reference patterns from stdin")),
OPT_END(),
};
@@ -76,9 +83,27 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
filter.ignore_case = icase;
- filter.name_patterns = argv;
+ if (from_stdin) {
+ struct strbuf line = STRBUF_INIT;
+
+ if (argv[0])
+ die(_("unknown arguments supplied with --stdin"));
+
+ while (strbuf_getline(&line, stdin) != EOF)
+ strvec_push(&vec, line.buf);
+
+ strbuf_release(&line);
+
+ /* vec.v is NULL-terminated, just like 'argv'. */
+ filter.name_patterns = vec.v;
+ } else {
+ filter.name_patterns = argv;
+ }
+
filter.match_as_path = 1;
filter_refs(&array, &filter, FILTER_REFS_ALL);
+ filter_ahead_behind(the_repository, &format, &array);
+
ref_array_sort(sorting, &array);
if (!maxcount || array.nr < maxcount)
@@ -89,7 +114,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
if (format_ref_array_item(array.items[i], &format, &output, &err))
die("%s", err.buf);
fwrite(output.buf, 1, output.len, stdout);
- putchar('\n');
+ if (output.len || !omit_empty)
+ putchar('\n');
}
strbuf_release(&err);
@@ -98,5 +124,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
free_commit_list(filter.with_commit);
free_commit_list(filter.no_commit);
ref_sorting_release(sorting);
+ strvec_clear(&vec);
return 0;
}
diff --git a/builtin/for-each-repo.c b/builtin/for-each-repo.c
index 27425c2fc9..598ca16c46 100644
--- a/builtin/for-each-repo.c
+++ b/builtin/for-each-repo.c
@@ -33,6 +33,7 @@ int cmd_for_each_repo(int argc, const char **argv, const char *prefix)
static const char *config_key = NULL;
int i, result = 0;
const struct string_list *values;
+ int err;
const struct option options[] = {
OPT_STRING(0, "config", &config_key, N_("config"),
@@ -46,14 +47,11 @@ int cmd_for_each_repo(int argc, const char **argv, const char *prefix)
if (!config_key)
die(_("missing --config=<config>"));
- values = repo_config_get_value_multi(the_repository,
- config_key);
-
- /*
- * Do nothing on an empty list, which is equivalent to the case
- * where the config variable does not exist at all.
- */
- if (!values)
+ err = repo_config_get_string_multi(the_repository, config_key, &values);
+ if (err < 0)
+ usage_msg_optf(_("got bad config --config=%s"),
+ for_each_repo_usage, options, config_key);
+ else if (err)
return 0;
for (i = 0; !result && i < values->nr; i++)
diff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c
index df876b41d6..f6dd9a784c 100644
--- a/builtin/fsmonitor--daemon.c
+++ b/builtin/fsmonitor--daemon.c
@@ -1579,7 +1579,7 @@ int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
}
#else
-int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
+int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix UNUSED)
{
struct option options[] = {
OPT_END()
diff --git a/builtin/gc.c b/builtin/gc.c
index b95154fba1..f8569bfacc 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -1501,7 +1501,6 @@ static int maintenance_register(int argc, const char **argv, const char *prefix)
};
int found = 0;
const char *key = "maintenance.repo";
- char *config_value;
char *maintpath = get_maintpath();
struct string_list_item *item;
const struct string_list *list;
@@ -1516,13 +1515,10 @@ static int maintenance_register(int argc, const char **argv, const char *prefix)
git_config_set("maintenance.auto", "false");
/* Set maintenance strategy, if unset */
- if (!git_config_get_string("maintenance.strategy", &config_value))
- free(config_value);
- else
+ if (git_config_get("maintenance.strategy"))
git_config_set("maintenance.strategy", "incremental");
- list = git_config_get_value_multi(key);
- if (list) {
+ if (!git_config_get_string_multi(key, &list)) {
for_each_string_list_item(item, list) {
if (!strcmp(maintpath, item->string)) {
found = 1;
@@ -1588,11 +1584,10 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi
if (config_file) {
git_configset_init(&cs);
git_configset_add_file(&cs, config_file);
- list = git_configset_get_value_multi(&cs, key);
- } else {
- list = git_config_get_value_multi(key);
}
- if (list) {
+ if (!(config_file
+ ? git_configset_get_string_multi(&cs, key, &list)
+ : git_config_get_string_multi(key, &list))) {
for_each_string_list_item(item, list) {
if (!strcmp(maintpath, item->string)) {
found = 1;
diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c
index 6745796998..564cfcac4f 100644
--- a/builtin/get-tar-commit-id.c
+++ b/builtin/get-tar-commit-id.c
@@ -15,7 +15,7 @@ static const char builtin_get_tar_commit_id_usage[] =
#define RECORDSIZE (512)
#define HEADERSIZE (2 * RECORDSIZE)
-int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
+int cmd_get_tar_commit_id(int argc, const char **argv UNUSED, const char *prefix)
{
char buffer[HEADERSIZE];
struct ustar_header *header = (struct ustar_header *)buffer;
@@ -25,6 +25,8 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
long len;
char *end;
+ BUG_ON_NON_EMPTY_PREFIX(prefix);
+
if (argc != 1)
usage(builtin_get_tar_commit_id_usage);
@@ -33,7 +35,7 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
die_errno("git get-tar-commit-id: read error");
if (n != HEADERSIZE)
die_errno("git get-tar-commit-id: EOF before reading tar header");
- if (header->typeflag[0] != 'g')
+ if (header->typeflag[0] != TYPEFLAG_GLOBAL_HEADER)
return 1;
len = strtol(content, &end, 10);
diff --git a/builtin/log.c b/builtin/log.c
index 95de51921f..4f162ff4d0 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -66,6 +66,7 @@ static int stdout_mboxrd;
static const char *fmt_patch_subject_prefix = "PATCH";
static int fmt_patch_name_max = FORMAT_PATCH_NAME_MAX_DEFAULT;
static const char *fmt_pretty;
+static int format_no_prefix;
static const char * const builtin_log_usage[] = {
N_("git log [<options>] [<revision-range>] [[--] <path>...]"),
@@ -192,10 +193,10 @@ static void set_default_decoration_filter(struct decoration_filter *decoration_f
int i;
char *value = NULL;
struct string_list *include = decoration_filter->include_ref_pattern;
- const struct string_list *config_exclude =
- git_config_get_value_multi("log.excludeDecoration");
+ const struct string_list *config_exclude;
- if (config_exclude) {
+ if (!git_config_get_string_multi("log.excludeDecoration",
+ &config_exclude)) {
struct string_list_item *item;
for_each_string_list_item(item, config_exclude)
string_list_append(decoration_filter->exclude_ref_config_pattern,
@@ -1092,6 +1093,19 @@ static int git_format_config(const char *var, const char *value, void *cb)
stdout_mboxrd = git_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "format.noprefix")) {
+ format_no_prefix = 1;
+ return 0;
+ }
+
+ /*
+ * ignore some porcelain config which would otherwise be parsed by
+ * git_diff_ui_config(), via git_log_config(); we can't just avoid
+ * diff_ui_config completely, because we do care about some ui options
+ * like color.
+ */
+ if (!strcmp(var, "diff.noprefix"))
+ return 0;
return git_log_config(var, value, cb);
}
@@ -2007,6 +2021,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
s_r_opt.def = "HEAD";
s_r_opt.revarg_opt = REVARG_COMMITTISH;
+ if (format_no_prefix)
+ diff_set_noprefix(&rev.diffopt);
+
if (default_attach) {
rev.mime_boundary = default_attach;
rev.no_inline = 1;
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index ce083cb8ef..625f48f0d6 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -93,12 +93,15 @@ static void write_name(const char *name)
static void write_name_to_buf(struct strbuf *sb, const char *name)
{
- const char *rel = relative_path(name, prefix_len ? prefix : NULL, sb);
+ struct strbuf buf = STRBUF_INIT;
+ const char *rel = relative_path(name, prefix_len ? prefix : NULL, &buf);
if (line_terminator)
quote_c_style(rel, sb, NULL, 0);
else
strbuf_addstr(sb, rel);
+
+ strbuf_release(&buf);
}
static const char *get_tag(const struct cache_entry *ce, const char *tag)
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index 11d9424804..3c74c4a104 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -6,6 +6,7 @@
#include "ref-filter.h"
#include "remote.h"
#include "refs.h"
+#include "parse-options.h"
static const char * const ls_remote_usage[] = {
N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index b08069ce60..0b6193a091 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -278,6 +278,8 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix)
const char **argp;
static const char *stdin_only[] = { "-", NULL };
+ BUG_ON_NON_EMPTY_PREFIX(prefix);
+
for (argp = argv+1; *argp; argp++) {
const char *arg = *argp;
diff --git a/builtin/merge-index.c b/builtin/merge-index.c
index c875f5d37e..b747b4ed98 100644
--- a/builtin/merge-index.c
+++ b/builtin/merge-index.c
@@ -71,7 +71,7 @@ static void merge_all(void)
}
}
-int cmd_merge_index(int argc, const char **argv, const char *prefix)
+int cmd_merge_index(int argc, const char **argv, const char *prefix UNUSED)
{
int i, force_file = 0;
diff --git a/builtin/merge-ours.c b/builtin/merge-ours.c
index 284eb48609..c2e519301e 100644
--- a/builtin/merge-ours.c
+++ b/builtin/merge-ours.c
@@ -14,7 +14,7 @@
static const char builtin_merge_ours_usage[] =
"git merge-ours <base>... -- HEAD <remote>...";
-int cmd_merge_ours(int argc, const char **argv, const char *prefix)
+int cmd_merge_ours(int argc, const char **argv, const char *prefix UNUSED)
{
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(builtin_merge_ours_usage);
diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c
index fa1035405c..708a8ffabe 100644
--- a/builtin/merge-recursive.c
+++ b/builtin/merge-recursive.c
@@ -23,7 +23,7 @@ static char *better_branch_name(const char *branch)
return xstrdup(name ? name : branch);
}
-int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
+int cmd_merge_recursive(int argc, const char **argv, const char *prefix UNUSED)
{
const struct object_id *bases[21];
unsigned bases_count = 0;
diff --git a/builtin/mktag.c b/builtin/mktag.c
index f33d267fcb..44fa56eff3 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -84,7 +84,7 @@ int cmd_mktag(int argc, const char **argv, const char *prefix)
int tagged_type;
struct object_id result;
- argc = parse_options(argc, argv, NULL,
+ argc = parse_options(argc, argv, prefix,
builtin_mktag_options,
builtin_mktag_usage, 0);
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 68c914a8e1..9297a077f8 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -42,6 +42,7 @@
#include "shallow.h"
#include "promisor-remote.h"
#include "pack-mtimes.h"
+#include "parse-options.h"
#include "wrapper.h"
/*
diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c
index 3451971b56..43e9d12dfd 100644
--- a/builtin/pack-redundant.c
+++ b/builtin/pack-redundant.c
@@ -559,7 +559,7 @@ static void load_all(void)
}
}
-int cmd_pack_redundant(int argc, const char **argv, const char *prefix)
+int cmd_pack_redundant(int argc, const char **argv, const char *prefix UNUSED)
{
int i;
int i_still_use_this = 0;
@@ -605,6 +605,7 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix)
"option, '--i-still-use-this', on the command line\n"
"and let us know you still use it by sending an e-mail\n"
"to <git@vger.kernel.org>. Thanks.\n"), stderr);
+ die(_("refusing to run without --i-still-use-this"));
}
if (load_all_packs)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 34fea4d822..ace1d5e8d1 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -124,13 +124,15 @@ struct rebase_options {
struct string_list exec;
int allow_empty_message;
int rebase_merges, rebase_cousins;
- char *strategy, *strategy_opts;
+ char *strategy;
+ struct string_list strategy_opts;
struct strbuf git_format_patch_opt;
int reschedule_failed_exec;
int reapply_cherry_picks;
int fork_point;
int update_refs;
int config_autosquash;
+ int config_rebase_merges;
int config_update_refs;
};
@@ -148,8 +150,11 @@ struct rebase_options {
.allow_empty_message = 1, \
.autosquash = -1, \
.config_autosquash = -1, \
+ .rebase_merges = -1, \
+ .config_rebase_merges = -1, \
.update_refs = -1, \
.config_update_refs = -1, \
+ .strategy_opts = STRING_LIST_INIT_NODUP,\
}
static struct replay_opts get_replay_opts(const struct rebase_options *opts)
@@ -183,8 +188,8 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
replay.default_strategy = NULL;
}
- if (opts->strategy_opts)
- parse_strategy_opts(&replay, opts->strategy_opts);
+ for (size_t i = 0; i < opts->strategy_opts.nr; i++)
+ strvec_push(&replay.xopts, opts->strategy_opts.items[i].string);
if (opts->squash_onto) {
oidcpy(&replay.squash_onto, opts->squash_onto);
@@ -492,24 +497,6 @@ static int read_basic_state(struct rebase_options *opts)
opts->gpg_sign_opt = xstrdup(buf.buf);
}
- if (file_exists(state_dir_path("strategy", opts))) {
- strbuf_reset(&buf);
- if (!read_oneliner(&buf, state_dir_path("strategy", opts),
- READ_ONELINER_WARN_MISSING))
- return -1;
- free(opts->strategy);
- opts->strategy = xstrdup(buf.buf);
- }
-
- if (file_exists(state_dir_path("strategy_opts", opts))) {
- strbuf_reset(&buf);
- if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts),
- READ_ONELINER_WARN_MISSING))
- return -1;
- free(opts->strategy_opts);
- opts->strategy_opts = xstrdup(buf.buf);
- }
-
strbuf_release(&buf);
return 0;
@@ -527,12 +514,6 @@ static int rebase_write_basic_state(struct rebase_options *opts)
write_file(state_dir_path("quiet", opts), "%s", "");
if (opts->flags & REBASE_VERBOSE)
write_file(state_dir_path("verbose", opts), "%s", "");
- if (opts->strategy)
- write_file(state_dir_path("strategy", opts), "%s",
- opts->strategy);
- if (opts->strategy_opts)
- write_file(state_dir_path("strategy_opts", opts), "%s",
- opts->strategy_opts);
if (opts->allow_rerere_autoupdate > 0)
write_file(state_dir_path("allow_rerere_autoupdate", opts),
"-%s-rerere-autoupdate",
@@ -670,7 +651,7 @@ static int run_am(struct rebase_options *opts)
format_patch.git_cmd = 1;
strvec_pushl(&format_patch.args, "format-patch", "-k", "--stdout",
"--full-index", "--cherry-pick", "--right-only",
- "--src-prefix=a/", "--dst-prefix=b/", "--no-renames",
+ "--default-prefix", "--no-renames",
"--no-cover-letter", "--pretty=mboxrd", "--topo-order",
"--no-base", NULL);
if (opts->git_format_patch_opt.len)
@@ -781,6 +762,16 @@ static int run_specific_rebase(struct rebase_options *opts)
return status ? -1 : 0;
}
+static void parse_rebase_merges_value(struct rebase_options *options, const char *value)
+{
+ if (!strcmp("no-rebase-cousins", value))
+ options->rebase_cousins = 0;
+ else if (!strcmp("rebase-cousins", value))
+ options->rebase_cousins = 1;
+ else
+ die(_("Unknown rebase-merges mode: %s"), value);
+}
+
static int rebase_config(const char *var, const char *value, void *data)
{
struct rebase_options *opts = data;
@@ -810,6 +801,17 @@ static int rebase_config(const char *var, const char *value, void *data)
return 0;
}
+ if (!strcmp(var, "rebase.rebasemerges")) {
+ opts->config_rebase_merges = git_parse_maybe_bool(value);
+ if (opts->config_rebase_merges < 0) {
+ opts->config_rebase_merges = 1;
+ parse_rebase_merges_value(opts, value);
+ } else {
+ opts->rebase_cousins = 0;
+ }
+ return 0;
+ }
+
if (!strcmp(var, "rebase.updaterefs")) {
opts->config_update_refs = git_config_bool(var, value);
return 0;
@@ -991,6 +993,28 @@ static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
return 0;
}
+static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)
+{
+ struct rebase_options *options = opt->value;
+
+ options->rebase_merges = !unset;
+ options->rebase_cousins = 0;
+
+ if (arg) {
+ if (!*arg) {
+ warning(_("--rebase-merges with an empty string "
+ "argument is deprecated and will stop "
+ "working in a future version of Git. Use "
+ "--rebase-merges without an argument "
+ "instead, which does the same thing."));
+ return 0;
+ }
+ parse_rebase_merges_value(options, arg);
+ }
+
+ return 0;
+}
+
static void NORETURN error_on_missing_default_upstream(void)
{
struct branch *current_branch = branch_get(NULL);
@@ -1046,8 +1070,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
struct object_id branch_base;
int ignore_whitespace = 0;
const char *gpg_sign = NULL;
- const char *rebase_merges = NULL;
- struct string_list strategy_options = STRING_LIST_INIT_NODUP;
struct object_id squash_onto;
char *squash_onto_name = NULL;
char *keep_base_onto_name = NULL;
@@ -1148,15 +1170,14 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
&options.allow_empty_message,
N_("allow rebasing commits with empty messages"),
PARSE_OPT_HIDDEN),
- {OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
- N_("mode"),
+ OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"),
N_("try to rebase merges instead of skipping them"),
- PARSE_OPT_OPTARG, NULL, (intptr_t)""},
+ PARSE_OPT_OPTARG, parse_opt_rebase_merges),
OPT_BOOL(0, "fork-point", &options.fork_point,
N_("use 'merge-base --fork-point' to refine upstream")),
OPT_STRING('s', "strategy", &options.strategy,
N_("strategy"), N_("use the given merge strategy")),
- OPT_STRING_LIST('X', "strategy-option", &strategy_options,
+ OPT_STRING_LIST('X', "strategy-option", &options.strategy_opts,
N_("option"),
N_("pass the argument through to the merge "
"strategy")),
@@ -1447,17 +1468,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (options.exec.nr)
imply_merge(&options, "--exec");
- if (rebase_merges) {
- if (!*rebase_merges)
- ; /* default mode; do nothing */
- else if (!strcmp("rebase-cousins", rebase_merges))
- options.rebase_cousins = 1;
- else if (strcmp("no-rebase-cousins", rebase_merges))
- die(_("Unknown mode: %s"), rebase_merges);
- options.rebase_merges = 1;
- imply_merge(&options, "--rebase-merges");
- }
-
if (options.type == REBASE_APPLY) {
if (ignore_whitespace)
strvec_push(&options.git_am_opts,
@@ -1470,23 +1480,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
} else {
/* REBASE_MERGE */
if (ignore_whitespace) {
- string_list_append(&strategy_options,
+ string_list_append(&options.strategy_opts,
"ignore-space-change");
}
}
- if (strategy_options.nr) {
- int i;
-
- if (!options.strategy)
- options.strategy = "ort";
-
- strbuf_reset(&buf);
- for (i = 0; i < strategy_options.nr; i++)
- strbuf_addf(&buf, " --%s",
- strategy_options.items[i].string);
- options.strategy_opts = xstrdup(buf.buf);
- }
+ if (options.strategy_opts.nr && !options.strategy)
+ options.strategy = "ort";
if (options.strategy) {
options.strategy = xstrdup(options.strategy);
@@ -1525,6 +1525,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
"cannot be used together"));
else if (options.autosquash == -1 && options.config_autosquash == 1)
die(_("apply options are incompatible with rebase.autoSquash. Consider adding --no-autosquash"));
+ else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
+ die(_("apply options are incompatible with rebase.rebaseMerges. Consider adding --no-rebase-merges"));
else if (options.update_refs == -1 && options.config_update_refs == 1)
die(_("apply options are incompatible with rebase.updateRefs. Consider adding --no-update-refs"));
else
@@ -1537,6 +1539,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.update_refs = (options.update_refs >= 0) ? options.update_refs :
((options.config_update_refs >= 0) ? options.config_update_refs : 0);
+ if (options.rebase_merges == 1)
+ imply_merge(&options, "--rebase-merges");
+ options.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :
+ ((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);
+
if (options.autosquash == 1)
imply_merge(&options, "--autosquash");
options.autosquash = (options.autosquash >= 0) ? options.autosquash :
@@ -1861,10 +1868,9 @@ cleanup:
free(options.gpg_sign_opt);
string_list_clear(&options.exec, 0);
free(options.strategy);
- free(options.strategy_opts);
+ string_list_clear(&options.strategy_opts, 0);
strbuf_release(&options.git_format_patch_opt);
free(squash_onto_name);
free(keep_base_onto_name);
- string_list_clear(&strategy_options, 0);
return !!ret;
}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 7d921170f1..0dedcd7204 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -37,6 +37,7 @@
#include "trace2.h"
#include "worktree.h"
#include "shallow.h"
+#include "parse-options.h"
#include "wrapper.h"
static const char * const receive_pack_usage[] = {
@@ -2190,7 +2191,7 @@ static const char *parse_pack_header(struct pack_header *hdr)
}
}
-static const char *pack_lockfile;
+static struct tempfile *pack_lockfile;
static void push_header_arg(struct strvec *args, struct pack_header *hdr)
{
@@ -2257,6 +2258,7 @@ static const char *unpack(int err_fd, struct shallow_info *si)
return "unpack-objects abnormal exit";
} else {
char hostname[HOST_NAME_MAX + 1];
+ char *lockfile;
strvec_pushl(&child.args, "index-pack", "--stdin", NULL);
push_header_arg(&child.args, &hdr);
@@ -2286,8 +2288,14 @@ static const char *unpack(int err_fd, struct shallow_info *si)
status = start_command(&child);
if (status)
return "index-pack fork failed";
- pack_lockfile = index_pack_lockfile(child.out, NULL);
+
+ lockfile = index_pack_lockfile(child.out, NULL);
+ if (lockfile) {
+ pack_lockfile = register_tempfile(lockfile);
+ free(lockfile);
+ }
close(child.out);
+
status = finish_command(&child);
if (status)
return "index-pack abnormal exit";
@@ -2574,8 +2582,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
use_keepalive = KEEPALIVE_ALWAYS;
execute_commands(commands, unpack_status, &si,
&push_options);
- if (pack_lockfile)
- unlink_or_warn(pack_lockfile);
+ delete_tempfile(&pack_lockfile);
sigchain_push(SIGPIPE, SIG_IGN);
if (report_status_v2)
report_v2(commands, unpack_status);
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 0879d4d224..a1fa0c855f 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -5,6 +5,7 @@
#include "reachable.h"
#include "worktree.h"
#include "reflog.h"
+#include "parse-options.h"
#define BUILTIN_REFLOG_SHOW_USAGE \
N_("git reflog [show] [<log-options>] [<ref>]")
diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c
index ee338bf440..282782eccd 100644
--- a/builtin/remote-ext.c
+++ b/builtin/remote-ext.c
@@ -197,6 +197,8 @@ static int command_loop(const char *child)
int cmd_remote_ext(int argc, const char **argv, const char *prefix)
{
+ BUG_ON_NON_EMPTY_PREFIX(prefix);
+
if (argc != 3)
usage(usage_msg);
diff --git a/builtin/remote-fd.c b/builtin/remote-fd.c
index b2a3980b1d..9020fab9c5 100644
--- a/builtin/remote-fd.c
+++ b/builtin/remote-fd.c
@@ -59,6 +59,8 @@ int cmd_remote_fd(int argc, const char **argv, const char *prefix)
int output_fd = -1;
char *end;
+ BUG_ON_NON_EMPTY_PREFIX(prefix);
+
if (argc != 3)
usage(usage_msg);
diff --git a/builtin/revert.c b/builtin/revert.c
index dd6587a99d..0240ec8593 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -45,20 +45,6 @@ static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;
}
-static int option_parse_x(const struct option *opt,
- const char *arg, int unset)
-{
- struct replay_opts **opts_ptr = opt->value;
- struct replay_opts *opts = *opts_ptr;
-
- if (unset)
- return 0;
-
- ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
- opts->xopts[opts->xopts_nr++] = xstrdup(arg);
- return 0;
-}
-
static int option_parse_m(const struct option *opt,
const char *arg, int unset)
{
@@ -95,7 +81,8 @@ static void verify_opt_compatible(const char *me, const char *base_opt, ...)
die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt);
}
-static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
+static int run_sequencer(int argc, const char **argv, const char *prefix,
+ struct replay_opts *opts)
{
const char * const * usage_str = revert_or_cherry_pick_usage(opts);
const char *me = action_name(opts);
@@ -115,8 +102,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
N_("select mainline parent"), option_parse_m),
OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),
OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")),
- OPT_CALLBACK('X', "strategy-option", &opts, N_("option"),
- N_("option for merge strategy"), option_parse_x),
+ OPT_STRVEC('X', "strategy-option", &opts->xopts, N_("option"),
+ N_("option for merge strategy")),
{ OPTION_STRING, 'S', "gpg-sign", &opts->gpg_sign, N_("key-id"),
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
OPT_END()
@@ -142,7 +129,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
options = parse_options_concat(options, cp_extra);
}
- argc = parse_options(argc, argv, NULL, options, usage_str,
+ argc = parse_options(argc, argv, prefix, options, usage_str,
PARSE_OPT_KEEP_ARGV0 |
PARSE_OPT_KEEP_UNKNOWN_OPT);
@@ -177,7 +164,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
"--signoff", opts->signoff,
"--mainline", opts->mainline,
"--strategy", opts->strategy ? 1 : 0,
- "--strategy-option", opts->xopts ? 1 : 0,
+ "--strategy-option", opts->xopts.nr ? 1 : 0,
"-x", opts->record_origin,
"--ff", opts->allow_ff,
"--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
@@ -247,7 +234,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
opts.action = REPLAY_REVERT;
sequencer_init_config(&opts);
- res = run_sequencer(argc, argv, &opts);
+ res = run_sequencer(argc, argv, prefix, &opts);
if (res < 0)
die(_("revert failed"));
replay_opts_release(&opts);
@@ -261,7 +248,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
opts.action = REPLAY_PICK;
sequencer_init_config(&opts);
- res = run_sequencer(argc, argv, &opts);
+ res = run_sequencer(argc, argv, prefix, &opts);
if (res < 0)
die(_("cherry-pick failed"));
replay_opts_release(&opts);
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 2b360fee42..4784143004 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -16,6 +16,7 @@
#include "gpg-interface.h"
#include "gettext.h"
#include "protocol.h"
+#include "parse-options.h"
#include "write-or-die.h"
static const char * const send_pack_usage[] = {
@@ -275,7 +276,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
fd[0] = 0;
fd[1] = 1;
} else {
- conn = git_connect(fd, dest, receivepack,
+ conn = git_connect(fd, dest, "git-receive-pack", receivepack,
args.verbose ? CONNECT_VERBOSE : 0);
}
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 4de37d07fd..40d420f06c 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -25,7 +25,7 @@
static const char *empty_base = "";
static char const * const builtin_sparse_checkout_usage[] = {
- N_("git sparse-checkout (init | list | set | add | reapply | disable) [<options>]"),
+ N_("git sparse-checkout (init | list | set | add | reapply | disable | check-rules) [<options>]"),
NULL
};
@@ -62,6 +62,7 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix)
char *sparse_filename;
int res;
+ setup_work_tree();
if (!core_apply_sparse_checkout)
die(_("this worktree is not sparse"));
@@ -386,13 +387,7 @@ static int set_config(enum sparse_checkout_mode mode)
return 0;
}
-static int update_modes(int *cone_mode, int *sparse_index)
-{
- int mode, record_mode;
-
- /* Determine if we need to record the mode; ensure sparse checkout on */
- record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
-
+static enum sparse_checkout_mode update_cone_mode(int *cone_mode) {
/* If not specified, use previous definition of cone mode */
if (*cone_mode == -1 && core_apply_sparse_checkout)
*cone_mode = core_sparse_checkout_cone;
@@ -400,12 +395,21 @@ static int update_modes(int *cone_mode, int *sparse_index)
/* Set cone/non-cone mode appropriately */
core_apply_sparse_checkout = 1;
if (*cone_mode == 1 || *cone_mode == -1) {
- mode = MODE_CONE_PATTERNS;
core_sparse_checkout_cone = 1;
- } else {
- mode = MODE_ALL_PATTERNS;
- core_sparse_checkout_cone = 0;
+ return MODE_CONE_PATTERNS;
}
+ core_sparse_checkout_cone = 0;
+ return MODE_ALL_PATTERNS;
+}
+
+static int update_modes(int *cone_mode, int *sparse_index)
+{
+ int mode, record_mode;
+
+ /* Determine if we need to record the mode; ensure sparse checkout on */
+ record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
+
+ mode = update_cone_mode(cone_mode);
if (record_mode && set_config(mode))
return 1;
@@ -451,6 +455,7 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix)
OPT_END(),
};
+ setup_work_tree();
repo_read_index(the_repository);
init_opts.cone_mode = -1;
@@ -548,7 +553,7 @@ static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
static void add_patterns_from_input(struct pattern_list *pl,
int argc, const char **argv,
- int use_stdin)
+ FILE *file)
{
int i;
if (core_sparse_checkout_cone) {
@@ -558,9 +563,9 @@ static void add_patterns_from_input(struct pattern_list *pl,
hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0);
pl->use_cone_patterns = 1;
- if (use_stdin) {
+ if (file) {
struct strbuf unquoted = STRBUF_INIT;
- while (!strbuf_getline(&line, stdin)) {
+ while (!strbuf_getline(&line, file)) {
if (line.buf[0] == '"') {
strbuf_reset(&unquoted);
if (unquote_c_style(&unquoted, line.buf, NULL))
@@ -582,10 +587,10 @@ static void add_patterns_from_input(struct pattern_list *pl,
}
}
} else {
- if (use_stdin) {
+ if (file) {
struct strbuf line = STRBUF_INIT;
- while (!strbuf_getline(&line, stdin)) {
+ while (!strbuf_getline(&line, file)) {
size_t len;
char *buf = strbuf_detach(&line, &len);
add_pattern(buf, empty_base, 0, pl, 0);
@@ -612,7 +617,8 @@ static void add_patterns_cone_mode(int argc, const char **argv,
struct pattern_list existing;
char *sparse_filename = get_sparse_checkout_filename();
- add_patterns_from_input(pl, argc, argv, use_stdin);
+ add_patterns_from_input(pl, argc, argv,
+ use_stdin ? stdin : NULL);
memset(&existing, 0, sizeof(existing));
existing.use_cone_patterns = core_sparse_checkout_cone;
@@ -649,7 +655,7 @@ static void add_patterns_literal(int argc, const char **argv,
pl, NULL, 0))
die(_("unable to load existing sparse-checkout patterns"));
free(sparse_filename);
- add_patterns_from_input(pl, argc, argv, use_stdin);
+ add_patterns_from_input(pl, argc, argv, use_stdin ? stdin : NULL);
}
static int modify_pattern_list(int argc, const char **argv, int use_stdin,
@@ -668,7 +674,8 @@ static int modify_pattern_list(int argc, const char **argv, int use_stdin,
break;
case REPLACE:
- add_patterns_from_input(pl, argc, argv, use_stdin);
+ add_patterns_from_input(pl, argc, argv,
+ use_stdin ? stdin : NULL);
break;
}
@@ -763,6 +770,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix)
OPT_END(),
};
+ setup_work_tree();
if (!core_apply_sparse_checkout)
die(_("no sparse-checkout to add to"));
@@ -809,6 +817,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
OPT_END(),
};
+ setup_work_tree();
repo_read_index(the_repository);
set_opts.cone_mode = -1;
@@ -858,6 +867,7 @@ static int sparse_checkout_reapply(int argc, const char **argv,
OPT_END(),
};
+ setup_work_tree();
if (!core_apply_sparse_checkout)
die(_("must be in a sparse-checkout to reapply sparsity patterns"));
@@ -901,6 +911,7 @@ static int sparse_checkout_disable(int argc, const char **argv,
* forcibly return to a dense checkout regardless of initial state.
*/
+ setup_work_tree();
argc = parse_options(argc, argv, prefix,
builtin_sparse_checkout_disable_options,
builtin_sparse_checkout_disable_usage, 0);
@@ -926,6 +937,91 @@ static int sparse_checkout_disable(int argc, const char **argv,
return set_config(MODE_NO_PATTERNS);
}
+static char const * const builtin_sparse_checkout_check_rules_usage[] = {
+ N_("git sparse-checkout check-rules [-z] [--skip-checks]"
+ "[--[no-]cone] [--rules-file <file>]"),
+ NULL
+};
+
+static struct sparse_checkout_check_rules_opts {
+ int cone_mode;
+ int null_termination;
+ char *rules_file;
+} check_rules_opts;
+
+static int check_rules(struct pattern_list *pl, int null_terminated) {
+ struct strbuf line = STRBUF_INIT;
+ struct strbuf unquoted = STRBUF_INIT;
+ char *path;
+ int line_terminator = null_terminated ? 0 : '\n';
+ strbuf_getline_fn getline_fn = null_terminated ? strbuf_getline_nul
+ : strbuf_getline;
+ the_repository->index->sparse_checkout_patterns = pl;
+ while (!getline_fn(&line, stdin)) {
+ path = line.buf;
+ if (!null_terminated && line.buf[0] == '"') {
+ strbuf_reset(&unquoted);
+ if (unquote_c_style(&unquoted, line.buf, NULL))
+ die(_("unable to unquote C-style string '%s'"),
+ line.buf);
+
+ path = unquoted.buf;
+ }
+
+ if (path_in_sparse_checkout(path, the_repository->index))
+ write_name_quoted(path, stdout, line_terminator);
+ }
+ strbuf_release(&line);
+ strbuf_release(&unquoted);
+
+ return 0;
+}
+
+static int sparse_checkout_check_rules(int argc, const char **argv, const char *prefix)
+{
+ static struct option builtin_sparse_checkout_check_rules_options[] = {
+ OPT_BOOL('z', NULL, &check_rules_opts.null_termination,
+ N_("terminate input and output files by a NUL character")),
+ OPT_BOOL(0, "cone", &check_rules_opts.cone_mode,
+ N_("when used with --rules-file interpret patterns as cone mode patterns")),
+ OPT_FILENAME(0, "rules-file", &check_rules_opts.rules_file,
+ N_("use patterns in <file> instead of the current ones.")),
+ OPT_END(),
+ };
+
+ FILE *fp;
+ int ret;
+ struct pattern_list pl = {0};
+ char *sparse_filename;
+ check_rules_opts.cone_mode = -1;
+
+ argc = parse_options(argc, argv, prefix,
+ builtin_sparse_checkout_check_rules_options,
+ builtin_sparse_checkout_check_rules_usage,
+ PARSE_OPT_KEEP_UNKNOWN_OPT);
+
+ if (check_rules_opts.rules_file && check_rules_opts.cone_mode < 0)
+ check_rules_opts.cone_mode = 1;
+
+ update_cone_mode(&check_rules_opts.cone_mode);
+ pl.use_cone_patterns = core_sparse_checkout_cone;
+ if (check_rules_opts.rules_file) {
+ fp = xfopen(check_rules_opts.rules_file, "r");
+ add_patterns_from_input(&pl, argc, argv, fp);
+ fclose(fp);
+ } else {
+ sparse_filename = get_sparse_checkout_filename();
+ if (add_patterns_from_file_to_list(sparse_filename, "", 0, &pl,
+ NULL, 0))
+ die(_("unable to load existing sparse-checkout patterns"));
+ free(sparse_filename);
+ }
+
+ ret = check_rules(&pl, check_rules_opts.null_termination);
+ clear_pattern_list(&pl);
+ return ret;
+}
+
int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
{
parse_opt_subcommand_fn *fn = NULL;
@@ -936,6 +1032,7 @@ int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
OPT_SUBCOMMAND("add", &fn, sparse_checkout_add),
OPT_SUBCOMMAND("reapply", &fn, sparse_checkout_reapply),
OPT_SUBCOMMAND("disable", &fn, sparse_checkout_disable),
+ OPT_SUBCOMMAND("check-rules", &fn, sparse_checkout_check_rules),
OPT_END(),
};
diff --git a/builtin/stash.c b/builtin/stash.c
index 6442b5e20a..a7e17ffe38 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1473,7 +1473,7 @@ done:
return ret;
}
-static int create_stash(int argc, const char **argv, const char *prefix)
+static int create_stash(int argc, const char **argv, const char *prefix UNUSED)
{
int ret;
struct strbuf stash_msg_buf = STRBUF_INIT;
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 5d999f814e..6bf8d666ce 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -565,7 +565,7 @@ static int module_init(int argc, const char **argv, const char *prefix)
* If there are no path args and submodule.active is set then,
* by default, only initialize 'active' modules.
*/
- if (!argc && git_config_get_value_multi("submodule.active"))
+ if (!argc && !git_config_get("submodule.active"))
module_list_active(&list);
info.prefix = prefix;
@@ -2751,7 +2751,7 @@ static int module_update(int argc, const char **argv, const char *prefix)
* If there are no path args and submodule.active is set then,
* by default, only initialize 'active' modules.
*/
- if (!argc && git_config_get_value_multi("submodule.active"))
+ if (!argc && !git_config_get("submodule.active"))
module_list_active(&list);
info.prefix = opt.prefix;
@@ -2772,7 +2772,7 @@ cleanup:
return ret;
}
-static int push_check(int argc, const char **argv, const char *prefix)
+static int push_check(int argc, const char **argv, const char *prefix UNUSED)
{
struct remote *remote;
const char *superproject_head;
@@ -3148,7 +3148,6 @@ static int config_submodule_in_gitmodules(const char *name, const char *var, con
static void configure_added_submodule(struct add_data *add_data)
{
char *key;
- const char *val;
struct child_process add_submod = CHILD_PROCESS_INIT;
struct child_process add_gitmodules = CHILD_PROCESS_INIT;
@@ -3193,7 +3192,7 @@ static void configure_added_submodule(struct add_data *add_data)
* is_submodule_active(), since that function needs to find
* out the value of "submodule.active" again anyway.
*/
- if (!git_config_get_string_tmp("submodule.active", &val)) {
+ if (!git_config_get("submodule.active")) {
/*
* If the submodule being added isn't already covered by the
* current configured pathspec, set the submodule's active flag
diff --git a/builtin/tag.c b/builtin/tag.c
index 7d189a4a5d..1850a6a6fd 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -44,6 +44,7 @@ static const char * const git_tag_usage[] = {
static unsigned int colopts;
static int force_sign_annotate;
static int config_sign_tag = -1; /* unspecified */
+static int omit_empty = 0;
static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
struct ref_format *format)
@@ -73,6 +74,7 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
die(_("unable to parse format string"));
filter->with_commit_tag_algo = 1;
filter_refs(&array, filter, FILTER_REFS_TAGS);
+ filter_ahead_behind(the_repository, format, &array);
ref_array_sort(sorting, &array);
for (i = 0; i < array.nr; i++) {
@@ -81,7 +83,8 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
if (format_ref_array_item(array.items[i], format, &output, &err))
die("%s", err.buf);
fwrite(output.buf, 1, output.len, stdout);
- putchar('\n');
+ if (output.len || !omit_empty)
+ putchar('\n');
}
strbuf_release(&err);
@@ -476,6 +479,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")),
OPT_MERGED(&filter, N_("print only tags that are merged")),
OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
+ OPT_BOOL(0, "omit-empty", &omit_empty,
+ N_("do not output a newline after empty formatted refs")),
OPT_REF_SORT(&sorting_options),
{
OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
diff --git a/builtin/unpack-file.c b/builtin/unpack-file.c
index 21082ceb60..b35a4b9dfe 100644
--- a/builtin/unpack-file.c
+++ b/builtin/unpack-file.c
@@ -26,7 +26,7 @@ static char *create_temp_file(struct object_id *oid)
return path;
}
-int cmd_unpack_file(int argc, const char **argv, const char *prefix)
+int cmd_unpack_file(int argc, const char **argv, const char *prefix UNUSED)
{
struct object_id oid;
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index ddbdb3dd22..2c52c3a741 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -604,7 +604,7 @@ static void unpack_all(void)
die("unresolved deltas left after unpacking");
}
-int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
+int cmd_unpack_objects(int argc, const char **argv, const char *prefix UNUSED)
{
int i;
struct object_id oid;
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
index 945ee2b412..7f9320ac6d 100644
--- a/builtin/upload-archive.c
+++ b/builtin/upload-archive.c
@@ -79,6 +79,8 @@ int cmd_upload_archive(int argc, const char **argv, const char *prefix)
{
struct child_process writer = CHILD_PROCESS_INIT;
+ BUG_ON_NON_EMPTY_PREFIX(prefix);
+
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(upload_archive_usage);
diff --git a/builtin/var.c b/builtin/var.c
index aa8150fa92..2149998980 100644
--- a/builtin/var.c
+++ b/builtin/var.c
@@ -80,7 +80,7 @@ static int show_config(const char *var, const char *value, void *cb)
return git_default_config(var, value, cb);
}
-int cmd_var(int argc, const char **argv, const char *prefix)
+int cmd_var(int argc, const char **argv, const char *prefix UNUSED)
{
const struct git_var *git_var;
const char *val;
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 0621f6f708..a61bc32189 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -326,7 +326,6 @@ static void copy_filtered_worktree_config(const char *worktree_git_dir)
if (file_exists(from_file)) {
struct config_set cs = { { 0 } };
- const char *core_worktree;
int bare;
if (safe_create_leading_directories(to_file) ||
@@ -345,7 +344,7 @@ static void copy_filtered_worktree_config(const char *worktree_git_dir)
to_file, "core.bare", NULL, "true", 0))
error(_("failed to unset '%s' in '%s'"),
"core.bare", to_file);
- if (!git_configset_get_value(&cs, "core.worktree", &core_worktree) &&
+ if (!git_configset_get(&cs, "core.worktree") &&
git_config_set_in_file_gently(to_file,
"core.worktree", NULL))
error(_("failed to unset '%s' in '%s'"),
diff --git a/builtin/write-tree.c b/builtin/write-tree.c
index 6085f64d10..32e302a813 100644
--- a/builtin/write-tree.c
+++ b/builtin/write-tree.c
@@ -41,6 +41,9 @@ int cmd_write_tree(int argc, const char **argv, const char *cmd_prefix)
argc = parse_options(argc, argv, cmd_prefix, write_tree_options,
write_tree_usage, 0);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
ret = write_index_as_tree(&oid, &the_index, get_index_file(), flags,
tree_prefix);
switch (ret) {
diff --git a/bundle-uri.c b/bundle-uri.c
index 1ff1cf51da..e2b267cc02 100644
--- a/bundle-uri.c
+++ b/bundle-uri.c
@@ -794,6 +794,15 @@ int fetch_bundle_uri(struct repository *r, const char *uri,
init_bundle_list(&list);
+ /*
+ * Do not fetch a NULL or empty bundle URI. An empty bundle URI
+ * could signal that a configured bundle URI has been disabled.
+ */
+ if (!uri || !*uri) {
+ result = 0;
+ goto cleanup;
+ }
+
/* If a bundle is added to this global list, then it is required. */
list.mode = BUNDLE_MODE_ALL;
diff --git a/commit-graph.c b/commit-graph.c
index 0c4f226644..43558b4d9b 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -121,12 +121,10 @@ timestamp_t commit_graph_generation(const struct commit *c)
struct commit_graph_data *data =
commit_graph_data_slab_peek(&commit_graph_data_slab, c);
- if (!data)
- return GENERATION_NUMBER_INFINITY;
- else if (data->graph_pos == COMMIT_NOT_FROM_GRAPH)
- return GENERATION_NUMBER_INFINITY;
+ if (data && data->generation)
+ return data->generation;
- return data->generation;
+ return GENERATION_NUMBER_INFINITY;
}
static struct commit_graph_data *commit_graph_data_at(const struct commit *c)
@@ -1451,24 +1449,52 @@ static void close_reachable(struct write_commit_graph_context *ctx)
stop_progress(&ctx->progress);
}
-static void compute_topological_levels(struct write_commit_graph_context *ctx)
+struct compute_generation_info {
+ struct repository *r;
+ struct packed_commit_list *commits;
+ struct progress *progress;
+ int progress_cnt;
+
+ timestamp_t (*get_generation)(struct commit *c, void *data);
+ void (*set_generation)(struct commit *c, timestamp_t gen, void *data);
+ void *data;
+};
+
+static timestamp_t compute_generation_from_max(struct commit *c,
+ timestamp_t max_gen,
+ int generation_version)
+{
+ switch (generation_version) {
+ case 1: /* topological levels */
+ if (max_gen > GENERATION_NUMBER_V1_MAX - 1)
+ max_gen = GENERATION_NUMBER_V1_MAX - 1;
+ return max_gen + 1;
+
+ case 2: /* corrected commit date */
+ if (c->date && c->date > max_gen)
+ max_gen = c->date - 1;
+ return max_gen + 1;
+
+ default:
+ BUG("attempting unimplemented version");
+ }
+}
+
+static void compute_reachable_generation_numbers(
+ struct compute_generation_info *info,
+ int generation_version)
{
int i;
struct commit_list *list = NULL;
- if (ctx->report_progress)
- ctx->progress = start_delayed_progress(
- _("Computing commit graph topological levels"),
- ctx->commits.nr);
- for (i = 0; i < ctx->commits.nr; i++) {
- struct commit *c = ctx->commits.list[i];
- uint32_t level;
-
- repo_parse_commit(ctx->r, c);
- level = *topo_level_slab_at(ctx->topo_levels, c);
+ for (i = 0; i < info->commits->nr; i++) {
+ struct commit *c = info->commits->list[i];
+ timestamp_t gen;
+ repo_parse_commit(info->r, c);
+ gen = info->get_generation(c, info->data);
+ display_progress(info->progress, info->progress_cnt + 1);
- display_progress(ctx->progress, i + 1);
- if (level != GENERATION_NUMBER_ZERO)
+ if (gen != GENERATION_NUMBER_ZERO && gen != GENERATION_NUMBER_INFINITY)
continue;
commit_list_insert(c, &list);
@@ -1476,41 +1502,91 @@ static void compute_topological_levels(struct write_commit_graph_context *ctx)
struct commit *current = list->item;
struct commit_list *parent;
int all_parents_computed = 1;
- uint32_t max_level = 0;
+ uint32_t max_gen = 0;
for (parent = current->parents; parent; parent = parent->next) {
- repo_parse_commit(ctx->r, parent->item);
- level = *topo_level_slab_at(ctx->topo_levels, parent->item);
+ repo_parse_commit(info->r, parent->item);
+ gen = info->get_generation(parent->item, info->data);
- if (level == GENERATION_NUMBER_ZERO) {
+ if (gen == GENERATION_NUMBER_ZERO) {
all_parents_computed = 0;
commit_list_insert(parent->item, &list);
break;
}
- if (level > max_level)
- max_level = level;
+ if (gen > max_gen)
+ max_gen = gen;
}
if (all_parents_computed) {
pop_commit(&list);
-
- if (max_level > GENERATION_NUMBER_V1_MAX - 1)
- max_level = GENERATION_NUMBER_V1_MAX - 1;
- *topo_level_slab_at(ctx->topo_levels, current) = max_level + 1;
+ gen = compute_generation_from_max(
+ current, max_gen,
+ generation_version);
+ info->set_generation(current, gen, info->data);
}
}
}
+}
+
+static timestamp_t get_topo_level(struct commit *c, void *data)
+{
+ struct write_commit_graph_context *ctx = data;
+ return *topo_level_slab_at(ctx->topo_levels, c);
+}
+
+static void set_topo_level(struct commit *c, timestamp_t t, void *data)
+{
+ struct write_commit_graph_context *ctx = data;
+ *topo_level_slab_at(ctx->topo_levels, c) = (uint32_t)t;
+}
+
+static void compute_topological_levels(struct write_commit_graph_context *ctx)
+{
+ struct compute_generation_info info = {
+ .r = ctx->r,
+ .commits = &ctx->commits,
+ .get_generation = get_topo_level,
+ .set_generation = set_topo_level,
+ .data = ctx,
+ };
+
+ if (ctx->report_progress)
+ info.progress = ctx->progress
+ = start_delayed_progress(
+ _("Computing commit graph topological levels"),
+ ctx->commits.nr);
+
+ compute_reachable_generation_numbers(&info, 1);
+
stop_progress(&ctx->progress);
}
+static timestamp_t get_generation_from_graph_data(struct commit *c, void *data)
+{
+ return commit_graph_data_at(c)->generation;
+}
+
+static void set_generation_v2(struct commit *c, timestamp_t t, void *data)
+{
+ struct commit_graph_data *g = commit_graph_data_at(c);
+ g->generation = t;
+}
+
static void compute_generation_numbers(struct write_commit_graph_context *ctx)
{
int i;
- struct commit_list *list = NULL;
+ struct compute_generation_info info = {
+ .r = ctx->r,
+ .commits = &ctx->commits,
+ .get_generation = get_generation_from_graph_data,
+ .set_generation = set_generation_v2,
+ .data = ctx,
+ };
if (ctx->report_progress)
- ctx->progress = start_delayed_progress(
+ info.progress = ctx->progress
+ = start_delayed_progress(
_("Computing commit graph generation numbers"),
ctx->commits.nr);
@@ -1522,47 +1598,7 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
}
}
- for (i = 0; i < ctx->commits.nr; i++) {
- struct commit *c = ctx->commits.list[i];
- timestamp_t corrected_commit_date;
-
- repo_parse_commit(ctx->r, c);
- corrected_commit_date = commit_graph_data_at(c)->generation;
-
- display_progress(ctx->progress, i + 1);
- if (corrected_commit_date != GENERATION_NUMBER_ZERO)
- continue;
-
- commit_list_insert(c, &list);
- while (list) {
- struct commit *current = list->item;
- struct commit_list *parent;
- int all_parents_computed = 1;
- timestamp_t max_corrected_commit_date = 0;
-
- for (parent = current->parents; parent; parent = parent->next) {
- repo_parse_commit(ctx->r, parent->item);
- corrected_commit_date = commit_graph_data_at(parent->item)->generation;
-
- if (corrected_commit_date == GENERATION_NUMBER_ZERO) {
- all_parents_computed = 0;
- commit_list_insert(parent->item, &list);
- break;
- }
-
- if (corrected_commit_date > max_corrected_commit_date)
- max_corrected_commit_date = corrected_commit_date;
- }
-
- if (all_parents_computed) {
- pop_commit(&list);
-
- if (current->date && current->date > max_corrected_commit_date)
- max_corrected_commit_date = current->date - 1;
- commit_graph_data_at(current)->generation = max_corrected_commit_date + 1;
- }
- }
- }
+ compute_reachable_generation_numbers(&info, 2);
for (i = 0; i < ctx->commits.nr; i++) {
struct commit *c = ctx->commits.list[i];
@@ -1573,6 +1609,35 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
stop_progress(&ctx->progress);
}
+static void set_generation_in_graph_data(struct commit *c, timestamp_t t,
+ void *data)
+{
+ commit_graph_data_at(c)->generation = t;
+}
+
+/*
+ * After this method, all commits reachable from those in the given
+ * list will have non-zero, non-infinite generation numbers.
+ */
+void ensure_generations_valid(struct repository *r,
+ struct commit **commits, size_t nr)
+{
+ int generation_version = get_configured_generation_version(r);
+ struct packed_commit_list list = {
+ .list = commits,
+ .alloc = nr,
+ .nr = nr,
+ };
+ struct compute_generation_info info = {
+ .r = r,
+ .commits = &list,
+ .get_generation = get_generation_from_graph_data,
+ .set_generation = set_generation_in_graph_data,
+ };
+
+ compute_reachable_generation_numbers(&info, generation_version);
+}
+
static void trace2_bloom_filter_write_statistics(struct write_commit_graph_context *ctx)
{
trace2_data_intmax("commit-graph", ctx->r, "filter-computed",
diff --git a/commit-graph.h b/commit-graph.h
index bb88bec7aa..83aaa1dbb9 100644
--- a/commit-graph.h
+++ b/commit-graph.h
@@ -189,4 +189,12 @@ struct commit_graph_data {
*/
timestamp_t commit_graph_generation(const struct commit *);
uint32_t commit_graph_position(const struct commit *);
+
+/*
+ * After this method, all commits reachable from those in the given
+ * list will have non-zero, non-infinite generation numbers.
+ */
+void ensure_generations_valid(struct repository *r,
+ struct commit **commits, size_t nr);
+
#endif
diff --git a/commit-reach.c b/commit-reach.c
index 5957a8e950..70bde8af05 100644
--- a/commit-reach.c
+++ b/commit-reach.c
@@ -10,6 +10,7 @@
#include "revision.h"
#include "tag.h"
#include "commit-reach.h"
+#include "ewah/ewok.h"
/* Remember to update object flag allocation in object.h */
#define PARENT1 (1u<<16)
@@ -948,3 +949,218 @@ struct commit_list *get_reachable_subset(struct commit **from, int nr_from,
return found_commits;
}
+
+define_commit_slab(bit_arrays, struct bitmap *);
+static struct bit_arrays bit_arrays;
+
+static void insert_no_dup(struct prio_queue *queue, struct commit *c)
+{
+ if (c->object.flags & PARENT2)
+ return;
+ prio_queue_put(queue, c);
+ c->object.flags |= PARENT2;
+}
+
+static struct bitmap *get_bit_array(struct commit *c, int width)
+{
+ struct bitmap **bitmap = bit_arrays_at(&bit_arrays, c);
+ if (!*bitmap)
+ *bitmap = bitmap_word_alloc(width);
+ return *bitmap;
+}
+
+static void free_bit_array(struct commit *c)
+{
+ struct bitmap **bitmap = bit_arrays_at(&bit_arrays, c);
+ if (!*bitmap)
+ return;
+ bitmap_free(*bitmap);
+ *bitmap = NULL;
+}
+
+void ahead_behind(struct repository *r,
+ struct commit **commits, size_t commits_nr,
+ struct ahead_behind_count *counts, size_t counts_nr)
+{
+ struct prio_queue queue = { .compare = compare_commits_by_gen_then_commit_date };
+ size_t width = DIV_ROUND_UP(commits_nr, BITS_IN_EWORD);
+
+ if (!commits_nr || !counts_nr)
+ return;
+
+ for (size_t i = 0; i < counts_nr; i++) {
+ counts[i].ahead = 0;
+ counts[i].behind = 0;
+ }
+
+ ensure_generations_valid(r, commits, commits_nr);
+
+ init_bit_arrays(&bit_arrays);
+
+ for (size_t i = 0; i < commits_nr; i++) {
+ struct commit *c = commits[i];
+ struct bitmap *bitmap = get_bit_array(c, width);
+
+ bitmap_set(bitmap, i);
+ insert_no_dup(&queue, c);
+ }
+
+ while (queue_has_nonstale(&queue)) {
+ struct commit *c = prio_queue_get(&queue);
+ struct commit_list *p;
+ struct bitmap *bitmap_c = get_bit_array(c, width);
+
+ for (size_t i = 0; i < counts_nr; i++) {
+ int reach_from_tip = !!bitmap_get(bitmap_c, counts[i].tip_index);
+ int reach_from_base = !!bitmap_get(bitmap_c, counts[i].base_index);
+
+ if (reach_from_tip ^ reach_from_base) {
+ if (reach_from_base)
+ counts[i].behind++;
+ else
+ counts[i].ahead++;
+ }
+ }
+
+ for (p = c->parents; p; p = p->next) {
+ struct bitmap *bitmap_p;
+
+ repo_parse_commit(r, p->item);
+
+ bitmap_p = get_bit_array(p->item, width);
+ bitmap_or(bitmap_p, bitmap_c);
+
+ /*
+ * If this parent is reachable from every starting
+ * commit, then none of its ancestors can contribute
+ * to the ahead/behind count. Mark it as STALE, so
+ * we can stop the walk when every commit in the
+ * queue is STALE.
+ */
+ if (bitmap_popcount(bitmap_p) == commits_nr)
+ p->item->object.flags |= STALE;
+
+ insert_no_dup(&queue, p->item);
+ }
+
+ free_bit_array(c);
+ }
+
+ /* STALE is used here, PARENT2 is used by insert_no_dup(). */
+ repo_clear_commit_marks(r, PARENT2 | STALE);
+ clear_bit_arrays(&bit_arrays);
+ clear_prio_queue(&queue);
+}
+
+struct commit_and_index {
+ struct commit *commit;
+ unsigned int index;
+ timestamp_t generation;
+};
+
+static int compare_commit_and_index_by_generation(const void *va, const void *vb)
+{
+ const struct commit_and_index *a = (const struct commit_and_index *)va;
+ const struct commit_and_index *b = (const struct commit_and_index *)vb;
+
+ if (a->generation > b->generation)
+ return 1;
+ if (a->generation < b->generation)
+ return -1;
+ return 0;
+}
+
+void tips_reachable_from_bases(struct repository *r,
+ struct commit_list *bases,
+ struct commit **tips, size_t tips_nr,
+ int mark)
+{
+ struct commit_and_index *commits;
+ size_t min_generation_index = 0;
+ timestamp_t min_generation;
+ struct commit_list *stack = NULL;
+
+ if (!bases || !tips || !tips_nr)
+ return;
+
+ /*
+ * Do a depth-first search starting at 'bases' to search for the
+ * tips. Stop at the lowest (un-found) generation number. When
+ * finding the lowest commit, increase the minimum generation
+ * number to the next lowest (un-found) generation number.
+ */
+
+ CALLOC_ARRAY(commits, tips_nr);
+
+ for (size_t i = 0; i < tips_nr; i++) {
+ commits[i].commit = tips[i];
+ commits[i].index = i;
+ commits[i].generation = commit_graph_generation(tips[i]);
+ }
+
+ /* Sort with generation number ascending. */
+ QSORT(commits, tips_nr, compare_commit_and_index_by_generation);
+ min_generation = commits[0].generation;
+
+ while (bases) {
+ repo_parse_commit(r, bases->item);
+ commit_list_insert(bases->item, &stack);
+ bases = bases->next;
+ }
+
+ while (stack) {
+ int explored_all_parents = 1;
+ struct commit_list *p;
+ struct commit *c = stack->item;
+ timestamp_t c_gen = commit_graph_generation(c);
+
+ /* Does it match any of our tips? */
+ for (size_t j = min_generation_index; j < tips_nr; j++) {
+ if (c_gen < commits[j].generation)
+ break;
+
+ if (commits[j].commit == c) {
+ tips[commits[j].index]->object.flags |= mark;
+
+ if (j == min_generation_index) {
+ unsigned int k = j + 1;
+ while (k < tips_nr &&
+ (tips[commits[k].index]->object.flags & mark))
+ k++;
+
+ /* Terminate early if all found. */
+ if (k >= tips_nr)
+ goto done;
+
+ min_generation_index = k;
+ min_generation = commits[k].generation;
+ }
+ }
+ }
+
+ for (p = c->parents; p; p = p->next) {
+ repo_parse_commit(r, p->item);
+
+ /* Have we already explored this parent? */
+ if (p->item->object.flags & SEEN)
+ continue;
+
+ /* Is it below the current minimum generation? */
+ if (commit_graph_generation(p->item) < min_generation)
+ continue;
+
+ /* Ok, we will explore from here on. */
+ p->item->object.flags |= SEEN;
+ explored_all_parents = 0;
+ commit_list_insert(p->item, &stack);
+ break;
+ }
+
+ if (explored_all_parents)
+ pop_commit(&stack);
+ }
+
+done:
+ free(commits);
+ repo_clear_commit_marks(r, SEEN);
+}
diff --git a/commit-reach.h b/commit-reach.h
index 94a96e0329..35c4da4948 100644
--- a/commit-reach.h
+++ b/commit-reach.h
@@ -95,4 +95,44 @@ struct commit_list *get_reachable_subset(struct commit **from, int nr_from,
struct commit **to, int nr_to,
unsigned int reachable_flag);
+struct ahead_behind_count {
+ /**
+ * As input, the *_index members indicate which positions in
+ * the 'tips' array correspond to the tip and base of this
+ * comparison.
+ */
+ size_t tip_index;
+ size_t base_index;
+
+ /**
+ * These values store the computed counts for each side of the
+ * symmetric difference:
+ *
+ * 'ahead' stores the number of commits reachable from the tip
+ * and not reachable from the base.
+ *
+ * 'behind' stores the number of commits reachable from the base
+ * and not reachable from the tip.
+ */
+ unsigned int ahead;
+ unsigned int behind;
+};
+
+/*
+ * Given an array of commits and an array of ahead_behind_count pairs,
+ * compute the ahead/behind counts for each pair.
+ */
+void ahead_behind(struct repository *r,
+ struct commit **commits, size_t commits_nr,
+ struct ahead_behind_count *counts, size_t counts_nr);
+
+/*
+ * For all tip commits, add 'mark' to their flags if and only if they
+ * are reachable from one of the commits in 'bases'.
+ */
+void tips_reachable_from_bases(struct repository *r,
+ struct commit_list *bases,
+ struct commit **tips, size_t tips_nr,
+ int mark);
+
#endif
diff --git a/config.c b/config.c
index 9beba19b41..43b0d3fb57 100644
--- a/config.c
+++ b/config.c
@@ -63,34 +63,79 @@ struct config_source {
int (*do_ungetc)(int c, struct config_source *conf);
long (*do_ftell)(struct config_source *c);
};
+#define CONFIG_SOURCE_INIT { 0 }
+struct config_reader {
+ /*
+ * These members record the "current" config source, which can be
+ * accessed by parsing callbacks.
+ *
+ * The "source" variable will be non-NULL only when we are actually
+ * parsing a real config source (file, blob, cmdline, etc).
+ *
+ * The "config_kvi" variable will be non-NULL only when we are feeding
+ * cached config from a configset into a callback.
+ *
+ * They cannot be non-NULL at the same time. If they are both NULL, then
+ * we aren't parsing anything (and depending on the function looking at
+ * the variables, it's either a bug for it to be called in the first
+ * place, or it's a function which can be reused for non-config
+ * purposes, and should fall back to some sane behavior).
+ */
+ struct config_source *source;
+ struct key_value_info *config_kvi;
+ /*
+ * The "scope" of the current config source being parsed (repo, global,
+ * etc). Like "source", this is only set when parsing a config source.
+ * It's not part of "source" because it transcends a single file (i.e.,
+ * a file included from .git/config is still in "repo" scope).
+ *
+ * When iterating through a configset, the equivalent value is
+ * "config_kvi.scope" (see above).
+ */
+ enum config_scope parsing_scope;
+};
/*
- * These variables record the "current" config source, which
- * can be accessed by parsing callbacks.
- *
- * The "cf" variable will be non-NULL only when we are actually parsing a real
- * config source (file, blob, cmdline, etc).
- *
- * The "current_config_kvi" variable will be non-NULL only when we are feeding
- * cached config from a configset into a callback.
- *
- * They should generally never be non-NULL at the same time. If they are both
- * NULL, then we aren't parsing anything (and depending on the function looking
- * at the variables, it's either a bug for it to be called in the first place,
- * or it's a function which can be reused for non-config purposes, and should
- * fall back to some sane behavior).
+ * Where possible, prefer to accept "struct config_reader" as an arg than to use
+ * "the_reader". "the_reader" should only be used if that is infeasible, e.g. in
+ * a public function.
*/
-static struct config_source *cf;
-static struct key_value_info *current_config_kvi;
+static struct config_reader the_reader;
-/*
- * Similar to the variables above, this gives access to the "scope" of the
- * current value (repo, global, etc). For cached values, it can be found via
- * the current_config_kvi as above. During parsing, the current value can be
- * found in this variable. It's not part of "cf" because it transcends a single
- * file (i.e., a file included from .git/config is still in "repo" scope).
- */
-static enum config_scope current_parsing_scope;
+static inline void config_reader_push_source(struct config_reader *reader,
+ struct config_source *top)
+{
+ if (reader->config_kvi)
+ BUG("source should not be set while iterating a config set");
+ top->prev = reader->source;
+ reader->source = top;
+}
+
+static inline struct config_source *config_reader_pop_source(struct config_reader *reader)
+{
+ struct config_source *ret;
+ if (!reader->source)
+ BUG("tried to pop config source, but we weren't reading config");
+ ret = reader->source;
+ reader->source = reader->source->prev;
+ return ret;
+}
+
+static inline void config_reader_set_kvi(struct config_reader *reader,
+ struct key_value_info *kvi)
+{
+ if (kvi && (reader->source || reader->parsing_scope))
+ BUG("kvi should not be set while parsing a config source");
+ reader->config_kvi = kvi;
+}
+
+static inline void config_reader_set_scope(struct config_reader *reader,
+ enum config_scope scope)
+{
+ if (scope && reader->config_kvi)
+ BUG("scope should only be set when iterating through a config source");
+ reader->parsing_scope = scope;
+}
static int pack_compression_seen;
static int zlib_compression_seen;
@@ -153,6 +198,7 @@ struct config_include_data {
void *data;
const struct config_options *opts;
struct git_config_source *config_source;
+ struct config_reader *config_reader;
/*
* All remote URLs discovered when reading all config files.
@@ -170,7 +216,8 @@ static const char include_depth_advice[] = N_(
"from\n"
" %s\n"
"This might be due to circular includes.");
-static int handle_path_include(const char *path, struct config_include_data *inc)
+static int handle_path_include(struct config_source *cs, const char *path,
+ struct config_include_data *inc)
{
int ret = 0;
struct strbuf buf = STRBUF_INIT;
@@ -191,14 +238,14 @@ static int handle_path_include(const char *path, struct config_include_data *inc
if (!is_absolute_path(path)) {
char *slash;
- if (!cf || !cf->path) {
+ if (!cs || !cs->path) {
ret = error(_("relative config includes must come from files"));
goto cleanup;
}
- slash = find_last_dir_sep(cf->path);
+ slash = find_last_dir_sep(cs->path);
if (slash)
- strbuf_add(&buf, cf->path, slash - cf->path + 1);
+ strbuf_add(&buf, cs->path, slash - cs->path + 1);
strbuf_addstr(&buf, path);
path = buf.buf;
}
@@ -206,8 +253,8 @@ static int handle_path_include(const char *path, struct config_include_data *inc
if (!access_or_die(path, R_OK, 0)) {
if (++inc->depth > MAX_INCLUDE_DEPTH)
die(_(include_depth_advice), MAX_INCLUDE_DEPTH, path,
- !cf ? "<unknown>" :
- cf->name ? cf->name :
+ !cs ? "<unknown>" :
+ cs->name ? cs->name :
"the command line");
ret = git_config_from_file(git_config_include, path, inc);
inc->depth--;
@@ -224,7 +271,8 @@ static void add_trailing_starstar_for_dir(struct strbuf *pat)
strbuf_addstr(pat, "**");
}
-static int prepare_include_condition_pattern(struct strbuf *pat)
+static int prepare_include_condition_pattern(struct config_source *cs,
+ struct strbuf *pat)
{
struct strbuf path = STRBUF_INIT;
char *expanded;
@@ -240,11 +288,11 @@ static int prepare_include_condition_pattern(struct strbuf *pat)
if (pat->buf[0] == '.' && is_dir_sep(pat->buf[1])) {
const char *slash;
- if (!cf || !cf->path)
+ if (!cs || !cs->path)
return error(_("relative config include "
"conditionals must come from files"));
- strbuf_realpath(&path, cf->path, 1);
+ strbuf_realpath(&path, cs->path, 1);
slash = find_last_dir_sep(path.buf);
if (!slash)
BUG("how is this possible?");
@@ -259,7 +307,8 @@ static int prepare_include_condition_pattern(struct strbuf *pat)
return prefix;
}
-static int include_by_gitdir(const struct config_options *opts,
+static int include_by_gitdir(struct config_source *cs,
+ const struct config_options *opts,
const char *cond, size_t cond_len, int icase)
{
struct strbuf text = STRBUF_INIT;
@@ -275,7 +324,7 @@ static int include_by_gitdir(const struct config_options *opts,
strbuf_realpath(&text, git_dir, 1);
strbuf_add(&pattern, cond, cond_len);
- prefix = prepare_include_condition_pattern(&pattern);
+ prefix = prepare_include_condition_pattern(cs, &pattern);
again:
if (prefix < 0)
@@ -356,24 +405,18 @@ static void populate_remote_urls(struct config_include_data *inc)
{
struct config_options opts;
- struct config_source *store_cf = cf;
- struct key_value_info *store_kvi = current_config_kvi;
- enum config_scope store_scope = current_parsing_scope;
+ enum config_scope store_scope = inc->config_reader->parsing_scope;
opts = *inc->opts;
opts.unconditional_remote_url = 1;
- cf = NULL;
- current_config_kvi = NULL;
- current_parsing_scope = 0;
+ config_reader_set_scope(inc->config_reader, 0);
inc->remote_urls = xmalloc(sizeof(*inc->remote_urls));
string_list_init_dup(inc->remote_urls);
config_with_options(add_remote_url, inc->remote_urls, inc->config_source, &opts);
- cf = store_cf;
- current_config_kvi = store_kvi;
- current_parsing_scope = store_scope;
+ config_reader_set_scope(inc->config_reader, store_scope);
}
static int forbid_remote_url(const char *var, const char *value UNUSED,
@@ -420,15 +463,16 @@ static int include_by_remote_url(struct config_include_data *inc,
inc->remote_urls);
}
-static int include_condition_is_true(struct config_include_data *inc,
+static int include_condition_is_true(struct config_source *cs,
+ struct config_include_data *inc,
const char *cond, size_t cond_len)
{
const struct config_options *opts = inc->opts;
if (skip_prefix_mem(cond, cond_len, "gitdir:", &cond, &cond_len))
- return include_by_gitdir(opts, cond, cond_len, 0);
+ return include_by_gitdir(cs, opts, cond, cond_len, 0);
else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
- return include_by_gitdir(opts, cond, cond_len, 1);
+ return include_by_gitdir(cs, opts, cond, cond_len, 1);
else if (skip_prefix_mem(cond, cond_len, "onbranch:", &cond, &cond_len))
return include_by_branch(cond, cond_len);
else if (skip_prefix_mem(cond, cond_len, "hasconfig:remote.*.url:", &cond,
@@ -442,6 +486,7 @@ static int include_condition_is_true(struct config_include_data *inc,
static int git_config_include(const char *var, const char *value, void *data)
{
struct config_include_data *inc = data;
+ struct config_source *cs = inc->config_reader->source;
const char *cond, *key;
size_t cond_len;
int ret;
@@ -455,16 +500,16 @@ static int git_config_include(const char *var, const char *value, void *data)
return ret;
if (!strcmp(var, "include.path"))
- ret = handle_path_include(value, inc);
+ ret = handle_path_include(cs, value, inc);
if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
- cond && include_condition_is_true(inc, cond, cond_len) &&
+ cond && include_condition_is_true(cs, inc, cond, cond_len) &&
!strcmp(key, "path")) {
config_fn_t old_fn = inc->fn;
if (inc->opts->unconditional_remote_url)
inc->fn = forbid_remote_url;
- ret = handle_path_include(value, inc);
+ ret = handle_path_include(cs, value, inc);
inc->fn = old_fn;
}
@@ -724,12 +769,10 @@ int git_config_from_parameters(config_fn_t fn, void *data)
struct strvec to_free = STRVEC_INIT;
int ret = 0;
char *envw = NULL;
- struct config_source source;
+ struct config_source source = CONFIG_SOURCE_INIT;
- memset(&source, 0, sizeof(source));
- source.prev = cf;
source.origin_type = CONFIG_ORIGIN_CMDLINE;
- cf = &source;
+ config_reader_push_source(&the_reader, &source);
env = getenv(CONFIG_COUNT_ENVIRONMENT);
if (env) {
@@ -787,25 +830,25 @@ out:
strbuf_release(&envvar);
strvec_clear(&to_free);
free(envw);
- cf = source.prev;
+ config_reader_pop_source(&the_reader);
return ret;
}
-static int get_next_char(void)
+static int get_next_char(struct config_source *cs)
{
- int c = cf->do_fgetc(cf);
+ int c = cs->do_fgetc(cs);
if (c == '\r') {
/* DOS like systems */
- c = cf->do_fgetc(cf);
+ c = cs->do_fgetc(cs);
if (c != '\n') {
if (c != EOF)
- cf->do_ungetc(c, cf);
+ cs->do_ungetc(c, cs);
c = '\r';
}
}
- if (c != EOF && ++cf->total_len > INT_MAX) {
+ if (c != EOF && ++cs->total_len > INT_MAX) {
/*
* This is an absurdly long config file; refuse to parse
* further in order to protect downstream code from integer
@@ -813,38 +856,38 @@ static int get_next_char(void)
* but we can mark EOF and put trash in the return value,
* which will trigger a parse error.
*/
- cf->eof = 1;
+ cs->eof = 1;
return 0;
}
if (c == '\n')
- cf->linenr++;
+ cs->linenr++;
if (c == EOF) {
- cf->eof = 1;
- cf->linenr++;
+ cs->eof = 1;
+ cs->linenr++;
c = '\n';
}
return c;
}
-static char *parse_value(void)
+static char *parse_value(struct config_source *cs)
{
int quote = 0, comment = 0, space = 0;
- strbuf_reset(&cf->value);
+ strbuf_reset(&cs->value);
for (;;) {
- int c = get_next_char();
+ int c = get_next_char(cs);
if (c == '\n') {
if (quote) {
- cf->linenr--;
+ cs->linenr--;
return NULL;
}
- return cf->value.buf;
+ return cs->value.buf;
}
if (comment)
continue;
if (isspace(c) && !quote) {
- if (cf->value.len)
+ if (cs->value.len)
space++;
continue;
}
@@ -855,9 +898,9 @@ static char *parse_value(void)
}
}
for (; space; space--)
- strbuf_addch(&cf->value, ' ');
+ strbuf_addch(&cs->value, ' ');
if (c == '\\') {
- c = get_next_char();
+ c = get_next_char(cs);
switch (c) {
case '\n':
continue;
@@ -877,18 +920,19 @@ static char *parse_value(void)
default:
return NULL;
}
- strbuf_addch(&cf->value, c);
+ strbuf_addch(&cs->value, c);
continue;
}
if (c == '"') {
quote = 1-quote;
continue;
}
- strbuf_addch(&cf->value, c);
+ strbuf_addch(&cs->value, c);
}
}
-static int get_value(config_fn_t fn, void *data, struct strbuf *name)
+static int get_value(struct config_source *cs, config_fn_t fn, void *data,
+ struct strbuf *name)
{
int c;
char *value;
@@ -896,8 +940,8 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name)
/* Get the full name */
for (;;) {
- c = get_next_char();
- if (cf->eof)
+ c = get_next_char(cs);
+ if (cs->eof)
break;
if (!iskeychar(c))
break;
@@ -905,13 +949,13 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name)
}
while (c == ' ' || c == '\t')
- c = get_next_char();
+ c = get_next_char(cs);
value = NULL;
if (c != '\n') {
if (c != '=')
return -1;
- value = parse_value();
+ value = parse_value(cs);
if (!value)
return -1;
}
@@ -920,20 +964,21 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name)
* the line we just parsed during the call to fn to get
* accurate line number in error messages.
*/
- cf->linenr--;
+ cs->linenr--;
ret = fn(name->buf, value, data);
if (ret >= 0)
- cf->linenr++;
+ cs->linenr++;
return ret;
}
-static int get_extended_base_var(struct strbuf *name, int c)
+static int get_extended_base_var(struct config_source *cs, struct strbuf *name,
+ int c)
{
- cf->subsection_case_sensitive = 0;
+ cs->subsection_case_sensitive = 0;
do {
if (c == '\n')
goto error_incomplete_line;
- c = get_next_char();
+ c = get_next_char(cs);
} while (isspace(c));
/* We require the format to be '[base "extension"]' */
@@ -942,13 +987,13 @@ static int get_extended_base_var(struct strbuf *name, int c)
strbuf_addch(name, '.');
for (;;) {
- int c = get_next_char();
+ int c = get_next_char(cs);
if (c == '\n')
goto error_incomplete_line;
if (c == '"')
break;
if (c == '\\') {
- c = get_next_char();
+ c = get_next_char(cs);
if (c == '\n')
goto error_incomplete_line;
}
@@ -956,25 +1001,25 @@ static int get_extended_base_var(struct strbuf *name, int c)
}
/* Final ']' */
- if (get_next_char() != ']')
+ if (get_next_char(cs) != ']')
return -1;
return 0;
error_incomplete_line:
- cf->linenr--;
+ cs->linenr--;
return -1;
}
-static int get_base_var(struct strbuf *name)
+static int get_base_var(struct config_source *cs, struct strbuf *name)
{
- cf->subsection_case_sensitive = 1;
+ cs->subsection_case_sensitive = 1;
for (;;) {
- int c = get_next_char();
- if (cf->eof)
+ int c = get_next_char(cs);
+ if (cs->eof)
return -1;
if (c == ']')
return 0;
if (isspace(c))
- return get_extended_base_var(name, c);
+ return get_extended_base_var(cs, name, c);
if (!iskeychar(c) && c != '.')
return -1;
strbuf_addch(name, tolower(c));
@@ -987,7 +1032,8 @@ struct parse_event_data {
const struct config_options *opts;
};
-static int do_event(enum config_event_t type, struct parse_event_data *data)
+static int do_event(struct config_source *cs, enum config_event_t type,
+ struct parse_event_data *data)
{
size_t offset;
@@ -998,7 +1044,7 @@ static int do_event(enum config_event_t type, struct parse_event_data *data)
data->previous_type == type)
return 0;
- offset = cf->do_ftell(cf);
+ offset = cs->do_ftell(cs);
/*
* At EOF, the parser always "inserts" an extra '\n', therefore
* the end offset of the event is the current file position, otherwise
@@ -1018,12 +1064,12 @@ static int do_event(enum config_event_t type, struct parse_event_data *data)
return 0;
}
-static int git_parse_source(config_fn_t fn, void *data,
- const struct config_options *opts)
+static int git_parse_source(struct config_source *cs, config_fn_t fn,
+ void *data, const struct config_options *opts)
{
int comment = 0;
size_t baselen = 0;
- struct strbuf *var = &cf->var;
+ struct strbuf *var = &cs->var;
int error_return = 0;
char *error_msg = NULL;
@@ -1038,7 +1084,7 @@ static int git_parse_source(config_fn_t fn, void *data,
for (;;) {
int c;
- c = get_next_char();
+ c = get_next_char(cs);
if (bomptr && *bomptr) {
/* We are at the file beginning; skip UTF8-encoded BOM
* if present. Sane editors won't put this in on their
@@ -1055,12 +1101,12 @@ static int git_parse_source(config_fn_t fn, void *data,
}
}
if (c == '\n') {
- if (cf->eof) {
- if (do_event(CONFIG_EVENT_EOF, &event_data) < 0)
+ if (cs->eof) {
+ if (do_event(cs, CONFIG_EVENT_EOF, &event_data) < 0)
return -1;
return 0;
}
- if (do_event(CONFIG_EVENT_WHITESPACE, &event_data) < 0)
+ if (do_event(cs, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
return -1;
comment = 0;
continue;
@@ -1068,23 +1114,23 @@ static int git_parse_source(config_fn_t fn, void *data,
if (comment)
continue;
if (isspace(c)) {
- if (do_event(CONFIG_EVENT_WHITESPACE, &event_data) < 0)
+ if (do_event(cs, CONFIG_EVENT_WHITESPACE, &event_data) < 0)
return -1;
continue;
}
if (c == '#' || c == ';') {
- if (do_event(CONFIG_EVENT_COMMENT, &event_data) < 0)
+ if (do_event(cs, CONFIG_EVENT_COMMENT, &event_data) < 0)
return -1;
comment = 1;
continue;
}
if (c == '[') {
- if (do_event(CONFIG_EVENT_SECTION, &event_data) < 0)
+ if (do_event(cs, CONFIG_EVENT_SECTION, &event_data) < 0)
return -1;
/* Reset prior to determining a new stem */
strbuf_reset(var);
- if (get_base_var(var) < 0 || var->len < 1)
+ if (get_base_var(cs, var) < 0 || var->len < 1)
break;
strbuf_addch(var, '.');
baselen = var->len;
@@ -1093,7 +1139,7 @@ static int git_parse_source(config_fn_t fn, void *data,
if (!isalpha(c))
break;
- if (do_event(CONFIG_EVENT_ENTRY, &event_data) < 0)
+ if (do_event(cs, CONFIG_EVENT_ENTRY, &event_data) < 0)
return -1;
/*
@@ -1103,42 +1149,42 @@ static int git_parse_source(config_fn_t fn, void *data,
*/
strbuf_setlen(var, baselen);
strbuf_addch(var, tolower(c));
- if (get_value(fn, data, var) < 0)
+ if (get_value(cs, fn, data, var) < 0)
break;
}
- if (do_event(CONFIG_EVENT_ERROR, &event_data) < 0)
+ if (do_event(cs, CONFIG_EVENT_ERROR, &event_data) < 0)
return -1;
- switch (cf->origin_type) {
+ switch (cs->origin_type) {
case CONFIG_ORIGIN_BLOB:
error_msg = xstrfmt(_("bad config line %d in blob %s"),
- cf->linenr, cf->name);
+ cs->linenr, cs->name);
break;
case CONFIG_ORIGIN_FILE:
error_msg = xstrfmt(_("bad config line %d in file %s"),
- cf->linenr, cf->name);
+ cs->linenr, cs->name);
break;
case CONFIG_ORIGIN_STDIN:
error_msg = xstrfmt(_("bad config line %d in standard input"),
- cf->linenr);
+ cs->linenr);
break;
case CONFIG_ORIGIN_SUBMODULE_BLOB:
error_msg = xstrfmt(_("bad config line %d in submodule-blob %s"),
- cf->linenr, cf->name);
+ cs->linenr, cs->name);
break;
case CONFIG_ORIGIN_CMDLINE:
error_msg = xstrfmt(_("bad config line %d in command line %s"),
- cf->linenr, cf->name);
+ cs->linenr, cs->name);
break;
default:
error_msg = xstrfmt(_("bad config line %d in %s"),
- cf->linenr, cf->name);
+ cs->linenr, cs->name);
}
switch (opts && opts->error_action ?
opts->error_action :
- cf->default_error_action) {
+ cs->default_error_action) {
case CONFIG_ERROR_DIE:
die("%s", error_msg);
break;
@@ -1279,38 +1325,48 @@ int git_parse_ssize_t(const char *value, ssize_t *ret)
return 1;
}
+static int reader_config_name(struct config_reader *reader, const char **out);
+static int reader_origin_type(struct config_reader *reader,
+ enum config_origin_type *type);
NORETURN
-static void die_bad_number(const char *name, const char *value)
+static void die_bad_number(struct config_reader *reader, const char *name,
+ const char *value)
{
const char *error_type = (errno == ERANGE) ?
N_("out of range") : N_("invalid unit");
const char *bad_numeric = N_("bad numeric config value '%s' for '%s': %s");
+ const char *config_name = NULL;
+ enum config_origin_type config_origin = CONFIG_ORIGIN_UNKNOWN;
if (!value)
value = "";
- if (!(cf && cf->name))
+ /* Ignoring the return value is okay since we handle missing values. */
+ reader_config_name(reader, &config_name);
+ reader_origin_type(reader, &config_origin);
+
+ if (!config_name)
die(_(bad_numeric), value, name, _(error_type));
- switch (cf->origin_type) {
+ switch (config_origin) {
case CONFIG_ORIGIN_BLOB:
die(_("bad numeric config value '%s' for '%s' in blob %s: %s"),
- value, name, cf->name, _(error_type));
+ value, name, config_name, _(error_type));
case CONFIG_ORIGIN_FILE:
die(_("bad numeric config value '%s' for '%s' in file %s: %s"),
- value, name, cf->name, _(error_type));
+ value, name, config_name, _(error_type));
case CONFIG_ORIGIN_STDIN:
die(_("bad numeric config value '%s' for '%s' in standard input: %s"),
value, name, _(error_type));
case CONFIG_ORIGIN_SUBMODULE_BLOB:
die(_("bad numeric config value '%s' for '%s' in submodule-blob %s: %s"),
- value, name, cf->name, _(error_type));
+ value, name, config_name, _(error_type));
case CONFIG_ORIGIN_CMDLINE:
die(_("bad numeric config value '%s' for '%s' in command line %s: %s"),
- value, name, cf->name, _(error_type));
+ value, name, config_name, _(error_type));
default:
die(_("bad numeric config value '%s' for '%s' in %s: %s"),
- value, name, cf->name, _(error_type));
+ value, name, config_name, _(error_type));
}
}
@@ -1318,7 +1374,7 @@ int git_config_int(const char *name, const char *value)
{
int ret;
if (!git_parse_int(value, &ret))
- die_bad_number(name, value);
+ die_bad_number(&the_reader, name, value);
return ret;
}
@@ -1326,7 +1382,7 @@ int64_t git_config_int64(const char *name, const char *value)
{
int64_t ret;
if (!git_parse_int64(value, &ret))
- die_bad_number(name, value);
+ die_bad_number(&the_reader, name, value);
return ret;
}
@@ -1334,7 +1390,7 @@ unsigned long git_config_ulong(const char *name, const char *value)
{
unsigned long ret;
if (!git_parse_ulong(value, &ret))
- die_bad_number(name, value);
+ die_bad_number(&the_reader, name, value);
return ret;
}
@@ -1342,7 +1398,7 @@ ssize_t git_config_ssize_t(const char *name, const char *value)
{
ssize_t ret;
if (!git_parse_ssize_t(value, &ret))
- die_bad_number(name, value);
+ die_bad_number(&the_reader, name, value);
return ret;
}
@@ -1697,7 +1753,7 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
comment_line_char = value[0];
auto_comment_line_char = 0;
} else
- return error(_("core.commentChar should only be one character"));
+ return error(_("core.commentChar should only be one ASCII character"));
return 0;
}
@@ -1948,36 +2004,37 @@ int git_default_config(const char *var, const char *value, void *cb)
* fgetc, ungetc, ftell of top need to be initialized before calling
* this function.
*/
-static int do_config_from(struct config_source *top, config_fn_t fn, void *data,
+static int do_config_from(struct config_reader *reader,
+ struct config_source *top, config_fn_t fn, void *data,
const struct config_options *opts)
{
int ret;
/* push config-file parsing state stack */
- top->prev = cf;
top->linenr = 1;
top->eof = 0;
top->total_len = 0;
strbuf_init(&top->value, 1024);
strbuf_init(&top->var, 1024);
- cf = top;
+ config_reader_push_source(reader, top);
- ret = git_parse_source(fn, data, opts);
+ ret = git_parse_source(top, fn, data, opts);
/* pop config-file parsing state stack */
strbuf_release(&top->value);
strbuf_release(&top->var);
- cf = top->prev;
+ config_reader_pop_source(reader);
return ret;
}
-static int do_config_from_file(config_fn_t fn,
- const enum config_origin_type origin_type,
- const char *name, const char *path, FILE *f,
- void *data, const struct config_options *opts)
+static int do_config_from_file(struct config_reader *reader,
+ config_fn_t fn,
+ const enum config_origin_type origin_type,
+ const char *name, const char *path, FILE *f,
+ void *data, const struct config_options *opts)
{
- struct config_source top;
+ struct config_source top = CONFIG_SOURCE_INIT;
int ret;
top.u.file = f;
@@ -1990,15 +2047,15 @@ static int do_config_from_file(config_fn_t fn,
top.do_ftell = config_file_ftell;
flockfile(f);
- ret = do_config_from(&top, fn, data, opts);
+ ret = do_config_from(reader, &top, fn, data, opts);
funlockfile(f);
return ret;
}
static int git_config_from_stdin(config_fn_t fn, void *data)
{
- return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin,
- data, NULL);
+ return do_config_from_file(&the_reader, fn, CONFIG_ORIGIN_STDIN, "",
+ NULL, stdin, data, NULL);
}
int git_config_from_file_with_options(config_fn_t fn, const char *filename,
@@ -2012,8 +2069,8 @@ int git_config_from_file_with_options(config_fn_t fn, const char *filename,
BUG("filename cannot be NULL");
f = fopen_or_warn(filename, "r");
if (f) {
- ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename,
- filename, f, data, opts);
+ ret = do_config_from_file(&the_reader, fn, CONFIG_ORIGIN_FILE,
+ filename, filename, f, data, opts);
fclose(f);
}
return ret;
@@ -2029,7 +2086,7 @@ int git_config_from_mem(config_fn_t fn,
const char *name, const char *buf, size_t len,
void *data, const struct config_options *opts)
{
- struct config_source top;
+ struct config_source top = CONFIG_SOURCE_INIT;
top.u.buf.buf = buf;
top.u.buf.len = len;
@@ -2042,7 +2099,7 @@ int git_config_from_mem(config_fn_t fn,
top.do_ungetc = config_buf_ungetc;
top.do_ftell = config_buf_ftell;
- return do_config_from(&top, fn, data, opts);
+ return do_config_from(&the_reader, &top, fn, data, opts);
}
int git_config_from_blob_oid(config_fn_t fn,
@@ -2133,7 +2190,8 @@ int git_config_system(void)
return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
}
-static int do_git_config_sequence(const struct config_options *opts,
+static int do_git_config_sequence(struct config_reader *reader,
+ const struct config_options *opts,
config_fn_t fn, void *data)
{
int ret = 0;
@@ -2141,7 +2199,7 @@ static int do_git_config_sequence(const struct config_options *opts,
char *xdg_config = NULL;
char *user_config = NULL;
char *repo_config;
- enum config_scope prev_parsing_scope = current_parsing_scope;
+ enum config_scope prev_parsing_scope = reader->parsing_scope;
if (opts->commondir)
repo_config = mkpathdup("%s/config", opts->commondir);
@@ -2150,13 +2208,13 @@ static int do_git_config_sequence(const struct config_options *opts,
else
repo_config = NULL;
- current_parsing_scope = CONFIG_SCOPE_SYSTEM;
+ config_reader_set_scope(reader, CONFIG_SCOPE_SYSTEM);
if (git_config_system() && system_config &&
!access_or_die(system_config, R_OK,
opts->system_gently ? ACCESS_EACCES_OK : 0))
ret += git_config_from_file(fn, system_config, data);
- current_parsing_scope = CONFIG_SCOPE_GLOBAL;
+ config_reader_set_scope(reader, CONFIG_SCOPE_GLOBAL);
git_global_config(&user_config, &xdg_config);
if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
@@ -2165,12 +2223,12 @@ static int do_git_config_sequence(const struct config_options *opts,
if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
ret += git_config_from_file(fn, user_config, data);
- current_parsing_scope = CONFIG_SCOPE_LOCAL;
+ config_reader_set_scope(reader, CONFIG_SCOPE_LOCAL);
if (!opts->ignore_repo && repo_config &&
!access_or_die(repo_config, R_OK, 0))
ret += git_config_from_file(fn, repo_config, data);
- current_parsing_scope = CONFIG_SCOPE_WORKTREE;
+ config_reader_set_scope(reader, CONFIG_SCOPE_WORKTREE);
if (!opts->ignore_worktree && repository_format_worktree_config) {
char *path = git_pathdup("config.worktree");
if (!access_or_die(path, R_OK, 0))
@@ -2178,11 +2236,11 @@ static int do_git_config_sequence(const struct config_options *opts,
free(path);
}
- current_parsing_scope = CONFIG_SCOPE_COMMAND;
+ config_reader_set_scope(reader, CONFIG_SCOPE_COMMAND);
if (!opts->ignore_cmdline && git_config_from_parameters(fn, data) < 0)
die(_("unable to parse command-line config"));
- current_parsing_scope = prev_parsing_scope;
+ config_reader_set_scope(reader, prev_parsing_scope);
free(system_config);
free(xdg_config);
free(user_config);
@@ -2195,6 +2253,7 @@ int config_with_options(config_fn_t fn, void *data,
const struct config_options *opts)
{
struct config_include_data inc = CONFIG_INCLUDE_INIT;
+ enum config_scope prev_scope = the_reader.parsing_scope;
int ret;
if (opts->respect_includes) {
@@ -2202,12 +2261,13 @@ int config_with_options(config_fn_t fn, void *data,
inc.data = data;
inc.opts = opts;
inc.config_source = config_source;
+ inc.config_reader = &the_reader;
fn = git_config_include;
data = &inc;
}
if (config_source)
- current_parsing_scope = config_source->scope;
+ config_reader_set_scope(&the_reader, config_source->scope);
/*
* If we have a specific filename, use it. Otherwise, follow the
@@ -2223,36 +2283,38 @@ int config_with_options(config_fn_t fn, void *data,
ret = git_config_from_blob_ref(fn, repo, config_source->blob,
data);
} else {
- ret = do_git_config_sequence(opts, fn, data);
+ ret = do_git_config_sequence(&the_reader, opts, fn, data);
}
if (inc.remote_urls) {
string_list_clear(inc.remote_urls, 0);
FREE_AND_NULL(inc.remote_urls);
}
+ config_reader_set_scope(&the_reader, prev_scope);
return ret;
}
-static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
+static void configset_iter(struct config_reader *reader, struct config_set *set,
+ config_fn_t fn, void *data)
{
int i, value_index;
struct string_list *values;
struct config_set_element *entry;
- struct configset_list *list = &cs->list;
+ struct configset_list *list = &set->list;
for (i = 0; i < list->nr; i++) {
entry = list->items[i].e;
value_index = list->items[i].value_index;
values = &entry->value_list;
- current_config_kvi = values->items[value_index].util;
+ config_reader_set_kvi(reader, values->items[value_index].util);
if (fn(entry->key, values->items[value_index].string, data) < 0)
git_die_config_linenr(entry->key,
- current_config_kvi->filename,
- current_config_kvi->linenr);
+ reader->config_kvi->filename,
+ reader->config_kvi->linenr);
- current_config_kvi = NULL;
+ config_reader_set_kvi(reader, NULL);
}
}
@@ -2303,33 +2365,44 @@ void read_very_early_config(config_fn_t cb, void *data)
config_with_options(cb, data, NULL, &opts);
}
-static struct config_set_element *configset_find_element(struct config_set *cs, const char *key)
+RESULT_MUST_BE_USED
+static int configset_find_element(struct config_set *set, const char *key,
+ struct config_set_element **dest)
{
struct config_set_element k;
struct config_set_element *found_entry;
char *normalized_key;
+ int ret;
+
/*
* `key` may come from the user, so normalize it before using it
* for querying entries from the hashmap.
*/
- if (git_config_parse_key(key, &normalized_key, NULL))
- return NULL;
+ ret = git_config_parse_key(key, &normalized_key, NULL);
+ if (ret)
+ return ret;
hashmap_entry_init(&k.ent, strhash(normalized_key));
k.key = normalized_key;
- found_entry = hashmap_get_entry(&cs->config_hash, &k, ent, NULL);
+ found_entry = hashmap_get_entry(&set->config_hash, &k, ent, NULL);
free(normalized_key);
- return found_entry;
+ *dest = found_entry;
+ return 0;
}
-static int configset_add_value(struct config_set *cs, const char *key, const char *value)
+static int configset_add_value(struct config_reader *reader,
+ struct config_set *set, const char *key,
+ const char *value)
{
struct config_set_element *e;
struct string_list_item *si;
struct configset_list_item *l_item;
struct key_value_info *kv_info = xmalloc(sizeof(*kv_info));
+ int ret;
- e = configset_find_element(cs, key);
+ ret = configset_find_element(set, key, &e);
+ if (ret)
+ return ret;
/*
* Since the keys are being fed by git_config*() callback mechanism, they
* are already normalized. So simply add them without any further munging.
@@ -2339,28 +2412,28 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
hashmap_entry_init(&e->ent, strhash(key));
e->key = xstrdup(key);
string_list_init_dup(&e->value_list);
- hashmap_add(&cs->config_hash, &e->ent);
+ hashmap_add(&set->config_hash, &e->ent);
}
si = string_list_append_nodup(&e->value_list, xstrdup_or_null(value));
- ALLOC_GROW(cs->list.items, cs->list.nr + 1, cs->list.alloc);
- l_item = &cs->list.items[cs->list.nr++];
+ ALLOC_GROW(set->list.items, set->list.nr + 1, set->list.alloc);
+ l_item = &set->list.items[set->list.nr++];
l_item->e = e;
l_item->value_index = e->value_list.nr - 1;
- if (!cf)
+ if (!reader->source)
BUG("configset_add_value has no source");
- if (cf->name) {
- kv_info->filename = strintern(cf->name);
- kv_info->linenr = cf->linenr;
- kv_info->origin_type = cf->origin_type;
+ if (reader->source->name) {
+ kv_info->filename = strintern(reader->source->name);
+ kv_info->linenr = reader->source->linenr;
+ kv_info->origin_type = reader->source->origin_type;
} else {
/* for values read from `git_config_from_parameters()` */
kv_info->filename = NULL;
kv_info->linenr = -1;
kv_info->origin_type = CONFIG_ORIGIN_CMDLINE;
}
- kv_info->scope = current_parsing_scope;
+ kv_info->scope = reader->parsing_scope;
si->util = kv_info;
return 0;
@@ -2379,84 +2452,134 @@ static int config_set_element_cmp(const void *cmp_data UNUSED,
return strcmp(e1->key, e2->key);
}
-void git_configset_init(struct config_set *cs)
+void git_configset_init(struct config_set *set)
{
- hashmap_init(&cs->config_hash, config_set_element_cmp, NULL, 0);
- cs->hash_initialized = 1;
- cs->list.nr = 0;
- cs->list.alloc = 0;
- cs->list.items = NULL;
+ hashmap_init(&set->config_hash, config_set_element_cmp, NULL, 0);
+ set->hash_initialized = 1;
+ set->list.nr = 0;
+ set->list.alloc = 0;
+ set->list.items = NULL;
}
-void git_configset_clear(struct config_set *cs)
+void git_configset_clear(struct config_set *set)
{
struct config_set_element *entry;
struct hashmap_iter iter;
- if (!cs->hash_initialized)
+ if (!set->hash_initialized)
return;
- hashmap_for_each_entry(&cs->config_hash, &iter, entry,
+ hashmap_for_each_entry(&set->config_hash, &iter, entry,
ent /* member name */) {
free(entry->key);
string_list_clear(&entry->value_list, 1);
}
- hashmap_clear_and_free(&cs->config_hash, struct config_set_element, ent);
- cs->hash_initialized = 0;
- free(cs->list.items);
- cs->list.nr = 0;
- cs->list.alloc = 0;
- cs->list.items = NULL;
+ hashmap_clear_and_free(&set->config_hash, struct config_set_element, ent);
+ set->hash_initialized = 0;
+ free(set->list.items);
+ set->list.nr = 0;
+ set->list.alloc = 0;
+ set->list.items = NULL;
}
+struct configset_add_data {
+ struct config_set *config_set;
+ struct config_reader *config_reader;
+};
+#define CONFIGSET_ADD_INIT { 0 }
+
static int config_set_callback(const char *key, const char *value, void *cb)
{
- struct config_set *cs = cb;
- configset_add_value(cs, key, value);
+ struct configset_add_data *data = cb;
+ configset_add_value(data->config_reader, data->config_set, key, value);
return 0;
}
-int git_configset_add_file(struct config_set *cs, const char *filename)
+int git_configset_add_file(struct config_set *set, const char *filename)
{
- return git_config_from_file(config_set_callback, filename, cs);
+ struct configset_add_data data = CONFIGSET_ADD_INIT;
+ data.config_reader = &the_reader;
+ data.config_set = set;
+ return git_config_from_file(config_set_callback, filename, &data);
}
-int git_configset_get_value(struct config_set *cs, const char *key, const char **value)
+int git_configset_get_value(struct config_set *set, const char *key, const char **value)
{
const struct string_list *values = NULL;
+ int ret;
+
/*
* Follows "last one wins" semantic, i.e., if there are multiple matches for the
* queried key in the files of the configset, the value returned will be the last
* value in the value list for that key.
*/
- values = git_configset_get_value_multi(cs, key);
+ if ((ret = git_configset_get_value_multi(set, key, &values)))
+ return ret;
- if (!values)
- return 1;
assert(values->nr > 0);
*value = values->items[values->nr - 1].string;
return 0;
}
-const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key)
+int git_configset_get_value_multi(struct config_set *set, const char *key,
+ const struct string_list **dest)
{
- struct config_set_element *e = configset_find_element(cs, key);
- return e ? &e->value_list : NULL;
+ struct config_set_element *e;
+ int ret;
+
+ if ((ret = configset_find_element(set, key, &e)))
+ return ret;
+ else if (!e)
+ return 1;
+ *dest = &e->value_list;
+
+ return 0;
}
-int git_configset_get_string(struct config_set *cs, const char *key, char **dest)
+static int check_multi_string(struct string_list_item *item, void *util)
+{
+ return item->string ? 0 : config_error_nonbool(util);
+}
+
+int git_configset_get_string_multi(struct config_set *cs, const char *key,
+ const struct string_list **dest)
+{
+ int ret;
+
+ if ((ret = git_configset_get_value_multi(cs, key, dest)))
+ return ret;
+ if ((ret = for_each_string_list((struct string_list *)*dest,
+ check_multi_string, (void *)key)))
+ return ret;
+
+ return 0;
+}
+
+int git_configset_get(struct config_set *set, const char *key)
+{
+ struct config_set_element *e;
+ int ret;
+
+ if ((ret = configset_find_element(set, key, &e)))
+ return ret;
+ else if (!e)
+ return 1;
+ return 0;
+}
+
+int git_configset_get_string(struct config_set *set, const char *key, char **dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value))
+ if (!git_configset_get_value(set, key, &value))
return git_config_string((const char **)dest, key, value);
else
return 1;
}
-static int git_configset_get_string_tmp(struct config_set *cs, const char *key,
+static int git_configset_get_string_tmp(struct config_set *set, const char *key,
const char **dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
if (!value)
return config_error_nonbool(key);
*dest = value;
@@ -2466,51 +2589,51 @@ static int git_configset_get_string_tmp(struct config_set *cs, const char *key,
}
}
-int git_configset_get_int(struct config_set *cs, const char *key, int *dest)
+int git_configset_get_int(struct config_set *set, const char *key, int *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_config_int(key, value);
return 0;
} else
return 1;
}
-int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest)
+int git_configset_get_ulong(struct config_set *set, const char *key, unsigned long *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_config_ulong(key, value);
return 0;
} else
return 1;
}
-int git_configset_get_bool(struct config_set *cs, const char *key, int *dest)
+int git_configset_get_bool(struct config_set *set, const char *key, int *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_config_bool(key, value);
return 0;
} else
return 1;
}
-int git_configset_get_bool_or_int(struct config_set *cs, const char *key,
+int git_configset_get_bool_or_int(struct config_set *set, const char *key,
int *is_bool, int *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_config_bool_or_int(key, value, is_bool);
return 0;
} else
return 1;
}
-int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest)
+int git_configset_get_maybe_bool(struct config_set *set, const char *key, int *dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value)) {
+ if (!git_configset_get_value(set, key, &value)) {
*dest = git_parse_maybe_bool(value);
if (*dest == -1)
return -1;
@@ -2519,10 +2642,10 @@ int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *de
return 1;
}
-int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest)
+int git_configset_get_pathname(struct config_set *set, const char *key, const char **dest)
{
const char *value;
- if (!git_configset_get_value(cs, key, &value))
+ if (!git_configset_get_value(set, key, &value))
return git_config_pathname(dest, key, value);
else
return 1;
@@ -2532,6 +2655,7 @@ int git_configset_get_pathname(struct config_set *cs, const char *key, const cha
static void repo_read_config(struct repository *repo)
{
struct config_options opts = { 0 };
+ struct configset_add_data data = CONFIGSET_ADD_INIT;
opts.respect_includes = 1;
opts.commondir = repo->commondir;
@@ -2543,8 +2667,10 @@ static void repo_read_config(struct repository *repo)
git_configset_clear(repo->config);
git_configset_init(repo->config);
+ data.config_set = repo->config;
+ data.config_reader = &the_reader;
- if (config_with_options(config_set_callback, repo->config, NULL, &opts) < 0)
+ if (config_with_options(config_set_callback, &data, NULL, &opts) < 0)
/*
* config_with_options() normally returns only
* zero, as most errors are fatal, and
@@ -2576,7 +2702,13 @@ static void repo_config_clear(struct repository *repo)
void repo_config(struct repository *repo, config_fn_t fn, void *data)
{
git_config_check_init(repo);
- configset_iter(repo->config, fn, data);
+ configset_iter(&the_reader, repo->config, fn, data);
+}
+
+int repo_config_get(struct repository *repo, const char *key)
+{
+ git_config_check_init(repo);
+ return git_configset_get(repo->config, key);
}
int repo_config_get_value(struct repository *repo,
@@ -2586,11 +2718,18 @@ int repo_config_get_value(struct repository *repo,
return git_configset_get_value(repo->config, key, value);
}
-const struct string_list *repo_config_get_value_multi(struct repository *repo,
- const char *key)
+int repo_config_get_value_multi(struct repository *repo, const char *key,
+ const struct string_list **dest)
{
git_config_check_init(repo);
- return git_configset_get_value_multi(repo->config, key);
+ return git_configset_get_value_multi(repo->config, key, dest);
+}
+
+int repo_config_get_string_multi(struct repository *repo, const char *key,
+ const struct string_list **dest)
+{
+ git_config_check_init(repo);
+ return git_configset_get_string_multi(repo->config, key, dest);
}
int repo_config_get_string(struct repository *repo,
@@ -2670,16 +2809,19 @@ static void read_protected_config(void)
.ignore_worktree = 1,
.system_gently = 1,
};
+ struct configset_add_data data = CONFIGSET_ADD_INIT;
+
git_configset_init(&protected_config);
- config_with_options(config_set_callback, &protected_config,
- NULL, &opts);
+ data.config_set = &protected_config;
+ data.config_reader = &the_reader;
+ config_with_options(config_set_callback, &data, NULL, &opts);
}
void git_protected_config(config_fn_t fn, void *data)
{
if (!protected_config.hash_initialized)
read_protected_config();
- configset_iter(&protected_config, fn, data);
+ configset_iter(&the_reader, &protected_config, fn, data);
}
/* Functions used historically to read configuration from 'the_repository' */
@@ -2693,14 +2835,25 @@ void git_config_clear(void)
repo_config_clear(the_repository);
}
+int git_config_get(const char *key)
+{
+ return repo_config_get(the_repository, key);
+}
+
int git_config_get_value(const char *key, const char **value)
{
return repo_config_get_value(the_repository, key, value);
}
-const struct string_list *git_config_get_value_multi(const char *key)
+int git_config_get_value_multi(const char *key, const struct string_list **dest)
+{
+ return repo_config_get_value_multi(the_repository, key, dest);
+}
+
+int git_config_get_string_multi(const char *key,
+ const struct string_list **dest)
{
- return repo_config_get_value_multi(the_repository, key);
+ return repo_config_get_string_multi(the_repository, key, dest);
}
int git_config_get_string(const char *key, char **dest)
@@ -2847,7 +3000,8 @@ void git_die_config(const char *key, const char *err, ...)
error_fn(err, params);
va_end(params);
}
- values = git_config_get_value_multi(key);
+ if (git_config_get_value_multi(key, &values))
+ BUG("for key '%s' we must have a value to report on", key);
kv_info = values->items[values->nr - 1].util;
git_die_config_linenr(key, kv_info->filename, kv_info->linenr);
}
@@ -2857,6 +3011,7 @@ void git_die_config(const char *key, const char *err, ...)
*/
struct config_store_data {
+ struct config_reader *config_reader;
size_t baselen;
char *key;
int do_not_match;
@@ -2871,6 +3026,7 @@ struct config_store_data {
unsigned int parsed_nr, parsed_alloc, *seen, seen_nr, seen_alloc;
unsigned int key_seen:1, section_seen:1, is_keys_section:1;
};
+#define CONFIG_STORE_INIT { 0 }
static void config_store_data_clear(struct config_store_data *store)
{
@@ -2905,6 +3061,7 @@ static int store_aux_event(enum config_event_t type,
size_t begin, size_t end, void *data)
{
struct config_store_data *store = data;
+ struct config_source *cs = store->config_reader->source;
ALLOC_GROW(store->parsed, store->parsed_nr + 1, store->parsed_alloc);
store->parsed[store->parsed_nr].begin = begin;
@@ -2914,10 +3071,10 @@ static int store_aux_event(enum config_event_t type,
if (type == CONFIG_EVENT_SECTION) {
int (*cmpfn)(const char *, const char *, size_t);
- if (cf->var.len < 2 || cf->var.buf[cf->var.len - 1] != '.')
- return error(_("invalid section name '%s'"), cf->var.buf);
+ if (cs->var.len < 2 || cs->var.buf[cs->var.len - 1] != '.')
+ return error(_("invalid section name '%s'"), cs->var.buf);
- if (cf->subsection_case_sensitive)
+ if (cs->subsection_case_sensitive)
cmpfn = strncasecmp;
else
cmpfn = strncmp;
@@ -2925,8 +3082,8 @@ static int store_aux_event(enum config_event_t type,
/* Is this the section we were looking for? */
store->is_keys_section =
store->parsed[store->parsed_nr].is_keys_section =
- cf->var.len - 1 == store->baselen &&
- !cmpfn(cf->var.buf, store->key, store->baselen);
+ cs->var.len - 1 == store->baselen &&
+ !cmpfn(cs->var.buf, store->key, store->baselen);
if (store->is_keys_section) {
store->section_seen = 1;
ALLOC_GROW(store->seen, store->seen_nr + 1,
@@ -3222,9 +3379,9 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
char *filename_buf = NULL;
char *contents = NULL;
size_t contents_sz;
- struct config_store_data store;
+ struct config_store_data store = CONFIG_STORE_INIT;
- memset(&store, 0, sizeof(store));
+ store.config_reader = &the_reader;
/* parse-key returns negative; flip the sign to feed exit(3) */
ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
@@ -3501,9 +3658,10 @@ void git_config_set_multivar(const char *key, const char *value,
flags);
}
-static int section_name_match (const char *buf, const char *name)
+static size_t section_name_match (const char *buf, const char *name)
{
- int i = 0, j = 0, dot = 0;
+ size_t i = 0, j = 0;
+ int dot = 0;
if (buf[i] != '[')
return 0;
for (i = 1; buf[i] && buf[i] != ']'; i++) {
@@ -3556,6 +3714,8 @@ static int section_name_is_ok(const char *name)
return 1;
}
+#define GIT_CONFIG_MAX_LINE_LEN (512 * 1024)
+
/* if new_name == NULL, the section is removed instead */
static int git_config_copy_or_rename_section_in_file(const char *config_filename,
const char *old_name,
@@ -3565,11 +3725,12 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
char *filename_buf = NULL;
struct lock_file lock = LOCK_INIT;
int out_fd;
- char buf[1024];
+ struct strbuf buf = STRBUF_INIT;
FILE *config_file = NULL;
struct stat st;
struct strbuf copystr = STRBUF_INIT;
struct config_store_data store;
+ uint32_t line_nr = 0;
memset(&store, 0, sizeof(store));
@@ -3606,16 +3767,25 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
goto out;
}
- while (fgets(buf, sizeof(buf), config_file)) {
- unsigned i;
- int length;
+ while (!strbuf_getwholeline(&buf, config_file, '\n')) {
+ size_t i, length;
int is_section = 0;
- char *output = buf;
- for (i = 0; buf[i] && isspace(buf[i]); i++)
+ char *output = buf.buf;
+
+ line_nr++;
+
+ if (buf.len >= GIT_CONFIG_MAX_LINE_LEN) {
+ ret = error(_("refusing to work with overly long line "
+ "in '%s' on line %"PRIuMAX),
+ config_filename, (uintmax_t)line_nr);
+ goto out;
+ }
+
+ for (i = 0; buf.buf[i] && isspace(buf.buf[i]); i++)
; /* do nothing */
- if (buf[i] == '[') {
+ if (buf.buf[i] == '[') {
/* it's a section */
- int offset;
+ size_t offset;
is_section = 1;
/*
@@ -3632,7 +3802,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
strbuf_reset(&copystr);
}
- offset = section_name_match(&buf[i], old_name);
+ offset = section_name_match(&buf.buf[i], old_name);
if (offset > 0) {
ret++;
if (!new_name) {
@@ -3707,6 +3877,7 @@ out:
out_no_rollback:
free(filename_buf);
config_store_data_clear(&store);
+ strbuf_release(&buf);
return ret;
}
@@ -3780,14 +3951,23 @@ int parse_config_key(const char *var,
return 0;
}
-const char *current_config_origin_type(void)
+static int reader_origin_type(struct config_reader *reader,
+ enum config_origin_type *type)
{
- int type;
- if (current_config_kvi)
- type = current_config_kvi->origin_type;
- else if(cf)
- type = cf->origin_type;
+ if (the_reader.config_kvi)
+ *type = reader->config_kvi->origin_type;
+ else if(the_reader.source)
+ *type = reader->source->origin_type;
else
+ return 1;
+ return 0;
+}
+
+const char *current_config_origin_type(void)
+{
+ enum config_origin_type type = CONFIG_ORIGIN_UNKNOWN;
+
+ if (reader_origin_type(&the_reader, &type))
BUG("current_config_origin_type called outside config callback");
switch (type) {
@@ -3826,32 +4006,39 @@ const char *config_scope_name(enum config_scope scope)
}
}
+static int reader_config_name(struct config_reader *reader, const char **out)
+{
+ if (the_reader.config_kvi)
+ *out = reader->config_kvi->filename;
+ else if (the_reader.source)
+ *out = reader->source->name;
+ else
+ return 1;
+ return 0;
+}
+
const char *current_config_name(void)
{
const char *name;
- if (current_config_kvi)
- name = current_config_kvi->filename;
- else if (cf)
- name = cf->name;
- else
+ if (reader_config_name(&the_reader, &name))
BUG("current_config_name called outside config callback");
return name ? name : "";
}
enum config_scope current_config_scope(void)
{
- if (current_config_kvi)
- return current_config_kvi->scope;
+ if (the_reader.config_kvi)
+ return the_reader.config_kvi->scope;
else
- return current_parsing_scope;
+ return the_reader.parsing_scope;
}
int current_config_line(void)
{
- if (current_config_kvi)
- return current_config_kvi->linenr;
+ if (the_reader.config_kvi)
+ return the_reader.config_kvi->linenr;
else
- return cf->linenr;
+ return the_reader.source->linenr;
}
int lookup_config(const char **mapping, int nr_mapping, const char *var)
diff --git a/config.h b/config.h
index 7606246531..247b572b37 100644
--- a/config.h
+++ b/config.h
@@ -56,6 +56,7 @@ struct git_config_source {
};
enum config_origin_type {
+ CONFIG_ORIGIN_UNKNOWN = 0,
CONFIG_ORIGIN_BLOB,
CONFIG_ORIGIN_FILE,
CONFIG_ORIGIN_STDIN,
@@ -450,10 +451,31 @@ int git_configset_add_file(struct config_set *cs, const char *filename);
/**
* Finds and returns the value list, sorted in order of increasing priority
* for the configuration variable `key` and config set `cs`. When the
- * configuration variable `key` is not found, returns NULL. The caller
- * should not free or modify the returned pointer, as it is owned by the cache.
+ * configuration variable `key` is not found, returns 1 without touching
+ * `value`.
+ *
+ * The key will be parsed for validity with git_config_parse_key(), on
+ * error a negative value will be returned.
+ *
+ * The caller should not free or modify the returned pointer, as it is
+ * owned by the cache.
*/
-const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key);
+RESULT_MUST_BE_USED
+int git_configset_get_value_multi(struct config_set *cs, const char *key,
+ const struct string_list **dest);
+
+/**
+ * A validation wrapper for git_configset_get_value_multi() which does
+ * for it what git_configset_get_string() does for
+ * git_configset_get_value().
+ *
+ * The configuration syntax allows for "[section] key", which will
+ * give us a NULL entry in the "struct string_list", as opposed to
+ * "[section] key =" which is the empty string. Most users of the API
+ * are not prepared to handle NULL in a "struct string_list".
+ */
+int git_configset_get_string_multi(struct config_set *cs, const char *key,
+ const struct string_list **dest);
/**
* Clears `config_set` structure, removes all saved variable-value pairs.
@@ -465,6 +487,13 @@ void git_configset_clear(struct config_set *cs);
* value in the 'dest' pointer.
*/
+/**
+ * git_configset_get() returns negative values on error, see
+ * repo_config_get() below.
+ */
+RESULT_MUST_BE_USED
+int git_configset_get(struct config_set *cs, const char *key);
+
/*
* Finds the highest-priority value for the configuration variable `key`
* and config set `cs`, stores the pointer to it in `value` and returns 0.
@@ -485,10 +514,22 @@ int git_configset_get_pathname(struct config_set *cs, const char *key, const cha
/* Functions for reading a repository's config */
struct repository;
void repo_config(struct repository *repo, config_fn_t fn, void *data);
+
+/**
+ * Run only the discover part of the repo_config_get_*() functions
+ * below, in addition to 1 if not found, returns negative values on
+ * error (e.g. if the key itself is invalid).
+ */
+RESULT_MUST_BE_USED
+int repo_config_get(struct repository *repo, const char *key);
int repo_config_get_value(struct repository *repo,
const char *key, const char **value);
-const struct string_list *repo_config_get_value_multi(struct repository *repo,
- const char *key);
+RESULT_MUST_BE_USED
+int repo_config_get_value_multi(struct repository *repo, const char *key,
+ const struct string_list **dest);
+RESULT_MUST_BE_USED
+int repo_config_get_string_multi(struct repository *repo, const char *key,
+ const struct string_list **dest);
int repo_config_get_string(struct repository *repo,
const char *key, char **dest);
int repo_config_get_string_tmp(struct repository *repo,
@@ -521,8 +562,15 @@ void git_protected_config(config_fn_t fn, void *data);
* manner, the config API provides two functions `git_config_get_value`
* and `git_config_get_value_multi`. They both read values from an internal
* cache generated previously from reading the config files.
+ *
+ * For those git_config_get*() functions that aren't documented,
+ * consult the corresponding repo_config_get*() function's
+ * documentation.
*/
+RESULT_MUST_BE_USED
+int git_config_get(const char *key);
+
/**
* Finds the highest-priority value for the configuration variable `key`,
* stores the pointer to it in `value` and returns 0. When the
@@ -535,10 +583,17 @@ int git_config_get_value(const char *key, const char **value);
/**
* Finds and returns the value list, sorted in order of increasing priority
* for the configuration variable `key`. When the configuration variable
- * `key` is not found, returns NULL. The caller should not free or modify
- * the returned pointer, as it is owned by the cache.
- */
-const struct string_list *git_config_get_value_multi(const char *key);
+ * `key` is not found, returns 1 without touching `value`.
+ *
+ * The caller should not free or modify the returned pointer, as it is
+ * owned by the cache.
+ */
+RESULT_MUST_BE_USED
+int git_config_get_value_multi(const char *key,
+ const struct string_list **dest);
+RESULT_MUST_BE_USED
+int git_config_get_string_multi(const char *key,
+ const struct string_list **dest);
/**
* Resets and invalidates the config cache.
diff --git a/connect.c b/connect.c
index 5d8036197d..fd3179e545 100644
--- a/connect.c
+++ b/connect.c
@@ -1410,6 +1410,7 @@ static void fill_ssh_args(struct child_process *conn, const char *ssh_host,
* the connection failed).
*/
struct child_process *git_connect(int fd[2], const char *url,
+ const char *name,
const char *prog, int flags)
{
char *hostandport, *path;
@@ -1419,10 +1420,11 @@ struct child_process *git_connect(int fd[2], const char *url,
/*
* NEEDSWORK: If we are trying to use protocol v2 and we are planning
- * to perform a push, then fallback to v0 since the client doesn't know
- * how to push yet using v2.
+ * to perform any operation that doesn't involve upload-pack (i.e., a
+ * fetch, ls-remote, etc), then fallback to v0 since we don't know how
+ * to do anything else (like push or remote archive) via v2.
*/
- if (version == protocol_v2 && !strcmp("git-receive-pack", prog))
+ if (version == protocol_v2 && strcmp("git-upload-pack", name))
version = protocol_v0;
/* Without this we cannot rely on waitpid() to tell
diff --git a/connect.h b/connect.h
index b26f7de784..f41a0b4c1f 100644
--- a/connect.h
+++ b/connect.h
@@ -7,7 +7,7 @@
#define CONNECT_DIAG_URL (1u << 1)
#define CONNECT_IPV4 (1u << 2)
#define CONNECT_IPV6 (1u << 3)
-struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
+struct child_process *git_connect(int fd[2], const char *url, const char *name, const char *prog, int flags);
int finish_connect(struct child_process *conn);
int git_connection_is_socket(struct child_process *conn);
int server_supports(const char *feature);
diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
index 57972c2845..76ee4ab1e5 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -100,9 +100,7 @@
#
# If you would like a colored hint about the current dirty state, set
# GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on
-# the colored output of "git status -sb" and are available only when
-# using __git_ps1 for PROMPT_COMMAND or precmd in Bash,
-# but always available in Zsh.
+# the colored output of "git status -sb".
#
# If you would like __git_ps1 to do nothing in the case when the current
# directory is set up to be ignored by git, then set
@@ -259,12 +257,12 @@ __git_ps1_colorize_gitstring ()
local c_lblue='%F{blue}'
local c_clear='%f'
else
- # Using \[ and \] around colors is necessary to prevent
+ # Using \001 and \002 around colors is necessary to prevent
# issues with command line editing/browsing/completion!
- local c_red='\[\e[31m\]'
- local c_green='\[\e[32m\]'
- local c_lblue='\[\e[1;34m\]'
- local c_clear='\[\e[0m\]'
+ local c_red=$'\001\e[31m\002'
+ local c_green=$'\001\e[32m\002'
+ local c_lblue=$'\001\e[1;34m\002'
+ local c_clear=$'\001\e[0m\002'
fi
local bad_color=$c_red
local ok_color=$c_green
@@ -574,11 +572,8 @@ __git_ps1 ()
b="\${__git_ps1_branch_name}"
fi
- # NO color option unless in PROMPT_COMMAND mode or it's Zsh
if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
- if [ $pcmode = yes ] || [ -n "${ZSH_VERSION-}" ]; then
- __git_ps1_colorize_gitstring
- fi
+ __git_ps1_colorize_gitstring
fi
local f="$h$w$i$s$u$p"
diff --git a/contrib/subtree/t/Makefile b/contrib/subtree/t/Makefile
index 4655e0987b..093399c788 100644
--- a/contrib/subtree/t/Makefile
+++ b/contrib/subtree/t/Makefile
@@ -74,9 +74,7 @@ aggregate-results-and-cleanup: $(T)
$(MAKE) clean
aggregate-results:
- for f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \
- echo "$$f"; \
- done | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh
+ @'$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh '$(TEST_RESULTS_DIRECTORY_SQ)'
valgrind:
$(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind"
diff --git a/date.c b/date.c
index e944c8905a..e867ebf6b7 100644
--- a/date.c
+++ b/date.c
@@ -1367,20 +1367,6 @@ static timestamp_t approxidate_str(const char *date,
return (timestamp_t)update_tm(&tm, &now, 0);
}
-timestamp_t approxidate_relative(const char *date)
-{
- struct timeval tv;
- timestamp_t timestamp;
- int offset;
- int errors = 0;
-
- if (!parse_date_basic(date, &timestamp, &offset))
- return timestamp;
-
- get_time(&tv);
- return approxidate_str(date, (const struct timeval *) &tv, &errors);
-}
-
timestamp_t approxidate_careful(const char *date, int *error_ret)
{
struct timeval tv;
diff --git a/date.h b/date.h
index 5d4eaba0a9..6136212a19 100644
--- a/date.h
+++ b/date.h
@@ -68,7 +68,6 @@ int parse_expiry_date(const char *date, timestamp_t *timestamp);
void datestamp(struct strbuf *out);
#define approxidate(s) approxidate_careful((s), NULL)
timestamp_t approxidate_careful(const char *, int *);
-timestamp_t approxidate_relative(const char *date);
int date_overflows(timestamp_t date);
time_t tm_to_time_t(const struct tm *tm);
#endif
diff --git a/diagnose.c b/diagnose.c
index f9a2496c7f..f1aebf0b50 100644
--- a/diagnose.c
+++ b/diagnose.c
@@ -9,6 +9,7 @@
#include "strvec.h"
#include "object-store.h"
#include "packfile.h"
+#include "parse-options.h"
#include "write-or-die.h"
struct archive_dir {
diff --git a/diagnose.h b/diagnose.h
index 7a4951a786..f525219ab0 100644
--- a/diagnose.h
+++ b/diagnose.h
@@ -2,7 +2,8 @@
#define DIAGNOSE_H
#include "strbuf.h"
-#include "parse-options.h"
+
+struct option;
enum diagnose_mode {
DIAGNOSE_NONE,
diff --git a/diff.c b/diff.c
index fa86d02319..067846b13c 100644
--- a/diff.c
+++ b/diff.c
@@ -3386,6 +3386,17 @@ void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const
options->b_prefix = b;
}
+void diff_set_noprefix(struct diff_options *options)
+{
+ options->a_prefix = options->b_prefix = "";
+}
+
+void diff_set_default_prefix(struct diff_options *options)
+{
+ options->a_prefix = "a/";
+ options->b_prefix = "b/";
+}
+
struct userdiff_driver *get_textconv(struct repository *r,
struct diff_filespec *one)
{
@@ -4686,10 +4697,9 @@ void repo_diff_setup(struct repository *r, struct diff_options *options)
options->flags.ignore_untracked_in_submodules = 1;
if (diff_no_prefix) {
- options->a_prefix = options->b_prefix = "";
+ diff_set_noprefix(options);
} else if (!diff_mnemonic_prefix) {
- options->a_prefix = "a/";
- options->b_prefix = "b/";
+ diff_set_default_prefix(options);
}
options->color_moved = diff_color_moved_default;
@@ -5273,8 +5283,18 @@ static int diff_opt_no_prefix(const struct option *opt,
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(optarg);
- options->a_prefix = "";
- options->b_prefix = "";
+ diff_set_noprefix(options);
+ return 0;
+}
+
+static int diff_opt_default_prefix(const struct option *opt,
+ const char *optarg, int unset)
+{
+ struct diff_options *options = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+ BUG_ON_OPT_ARG(optarg);
+ diff_set_default_prefix(options);
return 0;
}
@@ -5567,6 +5587,9 @@ struct option *add_diff_options(const struct option *opts,
OPT_CALLBACK_F(0, "no-prefix", options, NULL,
N_("do not show any source or destination prefix"),
PARSE_OPT_NONEG | PARSE_OPT_NOARG, diff_opt_no_prefix),
+ OPT_CALLBACK_F(0, "default-prefix", options, NULL,
+ N_("use default prefixes a/ and b/"),
+ PARSE_OPT_NONEG | PARSE_OPT_NOARG, diff_opt_default_prefix),
OPT_INTEGER_F(0, "inter-hunk-context", &options->interhunkcontext,
N_("show context between diff hunks up to the specified number of lines"),
PARSE_OPT_NONEG),
diff --git a/diff.h b/diff.h
index 53aeb02a54..6a0737b9c3 100644
--- a/diff.h
+++ b/diff.h
@@ -496,6 +496,8 @@ void diff_tree_combined(const struct object_id *oid, const struct oid_array *par
void diff_tree_combined_merge(const struct commit *commit, struct rev_info *rev);
void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b);
+void diff_set_noprefix(struct diff_options *options);
+void diff_set_default_prefix(struct diff_options *options);
int diff_can_quit_early(struct diff_options *);
diff --git a/fsmonitor.h b/fsmonitor.h
index 67faf592e1..c67e0ebc09 100644
--- a/fsmonitor.h
+++ b/fsmonitor.h
@@ -87,7 +87,7 @@ static inline void mark_fsmonitor_valid(struct index_state *istate, struct cache
!(ce->ce_flags & CE_FSMONITOR_VALID)) {
if (S_ISGITLINK(ce->ce_mode))
return;
- istate->cache_changed = 1;
+ istate->cache_changed |= FSMONITOR_CHANGED;
ce->ce_flags |= CE_FSMONITOR_VALID;
trace_printf_key(&trace_fsmonitor, "mark_fsmonitor_clean '%s'", ce->name);
}
diff --git a/gettext.c b/gettext.c
index 5f34870830..f27e94407b 100644
--- a/gettext.c
+++ b/gettext.c
@@ -102,6 +102,8 @@ static void init_gettext_charset(const char *domain)
setlocale(LC_CTYPE, "C");
}
+int git_gettext_enabled = 0;
+
void git_setup_gettext(void)
{
const char *podir = getenv(GIT_TEXT_DOMAIN_DIR_ENVIRONMENT);
@@ -121,6 +123,8 @@ void git_setup_gettext(void)
init_gettext_charset("git");
textdomain("git");
+ git_gettext_enabled = 1;
+
free(p);
}
diff --git a/gettext.h b/gettext.h
index d209911ebb..484cafa562 100644
--- a/gettext.h
+++ b/gettext.h
@@ -29,9 +29,11 @@
#define FORMAT_PRESERVING(n) __attribute__((format_arg(n)))
#ifndef NO_GETTEXT
+extern int git_gettext_enabled;
void git_setup_gettext(void);
int gettext_width(const char *s);
#else
+#define git_gettext_enabled (0)
static inline void git_setup_gettext(void)
{
}
@@ -45,12 +47,16 @@ static inline FORMAT_PRESERVING(1) const char *_(const char *msgid)
{
if (!*msgid)
return "";
+ if (!git_gettext_enabled)
+ return msgid;
return gettext(msgid);
}
static inline FORMAT_PRESERVING(1) FORMAT_PRESERVING(2)
const char *Q_(const char *msgid, const char *plu, unsigned long n)
{
+ if (!git_gettext_enabled)
+ return n == 1 ? msgid : plu;
return ngettext(msgid, plu, n);
}
diff --git a/git-compat-util.h b/git-compat-util.h
index 4fb771f42a..5b2b99c17c 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -339,6 +339,25 @@ static inline const char *precompose_string_if_needed(const char *in)
int compat_mkdir_wo_trailing_slash(const char*, mode_t);
#endif
+#ifdef time
+#undef time
+#endif
+static inline time_t git_time(time_t *tloc)
+{
+ struct timeval tv;
+
+ /*
+ * Avoid time(NULL), which can disagree with gettimeofday(2)
+ * and filesystem timestamps.
+ */
+ gettimeofday(&tv, NULL);
+
+ if (tloc)
+ *tloc = tv.tv_sec;
+ return tv.tv_sec;
+}
+#define time git_time
+
#ifdef NO_STRUCT_ITIMERVAL
struct itimerval {
struct timeval it_interval;
diff --git a/git-difftool--helper.sh b/git-difftool--helper.sh
index 992124cc67..e4e820e680 100755
--- a/git-difftool--helper.sh
+++ b/git-difftool--helper.sh
@@ -75,6 +75,11 @@ then
merge_tool="$GIT_DIFF_TOOL"
else
merge_tool="$(get_merge_tool)"
+ subshell_exit_status=$?
+ if test $subshell_exit_status -gt 1
+ then
+ exit $subshell_exit_status
+ fi
fi
fi
diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh
index 9f99201bcc..1ff26170ff 100644
--- a/git-mergetool--lib.sh
+++ b/git-mergetool--lib.sh
@@ -97,7 +97,42 @@ merge_mode () {
test "$TOOL_MODE" = merge
}
+get_gui_default () {
+ if diff_mode
+ then
+ GUI_DEFAULT_KEY="difftool.guiDefault"
+ else
+ GUI_DEFAULT_KEY="mergetool.guiDefault"
+ fi
+ GUI_DEFAULT_CONFIG_LCASE=$(git config --default false --get "$GUI_DEFAULT_KEY" | tr 'A-Z' 'a-z')
+ if test "$GUI_DEFAULT_CONFIG_LCASE" = "auto"
+ then
+ if test -n "$DISPLAY"
+ then
+ GUI_DEFAULT=true
+ else
+ GUI_DEFAULT=false
+ fi
+ else
+ GUI_DEFAULT=$(git config --default false --bool --get "$GUI_DEFAULT_KEY")
+ subshell_exit_status=$?
+ if test $subshell_exit_status -ne 0
+ then
+ exit $subshell_exit_status
+ fi
+ fi
+ echo $GUI_DEFAULT
+}
+
gui_mode () {
+ if test -z "$GIT_MERGETOOL_GUI"
+ then
+ GIT_MERGETOOL_GUI=$(get_gui_default)
+ if test $? -ne 0
+ then
+ exit 2
+ fi
+ fi
test "$GIT_MERGETOOL_GUI" = true
}
@@ -467,6 +502,11 @@ get_merge_tool () {
is_guessed=false
# Check if a merge tool has been configured
merge_tool=$(get_configured_merge_tool)
+ subshell_exit_status=$?
+ if test $subshell_exit_status -gt "1"
+ then
+ exit $subshell_exit_status
+ fi
# Try to guess an appropriate merge tool if no tool has been set.
if test -z "$merge_tool"
then
diff --git a/git-mergetool.sh b/git-mergetool.sh
index f751d9cfe2..8a922893f7 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -451,7 +451,7 @@ print_noop_and_exit () {
main () {
prompt=$(git config --bool mergetool.prompt)
- GIT_MERGETOOL_GUI=false
+ GIT_MERGETOOL_GUI=
guessed_merge_tool=false
orderfile=
@@ -511,9 +511,14 @@ main () {
if test -z "$merge_tool"
then
- if ! merge_tool=$(get_merge_tool)
+ merge_tool=$(get_merge_tool)
+ subshell_exit_status=$?
+ if test $subshell_exit_status = 1
then
guessed_merge_tool=true
+ elif test $subshell_exit_status -gt 1
+ then
+ exit $subshell_exit_status
fi
fi
merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)"
diff --git a/git-send-email.perl b/git-send-email.perl
index 07f2a0cbea..fd8cd0d46f 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -1537,7 +1537,7 @@ sub send_message {
To: $to${ccline}
Subject: $subject
Date: $date
-Message-Id: $message_id
+Message-ID: $message_id
";
if ($use_xmailer) {
$header .= "X-Mailer: git-send-email $gitversion\n";
@@ -1832,7 +1832,7 @@ sub process_file {
$has_mime_version = 1;
push @xh, $_;
}
- elsif (/^Message-Id: (.*)/i) {
+ elsif (/^Message-ID: (.*)/i) {
$message_id = $1;
}
elsif (/^Content-Transfer-Encoding: (.*)/i) {
diff --git a/git.c b/git.c
index 0241d2cf91..45899be826 100644
--- a/git.c
+++ b/git.c
@@ -590,7 +590,7 @@ static struct cmd_struct commands[] = {
{ "show-branch", cmd_show_branch, RUN_SETUP },
{ "show-index", cmd_show_index, RUN_SETUP_GENTLY },
{ "show-ref", cmd_show_ref, RUN_SETUP },
- { "sparse-checkout", cmd_sparse_checkout, RUN_SETUP | NEED_WORK_TREE },
+ { "sparse-checkout", cmd_sparse_checkout, RUN_SETUP },
{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
{ "stash", cmd_stash, RUN_SETUP | NEED_WORK_TREE },
{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
diff --git a/grep.c b/grep.c
index d144b37489..073559f2cd 100644
--- a/grep.c
+++ b/grep.c
@@ -323,6 +323,15 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt
if (!opt->ignore_locale && is_utf8_locale() && !literal)
options |= (PCRE2_UTF | PCRE2_UCP | PCRE2_MATCH_INVALID_UTF);
+#ifndef GIT_PCRE2_VERSION_10_35_OR_HIGHER
+ /*
+ * Work around a JIT bug related to invalid Unicode character handling
+ * fixed in 10.35:
+ * https://github.com/PCRE2Project/pcre2/commit/c21bd977547d
+ */
+ options &= ~PCRE2_UCP;
+#endif
+
#ifndef GIT_PCRE2_VERSION_10_36_OR_HIGHER
/* Work around https://bugs.exim.org/show_bug.cgi?id=2642 fixed in 10.36 */
if (PCRE2_MATCH_INVALID_UTF && options & (PCRE2_UTF | PCRE2_CASELESS))
diff --git a/grep.h b/grep.h
index 6075f997e6..c59592e3bd 100644
--- a/grep.h
+++ b/grep.h
@@ -7,6 +7,9 @@
#if (PCRE2_MAJOR >= 10 && PCRE2_MINOR >= 36) || PCRE2_MAJOR >= 11
#define GIT_PCRE2_VERSION_10_36_OR_HIGHER
#endif
+#if (PCRE2_MAJOR >= 10 && PCRE2_MINOR >= 35) || PCRE2_MAJOR >= 11
+#define GIT_PCRE2_VERSION_10_35_OR_HIGHER
+#endif
#if (PCRE2_MAJOR >= 10 && PCRE2_MINOR >= 34) || PCRE2_MAJOR >= 11
#define GIT_PCRE2_VERSION_10_34_OR_HIGHER
#endif
diff --git a/hashmap.h b/hashmap.h
index 7251687d73..e2cf9c78d8 100644
--- a/hashmap.h
+++ b/hashmap.h
@@ -270,7 +270,7 @@ void hashmap_clear_(struct hashmap *map, ssize_t offset);
#define hashmap_clear(map) hashmap_clear_(map, -1)
/*
- * Similar to hashmap_clear(), except that the table is no deallocated; it
+ * Similar to hashmap_clear(), except that the table is not deallocated; it
* is merely zeroed out but left the same size as before. If the hashmap
* will be reused, this avoids the overhead of deallocating and
* reallocating map->table. As with hashmap_clear(), you may need to free
diff --git a/http-push.c b/http-push.c
index 637a4e91f7..76ab5dbb09 100644
--- a/http-push.c
+++ b/http-push.c
@@ -605,7 +605,7 @@ static void finish_request(struct transfer_request *request)
}
static int is_running_queue;
-static int fill_active_slot(void *unused)
+static int fill_active_slot(void *data UNUSED)
{
struct transfer_request *request;
diff --git a/http-walker.c b/http-walker.c
index 3b41f5654b..bba306b2d5 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -53,8 +53,7 @@ static void fetch_alternates(struct walker *walker, const char *base);
static void process_object_response(void *callback_data);
-static void start_object_request(struct walker *walker,
- struct object_request *obj_req)
+static void start_object_request(struct object_request *obj_req)
{
struct active_request_slot *slot;
struct http_object_request *req;
@@ -111,7 +110,7 @@ static void process_object_response(void *callback_data)
obj_req->repo =
obj_req->repo->next;
release_http_object_request(obj_req->req);
- start_object_request(walker, obj_req);
+ start_object_request(obj_req);
return;
}
}
@@ -128,7 +127,7 @@ static void release_object_request(struct object_request *obj_req)
free(obj_req);
}
-static int fill_active_slot(struct walker *walker)
+static int fill_active_slot(void *data UNUSED)
{
struct object_request *obj_req;
struct list_head *pos, *tmp, *head = &object_queue_head;
@@ -139,7 +138,7 @@ static int fill_active_slot(struct walker *walker)
if (repo_has_object_file(the_repository, &obj_req->oid))
obj_req->state = COMPLETE;
else {
- start_object_request(walker, obj_req);
+ start_object_request(obj_req);
return 1;
}
}
@@ -614,7 +613,7 @@ struct walker *get_http_walker(const char *url)
walker->cleanup = cleanup;
walker->data = data;
- add_fill_function(walker, (int (*)(void *)) fill_active_slot);
+ add_fill_function(NULL, fill_active_slot);
return walker;
}
diff --git a/http.c b/http.c
index c3916ceb4d..bb58bb3e6a 100644
--- a/http.c
+++ b/http.c
@@ -42,6 +42,7 @@ static int curl_ssl_verify = -1;
static int curl_ssl_try;
static const char *curl_http_version = NULL;
static const char *ssl_cert;
+static const char *ssl_cert_type;
static const char *ssl_cipherlist;
static const char *ssl_version;
static struct {
@@ -61,6 +62,7 @@ static struct {
#endif
};
static const char *ssl_key;
+static const char *ssl_key_type;
static const char *ssl_capath;
static const char *curl_no_proxy;
#ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY
@@ -376,8 +378,12 @@ static int http_options(const char *var, const char *value, void *cb)
return git_config_string(&ssl_version, var, value);
if (!strcmp("http.sslcert", var))
return git_config_pathname(&ssl_cert, var, value);
+ if (!strcmp("http.sslcerttype", var))
+ return git_config_string(&ssl_cert_type, var, value);
if (!strcmp("http.sslkey", var))
return git_config_pathname(&ssl_key, var, value);
+ if (!strcmp("http.sslkeytype", var))
+ return git_config_string(&ssl_key_type, var, value);
if (!strcmp("http.sslcapath", var))
return git_config_pathname(&ssl_capath, var, value);
if (!strcmp("http.sslcainfo", var))
@@ -1016,10 +1022,14 @@ static CURL *get_curl_handle(void)
if (ssl_cert)
curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
+ if (ssl_cert_type)
+ curl_easy_setopt(result, CURLOPT_SSLCERTTYPE, ssl_cert_type);
if (has_cert_password())
curl_easy_setopt(result, CURLOPT_KEYPASSWD, cert_auth.password);
if (ssl_key)
curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
+ if (ssl_key_type)
+ curl_easy_setopt(result, CURLOPT_SSLKEYTYPE, ssl_key_type);
if (ssl_capath)
curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);
#ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY
@@ -1254,7 +1264,9 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
curl_ssl_verify = 0;
set_from_env(&ssl_cert, "GIT_SSL_CERT");
+ set_from_env(&ssl_cert_type, "GIT_SSL_CERT_TYPE");
set_from_env(&ssl_key, "GIT_SSL_KEY");
+ set_from_env(&ssl_key_type, "GIT_SSL_KEY_TYPE");
set_from_env(&ssl_capath, "GIT_SSL_CAPATH");
set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");
diff --git a/list-objects-filter-options.c b/list-objects-filter-options.c
index b82ba0a2e5..2a3b7881af 100644
--- a/list-objects-filter-options.c
+++ b/list-objects-filter-options.c
@@ -11,6 +11,7 @@
#include "promisor-remote.h"
#include "trace.h"
#include "url.h"
+#include "parse-options.h"
static int parse_combine_filter(
struct list_objects_filter_options *filter_options,
diff --git a/list-objects-filter-options.h b/list-objects-filter-options.h
index 65c6119e9d..f620612586 100644
--- a/list-objects-filter-options.h
+++ b/list-objects-filter-options.h
@@ -3,10 +3,11 @@
#include "gettext.h"
#include "object.h"
-#include "parse-options.h"
#include "string-list.h"
#include "strbuf.h"
+struct option;
+
/*
* The list of defined filters for list-objects.
*/
diff --git a/log-tree.c b/log-tree.c
index b5cf3d8439..143b86fecf 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -449,7 +449,7 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
fprintf(opt->diffopt.file, "From %s Mon Sep 17 00:00:00 2001\n", name);
graph_show_oneline(opt->graph);
if (opt->message_id) {
- fprintf(opt->diffopt.file, "Message-Id: <%s>\n", opt->message_id);
+ fprintf(opt->diffopt.file, "Message-ID: <%s>\n", opt->message_id);
graph_show_oneline(opt->graph);
}
if (opt->ref_message_ids && opt->ref_message_ids->nr > 0) {
diff --git a/mailinfo.c b/mailinfo.c
index 9f1495ddcf..2aeb20e5e6 100644
--- a/mailinfo.c
+++ b/mailinfo.c
@@ -599,7 +599,7 @@ static int check_header(struct mailinfo *mi,
ret = 1;
goto check_header_out;
}
- if (parse_header(line, "Message-Id", mi, &sb)) {
+ if (parse_header(line, "Message-ID", mi, &sb)) {
if (mi->add_message_id)
mi->message_id = strbuf_detach(&sb, NULL);
ret = 1;
@@ -831,7 +831,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
if (patchbreak(line)) {
if (mi->message_id)
strbuf_addf(&mi->log_message,
- "Message-Id: %s\n", mi->message_id);
+ "Message-ID: %s\n", mi->message_id);
return 1;
}
diff --git a/mailmap.c b/mailmap.c
index 28d389c1f9..5dc5223c43 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -6,16 +6,6 @@
#include "object-store.h"
#include "setup.h"
-#define DEBUG_MAILMAP 0
-#if DEBUG_MAILMAP
-#define debug_mm(...) fprintf(stderr, __VA_ARGS__)
-#define debug_str(X) ((X) ? (X) : "(none)")
-#else
-__attribute__((format (printf, 1, 2)))
-static inline void debug_mm(const char *format, ...) {}
-static inline const char *debug_str(const char *s) { return s; }
-#endif
-
const char *git_mailmap_file;
const char *git_mailmap_blob;
@@ -33,23 +23,17 @@ struct mailmap_entry {
struct string_list namemap;
};
-static void free_mailmap_info(void *p, const char *s)
+static void free_mailmap_info(void *p, const char *s UNUSED)
{
struct mailmap_info *mi = (struct mailmap_info *)p;
- debug_mm("mailmap: -- complex: '%s' -> '%s' <%s>\n",
- s, debug_str(mi->name), debug_str(mi->email));
free(mi->name);
free(mi->email);
free(mi);
}
-static void free_mailmap_entry(void *p, const char *s)
+static void free_mailmap_entry(void *p, const char *s UNUSED)
{
struct mailmap_entry *me = (struct mailmap_entry *)p;
- debug_mm("mailmap: removing entries for <%s>, with %"PRIuMAX" sub-entries\n",
- s, (uintmax_t)me->namemap.nr);
- debug_mm("mailmap: - simple: '%s' <%s>\n",
- debug_str(me->name), debug_str(me->email));
free(me->name);
free(me->email);
@@ -96,8 +80,6 @@ static void add_mapping(struct string_list *map,
}
if (!old_name) {
- debug_mm("mailmap: adding (simple) entry for '%s'\n", old_email);
-
/* Replace current name and new email for simple entry */
if (new_name) {
free(me->name);
@@ -109,15 +91,10 @@ static void add_mapping(struct string_list *map,
}
} else {
struct mailmap_info *mi = xcalloc(1, sizeof(struct mailmap_info));
- debug_mm("mailmap: adding (complex) entry for '%s'\n", old_email);
mi->name = xstrdup_or_null(new_name);
mi->email = xstrdup_or_null(new_email);
string_list_insert(&me->namemap, old_name)->util = mi;
}
-
- debug_mm("mailmap: '%s' <%s> -> '%s' <%s>\n",
- debug_str(old_name), old_email,
- debug_str(new_name), debug_str(new_email));
}
static char *parse_name_and_email(char *buffer, char **name,
@@ -253,11 +230,8 @@ int read_mailmap(struct string_list *map)
void clear_mailmap(struct string_list *map)
{
- debug_mm("mailmap: clearing %"PRIuMAX" entries...\n",
- (uintmax_t)map->nr);
map->strdup_strings = 1;
string_list_clear_func(map, free_mailmap_entry);
- debug_mm("mailmap: cleared\n");
}
/*
@@ -318,10 +292,6 @@ int map_user(struct string_list *map,
struct string_list_item *item;
struct mailmap_entry *me;
- debug_mm("map_user: map '%.*s' <%.*s>\n",
- (int)*namelen, debug_str(*name),
- (int)*emaillen, debug_str(*email));
-
item = lookup_prefix(map, *email, *emaillen);
if (item) {
me = (struct mailmap_entry *)item->util;
@@ -339,10 +309,8 @@ int map_user(struct string_list *map,
}
if (item) {
struct mailmap_info *mi = (struct mailmap_info *)item->util;
- if (mi->name == NULL && mi->email == NULL) {
- debug_mm("map_user: -- (no simple mapping)\n");
+ if (mi->name == NULL && mi->email == NULL)
return 0;
- }
if (mi->email) {
*email = mi->email;
*emaillen = strlen(*email);
@@ -351,11 +319,7 @@ int map_user(struct string_list *map,
*name = mi->name;
*namelen = strlen(*name);
}
- debug_mm("map_user: to '%.*s' <%.*s>\n",
- (int)*namelen, debug_str(*name),
- (int)*emaillen, debug_str(*email));
return 1;
}
- debug_mm("map_user: --\n");
return 0;
}
diff --git a/object-name.c b/object-name.c
index 3cd5b32729..538e8a8f62 100644
--- a/object-name.c
+++ b/object-name.c
@@ -906,6 +906,7 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
char *real_ref = NULL;
int refs_found = 0;
int at, reflog_len, nth_prior = 0;
+ int fatal = !(flags & GET_OID_QUIETLY);
if (len == r->hash_algo->hexsz && !get_oid_hex(str, oid)) {
if (warn_ambiguous_refs && warn_on_object_refname_ambiguity) {
@@ -960,11 +961,11 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
if (!len && reflog_len)
/* allow "@{...}" to mean the current branch reflog */
- refs_found = repo_dwim_ref(r, "HEAD", 4, oid, &real_ref, 0);
+ refs_found = repo_dwim_ref(r, "HEAD", 4, oid, &real_ref, !fatal);
else if (reflog_len)
refs_found = repo_dwim_log(r, str, len, oid, &real_ref);
else
- refs_found = repo_dwim_ref(r, str, len, oid, &real_ref, 0);
+ refs_found = repo_dwim_ref(r, str, len, oid, &real_ref, !fatal);
if (!refs_found)
return -1;
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 48fc2ec76d..295b8abc19 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -2321,7 +2321,11 @@ int bitmap_is_midx(struct bitmap_index *bitmap_git)
const struct string_list *bitmap_preferred_tips(struct repository *r)
{
- return repo_config_get_value_multi(r, "pack.preferbitmaptips");
+ const struct string_list *dest;
+
+ if (!repo_config_get_string_multi(r, "pack.preferbitmaptips", &dest))
+ return dest;
+ return NULL;
}
int bitmap_is_preferred_refname(struct repository *r, const char *refname)
diff --git a/parse-options-cb.c b/parse-options-cb.c
index 26a4c7d08a..298decf48f 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -210,24 +210,25 @@ int parse_opt_string_list(const struct option *opt, const char *arg, int unset)
return 0;
}
-int parse_opt_noop_cb(const struct option *opt, const char *arg, int unset)
+int parse_opt_strvec(const struct option *opt, const char *arg, int unset)
{
+ struct strvec *v = opt->value;
+
+ if (unset) {
+ strvec_clear(v);
+ return 0;
+ }
+
+ if (!arg)
+ return -1;
+
+ strvec_push(v, arg);
return 0;
}
-/**
- * Report that the option is unknown, so that other code can handle
- * it. This can be used as a callback together with
- * OPTION_LOWLEVEL_CALLBACK to allow an option to be documented in the
- * "-h" output even if it's not being handled directly by
- * parse_options().
- */
-enum parse_opt_result parse_opt_unknown_cb(struct parse_opt_ctx_t *ctx,
- const struct option *opt,
- const char *arg, int unset)
+int parse_opt_noop_cb(const struct option *opt, const char *arg, int unset)
{
- BUG_ON_OPT_ARG(arg);
- return PARSE_OPT_UNKNOWN;
+ return 0;
}
/**
diff --git a/parse-options.h b/parse-options.h
index d57868eff9..8e48efe524 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -160,71 +160,211 @@ struct option {
parse_opt_subcommand_fn *subcommand_fn;
};
-#define OPT_BIT_F(s, l, v, h, b, f) { OPTION_BIT, (s), (l), (v), NULL, (h), \
- PARSE_OPT_NOARG|(f), NULL, (b) }
-#define OPT_COUNTUP_F(s, l, v, h, f) { OPTION_COUNTUP, (s), (l), (v), NULL, \
- (h), PARSE_OPT_NOARG|(f) }
-#define OPT_SET_INT_F(s, l, v, h, i, f) { OPTION_SET_INT, (s), (l), (v), NULL, \
- (h), PARSE_OPT_NOARG | (f), NULL, (i) }
+#define OPT_BIT_F(s, l, v, h, b, f) { \
+ .type = OPTION_BIT, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .help = (h), \
+ .flags = PARSE_OPT_NOARG|(f), \
+ .callback = NULL, \
+ .defval = (b), \
+}
+#define OPT_COUNTUP_F(s, l, v, h, f) { \
+ .type = OPTION_COUNTUP, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .help = (h), \
+ .flags = PARSE_OPT_NOARG|(f), \
+}
+#define OPT_SET_INT_F(s, l, v, h, i, f) { \
+ .type = OPTION_SET_INT, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .help = (h), \
+ .flags = PARSE_OPT_NOARG | (f), \
+ .defval = (i), \
+}
#define OPT_BOOL_F(s, l, v, h, f) OPT_SET_INT_F(s, l, v, h, 1, f)
-#define OPT_CALLBACK_F(s, l, v, a, h, f, cb) \
- { OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), (cb) }
-#define OPT_STRING_F(s, l, v, a, h, f) { OPTION_STRING, (s), (l), (v), (a), (h), (f) }
-#define OPT_INTEGER_F(s, l, v, h, f) { OPTION_INTEGER, (s), (l), (v), N_("n"), (h), (f) }
+#define OPT_CALLBACK_F(s, l, v, a, h, f, cb) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = (a), \
+ .help = (h), \
+ .flags = (f), \
+ .callback = (cb), \
+}
+#define OPT_STRING_F(s, l, v, a, h, f) { \
+ .type = OPTION_STRING, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = (a), \
+ .help = (h), \
+ .flags = (f), \
+}
+#define OPT_INTEGER_F(s, l, v, h, f) { \
+ .type = OPTION_INTEGER, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = N_("n"), \
+ .help = (h), \
+ .flags = (f), \
+}
-#define OPT_END() { OPTION_END }
-#define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
+#define OPT_END() { \
+ .type = OPTION_END, \
+}
+#define OPT_GROUP(h) { \
+ .type = OPTION_GROUP, \
+ .help = (h), \
+}
#define OPT_BIT(s, l, v, h, b) OPT_BIT_F(s, l, v, h, b, 0)
-#define OPT_BITOP(s, l, v, h, set, clear) { OPTION_BITOP, (s), (l), (v), NULL, (h), \
- PARSE_OPT_NOARG|PARSE_OPT_NONEG, NULL, \
- (set), NULL, (clear) }
-#define OPT_NEGBIT(s, l, v, h, b) { OPTION_NEGBIT, (s), (l), (v), NULL, \
- (h), PARSE_OPT_NOARG, NULL, (b) }
+#define OPT_BITOP(s, l, v, h, set, clear) { \
+ .type = OPTION_BITOP, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .help = (h), \
+ .flags = PARSE_OPT_NOARG|PARSE_OPT_NONEG, \
+ .defval = (set), \
+ .extra = (clear), \
+}
+#define OPT_NEGBIT(s, l, v, h, b) { \
+ .type = OPTION_NEGBIT, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .help = (h), \
+ .flags = PARSE_OPT_NOARG, \
+ .defval = (b), \
+}
#define OPT_COUNTUP(s, l, v, h) OPT_COUNTUP_F(s, l, v, h, 0)
#define OPT_SET_INT(s, l, v, h, i) OPT_SET_INT_F(s, l, v, h, i, 0)
#define OPT_BOOL(s, l, v, h) OPT_BOOL_F(s, l, v, h, 0)
-#define OPT_HIDDEN_BOOL(s, l, v, h) { OPTION_SET_INT, (s), (l), (v), NULL, \
- (h), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1}
-#define OPT_CMDMODE_F(s, l, v, h, i, f) { OPTION_SET_INT, (s), (l), (v), NULL, \
- (h), PARSE_OPT_CMDMODE|PARSE_OPT_NOARG|PARSE_OPT_NONEG | (f), NULL, (i) }
+#define OPT_HIDDEN_BOOL(s, l, v, h) { \
+ .type = OPTION_SET_INT, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .help = (h), \
+ .flags = PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, \
+ .defval = 1, \
+}
+#define OPT_CMDMODE_F(s, l, v, h, i, f) { \
+ .type = OPTION_SET_INT, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .help = (h), \
+ .flags = PARSE_OPT_CMDMODE|PARSE_OPT_NOARG|PARSE_OPT_NONEG | (f), \
+ .defval = (i), \
+}
#define OPT_CMDMODE(s, l, v, h, i) OPT_CMDMODE_F(s, l, v, h, i, 0)
#define OPT_INTEGER(s, l, v, h) OPT_INTEGER_F(s, l, v, h, 0)
-#define OPT_MAGNITUDE(s, l, v, h) { OPTION_MAGNITUDE, (s), (l), (v), \
- N_("n"), (h), PARSE_OPT_NONEG }
+#define OPT_MAGNITUDE(s, l, v, h) { \
+ .type = OPTION_MAGNITUDE, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = N_("n"), \
+ .help = (h), \
+ .flags = PARSE_OPT_NONEG, \
+}
#define OPT_STRING(s, l, v, a, h) OPT_STRING_F(s, l, v, a, h, 0)
-#define OPT_STRING_LIST(s, l, v, a, h) \
- { OPTION_CALLBACK, (s), (l), (v), (a), \
- (h), 0, &parse_opt_string_list }
-#define OPT_UYN(s, l, v, h) { OPTION_CALLBACK, (s), (l), (v), NULL, \
- (h), PARSE_OPT_NOARG, &parse_opt_tertiary }
-#define OPT_EXPIRY_DATE(s, l, v, h) \
- { OPTION_CALLBACK, (s), (l), (v), N_("expiry-date"),(h), 0, \
- parse_opt_expiry_date_cb }
-#define OPT_CALLBACK(s, l, v, a, h, f) OPT_CALLBACK_F(s, l, v, a, h, 0, f)
-#define OPT_NUMBER_CALLBACK(v, h, f) \
- { OPTION_NUMBER, 0, NULL, (v), NULL, (h), \
- PARSE_OPT_NOARG | PARSE_OPT_NONEG, (f) }
-#define OPT_FILENAME(s, l, v, h) { OPTION_FILENAME, (s), (l), (v), \
- N_("file"), (h) }
-#define OPT_COLOR_FLAG(s, l, v, h) \
- { OPTION_CALLBACK, (s), (l), (v), N_("when"), (h), PARSE_OPT_OPTARG, \
- parse_opt_color_flag_cb, (intptr_t)"always" }
-
-#define OPT_NOOP_NOARG(s, l) \
- { OPTION_CALLBACK, (s), (l), NULL, NULL, \
- N_("no-op (backward compatibility)"), \
- PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, parse_opt_noop_cb }
-
-#define OPT_ALIAS(s, l, source_long_name) \
- { OPTION_ALIAS, (s), (l), (source_long_name) }
+#define OPT_STRING_LIST(s, l, v, a, h) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = (a), \
+ .help = (h), \
+ .callback = &parse_opt_string_list, \
+}
+#define OPT_STRVEC(s, l, v, a, h) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = (a), \
+ .help = (h), \
+ .callback = &parse_opt_strvec, \
+}
+#define OPT_UYN(s, l, v, h) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .help = (h), \
+ .flags = PARSE_OPT_NOARG, \
+ .callback = &parse_opt_tertiary, \
+}
+#define OPT_EXPIRY_DATE(s, l, v, h) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = N_("expiry-date"), \
+ .help = (h), \
+ .callback = parse_opt_expiry_date_cb, \
+}
+#define OPT_CALLBACK(s, l, v, a, h, cb) OPT_CALLBACK_F(s, l, v, a, h, 0, cb)
+#define OPT_NUMBER_CALLBACK(v, h, cb) { \
+ .type = OPTION_NUMBER, \
+ .value = (v), \
+ .help = (h), \
+ .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, \
+ .callback = (cb), \
+}
+#define OPT_FILENAME(s, l, v, h) { \
+ .type = OPTION_FILENAME, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = N_("file"), \
+ .help = (h), \
+}
+#define OPT_COLOR_FLAG(s, l, v, h) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = N_("when"), \
+ .help = (h), \
+ .flags = PARSE_OPT_OPTARG, \
+ .callback = parse_opt_color_flag_cb, \
+ .defval = (intptr_t)"always", \
+}
+
+#define OPT_NOOP_NOARG(s, l) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .help = N_("no-op (backward compatibility)"), \
+ .flags = PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, \
+ .callback = parse_opt_noop_cb, \
+}
+
+#define OPT_ALIAS(s, l, source_long_name) { \
+ .type = OPTION_ALIAS, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (source_long_name), \
+}
#define OPT_SUBCOMMAND_F(l, v, fn, f) { \
.type = OPTION_SUBCOMMAND, \
.long_name = (l), \
.value = (v), \
.flags = (f), \
- .subcommand_fn = (fn) }
+ .subcommand_fn = (fn), \
+}
#define OPT_SUBCOMMAND(l, v, fn) OPT_SUBCOMMAND_F((l), (v), (fn), 0)
/*
@@ -349,10 +489,8 @@ int parse_opt_commits(const struct option *, const char *, int);
int parse_opt_commit(const struct option *, const char *, int);
int parse_opt_tertiary(const struct option *, const char *, int);
int parse_opt_string_list(const struct option *, const char *, int);
+int parse_opt_strvec(const struct option *, const char *, int);
int parse_opt_noop_cb(const struct option *, const char *, int);
-enum parse_opt_result parse_opt_unknown_cb(struct parse_opt_ctx_t *ctx,
- const struct option *,
- const char *, int);
int parse_opt_passthru(const struct option *, const char *, int);
int parse_opt_passthru_argv(const struct option *, const char *, int);
/* value is enum branch_track* */
@@ -360,34 +498,80 @@ int parse_opt_tracking_mode(const struct option *, const char *, int);
#define OPT__VERBOSE(var, h) OPT_COUNTUP('v', "verbose", (var), (h))
#define OPT__QUIET(var, h) OPT_COUNTUP('q', "quiet", (var), (h))
-#define OPT__VERBOSITY(var) \
- { OPTION_CALLBACK, 'v', "verbose", (var), NULL, N_("be more verbose"), \
- PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \
- { OPTION_CALLBACK, 'q', "quiet", (var), NULL, N_("be more quiet"), \
- PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }
+#define OPT__VERBOSITY(var) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = 'v', \
+ .long_name = "verbose", \
+ .value = (var), \
+ .help = N_("be more verbose"), \
+ .flags = PARSE_OPT_NOARG, \
+ .callback = &parse_opt_verbosity_cb, \
+}, { \
+ .type = OPTION_CALLBACK, \
+ .short_name = 'q', \
+ .long_name = "quiet", \
+ .value = (var), \
+ .help = N_("be more quiet"), \
+ .flags = PARSE_OPT_NOARG, \
+ .callback = &parse_opt_verbosity_cb, \
+}
#define OPT__DRY_RUN(var, h) OPT_BOOL('n', "dry-run", (var), (h))
#define OPT__FORCE(var, h, f) OPT_COUNTUP_F('f', "force", (var), (h), (f))
-#define OPT__ABBREV(var) \
- { OPTION_CALLBACK, 0, "abbrev", (var), N_("n"), \
- N_("use <n> digits to display object names"), \
- PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }
+#define OPT__ABBREV(var) { \
+ .type = OPTION_CALLBACK, \
+ .long_name = "abbrev", \
+ .value = (var), \
+ .argh = N_("n"), \
+ .help = N_("use <n> digits to display object names"), \
+ .flags = PARSE_OPT_OPTARG, \
+ .callback = &parse_opt_abbrev_cb, \
+}
#define OPT__SUPER_PREFIX(var) \
OPT_STRING_F(0, "super-prefix", (var), N_("prefix"), \
N_("prefixed path to initial superproject"), PARSE_OPT_HIDDEN)
#define OPT__COLOR(var, h) \
OPT_COLOR_FLAG(0, "color", (var), (h))
-#define OPT_COLUMN(s, l, v, h) \
- { OPTION_CALLBACK, (s), (l), (v), N_("style"), (h), PARSE_OPT_OPTARG, parseopt_column_callback }
-#define OPT_PASSTHRU(s, l, v, a, h, f) \
- { OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), parse_opt_passthru }
-#define OPT_PASSTHRU_ARGV(s, l, v, a, h, f) \
- { OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), parse_opt_passthru_argv }
-#define _OPT_CONTAINS_OR_WITH(name, variable, help, flag) \
- { OPTION_CALLBACK, 0, name, (variable), N_("commit"), (help), \
- PARSE_OPT_LASTARG_DEFAULT | flag, \
- parse_opt_commits, (intptr_t) "HEAD" \
- }
+#define OPT_COLUMN(s, l, v, h) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = N_("style"), \
+ .help = (h), \
+ .flags = PARSE_OPT_OPTARG, \
+ .callback = parseopt_column_callback, \
+}
+#define OPT_PASSTHRU(s, l, v, a, h, f) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = (a), \
+ .help = (h), \
+ .flags = (f), \
+ .callback = parse_opt_passthru, \
+}
+#define OPT_PASSTHRU_ARGV(s, l, v, a, h, f) { \
+ .type = OPTION_CALLBACK, \
+ .short_name = (s), \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = (a), \
+ .help = (h), \
+ .flags = (f), \
+ .callback = parse_opt_passthru_argv, \
+}
+#define _OPT_CONTAINS_OR_WITH(l, v, h, f) { \
+ .type = OPTION_CALLBACK, \
+ .long_name = (l), \
+ .value = (v), \
+ .argh = N_("commit"), \
+ .help = (h), \
+ .flags = PARSE_OPT_LASTARG_DEFAULT | (f), \
+ .callback = parse_opt_commits, \
+ .defval = (intptr_t) "HEAD", \
+}
#define OPT_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("contains", v, h, PARSE_OPT_NONEG)
#define OPT_NO_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("no-contains", v, h, PARSE_OPT_NONEG)
#define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
diff --git a/protocol-caps.c b/protocol-caps.c
index 874bc815b4..94c51862c5 100644
--- a/protocol-caps.c
+++ b/protocol-caps.c
@@ -79,7 +79,7 @@ static void send_info(struct repository *r, struct packet_writer *writer,
int cap_object_info(struct repository *r, struct packet_reader *request)
{
- struct requested_info info;
+ struct requested_info info = { 0 };
struct packet_writer writer;
struct string_list oid_str_list = STRING_LIST_INIT_DUP;
diff --git a/read-cache.c b/read-cache.c
index f225bf44cd..e919af3c77 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -2911,6 +2911,16 @@ static int record_ieot(void)
return !git_config_get_index_threads(&val) && val != 1;
}
+enum write_extensions {
+ WRITE_NO_EXTENSION = 0,
+ WRITE_SPLIT_INDEX_EXTENSION = 1<<0,
+ WRITE_CACHE_TREE_EXTENSION = 1<<1,
+ WRITE_RESOLVE_UNDO_EXTENSION = 1<<2,
+ WRITE_UNTRACKED_CACHE_EXTENSION = 1<<3,
+ WRITE_FSMONITOR_EXTENSION = 1<<4,
+};
+#define WRITE_ALL_EXTENSIONS ((enum write_extensions)-1)
+
/*
* On success, `tempfile` is closed. If it is the temporary file
* of a `struct lock_file`, we will therefore effectively perform
@@ -2919,7 +2929,7 @@ static int record_ieot(void)
* rely on it.
*/
static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
- int strip_extensions, unsigned flags)
+ enum write_extensions write_extensions, unsigned flags)
{
uint64_t start = getnanotime();
struct hashfile *f;
@@ -3092,8 +3102,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
return -1;
}
- if (!strip_extensions && istate->split_index &&
- !is_null_oid(&istate->split_index->base_oid)) {
+ if (write_extensions & WRITE_SPLIT_INDEX_EXTENSION &&
+ istate->split_index) {
struct strbuf sb = STRBUF_INIT;
if (istate->sparse_index)
@@ -3107,7 +3117,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
if (err)
return -1;
}
- if (!strip_extensions && !drop_cache_tree && istate->cache_tree) {
+ if (write_extensions & WRITE_CACHE_TREE_EXTENSION &&
+ !drop_cache_tree && istate->cache_tree) {
struct strbuf sb = STRBUF_INIT;
cache_tree_write(&sb, istate->cache_tree);
@@ -3117,7 +3128,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
if (err)
return -1;
}
- if (!strip_extensions && istate->resolve_undo) {
+ if (write_extensions & WRITE_RESOLVE_UNDO_EXTENSION &&
+ istate->resolve_undo) {
struct strbuf sb = STRBUF_INIT;
resolve_undo_write(&sb, istate->resolve_undo);
@@ -3128,7 +3140,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
if (err)
return -1;
}
- if (!strip_extensions && istate->untracked) {
+ if (write_extensions & WRITE_UNTRACKED_CACHE_EXTENSION &&
+ istate->untracked) {
struct strbuf sb = STRBUF_INIT;
write_untracked_extension(&sb, istate->untracked);
@@ -3139,7 +3152,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
if (err)
return -1;
}
- if (!strip_extensions && istate->fsmonitor_last_update) {
+ if (write_extensions & WRITE_FSMONITOR_EXTENSION &&
+ istate->fsmonitor_last_update) {
struct strbuf sb = STRBUF_INIT;
write_fsmonitor_extension(&sb, istate);
@@ -3213,8 +3227,10 @@ static int commit_locked_index(struct lock_file *lk)
return commit_lock_file(lk);
}
-static int do_write_locked_index(struct index_state *istate, struct lock_file *lock,
- unsigned flags)
+static int do_write_locked_index(struct index_state *istate,
+ struct lock_file *lock,
+ unsigned flags,
+ enum write_extensions write_extensions)
{
int ret;
int was_full = istate->sparse_index == INDEX_EXPANDED;
@@ -3232,7 +3248,7 @@ static int do_write_locked_index(struct index_state *istate, struct lock_file *l
*/
trace2_region_enter_printf("index", "do_write_index", the_repository,
"%s", get_lock_file_path(lock));
- ret = do_write_index(istate, lock->tempfile, 0, flags);
+ ret = do_write_index(istate, lock->tempfile, write_extensions, flags);
trace2_region_leave_printf("index", "do_write_index", the_repository,
"%s", get_lock_file_path(lock));
@@ -3261,7 +3277,7 @@ static int write_split_index(struct index_state *istate,
{
int ret;
prepare_to_write_split_index(istate);
- ret = do_write_locked_index(istate, lock, flags);
+ ret = do_write_locked_index(istate, lock, flags, WRITE_ALL_EXTENSIONS);
finish_writing_split_index(istate);
return ret;
}
@@ -3336,7 +3352,7 @@ static int write_shared_index(struct index_state *istate,
trace2_region_enter_printf("index", "shared/do_write_index",
the_repository, "%s", get_tempfile_path(*temp));
- ret = do_write_index(si->base, *temp, 1, flags);
+ ret = do_write_index(si->base, *temp, WRITE_NO_EXTENSION, flags);
trace2_region_leave_printf("index", "shared/do_write_index",
the_repository, "%s", get_tempfile_path(*temp));
@@ -3413,9 +3429,8 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
if ((!si && !test_split_index_env) ||
alternate_index_output ||
(istate->cache_changed & ~EXTMASK)) {
- if (si)
- oidclr(&si->base_oid);
- ret = do_write_locked_index(istate, lock, flags);
+ ret = do_write_locked_index(istate, lock, flags,
+ ~WRITE_SPLIT_INDEX_EXTENSION);
goto out;
}
@@ -3441,8 +3456,8 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
/* Same initial permissions as the main .git/index file */
temp = mks_tempfile_sm(git_path("sharedindex_XXXXXX"), 0, 0666);
if (!temp) {
- oidclr(&si->base_oid);
- ret = do_write_locked_index(istate, lock, flags);
+ ret = do_write_locked_index(istate, lock, flags,
+ ~WRITE_SPLIT_INDEX_EXTENSION);
goto out;
}
ret = write_shared_index(istate, &temp, flags);
diff --git a/ref-filter.c b/ref-filter.c
index 57a5884aec..10aab14f03 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -162,6 +162,7 @@ enum atom_type {
ATOM_THEN,
ATOM_ELSE,
ATOM_REST,
+ ATOM_AHEADBEHIND,
};
/*
@@ -600,7 +601,22 @@ static int rest_atom_parser(struct ref_format *format,
{
if (arg)
return err_no_arg(err, "rest");
- format->use_rest = 1;
+ return 0;
+}
+
+static int ahead_behind_atom_parser(struct ref_format *format, struct used_atom *atom,
+ const char *arg, struct strbuf *err)
+{
+ struct string_list_item *item;
+
+ if (!arg)
+ return strbuf_addf_ret(err, -1, _("expected format: %%(ahead-behind:<committish>)"));
+
+ item = string_list_append(&format->bases, arg);
+ item->util = lookup_commit_reference_by_name(arg);
+ if (!item->util)
+ die("failed to find '%s'", arg);
+
return 0;
}
@@ -664,6 +680,7 @@ static struct {
[ATOM_THEN] = { "then", SOURCE_NONE },
[ATOM_ELSE] = { "else", SOURCE_NONE },
[ATOM_REST] = { "rest", SOURCE_NONE, FIELD_STR, rest_atom_parser },
+ [ATOM_AHEADBEHIND] = { "ahead-behind", SOURCE_OTHER, FIELD_STR, ahead_behind_atom_parser },
/*
* Please update $__git_ref_fieldlist in git-completion.bash
* when you add new atoms
@@ -1872,6 +1889,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
struct object *obj;
int i;
struct object_info empty = OBJECT_INFO_INIT;
+ int ahead_behind_atoms = 0;
CALLOC_ARRAY(ref->value, used_atom_cnt);
@@ -2002,6 +2020,16 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
else
v->s = xstrdup("");
continue;
+ } else if (atom_type == ATOM_AHEADBEHIND) {
+ if (ref->counts) {
+ const struct ahead_behind_count *count;
+ count = ref->counts[ahead_behind_atoms++];
+ v->s = xstrfmt("%d %d", count->ahead, count->behind);
+ } else {
+ /* Not a commit. */
+ v->s = xstrdup("");
+ }
+ continue;
} else
continue;
@@ -2352,6 +2380,7 @@ static void free_array_item(struct ref_array_item *item)
free((char *)item->value[i].s);
free(item->value);
}
+ free(item->counts);
free(item);
}
@@ -2380,6 +2409,8 @@ void ref_array_clear(struct ref_array *array)
free_worktrees(ref_to_worktree_map.worktrees);
ref_to_worktree_map.worktrees = NULL;
}
+
+ FREE_AND_NULL(array->counts);
}
#define EXCLUDE_REACHED 0
@@ -2388,33 +2419,22 @@ static void reach_filter(struct ref_array *array,
struct commit_list *check_reachable,
int include_reached)
{
- struct rev_info revs;
int i, old_nr;
struct commit **to_clear;
- struct commit_list *cr;
if (!check_reachable)
return;
CALLOC_ARRAY(to_clear, array->nr);
-
- repo_init_revisions(the_repository, &revs, NULL);
-
for (i = 0; i < array->nr; i++) {
struct ref_array_item *item = array->items[i];
- add_pending_object(&revs, &item->commit->object, item->refname);
to_clear[i] = item->commit;
}
- for (cr = check_reachable; cr; cr = cr->next) {
- struct commit *merge_commit = cr->item;
- merge_commit->object.flags |= UNINTERESTING;
- add_pending_object(&revs, &merge_commit->object, "");
- }
-
- revs.limited = 1;
- if (prepare_revision_walk(&revs))
- die(_("revision walk setup failed"));
+ tips_reachable_from_bases(the_repository,
+ check_reachable,
+ to_clear, array->nr,
+ UNINTERESTING);
old_nr = array->nr;
array->nr = 0;
@@ -2438,10 +2458,50 @@ static void reach_filter(struct ref_array *array,
clear_commit_marks(merge_commit, ALL_REV_FLAGS);
}
- release_revisions(&revs);
free(to_clear);
}
+void filter_ahead_behind(struct repository *r,
+ struct ref_format *format,
+ struct ref_array *array)
+{
+ struct commit **commits;
+ size_t commits_nr = format->bases.nr + array->nr;
+
+ if (!format->bases.nr || !array->nr)
+ return;
+
+ ALLOC_ARRAY(commits, commits_nr);
+ for (size_t i = 0; i < format->bases.nr; i++)
+ commits[i] = format->bases.items[i].util;
+
+ ALLOC_ARRAY(array->counts, st_mult(format->bases.nr, array->nr));
+
+ commits_nr = format->bases.nr;
+ array->counts_nr = 0;
+ for (size_t i = 0; i < array->nr; i++) {
+ const char *name = array->items[i]->refname;
+ commits[commits_nr] = lookup_commit_reference_by_name(name);
+
+ if (!commits[commits_nr])
+ continue;
+
+ CALLOC_ARRAY(array->items[i]->counts, format->bases.nr);
+ for (size_t j = 0; j < format->bases.nr; j++) {
+ struct ahead_behind_count *count;
+ count = &array->counts[array->counts_nr++];
+ count->tip_index = commits_nr;
+ count->base_index = j;
+
+ array->items[i]->counts[j] = count;
+ }
+ commits_nr++;
+ }
+
+ ahead_behind(r, commits, commits_nr, array->counts, array->counts_nr);
+ free(commits);
+}
+
/*
* API for filtering a set of refs. Based on the type of refs the user
* has requested, we iterate through those refs and apply filters
diff --git a/ref-filter.h b/ref-filter.h
index cc811c2cad..430701cfb7 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -5,7 +5,7 @@
#include "oid-array.h"
#include "refs.h"
#include "commit.h"
-#include "parse-options.h"
+#include "string-list.h"
/* Quoting styles */
#define QUOTE_NONE 0
@@ -25,6 +25,8 @@
struct atom_value;
struct ref_sorting;
+struct ahead_behind_count;
+struct option;
enum ref_sorting_order {
REF_SORTING_REVERSE = 1<<0,
@@ -41,6 +43,8 @@ struct ref_array_item {
const char *symref;
struct commit *commit;
struct atom_value *value;
+ struct ahead_behind_count **counts;
+
char refname[FLEX_ARRAY];
};
@@ -48,6 +52,9 @@ struct ref_array {
int nr, alloc;
struct ref_array_item **items;
struct rev_info *revs;
+
+ struct ahead_behind_count *counts;
+ size_t counts_nr;
};
struct ref_filter {
@@ -76,14 +83,19 @@ struct ref_format {
const char *format;
const char *rest;
int quote_style;
- int use_rest;
int use_color;
/* Internal state to ref-filter */
int need_color_reset_at_eol;
+
+ /* List of bases for ahead-behind counts. */
+ struct string_list bases;
};
-#define REF_FORMAT_INIT { .use_color = -1 }
+#define REF_FORMAT_INIT { \
+ .use_color = -1, \
+ .bases = STRING_LIST_INIT_DUP, \
+}
/* Macros for checking --merged and --no-merged options */
#define _OPT_MERGED_NO_MERGED(option, filter, h) \
@@ -144,4 +156,15 @@ struct ref_array_item *ref_array_push(struct ref_array *array,
const char *refname,
const struct object_id *oid);
+/*
+ * If the provided format includes ahead-behind atoms, then compute the
+ * ahead-behind values for the array of filtered references. Must be
+ * called after filter_refs() but before outputting the formatted refs.
+ *
+ * If this is not called, then any ahead-behind atoms will be blank.
+ */
+void filter_ahead_behind(struct repository *r,
+ struct ref_format *format,
+ struct ref_array *array);
+
#endif /* REF_FILTER_H */
diff --git a/remote-curl.c b/remote-curl.c
index 0ada1dd802..acf7b2bb40 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -479,10 +479,11 @@ static struct discovery *discover_refs(const char *service, int for_push)
/*
* NEEDSWORK: If we are trying to use protocol v2 and we are planning
- * to perform a push, then fallback to v0 since the client doesn't know
- * how to push yet using v2.
+ * to perform any operation that doesn't involve upload-pack (i.e., a
+ * fetch, ls-remote, etc), then fallback to v0 since we don't know how
+ * to do anything else (like push or remote archive) via v2.
*/
- if (version == protocol_v2 && !strcmp("git-receive-pack", service))
+ if (version == protocol_v2 && strcmp("git-upload-pack", service))
version = protocol_v0;
/* Add the extra Git-Protocol header */
diff --git a/remote.c b/remote.c
index 2947743d60..0764fca0db 100644
--- a/remote.c
+++ b/remote.c
@@ -22,6 +22,7 @@
#include "commit-reach.h"
#include "advice.h"
#include "connect.h"
+#include "parse-options.h"
enum map_direction { FROM_SRC, FROM_DST };
diff --git a/remote.h b/remote.h
index 5b38ee20b8..73638cefeb 100644
--- a/remote.h
+++ b/remote.h
@@ -1,10 +1,10 @@
#ifndef REMOTE_H
#define REMOTE_H
-#include "parse-options.h"
#include "hashmap.h"
#include "refspec.h"
+struct option;
struct transport_ls_refs_options;
/**
diff --git a/revision.c b/revision.c
index 43f88eaf56..b33cc1d106 100644
--- a/revision.c
+++ b/revision.c
@@ -43,6 +43,7 @@
#include "json-writer.h"
#include "list-objects-filter-options.h"
#include "resolve-undo.h"
+#include "parse-options.h"
volatile show_early_output_fn_t show_early_output;
diff --git a/revision.h b/revision.h
index 742700504a..e8f6de9684 100644
--- a/revision.h
+++ b/revision.h
@@ -2,7 +2,6 @@
#define REVISION_H
#include "commit.h"
-#include "parse-options.h"
#include "grep.h"
#include "notes.h"
#include "pretty.h"
@@ -62,6 +61,8 @@ struct string_list;
struct saved_parents;
struct bloom_key;
struct bloom_filter_settings;
+struct option;
+struct parse_opt_ctx_t;
define_shared_commit_slab(revision_sources, char *);
struct rev_cmdline_info {
diff --git a/scalar.c b/scalar.c
index af64f75ca6..1326e1f608 100644
--- a/scalar.c
+++ b/scalar.c
@@ -566,7 +566,7 @@ static int cmd_diagnose(int argc, const char **argv)
return res;
}
-static int cmd_list(int argc, const char **argv)
+static int cmd_list(int argc, const char **argv UNUSED)
{
if (argc != 1)
die(_("`scalar list` does not take arguments"));
diff --git a/send-pack.c b/send-pack.c
index 351037b07b..7ebd1b3507 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -18,6 +18,7 @@
#include "oid-array.h"
#include "gpg-interface.h"
#include "shallow.h"
+#include "parse-options.h"
#include "trace2.h"
#include "write-or-die.h"
diff --git a/sequencer.c b/sequencer.c
index fcca3b8144..c88d1d9553 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -363,9 +363,7 @@ void replay_opts_release(struct replay_opts *opts)
free(opts->reflog_action);
free(opts->default_strategy);
free(opts->strategy);
- for (size_t i = 0; i < opts->xopts_nr; i++)
- free(opts->xopts[i]);
- free(opts->xopts);
+ strvec_clear (&opts->xopts);
strbuf_release(&opts->current_fixups);
if (opts->revs)
release_revisions(opts->revs);
@@ -703,8 +701,8 @@ static int do_recursive_merge(struct repository *r,
next_tree = next ? repo_get_commit_tree(r, next) : empty_tree(r);
base_tree = base ? repo_get_commit_tree(r, base) : empty_tree(r);
- for (i = 0; i < opts->xopts_nr; i++)
- parse_merge_opt(&o, opts->xopts[i]);
+ for (i = 0; i < opts->xopts.nr; i++)
+ parse_merge_opt(&o, opts->xopts.v[i]);
if (!opts->strategy || !strcmp(opts->strategy, "ort")) {
memset(&result, 0, sizeof(result));
@@ -2340,7 +2338,7 @@ static int do_pick_commit(struct repository *r,
commit_list_insert(base, &common);
commit_list_insert(next, &remotes);
res |= try_merge_command(r, opts->strategy,
- opts->xopts_nr, (const char **)opts->xopts,
+ opts->xopts.nr, opts->xopts.v,
common, oid_to_hex(&head), remotes);
free_commit_list(common);
free_commit_list(remotes);
@@ -2913,8 +2911,7 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
else if (!strcmp(key, "options.gpg-sign"))
git_config_string_dup(&opts->gpg_sign, key, value);
else if (!strcmp(key, "options.strategy-option")) {
- ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
- opts->xopts[opts->xopts_nr++] = xstrdup(value);
+ strvec_push(&opts->xopts, value);
} else if (!strcmp(key, "options.allow-rerere-auto"))
opts->allow_rerere_auto =
git_config_bool_or_int(key, value, &error_flag) ?
@@ -2931,27 +2928,27 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
return 0;
}
-void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)
+static void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)
{
int i;
int count;
+ const char **argv;
char *strategy_opts_string = raw_opts;
if (*strategy_opts_string == ' ')
strategy_opts_string++;
- count = split_cmdline(strategy_opts_string,
- (const char ***)&opts->xopts);
+ count = split_cmdline(strategy_opts_string, &argv);
if (count < 0)
- die(_("could not split '%s': %s"), strategy_opts_string,
+ BUG("could not split '%s': %s", strategy_opts_string,
split_cmdline_strerror(count));
- opts->xopts_nr = count;
- for (i = 0; i < opts->xopts_nr; i++) {
- const char *arg = opts->xopts[i];
+ for (i = 0; i < count; i++) {
+ const char *arg = argv[i];
skip_prefix(arg, "--", &arg);
- opts->xopts[i] = xstrdup(arg);
+ strvec_push(&opts->xopts, arg);
}
+ free(argv);
}
static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
@@ -2959,7 +2956,6 @@ static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
strbuf_reset(buf);
if (!read_oneliner(buf, rebase_path_strategy(), 0))
return;
- free(opts->strategy);
opts->strategy = strbuf_detach(buf, NULL);
if (!read_oneliner(buf, rebase_path_strategy_opts(), 0))
return;
@@ -3068,12 +3064,13 @@ done_rebase_i:
static void write_strategy_opts(struct replay_opts *opts)
{
- int i;
struct strbuf buf = STRBUF_INIT;
- for (i = 0; i < opts->xopts_nr; ++i)
- strbuf_addf(&buf, " --%s", opts->xopts[i]);
-
+ /*
+ * Quote strategy options so that they can be read correctly
+ * by split_cmdline().
+ */
+ quote_cmdline(&buf, opts->xopts.v);
write_file(rebase_path_strategy_opts(), "%s\n", buf.buf);
strbuf_release(&buf);
}
@@ -3096,7 +3093,7 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
write_file(rebase_path_verbose(), "%s", "");
if (opts->strategy)
write_file(rebase_path_strategy(), "%s\n", opts->strategy);
- if (opts->xopts_nr > 0)
+ if (opts->xopts.nr > 0)
write_strategy_opts(opts);
if (opts->allow_rerere_auto == RERERE_AUTOUPDATE)
@@ -3205,25 +3202,7 @@ static int create_seq_dir(struct repository *r)
static int save_head(const char *head)
{
- struct lock_file head_lock = LOCK_INIT;
- struct strbuf buf = STRBUF_INIT;
- int fd;
- ssize_t written;
-
- fd = hold_lock_file_for_update(&head_lock, git_path_head_file(), 0);
- if (fd < 0)
- return error_errno(_("could not lock HEAD"));
- strbuf_addf(&buf, "%s\n", head);
- written = write_in_full(fd, buf.buf, buf.len);
- strbuf_release(&buf);
- if (written < 0) {
- error_errno(_("could not write to '%s'"), git_path_head_file());
- rollback_lock_file(&head_lock);
- return -1;
- }
- if (commit_lock_file(&head_lock) < 0)
- return error(_("failed to finalize '%s'"), git_path_head_file());
- return 0;
+ return write_message(head, strlen(head), git_path_head_file(), 1);
}
static int rollback_is_safe(void)
@@ -3483,13 +3462,10 @@ static int save_opts(struct replay_opts *opts)
if (opts->gpg_sign)
res |= git_config_set_in_file_gently(opts_file,
"options.gpg-sign", opts->gpg_sign);
- if (opts->xopts) {
- int i;
- for (i = 0; i < opts->xopts_nr; i++)
- res |= git_config_set_multivar_in_file_gently(opts_file,
- "options.strategy-option",
- opts->xopts[i], "^$", 0);
- }
+ for (size_t i = 0; i < opts->xopts.nr; i++)
+ res |= git_config_set_multivar_in_file_gently(opts_file,
+ "options.strategy-option",
+ opts->xopts.v[i], "^$", 0);
if (opts->allow_rerere_auto)
res |= git_config_set_in_file_gently(opts_file,
"options.allow-rerere-auto",
@@ -3697,7 +3673,6 @@ static int safe_append(const char *filename, const char *fmt, ...)
}
if (commit_lock_file(&lock) < 0) {
strbuf_release(&buf);
- rollback_lock_file(&lock);
return error(_("failed to finalize '%s'"), filename);
}
@@ -3902,7 +3877,7 @@ static int do_merge(struct repository *r,
struct commit *head_commit, *merge_commit, *i;
struct commit_list *bases, *j;
struct commit_list *to_merge = NULL, **tail = &to_merge;
- const char *strategy = !opts->xopts_nr &&
+ const char *strategy = !opts->xopts.nr &&
(!opts->strategy ||
!strcmp(opts->strategy, "recursive") ||
!strcmp(opts->strategy, "ort")) ?
@@ -4086,9 +4061,9 @@ static int do_merge(struct repository *r,
strvec_push(&cmd.args, "octopus");
else {
strvec_push(&cmd.args, strategy);
- for (k = 0; k < opts->xopts_nr; k++)
+ for (k = 0; k < opts->xopts.nr; k++)
strvec_pushf(&cmd.args,
- "-X%s", opts->xopts[k]);
+ "-X%s", opts->xopts.v[k]);
}
if (!(flags & TODO_EDIT_MERGE_MSG))
strvec_push(&cmd.args, "--no-edit");
diff --git a/sequencer.h b/sequencer.h
index 33dbaf5b66..913a0f652d 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -2,6 +2,7 @@
#define SEQUENCER_H
#include "strbuf.h"
+#include "strvec.h"
#include "wt-status.h"
struct commit;
@@ -60,8 +61,7 @@ struct replay_opts {
/* Merge strategy */
char *default_strategy; /* from config options */
char *strategy;
- char **xopts;
- size_t xopts_nr, xopts_alloc;
+ struct strvec xopts;
/* Reflog */
char *reflog_action;
@@ -80,7 +80,12 @@ struct replay_opts {
/* Private use */
const char *reflog_message;
};
-#define REPLAY_OPTS_INIT { .edit = -1, .action = -1, .current_fixups = STRBUF_INIT }
+#define REPLAY_OPTS_INIT { \
+ .edit = -1, \
+ .action = -1, \
+ .current_fixups = STRBUF_INIT, \
+ .xopts = STRVEC_INIT, \
+}
/*
* Note that ordering matters in this enum. Not only must it match the mapping
@@ -247,7 +252,6 @@ int read_oneliner(struct strbuf *buf,
const char *path, unsigned flags);
int read_author_script(const char *path, char **name, char **email, char **date,
int allow_missing);
-void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);
int write_basic_state(struct replay_opts *opts, const char *head_name,
struct commit *onto, const struct object_id *orig_head);
void sequencer_post_commit_cleanup(struct repository *r, int verbose);
diff --git a/submodule.c b/submodule.c
index d09bc22d4b..2e78f51349 100644
--- a/submodule.c
+++ b/submodule.c
@@ -282,8 +282,7 @@ int is_tree_submodule_active(struct repository *repo,
free(key);
/* submodule.active is set */
- sl = repo_config_get_value_multi(repo, "submodule.active");
- if (sl) {
+ if (!repo_config_get_string_multi(repo, "submodule.active", &sl)) {
struct pathspec ps;
struct strvec args = STRVEC_INIT;
const struct string_list_item *item;
diff --git a/t/Makefile b/t/Makefile
index 2c2b252240..3e00cdd801 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -44,8 +44,8 @@ CHAINLINT = '$(PERL_PATH_SQ)' chainlint.pl
# `test-chainlint` (which is a dependency of `test-lint`, `test` and `prove`)
# checks all tests in all scripts via a single invocation, so tell individual
-# scripts not to "chainlint" themselves
-CHAINLINTSUPPRESS = GIT_TEST_CHAIN_LINT=0 && export GIT_TEST_CHAIN_LINT &&
+# scripts not to run the external "chainlint.pl" script themselves
+CHAINLINTSUPPRESS = GIT_TEST_EXT_CHAIN_LINT=0 && export GIT_TEST_EXT_CHAIN_LINT &&
all: $(DEFAULT_TEST_TARGET)
@@ -140,9 +140,7 @@ aggregate-results-and-cleanup: $(T)
$(MAKE) clean
aggregate-results:
- for f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \
- echo "$$f"; \
- done | '$(SHELL_PATH_SQ)' ./aggregate-results.sh
+ @'$(SHELL_PATH_SQ)' ./aggregate-results.sh '$(TEST_RESULTS_DIRECTORY_SQ)'
valgrind:
$(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind"
diff --git a/t/aggregate-results.sh b/t/aggregate-results.sh
index 7f2b83bdc8..6e3bcc4aec 100755
--- a/t/aggregate-results.sh
+++ b/t/aggregate-results.sh
@@ -8,7 +8,7 @@ broken=0
total=0
missing_prereq=
-while read file
+for file in "$1"/t*-*.counts
do
while read type value
do
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index f1b9a6ce4d..b35be20cf3 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -72,6 +72,16 @@ test_expect_success 'blame 1 author' '
check_count A 2
'
+test_expect_success 'blame with --contents' '
+ check_count --contents=file A 2
+'
+
+test_expect_success 'blame with --contents changed' '
+ echo "1A quick brown fox jumps over the" >contents &&
+ echo "another lazy dog" >>contents &&
+ check_count --contents=contents A 1 "Not Committed Yet" 1
+'
+
test_expect_success 'blame in a bare repo without starting commit' '
git clone --bare . bare.git &&
(
@@ -98,6 +108,10 @@ test_expect_success 'blame 2 authors' '
check_count A 2 B 2
'
+test_expect_success 'blame with --contents and revision' '
+ check_count -h testTag --contents=file A 2 "Not Committed Yet" 2
+'
+
test_expect_success 'setup B1 lines (branch1)' '
git checkout -b branch1 main &&
echo "3A slow green fox jumps into the" >>file &&
diff --git a/t/chainlint.pl b/t/chainlint.pl
index e966412999..556ee91a15 100755
--- a/t/chainlint.pl
+++ b/t/chainlint.pl
@@ -80,7 +80,8 @@ sub scan_heredoc_tag {
return "<<$indented" unless $token;
my $tag = $token->[0];
$tag =~ s/['"\\]//g;
- push(@{$self->{heretags}}, $indented ? "\t$tag" : "$tag");
+ $$token[0] = $indented ? "\t$tag" : "$tag";
+ push(@{$self->{heretags}}, $token);
return "<<$indented$tag";
}
@@ -169,10 +170,18 @@ sub swallow_heredocs {
my $tags = $self->{heretags};
while (my $tag = shift @$tags) {
my $start = pos($$b);
- my $indent = $tag =~ s/^\t// ? '\\s*' : '';
- $$b =~ /(?:\G|\n)$indent\Q$tag\E(?:\n|\z)/gc;
+ my $indent = $$tag[0] =~ s/^\t// ? '\\s*' : '';
+ $$b =~ /(?:\G|\n)$indent\Q$$tag[0]\E(?:\n|\z)/gc;
+ if (pos($$b) > $start) {
+ my $body = substr($$b, $start, pos($$b) - $start);
+ $self->{lineno} += () = $body =~ /\n/sg;
+ next;
+ }
+ push(@{$self->{parser}->{problems}}, ['UNCLOSED-HEREDOC', $tag]);
+ $$b =~ /(?:\G|\n).*\z/gc; # consume rest of input
my $body = substr($$b, $start, pos($$b) - $start);
$self->{lineno} += () = $body =~ /\n/sg;
+ last;
}
}
diff --git a/t/chainlint/unclosed-here-doc-indent.expect b/t/chainlint/unclosed-here-doc-indent.expect
new file mode 100644
index 0000000000..7c30a1a024
--- /dev/null
+++ b/t/chainlint/unclosed-here-doc-indent.expect
@@ -0,0 +1,4 @@
+command_which_is_run &&
+cat >expect <<-\EOF ?!UNCLOSED-HEREDOC?! &&
+we forget to end the here-doc
+command_which_is_gobbled
diff --git a/t/chainlint/unclosed-here-doc-indent.test b/t/chainlint/unclosed-here-doc-indent.test
new file mode 100644
index 0000000000..5c841a9dfd
--- /dev/null
+++ b/t/chainlint/unclosed-here-doc-indent.test
@@ -0,0 +1,4 @@
+command_which_is_run &&
+cat >expect <<-\EOF &&
+we forget to end the here-doc
+command_which_is_gobbled
diff --git a/t/chainlint/unclosed-here-doc.expect b/t/chainlint/unclosed-here-doc.expect
new file mode 100644
index 0000000000..d65e50f78d
--- /dev/null
+++ b/t/chainlint/unclosed-here-doc.expect
@@ -0,0 +1,7 @@
+command_which_is_run &&
+cat >expect <<\EOF ?!UNCLOSED-HEREDOC?! &&
+ we try to end the here-doc below,
+ but the indentation throws us off
+ since the operator is not "<<-".
+ EOF
+command_which_is_gobbled
diff --git a/t/chainlint/unclosed-here-doc.test b/t/chainlint/unclosed-here-doc.test
new file mode 100644
index 0000000000..69d3786c34
--- /dev/null
+++ b/t/chainlint/unclosed-here-doc.test
@@ -0,0 +1,7 @@
+command_which_is_run &&
+cat >expect <<\EOF &&
+ we try to end the here-doc below,
+ but the indentation throws us off
+ since the operator is not "<<-".
+ EOF
+command_which_is_gobbled
diff --git a/t/helper/test-config.c b/t/helper/test-config.c
index 32d170ec30..ad78fc1768 100644
--- a/t/helper/test-config.c
+++ b/t/helper/test-config.c
@@ -14,6 +14,8 @@
* get_value_multi -> prints all values for the entered key in increasing order
* of priority
*
+ * get -> print return value for the entered key
+ *
* get_int -> print integer value for the entered key or die
*
* get_bool -> print bool value for the entered key or die
@@ -30,6 +32,9 @@
* iterate -> iterate over all values using git_config(), and print some
* data for each
*
+ * git_config_int -> iterate over all values using git_config() and print the
+ * integer value for the entered key or die
+ *
* Examples:
*
* To print the value with highest priority for key "foo.bAr Baz.rock":
@@ -54,6 +59,17 @@ static int iterate_cb(const char *var, const char *value, void *data UNUSED)
return 0;
}
+static int parse_int_cb(const char *var, const char *value, void *data)
+{
+ const char *key_to_match = data;
+
+ if (!strcmp(key_to_match, var)) {
+ int parsed = git_config_int(value, value);
+ printf("%d\n", parsed);
+ }
+ return 0;
+}
+
static int early_config_cb(const char *var, const char *value, void *vdata)
{
const char *key = vdata;
@@ -95,8 +111,7 @@ int cmd__config(int argc, const char **argv)
goto exit1;
}
} else if (argc == 3 && !strcmp(argv[1], "get_value_multi")) {
- strptr = git_config_get_value_multi(argv[2]);
- if (strptr) {
+ if (!git_config_get_value_multi(argv[2], &strptr)) {
for (i = 0; i < strptr->nr; i++) {
v = strptr->items[i].string;
if (!v)
@@ -109,6 +124,26 @@ int cmd__config(int argc, const char **argv)
printf("Value not found for \"%s\"\n", argv[2]);
goto exit1;
}
+ } else if (argc == 3 && !strcmp(argv[1], "get")) {
+ int ret;
+
+ if (!(ret = git_config_get(argv[2])))
+ goto exit0;
+ else if (ret == 1)
+ printf("Value not found for \"%s\"\n", argv[2]);
+ else if (ret == -CONFIG_INVALID_KEY)
+ printf("Key \"%s\" is invalid\n", argv[2]);
+ else if (ret == -CONFIG_NO_SECTION_OR_NAME)
+ printf("Key \"%s\" has no section\n", argv[2]);
+ else
+ /*
+ * A normal caller should just check "ret <
+ * 0", but for our own tests let's BUG() if
+ * our whitelist of git_config_parse_key()
+ * return values isn't exhaustive.
+ */
+ BUG("Key \"%s\" has unknown return %d", argv[2], ret);
+ goto exit1;
} else if (argc == 3 && !strcmp(argv[1], "get_int")) {
if (!git_config_get_int(argv[2], &val)) {
printf("%d\n", val);
@@ -159,8 +194,7 @@ int cmd__config(int argc, const char **argv)
goto exit2;
}
}
- strptr = git_configset_get_value_multi(&cs, argv[2]);
- if (strptr) {
+ if (!git_configset_get_value_multi(&cs, argv[2], &strptr)) {
for (i = 0; i < strptr->nr; i++) {
v = strptr->items[i].string;
if (!v)
@@ -176,6 +210,9 @@ int cmd__config(int argc, const char **argv)
} else if (!strcmp(argv[1], "iterate")) {
git_config(iterate_cb, NULL);
goto exit0;
+ } else if (argc == 3 && !strcmp(argv[1], "git_config_int")) {
+ git_config(parse_int_cb, (void *) argv[2]);
+ goto exit0;
}
die("%s: Please check the syntax and the function name", argv[0]);
diff --git a/t/helper/test-ctype.c b/t/helper/test-ctype.c
index 534ca66441..71a1a5c9b0 100644
--- a/t/helper/test-ctype.c
+++ b/t/helper/test-ctype.c
@@ -47,7 +47,7 @@ static int is_in(const char *s, int ch)
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
"\x7f"
-int cmd__ctype(int argc, const char **argv)
+int cmd__ctype(int argc UNUSED, const char **argv UNUSED)
{
TEST_CLASS(isdigit, DIGIT);
TEST_CLASS(isspace, " \n\r\t");
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 7adadd41e0..0683d46574 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -81,7 +81,7 @@ static void parse_approxidate(const char **argv)
{
for (; *argv; argv++) {
timestamp_t t;
- t = approxidate_relative(*argv);
+ t = approxidate(*argv);
printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(ISO8601)));
}
}
@@ -90,7 +90,7 @@ static void parse_approx_timestamp(const char **argv)
{
for (; *argv; argv++) {
timestamp_t t;
- t = approxidate_relative(*argv);
+ t = approxidate(*argv);
printf("%s -> %"PRItime"\n", *argv, t);
}
}
@@ -104,7 +104,7 @@ static void getnanos(const char **argv)
printf("%lf\n", seconds);
}
-int cmd__date(int argc, const char **argv)
+int cmd__date(int argc UNUSED, const char **argv)
{
const char *x;
diff --git a/t/helper/test-drop-caches.c b/t/helper/test-drop-caches.c
index e37396dd9c..73e551cfc2 100644
--- a/t/helper/test-drop-caches.c
+++ b/t/helper/test-drop-caches.c
@@ -155,7 +155,7 @@ static int cmd_dropcaches(void)
#endif
-int cmd__drop_caches(int argc, const char **argv)
+int cmd__drop_caches(int argc UNUSED, const char **argv UNUSED)
{
cmd_sync();
return cmd_dropcaches();
diff --git a/t/helper/test-dump-cache-tree.c b/t/helper/test-dump-cache-tree.c
index 715aabfbae..2041ca1857 100644
--- a/t/helper/test-dump-cache-tree.c
+++ b/t/helper/test-dump-cache-tree.c
@@ -57,7 +57,7 @@ static int dump_cache_tree(struct cache_tree *it,
return errs;
}
-int cmd__dump_cache_tree(int ac, const char **av)
+int cmd__dump_cache_tree(int ac UNUSED, const char **av UNUSED)
{
struct index_state istate;
struct cache_tree *another = cache_tree();
diff --git a/t/helper/test-dump-fsmonitor.c b/t/helper/test-dump-fsmonitor.c
index 7e9de296db..7c6f50158b 100644
--- a/t/helper/test-dump-fsmonitor.c
+++ b/t/helper/test-dump-fsmonitor.c
@@ -2,7 +2,7 @@
#include "cache.h"
#include "setup.h"
-int cmd__dump_fsmonitor(int ac, const char **av)
+int cmd__dump_fsmonitor(int ac UNUSED, const char **av UNUSED)
{
struct index_state *istate = the_repository->index;
int i;
diff --git a/t/helper/test-dump-split-index.c b/t/helper/test-dump-split-index.c
index 289a01c10a..d1badd7112 100644
--- a/t/helper/test-dump-split-index.c
+++ b/t/helper/test-dump-split-index.c
@@ -11,7 +11,7 @@ static void show_bit(size_t pos, void *data)
printf(" %d", (int)pos);
}
-int cmd__dump_split_index(int ac, const char **av)
+int cmd__dump_split_index(int ac UNUSED, const char **av)
{
struct split_index *si;
int i;
diff --git a/t/helper/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c
index 415f55f31d..9225ced534 100644
--- a/t/helper/test-dump-untracked-cache.c
+++ b/t/helper/test-dump-untracked-cache.c
@@ -42,7 +42,7 @@ static void dump(struct untracked_cache_dir *ucd, struct strbuf *base)
strbuf_setlen(base, len);
}
-int cmd__dump_untracked_cache(int ac, const char **av)
+int cmd__dump_untracked_cache(int ac UNUSED, const char **av UNUSED)
{
struct untracked_cache *uc;
struct strbuf base = STRBUF_INIT;
diff --git a/t/helper/test-example-decorate.c b/t/helper/test-example-decorate.c
index 7c7fc8efc1..2cf302ffcb 100644
--- a/t/helper/test-example-decorate.c
+++ b/t/helper/test-example-decorate.c
@@ -3,7 +3,7 @@
#include "object.h"
#include "decorate.h"
-int cmd__example_decorate(int argc, const char **argv)
+int cmd__example_decorate(int argc UNUSED, const char **argv UNUSED)
{
struct decoration n;
struct object_id one_oid = { {1} };
diff --git a/t/helper/test-fsmonitor-client.c b/t/helper/test-fsmonitor-client.c
index a37236cd0a..14522b4c47 100644
--- a/t/helper/test-fsmonitor-client.c
+++ b/t/helper/test-fsmonitor-client.c
@@ -13,7 +13,7 @@
#include "wrapper.h"
#ifndef HAVE_FSMONITOR_DAEMON_BACKEND
-int cmd__fsmonitor_client(int argc, const char **argv)
+int cmd__fsmonitor_client(int argc UNUSED, const char **argv UNUSED)
{
die("fsmonitor--daemon not available on this platform");
}
diff --git a/t/helper/test-hexdump.c b/t/helper/test-hexdump.c
index 811e89c1bc..05f55eca21 100644
--- a/t/helper/test-hexdump.c
+++ b/t/helper/test-hexdump.c
@@ -4,7 +4,7 @@
/*
* Read stdin and print a hexdump to stdout.
*/
-int cmd__hexdump(int argc, const char **argv)
+int cmd__hexdump(int argc UNUSED, const char **argv UNUSED)
{
char buf[1024];
ssize_t i, len;
diff --git a/t/helper/test-index-version.c b/t/helper/test-index-version.c
index fcd10968cc..a06c45c1f8 100644
--- a/t/helper/test-index-version.c
+++ b/t/helper/test-index-version.c
@@ -1,7 +1,7 @@
#include "test-tool.h"
#include "cache.h"
-int cmd__index_version(int argc, const char **argv)
+int cmd__index_version(int argc UNUSED, const char **argv UNUSED)
{
struct cache_header hdr;
int version;
diff --git a/t/helper/test-match-trees.c b/t/helper/test-match-trees.c
index 3808e1ac38..35a2b8005c 100644
--- a/t/helper/test-match-trees.c
+++ b/t/helper/test-match-trees.c
@@ -5,7 +5,7 @@
#include "setup.h"
#include "tree.h"
-int cmd__match_trees(int ac, const char **av)
+int cmd__match_trees(int ac UNUSED, const char **av)
{
struct object_id hash1, hash2, shifted;
struct tree *one, *two;
diff --git a/t/helper/test-oid-array.c b/t/helper/test-oid-array.c
index fd6f73ea03..30e1947b90 100644
--- a/t/helper/test-oid-array.c
+++ b/t/helper/test-oid-array.c
@@ -10,7 +10,7 @@ static int print_oid(const struct object_id *oid, void *data)
return 0;
}
-int cmd__oid_array(int argc, const char **argv)
+int cmd__oid_array(int argc UNUSED, const char **argv UNUSED)
{
struct oid_array array = OID_ARRAY_INIT;
struct strbuf line = STRBUF_INIT;
diff --git a/t/helper/test-oidmap.c b/t/helper/test-oidmap.c
index de6ab77fda..1b0e7eecb4 100644
--- a/t/helper/test-oidmap.c
+++ b/t/helper/test-oidmap.c
@@ -23,7 +23,7 @@ struct test_entry {
* iterate -> oidkey1 namevalue1\noidkey2 namevalue2\n...
*
*/
-int cmd__oidmap(int argc, const char **argv)
+int cmd__oidmap(int argc UNUSED, const char **argv UNUSED)
{
struct strbuf line = STRBUF_INIT;
struct oidmap map = OIDMAP_INIT;
diff --git a/t/helper/test-oidtree.c b/t/helper/test-oidtree.c
index edcb7e9f44..5b98f2f70a 100644
--- a/t/helper/test-oidtree.c
+++ b/t/helper/test-oidtree.c
@@ -4,13 +4,13 @@
#include "oidtree.h"
#include "setup.h"
-static enum cb_next print_oid(const struct object_id *oid, void *data)
+static enum cb_next print_oid(const struct object_id *oid, void *data UNUSED)
{
puts(oid_to_hex(oid));
return CB_CONTINUE;
}
-int cmd__oidtree(int argc, const char **argv)
+int cmd__oidtree(int argc UNUSED, const char **argv UNUSED)
{
struct oidtree ot;
struct strbuf line = STRBUF_INIT;
diff --git a/t/helper/test-online-cpus.c b/t/helper/test-online-cpus.c
index 8cb0d53840..47dc211711 100644
--- a/t/helper/test-online-cpus.c
+++ b/t/helper/test-online-cpus.c
@@ -2,7 +2,7 @@
#include "git-compat-util.h"
#include "thread-utils.h"
-int cmd__online_cpus(int argc, const char **argv)
+int cmd__online_cpus(int argc UNUSED, const char **argv UNUSED)
{
printf("%d\n", online_cpus());
return 0;
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index 506835521a..b66039e575 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -263,14 +263,14 @@ int cmd__parse_options_flags(int argc, const char **argv)
return parse_options_flags__cmd(argc, argv, test_flags);
}
-static int subcmd_one(int argc, const char **argv, const char *prefix)
+static int subcmd_one(int argc, const char **argv, const char *prefix UNUSED)
{
printf("fn: subcmd_one\n");
print_args(argc, argv);
return 0;
}
-static int subcmd_two(int argc, const char **argv, const char *prefix)
+static int subcmd_two(int argc, const char **argv, const char *prefix UNUSED)
{
printf("fn: subcmd_two\n");
print_args(argc, argv);
diff --git a/t/helper/test-prio-queue.c b/t/helper/test-prio-queue.c
index 4915412e07..f0bf255f5f 100644
--- a/t/helper/test-prio-queue.c
+++ b/t/helper/test-prio-queue.c
@@ -16,7 +16,7 @@ static void show(int *v)
free(v);
}
-int cmd__prio_queue(int argc, const char **argv)
+int cmd__prio_queue(int argc UNUSED, const char **argv)
{
struct prio_queue pq = { intcmp };
diff --git a/t/helper/test-read-graph.c b/t/helper/test-read-graph.c
index 78965a6ebd..3ac496e27e 100644
--- a/t/helper/test-read-graph.c
+++ b/t/helper/test-read-graph.c
@@ -5,7 +5,7 @@
#include "bloom.h"
#include "setup.h"
-int cmd__read_graph(int argc, const char **argv)
+int cmd__read_graph(int argc UNUSED, const char **argv UNUSED)
{
struct commit_graph *graph = NULL;
struct object_directory *odb;
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index d1192c8c03..6d8f844e9c 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -201,7 +201,8 @@ static int cmd_verify_ref(struct ref_store *refs, const char **argv)
return ret;
}
-static int cmd_for_each_reflog(struct ref_store *refs, const char **argv)
+static int cmd_for_each_reflog(struct ref_store *refs,
+ const char **argv UNUSED)
{
return refs_for_each_reflog(refs, each_ref, NULL);
}
@@ -323,7 +324,7 @@ static struct command commands[] = {
{ NULL, NULL }
};
-int cmd__ref_store(int argc, const char **argv)
+int cmd__ref_store(int argc UNUSED, const char **argv)
{
struct ref_store *refs;
const char *func;
diff --git a/t/helper/test-scrap-cache-tree.c b/t/helper/test-scrap-cache-tree.c
index 15b7688774..3fecd06d17 100644
--- a/t/helper/test-scrap-cache-tree.c
+++ b/t/helper/test-scrap-cache-tree.c
@@ -6,7 +6,7 @@
#include "tree.h"
#include "cache-tree.h"
-int cmd__scrap_cache_tree(int ac, const char **av)
+int cmd__scrap_cache_tree(int ac UNUSED, const char **av UNUSED)
{
struct lock_file index_lock = LOCK_INIT;
diff --git a/t/helper/test-sigchain.c b/t/helper/test-sigchain.c
index d1cf7377b7..2d5ecf7383 100644
--- a/t/helper/test-sigchain.c
+++ b/t/helper/test-sigchain.c
@@ -13,7 +13,7 @@ X(two)
X(three)
#undef X
-int cmd__sigchain(int argc, const char **argv)
+int cmd__sigchain(int argc UNUSED, const char **argv UNUSED)
{
sigchain_push(SIGTERM, one);
sigchain_push(SIGTERM, two);
diff --git a/t/helper/test-strcmp-offset.c b/t/helper/test-strcmp-offset.c
index 44e4a6d143..96b9a5b529 100644
--- a/t/helper/test-strcmp-offset.c
+++ b/t/helper/test-strcmp-offset.c
@@ -1,7 +1,7 @@
#include "test-tool.h"
#include "cache.h"
-int cmd__strcmp_offset(int argc, const char **argv)
+int cmd__strcmp_offset(int argc UNUSED, const char **argv)
{
int result;
size_t offset;
diff --git a/t/helper/test-submodule-config.c b/t/helper/test-submodule-config.c
index c7c7fdbea9..5e462faa9d 100644
--- a/t/helper/test-submodule-config.c
+++ b/t/helper/test-submodule-config.c
@@ -5,7 +5,7 @@
#include "submodule-config.h"
#include "submodule.h"
-static void die_usage(int argc, const char **argv, const char *msg)
+static void die_usage(int argc UNUSED, const char **argv, const char *msg)
{
fprintf(stderr, "%s\n", msg);
fprintf(stderr, "Usage: %s [<commit> <submodulepath>] ...\n", argv[0]);
diff --git a/t/helper/test-submodule.c b/t/helper/test-submodule.c
index 0e34581b20..7cbd59922a 100644
--- a/t/helper/test-submodule.c
+++ b/t/helper/test-submodule.c
@@ -174,7 +174,7 @@ static int cmd__submodule_config_unset(int argc, const char **argv)
usage_with_options(usage, options);
}
-static int cmd__submodule_config_writeable(int argc, const char **argv)
+static int cmd__submodule_config_writeable(int argc, const char **argv UNUSED)
{
struct option options[] = {
OPT_END()
diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c
index a476df6c6c..98f071452a 100644
--- a/t/helper/test-trace2.c
+++ b/t/helper/test-trace2.c
@@ -208,7 +208,7 @@ static int ut_007BUG(int argc, const char **argv)
BUG("the bug message");
}
-static int ut_008bug(int argc, const char **argv)
+static int ut_008bug(int argc UNUSED, const char **argv UNUSED)
{
bug("a bug message");
bug("another bug message");
@@ -216,7 +216,7 @@ static int ut_008bug(int argc, const char **argv)
return 0;
}
-static int ut_009bug_BUG(int argc, const char **argv)
+static int ut_009bug_BUG(int argc UNUSED, const char **argv UNUSED)
{
bug("a bug message");
bug("another bug message");
@@ -224,7 +224,7 @@ static int ut_009bug_BUG(int argc, const char **argv)
return 0;
}
-static int ut_010bug_BUG(int argc, const char **argv)
+static int ut_010bug_BUG(int argc UNUSED, const char **argv UNUSED)
{
bug("a %s message", "bug");
BUG("a %s message", "BUG");
diff --git a/t/helper/test-xml-encode.c b/t/helper/test-xml-encode.c
index a648bbd961..b2f330d1a4 100644
--- a/t/helper/test-xml-encode.c
+++ b/t/helper/test-xml-encode.c
@@ -6,7 +6,7 @@ static const char *utf8_replace_character = "&#xfffd;";
* Encodes (possibly incorrect) UTF-8 on <stdin> to <stdout>, to be embedded
* in an XML file.
*/
-int cmd__xml_encode(int argc, const char **argv)
+int cmd__xml_encode(int argc UNUSED, const char **argv UNUSED)
{
unsigned char buf[1024], tmp[4], *tmp2 = NULL;
ssize_t cur = 0, len = 1, remaining = 0;
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index f43a25c1f1..9e6892970d 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -101,6 +101,8 @@ PassEnv LC_ALL
Alias /dumb/ www/
Alias /auth/dumb/ www/auth/dumb/
+SetEnv PERL_PATH ${PERL_PATH}
+
<LocationMatch /smart/>
SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
SetEnv GIT_HTTP_EXPORT_ALL
diff --git a/t/lib-httpd/apply-one-time-perl.sh b/t/lib-httpd/apply-one-time-perl.sh
index 09a0abdff7..d7f9fed6ae 100644
--- a/t/lib-httpd/apply-one-time-perl.sh
+++ b/t/lib-httpd/apply-one-time-perl.sh
@@ -13,7 +13,7 @@ then
export LC_ALL
"$GIT_EXEC_PATH/git-http-backend" >out
- perl -pe "$(cat one-time-perl)" out >out_modified
+ "$PERL_PATH" -pe "$(cat one-time-perl)" out >out_modified
if cmp -s out out_modified
then
diff --git a/t/perf/p1500-graph-walks.sh b/t/perf/p1500-graph-walks.sh
new file mode 100755
index 0000000000..e14e7620cc
--- /dev/null
+++ b/t/perf/p1500-graph-walks.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='Commit walk performance tests'
+. ./perf-lib.sh
+
+test_perf_large_repo
+
+test_expect_success 'setup' '
+ git for-each-ref --format="%(refname)" "refs/heads/*" "refs/tags/*" >allrefs &&
+ sort -r allrefs | head -n 50 >refs &&
+ for ref in $(cat refs)
+ do
+ git branch -f ref-$ref $ref &&
+ echo ref-$ref ||
+ return 1
+ done >branches &&
+ for ref in $(cat refs)
+ do
+ git tag -f tag-$ref $ref &&
+ echo tag-$ref ||
+ return 1
+ done >tags &&
+ git commit-graph write --reachable
+'
+
+test_perf 'ahead-behind counts: git for-each-ref' '
+ git for-each-ref --format="%(ahead-behind:HEAD)" --stdin <refs
+'
+
+test_perf 'ahead-behind counts: git branch' '
+ xargs git branch -l --format="%(ahead-behind:HEAD)" <branches
+'
+
+test_perf 'ahead-behind counts: git tag' '
+ xargs git tag -l --format="%(ahead-behind:HEAD)" <tags
+'
+
+test_perf 'contains: git for-each-ref --merged' '
+ git for-each-ref --merged=HEAD --stdin <refs
+'
+
+test_perf 'contains: git branch --merged' '
+ xargs git branch --merged=HEAD <branches
+'
+
+test_perf 'contains: git tag --merged' '
+ xargs git tag --merged=HEAD <tags
+'
+
+test_done
diff --git a/t/perf/p2000-sparse-operations.sh b/t/perf/p2000-sparse-operations.sh
index 3242cfe91a..60d1de0662 100755
--- a/t/perf/p2000-sparse-operations.sh
+++ b/t/perf/p2000-sparse-operations.sh
@@ -43,6 +43,7 @@ test_expect_success 'setup repo and indexes' '
done &&
git sparse-checkout init --cone &&
+ git tag -a v1.0 -m "Final" &&
git sparse-checkout set $SPARSE_CONE &&
git checkout -b wide $OLD_COMMIT &&
@@ -124,6 +125,9 @@ 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_perf_on_all git grep --cached bogus -- "f2/f1/f1/*"
+test_perf_on_all git write-tree
+test_perf_on_all git describe --dirty
+test_perf_on_all 'echo >>new && git describe --dirty'
test_done
diff --git a/t/t0068-for-each-repo.sh b/t/t0068-for-each-repo.sh
index 3648d439a8..4b90b74d5d 100755
--- a/t/t0068-for-each-repo.sh
+++ b/t/t0068-for-each-repo.sh
@@ -40,4 +40,23 @@ test_expect_success 'do nothing on empty config' '
git for-each-repo --config=bogus.config -- help --no-such-option
'
+test_expect_success 'error on bad config keys' '
+ test_expect_code 129 git for-each-repo --config=a &&
+ test_expect_code 129 git for-each-repo --config=a.b. &&
+ test_expect_code 129 git for-each-repo --config="'\''.b"
+'
+
+test_expect_success 'error on NULL value for config keys' '
+ cat >>.git/config <<-\EOF &&
+ [empty]
+ key
+ EOF
+ cat >expect <<-\EOF &&
+ error: missing value for '\''empty.key'\''
+ EOF
+ test_expect_code 129 git for-each-repo --config=empty.key 2>actual.raw &&
+ grep ^error actual.raw >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1005-read-tree-reset.sh b/t/t1005-read-tree-reset.sh
index 12e30d77d0..26be4a2b5a 100755
--- a/t/t1005-read-tree-reset.sh
+++ b/t/t1005-read-tree-reset.sh
@@ -41,7 +41,8 @@ test_expect_success 'reset should remove remnants from a failed merge' '
git ls-files -s &&
read_tree_u_must_succeed --reset -u HEAD &&
git ls-files -s >actual &&
- ! test -f old
+ ! test -f old &&
+ test_cmp expect actual
'
test_expect_success 'two-way reset should remove remnants too' '
@@ -56,7 +57,8 @@ test_expect_success 'two-way reset should remove remnants too' '
git ls-files -s &&
read_tree_u_must_succeed --reset -u HEAD HEAD &&
git ls-files -s >actual &&
- ! test -f old
+ ! test -f old &&
+ test_cmp expect actual
'
test_expect_success 'Porcelain reset should remove remnants too' '
@@ -71,7 +73,8 @@ test_expect_success 'Porcelain reset should remove remnants too' '
git ls-files -s &&
git reset --hard &&
git ls-files -s >actual &&
- ! test -f old
+ ! test -f old &&
+ test_cmp expect actual
'
test_expect_success 'Porcelain checkout -f should remove remnants too' '
@@ -86,7 +89,8 @@ test_expect_success 'Porcelain checkout -f should remove remnants too' '
git ls-files -s &&
git checkout -f &&
git ls-files -s >actual &&
- ! test -f old
+ ! test -f old &&
+ test_cmp expect actual
'
test_expect_success 'Porcelain checkout -f HEAD should remove remnants too' '
@@ -101,7 +105,8 @@ test_expect_success 'Porcelain checkout -f HEAD should remove remnants too' '
git ls-files -s &&
git checkout -f HEAD &&
git ls-files -s >actual &&
- ! test -f old
+ ! test -f old &&
+ test_cmp expect actual
'
test_done
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index 2d875b17d8..8eac74b59c 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -603,7 +603,8 @@ do
fatal: Not a valid object name $(test_oid deadbeef_short)
EOF
test_must_fail git cat-file $arg1 $arg2 $(test_oid deadbeef_short) >out 2>err.actual &&
- test_must_be_empty out
+ test_must_be_empty out &&
+ test_cmp expect.err err.actual
'
test_expect_success "cat-file $arg1 $arg2 error on missing full OID" '
diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh
index 3c08194526..22875ba598 100755
--- a/t/t1010-mktree.sh
+++ b/t/t1010-mktree.sh
@@ -60,11 +60,11 @@ test_expect_success 'allow missing object with --missing' '
'
test_expect_success 'mktree refuses to read ls-tree -r output (1)' '
- test_must_fail git mktree <all >actual
+ test_must_fail git mktree <all
'
test_expect_success 'mktree refuses to read ls-tree -r output (2)' '
- test_must_fail git mktree <all.withsub >actual
+ test_must_fail git mktree <all.withsub
'
test_done
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index 627267be15..9ceb17f911 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -555,7 +555,7 @@ test_expect_success 'cone mode: set with core.ignoreCase=true' '
check_files repo a folder1
'
-test_expect_success 'interaction with submodules' '
+test_expect_success 'setup submodules' '
git clone repo super &&
(
cd super &&
@@ -566,11 +566,22 @@ test_expect_success 'interaction with submodules' '
git commit -m "add submodule" &&
git sparse-checkout init --cone &&
git sparse-checkout set folder1
- ) &&
+ )
+'
+
+test_expect_success 'interaction with submodules' '
check_files super a folder1 modules &&
check_files super/modules/child a deep folder1 folder2
'
+test_expect_success 'check-rules interaction with submodules' '
+ git -C super ls-tree --name-only -r HEAD >all-files &&
+ git -C super sparse-checkout check-rules >check-rules-matches <all-files &&
+
+ test_i18ngrep ! "modules/" check-rules-matches &&
+ test_i18ngrep "folder1/" check-rules-matches
+'
+
test_expect_success 'different sparse-checkouts with worktrees' '
git -C repo sparse-checkout set --cone deep folder1 &&
git -C repo worktree add --detach ../worktree &&
@@ -882,4 +893,156 @@ test_expect_success 'by default, non-cone mode will warn on individual files' '
grep "pass a leading slash before paths.*if you want a single file" warning
'
+test_expect_success 'setup bare repo' '
+ git clone --bare "file://$(pwd)/repo" bare
+'
+test_expect_success 'list fails outside work tree' '
+ test_must_fail git -C bare sparse-checkout list 2>err &&
+ test_i18ngrep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'add fails outside work tree' '
+ test_must_fail git -C bare sparse-checkout add deeper 2>err &&
+ test_i18ngrep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'set fails outside work tree' '
+ test_must_fail git -C bare sparse-checkout set deeper 2>err &&
+ test_i18ngrep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'init fails outside work tree' '
+ test_must_fail git -C bare sparse-checkout init 2>err &&
+ test_i18ngrep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'reapply fails outside work tree' '
+ test_must_fail git -C bare sparse-checkout reapply 2>err &&
+ test_i18ngrep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'disable fails outside work tree' '
+ test_must_fail git -C bare sparse-checkout disable 2>err &&
+ test_i18ngrep "this operation must be run in a work tree" err
+'
+
+test_expect_success 'setup clean' '
+ git -C repo clean -fdx
+'
+
+test_expect_success 'check-rules cone mode' '
+ cat >rules <<-\EOF &&
+ folder1
+ deep/deeper1/deepest
+ EOF
+
+ git -C bare ls-tree -r --name-only HEAD >all-files &&
+ git -C bare sparse-checkout check-rules --cone \
+ --rules-file ../rules >check-rules-file <all-files &&
+
+ git -C repo sparse-checkout set --cone --stdin <rules&&
+ git -C repo ls-files -t >out &&
+ sed -n "/^S /!s/^. //p" out >ls-files &&
+
+ git -C repo sparse-checkout check-rules >check-rules-default <all-files &&
+
+ test_i18ngrep "deep/deeper1/deepest/a" check-rules-file &&
+ test_i18ngrep ! "deep/deeper2" check-rules-file &&
+
+ test_cmp check-rules-file ls-files &&
+ test_cmp check-rules-file check-rules-default
+'
+
+test_expect_success 'check-rules non-cone mode' '
+ cat >rules <<-\EOF &&
+ deep/deeper1/deepest/a
+ EOF
+
+ git -C bare ls-tree -r --name-only HEAD >all-files &&
+ git -C bare sparse-checkout check-rules --no-cone --rules-file ../rules\
+ >check-rules-file <all-files &&
+
+ cat rules | git -C repo sparse-checkout set --no-cone --stdin &&
+ git -C repo ls-files -t >out &&
+ sed -n "/^S /!s/^. //p" out >ls-files &&
+
+ git -C repo sparse-checkout check-rules >check-rules-default <all-files &&
+
+ cat >expect <<-\EOF &&
+ deep/deeper1/deepest/a
+ EOF
+
+ test_cmp expect check-rules-file &&
+ test_cmp check-rules-file ls-files &&
+ test_cmp check-rules-file check-rules-default
+'
+
+test_expect_success 'check-rules cone mode is default' '
+ cat >rules <<-\EOF &&
+ folder1
+ EOF
+
+ cat >all-files <<-\EOF &&
+ toplevel
+ folder2/file
+ folder1/file
+ EOF
+
+ cat >expect <<-\EOF &&
+ toplevel
+ folder1/file
+ EOF
+
+ git -C repo sparse-checkout set --no-cone &&
+ git -C repo sparse-checkout check-rules \
+ --rules-file ../rules >actual <all-files &&
+
+ git -C bare sparse-checkout check-rules \
+ --rules-file ../rules >actual-bare <all-files &&
+
+ test_cmp expect actual &&
+ test_cmp expect actual-bare
+'
+
+test_expect_success 'check-rules quoting' '
+ cat >rules <<-EOF &&
+ "folder\" a"
+ EOF
+ cat >files <<-EOF &&
+ "folder\" a/file"
+ "folder\" b/file"
+ EOF
+ cat >expect <<-EOF &&
+ "folder\" a/file"
+ EOF
+ git sparse-checkout check-rules --cone \
+ --rules-file rules >actual <files &&
+
+ test_cmp expect actual
+'
+
+test_expect_success 'check-rules null termination' '
+ cat >rules <<-EOF &&
+ "folder\" a"
+ EOF
+
+ lf_to_nul >files <<-EOF &&
+ folder" a/a
+ folder" a/b
+ folder" b/fileQ
+ EOF
+
+ cat >expect <<-EOF &&
+ folder" a/aQfolder" a/bQ
+ EOF
+
+ git sparse-checkout check-rules --cone -z \
+ --rules-file rules >actual.nul <files &&
+ nul_to_q <actual.nul >actual &&
+ echo >>actual &&
+
+ test_cmp expect actual
+'
+
+
test_done
diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
index 801919009e..0c784813f1 100755
--- a/t/t1092-sparse-checkout-compatibility.sh
+++ b/t/t1092-sparse-checkout-compatibility.sh
@@ -1514,6 +1514,31 @@ test_expect_success 'sparse-index is not expanded: stash' '
ensure_not_expanded stash pop
'
+test_expect_success 'describe tested on all' '
+ init_repos &&
+
+ # Add tag to be read by describe
+
+ run_on_all git tag -a v1.0 -m "Version 1" &&
+ test_all_match git describe --dirty &&
+ run_on_all rm g &&
+ test_all_match git describe --dirty
+'
+
+
+test_expect_success 'sparse-index is not expanded: describe' '
+ init_repos &&
+
+ # Add tag to be read by describe
+
+ git -C sparse-index tag -a v1.0 -m "Version 1" &&
+
+ ensure_not_expanded describe --dirty &&
+ echo "test" >>sparse-index/g &&
+ ensure_not_expanded describe --dirty &&
+ ensure_not_expanded describe
+'
+
test_expect_success 'sparse index is not expanded: diff' '
init_repos &&
@@ -2055,4 +2080,32 @@ test_expect_success 'grep sparse directory within submodules' '
test_cmp actual expect
'
+test_expect_success 'write-tree on all' '
+ init_repos &&
+
+ write_script edit-contents <<-\EOF &&
+ echo text >>"$1"
+ EOF
+
+ run_on_all ../edit-contents deep/a &&
+ run_on_all git update-index deep/a &&
+ test_all_match git write-tree &&
+
+ run_on_all mkdir -p folder1 &&
+ run_on_all cp a folder1/a &&
+ run_on_all ../edit-contents folder1/a &&
+ run_on_all git update-index folder1/a &&
+ test_all_match git write-tree
+'
+
+test_expect_success 'sparse-index is not expanded: write-tree' '
+ init_repos &&
+
+ ensure_not_expanded write-tree &&
+
+ echo "test1" >>sparse-index/a &&
+ git -C sparse-index update-index a &&
+ ensure_not_expanded write-tree
+'
+
test_done
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 2575279ab8..f1d42b62b0 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -617,6 +617,36 @@ test_expect_success 'renaming to bogus section is rejected' '
test_must_fail git config --rename-section branch.zwei "bogus name"
'
+test_expect_success 'renaming a section with a long line' '
+ {
+ printf "[b]\\n" &&
+ printf " c = d %1024s [a] e = f\\n" " " &&
+ printf "[a] g = h\\n"
+ } >y &&
+ git config -f y --rename-section a xyz &&
+ test_must_fail git config -f y b.e
+'
+
+test_expect_success 'renaming an embedded section with a long line' '
+ {
+ printf "[b]\\n" &&
+ printf " c = d %1024s [a] [foo] e = f\\n" " " &&
+ printf "[a] g = h\\n"
+ } >y &&
+ git config -f y --rename-section a xyz &&
+ test_must_fail git config -f y foo.e
+'
+
+test_expect_success 'renaming a section with an overly-long line' '
+ {
+ printf "[b]\\n" &&
+ printf " c = d %525000s e" " " &&
+ printf "[a] g = h\\n"
+ } >y &&
+ test_must_fail git config -f y --rename-section a xyz 2>err &&
+ grep "refusing to work with overly long line in .y. on line 2" err
+'
+
cat >> .git/config << EOF
[branch "zwei"] a = 1 [branch "vier"]
EOF
diff --git a/t/t1302-repo-version.sh b/t/t1302-repo-version.sh
index 70389fa2eb..179474fa65 100755
--- a/t/t1302-repo-version.sh
+++ b/t/t1302-repo-version.sh
@@ -37,7 +37,7 @@ test_expect_success 'gitdir selection on normal repos' '
test_expect_success 'gitdir selection on unsupported repo' '
# Make sure it would stop at test2, not trash
- test_expect_code 1 git -C test2 config core.repositoryformatversion >actual
+ test_expect_code 1 git -C test2 config core.repositoryformatversion
'
test_expect_success 'gitdir not required mode' '
diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh
index b38e158d3b..777648722c 100755
--- a/t/t1308-config-set.sh
+++ b/t/t1308-config-set.sh
@@ -58,6 +58,8 @@ test_expect_success 'setup default config' '
skin = false
nose = 1
horns
+ [value]
+ less
EOF
'
@@ -116,10 +118,53 @@ test_expect_success 'find value with the highest priority' '
check_config get_value case.baz "hask"
'
+test_expect_success 'return value for an existing key' '
+ test-tool config get lamb.chop >out 2>err &&
+ test_must_be_empty out &&
+ test_must_be_empty err
+'
+
+test_expect_success 'return value for value-less key' '
+ test-tool config get value.less >out 2>err &&
+ test_must_be_empty out &&
+ test_must_be_empty err
+'
+
+test_expect_success 'return value for a missing key' '
+ cat >expect <<-\EOF &&
+ Value not found for "missing.key"
+ EOF
+ test_expect_code 1 test-tool config get missing.key >actual 2>err &&
+ test_cmp actual expect &&
+ test_must_be_empty err
+'
+
+test_expect_success 'return value for a bad key: CONFIG_INVALID_KEY' '
+ cat >expect <<-\EOF &&
+ Key "fails.iskeychar.-" is invalid
+ EOF
+ test_expect_code 1 test-tool config get fails.iskeychar.- >actual 2>err &&
+ test_cmp actual expect &&
+ test_must_be_empty out
+'
+
+test_expect_success 'return value for a bad key: CONFIG_NO_SECTION_OR_NAME' '
+ cat >expect <<-\EOF &&
+ Key "keynosection" has no section
+ EOF
+ test_expect_code 1 test-tool config get keynosection >actual 2>err &&
+ test_cmp actual expect &&
+ test_must_be_empty out
+'
+
test_expect_success 'find integer value for a key' '
check_config get_int lamb.chop 65
'
+test_expect_success 'parse integer value during iteration' '
+ check_config git_config_int lamb.chop 65
+'
+
test_expect_success 'find string value for a key' '
check_config get_string case.baz hask &&
check_config expect_code 1 get_string case.ba "Value not found for \"case.ba\""
@@ -134,6 +179,11 @@ test_expect_success 'find integer if value is non parse-able' '
check_config expect_code 128 get_int lamb.head
'
+test_expect_success 'non parse-able integer value during iteration' '
+ check_config expect_code 128 git_config_int lamb.head 2>result &&
+ grep "fatal: bad numeric config value .* in file \.git/config" result
+'
+
test_expect_success 'find bool value for the entered key' '
check_config get_bool goat.head 1 &&
check_config get_bool goat.skin 0 &&
@@ -146,6 +196,71 @@ test_expect_success 'find multiple values' '
check_config get_value_multi case.baz sam bat hask
'
+test_NULL_in_multi () {
+ local op="$1" &&
+ local file="$2" &&
+
+ test_expect_success "$op: NULL value in config${file:+ in $file}" '
+ config="$file" &&
+ if test -z "$config"
+ then
+ config=.git/config &&
+ test_when_finished "mv $config.old $config" &&
+ mv "$config" "$config".old
+ fi &&
+
+ # Value-less in the middle of a list
+ cat >"$config" <<-\EOF &&
+ [a]key=x
+ [a]key
+ [a]key=y
+ EOF
+ case "$op" in
+ *_multi)
+ cat >expect <<-\EOF
+ x
+ (NULL)
+ y
+ EOF
+ ;;
+ *)
+ cat >expect <<-\EOF
+ y
+ EOF
+ ;;
+ esac &&
+ test-tool config "$op" a.key $file >actual &&
+ test_cmp expect actual &&
+
+ # Value-less at the end of a least
+ cat >"$config" <<-\EOF &&
+ [a]key=x
+ [a]key=y
+ [a]key
+ EOF
+ case "$op" in
+ *_multi)
+ cat >expect <<-\EOF
+ x
+ y
+ (NULL)
+ EOF
+ ;;
+ *)
+ cat >expect <<-\EOF
+ (NULL)
+ EOF
+ ;;
+ esac &&
+ test-tool config "$op" a.key $file >actual &&
+ test_cmp expect actual
+ '
+}
+
+test_NULL_in_multi "get_value_multi"
+test_NULL_in_multi "configset_get_value" "my.config"
+test_NULL_in_multi "configset_get_value_multi" "my.config"
+
test_expect_success 'find value from a configset' '
cat >config2 <<-\EOF &&
[case]
@@ -207,7 +322,7 @@ test_expect_success 'proper error on error in default config files' '
cp .git/config .git/config.old &&
test_when_finished "mv .git/config.old .git/config" &&
echo "[" >>.git/config &&
- echo "fatal: bad config line 34 in file .git/config" >expect &&
+ echo "fatal: bad config line 36 in file .git/config" >expect &&
test_expect_code 128 test-tool config get_value foo.bar 2>actual &&
test_cmp expect actual
'
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index cf58cf025c..4d66cd7f4a 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -1568,6 +1568,7 @@ test_expect_success 'transaction can create and delete' '
EOF
git update-ref --stdin <stdin >actual &&
printf "%s: ok\n" start commit start commit >expect &&
+ test_cmp expect actual &&
test_must_fail git show-ref --verify refs/heads/create-and-delete
'
@@ -1595,6 +1596,8 @@ test_expect_success 'transaction cannot restart ongoing transaction' '
commit
EOF
test_must_fail git update-ref --stdin <stdin >actual &&
+ printf "%s: ok\n" start >expect &&
+ test_cmp expect actual &&
test_must_fail git show-ref --verify refs/heads/restart
'
diff --git a/t/t1404-update-ref-errors.sh b/t/t1404-update-ref-errors.sh
index b5606d93b5..937ae0d733 100755
--- a/t/t1404-update-ref-errors.sh
+++ b/t/t1404-update-ref-errors.sh
@@ -551,7 +551,6 @@ test_expect_success REFFILES 'no bogus intermediate values during delete' '
git update-ref $prefix/foo $C &&
git pack-refs --all &&
git update-ref $prefix/foo $D &&
- git for-each-ref $prefix >unchanged &&
# Now try to update the reference, but hold the `packed-refs` lock
# for a while to see what happens while the process is blocked:
: >.git/packed-refs.lock &&
diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh
index c34714ffe3..d94c72c672 100755
--- a/t/t1507-rev-parse-upstream.sh
+++ b/t/t1507-rev-parse-upstream.sh
@@ -183,6 +183,11 @@ test_expect_success '@{u} error message when no upstream' '
test_cmp expect actual
'
+test_expect_success '@{u} silent error when no upstream' '
+ test_must_fail git rev-parse --verify --quiet @{u} 2>actual &&
+ test_must_be_empty actual
+'
+
test_expect_success 'branch@{u} error message with misspelt branch' '
cat >expect <<-EOF &&
fatal: no such branch: ${SQ}no-such-branch${SQ}
@@ -258,7 +263,8 @@ test_expect_success '@{reflog}-parsing does not look beyond colon' '
git add @{yesterday} &&
git commit -m "funny reflog file" &&
git hash-object @{yesterday} >expect &&
- git rev-parse HEAD:@{yesterday} >actual
+ git rev-parse HEAD:@{yesterday} >actual &&
+ test_cmp expect actual
'
test_expect_success '@{upstream}-parsing does not look beyond colon' '
@@ -266,7 +272,8 @@ test_expect_success '@{upstream}-parsing does not look beyond colon' '
git add @{upstream} &&
git commit -m "funny upstream file" &&
git hash-object @{upstream} >expect &&
- git rev-parse HEAD:@{upstream} >actual
+ git rev-parse HEAD:@{upstream} >actual &&
+ test_cmp expect actual
'
test_done
diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh
index 4a1c901456..74049a9812 100755
--- a/t/t2024-checkout-dwim.sh
+++ b/t/t2024-checkout-dwim.sh
@@ -305,10 +305,13 @@ test_expect_success 'loosely defined local base branch is reported correctly' '
test_config branch.strict.merge refs/heads/main &&
test_config branch.loose.merge main &&
- git checkout strict | sed -e "s/strict/BRANCHNAME/g" >expect &&
+ git checkout strict >expect.raw 2>&1 &&
+ sed -e "s/strict/BRANCHNAME/g" <expect.raw >expect &&
status_uno_is_clean &&
- git checkout loose | sed -e "s/loose/BRANCHNAME/g" >actual &&
+ git checkout loose >actual.raw 2>&1 &&
+ sed -e "s/loose/BRANCHNAME/g" <actual.raw >actual &&
status_uno_is_clean &&
+ grep BRANCHNAME actual &&
test_cmp expect actual
'
diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh
index 07e6de84e6..89b285fa3a 100755
--- a/t/t2107-update-index-basic.sh
+++ b/t/t2107-update-index-basic.sh
@@ -83,7 +83,7 @@ test_expect_success '.lock files cleaned up' '
cd repo &&
git config core.worktree ../../worktree &&
# --refresh triggers late setup_work_tree,
- # active_cache_changed is zero, rollback_lock_file fails
+ # the_index.cache_changed is zero, rollback_lock_file fails
git update-index --refresh --verbose >out &&
test_must_be_empty out &&
! test -f .git/index.lock
diff --git a/t/t3013-ls-files-format.sh b/t/t3013-ls-files-format.sh
index efb7450bf1..ef6fb53f7f 100755
--- a/t/t3013-ls-files-format.sh
+++ b/t/t3013-ls-files-format.sh
@@ -54,6 +54,22 @@ test_expect_success 'git ls-files --format path v.s. -s' '
test_cmp expect actual
'
+test_expect_success 'git ls-files --format with relative path' '
+ cat >expect <<-\EOF &&
+ ../o1.txt
+ ../o2.txt
+ ../o3.txt
+ ../o4.txt
+ ../o5.txt
+ ../o6.txt
+ EOF
+ mkdir sub &&
+ cd sub &&
+ git ls-files --format="%(path)" ":/" >../actual &&
+ cd .. &&
+ test_cmp expect actual
+'
+
test_expect_success 'git ls-files --format with -m' '
echo change >o1.txt &&
cat >expect <<-\EOF &&
diff --git a/t/t3060-ls-files-with-tree.sh b/t/t3060-ls-files-with-tree.sh
index c4a72ae446..5a06732ca7 100755
--- a/t/t3060-ls-files-with-tree.sh
+++ b/t/t3060-ls-files-with-tree.sh
@@ -40,7 +40,7 @@ test_expect_success 'setup' '
git commit -a -m "remove them all" &&
# The bug also requires some entry before our directory so that
- # prune_path will modify the_index.cache
+ # prune_index will modify the_repository->index.cache
mkdir a_directory_that_sorts_before_sub &&
>a_directory_that_sorts_before_sub/file &&
@@ -56,7 +56,7 @@ test_expect_success 'usage' '
'
test_expect_success 'git ls-files --with-tree should succeed from subdir' '
- # We have to run from a sub-directory to trigger prune_path
+ # We have to run from a sub-directory to trigger prune_index
# Then we finally get to run our --with-tree test
(
cd sub &&
diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
index 5d871fde96..4dd42df38c 100755
--- a/t/t3070-wildmatch.sh
+++ b/t/t3070-wildmatch.sh
@@ -431,4 +431,15 @@ match 1 1 1 1 'a' '[B-a]'
match 0 1 0 1 'z' '[Z-y]'
match 1 1 1 1 'Z' '[Z-y]'
+test_expect_success 'matching does not exhibit exponential behavior' '
+ {
+ test-tool wildmatch wildmatch \
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab \
+ "*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a" &
+ pid=$!
+ } &&
+ sleep 2 &&
+ ! kill $!
+'
+
test_done
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index d34d77f893..93f8295339 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -337,10 +337,48 @@ test_expect_success 'git branch --format option' '
test_cmp expect actual
'
+test_expect_success 'git branch --format with ahead-behind' '
+ cat >expect <<-\EOF &&
+ (HEAD detached from fromtag) 0 0
+ refs/heads/ambiguous 0 0
+ refs/heads/branch-one 1 0
+ refs/heads/branch-two 0 0
+ refs/heads/main 1 0
+ refs/heads/ref-to-branch 1 0
+ refs/heads/ref-to-remote 1 0
+ EOF
+ git branch --format="%(refname) %(ahead-behind:HEAD)" >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'git branch with --format=%(rest) must fail' '
test_must_fail git branch --format="%(rest)" >actual
'
+test_expect_success 'git branch --format --omit-empty' '
+ cat >expect <<-\EOF &&
+ Refname is (HEAD detached from fromtag)
+ Refname is refs/heads/ambiguous
+ Refname is refs/heads/branch-one
+ Refname is refs/heads/branch-two
+
+ Refname is refs/heads/ref-to-branch
+ Refname is refs/heads/ref-to-remote
+ EOF
+ git branch --format="%(if:notequals=refs/heads/main)%(refname)%(then)Refname is %(refname)%(end)" >actual &&
+ test_cmp expect actual &&
+ cat >expect <<-\EOF &&
+ Refname is (HEAD detached from fromtag)
+ Refname is refs/heads/ambiguous
+ Refname is refs/heads/branch-one
+ Refname is refs/heads/branch-two
+ Refname is refs/heads/ref-to-branch
+ Refname is refs/heads/ref-to-remote
+ EOF
+ git branch --omit-empty --format="%(if:notequals=refs/heads/main)%(refname)%(then)Refname is %(refname)%(end)" >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'worktree colors correct' '
cat >expect <<-EOF &&
* <GREEN>(HEAD detached from fromtag)<RESET>
diff --git a/t/t3309-notes-merge-auto-resolve.sh b/t/t3309-notes-merge-auto-resolve.sh
index 141d3e4ca4..9bd5dbf341 100755
--- a/t/t3309-notes-merge-auto-resolve.sh
+++ b/t/t3309-notes-merge-auto-resolve.sh
@@ -360,7 +360,12 @@ test_expect_success 'merge z into y with invalid strategy => Fail/No changes' '
test_expect_success 'merge z into y with invalid configuration option => Fail/No changes' '
git config core.notesRef refs/notes/y &&
- test_must_fail git -c notes.mergeStrategy="foo" notes merge z &&
+ cat >expect <<-\EOF &&
+ error: unknown notes merge strategy foo
+ fatal: unable to parse '\''notes.mergeStrategy'\'' from command-line config
+ EOF
+ test_must_fail git -c notes.mergeStrategy="foo" notes merge z 2>actual &&
+ test_cmp expect actual &&
# Verify no changes (y)
verify_notes y y
'
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
index 7e46f4ca85..79b0640c00 100755
--- a/t/t3402-rebase-merge.sh
+++ b/t/t3402-rebase-merge.sh
@@ -131,27 +131,6 @@ test_expect_success 'picking rebase' '
esac
'
-test_expect_success 'rebase -s funny -Xopt' '
- test_when_finished "rm -fr test-bin funny.was.run" &&
- mkdir test-bin &&
- cat >test-bin/git-merge-funny <<-EOF &&
- #!$SHELL_PATH
- case "\$1" in --opt) ;; *) exit 2 ;; esac
- shift &&
- >funny.was.run &&
- exec git merge-recursive "\$@"
- EOF
- chmod +x test-bin/git-merge-funny &&
- git reset --hard &&
- git checkout -b test-funny main^ &&
- test_commit funny &&
- (
- PATH=./test-bin:$PATH &&
- git rebase -s funny -Xopt main
- ) &&
- test -f funny.was.run
-'
-
test_expect_success 'rebase --skip works with two conflicts in a row' '
git checkout second-side &&
tr "[A-Z]" "[a-z]" <newfile >tmp &&
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
index 130e2f9b55..2d0789e554 100755
--- a/t/t3418-rebase-continue.sh
+++ b/t/t3418-rebase-continue.sh
@@ -62,61 +62,39 @@ test_expect_success 'rebase --continue remembers merge strategy and options' '
rm -fr .git/rebase-* &&
git reset --hard commit-new-file-F2-on-topic-branch &&
test_commit "commit-new-file-F3-on-topic-branch" F3 32 &&
- test_when_finished "rm -fr test-bin funny.was.run" &&
+ test_when_finished "rm -fr test-bin" &&
mkdir test-bin &&
- cat >test-bin/git-merge-funny <<-EOF &&
- #!$SHELL_PATH
- case "\$1" in --opt) ;; *) exit 2 ;; esac
- shift &&
- >funny.was.run &&
- exec git merge-recursive "\$@"
+
+ write_script test-bin/git-merge-funny <<-\EOF &&
+ printf "[%s]\n" $# "$1" "$2" "$3" "$5" >actual
+ shift 3 &&
+ exec git merge-recursive "$@"
EOF
- chmod +x test-bin/git-merge-funny &&
- (
- PATH=./test-bin:$PATH &&
- test_must_fail git rebase -s funny -Xopt main topic
- ) &&
- test -f funny.was.run &&
- rm funny.was.run &&
- echo "Resolved" >F2 &&
- git add F2 &&
- (
- PATH=./test-bin:$PATH &&
- git rebase --continue
- ) &&
- test -f funny.was.run
-'
-test_expect_success 'rebase -i --continue handles merge strategy and options' '
- rm -fr .git/rebase-* &&
- git reset --hard commit-new-file-F2-on-topic-branch &&
- test_commit "commit-new-file-F3-on-topic-branch-for-dash-i" F3 32 &&
- test_when_finished "rm -fr test-bin funny.was.run funny.args" &&
- mkdir test-bin &&
- cat >test-bin/git-merge-funny <<-EOF &&
- #!$SHELL_PATH
- echo "\$@" >>funny.args
- case "\$1" in --opt) ;; *) exit 2 ;; esac
- case "\$2" in --foo) ;; *) exit 2 ;; esac
- case "\$4" in --) ;; *) exit 2 ;; esac
- shift 2 &&
- >funny.was.run &&
- exec git merge-recursive "\$@"
+ cat >expect <<-\EOF &&
+ [7]
+ [--option=arg with space]
+ [--op"tion\]
+ [--new
+ line ]
+ [--]
EOF
- chmod +x test-bin/git-merge-funny &&
+
+ rm -f actual &&
(
PATH=./test-bin:$PATH &&
- test_must_fail git rebase -i -s funny -Xopt -Xfoo main topic
+ test_must_fail git rebase -s funny -X"option=arg with space" \
+ -Xop\"tion\\ -X"new${LF}line " main topic
) &&
- test -f funny.was.run &&
- rm funny.was.run &&
+ test_cmp expect actual &&
+ rm actual &&
echo "Resolved" >F2 &&
git add F2 &&
(
PATH=./test-bin:$PATH &&
git rebase --continue
) &&
- test -f funny.was.run
+ test_cmp expect actual
'
test_expect_success 'rebase -r passes merge strategy options correctly' '
diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
index 4711b37a28..2eba00bdf5 100755
--- a/t/t3422-rebase-incompatible-options.sh
+++ b/t/t3422-rebase-incompatible-options.sh
@@ -85,6 +85,11 @@ test_rebase_am_only () {
test_must_fail git rebase $opt --reapply-cherry-picks A
"
+ test_expect_success "$opt incompatible with --rebase-merges" "
+ git checkout B^0 &&
+ test_must_fail git rebase $opt --rebase-merges A
+ "
+
test_expect_success "$opt incompatible with --update-refs" "
git checkout B^0 &&
test_must_fail git rebase $opt --update-refs A
@@ -101,6 +106,12 @@ test_rebase_am_only () {
grep -e --no-autosquash err
"
+ test_expect_success "$opt incompatible with rebase.rebaseMerges" "
+ git checkout B^0 &&
+ test_must_fail git -c rebase.rebaseMerges=true rebase $opt A 2>err &&
+ grep -e --no-rebase-merges err
+ "
+
test_expect_success "$opt incompatible with rebase.updateRefs" "
git checkout B^0 &&
test_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err &&
@@ -113,6 +124,12 @@ test_rebase_am_only () {
git -c rebase.autosquash=true rebase --no-autosquash $opt A
"
+ test_expect_success "$opt okay with overridden rebase.rebaseMerges" "
+ test_when_finished \"git reset --hard B^0\" &&
+ git checkout B^0 &&
+ git -c rebase.rebaseMerges=true rebase --no-rebase-merges $opt A
+ "
+
test_expect_success "$opt okay with overridden rebase.updateRefs" "
test_when_finished \"git reset --hard B^0\" &&
git checkout B^0 &&
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index fa2a06c19f..f03599c63b 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -250,6 +250,16 @@ test_expect_success 'with a branch tip that was cherry-picked already' '
EOF
'
+test_expect_success '--no-rebase-merges countermands --rebase-merges' '
+ git checkout -b no-rebase-merges E &&
+ git rebase --rebase-merges --no-rebase-merges C &&
+ test_cmp_graph C.. <<-\EOF
+ * B
+ * D
+ o C
+ EOF
+'
+
test_expect_success 'do not rebase cousins unless asked for' '
git checkout -b cousins main &&
before="$(git rev-parse --verify HEAD)" &&
@@ -268,6 +278,40 @@ test_expect_success 'do not rebase cousins unless asked for' '
EOF
'
+test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
+ test_config rebase.rebaseMerges rebase-cousins &&
+ git checkout -b config-rebase-cousins main &&
+ git rebase HEAD^ &&
+ test_cmp_graph HEAD^.. <<-\EOF
+ * Merge the topic branch '\''onebranch'\''
+ |\
+ | * D
+ | * G
+ |/
+ o H
+ EOF
+'
+
+test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '
+ test_config rebase.rebaseMerges no-rebase-cousins &&
+ git checkout -b override-config-no-rebase-cousins E &&
+ git rebase --no-rebase-merges C &&
+ test_cmp_graph C.. <<-\EOF
+ * B
+ * D
+ o C
+ EOF
+'
+
+test_expect_success '--rebase-merges overrides rebase.rebaseMerges=rebase-cousins' '
+ test_config rebase.rebaseMerges rebase-cousins &&
+ git checkout -b override-config-rebase-cousins E &&
+ before="$(git rev-parse --verify HEAD)" &&
+ test_tick &&
+ git rebase --rebase-merges C &&
+ test_cmp_rev HEAD $before
+'
+
test_expect_success 'refs/rewritten/* is worktree-local' '
git worktree add wt &&
cat >wt/script-from-scratch <<-\EOF &&
diff --git a/t/t3436-rebase-more-options.sh b/t/t3436-rebase-more-options.sh
index c3184c9ade..94671d3c46 100755
--- a/t/t3436-rebase-more-options.sh
+++ b/t/t3436-rebase-more-options.sh
@@ -40,24 +40,6 @@ test_expect_success 'setup' '
EOF
'
-test_expect_success 'bad -X <strategy-option> arguments: unclosed quote' '
- cat >expect <<-\EOF &&
- fatal: could not split '\''--bad'\'': unclosed quote
- EOF
- test_expect_code 128 git rebase -X"bad argument\"" side main >out 2>actual &&
- test_must_be_empty out &&
- test_cmp expect actual
-'
-
-test_expect_success 'bad -X <strategy-option> arguments: bad escape' '
- cat >expect <<-\EOF &&
- fatal: could not split '\''--bad'\'': cmdline ends with \
- EOF
- test_expect_code 128 git rebase -X"bad escape \\" side main >out 2>actual &&
- test_must_be_empty out &&
- test_cmp expect actual
-'
-
test_expect_success '--ignore-whitespace works with apply backend' '
test_must_fail git rebase --apply main side &&
git rebase --abort &&
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index b8aaece860..3982b6b49d 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -7,12 +7,6 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh
-if test_have_prereq !PERL
-then
- skip_all='skipping add -i (scripted) tests, perl not available'
- test_done
-fi
-
diff_cmp () {
for x
do
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index dfcf3a0aaa..5de1d19075 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -616,4 +616,46 @@ test_expect_success 'diff -I<regex>: detect malformed regex' '
test_i18ngrep "invalid regex given to -I: " error
'
+# check_prefix <patch> <src> <dst>
+# check only lines with paths to avoid dependency on exact oid/contents
+check_prefix () {
+ grep -E '^(diff|---|\+\+\+) ' "$1" >actual.paths &&
+ cat >expect <<-EOF &&
+ diff --git $2 $3
+ --- $2
+ +++ $3
+ EOF
+ test_cmp expect actual.paths
+}
+
+test_expect_success 'diff-files does not respect diff.noprefix' '
+ git -c diff.noprefix diff-files -p >actual &&
+ check_prefix actual a/file0 b/file0
+'
+
+test_expect_success 'diff-files respects --no-prefix' '
+ git diff-files -p --no-prefix >actual &&
+ check_prefix actual file0 file0
+'
+
+test_expect_success 'diff respects diff.noprefix' '
+ git -c diff.noprefix diff >actual &&
+ check_prefix actual file0 file0
+'
+
+test_expect_success 'diff --default-prefix overrides diff.noprefix' '
+ git -c diff.noprefix diff --default-prefix >actual &&
+ check_prefix actual a/file0 b/file0
+'
+
+test_expect_success 'diff respects diff.mnemonicprefix' '
+ git -c diff.mnemonicprefix diff >actual &&
+ check_prefix actual i/file0 w/file0
+'
+
+test_expect_success 'diff --default-prefix overrides diff.mnemonicprefix' '
+ git -c diff.mnemonicprefix diff --default-prefix >actual &&
+ check_prefix actual a/file0 b/file0
+'
+
test_done
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index ffc7c60680..3cf2b7a7fb 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -455,13 +455,13 @@ test_expect_success 'no threading' '
cat >expect.thread <<EOF
---
-Message-Id: <0>
+Message-ID: <0>
---
-Message-Id: <1>
+Message-ID: <1>
In-Reply-To: <0>
References: <0>
---
-Message-Id: <2>
+Message-ID: <2>
In-Reply-To: <0>
References: <0>
EOF
@@ -470,17 +470,22 @@ test_expect_success 'thread' '
check_threading expect.thread --thread main
'
+test_expect_success '--thread overrides format.thread=deep' '
+ test_config format.thread deep &&
+ check_threading expect.thread --thread main
+'
+
cat >expect.in-reply-to <<EOF
---
-Message-Id: <0>
+Message-ID: <0>
In-Reply-To: <1>
References: <1>
---
-Message-Id: <2>
+Message-ID: <2>
In-Reply-To: <1>
References: <1>
---
-Message-Id: <3>
+Message-ID: <3>
In-Reply-To: <1>
References: <1>
EOF
@@ -492,17 +497,17 @@ test_expect_success 'thread in-reply-to' '
cat >expect.cover-letter <<EOF
---
-Message-Id: <0>
+Message-ID: <0>
---
-Message-Id: <1>
+Message-ID: <1>
In-Reply-To: <0>
References: <0>
---
-Message-Id: <2>
+Message-ID: <2>
In-Reply-To: <0>
References: <0>
---
-Message-Id: <3>
+Message-ID: <3>
In-Reply-To: <0>
References: <0>
EOF
@@ -513,21 +518,21 @@ test_expect_success 'thread cover-letter' '
cat >expect.cl-irt <<EOF
---
-Message-Id: <0>
+Message-ID: <0>
In-Reply-To: <1>
References: <1>
---
-Message-Id: <2>
+Message-ID: <2>
In-Reply-To: <0>
References: <1>
<0>
---
-Message-Id: <3>
+Message-ID: <3>
In-Reply-To: <0>
References: <1>
<0>
---
-Message-Id: <4>
+Message-ID: <4>
In-Reply-To: <0>
References: <1>
<0>
@@ -545,13 +550,13 @@ test_expect_success 'thread explicit shallow' '
cat >expect.deep <<EOF
---
-Message-Id: <0>
+Message-ID: <0>
---
-Message-Id: <1>
+Message-ID: <1>
In-Reply-To: <0>
References: <0>
---
-Message-Id: <2>
+Message-ID: <2>
In-Reply-To: <1>
References: <0>
<1>
@@ -563,16 +568,16 @@ test_expect_success 'thread deep' '
cat >expect.deep-irt <<EOF
---
-Message-Id: <0>
+Message-ID: <0>
In-Reply-To: <1>
References: <1>
---
-Message-Id: <2>
+Message-ID: <2>
In-Reply-To: <0>
References: <1>
<0>
---
-Message-Id: <3>
+Message-ID: <3>
In-Reply-To: <2>
References: <1>
<0>
@@ -586,18 +591,18 @@ test_expect_success 'thread deep in-reply-to' '
cat >expect.deep-cl <<EOF
---
-Message-Id: <0>
+Message-ID: <0>
---
-Message-Id: <1>
+Message-ID: <1>
In-Reply-To: <0>
References: <0>
---
-Message-Id: <2>
+Message-ID: <2>
In-Reply-To: <1>
References: <0>
<1>
---
-Message-Id: <3>
+Message-ID: <3>
In-Reply-To: <2>
References: <0>
<1>
@@ -610,22 +615,22 @@ test_expect_success 'thread deep cover-letter' '
cat >expect.deep-cl-irt <<EOF
---
-Message-Id: <0>
+Message-ID: <0>
In-Reply-To: <1>
References: <1>
---
-Message-Id: <2>
+Message-ID: <2>
In-Reply-To: <0>
References: <1>
<0>
---
-Message-Id: <3>
+Message-ID: <3>
In-Reply-To: <2>
References: <1>
<0>
<2>
---
-Message-Id: <4>
+Message-ID: <4>
In-Reply-To: <3>
References: <1>
<0>
@@ -2396,4 +2401,20 @@ test_expect_success 'interdiff: solo-patch' '
test_cmp expect actual
'
+test_expect_success 'format-patch does not respect diff.noprefix' '
+ git -c diff.noprefix format-patch -1 --stdout >actual &&
+ grep "^--- a/blorp" actual
+'
+
+test_expect_success 'format-patch respects format.noprefix' '
+ git -c format.noprefix format-patch -1 --stdout >actual &&
+ grep "^--- blorp" actual
+'
+
+test_expect_success 'format-patch --default-prefix overrides format.noprefix' '
+ git -c format.noprefix \
+ format-patch -1 --default-prefix --stdout >actual &&
+ grep "^--- a/blorp" actual
+'
+
test_done
diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
index 15764ee9ac..74586f3813 100755
--- a/t/t4034-diff-words.sh
+++ b/t/t4034-diff-words.sh
@@ -69,6 +69,10 @@ test_language_driver () {
echo "* diff='"$lang"'" >.gitattributes &&
word_diff --color-words
'
+ test_expect_success "diff driver '$lang' in Islandic" '
+ LANG=is_IS.UTF-8 LANGUAGE=is LC_ALL="$is_IS_locale" \
+ word_diff --color-words
+ '
}
test_expect_success setup '
diff --git a/t/t4115-apply-symlink.sh b/t/t4115-apply-symlink.sh
index 65ac7df2d7..e95e6d4e7d 100755
--- a/t/t4115-apply-symlink.sh
+++ b/t/t4115-apply-symlink.sh
@@ -126,4 +126,19 @@ test_expect_success SYMLINKS 'symlink escape when deleting file' '
test_path_is_file .git/delete-me
'
+test_expect_success SYMLINKS '--reject removes .rej symlink if it exists' '
+ test_when_finished "git reset --hard && git clean -dfx" &&
+
+ test_commit file &&
+ echo modified >file.t &&
+ git diff -- file.t >patch &&
+ echo modified-again >file.t &&
+
+ ln -s foo file.t.rej &&
+ test_must_fail git apply patch --reject 2>err &&
+ test_i18ngrep "Rejected hunk" err &&
+ test_path_is_missing foo &&
+ test_path_is_file file.t.rej
+'
+
test_done
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 78cf1c880e..2935fe1b2d 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -103,7 +103,7 @@ test_expect_success setup '
git format-patch --stdout first >patch1 &&
{
- echo "Message-Id: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>" &&
+ echo "Message-ID: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>" &&
echo "X-Fake-Field: Line One" &&
echo "X-Fake-Field: Line Two" &&
echo "X-Fake-Field: Line Three" &&
@@ -942,7 +942,7 @@ test_expect_success 'am --message-id really adds the message id' '
git am --message-id patch1.eml &&
test_path_is_missing .git/rebase-apply &&
git cat-file commit HEAD | tail -n1 >actual &&
- grep Message-Id patch1.eml >expected &&
+ grep Message-ID patch1.eml >expected &&
test_cmp expected actual
'
@@ -954,7 +954,7 @@ test_expect_success 'am.messageid really adds the message id' '
git am patch1.eml &&
test_path_is_missing .git/rebase-apply &&
git cat-file commit HEAD | tail -n1 >actual &&
- grep Message-Id patch1.eml >expected &&
+ grep Message-ID patch1.eml >expected &&
test_cmp expected actual
'
@@ -965,7 +965,7 @@ test_expect_success 'am --message-id -s signs off after the message id' '
git am -s --message-id patch1.eml &&
test_path_is_missing .git/rebase-apply &&
git cat-file commit HEAD | tail -n2 | head -n1 >actual &&
- grep Message-Id patch1.eml >expected &&
+ grep Message-ID patch1.eml >expected &&
test_cmp expected actual
'
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 2ce2b41174..ae73aef922 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -835,6 +835,21 @@ test_expect_success 'log.decorate configuration' '
'
+test_expect_success 'parse log.excludeDecoration with no value' '
+ cp .git/config .git/config.orig &&
+ test_when_finished mv .git/config.orig .git/config &&
+
+ cat >>.git/config <<-\EOF &&
+ [log]
+ excludeDecoration
+ EOF
+ cat >expect <<-\EOF &&
+ error: missing value for '\''log.excludeDecoration'\''
+ EOF
+ git log --decorate=short 2>actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'decorate-refs with glob' '
cat >expect.decorate <<-\EOF &&
Merge-tag-reach
diff --git a/t/t4258/mbox b/t/t4258/mbox
index c62819f3d2..1ae528ba78 100644
--- a/t/t4258/mbox
+++ b/t/t4258/mbox
@@ -2,7 +2,7 @@ From: A U Thor <mail@example.com>
To: list@example.org
Subject: [PATCH v2] sample
Date: Mon, 3 Aug 2020 22:40:55 +0700
-Message-Id: <msg-id@example.com>
+Message-ID: <msg-id@example.com>
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 918a2fc7c6..4b4c3315d8 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -185,6 +185,7 @@ test_expect_success 'git archive' '
'
check_tar b
+check_mtime b a/a 1117231200
test_expect_success 'git archive --mtime' '
git archive --mtime=2002-02-02T02:02:02-0200 HEAD >with_mtime.tar
@@ -257,14 +258,6 @@ test_expect_success 'git archive --remote with configured remote' '
test_cmp_bin b.tar b5-nick.tar
'
-test_expect_success 'validate file modification time' '
- mkdir extract &&
- "$TAR" xf b.tar -C extract a/a &&
- test-tool chmtime --get extract/a/a >b.mtime &&
- echo "1117231200" >expected.mtime &&
- test_cmp expected.mtime b.mtime
-'
-
test_expect_success 'git get-tar-commit-id' '
git get-tar-commit-id <b.tar >actual &&
git rev-parse HEAD >expect &&
@@ -433,6 +426,19 @@ test_expect_success 'catch non-matching pathspec' '
test_must_fail git archive -v HEAD -- "*.abc" >/dev/null
'
+test_expect_success 'reject paths outside the current directory' '
+ test_must_fail git -C a/bin archive HEAD .. >/dev/null 2>err &&
+ grep "outside the current directory" err
+'
+
+test_expect_success 'allow pathspecs that resolve to the current directory' '
+ git -C a/bin archive -v HEAD ../bin >/dev/null 2>actual &&
+ cat >expect <<-\EOF &&
+ sh
+ EOF
+ test_cmp expect actual
+'
+
# Pull the size and date of each entry in a tarfile using the system tar.
#
# We'll pull out only the year from the date; that avoids any question of
diff --git a/t/t5001-archive-attr.sh b/t/t5001-archive-attr.sh
index 04d300eeda..0ff47a239d 100755
--- a/t/t5001-archive-attr.sh
+++ b/t/t5001-archive-attr.sh
@@ -33,6 +33,13 @@ test_expect_success 'setup' '
echo ignored-by-tree.d export-ignore >>.gitattributes &&
git add ignored-by-tree ignored-by-tree.d .gitattributes &&
+ mkdir subdir &&
+ >subdir/included &&
+ >subdir/ignored-by-subtree &&
+ >subdir/ignored-by-tree &&
+ echo ignored-by-subtree export-ignore >subdir/.gitattributes &&
+ git add subdir &&
+
echo ignored by worktree >ignored-by-worktree &&
echo ignored-by-worktree export-ignore >.gitattributes &&
git add ignored-by-worktree &&
@@ -93,6 +100,15 @@ test_expect_exists archive-pathspec-wildcard/ignored-by-worktree
test_expect_missing archive-pathspec-wildcard/excluded-by-pathspec.d
test_expect_missing archive-pathspec-wildcard/excluded-by-pathspec.d/file
+test_expect_success 'git -C subdir archive' '
+ git -C subdir archive HEAD >archive-subdir.tar &&
+ extract_tar_to_dir archive-subdir
+'
+
+test_expect_exists archive-subdir/included
+test_expect_missing archive-subdir/ignored-by-subtree
+test_expect_missing archive-subdir/ignored-by-tree
+
test_expect_success 'git archive with worktree attributes' '
git archive --worktree-attributes HEAD >worktree.tar &&
(mkdir worktree && cd worktree && "$TAR" xf -) <worktree.tar
diff --git a/t/t5100/msg0002 b/t/t5100/msg0002
index e2546ec733..1089382425 100644
--- a/t/t5100/msg0002
+++ b/t/t5100/msg0002
@@ -3,7 +3,7 @@ message:
From: Nit Picker <nit.picker@example.net>
Subject: foo is too old
-Message-Id: <nitpicker.12121212@example.net>
+Message-ID: <nitpicker.12121212@example.net>
Hopefully this would fix the problem stated there.
diff --git a/t/t5100/msg0003 b/t/t5100/msg0003
index 1ac68101b1..3402b534a6 100644
--- a/t/t5100/msg0003
+++ b/t/t5100/msg0003
@@ -3,7 +3,7 @@ message:
From: Nit Picker <nit.picker@example.net>
Subject: foo is too old
-Message-Id: <nitpicker.12121212@example.net>
+Message-ID: <nitpicker.12121212@example.net>
Hopefully this would fix the problem stated there.
diff --git a/t/t5100/msg0012--message-id b/t/t5100/msg0012--message-id
index 376e26e9ae..44482958ce 100644
--- a/t/t5100/msg0012--message-id
+++ b/t/t5100/msg0012--message-id
@@ -5,4 +5,4 @@ docutils заменён на python-docutils
python-docutils. В то время как сам rest2web не нужен.
Signed-off-by: Dmitriy Blinov <bda@mnsspb.ru>
-Message-Id: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
+Message-ID: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
diff --git a/t/t5100/quoted-cr.mbox b/t/t5100/quoted-cr.mbox
index 909021bb7a..a529d4de08 100644
--- a/t/t5100/quoted-cr.mbox
+++ b/t/t5100/quoted-cr.mbox
@@ -3,7 +3,7 @@ From: A U Thor <mail@example.com>
To: list@example.org
Subject: [PATCH v2] sample
Date: Mon, 3 Aug 2020 22:40:55 +0700
-Message-Id: <msg-id@example.com>
+Message-ID: <msg-id@example.com>
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64
@@ -27,7 +27,7 @@ From: A U Thor <mail@example.com>
To: list@example.org
Subject: [PATCH v2] sample
Date: Mon, 3 Aug 2020 22:40:55 +0700
-Message-Id: <msg-id2@example.com>
+Message-ID: <msg-id2@example.com>
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64
diff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox
index 6d4d0e4474..4a54ee5171 100644
--- a/t/t5100/sample.mbox
+++ b/t/t5100/sample.mbox
@@ -35,7 +35,7 @@ message:
From: Nit Picker <nit.picker@example.net>
Subject: foo is too old
-Message-Id: <nitpicker.12121212@example.net>
+Message-ID: <nitpicker.12121212@example.net>
Hopefully this would fix the problem stated there.
@@ -78,7 +78,7 @@ message:
From: Nit Picker <nit.picker@example.net>
Subject: foo is too old
-Message-Id: <nitpicker.12121212@example.net>
+Message-ID: <nitpicker.12121212@example.net>
Hopefully this would fix the problem stated there.
@@ -508,7 +508,7 @@ From bda@mnsspb.ru Wed Nov 12 17:54:41 2008
From: Dmitriy Blinov <bda@mnsspb.ru>
To: navy-patches@dinar.mns.mnsspb.ru
Date: Wed, 12 Nov 2008 17:54:41 +0300
-Message-Id: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
+Message-ID: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
X-Mailer: git-send-email 1.5.6.5
MIME-Version: 1.0
Content-Type: text/plain;
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index d65a5f94b4..5500dd0842 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -72,8 +72,16 @@ test_expect_success 'gc: implicit prune --expire' '
'
test_expect_success 'gc: refuse to start with invalid gc.pruneExpire' '
- git config gc.pruneExpire invalid &&
- test_must_fail git gc
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ >repo/.git/config &&
+ git -C repo config gc.pruneExpire invalid &&
+ cat >expect <<-\EOF &&
+ error: Invalid gc.pruneexpire: '\''invalid'\''
+ fatal: bad config variable '\''gc.pruneexpire'\'' in file '\''.git/config'\'' at line 2
+ EOF
+ test_must_fail git -C repo gc 2>actual &&
+ test_cmp expect actual
'
test_expect_success 'gc: start with ok gc.pruneExpire' '
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
index 7d8dee41b0..526a5a506e 100755
--- a/t/t5310-pack-bitmaps.sh
+++ b/t/t5310-pack-bitmaps.sh
@@ -404,6 +404,26 @@ test_bitmap_cases () {
)
'
+ test_expect_success 'pack.preferBitmapTips' '
+ git init repo &&
+ test_when_finished "rm -rf repo" &&
+ (
+ cd repo &&
+ git config pack.writeBitmapLookupTable '"$writeLookupTable"' &&
+ test_commit_bulk --message="%s" 103 &&
+
+ cat >>.git/config <<-\EOF &&
+ [pack]
+ preferBitmapTips
+ EOF
+ cat >expect <<-\EOF &&
+ error: missing value for '\''pack.preferbitmaptips'\''
+ EOF
+ git repack -adb 2>actual &&
+ test_cmp expect actual
+ )
+ '
+
test_expect_success 'complains about multiple pack bitmaps' '
rm -fr repo &&
git init repo &&
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh
index 049c5fc8ea..b6e1211578 100755
--- a/t/t5318-commit-graph.sh
+++ b/t/t5318-commit-graph.sh
@@ -630,7 +630,7 @@ test_expect_success 'detect incorrect generation number' '
test_expect_success 'detect incorrect generation number' '
corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\01" \
- "non-zero generation number"
+ "commit-graph generation for commit"
'
test_expect_success 'detect incorrect commit date' '
diff --git a/t/t5328-commit-graph-64bit-time.sh b/t/t5328-commit-graph-64bit-time.sh
index 093f0c067a..57e4d9c699 100755
--- a/t/t5328-commit-graph-64bit-time.sh
+++ b/t/t5328-commit-graph-64bit-time.sh
@@ -63,4 +63,13 @@ test_expect_success 'set up and verify repo with generation data overflow chunk'
graph_git_behavior 'overflow 2' repo left right
+test_expect_success 'single commit with generation data exceeding UINT32_MAX' '
+ git init repo-uint32-max &&
+ cd repo-uint32-max &&
+ test_commit --date "@4294967297 +0000" 1 &&
+ git commit-graph write --reachable &&
+ graph_read_expect 1 "generation_data" &&
+ git commit-graph verify
+'
+
test_done
diff --git a/t/t5552-skipping-fetch-negotiator.sh b/t/t5552-skipping-fetch-negotiator.sh
index 165427d57e..b55a9f65e6 100755
--- a/t/t5552-skipping-fetch-negotiator.sh
+++ b/t/t5552-skipping-fetch-negotiator.sh
@@ -3,6 +3,22 @@
test_description='test skipping fetch negotiator'
. ./test-lib.sh
+test_expect_success 'fetch.negotiationalgorithm config' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ cat >repo/.git/config <<-\EOF &&
+ [fetch]
+ negotiationAlgorithm
+ EOF
+ cat >expect <<-\EOF &&
+ error: missing value for '\''fetch.negotiationalgorithm'\''
+ fatal: bad config variable '\''fetch.negotiationalgorithm'\'' in file '\''.git/config'\'' at line 2
+ EOF
+ test_expect_code 128 git -C repo fetch >out 2>actual &&
+ test_must_be_empty out &&
+ test_cmp expect actual
+'
+
have_sent () {
while test "$#" -ne 0
do
diff --git a/t/t5558-clone-bundle-uri.sh b/t/t5558-clone-bundle-uri.sh
index afd56926c5..996a08e90c 100755
--- a/t/t5558-clone-bundle-uri.sh
+++ b/t/t5558-clone-bundle-uri.sh
@@ -1018,6 +1018,40 @@ test_expect_success 'creationToken heuristic with failed downloads (fetch)' '
test_cmp expect refs
'
+test_expect_success 'bundles are downloaded once during fetch --all' '
+ test_when_finished rm -rf download-* trace*.txt fetch-mult &&
+
+ cat >"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+ [bundle]
+ version = 1
+ mode = all
+ heuristic = creationToken
+
+ [bundle "bundle-1"]
+ uri = bundle-1.bundle
+ creationToken = 1
+
+ [bundle "bundle-2"]
+ uri = bundle-2.bundle
+ creationToken = 2
+
+ [bundle "bundle-3"]
+ uri = bundle-3.bundle
+ creationToken = 3
+ EOF
+
+ git clone --single-branch --branch=left \
+ --bundle-uri="$HTTPD_URL/bundle-list" \
+ "$HTTPD_URL/smart/fetch.git" fetch-mult &&
+ git -C fetch-mult remote add dup1 "$HTTPD_URL/smart/fetch.git" &&
+ git -C fetch-mult remote add dup2 "$HTTPD_URL/smart/fetch.git" &&
+
+ GIT_TRACE2_EVENT="$(pwd)/trace-mult.txt" \
+ git -C fetch-mult fetch --all &&
+ grep "\"child_start\".*\"git-remote-https\",\"$HTTPD_URL/bundle-list\"" \
+ trace-mult.txt >bundle-fetches &&
+ test_line_count = 1 bundle-fetches
+'
# Do not add tests here unless they use the HTTP server, as they will
# not run unless the HTTP dependencies exist.
diff --git a/t/t5563-simple-http-auth.sh b/t/t5563-simple-http-auth.sh
index ccf7e54b07..f45a43b4b5 100755
--- a/t/t5563-simple-http-auth.sh
+++ b/t/t5563-simple-http-auth.sh
@@ -252,15 +252,14 @@ test_expect_success 'access using basic auth with wwwauth header empty continuat
# Note that leading and trailing whitespace is important to correctly
# simulate a continuation/folded header.
- printf "">$CHALLENGE &&
- printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >$CHALLENGE &&
- printf " \r\n" >>$CHALLENGE &&
- printf " param2=\"value2\"\r\n" >>$CHALLENGE &&
- printf "WWW-Authenticate: Bearer authorize_uri=\"id.example.com\"\r\n" >>$CHALLENGE &&
- printf " p=1\r\n" >>$CHALLENGE &&
- printf " \r\n" >>$CHALLENGE &&
- printf " q=0\r\n" >>$CHALLENGE &&
- printf "WWW-Authenticate: Basic realm=\"example.com\"\r\n" >>$CHALLENGE &&
+ printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >"$CHALLENGE" &&
+ printf " \r\n" >>"$CHALLENGE" &&
+ printf " param2=\"value2\"\r\n" >>"$CHALLENGE" &&
+ printf "WWW-Authenticate: Bearer authorize_uri=\"id.example.com\"\r\n" >>"$CHALLENGE" &&
+ printf " p=1\r\n" >>"$CHALLENGE" &&
+ printf " \r\n" >>"$CHALLENGE" &&
+ printf " q=0\r\n" >>"$CHALLENGE" &&
+ printf "WWW-Authenticate: Basic realm=\"example.com\"\r\n" >>"$CHALLENGE" &&
test_config_global credential.helper test-helper &&
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
@@ -298,11 +297,10 @@ test_expect_success 'access using basic auth with wwwauth header mixed line-endi
# Note that leading and trailing whitespace is important to correctly
# simulate a continuation/folded header.
- printf "">$CHALLENGE &&
- printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >$CHALLENGE &&
- printf " \r\n" >>$CHALLENGE &&
- printf "\tparam2=\"value2\"\r\n" >>$CHALLENGE &&
- printf "WWW-Authenticate: Basic realm=\"example.com\"" >>$CHALLENGE &&
+ printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >"$CHALLENGE" &&
+ printf " \r\n" >>"$CHALLENGE" &&
+ printf "\tparam2=\"value2\"\r\n" >>"$CHALLENGE" &&
+ printf "WWW-Authenticate: Basic realm=\"example.com\"" >>"$CHALLENGE" &&
test_config_global credential.helper test-helper &&
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
diff --git a/t/t5604-clone-reference.sh b/t/t5604-clone-reference.sh
index 83e3c97861..9845fc04d5 100755
--- a/t/t5604-clone-reference.sh
+++ b/t/t5604-clone-reference.sh
@@ -358,7 +358,7 @@ test_expect_success SYMLINKS 'clone repo with symlinked objects directory' '
test_must_fail git clone --local malicious clone 2>err &&
test_path_is_missing clone &&
- grep "failed to start iterator over" err
+ grep "is a symlink, refusing to clone with --local" err
'
test_done
diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh
index e4db7513f4..6af5c2062f 100755
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -269,6 +269,17 @@ test_expect_success 'clone propagates unborn HEAD from non-empty repo' '
grep "warning: remote HEAD refers to nonexistent ref" stderr
'
+test_expect_success 'clone propagates object-format from empty repo' '
+ test_when_finished "rm -fr src256 dst256" &&
+
+ echo sha256 >expect &&
+ git init --object-format=sha256 src256 &&
+ git clone src256 dst256 &&
+ git -C dst256 rev-parse --show-object-format >actual &&
+
+ test_cmp expect actual
+'
+
test_expect_success 'bare clone propagates unborn HEAD from non-empty repo' '
test_when_finished "rm -rf file_unborn_parent file_unborn_child.git" &&
@@ -728,6 +739,33 @@ test_expect_success 'file:// --negotiate-only with protocol v0' '
test_i18ngrep "negotiate-only requires protocol v2" err
'
+test_expect_success 'push with custom path does not request v2' '
+ rm -f env.trace &&
+ git -C client push \
+ --receive-pack="env >../env.trace; git-receive-pack" \
+ origin HEAD:refs/heads/custom-push-test &&
+ test_path_is_file env.trace &&
+ ! grep ^GIT_PROTOCOL env.trace
+'
+
+test_expect_success 'fetch with custom path does request v2' '
+ rm -f env.trace &&
+ git -C client fetch \
+ --upload-pack="env >../env.trace; git-upload-pack" \
+ origin HEAD &&
+ grep ^GIT_PROTOCOL=version=2 env.trace
+'
+
+test_expect_success 'archive with custom path does not request v2' '
+ rm -f env.trace &&
+ git -C client archive \
+ --exec="env >../env.trace; git-upload-archive" \
+ --remote=origin \
+ HEAD >/dev/null &&
+ test_path_is_file env.trace &&
+ ! grep ^GIT_PROTOCOL env.trace
+'
+
# Test protocol v2 with 'http://' transport
#
. "$TEST_DIRECTORY"/lib-httpd.sh
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index c466fd989f..5c00607608 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -1374,6 +1374,14 @@ test_expect_success 'for-each-ref --ignore-case ignores case' '
test_cmp expect actual
'
+test_expect_success 'for-each-ref --omit-empty works' '
+ git for-each-ref --format="%(refname)" >actual &&
+ test_line_count -gt 1 actual &&
+ git for-each-ref --format="%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)" --omit-empty >actual &&
+ echo refs/heads/main >expect &&
+ test_cmp expect actual
+'
+
test_expect_success 'for-each-ref --ignore-case works on multiple sort keys' '
# name refs numerically to avoid case-insensitive filesystem conflicts
nr=0 &&
@@ -1464,4 +1472,54 @@ sig_crlf="$(printf "%s" "$sig" | append_cr; echo dummy)"
sig_crlf=${sig_crlf%dummy}
test_atom refs/tags/fake-sig-crlf contents:signature "$sig_crlf"
+test_expect_success 'git for-each-ref --stdin: empty' '
+ >in &&
+ git for-each-ref --format="%(refname)" --stdin <in >actual &&
+ git for-each-ref --format="%(refname)" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git for-each-ref --stdin: fails if extra args' '
+ >in &&
+ test_must_fail git for-each-ref --format="%(refname)" \
+ --stdin refs/heads/extra <in 2>err &&
+ grep "unknown arguments supplied with --stdin" err
+'
+
+test_expect_success 'git for-each-ref --stdin: matches' '
+ cat >in <<-EOF &&
+ refs/tags/multi*
+ refs/heads/amb*
+ EOF
+
+ cat >expect <<-EOF &&
+ refs/heads/ambiguous
+ refs/tags/multi-ref1-100000-user1
+ refs/tags/multi-ref1-100000-user2
+ refs/tags/multi-ref1-200000-user1
+ refs/tags/multi-ref1-200000-user2
+ refs/tags/multi-ref2-100000-user1
+ refs/tags/multi-ref2-100000-user2
+ refs/tags/multi-ref2-200000-user1
+ refs/tags/multi-ref2-200000-user2
+ refs/tags/multiline
+ EOF
+
+ git for-each-ref --format="%(refname)" --stdin <in >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git for-each-ref with non-existing refs' '
+ cat >in <<-EOF &&
+ refs/heads/this-ref-does-not-exist
+ refs/tags/bogus
+ EOF
+
+ git for-each-ref --format="%(refname)" --stdin <in >actual &&
+ test_must_be_empty actual &&
+
+ xargs git for-each-ref --format="%(refname)" <in >actual &&
+ test_must_be_empty actual
+'
+
test_done
diff --git a/t/t6301-for-each-ref-errors.sh b/t/t6301-for-each-ref-errors.sh
index bfda1f46ad..2667dd13fe 100755
--- a/t/t6301-for-each-ref-errors.sh
+++ b/t/t6301-for-each-ref-errors.sh
@@ -54,4 +54,18 @@ test_expect_success 'Missing objects are reported correctly' '
test_must_be_empty brief-err
'
+test_expect_success 'ahead-behind requires an argument' '
+ test_must_fail git for-each-ref \
+ --format="%(ahead-behind)" 2>err &&
+ echo "fatal: expected format: %(ahead-behind:<committish>)" >expect &&
+ test_cmp expect err
+'
+
+test_expect_success 'missing ahead-behind base' '
+ test_must_fail git for-each-ref \
+ --format="%(ahead-behind:refs/heads/missing)" 2>err &&
+ echo "fatal: failed to find '\''refs/heads/missing'\''" >expect &&
+ test_cmp expect err
+'
+
test_done
diff --git a/t/t6600-test-reach.sh b/t/t6600-test-reach.sh
index 338a9c46a2..b330945f49 100755
--- a/t/t6600-test-reach.sh
+++ b/t/t6600-test-reach.sh
@@ -443,4 +443,173 @@ test_expect_success 'get_reachable_subset:none' '
test_all_modes get_reachable_subset
'
+test_expect_success 'for-each-ref ahead-behind:linear' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-1-3
+ refs/heads/commit-1-5
+ refs/heads/commit-1-8
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1 0 8
+ refs/heads/commit-1-3 0 6
+ refs/heads/commit-1-5 0 4
+ refs/heads/commit-1-8 0 1
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-1-9)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:all' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-2-4
+ refs/heads/commit-4-2
+ refs/heads/commit-4-4
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1 0 24
+ refs/heads/commit-2-4 0 17
+ refs/heads/commit-4-2 0 17
+ refs/heads/commit-4-4 0 9
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-5-5)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:some' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-5-3
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1 0 53
+ refs/heads/commit-4-8 8 30
+ refs/heads/commit-5-3 0 39
+ refs/heads/commit-9-9 27 0
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-9-6)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:some, multibase' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-5-3
+ refs/heads/commit-7-8
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1 0 53 0 53
+ refs/heads/commit-4-8 8 30 0 22
+ refs/heads/commit-5-3 0 39 0 39
+ refs/heads/commit-7-8 14 12 8 6
+ refs/heads/commit-9-9 27 0 27 0
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-9-6) %(ahead-behind:commit-6-9)" \
+ --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:none' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-7-5
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-4-8 16 16
+ refs/heads/commit-7-5 7 4
+ refs/heads/commit-9-9 49 0
+ EOF
+ run_all_modes git for-each-ref \
+ --format="%(refname) %(ahead-behind:commit-8-4)" --stdin
+'
+
+test_expect_success 'for-each-ref merged:linear' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-1-3
+ refs/heads/commit-1-5
+ refs/heads/commit-1-8
+ refs/heads/commit-2-1
+ refs/heads/commit-5-1
+ refs/heads/commit-9-1
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-1-3
+ refs/heads/commit-1-5
+ refs/heads/commit-1-8
+ EOF
+ run_all_modes git for-each-ref --merged=commit-1-9 \
+ --format="%(refname)" --stdin
+'
+
+test_expect_success 'for-each-ref merged:all' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-2-4
+ refs/heads/commit-4-2
+ refs/heads/commit-4-4
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-2-4
+ refs/heads/commit-4-2
+ refs/heads/commit-4-4
+ EOF
+ run_all_modes git for-each-ref --merged=commit-5-5 \
+ --format="%(refname)" --stdin
+'
+
+test_expect_success 'for-each-ref ahead-behind:some' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-5-3
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-5-3
+ EOF
+ run_all_modes git for-each-ref --merged=commit-9-6 \
+ --format="%(refname)" --stdin
+'
+
+test_expect_success 'for-each-ref merged:some, multibase' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-5-3
+ refs/heads/commit-7-8
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ cat >expect <<-\EOF &&
+ refs/heads/commit-1-1
+ refs/heads/commit-4-8
+ refs/heads/commit-5-3
+ EOF
+ run_all_modes git for-each-ref \
+ --merged=commit-5-8 \
+ --merged=commit-8-5 \
+ --format="%(refname)" \
+ --stdin
+'
+
+test_expect_success 'for-each-ref merged:none' '
+ cat >input <<-\EOF &&
+ refs/heads/commit-7-5
+ refs/heads/commit-4-8
+ refs/heads/commit-9-9
+ EOF
+ >expect &&
+ run_all_modes git for-each-ref --merged=commit-8-4 \
+ --format="%(refname)" --stdin
+'
+
test_done
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 9aa1660651..0fe6ba93a2 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -792,6 +792,34 @@ test_expect_success 'annotations for blobs are empty' '
test_cmp expect actual
'
+# Run this before doing any signing, so the test has the same results
+# regardless of the GPG prereq.
+test_expect_success 'git tag --format with ahead-behind' '
+ test_when_finished git reset --hard tag-one-line &&
+ git commit --allow-empty -m "left" &&
+ git tag -a -m left tag-left &&
+ git reset --hard HEAD~1 &&
+ git commit --allow-empty -m "right" &&
+ git tag -a -m left tag-right &&
+
+ # Use " !" at the end to demonstrate whitespace
+ # around empty ahead-behind token for tag-blob.
+ cat >expect <<-EOF &&
+ refs/tags/tag-blob !
+ refs/tags/tag-left 1 1 !
+ refs/tags/tag-lines 0 1 !
+ refs/tags/tag-one-line 0 1 !
+ refs/tags/tag-right 0 0 !
+ refs/tags/tag-zero-lines 0 1 !
+ EOF
+ git tag -l --format="%(refname) %(ahead-behind:HEAD) !" >actual 2>err &&
+ grep "refs/tags/tag" actual >actual.focus &&
+ test_cmp expect actual.focus &&
+
+ # Error reported for tags that point to non-commits.
+ grep "error: object [0-9a-f]* is a blob, not a commit" err
+'
+
# trying to verify annotated non-signed tags:
test_expect_success GPG \
@@ -1843,6 +1871,23 @@ test_expect_success 'invalid sort parameter in configuratoin' '
test_must_fail git tag -l "foo*"
'
+test_expect_success 'version sort handles empty value for versionsort.{prereleaseSuffix,suffix}' '
+ cp .git/config .git/config.orig &&
+ test_when_finished mv .git/config.orig .git/config &&
+
+ cat >>.git/config <<-\EOF &&
+ [versionsort]
+ prereleaseSuffix
+ suffix
+ EOF
+ cat >expect <<-\EOF &&
+ error: missing value for '\''versionsort.suffix'\''
+ error: missing value for '\''versionsort.prereleasesuffix'\''
+ EOF
+ git tag -l --sort=version:refname 2>actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'version sort with prerelease reordering' '
test_config versionsort.prereleaseSuffix -rc &&
git tag foo1.6-rc1 &&
@@ -2001,6 +2046,22 @@ test_expect_success '--format should list tags as per format given' '
test_cmp expect actual
'
+test_expect_success '--format --omit-empty works' '
+ cat >expect <<-\EOF &&
+ refname : refs/tags/v1.0
+
+ refname : refs/tags/v1.1.3
+ EOF
+ git tag -l --format="%(if:notequals=refs/tags/v1.0.1)%(refname)%(then)refname : %(refname)%(end)" "v1*" >actual &&
+ test_cmp expect actual &&
+ cat >expect <<-\EOF &&
+ refname : refs/tags/v1.0
+ refname : refs/tags/v1.1.3
+ EOF
+ git tag -l --omit-empty --format="%(if:notequals=refs/tags/v1.0.1)%(refname)%(then)refname : %(refname)%(end)" "v1*" >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'git tag -l with --format="%(rest)" must fail' '
test_must_fail git tag -l --format="%(rest)" "v1*"
'
diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh
index 7cdc263764..887d181b72 100755
--- a/t/t7413-submodule-is-active.sh
+++ b/t/t7413-submodule-is-active.sh
@@ -51,6 +51,22 @@ test_expect_success 'is-active works with submodule.<name>.active config' '
test-tool -C super submodule is-active sub1
'
+test_expect_success 'is-active handles submodule.active config missing a value' '
+ cp super/.git/config super/.git/config.orig &&
+ test_when_finished mv super/.git/config.orig super/.git/config &&
+
+ cat >>super/.git/config <<-\EOF &&
+ [submodule]
+ active
+ EOF
+
+ cat >expect <<-\EOF &&
+ error: missing value for '\''submodule.active'\''
+ EOF
+ test-tool -C super submodule is-active sub1 2>actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'is-active works with basic submodule.active config' '
test_when_finished "git -C super config submodule.sub1.URL ../sub" &&
test_when_finished "git -C super config --unset-all submodule.active" &&
diff --git a/t/t7527-builtin-fsmonitor.sh b/t/t7527-builtin-fsmonitor.sh
index 4c0327b2bb..0c241d6c14 100755
--- a/t/t7527-builtin-fsmonitor.sh
+++ b/t/t7527-builtin-fsmonitor.sh
@@ -995,4 +995,41 @@ test_expect_success !UNICODE_COMPOSITION_SENSITIVE 'Unicode nfc/nfd' '
grep -E "^event: nfd/d_${utf8_nfc}/?$" ./unicode.trace
'
+test_expect_success 'split-index and FSMonitor work well together' '
+ git init split-index &&
+ test_when_finished "git -C \"$PWD/split-index\" \
+ fsmonitor--daemon stop" &&
+ (
+ cd split-index &&
+ git config core.splitIndex true &&
+ # force split-index in most cases
+ git config splitIndex.maxPercentChange 99 &&
+ git config core.fsmonitor true &&
+
+ # Create the following commit topology:
+ #
+ # * merge three
+ # |\
+ # | * three
+ # * | merge two
+ # |\|
+ # | * two
+ # * | one
+ # |/
+ # * 5a5efd7 initial
+
+ test_commit initial &&
+ test_commit two &&
+ test_commit three &&
+ git reset --hard initial &&
+ test_commit one &&
+ test_tick &&
+ git merge two &&
+ test_tick &&
+ git merge three &&
+
+ git rebase --force-rebase -r one
+ )
+'
+
test_done
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
index 7b957022f1..22b3a85b3e 100755
--- a/t/t7610-mergetool.sh
+++ b/t/t7610-mergetool.sh
@@ -860,4 +860,42 @@ test_expect_success 'mergetool hideResolved' '
git commit -m "test resolved with mergetool"
'
+test_expect_success 'mergetool with guiDefault' '
+ test_config merge.guitool myguitool &&
+ test_config mergetool.myguitool.cmd "(printf \"gui \" && cat \"\$REMOTE\") >\"\$MERGED\"" &&
+ test_config mergetool.myguitool.trustExitCode true &&
+ test_when_finished "git reset --hard" &&
+ git checkout -b test$test_count branch1 &&
+ git submodule update -N &&
+ test_must_fail git merge main &&
+
+ test_config mergetool.guiDefault auto &&
+ DISPLAY=SOMETHING && export DISPLAY &&
+ yes "" | git mergetool both &&
+ yes "" | git mergetool file1 file1 &&
+
+ DISPLAY= && export DISPLAY &&
+ yes "" | git mergetool file2 "spaced name" &&
+
+ test_config mergetool.guiDefault true &&
+ yes "" | git mergetool subdir/file3 &&
+
+ yes "d" | git mergetool file11 &&
+ yes "d" | git mergetool file12 &&
+ yes "l" | git mergetool submod &&
+
+ echo "gui main updated" >expect &&
+ test_cmp expect file1 &&
+
+ echo "main new" >expect &&
+ test_cmp expect file2 &&
+
+ echo "gui main new sub" >expect &&
+ test_cmp expect subdir/file3 &&
+
+ echo "branch1 submodule" >expect &&
+ test_cmp expect submod/bar &&
+ git commit -m "branch1 resolved with mergetool"
+'
+
test_done
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index 24297e26ca..59d3847bf8 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -155,6 +155,58 @@ test_expect_success 'difftool honors --gui' '
test_cmp expect actual
'
+test_expect_success 'difftool with guiDefault auto selects gui tool when there is DISPLAY' '
+ difftool_test_setup &&
+ test_config merge.tool bogus-tool &&
+ test_config diff.tool bogus-tool &&
+ test_config diff.guitool test-tool &&
+ test_config difftool.guiDefault auto &&
+ DISPLAY=SOMETHING && export DISPLAY &&
+
+ echo branch >expect &&
+ git difftool --no-prompt branch >actual &&
+ test_cmp expect actual
+'
+test_expect_success 'difftool with guiDefault auto selects regular tool when no DISPLAY' '
+ difftool_test_setup &&
+ test_config diff.guitool bogus-tool &&
+ test_config diff.tool test-tool &&
+ test_config difftool.guiDefault Auto &&
+ DISPLAY= && export DISPLAY &&
+
+ echo branch >expect &&
+ git difftool --no-prompt branch >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'difftool with guiDefault true selects gui tool' '
+ difftool_test_setup &&
+ test_config diff.tool bogus-tool &&
+ test_config diff.guitool test-tool &&
+ test_config difftool.guiDefault true &&
+
+ DISPLAY= && export DISPLAY &&
+ echo branch >expect &&
+ git difftool --no-prompt branch >actual &&
+ test_cmp expect actual &&
+
+ DISPLAY=Something && export DISPLAY &&
+ echo branch >expect &&
+ git difftool --no-prompt branch >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'difftool --no-gui trumps config guiDefault' '
+ difftool_test_setup &&
+ test_config diff.guitool bogus-tool &&
+ test_config diff.tool test-tool &&
+ test_config difftool.guiDefault true &&
+
+ echo branch >expect &&
+ git difftool --no-prompt --no-gui branch >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'difftool --gui last setting wins' '
difftool_test_setup &&
: >expect &&
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index 823331e44a..487e326b3f 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -524,6 +524,44 @@ test_expect_success 'register and unregister' '
git maintenance unregister --config-file ./other --force
'
+test_expect_success 'register with no value for maintenance.repo' '
+ cp .git/config .git/config.orig &&
+ test_when_finished mv .git/config.orig .git/config &&
+
+ cat >>.git/config <<-\EOF &&
+ [maintenance]
+ repo
+ EOF
+ cat >expect <<-\EOF &&
+ error: missing value for '\''maintenance.repo'\''
+ EOF
+ git maintenance register 2>actual &&
+ test_cmp expect actual &&
+ git config maintenance.repo
+'
+
+test_expect_success 'unregister with no value for maintenance.repo' '
+ cp .git/config .git/config.orig &&
+ test_when_finished mv .git/config.orig .git/config &&
+
+ cat >>.git/config <<-\EOF &&
+ [maintenance]
+ repo
+ EOF
+ cat >expect <<-\EOF &&
+ error: missing value for '\''maintenance.repo'\''
+ EOF
+ test_expect_code 128 git maintenance unregister 2>actual.raw &&
+ grep ^error actual.raw >actual &&
+ test_cmp expect actual &&
+ git config maintenance.repo &&
+
+ git maintenance unregister --force 2>actual.raw &&
+ grep ^error actual.raw >actual &&
+ test_cmp expect actual &&
+ git config maintenance.repo
+'
+
test_expect_success !MINGW 'register and unregister with regex metacharacters' '
META="a+b*c" &&
git init "$META" &&
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 323952a572..0de83b5d2b 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -12,7 +12,7 @@ PREREQ="PERL"
replace_variable_fields () {
sed -e "s/^\(Date:\).*/\1 DATE-STRING/" \
- -e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \
+ -e "s/^\(Message-ID:\).*/\1 MESSAGE-ID-STRING/" \
-e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/"
}
@@ -225,7 +225,7 @@ Cc: cc@example.com,
two@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
In-Reply-To: <unique-message-id@example.com>
References: <unique-message-id@example.com>
@@ -617,7 +617,7 @@ test_expect_success $PREREQ 'In-Reply-To without --chain-reply-to' '
sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt1 >actual &&
test_cmp expect actual &&
# Second and subsequent messages are replies to the first one
- sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt1 >expect &&
+ sed -n -e "s/^Message-ID: *\(.*\)/\1/p" msgtxt1 >expect &&
sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt2 >actual &&
test_cmp expect actual &&
sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt3 >actual &&
@@ -637,10 +637,10 @@ test_expect_success $PREREQ 'In-Reply-To with --chain-reply-to' '
2>errors &&
sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt1 >actual &&
test_cmp expect actual &&
- sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt1 >expect &&
+ sed -n -e "s/^Message-ID: *\(.*\)/\1/p" msgtxt1 >expect &&
sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt2 >actual &&
test_cmp expect actual &&
- sed -n -e "s/^Message-Id: *\(.*\)/\1/p" msgtxt2 >expect &&
+ sed -n -e "s/^Message-ID: *\(.*\)/\1/p" msgtxt2 >expect &&
sed -n -e "s/^In-Reply-To: *\(.*\)/\1/p" msgtxt3 >actual &&
test_cmp expect actual
'
@@ -713,7 +713,7 @@ Cc: cc@example.com,
two@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
@@ -759,7 +759,7 @@ Cc: A <author@example.com>,
two@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
@@ -796,7 +796,7 @@ Cc: A <author@example.com>,
C O Mitter <committer@example.com>
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
@@ -824,7 +824,7 @@ From: Example <from@example.com>
To: to@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
@@ -860,7 +860,7 @@ Cc: A <author@example.com>,
cc-cmd@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
@@ -893,7 +893,7 @@ Cc: A <author@example.com>,
two@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
@@ -926,7 +926,7 @@ Cc: A <author@example.com>,
two@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
@@ -963,7 +963,7 @@ Cc: A <author@example.com>,
C O Mitter <committer@example.com>
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
@@ -993,7 +993,7 @@ Cc: A <author@example.com>,
C O Mitter <committer@example.com>
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
-Message-Id: MESSAGE-ID-STRING
+Message-ID: MESSAGE-ID-STRING
X-Mailer: X-MAILER-STRING
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
@@ -1478,7 +1478,7 @@ test_expect_success $PREREQ 'To headers from files reset each patch' '
test_expect_success $PREREQ 'setup expect' '
cat >email-using-8bit <<\EOF
From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
-Message-Id: <bogus-message-id@example.com>
+Message-ID: <bogus-message-id@example.com>
From: author@example.com
Date: Sat, 12 Jun 2010 15:53:58 +0200
Subject: subject goes here
@@ -1564,7 +1564,7 @@ test_expect_success $PREREQ '--8bit-encoding overrides sendemail.8bitEncoding' '
test_expect_success $PREREQ 'setup expect' '
cat >email-using-8bit <<-\EOF
From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
- Message-Id: <bogus-message-id@example.com>
+ Message-ID: <bogus-message-id@example.com>
From: author@example.com
Date: Sat, 12 Jun 2010 15:53:58 +0200
Subject: Dieser Betreff enthält auch einen Umlaut!
@@ -1593,7 +1593,7 @@ test_expect_success $PREREQ '--8bit-encoding also treats subject' '
test_expect_success $PREREQ 'setup expect' '
cat >email-using-8bit <<-\EOF
From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
- Message-Id: <bogus-message-id@example.com>
+ Message-ID: <bogus-message-id@example.com>
From: A U Thor <author@example.com>
Date: Sat, 12 Jun 2010 15:53:58 +0200
Content-Type: text/plain; charset=UTF-8
@@ -1674,7 +1674,7 @@ test_expect_success $PREREQ '8-bit and sendemail.transferencoding=base64' '
test_expect_success $PREREQ 'setup expect' '
cat >email-using-qp <<-\EOF
From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
- Message-Id: <bogus-message-id@example.com>
+ Message-ID: <bogus-message-id@example.com>
From: A U Thor <author@example.com>
Date: Sat, 12 Jun 2010 15:53:58 +0200
MIME-Version: 1.0
@@ -1700,7 +1700,7 @@ test_expect_success $PREREQ 'convert from quoted-printable to base64' '
test_expect_success $PREREQ 'setup expect' "
tr -d '\\015' | tr '%' '\\015' >email-using-crlf <<EOF
From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
-Message-Id: <bogus-message-id@example.com>
+Message-ID: <bogus-message-id@example.com>
From: A U Thor <author@example.com>
Date: Sat, 12 Jun 2010 15:53:58 +0200
Content-Type: text/plain; charset=UTF-8
diff --git a/t/t9304-fast-import-marks.sh b/t/t9304-fast-import-marks.sh
index a98ef032d9..410a871c52 100755
--- a/t/t9304-fast-import-marks.sh
+++ b/t/t9304-fast-import-marks.sh
@@ -49,4 +49,33 @@ test_expect_success 'import with submodule mapping' '
test_cmp expect actual
'
+test_expect_success 'paths adjusted for relative subdir' '
+ git init deep-dst &&
+ mkdir deep-dst/subdir &&
+ >deep-dst/subdir/empty-marks &&
+ git -C deep-dst/subdir fast-import \
+ --rewrite-submodules-from=sub:../../from \
+ --rewrite-submodules-to=sub:../../to \
+ --import-marks=empty-marks \
+ --export-marks=exported-marks \
+ --export-pack-edges=exported-edges \
+ <dump &&
+ # we do not bother checking resulting repo; we just care that nothing
+ # complained about failing to open files for reading, and that files
+ # for writing were created in the expected spot
+ test_path_is_file deep-dst/subdir/exported-marks &&
+ test_path_is_file deep-dst/subdir/exported-edges
+'
+
+test_expect_success 'relative marks are not affected by subdir' '
+ git init deep-relative &&
+ mkdir deep-relative/subdir &&
+ git -C deep-relative/subdir fast-import \
+ --relative-marks \
+ --export-marks=exported-marks \
+ <dump &&
+ test_path_is_missing deep-relative/subdir/exported-marks &&
+ test_path_is_file deep-relative/.git/info/fast-import/exported-marks
+'
+
test_done
diff --git a/t/t9351-fast-export-anonymize.sh b/t/t9351-fast-export-anonymize.sh
index 77047e250d..156a647484 100755
--- a/t/t9351-fast-export-anonymize.sh
+++ b/t/t9351-fast-export-anonymize.sh
@@ -25,6 +25,7 @@ test_expect_success 'setup simple repo' '
test_expect_success 'export anonymized stream' '
git fast-export --anonymize --all \
--anonymize-map=retain-me \
+ --anonymize-map=xyzzy:should-not-appear \
--anonymize-map=xyzzy:custom-name \
--anonymize-map=other \
>stream
@@ -41,6 +42,7 @@ test_expect_success 'stream omits path names' '
test_expect_success 'stream contains user-specified names' '
grep retain-me stream &&
+ ! grep should-not-appear stream &&
grep custom-name stream
'
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index d459fae655..d667dda654 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -13,10 +13,10 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
actual="$TRASH_DIRECTORY/actual"
-c_red='\\[\\e[31m\\]'
-c_green='\\[\\e[32m\\]'
-c_lblue='\\[\\e[1;34m\\]'
-c_clear='\\[\\e[0m\\]'
+c_red='\001\e[31m\002'
+c_green='\001\e[32m\002'
+c_lblue='\001\e[1;34m\002'
+c_clear='\001\e[0m\002'
test_expect_success 'setup for prompt tests' '
git init otherrepo &&
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 62136caee5..293caf0f20 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1041,10 +1041,7 @@ want_trace () {
# (and we want to make sure we run any cleanup like
# "set +x").
test_eval_inner_ () {
- # Do not add anything extra (including LF) after '$*'
- eval "
- want_trace && trace_level_=$(($trace_level_+1)) && set -x
- $*"
+ eval "$*"
}
test_eval_ () {
@@ -1069,7 +1066,10 @@ test_eval_ () {
# be _inside_ the block to avoid polluting the "set -x" output
#
- test_eval_inner_ "$@" </dev/null >&3 2>&4
+ # Do not add anything extra (including LF) after '$*'
+ test_eval_inner_ </dev/null >&3 2>&4 "
+ want_trace && trace_level_=$(($trace_level_+1)) && set -x
+ $*"
{
test_eval_ret_=$?
if want_trace
@@ -1086,22 +1086,22 @@ test_eval_ () {
return $test_eval_ret_
}
+fail_117 () {
+ return 117
+}
+
test_run_ () {
test_cleanup=:
expecting_failure=$2
if test "${GIT_TEST_CHAIN_LINT:-1}" != 0; then
- # turn off tracing for this test-eval, as it simply creates
- # confusing noise in the "-x" output
- trace_tmp=$trace
- trace=
# 117 is magic because it is unlikely to match the exit
# code of other programs
- if test "OK-117" != "$(test_eval_ "(exit 117) && $1${LF}${LF}echo OK-\$?" 3>&1)"
+ test_eval_inner_ "fail_117 && $1" </dev/null >&3 2>&4
+ if test $? != 117
then
- BUG "broken &&-chain or run-away HERE-DOC: $1"
+ BUG "broken &&-chain: $1"
fi
- trace=$trace_tmp
fi
setup_malloc_check
@@ -1593,7 +1593,8 @@ then
BAIL_OUT_ENV_NEEDS_SANITIZE_LEAK "GIT_TEST_SANITIZE_LEAK_LOG=true"
fi
-if test "${GIT_TEST_CHAIN_LINT:-1}" != 0
+if test "${GIT_TEST_CHAIN_LINT:-1}" != 0 &&
+ test "${GIT_TEST_EXT_CHAIN_LINT:-1}" != 0
then
"$PERL_PATH" "$TEST_DIRECTORY/chainlint.pl" "$0" ||
BUG "lint error (see '?!...!? annotations above)"
diff --git a/transport.c b/transport.c
index 8d3ad8022a..0bf2d6940c 100644
--- a/transport.c
+++ b/transport.c
@@ -175,7 +175,8 @@ static struct ref *get_refs_from_bundle(struct transport *transport,
}
static int fetch_refs_from_bundle(struct transport *transport,
- int nr_heads, struct ref **to_fetch)
+ int nr_heads UNUSED,
+ struct ref **to_fetch UNUSED)
{
struct bundle_transport_data *data = transport->data;
struct strvec extra_index_pack_args = STRVEC_INIT;
@@ -284,8 +285,12 @@ static int connect_setup(struct transport *transport, int for_push)
}
data->conn = git_connect(data->fd, transport->url,
- for_push ? data->options.receivepack :
- data->options.uploadpack,
+ for_push ?
+ "git-receive-pack" :
+ "git-upload-pack",
+ for_push ?
+ data->options.receivepack :
+ data->options.uploadpack,
flags);
return 0;
@@ -920,7 +925,7 @@ static int connect_git(struct transport *transport, const char *name,
{
struct git_transport_data *data = transport->data;
data->conn = git_connect(data->fd, transport->url,
- executable, 0);
+ name, executable, 0);
fd[0] = data->fd[0];
fd[1] = data->fd[1];
return 0;
diff --git a/unicode-width.h b/unicode-width.h
index 97c851b27d..e15fb0455b 100644
--- a/unicode-width.h
+++ b/unicode-width.h
@@ -94,7 +94,7 @@ static const struct interval zero_width[] = {
{ 0x0E47, 0x0E4E },
{ 0x0EB1, 0x0EB1 },
{ 0x0EB4, 0x0EBC },
-{ 0x0EC8, 0x0ECD },
+{ 0x0EC8, 0x0ECE },
{ 0x0F18, 0x0F19 },
{ 0x0F35, 0x0F35 },
{ 0x0F37, 0x0F37 },
@@ -228,6 +228,7 @@ static const struct interval zero_width[] = {
{ 0x10AE5, 0x10AE6 },
{ 0x10D24, 0x10D27 },
{ 0x10EAB, 0x10EAC },
+{ 0x10EFD, 0x10EFF },
{ 0x10F46, 0x10F50 },
{ 0x10F82, 0x10F85 },
{ 0x11001, 0x11001 },
@@ -252,6 +253,7 @@ static const struct interval zero_width[] = {
{ 0x11234, 0x11234 },
{ 0x11236, 0x11237 },
{ 0x1123E, 0x1123E },
+{ 0x11241, 0x11241 },
{ 0x112DF, 0x112DF },
{ 0x112E3, 0x112EA },
{ 0x11300, 0x11301 },
@@ -313,7 +315,12 @@ static const struct interval zero_width[] = {
{ 0x11D95, 0x11D95 },
{ 0x11D97, 0x11D97 },
{ 0x11EF3, 0x11EF4 },
-{ 0x13430, 0x13438 },
+{ 0x11F00, 0x11F01 },
+{ 0x11F36, 0x11F3A },
+{ 0x11F40, 0x11F40 },
+{ 0x11F42, 0x11F42 },
+{ 0x13430, 0x13440 },
+{ 0x13447, 0x13455 },
{ 0x16AF0, 0x16AF4 },
{ 0x16B30, 0x16B36 },
{ 0x16F4F, 0x16F4F },
@@ -339,9 +346,11 @@ static const struct interval zero_width[] = {
{ 0x1E01B, 0x1E021 },
{ 0x1E023, 0x1E024 },
{ 0x1E026, 0x1E02A },
+{ 0x1E08F, 0x1E08F },
{ 0x1E130, 0x1E136 },
{ 0x1E2AE, 0x1E2AE },
{ 0x1E2EC, 0x1E2EF },
+{ 0x1E4EC, 0x1E4EF },
{ 0x1E8D0, 0x1E8D6 },
{ 0x1E944, 0x1E94A },
{ 0xE0001, 0xE0001 },
@@ -417,7 +426,9 @@ static const struct interval double_width[] = {
{ 0x1AFF5, 0x1AFFB },
{ 0x1AFFD, 0x1AFFE },
{ 0x1B000, 0x1B122 },
+{ 0x1B132, 0x1B132 },
{ 0x1B150, 0x1B152 },
+{ 0x1B155, 0x1B155 },
{ 0x1B164, 0x1B167 },
{ 0x1B170, 0x1B2FB },
{ 0x1F004, 0x1F004 },
@@ -451,7 +462,7 @@ static const struct interval double_width[] = {
{ 0x1F6CC, 0x1F6CC },
{ 0x1F6D0, 0x1F6D2 },
{ 0x1F6D5, 0x1F6D7 },
-{ 0x1F6DD, 0x1F6DF },
+{ 0x1F6DC, 0x1F6DF },
{ 0x1F6EB, 0x1F6EC },
{ 0x1F6F4, 0x1F6FC },
{ 0x1F7E0, 0x1F7EB },
@@ -459,15 +470,13 @@ static const struct interval double_width[] = {
{ 0x1F90C, 0x1F93A },
{ 0x1F93C, 0x1F945 },
{ 0x1F947, 0x1F9FF },
-{ 0x1FA70, 0x1FA74 },
-{ 0x1FA78, 0x1FA7C },
-{ 0x1FA80, 0x1FA86 },
-{ 0x1FA90, 0x1FAAC },
-{ 0x1FAB0, 0x1FABA },
-{ 0x1FAC0, 0x1FAC5 },
-{ 0x1FAD0, 0x1FAD9 },
-{ 0x1FAE0, 0x1FAE7 },
-{ 0x1FAF0, 0x1FAF6 },
+{ 0x1FA70, 0x1FA7C },
+{ 0x1FA80, 0x1FA88 },
+{ 0x1FA90, 0x1FABD },
+{ 0x1FABF, 0x1FAC5 },
+{ 0x1FACE, 0x1FADB },
+{ 0x1FAE0, 0x1FAE8 },
+{ 0x1FAF0, 0x1FAF8 },
{ 0x20000, 0x2FFFD },
{ 0x30000, 0x3FFFD }
};
diff --git a/unpack-trees.c b/unpack-trees.c
index c6de2ca5a7..c0732aa0c2 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1931,6 +1931,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
* avoid having to create a new one.
*/
o->internal.result.split_index = o->src_index->split_index;
+ if (o->src_index->cache_changed & SPLIT_INDEX_ORDERED)
+ o->internal.result.cache_changed |= SPLIT_INDEX_ORDERED;
o->internal.result.split_index->refcount++;
} else {
o->internal.result.split_index =
diff --git a/userdiff.c b/userdiff.c
index 09203fbc35..eaec6ebb5e 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -17,6 +17,7 @@ static int drivers_alloc;
.cflags = REG_EXTENDED, \
}, \
.word_regex = wrx "|[^[:space:]]|[\xc0-\xff][\x80-\xbf]+", \
+ .word_regex_multi_byte = wrx "|[^[:space:]]", \
}
#define IPATTERN(lang, rx, wrx) { \
.name = lang, \
@@ -26,6 +27,7 @@ static int drivers_alloc;
.cflags = REG_EXTENDED | REG_ICASE, \
}, \
.word_regex = wrx "|[^[:space:]]|[\xc0-\xff][\x80-\xbf]+", \
+ .word_regex_multi_byte = wrx "|[^[:space:]]", \
}
/*
@@ -294,7 +296,7 @@ PATTERNS("scheme",
/* All other words should be delimited by spaces or parentheses */
"|([^][)(}{[ \t])+"),
PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
- "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+"),
+ "\\\\[a-zA-Z@]+|\\\\.|([a-zA-Z0-9]|[^\x01-\x7f])+"),
{ "default", NULL, NULL, -1, { NULL, 0 } },
};
#undef PATTERNS
@@ -330,6 +332,25 @@ static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,
return 0;
}
+static int regexec_supports_multi_byte_chars(void)
+{
+ static const char not_space[] = "[^[:space:]]";
+ static const char utf8_multi_byte_char[] = "\xc2\xa3";
+ regex_t re;
+ regmatch_t match;
+ static int result = -1;
+
+ if (result != -1)
+ return result;
+ if (regcomp(&re, not_space, REG_EXTENDED))
+ BUG("invalid regular expression: %s", not_space);
+ result = !regexec(&re, utf8_multi_byte_char, 1, &match, 0) &&
+ match.rm_so == 0 &&
+ match.rm_eo == strlen(utf8_multi_byte_char);
+ regfree(&re);
+ return result;
+}
+
static struct userdiff_driver *userdiff_find_by_namelen(const char *name, size_t len)
{
struct find_by_namelen_data udcbdata = {
@@ -405,7 +426,13 @@ int userdiff_config(const char *k, const char *v)
struct userdiff_driver *userdiff_find_by_name(const char *name)
{
int len = strlen(name);
- return userdiff_find_by_namelen(name, len);
+ struct userdiff_driver *driver = userdiff_find_by_namelen(name, len);
+ if (driver && driver->word_regex_multi_byte) {
+ if (regexec_supports_multi_byte_chars())
+ driver->word_regex = driver->word_regex_multi_byte;
+ driver->word_regex_multi_byte = NULL;
+ }
+ return driver;
}
struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
diff --git a/userdiff.h b/userdiff.h
index 24419db697..d726804c3e 100644
--- a/userdiff.h
+++ b/userdiff.h
@@ -18,6 +18,7 @@ struct userdiff_driver {
int binary;
struct userdiff_funcname funcname;
const char *word_regex;
+ const char *word_regex_multi_byte;
const char *textconv;
struct notes_cache *textconv_cache;
int textconv_want_cache;
diff --git a/versioncmp.c b/versioncmp.c
index 069ee94a4d..7498da96e0 100644
--- a/versioncmp.c
+++ b/versioncmp.c
@@ -160,15 +160,21 @@ int versioncmp(const char *s1, const char *s2)
}
if (!initialized) {
- const struct string_list *deprecated_prereleases;
+ const char *const newk = "versionsort.suffix";
+ const char *const oldk = "versionsort.prereleasesuffix";
+ const struct string_list *newl;
+ const struct string_list *oldl;
+ int new = git_config_get_string_multi(newk, &newl);
+ int old = git_config_get_string_multi(oldk, &oldl);
+
+ if (!new && !old)
+ warning("ignoring %s because %s is set", oldk, newk);
+ if (!new)
+ prereleases = newl;
+ else if (!old)
+ prereleases = oldl;
+
initialized = 1;
- prereleases = git_config_get_value_multi("versionsort.suffix");
- deprecated_prereleases = git_config_get_value_multi("versionsort.prereleasesuffix");
- if (prereleases) {
- if (deprecated_prereleases)
- warning("ignoring versionsort.prereleasesuffix because versionsort.suffix is set");
- } else
- prereleases = deprecated_prereleases;
}
if (prereleases && swap_prereleases(s1, s2, (const char *) p1 - s1 - 1,
&diff))
diff --git a/wildmatch.c b/wildmatch.c
index 42e38e3459..8ea29141bd 100644
--- a/wildmatch.c
+++ b/wildmatch.c
@@ -14,6 +14,10 @@
typedef unsigned char uchar;
+/* Internal return values */
+#define WM_ABORT_ALL -1
+#define WM_ABORT_TO_STARSTAR -2
+
/* What character marks an inverted character class? */
#define NEGATE_CLASS '!'
#define NEGATE_CLASS2 '^'
@@ -83,12 +87,12 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
continue;
case '*':
if (*++p == '*') {
- const uchar *prev_p = p - 2;
+ const uchar *prev_p = p;
while (*++p == '*') {}
if (!(flags & WM_PATHNAME))
/* without WM_PATHNAME, '*' == '**' */
match_slash = 1;
- else if ((prev_p < pattern || *prev_p == '/') &&
+ else if ((prev_p - pattern < 2 || *(prev_p - 2) == '/') &&
(*p == '\0' || *p == '/' ||
(p[0] == '\\' && p[1] == '/'))) {
/*
@@ -114,7 +118,7 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
* only if there are no more slash characters. */
if (!match_slash) {
if (strchr((char *)text, '/'))
- return WM_NOMATCH;
+ return WM_ABORT_TO_STARSTAR;
}
return WM_MATCH;
} else if (!match_slash && *p == '/') {
@@ -125,7 +129,7 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
*/
const char *slash = strchr((char*)text, '/');
if (!slash)
- return WM_NOMATCH;
+ return WM_ABORT_ALL;
text = (const uchar*)slash;
/* the slash is consumed by the top-level for loop */
break;
@@ -153,8 +157,12 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
break;
text++;
}
- if (t_ch != p_ch)
- return WM_NOMATCH;
+ if (t_ch != p_ch) {
+ if (match_slash)
+ return WM_ABORT_ALL;
+ else
+ return WM_ABORT_TO_STARSTAR;
+ }
}
if ((matched = dowild(p, text, flags)) != WM_NOMATCH) {
if (!match_slash || matched != WM_ABORT_TO_STARSTAR)
@@ -274,5 +282,6 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
/* Match the "pattern" against the "text" string. */
int wildmatch(const char *pattern, const char *text, unsigned int flags)
{
- return dowild((const uchar*)pattern, (const uchar*)text, flags);
+ int res = dowild((const uchar*)pattern, (const uchar*)text, flags);
+ return res == WM_MATCH ? WM_MATCH : WM_NOMATCH;
}
diff --git a/wildmatch.h b/wildmatch.h
index 5993696298..0c890cb56b 100644
--- a/wildmatch.h
+++ b/wildmatch.h
@@ -6,8 +6,6 @@
#define WM_NOMATCH 1
#define WM_MATCH 0
-#define WM_ABORT_ALL -1
-#define WM_ABORT_TO_STARSTAR -2
int wildmatch(const char *pattern, const char *text, unsigned int flags);
#endif