]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'rs/parse-options-with-keep-unknown-abbrev-fix'
authorJunio C Hamano <gitster@pobox.com>
Tue, 30 Jan 2024 21:34:12 +0000 (13:34 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 30 Jan 2024 21:34:12 +0000 (13:34 -0800)
"git diff --no-rename A B" did not disable rename detection but did
not trigger an error from the command line parser.

* rs/parse-options-with-keep-unknown-abbrev-fix:
  parse-options: simplify positivation handling
  parse-options: fully disable option abbreviation with PARSE_OPT_KEEP_UNKNOWN

534 files changed:
.cirrus.yml
.github/workflows/main.yml
.gitignore
.gitlab-ci.yml [new file with mode: 0644]
Documentation/CodingGuidelines
Documentation/Makefile
Documentation/MyFirstContribution.txt
Documentation/RelNotes/1.6.2.txt
Documentation/RelNotes/1.6.3.txt
Documentation/RelNotes/1.6.4.txt
Documentation/RelNotes/1.6.5.txt
Documentation/RelNotes/1.6.6.txt
Documentation/RelNotes/2.44.0.txt [new file with mode: 0644]
Documentation/SubmittingPatches
Documentation/config/advice.txt
Documentation/config/extensions.txt
Documentation/config/fetch.txt
Documentation/config/format.txt
Documentation/config/pack.txt
Documentation/config/rebase.txt
Documentation/fetch-options.txt
Documentation/git-add.txt
Documentation/git-bisect.txt
Documentation/git-branch.txt
Documentation/git-checkout.txt
Documentation/git-clone.txt
Documentation/git-cvsimport.txt
Documentation/git-diff.txt
Documentation/git-for-each-ref.txt
Documentation/git-format-patch.txt
Documentation/git-imap-send.txt
Documentation/git-init.txt
Documentation/git-merge-file.txt
Documentation/git-merge.txt
Documentation/git-rebase.txt
Documentation/git-replay.txt [new file with mode: 0644]
Documentation/git-rev-parse.txt
Documentation/git-send-email.txt
Documentation/git-switch.txt
Documentation/git-worktree.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitcore-tutorial.txt
Documentation/gitformat-pack.txt
Documentation/gitprotocol-http.txt
Documentation/gitrepository-layout.txt
Documentation/gitweb.conf.txt
Documentation/gitweb.txt
Documentation/glossary-content.txt
Documentation/howto/keep-canonical-history-correct.txt
Documentation/merge-options.txt
Documentation/ref-storage-format.txt [new file with mode: 0644]
Documentation/rev-list-options.txt
Documentation/signoff-option.txt
Documentation/technical/repository-version.txt
Documentation/technical/unit-tests.txt [new file with mode: 0644]
Documentation/user-manual.txt
GIT-VERSION-GEN
INSTALL
Makefile
README.md
RelNotes
add-patch.c
advice.c
advice.h
apply.c
archive-tar.c
archive-zip.c
archive.c
archive.h
attr.c
bisect.c
blame.c
blame.h
blob.c
bloom.c
branch.c
builtin.h
builtin/add.c
builtin/am.c
builtin/apply.c
builtin/archive.c
builtin/bisect.c
builtin/blame.c
builtin/branch.c
builtin/cat-file.c
builtin/checkout-index.c
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/column.c
builtin/commit-graph.c
builtin/commit-tree.c
builtin/commit.c
builtin/config.c
builtin/credential-cache.c
builtin/describe.c
builtin/diff-files.c
builtin/diff-index.c
builtin/diff-tree.c
builtin/diff.c
builtin/difftool.c
builtin/fast-export.c
builtin/fast-import.c
builtin/fetch.c
builtin/for-each-ref.c
builtin/fsck.c
builtin/fsmonitor--daemon.c
builtin/gc.c
builtin/get-tar-commit-id.c
builtin/grep.c
builtin/hash-object.c
builtin/hook.c
builtin/index-pack.c
builtin/init-db.c
builtin/log.c
builtin/ls-files.c
builtin/ls-remote.c
builtin/ls-tree.c
builtin/mailinfo.c
builtin/merge-base.c
builtin/merge-file.c
builtin/merge-recursive.c
builtin/merge-tree.c
builtin/merge.c
builtin/mktag.c
builtin/mv.c
builtin/notes.c
builtin/pack-objects.c
builtin/pull.c
builtin/push.c
builtin/range-diff.c
builtin/read-tree.c
builtin/rebase.c
builtin/receive-pack.c
builtin/reflog.c
builtin/repack.c
builtin/replay.c [new file with mode: 0644]
builtin/rerere.c
builtin/reset.c
builtin/rev-list.c
builtin/rev-parse.c
builtin/revert.c
builtin/rm.c
builtin/send-pack.c
builtin/show-ref.c
builtin/sparse-checkout.c
builtin/stash.c
builtin/submodule--helper.c
builtin/tag.c
builtin/unpack-objects.c
builtin/update-ref.c
builtin/var.c
builtin/verify-commit.c
builtin/verify-tag.c
builtin/worktree.c
bulk-checkin.c
bundle-uri.c
cache-tree.c
ci/install-dependencies.sh
ci/install-docker-dependencies.sh
ci/lib.sh
ci/print-test-failures.sh
ci/run-build-and-minimal-fuzzers.sh [new file with mode: 0755]
ci/run-build-and-tests.sh
ci/run-test-slice.sh
combine-diff.c
command-list.txt
commit-graph.c
commit-reach.c
commit.c
commit.h
compat/fsmonitor/fsm-health-win32.c
compat/fsmonitor/fsm-listen-darwin.c
compat/fsmonitor/fsm-listen-win32.c
compat/mingw.c
compat/simple-ipc/ipc-shared.c
compat/simple-ipc/ipc-unix-socket.c
config.c
config.h
config.mak.uname
configure.ac
contrib/buildsystems/CMakeLists.txt
contrib/completion/git-completion.bash
contrib/completion/git-prompt.sh
contrib/diff-highlight/DiffHighlight.pm
contrib/mw-to-git/Git/Mediawiki.pm
contrib/subtree/git-subtree.sh
convert.h
delta-islands.c
diff-lib.c
diff-no-index.c
diff.c
diffcore-break.c
diffcore-delta.c
dir.c
entry.c
environment.c
ewah/bitmap.c
ewah/ewok.h
exec-cmd.c
fetch-pack.c
fetch-pack.h
fsck.c
fsmonitor--daemon.h
fsmonitor-ipc.c
gettext.c
git-archimport.perl
git-compat-util.h
git-cvsexportcommit.perl
git-cvsimport.perl
git-cvsserver.perl
git-gui/git-gui.sh
git-gui/lib/encoding.tcl
git-gui/po/README
git-instaweb.sh
git-p4.py
git-send-email.perl
git-svn.perl
git.c
gitk-git/gitk
gitweb/INSTALL
gitweb/gitweb.perl
gitweb/static/gitweb.css
gitweb/static/js/lib/common-lib.js
gpg-interface.c
graph.h
grep.c
help.c
http-fetch.c
http-push.c
http-walker.c
http.c
http.h
imap-send.c
json-writer.h
kwset.c
kwset.h
line-log.c
line-log.h
line-range.c
list-objects-filter-options.c
list-objects-filter.c
list.h
log-tree.c
ls-refs.c
mailinfo.c
mem-pool.c
merge-blobs.c
merge-ll.c
merge-ort.c
merge-recursive.c
merge.c
midx.c
midx.h
negotiator/noop.c
neue [new file with mode: 0644]
notes-utils.c
notes.c
object-file.c
object-name.c
oss-fuzz/.gitignore
oss-fuzz/dummy-cmd-main.c [new file with mode: 0644]
oss-fuzz/fuzz-date.c [new file with mode: 0644]
pack-bitmap-write.c
pack-bitmap.c
pack-bitmap.h
pack-check.c
pack-objects.c
pack-objects.h
pack-revindex.c
pack-revindex.h
pack-write.c
packfile.c
packfile.h
parse-options.c
patch-ids.c
path.c
path.h
pathspec.c
perl/FromCPAN/Error.pm
perl/Git.pm
perl/Git/I18N.pm
perl/Git/LoadCPAN.pm
perl/Git/LoadCPAN/Error.pm
perl/Git/LoadCPAN/Mail/Address.pm
perl/Git/Packet.pm
perl/Git/SVN.pm
pkt-line.c
pkt-line.h
protocol-caps.c
protocol.h
reachable.c
read-cache-ll.h
read-cache.c
ref-filter.c
ref-filter.h
reflog.c
refs.c
refs.h
refs/debug.c
refs/files-backend.c
refs/packed-backend.c
refs/ref-cache.c
refs/refs-internal.h
reftable/block.c
reftable/block.h
reftable/block_test.c
reftable/blocksource.c
reftable/dump.c
reftable/generic.c
reftable/iter.h
reftable/merged.c
reftable/merged.h
reftable/merged_test.c
reftable/reader.c
reftable/readwrite_test.c
reftable/record.c
reftable/record_test.c
reftable/refname_test.c
reftable/reftable-record.h
reftable/stack.c
reftable/stack.h
reftable/stack_test.c
reftable/test_framework.c
reftable/test_framework.h
reftable/tree_test.c
reftable/writer.c
remote-curl.c
remote.c
remote.h
repo-settings.c
repository.c
repository.h
rerere.c
reset.c
revision.c
run-command.c
send-pack.c
sequencer.c
sequencer.h
setup.c
setup.h
sh-i18n--envsubst.c
sha1dc/sha1.c
shallow.c
shell.c
sideband.c
statinfo.c
statinfo.h
strvec.h
submodule-config.c
submodule-config.h
submodule.c
t/Makefile
t/README
t/chainlint/blank-line-before-esac.expect
t/chainlint/blank-line.expect
t/chainlint/block.expect
t/chainlint/chain-break-background.expect
t/chainlint/chain-break-return-exit.expect
t/chainlint/chain-break-status.expect
t/chainlint/chained-subshell.expect
t/chainlint/command-substitution-subsubshell.expect
t/chainlint/dqstring-line-splice.expect
t/chainlint/dqstring-no-interpolate.expect
t/chainlint/empty-here-doc.expect
t/chainlint/exclamation.expect
t/chainlint/for-loop-abbreviated.expect
t/chainlint/for-loop.expect
t/chainlint/function.expect
t/chainlint/here-doc.expect
t/chainlint/loop-detect-status.expect
t/chainlint/nested-cuddled-subshell.expect
t/chainlint/nested-loop-detect-failure.expect
t/chainlint/nested-subshell.expect
t/chainlint/pipe.expect
t/chainlint/subshell-here-doc.expect
t/chainlint/subshell-one-liner.expect
t/chainlint/t7900-subtree.expect
t/chainlint/token-pasting.expect
t/chainlint/while-loop.expect
t/helper/test-bundle-uri.c
t/helper/test-ctype.c [deleted file]
t/helper/test-fast-rebase.c [deleted file]
t/helper/test-pkt-line.c
t/helper/test-reach.c
t/helper/test-read-midx.c
t/helper/test-ref-store.c
t/helper/test-regex.c
t/helper/test-repository.c
t/helper/test-simple-ipc.c
t/helper/test-submodule.c
t/helper/test-tool.c
t/helper/test-tool.h
t/helper/test-trace2.c
t/lib-gpg.sh
t/lib-httpd.sh
t/lib-httpd/passwd
t/lib-httpd/proxy-passwd
t/perf/p5332-multi-pack-reuse.sh [new file with mode: 0755]
t/perf/p6300-for-each-ref.sh [new file with mode: 0755]
t/perf/perf-lib.sh
t/perf/run
t/t0001-init.sh
t/t0003-attributes.sh
t/t0024-crlf-archive.sh
t/t0040-parse-options.sh
t/t0070-fundamental.sh
t/t0080-unit-test-output.sh [new file with mode: 0755]
t/t0202/test.pl
t/t0210-trace2-normal.sh
t/t0211-trace2-perf.sh
t/t0212-trace2-event.sh
t/t0410-partial-clone.sh
t/t1006-cat-file.sh
t/t1091-sparse-checkout-builtin.sh
t/t1400-update-ref.sh
t/t1401-symbolic-ref.sh
t/t1403-show-ref.sh
t/t1410-reflog.sh
t/t1417-reflog-updateref.sh
t/t1430-bad-ref-name.sh
t/t1450-fsck.sh
t/t1500-rev-parse.sh
t/t2011-checkout-invalid-head.sh
t/t2060-switch.sh
t/t2400-worktree-add.sh
t/t3200-branch.sh
t/t3310-notes-merge-manual-resolve.sh
t/t3400-rebase.sh
t/t3404-rebase-interactive.sh
t/t3415-rebase-autosquash.sh
t/t3422-rebase-incompatible-options.sh
t/t3650-replay-basics.sh [new file with mode: 0755]
t/t4001-diff-rename.sh
t/t4013-diff-various.sh
t/t4014-format-patch.sh
t/t4129-apply-samemode.sh
t/t4202-log.sh
t/t4207-log-decoration-colors.sh
t/t4301-merge-tree-write-tree.sh
t/t5000-tar-tree.sh
t/t5100-mailinfo.sh
t/t5100/comment.expect
t/t5100/comment.in
t/t5318-commit-graph.sh
t/t5319-multi-pack-index.sh
t/t5332-multi-pack-reuse.sh [new file with mode: 0755]
t/t5401-update-hooks.sh
t/t5510-fetch.sh
t/t5514-fetch-multiple.sh
t/t5526-fetch-submodules.sh
t/t5541-http-push-smart.sh
t/t5550-http-fetch-dumb.sh
t/t5551-http-fetch-smart.sh
t/t5558-clone-bundle-uri.sh
t/t5562/invoke-with-content-length.pl
t/t5574-fetch-output.sh
t/t5601-clone.sh
t/t5605-clone-local.sh
t/t5606-clone-options.sh
t/t5702-protocol-v2.sh
t/t6005-rev-list-count.sh
t/t6009-rev-list-parent.sh
t/t6018-rev-list-glob.sh
t/t6021-rev-list-exclude-hidden.sh
t/t6022-rev-list-missing.sh
t/t6030-bisect-porcelain.sh
t/t6113-rev-list-bitmap-filters.sh
t/t6135-pathspec-with-attrs.sh
t/t6300-for-each-ref.sh
t/t6301-for-each-ref-errors.sh
t/t6302-for-each-ref-filter.sh
t/t6403-merge-file.sh
t/t6429-merge-sequence-rename-caching.sh
t/t7004-tag.sh
t/t7102-reset.sh
t/t7300-clean.sh
t/t7450-bad-git-dotfiles.sh
t/t7501-commit-basic-functionality.sh
t/t7512-status-help.sh
t/t7527-builtin-fsmonitor.sh
t/t7700-repack.sh
t/t7900-maintenance.sh
t/t9002-column.sh
t/t9114-git-svn-dcommit-merge.sh
t/t9133-git-svn-nested-git-repo.sh
t/t9164-git-svn-dcommit-concurrent.sh
t/t9350-fast-export.sh
t/t9500-gitweb-standalone-no-errors.sh
t/t9700/test.pl
t/t9801-git-p4-branch.sh
t/t9816-git-p4-locked.sh
t/t9902-completion.sh
t/test-lib-functions.sh
t/test-lib-github-workflow-markup.sh
t/test-lib-junit.sh
t/test-lib.sh
t/test-terminal.perl
t/unit-tests/.gitignore [new file with mode: 0644]
t/unit-tests/t-basic.c [new file with mode: 0644]
t/unit-tests/t-ctype.c [new file with mode: 0644]
t/unit-tests/t-mem-pool.c [new file with mode: 0644]
t/unit-tests/t-strbuf.c [new file with mode: 0644]
t/unit-tests/test-lib.c [new file with mode: 0644]
t/unit-tests/test-lib.h [new file with mode: 0644]
t/valgrind/valgrind.sh
templates/hooks--pre-commit.sample
tmp-objdir.c
trace.c
trace2.c
trace2.h
trace2/tr2_ctr.c
trace2/tr2_sysenv.c
trace2/tr2_tgt_normal.c
trace2/tr2_tls.c
trace2/tr2_tls.h
trace2/tr2_tmr.c
trailer.c
trailer.h
transport-helper.c
transport.c
tree.c
upload-pack.c
utf8.c
utf8.h
worktree.c
worktree.h
wrapper.c
write-or-die.c
wt-status.c
wt-status.h
xdiff-interface.c

index 4860bebd32f8d3f34c2382f097ac50c0b972d3a0..b6280692d2f21aaa17548985979279594c94323e 100644 (file)
@@ -19,4 +19,4 @@ freebsd_12_task:
   build_script:
     - su git -c gmake
   test_script:
-    - su git -c 'gmake test'
+    - su git -c 'gmake DEFAULT_UNIT_TEST_TARGET=unit-tests-prove test unit-tests'
index 9fdbd5402898bf1c8a188bfd24262109c96e5e0f..4d97da57ec4e7b8f9276527f24cc068ee786f179 100644 (file)
@@ -309,6 +309,17 @@ jobs:
       with:
         name: failed-tests-${{matrix.vector.jobname}}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
+  fuzz-smoke-test:
+    name: fuzz smoke test
+    needs: ci-config
+    if: needs.ci-config.outputs.enabled == 'yes'
+    env:
+      CC: clang
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v3
+    - run: ci/install-dependencies.sh
+    - run: ci/run-build-and-minimal-fuzzers.sh
   dockerized:
     name: ${{matrix.vector.jobname}} (${{matrix.vector.image}})
     needs: ci-config
index 5e56e471b344ef45e026847a6865645d61908be7..612c0f6a0ff1aa37415b275424a4e01942f5a2dd 100644 (file)
 /git-remote-ext
 /git-repack
 /git-replace
+/git-replay
 /git-request-pull
 /git-rerere
 /git-reset
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644 (file)
index 0000000..43bfbd8
--- /dev/null
@@ -0,0 +1,95 @@
+default:
+  timeout: 2h
+
+workflow:
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+    - if: $CI_COMMIT_TAG
+    - if: $CI_COMMIT_REF_PROTECTED == "true"
+
+test:linux:
+  image: $image
+  before_script:
+    - ./ci/install-docker-dependencies.sh
+  script:
+    - useradd builder --create-home
+    - chown -R builder "${CI_PROJECT_DIR}"
+    - sudo --preserve-env --set-home --user=builder ./ci/run-build-and-tests.sh
+  after_script:
+    - |
+      if test "$CI_JOB_STATUS" != 'success'
+      then
+        sudo --preserve-env --set-home --user=builder ./ci/print-test-failures.sh
+      fi
+  parallel:
+    matrix:
+      - jobname: linux-sha256
+        image: ubuntu:latest
+        CC: clang
+      - jobname: linux-gcc
+        image: ubuntu:20.04
+        CC: gcc
+        CC_PACKAGE: gcc-8
+      - jobname: linux-TEST-vars
+        image: ubuntu:20.04
+        CC: gcc
+        CC_PACKAGE: gcc-8
+      - jobname: linux-gcc-default
+        image: ubuntu:latest
+        CC: gcc
+      - jobname: linux-leaks
+        image: ubuntu:latest
+        CC: gcc
+      - jobname: linux-asan-ubsan
+        image: ubuntu:latest
+        CC: clang
+      - jobname: pedantic
+        image: fedora:latest
+      - jobname: linux-musl
+        image: alpine:latest
+  artifacts:
+    paths:
+      - t/failed-test-artifacts
+    when: on_failure
+
+test:osx:
+  image: $image
+  tags:
+    - saas-macos-medium-m1
+  variables:
+    TEST_OUTPUT_DIRECTORY: "/Volumes/RAMDisk"
+  before_script:
+    # Create a 4GB RAM disk that we use to store test output on. This small hack
+    # significantly speeds up tests by more than a factor of 2 because the
+    # macOS runners use network-attached storage as disks, which is _really_
+    # slow with the many small writes that our tests do.
+    - sudo diskutil apfs create $(hdiutil attach -nomount ram://8192000) RAMDisk
+    - ./ci/install-dependencies.sh
+  script:
+    - ./ci/run-build-and-tests.sh
+  after_script:
+    - |
+      if test "$CI_JOB_STATUS" != 'success'
+      then
+        ./ci/print-test-failures.sh
+        mv "$TEST_OUTPUT_DIRECTORY"/failed-test-artifacts t/
+      fi
+  parallel:
+    matrix:
+      - jobname: osx-clang
+        image: macos-13-xcode-14
+        CC: clang
+  artifacts:
+    paths:
+      - t/failed-test-artifacts
+    when: on_failure
+
+static-analysis:
+  image: ubuntu:22.04
+  variables:
+    jobname: StaticAnalysis
+  before_script:
+    - ./ci/install-docker-dependencies.sh
+  script:
+    - ./ci/run-static-analysis.sh
+    - ./ci/check-directional-formatting.bash
index 8d3a467c0135319162b71290810917cca5f8316f..578587a47155e929457e12862cd648c9fdf81acd 100644 (file)
@@ -450,7 +450,7 @@ For C programs:
    one of the approved headers that includes it first for you.  (The
    approved headers currently include "builtin.h",
    "t/helper/test-tool.h", "xdiff/xinclude.h", or
-   "reftable/system.h").  You do not have to include more than one of
+   "reftable/system.h".)  You do not have to include more than one of
    these.
 
  - A C file must directly include the header files that declare the
@@ -490,7 +490,7 @@ For Perl programs:
 
  - Most of the C guidelines above apply.
 
- - We try to support Perl 5.8 and later ("use Perl 5.008").
+ - We try to support Perl 5.8.1 and later ("use Perl 5.008001").
 
  - use strict and use warnings are strongly preferred.
 
@@ -518,7 +518,7 @@ For Perl programs:
 
 For Python scripts:
 
- - We follow PEP-8 (http://www.python.org/dev/peps/pep-0008/).
+ - We follow PEP-8 (https://peps.python.org/pep-0008/).
 
  - As a minimum, we aim to be compatible with Python 2.7.
 
@@ -578,7 +578,7 @@ Externally Visible Names
    . The variable name describes the effect of tweaking this knob.
 
    The section and variable names that consist of multiple words are
-   formed by concatenating the words without punctuations (e.g. `-`),
+   formed by concatenating the words without punctuation marks (e.g. `-`),
    and are broken using bumpyCaps in documentation as a hint to the
    reader.
 
index b629176d7d2d6776e0a66fbda466915219f32bc2..3f2383a12c77d04f027c64a0b6aed5ad7305e271 100644 (file)
@@ -122,6 +122,7 @@ TECH_DOCS += technical/scalar
 TECH_DOCS += technical/send-pack-pipeline
 TECH_DOCS += technical/shallow
 TECH_DOCS += technical/trivial-merge
+TECH_DOCS += technical/unit-tests
 SP_ARTICLES += $(TECH_DOCS)
 SP_ARTICLES += technical/api-index
 
index 7cfed60c2e984f4fa29aca68983c2260faf451ee..f06563e98174604d339e3ded16b762e13b41534a 100644 (file)
@@ -35,8 +35,9 @@ announcements, design discussions, and more take place. Those interested in
 contributing are welcome to post questions here. The Git list requires
 plain-text-only emails and prefers inline and bottom-posting when replying to
 mail; you will be CC'd in all replies to you. Optionally, you can subscribe to
-the list by sending an email to majordomo@vger.kernel.org with "subscribe git"
-in the body. The https://lore.kernel.org/git[archive] of this mailing list is
+the list by sending an email to <git+subscribe@vger.kernel.org>
+(see https://subspace.kernel.org/subscribing.html for details).
+The https://lore.kernel.org/git[archive] of this mailing list is
 available to view in a browser.
 
 ==== https://groups.google.com/forum/#!forum/git-mentoring[git-mentoring@googlegroups.com]
@@ -833,7 +834,7 @@ Johannes Schindelin to make life as a Git contributor easier for those used to
 the GitHub PR workflow. It allows contributors to open pull requests against its
 mirror of the Git project, and does some magic to turn the PR into a set of
 emails and send them out for you. It also runs the Git continuous integration
-suite for you. It's documented at http://gitgitgadget.github.io.
+suite for you. It's documented at https://gitgitgadget.github.io/.
 
 [[create-fork]]
 === Forking `git/git` on GitHub
index 980adfb31546974b69e4aff8ef91ab05e00da41f..166d73c60fb11e021b77afc0b759de52cfba0495 100644 (file)
@@ -10,7 +10,7 @@ To ease the transition plan, the receiving repository of such a
 push running this release will issue a big warning when the
 configuration variable is missing.  Please refer to:
 
-  http://git.or.cz/gitwiki/GitFaq#non-bare
+  https://archive.kernel.org/oldwiki/git.wiki.kernel.org/index.php/GitFaq.html#non-bare
   https://lore.kernel.org/git/7vbptlsuyv.fsf@gitster.siamese.dyndns.org/
 
 for more details on the reason why this change is needed and the
index 4bcff945e019d92f58cef08517fa1e7a70553b66..bbf177fc3c5ba4f76a1e002623e32b9106204ee5 100644 (file)
@@ -10,7 +10,7 @@ To ease the transition plan, the receiving repository of such a
 push running this release will issue a big warning when the
 configuration variable is missing.  Please refer to:
 
-  http://git.or.cz/gitwiki/GitFaq#non-bare
+  https://archive.kernel.org/oldwiki/git.wiki.kernel.org/index.php/GitFaq.html#non-bare
   https://lore.kernel.org/git/7vbptlsuyv.fsf@gitster.siamese.dyndns.org/
 
 for more details on the reason why this change is needed and the
index a2a34b43a75d1b213eb51afae09d06454607e644..0fccfb0bf0bddf99a181653648d0af630d631b95 100644 (file)
@@ -10,7 +10,7 @@ To ease the transition plan, the receiving repository of such a
 push running this release will issue a big warning when the
 configuration variable is missing.  Please refer to:
 
-  http://git.or.cz/gitwiki/GitFaq#non-bare
+  https://archive.kernel.org/oldwiki/git.wiki.kernel.org/index.php/GitFaq.html#non-bare
   https://lore.kernel.org/git/7vbptlsuyv.fsf@gitster.siamese.dyndns.org/
 
 for more details on the reason why this change is needed and the
index 6c7f7da7eb9a10971c42552cafc5e85f6b226319..79cb1b2b6df88a5ccdf9a237f83379839227057b 100644 (file)
@@ -21,7 +21,7 @@ To ease the transition plan, the receiving repository of such a
 push running this release will issue a big warning when the
 configuration variable is missing.  Please refer to:
 
-  http://git.or.cz/gitwiki/GitFaq#non-bare
+  https://archive.kernel.org/oldwiki/git.wiki.kernel.org/index.php/GitFaq.html#non-bare
   https://lore.kernel.org/git/7vbptlsuyv.fsf@gitster.siamese.dyndns.org/
 
 for more details on the reason why this change is needed and the
index 3ed1e01433745aed586a5de3770f88b8cdf08233..88b86a827e807e5463a3a353ec004a47b52ecb52 100644 (file)
@@ -63,7 +63,7 @@ users will fare this time.
 
    Please refer to:
 
-   http://git.or.cz/gitwiki/GitFaq#non-bare
+   https://archive.kernel.org/oldwiki/git.wiki.kernel.org/index.php/GitFaq.html#non-bare
    https://lore.kernel.org/git/7vbptlsuyv.fsf@gitster.siamese.dyndns.org/
 
    for more details on the reason why this change is needed and the
diff --git a/Documentation/RelNotes/2.44.0.txt b/Documentation/RelNotes/2.44.0.txt
new file mode 100644 (file)
index 0000000..8dcc97c
--- /dev/null
@@ -0,0 +1,265 @@
+Git v2.44 Release Notes
+=======================
+
+Backward Compatibility Notes
+
+ * "git chekcout -B <branch>" used to allow switching to a branch that
+   is in use on another worktree, but this was by mistake.  The users
+   need to use "--ignore-other-worktrees" option.
+
+
+UI, Workflows & Features
+
+ * "git add" and "git stash" learned to support the ":(attr:...)"
+   magic pathspec.
+
+ * "git rebase --autosquash" is now enabled for non-interactive rebase,
+   but it is still incompatible with the apply backend.
+
+ * Introduce "git replay", a tool meant on the server side without
+   working tree to recreate a history.
+
+ * "git merge-file" learned to take the "--diff-algorithm" option to
+   use algorithm different from the default "myers" diff.
+
+ * Command line completion (in contrib/) learned to complete path
+   arguments to the "add/set" subcommands of "git sparse-checkout"
+   better.
+
+ * "git checkout -B <branch> [<start-point>]" allowed a branch that is
+   in use in another worktree to be updated and checked out, which
+   might be a bit unexpected.  The rule has been tightened, which is a
+   breaking change.  "--ignore-other-worktrees" option is required to
+   unbreak you, if you are used to the current behaviour that "-B"
+   overrides the safety.
+
+ * The builtin_objectmode attribute is populated for each path
+   without adding anything in .gitattributes files, which would be
+   useful in magic pathspec, e.g., ":(attr:builtin_objectmode=100755)"
+   to limit to executables.
+
+ * "git fetch" learned to pay attention to "fetch.all" configuration
+   variable, which pretends as if "--all" was passed from the command
+   line when no remote parameter was given.
+
+ * In addition to (rather cryptic) Security Identifiers, show username
+   and domain in the error message when we barf on mismatch between
+   the Git directory and the current user on Windows.
+
+ * The error message given when "git branch -d branch" fails due to
+   commits unique to the branch has been split into an error and a new
+   conditional advice message.
+
+ * When given an existing but unreadable file as a configuration file,
+   gitweb behaved as if the file did not exist at all, but now it
+   errors out.  This is a change that may break backward compatibility.
+
+ * When $HOME/.gitignore is missing but XDG config file available, we
+   should write into the latter, not former.  "git gc" and "git
+   maintenance" wrote into a wrong "global config" file, which have
+   been corrected.
+
+ * Define "special ref" as a very narrow set that consists of
+   FETCH_HEAD and MERGE_HEAD, and clarify everything else that used to
+   be classified as such are actually just pseudorefs.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * Process to add some form of low-level unit tests has started.
+
+ * Add support for GitLab CI.
+
+ * "git for-each-ref --no-sort" still sorted the refs alphabetically
+   which paid non-trivial cost.  It has been redefined to show output
+   in an unspecified order, to allow certain optimizations to take
+   advantage of.
+
+ * Simplify API implementation to delete references by eliminating
+   duplication.
+
+ * Subject approxidate() and show_date() machinery to OSS-Fuzz.
+
+ * A new helper to let us pretend that we called lstat() when we know
+   our cache_entry is up-to-date via fsmonitor.
+
+ * The optimization based on fsmonitor in the "diff --cached"
+   codepath is resurrected with the "fake-lstat" introduced earlier.
+
+ * Test balloon to use C99 "bool" type from <stdbool.h> has been
+   added.
+
+ * "git clone" has been prepared to allow cloning a repository with
+   non-default hash function into a repository that uses the reftable
+   backend.
+
+ * Streaming spans of packfile data used to be done only from a
+   single, primary, pack in a repository with multiple packfiles.  It
+   has been extended to allow reuse from other packfiles, too.
+
+ * Comment updates to help developers not to attempt to modify
+   messages from plumbing commands that must stay constant.
+
+   It might make sense to reassess the plumbing needs every few years,
+   but that should be done as a separate effort.
+
+ * Move test-ctype helper to the unit-test framework.
+
+ * Instead of manually creating refs/ hierarchy on disk upon a
+   creation of a secondary worktree, which is only usable via the
+   files backend, use the refs API to populate it.
+
+ * CI for GitLab learned to drive macOS jobs.
+
+ * A few tests to "git commit -o <pathspec>" and "git commit -i
+   <pathspec>" has been added.
+
+
+Fixes since v2.43
+-----------------
+
+ * The way CI testing used "prove" could lead to running the test
+   suite twice needlessly, which has been corrected.
+   (merge e7e03ef995 js/ci-discard-prove-state later to maint).
+
+ * Update ref-related tests.
+
+ * "git format-patch --encode-email-headers" ignored the option when
+   preparing the cover letter, which has been corrected.
+
+ * Newer versions of Getopt::Long started giving warnings against our
+   (ab)use of it in "git send-email".  Bump the minimum version
+   requirement for Perl to 5.8.1 (from September 2002) to allow
+   simplifying our implementation.
+   (merge 6ff658cc78 tz/send-email-negatable-options later to maint).
+
+ * Earlier we stopped relying on commit-graph that (still) records
+   information about commits that are lost from the object store,
+   which has negative performance implications.  The default has been
+   flipped to disable this pessimization.
+   (merge b1df3b3867 ps/commit-graph-less-paranoid later to maint).
+
+ * Stale URLs have been updated to their current counterparts (or
+   archive.org) and HTTP links are replaced with working HTTPS links.
+   (merge 62b4f7b9c6 js/update-urls-in-doc-and-comment later to maint).
+
+ * trace2 streams used to record the URLs that potentially embed
+   authentication material, which has been corrected.
+   (merge 16fa3eebc0 jh/trace2-redact-auth later to maint).
+
+ * The sample pre-commit hook that tries to catch introduction of new
+   paths that use potentially non-portable characters did not notice
+   an existing path getting renamed to such a problematic path, when
+   rename detection was enabled.
+   (merge d9fd71fa2a jp/use-diff-index-in-pre-commit-sample later to maint).
+
+ * The command line parser for the "log" family of commands was too
+   loose when parsing certain numbers, e.g., silently ignoring the
+   extra 'q' in "git log -n 1q" without complaining, which has been
+   tightened up.
+   (merge 71a1e94821 jc/revision-parse-int later to maint).
+
+ * "git $cmd --end-of-options --rev -- --path" for some $cmd failed
+   to interpret "--rev" as a rev, and "--path" as a path.  This was
+   fixed for many programs like "reset" and "checkout".
+   (merge 9385174627 jk/end-of-options later to maint).
+
+ * "git bisect reset" has been taught to clean up state files and refs
+   even when BISECT_START file is gone.
+   (merge daaa03e54c jk/bisect-reset-fix later to maint).
+
+ * Some codepaths did not correctly parse configuration variables
+   specified with valueless "true", which has been corrected.
+   (merge d49cb162fa jk/implicit-true later to maint).
+
+ * Code clean-up for sanity checking of command line options for "git
+   show-ref".
+   (merge 7382497372 rs/show-ref-incompatible-options later to maint).
+
+ * The code to parse the From e-mail header has been updated to avoid
+   recursion.
+   (merge dee182941f jk/mailinfo-iterative-unquote-comment later to maint).
+
+ * "git fetch --atomic" issued an unnecessary empty error message,
+   which has been corrected.
+   (merge 18ce48918c jx/fetch-atomic-error-message-fix later to maint).
+
+ * Command line completion script (in contrib/) learned to work better
+   with the reftable backend.
+   (merge 44dbb3bf29 sh/completion-with-reftable later to maint).
+
+ * "git status" is taught to show both the branch being bisected and
+   being rebased when both are in effect at the same time.
+   (merge 990adccbdf rj/status-bisect-while-rebase later to maint).
+
+ * "git archive --list extra garbage" silently ignored excess command
+   line parameters, which has been corrected.
+   (merge d6b6cd1393 jc/archive-list-with-extra-args later to maint).
+
+ * "git sparse-checkout set" added default patterns even when the
+   patterns are being fed from the standard input, which has been
+   corrected.
+   (merge 53ded839ae jc/sparse-checkout-set-default-fix later to maint).
+
+ * "git sparse-checkout (add|set) --[no-]cone --end-of-options" did
+   not handle "--end-of-options" correctly after a recent update.
+
+ * Unlike other environment variables that took the usual
+   true/false/yes/no as well as 0/1, GIT_FLUSH only understood 0/1,
+   which has been corrected.
+   (merge 556e68032f cp/git-flush-is-an-env-bool later to maint).
+
+ * Clearing in-core repository (happens during e.g., "git fetch
+   --recurse-submodules" with commit graph enabled) made in-core
+   commit object in an inconsistent state by discarding the necessary
+   data from commit-graph too early, which has been corrected.
+   (merge d70f554cdf jk/commit-graph-slab-clear-fix later to maint).
+
+ * Update to a new feature recently added, "git show-ref --exists".
+   (merge 0aabeaa562 tc/show-ref-exists-fix later to maint).
+
+ * oss-fuzz tests are built and run in CI.
+   (merge c4a9cf1df3 js/oss-fuzz-build-in-ci later to maint).
+
+ * Rename detection logic ignored the final line of a file if it is an
+   incomplete line.
+   (merge 1c5bc6971e en/diffcore-delta-final-line-fix later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge 50f1abcff6 js/packfile-h-typofix later to maint).
+   (merge cbf498eb53 jb/reflog-expire-delete-dry-run-options later to maint).
+   (merge 7854bf4960 rs/i18n-cannot-be-used-together later to maint).
+   (merge cd3c28c53a rs/column-leakfix later to maint).
+   (merge 866a1b9026 ps/ref-tests-update-more later to maint).
+   (merge e4299d26d4 mk/doc-gitfile-more later to maint).
+   (merge 792b86283b rs/incompatible-options-messages later to maint).
+   (merge ea8f9494ab jk/config-cleanup later to maint).
+   (merge d1bd3a8c34 jk/mailinfo-oob-read-fix later to maint).
+   (merge c0cadb0576 ps/reftable-fixes later to maint).
+   (merge 647b5e0998 ps/chainlint-self-check-update later to maint).
+   (merge 68fcebfb1a es/add-doc-list-short-form-of-all-in-synopsis later to maint).
+   (merge bc62d27d5c jc/doc-most-refs-are-not-that-special later to maint).
+   (merge 6d6f1cd7ee jc/doc-misspelt-refs-fix later to maint).
+   (merge 37e8d795be sp/test-i18ngrep later to maint).
+   (merge fbc6526ea6 rs/t6300-compressed-size-fix later to maint).
+   (merge 45184afb4d rs/rebase-use-strvec-pushf later to maint).
+   (merge a762af3dfd jc/retire-cas-opt-name-constant later to maint).
+   (merge de7c27a186 la/trailer-cleanups later to maint).
+   (merge d44b517137 jc/orphan-unborn later to maint).
+   (merge 63956c553d ml/doc-merge-updates later to maint).
+   (merge d57c671a51 en/header-cleanup later to maint).
+   (merge 5b7eec4bc5 rs/fast-import-simplify-mempool-allocation later to maint).
+   (merge 291873e5d6 js/contributor-docs-updates later to maint).
+   (merge 54d8a2531b jk/t1006-cat-file-objectsize-disk later to maint).
+   (merge 7033d5479b jx/sideband-chomp-newline-fix later to maint).
+   (merge 9cd30af991 ms/rebase-insnformat-doc-fix later to maint).
+   (merge 03bcc93769 cp/sideband-array-index-comment-fix later to maint).
+   (merge 993d38a066 jk/index-pack-lsan-false-positive-fix later to maint).
+   (merge 25aec06326 ib/rebase-reschedule-doc later to maint).
+   (merge 5aea3955bc rj/clarify-branch-doc-m later to maint).
+   (merge 9cce3be2df bk/bisect-doc-fix later to maint).
+   (merge 8f50984cf4 ne/doc-filter-blob-limit-fix later to maint).
+   (merge f10b0989b8 la/strvec-comment-fix later to maint).
+   (merge 8430b438f6 vd/fsck-submodule-url-test later to maint).
+   (merge f10031fadd nb/rebase-x-shell-docfix later to maint).
+   (merge af3d2c160f jc/majordomo-to-subspace later to maint).
index bce7f97815cb52a5ebbba4289b4b5013929b99ee..e734a3f0f175795f0633e93c07afd13f9183efc8 100644 (file)
@@ -355,9 +355,21 @@ If you like, you can put extra tags at the end:
   patch after a detailed analysis.
 . `Tested-by:` is used to indicate that the person applied the patch
   and found it to have the desired effect.
-
-You can also create your own tag or use one that's in common usage
-such as "Thanks-to:", "Based-on-patch-by:", or "Mentored-by:".
+. `Co-authored-by:` is used to indicate that people exchanged drafts
+   of a patch before submitting it.
+. `Helped-by:` is used to credit someone who suggested ideas for
+  changes without providing the precise changes in patch form.
+. `Mentored-by:` is used to credit someone with helping develop a
+  patch as part of a mentorship program (e.g., GSoC or Outreachy).
+. `Suggested-by:` is used to credit someone with suggesting the idea
+  for a patch.
+
+While you can also create your own trailer if the situation warrants it, we
+encourage you to instead use one of the common trailers in this project
+highlighted above.
+
+Only capitalize the very first letter of tags, i.e. favor
+"Signed-off-by" over "Signed-Off-By" and "Acked-by:" over "Acked-By".
 
 [[git-tools]]
 === Generate your patch using Git tools out of your commits.
@@ -570,7 +582,7 @@ their trees themselves.
   master).
 
 * Read the Git mailing list, the maintainer regularly posts messages
-  entitled "What's cooking in git.git" and "What's in git.git" giving
+  entitled "What's cooking in git.git" giving
   the status of various proposed changes.
 
 == GitHub CI[[GHCI]]
@@ -590,11 +602,12 @@ After the initial setup, CI will run whenever you push new changes
 to your fork of Git on GitHub.  You can monitor the test state of all your
 branches here: `https://github.com/<Your GitHub handle>/git/actions/workflows/main.yml`
 
-If a branch did not pass all test cases then it is marked with a red
-cross. In that case you can click on the failing job and navigate to
-"ci/run-build-and-tests.sh" and/or "ci/print-test-failures.sh". You
-can also download "Artifacts" which are tarred (or zipped) archives
-with test data relevant for debugging.
+If a branch does not pass all test cases then it will be marked with a
+red +x+, instead of a green check. In that case, you can click on the
+failing job and navigate to "ci/run-build-and-tests.sh" and/or
+"ci/print-test-failures.sh". You can also download "Artifacts" which
+are zip archives containing tarred (or zipped) archives with test data
+relevant for debugging.
 
 Then fix the problem and push your fix to your GitHub fork. This will
 trigger a new CI build to ensure all tests pass.
@@ -686,7 +699,7 @@ message to an external program, and this is a handy way to drive
 `git am`.  However, if the message is MIME encoded, what is
 piped into the program is the representation you see in your
 `*Article*` buffer after unwrapping MIME.  This is often not what
-you would want for two reasons.  It tends to screw up non ASCII
+you would want for two reasons.  It tends to screw up non-ASCII
 characters (most notably in people's names), and also
 whitespaces (fatal in patches).  Running "C-u g" to display the
 message in raw form before using "|" to run the pipe can work
index 2737381a11a1b40dfb2ec3f6f6b182f83a91c9fd..25c091752445a820f9ef9849ab3c37673da42bd7 100644 (file)
@@ -4,27 +4,59 @@ advice.*::
        can tell Git that you do not need help by setting these to 'false':
 +
 --
+       addEmbeddedRepo::
+               Advice on what to do when you've accidentally added one
+               git repo inside of another.
+       addEmptyPathspec::
+               Advice shown if a user runs the add command without providing
+               the pathspec parameter.
+       addIgnoredFile::
+               Advice shown if a user attempts to add an ignored file to
+               the index.
+       amWorkDir::
+               Advice that shows the location of the patch file when
+               linkgit:git-am[1] fails to apply it.
        ambiguousFetchRefspec::
                Advice shown when a fetch refspec for multiple remotes maps to
                the same remote-tracking branch namespace and causes branch
                tracking set-up to fail.
+       checkoutAmbiguousRemoteBranchName::
+               Advice shown when the argument to
+               linkgit:git-checkout[1] and linkgit:git-switch[1]
+               ambiguously resolves to a
+               remote tracking branch on more than one remote in
+               situations where an unambiguous argument would have
+               otherwise caused a remote-tracking branch to be
+               checked out. See the `checkout.defaultRemote`
+               configuration variable for how to set a given remote
+               to be used by default in some situations where this
+               advice would be printed.
+       commitBeforeMerge::
+               Advice shown when linkgit:git-merge[1] refuses to
+               merge to avoid overwriting local changes.
+       detachedHead::
+               Advice shown when you used
+               linkgit:git-switch[1] or linkgit:git-checkout[1]
+               to move to the detached HEAD state, to instruct how to
+               create a local branch after the fact.
+       diverging::
+               Advice shown when a fast-forward is not possible.
        fetchShowForcedUpdates::
                Advice shown when linkgit:git-fetch[1] takes a long time
                to calculate forced updates after ref updates, or to warn
                that the check is disabled.
-       pushUpdateRejected::
-               Set this variable to 'false' if you want to disable
-               'pushNonFFCurrent', 'pushNonFFMatching', 'pushAlreadyExists',
-               'pushFetchFirst', 'pushNeedsForce', and 'pushRefNeedsUpdate'
-               simultaneously.
-       pushNonFFCurrent::
-               Advice shown when linkgit:git-push[1] fails due to a
-               non-fast-forward update to the current branch.
-       pushNonFFMatching::
-               Advice shown when you ran linkgit:git-push[1] and pushed
-               'matching refs' explicitly (i.e. you used ':', or
-               specified a refspec that isn't your current branch) and
-               it resulted in a non-fast-forward error.
+       forceDeleteBranch::
+               Advice shown when a user tries to delete a not fully merged
+               branch without the force option set.
+       ignoredHook::
+               Advice shown if a hook is ignored because the hook is not
+               set as executable.
+       implicitIdentity::
+               Advice on how to set your identity configuration when
+               your information is guessed from the system username and
+               domain name.
+       nestedTag::
+               Advice shown if a user attempts to recursively tag a tag object.
        pushAlreadyExists::
                Shown when linkgit:git-push[1] rejects an update that
                does not qualify for fast-forwarding (e.g., a tag.)
@@ -37,6 +69,18 @@ advice.*::
                tries to overwrite a remote ref that points at an
                object that is not a commit-ish, or make the remote
                ref point at an object that is not a commit-ish.
+       pushNonFFCurrent::
+               Advice shown when linkgit:git-push[1] fails due to a
+               non-fast-forward update to the current branch.
+       pushNonFFMatching::
+               Advice shown when you ran linkgit:git-push[1] and pushed
+               'matching refs' explicitly (i.e. you used ':', or
+               specified a refspec that isn't your current branch) and
+               it resulted in a non-fast-forward error.
+       pushRefNeedsUpdate::
+               Shown when linkgit:git-push[1] rejects a forced update of
+               a branch when its remote-tracking ref has updates that we
+               do not have locally.
        pushUnqualifiedRefname::
                Shown when linkgit:git-push[1] gives up trying to
                guess based on the source and destination refs what
@@ -44,10 +88,23 @@ advice.*::
                we can still suggest that the user push to either
                refs/heads/* or refs/tags/* based on the type of the
                source object.
-       pushRefNeedsUpdate::
-               Shown when linkgit:git-push[1] rejects a forced update of
-               a branch when its remote-tracking ref has updates that we
-               do not have locally.
+       pushUpdateRejected::
+               Set this variable to 'false' if you want to disable
+               'pushNonFFCurrent', 'pushNonFFMatching', 'pushAlreadyExists',
+               'pushFetchFirst', 'pushNeedsForce', and 'pushRefNeedsUpdate'
+               simultaneously.
+       resetNoRefresh::
+               Advice to consider using the `--no-refresh` option to
+               linkgit:git-reset[1] when the command takes more than 2 seconds
+               to refresh the index after reset.
+       resolveConflict::
+               Advice shown by various commands when conflicts
+               prevent the operation from being performed.
+       rmHints::
+               In case of failure in the output of linkgit:git-rm[1],
+               show directions on how to proceed from the current state.
+       sequencerInUse::
+               Advice shown when a sequencer command is already in progress.
        skippedCherryPicks::
                Shown when linkgit:git-rebase[1] skips a commit that has already
                been cherry-picked onto the upstream branch.
@@ -68,78 +125,24 @@ advice.*::
                Advise to consider using the `-u` option to linkgit:git-status[1]
                when the command takes more than 2 seconds to enumerate untracked
                files.
-       commitBeforeMerge::
-               Advice shown when linkgit:git-merge[1] refuses to
-               merge to avoid overwriting local changes.
-       resetNoRefresh::
-               Advice to consider using the `--no-refresh` option to
-               linkgit:git-reset[1] when the command takes more than 2 seconds
-               to refresh the index after reset.
-       resolveConflict::
-               Advice shown by various commands when conflicts
-               prevent the operation from being performed.
-       sequencerInUse::
-               Advice shown when a sequencer command is already in progress.
-       implicitIdentity::
-               Advice on how to set your identity configuration when
-               your information is guessed from the system username and
-               domain name.
-       detachedHead::
-               Advice shown when you used
-               linkgit:git-switch[1] or linkgit:git-checkout[1]
-               to move to the detached HEAD state, to instruct how to
-               create a local branch after the fact.
-       suggestDetachingHead::
-               Advice shown when linkgit:git-switch[1] refuses to detach HEAD
-               without the explicit `--detach` option.
-       checkoutAmbiguousRemoteBranchName::
-               Advice shown when the argument to
-               linkgit:git-checkout[1] and linkgit:git-switch[1]
-               ambiguously resolves to a
-               remote tracking branch on more than one remote in
-               situations where an unambiguous argument would have
-               otherwise caused a remote-tracking branch to be
-               checked out. See the `checkout.defaultRemote`
-               configuration variable for how to set a given remote
-               to be used by default in some situations where this
-               advice would be printed.
-       amWorkDir::
-               Advice that shows the location of the patch file when
-               linkgit:git-am[1] fails to apply it.
-       rmHints::
-               In case of failure in the output of linkgit:git-rm[1],
-               show directions on how to proceed from the current state.
-       addEmbeddedRepo::
-               Advice on what to do when you've accidentally added one
-               git repo inside of another.
-       ignoredHook::
-               Advice shown if a hook is ignored because the hook is not
-               set as executable.
-       waitingForEditor::
-               Print a message to the terminal whenever Git is waiting for
-               editor input from the user.
-       nestedTag::
-               Advice shown if a user attempts to recursively tag a tag object.
        submoduleAlternateErrorStrategyDie::
                Advice shown when a submodule.alternateErrorStrategy option
                configured to "die" causes a fatal error.
        submodulesNotUpdated::
                Advice shown when a user runs a submodule command that fails
                because `git submodule update --init` was not run.
-       addIgnoredFile::
-               Advice shown if a user attempts to add an ignored file to
-               the index.
-       addEmptyPathspec::
-               Advice shown if a user runs the add command without providing
-               the pathspec parameter.
+       suggestDetachingHead::
+               Advice shown when linkgit:git-switch[1] refuses to detach HEAD
+               without the explicit `--detach` option.
        updateSparsePath::
                Advice shown when either linkgit:git-add[1] or linkgit:git-rm[1]
                is asked to update index entries outside the current sparse
                checkout.
-       diverging::
-               Advice shown when a fast-forward is not possible.
+       waitingForEditor::
+               Print a message to the terminal whenever Git is waiting for
+               editor input from the user.
        worktreeAddOrphan::
                Advice shown when a user tries to create a worktree from an
-               invalid reference, to instruct how to create a new orphan
+               invalid reference, to instruct how to create a new unborn
                branch instead.
 --
index bccaec7a963679f8262d7c8d056fa4429b9586d1..66db0e15da7db82819de941626b7abc5983862c5 100644 (file)
@@ -7,6 +7,17 @@ Note that this setting should only be set by linkgit:git-init[1] or
 linkgit:git-clone[1].  Trying to change it after initialization will not
 work and will produce hard-to-diagnose issues.
 
+extensions.refStorage::
+       Specify the ref storage format to use. The acceptable values are:
++
+include::../ref-storage-format.txt[]
++
+It is an error to specify this key unless `core.repositoryFormatVersion` is 1.
++
+Note that this setting should only be set by linkgit:git-init[1] or
+linkgit:git-clone[1]. Trying to change it after initialization will not
+work and will produce hard-to-diagnose issues.
+
 extensions.worktreeConfig::
        If enabled, then worktrees will load config settings from the
        `$GIT_DIR/config.worktree` file in addition to the
index aea5b97477b64ef664b6b35faad78b2eb618db9a..d7dc461bd16ad782933a40e060c523b063cb1aca 100644 (file)
@@ -50,6 +50,12 @@ fetch.pruneTags::
        refs. See also `remote.<name>.pruneTags` and the PRUNING
        section of linkgit:git-fetch[1].
 
+fetch.all::
+       If true, fetch will attempt to update all available remotes.
+       This behavior can be overridden by passing `--no-all` or by
+       explicitly specifying one or more remote(s) to fetch from.
+       Defaults to false.
+
 fetch.output::
        Control how ref update status is printed. Valid values are
        `full` and `compact`. Default value is `full`. See the
index c98412b697efea446b5291c0bc52af3611ade5e0..7410e930e530fdf4b795da076dc6a3b3f1f4b0f1 100644 (file)
@@ -119,7 +119,7 @@ format.notes::
        `--notes=<ref>`, where `ref` is the non-boolean value. Defaults
        to false.
 +
-If one wishes to use the ref `ref/notes/true`, please use that literal
+If one wishes to use the ref `refs/notes/true`, please use that literal
 instead.
 +
 This configuration can be specified multiple times in order to allow
index f50df9dbce89891f0c6d4a5563c5ab73be4325df..9c630863e6ff18372172111619d4ad2a54f3264f 100644 (file)
@@ -28,11 +28,17 @@ all existing objects. You can force recompression by passing the -F option
 to linkgit:git-repack[1].
 
 pack.allowPackReuse::
-       When true, and when reachability bitmaps are enabled,
-       pack-objects will try to send parts of the bitmapped packfile
-       verbatim. This can reduce memory and CPU usage to serve fetches,
-       but might result in sending a slightly larger pack. Defaults to
-       true.
+       When true or "single", and when reachability bitmaps are
+       enabled, pack-objects will try to send parts of the bitmapped
+       packfile verbatim. When "multi", and when a multi-pack
+       reachability bitmap is available, pack-objects will try to send
+       parts of all packs in the MIDX.
++
+       If only a single pack bitmap is available, and
+       `pack.allowPackReuse` is set to "multi", reuse parts of just the
+       bitmapped packfile. This can reduce memory and CPU usage to
+       serve fetches, but might result in sending a slightly larger
+       pack. Defaults to true.
 
 pack.island::
        An extended regular expression configuring a set of delta
index 9c248accec2c5d572cf11f26ffcbfe8e7eab1465..c6187ab28b2fb731fad341c7d3c193872558645d 100644 (file)
@@ -9,7 +9,9 @@ rebase.stat::
        rebase. False by default.
 
 rebase.autoSquash::
-       If set to true enable `--autosquash` option by default.
+       If set to true, enable the `--autosquash` option of
+       linkgit:git-rebase[1] by default for interactive mode.
+       This can be overridden with the `--no-autosquash` option.
 
 rebase.autoStash::
        When set to true, automatically create a temporary stash entry
@@ -38,7 +40,7 @@ rebase.missingCommitsCheck::
 rebase.instructionFormat::
        A format string, as specified in linkgit:git-log[1], to be used for the
        todo list during an interactive rebase.  The format will
-       automatically have the long commit hash prepended to the format.
+       automatically have the commit hash prepended to the format.
 
 rebase.abbreviateCommands::
        If set to true, `git rebase` will use abbreviated command names in the
index a1d6633a4f15b719a33615ca0ce2805ec2fa0b3e..54ebb4452e997f8c62c9dd2421948086249102bd 100644 (file)
@@ -1,5 +1,6 @@
---all::
-       Fetch all remotes.
+--[no-]all::
+       Fetch all remotes. This overrides the configuration variable
+       `fetch.all`.
 
 -a::
 --append::
index ed44c1cb31ca6eaf7e63d7920cbc15c3a2e0619f..3d2e6707168b69d591e3bf2a50abe3a62fc2ee23 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git add' [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p]
-         [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]] [--sparse]
+         [--edit | -e] [--[no-]all | -A | --[no-]ignore-removal | [--update | -u]] [--sparse]
          [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [--renormalize]
          [--chmod=(+|-)x] [--pathspec-from-file=<file> [--pathspec-file-nul]]
          [--] [<pathspec>...]
index 191b4a42b6dcca3f270e9087ad9f7aa6b214f0e0..8e01f1d6189d523fc4c5a196c64349117ea389fe 100644 (file)
@@ -17,7 +17,7 @@ The command takes various subcommands, and different options depending
 on the subcommand:
 
  git bisect start [--term-(new|bad)=<term-new> --term-(old|good)=<term-old>]
-                 [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]
+                 [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]
  git bisect (bad|new|<term-new>) [<rev>]
  git bisect (good|old|<term-old>) [<rev>...]
  git bisect terms [--term-good | --term-bad]
@@ -299,7 +299,7 @@ Cutting down bisection by giving more parameters to bisect start
 
 You can further cut down the number of trials, if you know what part of
 the tree is involved in the problem you are tracking down, by specifying
-path parameters when issuing the `bisect start` command:
+pathspec parameters when issuing the `bisect start` command:
 
 ------------
 $ git bisect start -- arch/i386 include/asm-i386
@@ -362,7 +362,7 @@ OPTIONS
 --no-checkout::
 +
 Do not checkout the new working tree at each iteration of the bisection
-process. Instead just update a special reference named `BISECT_HEAD` to make
+process. Instead just update the reference named `BISECT_HEAD` to make
 it point to the commit that should be tested.
 +
 This option may be useful when the test you would perform in each step
index 4395aa935438aaac47cfe41f5db7a1eadafe8a25..0b08442932354fcdcbb321f0c45b80a3a5bc4fdf 100644 (file)
@@ -312,7 +312,8 @@ superproject's "origin/main", but tracks the submodule's "origin/main".
        option is omitted, the current HEAD will be used instead.
 
 <oldbranch>::
-       The name of an existing branch to rename.
+       The name of an existing branch.  If this option is omitted,
+       the name of the current branch will be used instead.
 
 <newbranch>::
        The new name for an existing branch. The same restrictions as for
index 240c54639e8e85b5a3ecf1fb8fec0266614ec9b5..8bdfa54ab09b4c4e8e4cb61767527e2ec06900b2 100644 (file)
@@ -63,7 +63,9 @@ $ git checkout <branch>
 ------------
 +
 that is to say, the branch is not reset/created unless "git checkout" is
-successful.
+successful (e.g., when the branch is in use in another worktree, not
+just the current branch stays the same, but the branch is not reset to
+the start-point, either).
 
 'git checkout' --detach [<branch>]::
 'git checkout' [--detach] <commit>::
@@ -215,7 +217,7 @@ variable.
        below for details.
 
 --orphan <new-branch>::
-       Create a new 'orphan' branch, named `<new-branch>`, started from
+       Create a new unborn branch, named `<new-branch>`, started from
        `<start-point>` and switch to it.  The first commit made on this
        new branch will have no parents and it will be the root of a new
        history totally disconnected from all the other branches and
index c37c4a37f7412c0b712d14067c2d7c7d975933c5..6e43eb9c205371548655c5abae5e59bb963c959a 100644 (file)
@@ -311,6 +311,12 @@ or `--mirror` is given)
        The result is Git repository can be separated from working
        tree.
 
+--ref-format=<ref-format::
+
+Specify the given ref storage format for the repository. The valid values are:
++
+include::ref-storage-format.txt[]
+
 -j <n>::
 --jobs <n>::
        The number of submodules fetched at the same time.
index b3f27671a0c6eb20bfd2bce88bcc74079691fe70..90fdc2551a349939a0cbf9ab12d792ab20f0b765 100644 (file)
@@ -22,7 +22,7 @@ DESCRIPTION
 deprecated; it does not work with cvsps version 3 and later.  If you are
 performing a one-shot import of a CVS repository consider using
 http://cvs2svn.tigris.org/cvs2git.html[cvs2git] or
-http://www.catb.org/esr/cvs-fast-export/[cvs-fast-export].
+https://gitlab.com/esr/cvs-fast-export[cvs-fast-export].
 
 Imports a CVS repository into Git. It will either create a new
 repository, or incrementally import into an existing one.
@@ -221,7 +221,7 @@ Problems related to tags:
 If you suspect that any of these issues may apply to the repository you
 want to import, consider using cvs2git:
 
-* cvs2git (part of cvs2svn), `http://subversion.apache.org/`
+* cvs2git (part of cvs2svn), `https://subversion.apache.org/`
 
 GIT
 ---
index 08087ffad5fe9929cc04629ba4aeaa4240f457e4..c065f023eca3fb1ce60c4c41d60d35c14b5c916b 100644 (file)
@@ -103,7 +103,7 @@ Just in case you are doing something exotic, it should be
 noted that all of the <commit> in the above description, except
 in the `--merge-base` case and in the last two forms that use `..`
 notations, can be any <tree>. A tree of interest is the one pointed to
-by the special ref `AUTO_MERGE`, which is written by the 'ort' merge
+by the ref named `AUTO_MERGE`, which is written by the 'ort' merge
 strategy upon hitting merge conflicts (see linkgit:git-merge[1]).
 Comparing the working tree with `AUTO_MERGE` shows changes you've made
 so far to resolve textual conflicts (see the examples below).
index e86d5700ddfe02e72e1cad809f29c555b15b6495..be9543f6840be3c6a477fb8537d014b0f7dfba7b 100644 (file)
@@ -51,17 +51,14 @@ OPTIONS
        key.
 
 --format=<format>::
-       A string that interpolates `%(fieldname)` from a ref being shown
-       and the object it points at.  If `fieldname`
-       is prefixed with an asterisk (`*`) and the ref points
-       at a tag object, use the value for the field in the object
-       which the tag object refers to (instead of the field in the tag object).
-       When unspecified, `<format>` defaults to
-       `%(objectname) SPC %(objecttype) TAB %(refname)`.
-       It also interpolates `%%` to `%`, and `%xx` where `xx`
-       are hex digits interpolates to character with hex code
-       `xx`; for example `%00` interpolates to `\0` (NUL),
-       `%09` to `\t` (TAB) and `%0a` to `\n` (LF).
+       A string that interpolates `%(fieldname)` from a ref being shown and
+       the object it points at. In addition, the string literal `%%`
+       renders as `%` and `%xx` - where `xx` are hex digits - renders as
+       the character with hex code `xx`. For example, `%00` interpolates to
+       `\0` (NUL), `%09` to `\t` (TAB), and `%0a` to `\n` (LF).
++
+When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype)
+TAB %(refname)`.
 
 --color[=<when>]::
        Respect any colors specified in the `--format` option. The
@@ -298,6 +295,10 @@ fields will correspond to the appropriate date or name-email-date tuple
 from the `committer` or `tagger` fields depending on the object type.
 These are intended for working on a mix of annotated and lightweight tags.
 
+For tag objects, a `fieldname` prefixed with an asterisk (`*`) expands to
+the `fieldname` value of the peeled object, rather than that of the tag
+object itself.
+
 Fields that have name-email-date tuple as its value (`author`,
 `committer`, and `tagger`) can be suffixed with `name`, `email`,
 and `date` to extract the named component.  For email fields (`authoremail`,
index aaafce24be20c5690adeb9d9e59133285fa10f96..414da6b73e7dc9bee177cd91227acb36c6ed070a 100644 (file)
@@ -610,8 +610,8 @@ Approach #3 (external editor)
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 The following Thunderbird extensions are needed:
-AboutConfig from http://aboutconfig.mozdev.org/ and
-External Editor from http://globs.org/articles.php?lng=en&pg=8
+AboutConfig from https://mjg.github.io/AboutConfig/ and
+External Editor from https://globs.org/articles.php?lng=en&pg=8
 
 1. Prepare the patch as a text file using your method of choice.
 
index f7b185151415b54b08fb21f54062842baf64e636..c8a89d7243bfbd43d68e3922998a378f68eb6c66 100644 (file)
@@ -135,7 +135,7 @@ flames ridiculing you if you don't check this.
 
 Thunderbird in particular is known to be problematic.  Thunderbird
 users may wish to visit this web page for more information:
-  http://kb.mozillazine.org/Plain_text_e-mail_-_Thunderbird#Completely_plain_email
+  https://kb.mozillazine.org/Plain_text_e-mail_-_Thunderbird#Completely_plain_email
 
 SEE ALSO
 --------
index 6f0d2973bf444a510ae4989dee9aa1e360eee247..e8dc645bb59a8664865c9eed00698ee4374ea4a3 100644 (file)
@@ -11,6 +11,7 @@ SYNOPSIS
 [verse]
 'git init' [-q | --quiet] [--bare] [--template=<template-directory>]
          [--separate-git-dir <git-dir>] [--object-format=<format>]
+         [--ref-format=<format>]
          [-b <branch-name> | --initial-branch=<branch-name>]
          [--shared[=<permissions>]] [<directory>]
 
@@ -57,6 +58,12 @@ values are 'sha1' and (if enabled) 'sha256'.  'sha1' is the default.
 +
 include::object-format-disclaimer.txt[]
 
+--ref-format=<format>::
+
+Specify the given ref storage format for the repository. The valid values are:
++
+include::ref-storage-format.txt[]
+
 --template=<template-directory>::
 
 Specify the directory from which templates will be used.  (See the "TEMPLATE
index 6a081eacb72372e332850118ab9df7280fa6f08d..71915a00fa472e73f5fadd2205f964919d3262db 100644 (file)
@@ -92,6 +92,12 @@ object store and the object ID of its blob is written to standard output.
        Instead of leaving conflicts in the file, resolve conflicts
        favouring our (or their or both) side of the lines.
 
+--diff-algorithm={patience|minimal|histogram|myers}::
+       Use a different diff algorithm while merging. The current default is "myers",
+       but selecting more recent algorithm such as "histogram" can help
+       avoid mismerges that occur due to unimportant matching lines
+       (such as braces from distinct functions). See also
+       linkgit:git-diff[1] `--diff-algorithm`.
 
 EXAMPLES
 --------
index e8ab34031919fa4d2ac920e1cd706c77c7c65138..1ab69f61f5749a21def2b08d7cd9466f59c3cd5c 100644 (file)
@@ -20,12 +20,12 @@ DESCRIPTION
 -----------
 Incorporates changes from the named commits (since the time their
 histories diverged from the current branch) into the current
-branch.  This command is used by 'git pull' to incorporate changes
+branch.  This command is used by `git pull` to incorporate changes
 from another repository and can be used by hand to merge changes
 from one branch into another.
 
 Assume the following history exists and the current branch is
-"`master`":
+`master`:
 
 ------------
          A---B---C topic
@@ -33,7 +33,7 @@ Assume the following history exists and the current branch is
     D---E---F---G master
 ------------
 
-Then "`git merge topic`" will replay the changes made on the
+Then `git merge topic` will replay the changes made on the
 `topic` branch since it diverged from `master` (i.e., `E`) until
 its current commit (`C`) on top of `master`, and record the result
 in a new commit along with the names of the two parent commits and
@@ -46,21 +46,21 @@ a log message from the user describing the changes. Before the operation,
     D---E---F---G---H master
 ------------
 
-The second syntax ("`git merge --abort`") can only be run after the
-merge has resulted in conflicts. 'git merge --abort' will abort the
-merge process and try to reconstruct the pre-merge state. However,
-if there were uncommitted changes when the merge started (and
-especially if those changes were further modified after the merge
-was started), 'git merge --abort' will in some cases be unable to
-reconstruct the original (pre-merge) changes. Therefore:
+A merge stops if there's a conflict that cannot be resolved
+automatically or if `--no-commit` was provided when initiating the
+merge. At that point you can run `git merge --abort` or `git merge
+--continue`.
 
-*Warning*: Running 'git merge' with non-trivial uncommitted changes is
+`git merge --abort` will abort the merge process and try to reconstruct
+the pre-merge state. However, if there were uncommitted changes when the
+merge started (and especially if those changes were further modified
+after the merge was started), `git merge --abort` will in some cases be
+unable to reconstruct the original (pre-merge) changes. Therefore:
+
+*Warning*: Running `git merge` with non-trivial uncommitted changes is
 discouraged: while possible, it may leave you in a state that is hard to
 back out of in the case of a conflict.
 
-The third syntax ("`git merge --continue`") can only be run after the
-merge has resulted in conflicts.
-
 OPTIONS
 -------
 :git-merge: 1
@@ -74,8 +74,8 @@ include::merge-options.txt[]
 If `--log` is specified, a shortlog of the commits being merged
 will be appended to the specified message.
 +
-The 'git fmt-merge-msg' command can be
-used to give a good default for automated 'git merge'
+The `git fmt-merge-msg` command can be
+used to give a good default for automated `git merge`
 invocations. The automated message can include the branch description.
 
 --into-name <branch>::
@@ -104,14 +104,14 @@ include::rerere-options.txt[]
        present, apply it to the worktree.
 +
 If there were uncommitted worktree changes present when the merge
-started, 'git merge --abort' will in some cases be unable to
+started, `git merge --abort` will in some cases be unable to
 reconstruct these changes. It is therefore recommended to always
-commit or stash your changes before running 'git merge'.
+commit or stash your changes before running `git merge`.
 +
-'git merge --abort' is equivalent to 'git reset --merge' when
+`git merge --abort` is equivalent to `git reset --merge` when
 `MERGE_HEAD` is present unless `MERGE_AUTOSTASH` is also present in
-which case 'git merge --abort' applies the stash entry to the worktree
-whereas 'git reset --merge' will save the stashed changes in the stash
+which case `git merge --abort` applies the stash entry to the worktree
+whereas `git reset --merge` will save the stashed changes in the stash
 list.
 
 --quit::
@@ -120,8 +120,8 @@ list.
        stash entry will be saved to the stash list.
 
 --continue::
-       After a 'git merge' stops due to conflicts you can conclude the
-       merge by running 'git merge --continue' (see "HOW TO RESOLVE
+       After a `git merge` stops due to conflicts you can conclude the
+       merge by running `git merge --continue` (see "HOW TO RESOLVE
        CONFLICTS" section below).
 
 <commit>...::
@@ -144,25 +144,25 @@ PRE-MERGE CHECKS
 Before applying outside changes, you should get your own work in
 good shape and committed locally, so it will not be clobbered if
 there are conflicts.  See also linkgit:git-stash[1].
-'git pull' and 'git merge' will stop without doing anything when
-local uncommitted changes overlap with files that 'git pull'/'git
-merge' may need to update.
+`git pull` and `git merge` will stop without doing anything when
+local uncommitted changes overlap with files that `git pull`/`git
+merge` may need to update.
 
 To avoid recording unrelated changes in the merge commit,
-'git pull' and 'git merge' will also abort if there are any changes
+`git pull` and `git merge` will also abort if there are any changes
 registered in the index relative to the `HEAD` commit.  (Special
 narrow exceptions to this rule may exist depending on which merge
 strategy is in use, but generally, the index must match HEAD.)
 
-If all named commits are already ancestors of `HEAD`, 'git merge'
+If all named commits are already ancestors of `HEAD`, `git merge`
 will exit early with the message "Already up to date."
 
 FAST-FORWARD MERGE
 ------------------
 
 Often the current branch head is an ancestor of the named commit.
-This is the most common case especially when invoked from 'git
-pull': you are tracking an upstream repository, you have committed
+This is the most common case especially when invoked from `git
+pull`: you are tracking an upstream repository, you have committed
 no local changes, and now you want to update to a newer upstream
 revision.  In this case, a new commit is not needed to store the
 combined history; instead, the `HEAD` (along with the index) is
@@ -196,7 +196,7 @@ happens:
    can inspect the stages with `git ls-files -u`).  The working
    tree files contain the result of the merge operation; i.e. 3-way
    merge results with familiar conflict markers `<<<` `===` `>>>`.
-5. A special ref `AUTO_MERGE` is written, pointing to a tree
+5. A ref named `AUTO_MERGE` is written, pointing to a tree
    corresponding to the current content of the working tree (including
    conflict markers for textual conflicts).  Note that this ref is only
    written when the 'ort' merge strategy is used (the default).
@@ -269,7 +269,7 @@ Barbie's remark on your side.  The only thing you can tell is that your
 side wants to say it is hard and you'd prefer to go shopping, while the
 other side wants to claim it is easy.
 
-An alternative style can be used by setting the "merge.conflictStyle"
+An alternative style can be used by setting the `merge.conflictStyle`
 configuration variable to either "diff3" or "zdiff3".  In "diff3"
 style, the above conflict may look like this:
 
@@ -328,10 +328,10 @@ After seeing a conflict, you can do two things:
 
  * Resolve the conflicts.  Git will mark the conflicts in
    the working tree.  Edit the files into shape and
-   'git add' them to the index.  Use 'git commit' or
-   'git merge --continue' to seal the deal. The latter command
+   `git add` them to the index.  Use `git commit` or
+   `git merge --continue` to seal the deal. The latter command
    checks whether there is a (interrupted) merge in progress
-   before calling 'git commit'.
+   before calling `git commit`.
 
 You can work through the conflict with a number of tools:
 
@@ -392,7 +392,7 @@ CONFIGURATION
 
 branch.<name>.mergeOptions::
        Sets default options for merging into branch <name>. The syntax and
-       supported options are the same as those of 'git merge', but option
+       supported options are the same as those of `git merge`, but option
        values containing whitespace characters are currently not supported.
 
 include::includes/cmd-config-section-rest.txt[]
index b4526ca24612803e7b9a5242d706afd0459b7089..06206521fc322b04d6c874193d215e60d2368836 100644 (file)
@@ -523,7 +523,7 @@ See also INCOMPATIBLE OPTIONS below.
 +
 The commit list format can be changed by setting the configuration option
 rebase.instructionFormat.  A customized instruction format will automatically
-have the long commit hash prepended to the format.
+have the commit hash prepended to the format.
 +
 See also INCOMPATIBLE OPTIONS below.
 
@@ -589,21 +589,27 @@ See also INCOMPATIBLE OPTIONS below.
 
 --autosquash::
 --no-autosquash::
-       When the commit log message begins with "squash! ..." or "fixup! ..."
-       or "amend! ...", and there is already a commit in the todo list that
-       matches the same `...`, automatically modify the todo list of
-       `rebase -i`, so that the commit marked for squashing comes right after
-       the commit to be modified, and change the action of the moved commit
-       from `pick` to `squash` or `fixup` or `fixup -C` respectively. A commit
-       matches the `...` if the commit subject matches, or if the `...` refers
-       to the commit's hash. As a fall-back, partial matches of the commit
-       subject work, too. The recommended way to create fixup/amend/squash
-       commits is by using the `--fixup`, `--fixup=amend:` or `--fixup=reword:`
-       and `--squash` options respectively of linkgit:git-commit[1].
+       Automatically squash commits with specially formatted messages into
+       previous commits being rebased.  If a commit message starts with
+       "squash! ", "fixup! " or "amend! ", the remainder of the subject line
+       is taken as a commit specifier, which matches a previous commit if it
+       matches the subject line or the hash of that commit.  If no commit
+       matches fully, matches of the specifier with the start of commit
+       subjects are considered.
 +
-If the `--autosquash` option is enabled by default using the
-configuration variable `rebase.autoSquash`, this option can be
-used to override and disable this setting.
+In the rebase todo list, the actions of squash, fixup and amend commits are
+changed from `pick` to `squash`, `fixup` or `fixup -C`, respectively, and they
+are moved right after the commit they modify.  The `--interactive` option can
+be used to review and edit the todo list before proceeding.
++
+The recommended way to create commits with squash markers is by using the
+`--squash`, `--fixup`, `--fixup=amend:` or `--fixup=reword:` options of
+linkgit:git-commit[1], which take the target commit as an argument and
+automatically fill in the subject line of the new commit from that.
++
+Settting configuration variable `rebase.autoSquash` to true enables
+auto-squashing by default for interactive rebase.  The `--no-autosquash`
+option can be used to override that setting.
 +
 See also INCOMPATIBLE OPTIONS below.
 
@@ -620,13 +626,16 @@ See also INCOMPATIBLE OPTIONS below.
        Automatically reschedule `exec` commands that failed. This only makes
        sense in interactive mode (or when an `--exec` option was provided).
 +
-Even though this option applies once a rebase is started, it's set for
-the whole rebase at the start based on either the
-`rebase.rescheduleFailedExec` configuration (see linkgit:git-config[1]
-or "CONFIGURATION" below) or whether this option is
-provided. Otherwise an explicit `--no-reschedule-failed-exec` at the
-start would be overridden by the presence of
-`rebase.rescheduleFailedExec=true` configuration.
+This option applies once a rebase is started. It is preserved for the whole
+rebase based on, in order, the command line option provided to the initial `git
+rebase`, the `rebase.rescheduleFailedExec` configuration (see
+linkgit:git-config[1] or "CONFIGURATION" below), or it defaults to false.
++
+Recording this option for the whole rebase is a convenience feature. Otherwise
+an explicit `--no-reschedule-failed-exec` at the start would be overridden by
+the presence of a `rebase.rescheduleFailedExec=true` configuration when `git
+rebase --continue` is invoked. Currently, you cannot pass
+`--[no-]reschedule-failed-exec` to `git rebase --continue`.
 
 --update-refs::
 --no-update-refs::
@@ -957,10 +966,9 @@ The interactive rebase will stop when a command fails (i.e. exits with
 non-0 status) to give you an opportunity to fix the problem. You can
 continue with `git rebase --continue`.
 
-The "exec" command launches the command in a shell (the one specified
-in `$SHELL`, or the default shell if `$SHELL` is not set), so you can
-use shell features (like "cd", ">", ";" ...). The command is run from
-the root of the working tree.
+The "exec" command launches the command in a shell (the default one, usually
+/bin/sh), so you can use shell features (like "cd", ">", ";" ...). The command
+is run from the root of the working tree.
 
 ----------------------------------
 $ git rebase -i --exec "make test"
diff --git a/Documentation/git-replay.txt b/Documentation/git-replay.txt
new file mode 100644 (file)
index 0000000..f6c269c
--- /dev/null
@@ -0,0 +1,127 @@
+git-replay(1)
+=============
+
+NAME
+----
+git-replay - EXPERIMENTAL: Replay commits on a new base, works with bare repos too
+
+
+SYNOPSIS
+--------
+[verse]
+(EXPERIMENTAL!) 'git replay' ([--contained] --onto <newbase> | --advance <branch>) <revision-range>...
+
+DESCRIPTION
+-----------
+
+Takes ranges of commits and replays them onto a new location. Leaves
+the working tree and the index untouched, and updates no references.
+The output of this command is meant to be used as input to
+`git update-ref --stdin`, which would update the relevant branches
+(see the OUTPUT section below).
+
+THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.
+
+OPTIONS
+-------
+
+--onto <newbase>::
+       Starting point at which to create the new commits.  May be any
+       valid commit, and not just an existing branch name.
++
+When `--onto` is specified, the update-ref command(s) in the output will
+update the branch(es) in the revision range to point at the new
+commits, similar to the way how `git rebase --update-refs` updates
+multiple branches in the affected range.
+
+--advance <branch>::
+       Starting point at which to create the new commits; must be a
+       branch name.
++
+When `--advance` is specified, the update-ref command(s) in the output
+will update the branch passed as an argument to `--advance` to point at
+the new commits (in other words, this mimics a cherry-pick operation).
+
+<revision-range>::
+       Range of commits to replay. More than one <revision-range> can
+       be passed, but in `--advance <branch>` mode, they should have
+       a single tip, so that it's clear where <branch> should point
+       to. See "Specifying Ranges" in linkgit:git-rev-parse and the
+       "Commit Limiting" options below.
+
+include::rev-list-options.txt[]
+
+OUTPUT
+------
+
+When there are no conflicts, the output of this command is usable as
+input to `git update-ref --stdin`.  It is of the form:
+
+       update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
+       update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
+       update refs/heads/branch3 ${NEW_branch3_HASH} ${OLD_branch3_HASH}
+
+where the number of refs updated depends on the arguments passed and
+the shape of the history being replayed.  When using `--advance`, the
+number of refs updated is always one, but for `--onto`, it can be one
+or more (rebasing multiple branches simultaneously is supported).
+
+EXIT STATUS
+-----------
+
+For a successful, non-conflicted replay, the exit status is 0.  When
+the replay has conflicts, the exit status is 1.  If the replay is not
+able to complete (or start) due to some kind of error, the exit status
+is something other than 0 or 1.
+
+EXAMPLES
+--------
+
+To simply rebase `mybranch` onto `target`:
+
+------------
+$ git replay --onto target origin/main..mybranch
+update refs/heads/mybranch ${NEW_mybranch_HASH} ${OLD_mybranch_HASH}
+------------
+
+To cherry-pick the commits from mybranch onto target:
+
+------------
+$ git replay --advance target origin/main..mybranch
+update refs/heads/target ${NEW_target_HASH} ${OLD_target_HASH}
+------------
+
+Note that the first two examples replay the exact same commits and on
+top of the exact same new base, they only differ in that the first
+provides instructions to make mybranch point at the new commits and
+the second provides instructions to make target point at them.
+
+What if you have a stack of branches, one depending upon another, and
+you'd really like to rebase the whole set?
+
+------------
+$ git replay --contained --onto origin/main origin/main..tipbranch
+update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
+update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
+update refs/heads/tipbranch ${NEW_tipbranch_HASH} ${OLD_tipbranch_HASH}
+------------
+
+When calling `git replay`, one does not need to specify a range of
+commits to replay using the syntax `A..B`; any range expression will
+do:
+
+------------
+$ git replay --onto origin/main ^base branch1 branch2 branch3
+update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
+update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
+update refs/heads/branch3 ${NEW_branch3_HASH} ${OLD_branch3_HASH}
+------------
+
+This will simultaneously rebase `branch1`, `branch2`, and `branch3`,
+all commits they have since `base`, playing them on top of
+`origin/main`. These three branches may have commits on top of `base`
+that they have in common, but that does not need to be the case.
+
+GIT
+---
+Part of the linkgit:git[1] suite
index 912fab9f5e00b6e286b1d80a8164d76224408682..546faf9017723581ea8e13bea6f5f90b00372403 100644 (file)
@@ -307,6 +307,9 @@ The following options are unaffected by `--path-format`:
        input, multiple algorithms may be printed, space-separated.
        If not specified, the default is "storage".
 
+--show-ref-format::
+       Show the reference storage format used for the repository.
+
 
 Other Options
 ~~~~~~~~~~~~~
index 465011bad5021a602ef74169a499e29a61c32e1a..30deb7fe2a4f5506745a938b76d8bbcfb504e34b 100644 (file)
@@ -454,7 +454,7 @@ have been specified, in which case default to 'compose'.
                        998 characters unless a suitable transfer encoding
                        ('auto', 'base64', or 'quoted-printable') is used;
                        this is due to SMTP limits as described by
-                       http://www.ietf.org/rfc/rfc5322.txt.
+                       https://www.ietf.org/rfc/rfc5322.txt.
 --
 +
 Default is the value of `sendemail.validate`; if this is not set,
index c60fc9c138b5981ce5727efa4482350e699dbac5..f38e4c8afa1bcbcfba7bd8b3e1da0bbad9703f77 100644 (file)
@@ -59,13 +59,18 @@ out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 -c <new-branch>::
 --create <new-branch>::
        Create a new branch named `<new-branch>` starting at
-       `<start-point>` before switching to the branch. This is a
-       convenient shortcut for:
+       `<start-point>` before switching to the branch. This is the
+       transactional equivalent of
 +
 ------------
 $ git branch <new-branch>
 $ git switch <new-branch>
 ------------
++
+that is to say, the branch is not reset/created unless "git switch" is
+successful (e.g., when the branch is in use in another worktree, not
+just the current branch stays the same, but the branch is not reset to
+the start-point, either).
 
 -C <new-branch>::
 --force-create <new-branch>::
@@ -171,7 +176,7 @@ name, the guessing is aborted.  You can explicitly give a name with
        `branch.autoSetupMerge` configuration variable is true.
 
 --orphan <new-branch>::
-       Create a new 'orphan' branch, named `<new-branch>`. All
+       Create a new unborn branch, named `<new-branch>`. All
        tracked files are removed.
 
 --ignore-other-worktrees::
index 93d76f5d665a57a8bdca565add921cbd8b76bc37..2a240f53ba7f987b367a7fef689fe916543dedfd 100644 (file)
@@ -99,7 +99,7 @@ command will refuse to create the worktree (unless `--force` is used).
 If `<commit-ish>` is omitted, neither `--detach`, or `--orphan` is
 used, and there are no valid local branches (or remote branches if
 `--guess-remote` is specified) then, as a convenience, the new worktree is
-associated with a new orphan branch named `<branch>` (after
+associated with a new unborn branch named `<branch>` (after
 `$(basename <path>)` if neither `-b` or `-B` is used) as if `--orphan` was
 passed to the command. In the event the repository has a remote and
 `--guess-remote` is used, but no remote or local branches exist, then the
@@ -234,7 +234,7 @@ This can also be set up as the default behaviour by using the
 
 --orphan::
        With `add`, make the new worktree and index empty, associating
-       the worktree with a new orphan/unborn branch named `<new-branch>`.
+       the worktree with a new unborn branch named `<new-branch>`.
 
 --porcelain::
        With `list`, output in an easy-to-parse format for scripts.
index 2535a30194f978af900c0f84228680b3b8edf93a..da4e8d1303a07c85964acfbd564ed4c521140cbb 100644 (file)
@@ -556,6 +556,11 @@ double-quotes and respecting backslash escapes. E.g., the value
        is always used. The default is "sha1".
        See `--object-format` in linkgit:git-init[1].
 
+`GIT_DEFAULT_REF_FORMAT`::
+       If this variable is set, the default reference backend format for new
+       repositories will be set to this value. The default is "files".
+       See `--ref-format` in linkgit:git-init[1].
+
 Git Commits
 ~~~~~~~~~~~
 `GIT_AUTHOR_NAME`::
@@ -724,13 +729,12 @@ for further details.
        waiting for someone with sufficient permissions to fix it.
 
 `GIT_FLUSH`::
-// NEEDSWORK: make it into a usual Boolean environment variable
-       If this environment variable is set to "1", then commands such
+       If this Boolean environment variable is set to true, then commands such
        as 'git blame' (in incremental mode), 'git rev-list', 'git log',
        'git check-attr' and 'git check-ignore' will
        force a flush of the output stream after each record have been
        flushed. If this
-       variable is set to "0", the output of these commands will be done
+       variable is set to false, the output of these commands will be done
        using completely buffered I/O.   If this environment variable is
        not set, Git will choose buffered or record-oriented flushing
        based on whether stdout appears to be redirected to a file or not.
@@ -917,9 +921,9 @@ for full details.
        avoid issues with stale commit-graphs that contain references to
        already-deleted commits, but comes with a performance penalty.
 +
-The default is "true", which enables the aforementioned behavior.
-Setting this to "false" disables the existence check. This can lead to
-a performance improvement at the cost of consistency.
+The default is "false", which disables the aforementioned behavior.
+Setting this to "true" enables the existence check so that stale commits
+will never be returned from the commit-graph at the cost of performance.
 
 `GIT_ALLOW_PROTOCOL`::
        If set to a colon-separated list of protocols, behave as if
@@ -1025,10 +1029,11 @@ When first created, objects are stored in individual files, but for
 efficiency may later be compressed together into "pack files".
 
 Named pointers called refs mark interesting points in history.  A ref
-may contain the SHA-1 name of an object or the name of another ref.  Refs
-with names beginning `ref/head/` contain the SHA-1 name of the most
+may contain the SHA-1 name of an object or the name of another ref (the
+latter is called a "symbolic ref").
+Refs with names beginning `refs/head/` contain the SHA-1 name of the most
 recent commit (or "head") of a branch under development.  SHA-1 names of
-tags of interest are stored under `ref/tags/`.  A special ref named
+tags of interest are stored under `refs/tags/`.  A symbolic ref named
 `HEAD` contains the name of the currently checked-out branch.
 
 The index file is initialized with a list of all paths and, for each
@@ -1071,7 +1076,7 @@ Authors
 -------
 Git was started by Linus Torvalds, and is currently maintained by Junio
 C Hamano. Numerous contributions have come from the Git mailing list
-<git@vger.kernel.org>.  http://www.openhub.net/p/git/contributors/summary
+<git@vger.kernel.org>.  https://openhub.net/p/git/contributors/summary
 gives you a more complete list of contributors.
 
 If you have a clone of git.git itself, the
index 8c1793c14880439da7103bc07f0f89a11137fc86..201bdf5edbd1badb08e73d32062b1c61ce38a4c3 100644 (file)
@@ -100,6 +100,21 @@ for a path to `Unspecified` state.  This can be done by listing
 the name of the attribute prefixed with an exclamation point `!`.
 
 
+RESERVED BUILTIN_* ATTRIBUTES
+-----------------------------
+
+builtin_* is a reserved namespace for builtin attribute values. Any
+user defined attributes under this namespace will be ignored and
+trigger a warning.
+
+`builtin_objectmode`
+~~~~~~~~~~~~~~~~~~~~
+This attribute is for filtering files by their file bit modes (40000,
+120000, 160000, 100755, 100644). e.g. ':(attr:builtin_objectmode=160000)'.
+You may also check these values with `git check-attr builtin_objectmode -- <file>`.
+If the object is not in the index `git check-attr --cached` will return unspecified.
+
+
 EFFECTS
 -------
 
index c0b95256cc8c8d640d284f77b23d63c8785601d1..2122aeb976915750b843b89d49210e7c934cf2c8 100644 (file)
@@ -1089,7 +1089,7 @@ the remote repository URL in the local repository's config file
 like this:
 
 ------------------------------------------------
-$ git config remote.linus.url http://www.kernel.org/pub/scm/git/git.git/
+$ git config remote.linus.url https://git.kernel.org/pub/scm/git/git.git/
 ------------------------------------------------
 
 and use the "linus" keyword with 'git pull' instead of the full URL.
index 9fcb29a9c844cc2c2efb1cb1767bd8f146270352..d6ae229be5685950da9a8cff4bbe215c62e0c17c 100644 (file)
@@ -396,6 +396,15 @@ CHUNK DATA:
            is padded at the end with between 0 and 3 NUL bytes to make the
            chunk size a multiple of 4 bytes.
 
+       Bitmapped Packfiles (ID: {'B', 'T', 'M', 'P'})
+           Stores a table of two 4-byte unsigned integers in network order.
+           Each table entry corresponds to a single pack (in the order that
+           they appear above in the `PNAM` chunk). The values for each table
+           entry are as follows:
+           - The first bit position (in pseudo-pack order, see below) to
+             contain an object from that pack.
+           - The number of bits whose objects are selected from that pack.
+
        OID Fanout (ID: {'O', 'I', 'D', 'F'})
            The ith entry, F[i], stores the number of OIDs with first
            byte at most i. Thus F[255] stores the total
@@ -509,6 +518,73 @@ packs arranged in MIDX order (with the preferred pack coming first).
 The MIDX's reverse index is stored in the optional 'RIDX' chunk within
 the MIDX itself.
 
+=== `BTMP` chunk
+
+The Bitmapped Packfiles (`BTMP`) chunk encodes additional information
+about the objects in the multi-pack index's reachability bitmap. Recall
+that objects from the MIDX are arranged in "pseudo-pack" order (see
+above) for reachability bitmaps.
+
+From the example above, suppose we have packs "a", "b", and "c", with
+10, 15, and 20 objects, respectively. In pseudo-pack order, those would
+be arranged as follows:
+
+    |a,0|a,1|...|a,9|b,0|b,1|...|b,14|c,0|c,1|...|c,19|
+
+When working with single-pack bitmaps (or, equivalently, multi-pack
+reachability bitmaps with a preferred pack), linkgit:git-pack-objects[1]
+performs ``verbatim'' reuse, attempting to reuse chunks of the bitmapped
+or preferred packfile instead of adding objects to the packing list.
+
+When a chunk of bytes is reused from an existing pack, any objects
+contained therein do not need to be added to the packing list, saving
+memory and CPU time. But a chunk from an existing packfile can only be
+reused when the following conditions are met:
+
+  - The chunk contains only objects which were requested by the caller
+    (i.e. does not contain any objects which the caller didn't ask for
+    explicitly or implicitly).
+
+  - All objects stored in non-thin packs as offset- or reference-deltas
+    also include their base object in the resulting pack.
+
+The `BTMP` chunk encodes the necessary information in order to implement
+multi-pack reuse over a set of packfiles as described above.
+Specifically, the `BTMP` chunk encodes three pieces of information (all
+32-bit unsigned integers in network byte-order) for each packfile `p`
+that is stored in the MIDX, as follows:
+
+`bitmap_pos`:: The first bit position (in pseudo-pack order) in the
+  multi-pack index's reachability bitmap occupied by an object from `p`.
+
+`bitmap_nr`:: The number of bit positions (including the one at
+  `bitmap_pos`) that encode objects from that pack `p`.
+
+For example, the `BTMP` chunk corresponding to the above example (with
+packs ``a'', ``b'', and ``c'') would look like:
+
+[cols="1,2,2"]
+|===
+| |`bitmap_pos` |`bitmap_nr`
+
+|packfile ``a''
+|`0`
+|`10`
+
+|packfile ``b''
+|`10`
+|`15`
+
+|packfile ``c''
+|`25`
+|`20`
+|===
+
+With this information in place, we can treat each packfile as
+individually reusable in the same fashion as verbatim pack reuse is
+performed on individual packs prior to the implementation of the `BTMP`
+chunk.
+
 == cruft packs
 
 The cruft packs feature offer an alternative to Git's traditional mechanism of
index 21b73b7a1f5bd0bee369cb56070ea6b684390446..836b3490ccda97f6fb8356b25244365198a8f681 100644 (file)
@@ -529,8 +529,8 @@ TODO: Document this further.
 REFERENCES
 ----------
 
-http://www.ietf.org/rfc/rfc1738.txt[RFC 1738: Uniform Resource Locators (URL)]
-http://www.ietf.org/rfc/rfc2616.txt[RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1]
+https://www.ietf.org/rfc/rfc1738.txt[RFC 1738: Uniform Resource Locators (URL)]
+https://www.ietf.org/rfc/rfc2616.txt[RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1]
 
 SEE ALSO
 --------
index 1a2ef4c15055a20eaa65cbbf0f40525ba7e2ba48..949cd8a31e9a9e896ccec63d5c7e2f23f740973a 100644 (file)
@@ -23,7 +23,9 @@ A Git repository comes in two different flavours:
 
 *Note*: Also you can have a plain text file `.git` at the root of
 your working tree, containing `gitdir: <path>` to point at the real
-directory that has the repository.  This mechanism is often used for
+directory that has the repository.
+This mechanism is called a 'gitfile' and is usually managed via the
+`git submodule` and `git worktree` commands. It is often used for
 a working tree of a submodule checkout, to allow you in the
 containing superproject to `git checkout` a branch that does not
 have the submodule.  The `checkout` has to remove the entire
index b078fef6f5c6ff09c9db52ee8a3d3945144b6fc1..59fc1d27419f534edd42968c41ec83f00ff3647e 100644 (file)
@@ -242,7 +242,7 @@ $mimetypes_file::
 
 $highlight_bin::
        Path to the highlight executable to use (it must be the one from
-       http://www.andre-simon.de[] due to assumptions about parameters and output).
+       http://andre-simon.de/zip/download.php[] due to assumptions about parameters and output).
        By default set to 'highlight'; set it to full path to highlight
        executable if it is not installed on your web server's PATH.
        Note that 'highlight' feature must be set for gitweb to actually
@@ -820,7 +820,7 @@ filesystem (i.e. "$projectroot/$project"), `%h` to the current hash
 (\'h' gitweb parameter) and `%b` to the current hash base
 (\'hb' gitweb parameter); `%%` expands to \'%'.
 +
-For example, at the time this page was written, the http://repo.or.cz[]
+For example, at the time this page was written, the https://repo.or.cz[]
 Git hosting site set it to the following to enable graphical log
 (using the third party tool *git-browser*):
 +
index 1030e9667ea8c2a0089cef0a725f57581093aa19..ddd4a0fc70571b83ab6c00bbfc4fd94cabfd4464 100644 (file)
@@ -28,7 +28,7 @@ Gitweb provides a web interface to Git repositories.  Its features include:
   revisions one at a time, viewing the history of the repository.
 * Finding commits whose commit messages match a given search term.
 
-See http://repo.or.cz/w/git.git/tree/HEAD:/gitweb/[] for gitweb source code,
+See https://repo.or.cz/w/git.git/tree/HEAD:/gitweb/[] for gitweb source code,
 browsed using gitweb itself.
 
 
index 59d8ab85721b93daaaa597eab45ab42cc0a9e6ff..d71b199955e15578c1392448cc5db0ce01240766 100644 (file)
@@ -202,6 +202,8 @@ current branch integrates with) obviously do not work, as there is no
 [[def_gitfile]]gitfile::
        A plain file `.git` at the root of a working tree that
        points at the directory that is the real repository.
+       For proper use see linkgit:git-worktree[1] or linkgit:git-submodule[1].
+       For syntax see linkgit:gitrepository-layout[5].
 
 [[def_grafts]]grafts::
        Grafts enable two otherwise different lines of development to be joined
@@ -312,6 +314,12 @@ This commit is referred to as a "merge commit", or sometimes just a
 [[def_octopus]]octopus::
        To <<def_merge,merge>> more than two <<def_branch,branches>>.
 
+[[def_orphan]]orphan::
+       The act of getting on a <<def_branch,branch>> that does not
+       exist yet (i.e., an <<def_unborn,unborn>> branch).  After
+       such an operation, the commit first created becomes a commit
+       without a parent, starting a new history.
+
 [[def_origin]]origin::
        The default upstream <<def_repository,repository>>. Most projects have
        at least one upstream project which they track. By default
@@ -630,6 +638,20 @@ The most notable example is `HEAD`.
        An <<def_object,object>> used to temporarily store the contents of a
        <<def_dirty,dirty>> working directory and the index for future reuse.
 
+[[def_special_ref]]special ref::
+       A ref that has different semantics than normal refs. These refs can be
+       accessed via normal Git commands but may not behave the same as a
+       normal ref in some cases.
++
+The following special refs are known to Git:
+
+ - "`FETCH_HEAD`" is written by linkgit:git-fetch[1] or linkgit:git-pull[1]. It
+   may refer to multiple object IDs. Each object ID is annotated with metadata
+   indicating where it was fetched from and its fetch status.
+
+ - "`MERGE_HEAD`" is written by linkgit:git-merge[1] when resolving merge
+   conflicts. It contains all commit IDs which are being merged.
+
 [[def_submodule]]submodule::
        A <<def_repository,repository>> that holds the history of a
        separate project inside another repository (the latter of
@@ -695,6 +717,18 @@ The most notable example is `HEAD`.
        object,
        etc.
 
+[[def_unborn]]unborn::
+       The <<def_HEAD,HEAD>> can point at a <<def_branch,branch>>
+       that does not yet exist and that does not have any commit on
+       it yet, and such a branch is called an unborn branch.  The
+       most typical way users encounter an unborn branch is by
+       creating a repository anew without cloning from elsewhere.
+       The HEAD would point at the 'main' (or 'master', depending
+       on your configuration) branch that is yet to be born.  Also
+       some operations can get you on an unborn branch with their
+       <<def_orphan,orphan>> option.
+
+
 [[def_unmerged_index]]unmerged index::
        An <<def_index,index>> which contains unmerged
        <<def_index_entry,index entries>>.
index 35d48ef714e9b2cddbbff68b63a14901844ded90..5f800fd85a3dbd4218bb0aaa2bd4de08a3c71434 100644 (file)
@@ -213,4 +213,4 @@ The procedure will result in a history that looks like this:
                 B0--B1---------B2
 ------------
 
-See also http://git-blame.blogspot.com/2013/09/fun-with-first-parent-history.html
+See also https://git-blame.blogspot.com/2013/09/fun-with-first-parent-history.html
index d8f7cd7ca017f4a257129e19ea7ce407aad5c3e8..3eaefc4e940518713e7d140badcd3734ce9abbf0 100644 (file)
@@ -191,7 +191,7 @@ endif::git-pull[]
 --autostash::
 --no-autostash::
        Automatically create a temporary stash entry before the operation
-       begins, record it in the special ref `MERGE_AUTOSTASH`
+       begins, record it in the ref `MERGE_AUTOSTASH`
        and apply it after the operation ends.  This means
        that you can run the operation on a dirty worktree.  However, use
        with care: the final stash application after a successful
diff --git a/Documentation/ref-storage-format.txt b/Documentation/ref-storage-format.txt
new file mode 100644 (file)
index 0000000..1a65cac
--- /dev/null
@@ -0,0 +1 @@
+* `files` for loose files with packed-refs. This is the default.
index 2bf239ff0309aa0e45436c06788f49c094bfac8f..a583b52c612aece1f7d1fd24086c0f7961eecceb 100644 (file)
@@ -947,10 +947,10 @@ ifdef::git-rev-list[]
 +
 The form '--filter=blob:none' omits all blobs.
 +
-The form '--filter=blob:limit=<n>[kmg]' omits blobs larger than n bytes
-or units.  n may be zero.  The suffixes k, m, and g can be used to name
-units in KiB, MiB, or GiB.  For example, 'blob:limit=1k' is the same
-as 'blob:limit=1024'.
+The form '--filter=blob:limit=<n>[kmg]' omits blobs of size at least n
+bytes or units.  n may be zero.  The suffixes k, m, and g can be used
+to name units in KiB, MiB, or GiB.  For example, 'blob:limit=1k'
+is the same as 'blob:limit=1024'.
 +
 The form '--filter=object:type=(tag|commit|tree|blob)' omits all objects
 which are not of the requested type.
index 12aa2333e46b02aaf86106705bac722ceeec09f8..d98758f3cb70d7298cb5bd5a18c8fa23bfe2cd6e 100644 (file)
@@ -9,7 +9,7 @@ endif::git-commit[]
        the committer has the rights to submit the work under the
        project's license or agrees to some contributor representation,
        such as a Developer Certificate of Origin.
-       (See http://developercertificate.org for the one used by the
+       (See https://developercertificate.org for the one used by the
        Linux kernel and Git projects.)  Consult the documentation or
        leadership of the project to which you're contributing to
        understand how the signoffs are used in that project.
index 045a76756fcf47401dc61cb55f37df1deff480d7..27be3741e6040ed9f9915f504793b4ad978d40ad 100644 (file)
@@ -100,3 +100,8 @@ If set, by default "git config" reads from both "config" and
 multiple working directory mode, "config" file is shared while
 "config.worktree" is per-working directory (i.e., it's in
 GIT_COMMON_DIR/worktrees/<id>/config.worktree)
+
+==== `refStorage`
+
+Specifies the file format for the ref database. The only valid value
+is `files` (loose references with a packed-refs file).
diff --git a/Documentation/technical/unit-tests.txt b/Documentation/technical/unit-tests.txt
new file mode 100644 (file)
index 0000000..206037f
--- /dev/null
@@ -0,0 +1,240 @@
+= Unit Testing
+
+In our current testing environment, we spend a significant amount of effort
+crafting end-to-end tests for error conditions that could easily be captured by
+unit tests (or we simply forgo some hard-to-setup and rare error conditions).
+Unit tests additionally provide stability to the codebase and can simplify
+debugging through isolation. Writing unit tests in pure C, rather than with our
+current shell/test-tool helper setup, simplifies test setup, simplifies passing
+data around (no shell-isms required), and reduces testing runtime by not
+spawning a separate process for every test invocation.
+
+We believe that a large body of unit tests, living alongside the existing test
+suite, will improve code quality for the Git project.
+
+== Definitions
+
+For the purposes of this document, we'll use *test framework* to refer to
+projects that support writing test cases and running tests within the context
+of a single executable. *Test harness* will refer to projects that manage
+running multiple executables (each of which may contain multiple test cases) and
+aggregating their results.
+
+In reality, these terms are not strictly defined, and many of the projects
+discussed below contain features from both categories.
+
+For now, we will evaluate projects solely on their framework features. Since we
+are relying on having TAP output (see below), we can assume that any framework
+can be made to work with a harness that we can choose later.
+
+
+== Summary
+
+We believe the best way forward is to implement a custom TAP framework for the
+Git project. We use a version of the framework originally proposed in
+https://lore.kernel.org/git/c902a166-98ce-afba-93f2-ea6027557176@gmail.com/[1].
+
+See the <<framework-selection,Framework Selection>> section below for the
+rationale behind this decision.
+
+
+== Choosing a test harness
+
+During upstream discussion, it was occasionally noted that `prove` provides many
+convenient features, such as scheduling slower tests first, or re-running
+previously failed tests.
+
+While we already support the use of `prove` as a test harness for the shell
+tests, it is not strictly required. The t/Makefile allows running shell tests
+directly (though with interleaved output if parallelism is enabled). Git
+developers who wish to use `prove` as a more advanced harness can do so by
+setting DEFAULT_TEST_TARGET=prove in their config.mak.
+
+We will follow a similar approach for unit tests: by default the test
+executables will be run directly from the t/Makefile, but `prove` can be
+configured with DEFAULT_UNIT_TEST_TARGET=prove.
+
+
+[[framework-selection]]
+== Framework selection
+
+There are a variety of features we can use to rank the candidate frameworks, and
+those features have different priorities:
+
+* Critical features: we probably won't consider a framework without these
+** Can we legally / easily use the project?
+*** <<license,License>>
+*** <<vendorable-or-ubiquitous,Vendorable or ubiquitous>>
+*** <<maintainable-extensible,Maintainable / extensible>>
+*** <<major-platform-support,Major platform support>>
+** Does the project support our bare-minimum needs?
+*** <<tap-support,TAP support>>
+*** <<diagnostic-output,Diagnostic output>>
+*** <<runtime-skippable-tests,Runtime-skippable tests>>
+* Nice-to-have features:
+** <<parallel-execution,Parallel execution>>
+** <<mock-support,Mock support>>
+** <<signal-error-handling,Signal & error-handling>>
+* Tie-breaker stats
+** <<project-kloc,Project KLOC>>
+** <<adoption,Adoption>>
+
+[[license]]
+=== License
+
+We must be able to legally use the framework in connection with Git. As Git is
+licensed only under GPLv2, we must eliminate any LGPLv3, GPLv3, or Apache 2.0
+projects.
+
+[[vendorable-or-ubiquitous]]
+=== Vendorable or ubiquitous
+
+We want to avoid forcing Git developers to install new tools just to run unit
+tests. Any prospective frameworks and harnesses must either be vendorable
+(meaning, we can copy their source directly into Git's repository), or so
+ubiquitous that it is reasonable to expect that most developers will have the
+tools installed already.
+
+[[maintainable-extensible]]
+=== Maintainable / extensible
+
+It is unlikely that any pre-existing project perfectly fits our needs, so any
+project we select will need to be actively maintained and open to accepting
+changes. Alternatively, assuming we are vendoring the source into our repo, it
+must be simple enough that Git developers can feel comfortable making changes as
+needed to our version.
+
+In the comparison table below, "True" means that the framework seems to have
+active developers, that it is simple enough that Git developers can make changes
+to it, and that the project seems open to accepting external contributions (or
+that it is vendorable). "Partial" means that at least one of the above
+conditions holds.
+
+[[major-platform-support]]
+=== Major platform support
+
+At a bare minimum, unit-testing must work on Linux, MacOS, and Windows.
+
+In the comparison table below, "True" means that it works on all three major
+platforms with no issues. "Partial" means that there may be annoyances on one or
+more platforms, but it is still usable in principle.
+
+[[tap-support]]
+=== TAP support
+
+The https://testanything.org/[Test Anything Protocol] is a text-based interface
+that allows tests to communicate with a test harness. It is already used by
+Git's integration test suite. Supporting TAP output is a mandatory feature for
+any prospective test framework.
+
+In the comparison table below, "True" means this is natively supported.
+"Partial" means TAP output must be generated by post-processing the native
+output.
+
+Frameworks that do not have at least Partial support will not be evaluated
+further.
+
+[[diagnostic-output]]
+=== Diagnostic output
+
+When a test case fails, the framework must generate enough diagnostic output to
+help developers find the appropriate test case in source code in order to debug
+the failure.
+
+[[runtime-skippable-tests]]
+=== Runtime-skippable tests
+
+Test authors may wish to skip certain test cases based on runtime circumstances,
+so the framework should support this.
+
+[[parallel-execution]]
+=== Parallel execution
+
+Ideally, we will build up a significant collection of unit test cases, most
+likely split across multiple executables. It will be necessary to run these
+tests in parallel to enable fast develop-test-debug cycles.
+
+In the comparison table below, "True" means that individual test cases within a
+single test executable can be run in parallel. We assume that executable-level
+parallelism can be handled by the test harness.
+
+[[mock-support]]
+=== Mock support
+
+Unit test authors may wish to test code that interacts with objects that may be
+inconvenient to handle in a test (e.g. interacting with a network service).
+Mocking allows test authors to provide a fake implementation of these objects
+for more convenient tests.
+
+[[signal-error-handling]]
+=== Signal & error handling
+
+The test framework should fail gracefully when test cases are themselves buggy
+or when they are interrupted by signals during runtime.
+
+[[project-kloc]]
+=== Project KLOC
+
+The size of the project, in thousands of lines of code as measured by
+https://dwheeler.com/sloccount/[sloccount] (rounded up to the next multiple of
+1,000). As a tie-breaker, we probably prefer a project with fewer LOC.
+
+[[adoption]]
+=== Adoption
+
+As a tie-breaker, we prefer a more widely-used project. We use the number of
+GitHub / GitLab stars to estimate this.
+
+
+=== Comparison
+
+:true: [lime-background]#True#
+:false: [red-background]#False#
+:partial: [yellow-background]#Partial#
+
+:gpl: [lime-background]#GPL v2#
+:isc: [lime-background]#ISC#
+:mit: [lime-background]#MIT#
+:expat: [lime-background]#Expat#
+:lgpl: [lime-background]#LGPL v2.1#
+
+:custom-impl: https://lore.kernel.org/git/c902a166-98ce-afba-93f2-ea6027557176@gmail.com/[Custom Git impl.]
+:greatest: https://github.com/silentbicycle/greatest[Greatest]
+:criterion: https://github.com/Snaipe/Criterion[Criterion]
+:c-tap: https://github.com/rra/c-tap-harness/[C TAP]
+:check: https://libcheck.github.io/check/[Check]
+
+[format="csv",options="header",width="33%",subs="specialcharacters,attributes,quotes,macros"]
+|=====
+Framework,"<<license,License>>","<<vendorable-or-ubiquitous,Vendorable or ubiquitous>>","<<maintainable-extensible,Maintainable / extensible>>","<<major-platform-support,Major platform support>>","<<tap-support,TAP support>>","<<diagnostic-output,Diagnostic output>>","<<runtime--skippable-tests,Runtime- skippable tests>>","<<parallel-execution,Parallel execution>>","<<mock-support,Mock support>>","<<signal-error-handling,Signal & error handling>>","<<project-kloc,Project KLOC>>","<<adoption,Adoption>>"
+{custom-impl},{gpl},{true},{true},{true},{true},{true},{true},{false},{false},{false},1,0
+{greatest},{isc},{true},{partial},{true},{partial},{true},{true},{false},{false},{false},3,1400
+{criterion},{mit},{false},{partial},{true},{true},{true},{true},{true},{false},{true},19,1800
+{c-tap},{expat},{true},{partial},{partial},{true},{false},{true},{false},{false},{false},4,33
+{check},{lgpl},{false},{partial},{true},{true},{true},{false},{false},{false},{true},17,973
+|=====
+
+=== Additional framework candidates
+
+Several suggested frameworks have been eliminated from consideration:
+
+* Incompatible licenses:
+** https://github.com/zorgnax/libtap[libtap] (LGPL v3)
+** https://cmocka.org/[cmocka] (Apache 2.0)
+* Missing source: https://www.kindahl.net/mytap/doc/index.html[MyTap]
+* No TAP support:
+** https://nemequ.github.io/munit/[µnit]
+** https://github.com/google/cmockery[cmockery]
+** https://github.com/lpabon/cmockery2[cmockery2]
+** https://github.com/ThrowTheSwitch/Unity[Unity]
+** https://github.com/siu/minunit[minunit]
+** https://cunit.sourceforge.net/[CUnit]
+
+
+== Milestones
+
+* Add useful tests of library-like code
+* Integrate with
+  https://lore.kernel.org/git/20230502211454.1673000-1-calvinwan@google.com/[stdlib
+  work]
+* Run alongside regular `make test` target
index d8dbe6b56d42a693461d7deae9ac76e214f3d1e4..5d32ff23844108f5a388ecd35f7a38d5286310eb 100644 (file)
@@ -1344,7 +1344,7 @@ $ git diff --theirs file.txt      # same as the above.
 -------------------------------------------------
 
 When using the 'ort' merge strategy (the default), before updating the working
-tree with the result of the merge, Git writes a special ref named AUTO_MERGE
+tree with the result of the merge, Git writes a ref named AUTO_MERGE
 reflecting the state of the tree it is about to write. Conflicted paths with
 textual conflicts that could not be automatically merged are written to this
 tree with conflict markers, just as in the working tree. AUTO_MERGE can thus be
index bc3e656eb8afea2a8c3085eb5365c2a363bd69d9..0901e1bb235c0419a628819c4f6ec8158d121899 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.43.0
+DEF_VER=v2.43.GIT
 
 LF='
 '
@@ -11,7 +11,7 @@ LF='
 if test -f version
 then
        VN=$(cat version) || VN="$DEF_VER"
-elif test -d ${GIT_DIR:-.git} -o -f .git &&
+elif { test -d "${GIT_DIR:-.git}" || test -f .git; } &&
        VN=$(git describe --match "v[0-9]*" HEAD 2>/dev/null) &&
        case "$VN" in
        *$LF*) (exit 1) ;;
diff --git a/INSTALL b/INSTALL
index 4b422888828d0e99b7398f9e55055525330cf597..c6fb240c91eb9044f1baea43ae29c2991447bbc6 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -119,12 +119,12 @@ Issues of note:
        - A POSIX-compliant shell is required to run some scripts needed
          for everyday use (e.g. "bisect", "request-pull").
 
-       - "Perl" version 5.8 or later is needed to use some of the
+       - "Perl" version 5.8.1 or later is needed to use some of the
          features (e.g. sending patches using "git send-email",
          interacting with svn repositories with "git svn").  If you can
          live without these, use NO_PERL.  Note that recent releases of
          Redhat/Fedora are reported to ship Perl binary package with some
-         core modules stripped away (see http://lwn.net/Articles/477234/),
+         core modules stripped away (see https://lwn.net/Articles/477234/),
          so you might need to install additional packages other than Perl
          itself, e.g. Digest::MD5, File::Spec, File::Temp, Net::Domain,
          Net::SMTP, and Time::HiRes.
index 03adcb5a48072e2c7edd006ca9b249812f9a39c5..0f748a52e6b5b500a00ced3b9d9e6617ddd8293c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -186,7 +186,7 @@ include shared.mak
 # Define NO_DEFLATE_BOUND if your zlib does not have deflateBound.
 #
 # Define NO_NORETURN if using buggy versions of gcc 4.6+ and profile feedback,
-# as the compiler can crash (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
+# as the compiler can crash (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
 #
 # Define USE_NSEC below if you want git to care about sub-second file mtimes
 # and ctimes. Note that you need recent glibc (at least 2.2.4) for this. On
@@ -682,6 +682,9 @@ TEST_BUILTINS_OBJS =
 TEST_OBJS =
 TEST_PROGRAMS_NEED_X =
 THIRD_PARTY_SOURCES =
+UNIT_TEST_PROGRAMS =
+UNIT_TEST_DIR = t/unit-tests
+UNIT_TEST_BIN = $(UNIT_TEST_DIR)/bin
 
 # Having this variable in your environment would break pipelines because
 # you cause "cd" to echo its destination to stdout.  It can also take
@@ -749,7 +752,12 @@ SCRIPTS = $(SCRIPT_SH_GEN) \
 
 ETAGS_TARGET = TAGS
 
+# If you add a new fuzzer, please also make sure to run it in
+# ci/run-build-and-minimal-fuzzers.sh so that we make sure it still links and
+# runs in the future.
+FUZZ_OBJS += oss-fuzz/dummy-cmd-main.o
 FUZZ_OBJS += oss-fuzz/fuzz-commit-graph.o
+FUZZ_OBJS += oss-fuzz/fuzz-date.o
 FUZZ_OBJS += oss-fuzz/fuzz-pack-headers.o
 FUZZ_OBJS += oss-fuzz/fuzz-pack-idx.o
 .PHONY: fuzz-objs
@@ -758,7 +766,7 @@ fuzz-objs: $(FUZZ_OBJS)
 # Always build fuzz objects even if not testing, to prevent bit-rot.
 all:: $(FUZZ_OBJS)
 
-FUZZ_PROGRAMS += $(patsubst %.o,%,$(FUZZ_OBJS))
+FUZZ_PROGRAMS += $(patsubst %.o,%,$(filter-out %dummy-cmd-main.o,$(FUZZ_OBJS)))
 
 # Empty...
 EXTRA_PROGRAMS =
@@ -788,7 +796,6 @@ TEST_BUILTINS_OBJS += test-chmtime.o
 TEST_BUILTINS_OBJS += test-config.o
 TEST_BUILTINS_OBJS += test-crontab.o
 TEST_BUILTINS_OBJS += test-csprng.o
-TEST_BUILTINS_OBJS += test-ctype.o
 TEST_BUILTINS_OBJS += test-date.o
 TEST_BUILTINS_OBJS += test-delta.o
 TEST_BUILTINS_OBJS += test-dir-iterator.o
@@ -799,7 +806,6 @@ TEST_BUILTINS_OBJS += test-dump-split-index.o
 TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
 TEST_BUILTINS_OBJS += test-env-helper.o
 TEST_BUILTINS_OBJS += test-example-decorate.o
-TEST_BUILTINS_OBJS += test-fast-rebase.o
 TEST_BUILTINS_OBJS += test-find-pack.o
 TEST_BUILTINS_OBJS += test-fsmonitor-client.o
 TEST_BUILTINS_OBJS += test-genrandom.o
@@ -1290,6 +1296,7 @@ BUILTIN_OBJS += builtin/remote-fd.o
 BUILTIN_OBJS += builtin/remote.o
 BUILTIN_OBJS += builtin/repack.o
 BUILTIN_OBJS += builtin/replace.o
+BUILTIN_OBJS += builtin/replay.o
 BUILTIN_OBJS += builtin/rerere.o
 BUILTIN_OBJS += builtin/reset.o
 BUILTIN_OBJS += builtin/rev-list.o
@@ -1335,6 +1342,14 @@ THIRD_PARTY_SOURCES += compat/regex/%
 THIRD_PARTY_SOURCES += sha1collisiondetection/%
 THIRD_PARTY_SOURCES += sha1dc/%
 
+UNIT_TEST_PROGRAMS += t-basic
+UNIT_TEST_PROGRAMS += t-mem-pool
+UNIT_TEST_PROGRAMS += t-strbuf
+UNIT_TEST_PROGRAMS += t-ctype
+UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
+UNIT_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
+UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
+
 # xdiff and reftable libs may in turn depend on what is in libgit.a
 GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(LIB_FILE)
 EXTLIBS =
@@ -2342,7 +2357,7 @@ profile-fast: profile-clean
 
 all:: $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
 ifneq (,$X)
-       $(QUIET_BUILT_IN)$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_COMMANDS_TO_INSTALL) $(OTHER_PROGRAMS))), test -d '$p' -o '$p' -ef '$p$X' || $(RM) '$p';)
+       $(QUIET_BUILT_IN)$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_COMMANDS_TO_INSTALL) $(OTHER_PROGRAMS))), if test ! -d '$p' && test ! '$p' -ef '$p$X'; then $(RM) '$p'; fi;)
 endif
 
 all::
@@ -2676,6 +2691,7 @@ OBJECTS += $(TEST_OBJS)
 OBJECTS += $(XDIFF_OBJS)
 OBJECTS += $(FUZZ_OBJS)
 OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)
+OBJECTS += $(UNIT_TEST_OBJS)
 
 ifndef NO_CURL
        OBJECTS += http.o http-walker.o remote-curl.o
@@ -2723,7 +2739,7 @@ $(OBJECTS): %.o: %.c GIT-CFLAGS $(missing_dep_dirs) $(missing_compdb_dir)
 
 ifdef USE_COMPUTED_HEADER_DEPENDENCIES
 # Take advantage of gcc's on-the-fly dependency generation
-# See <http://gcc.gnu.org/gcc-3.0/features.html>.
+# See <https://gcc.gnu.org/gcc-3.0/features.html>.
 dep_files_present := $(wildcard $(dep_files))
 ifneq ($(dep_files_present),)
 include $(dep_files_present)
@@ -3178,7 +3194,7 @@ endif
 
 test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X))
 
-all:: $(TEST_PROGRAMS) $(test_bindir_programs)
+all:: $(TEST_PROGRAMS) $(test_bindir_programs) $(UNIT_TEST_PROGS)
 
 bin-wrappers/%: wrap-for-bin.sh
        $(call mkdir_p_parent_template)
@@ -3604,12 +3620,12 @@ rpm::
 .PHONY: rpm
 
 ifneq ($(INCLUDE_DLLS_IN_ARTIFACTS),)
-OTHER_PROGRAMS += $(shell echo *.dll t/helper/*.dll)
+OTHER_PROGRAMS += $(shell echo *.dll t/helper/*.dll t/unit-tests/bin/*.dll)
 endif
 
 artifacts-tar:: $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB) $(OTHER_PROGRAMS) \
                GIT-BUILD-OPTIONS $(TEST_PROGRAMS) $(test_bindir_programs) \
-               $(MOFILES)
+               $(UNIT_TEST_PROGS) $(MOFILES)
        $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) \
                SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)'
        test -n "$(ARTIFACTS_DIRECTORY)"
@@ -3671,7 +3687,7 @@ clean: profile-clean coverage-clean cocciclean
        $(RM) headless-git.o
        $(RM) $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(REFTABLE_TEST_LIB)
        $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS)
-       $(RM) $(TEST_PROGRAMS)
+       $(RM) $(TEST_PROGRAMS) $(UNIT_TEST_PROGS)
        $(RM) $(FUZZ_PROGRAMS)
        $(RM) $(SP_OBJ)
        $(RM) $(HCC)
@@ -3838,15 +3854,28 @@ cover_db_html: cover_db
 #
 # make CC=clang CXX=clang++ \
 #      CFLAGS="-fsanitize=fuzzer-no-link,address" \
-#      LIB_FUZZING_ENGINE="-fsanitize=fuzzer" \
+#      LIB_FUZZING_ENGINE="-fsanitize=fuzzer,address" \
 #      fuzz-all
 #
-FUZZ_CXXFLAGS ?= $(CFLAGS)
+FUZZ_CXXFLAGS ?= $(ALL_CFLAGS)
 
 .PHONY: fuzz-all
 
-$(FUZZ_PROGRAMS): all
-       $(QUIET_LINK)$(CXX) $(FUZZ_CXXFLAGS) $(LIB_OBJS) $(BUILTIN_OBJS) \
-               $(XDIFF_OBJS) $(EXTLIBS) git.o $@.o $(LIB_FUZZING_ENGINE) -o $@
+$(FUZZ_PROGRAMS): %: %.o oss-fuzz/dummy-cmd-main.o $(GITLIBS) GIT-LDFLAGS
+       $(QUIET_LINK)$(CXX) $(FUZZ_CXXFLAGS) -o $@ $(ALL_LDFLAGS) \
+               -Wl,--allow-multiple-definition \
+               $(filter %.o,$^) $(filter %.a,$^) $(LIBS) $(LIB_FUZZING_ENGINE)
 
 fuzz-all: $(FUZZ_PROGRAMS)
+
+$(UNIT_TEST_BIN):
+       @mkdir -p $(UNIT_TEST_BIN)
+
+$(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o $(UNIT_TEST_DIR)/test-lib.o $(GITLIBS) GIT-LDFLAGS $(UNIT_TEST_BIN)
+       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
+               $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
+
+.PHONY: build-unit-tests unit-tests
+build-unit-tests: $(UNIT_TEST_PROGS)
+unit-tests: $(UNIT_TEST_PROGS)
+       $(MAKE) -C t/ unit-tests
index 7ce4f05bae8120d9fa258e854a8669f6ea9cb7b1..665ce5f5a83647619fba9157fa9b0141ae8b228b 100644 (file)
--- a/README.md
+++ b/README.md
@@ -39,10 +39,10 @@ Those wishing to help with error message, usage and informational message
 string translations (localization l10) should see [po/README.md][]
 (a `po` file is a Portable Object file that holds the translations).
 
-To subscribe to the list, send an email with just "subscribe git" in
-the body to majordomo@vger.kernel.org (not the Git list). The mailing
+To subscribe to the list, send an email to <git+subscribe@vger.kernel.org>
+(see https://subspace.kernel.org/subscribing.html for details). The mailing
 list archives are available at <https://lore.kernel.org/git/>,
-<http://marc.info/?l=git> and other archival sites.
+<https://marc.info/?l=git> and other archival sites.
 
 Issues which are security relevant should be disclosed privately to
 the Git Security mailing list <git-security@googlegroups.com>.
index 278cea33c4cc4cfaf34427ee340c163755d935df..a55478f9ad4b2cd26617696780296a7b4afc204a 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.43.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.44.0.txt
\ No newline at end of file
index bfe19876cd50c5aca2e465d9334c49e65a1972fc..79eda168ebb7cdb739720c2c0d16a44484522822 100644 (file)
@@ -12,7 +12,6 @@
 #include "strvec.h"
 #include "pathspec.h"
 #include "color.h"
-#include "diff.h"
 #include "compat/terminal.h"
 #include "prompt.h"
 
index 50c79443ba749fc56437806c21a20755c1988714..f6e4c2f302e9e8686ae515446b7bbd9a5827081f 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -40,14 +40,14 @@ static struct {
        [ADVICE_ADD_EMBEDDED_REPO]                      = { "addEmbeddedRepo", 1 },
        [ADVICE_ADD_EMPTY_PATHSPEC]                     = { "addEmptyPathspec", 1 },
        [ADVICE_ADD_IGNORED_FILE]                       = { "addIgnoredFile", 1 },
-       [ADVICE_AM_WORK_DIR]                            = { "amWorkDir", 1 },
        [ADVICE_AMBIGUOUS_FETCH_REFSPEC]                = { "ambiguousFetchRefspec", 1 },
+       [ADVICE_AM_WORK_DIR]                            = { "amWorkDir", 1 },
        [ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME]  = { "checkoutAmbiguousRemoteBranchName", 1 },
        [ADVICE_COMMIT_BEFORE_MERGE]                    = { "commitBeforeMerge", 1 },
        [ADVICE_DETACHED_HEAD]                          = { "detachedHead", 1 },
-       [ADVICE_SUGGEST_DETACHING_HEAD]                 = { "suggestDetachingHead", 1 },
        [ADVICE_DIVERGING]                              = { "diverging", 1 },
        [ADVICE_FETCH_SHOW_FORCED_UPDATES]              = { "fetchShowForcedUpdates", 1 },
+       [ADVICE_FORCE_DELETE_BRANCH]                    = { "forceDeleteBranch", 1 },
        [ADVICE_GRAFT_FILE_DEPRECATED]                  = { "graftFileDeprecated", 1 },
        [ADVICE_IGNORED_HOOK]                           = { "ignoredHook", 1 },
        [ADVICE_IMPLICIT_IDENTITY]                      = { "implicitIdentity", 1 },
@@ -56,15 +56,12 @@ static struct {
        [ADVICE_PUSH_ALREADY_EXISTS]                    = { "pushAlreadyExists", 1 },
        [ADVICE_PUSH_FETCH_FIRST]                       = { "pushFetchFirst", 1 },
        [ADVICE_PUSH_NEEDS_FORCE]                       = { "pushNeedsForce", 1 },
-       [ADVICE_PUSH_REF_NEEDS_UPDATE]                  = { "pushRefNeedsUpdate", 1 },
-
-       /* make this an alias for backward compatibility */
-       [ADVICE_PUSH_UPDATE_REJECTED_ALIAS]             = { "pushNonFastForward", 1 },
-
        [ADVICE_PUSH_NON_FF_CURRENT]                    = { "pushNonFFCurrent", 1 },
        [ADVICE_PUSH_NON_FF_MATCHING]                   = { "pushNonFFMatching", 1 },
+       [ADVICE_PUSH_REF_NEEDS_UPDATE]                  = { "pushRefNeedsUpdate", 1 },
        [ADVICE_PUSH_UNQUALIFIED_REF_NAME]              = { "pushUnqualifiedRefName", 1 },
        [ADVICE_PUSH_UPDATE_REJECTED]                   = { "pushUpdateRejected", 1 },
+       [ADVICE_PUSH_UPDATE_REJECTED_ALIAS]             = { "pushNonFastForward", 1 }, /* backwards compatibility */
        [ADVICE_RESET_NO_REFRESH_WARNING]               = { "resetNoRefresh", 1 },
        [ADVICE_RESOLVE_CONFLICT]                       = { "resolveConflict", 1 },
        [ADVICE_RM_HINTS]                               = { "rmHints", 1 },
@@ -74,8 +71,9 @@ static struct {
        [ADVICE_STATUS_AHEAD_BEHIND_WARNING]            = { "statusAheadBehindWarning", 1 },
        [ADVICE_STATUS_HINTS]                           = { "statusHints", 1 },
        [ADVICE_STATUS_U_OPTION]                        = { "statusUoption", 1 },
-       [ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE] = { "submoduleAlternateErrorStrategyDie", 1 },
        [ADVICE_SUBMODULES_NOT_UPDATED]                 = { "submodulesNotUpdated", 1 },
+       [ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE] = { "submoduleAlternateErrorStrategyDie", 1 },
+       [ADVICE_SUGGEST_DETACHING_HEAD]                 = { "suggestDetachingHead", 1 },
        [ADVICE_UPDATE_SPARSE_PATH]                     = { "updateSparsePath", 1 },
        [ADVICE_WAITING_FOR_EDITOR]                     = { "waitingForEditor", 1 },
        [ADVICE_WORKTREE_ADD_ORPHAN]                    = { "worktreeAddOrphan", 1 },
index 2affbe142616de0d329c9aaaaee9ca355fb73adf..9d4f49ae38bcfe3b0bdf9999cf9d66ee6a95739e 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -10,18 +10,18 @@ struct string_list;
  * Add the new config variable to Documentation/config/advice.txt.
  * Call advise_if_enabled to print your advice.
  */
- enum advice_type {
+enum advice_type {
        ADVICE_ADD_EMBEDDED_REPO,
        ADVICE_ADD_EMPTY_PATHSPEC,
        ADVICE_ADD_IGNORED_FILE,
-       ADVICE_AM_WORK_DIR,
        ADVICE_AMBIGUOUS_FETCH_REFSPEC,
+       ADVICE_AM_WORK_DIR,
        ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME,
        ADVICE_COMMIT_BEFORE_MERGE,
        ADVICE_DETACHED_HEAD,
        ADVICE_DIVERGING,
-       ADVICE_SUGGEST_DETACHING_HEAD,
        ADVICE_FETCH_SHOW_FORCED_UPDATES,
+       ADVICE_FORCE_DELETE_BRANCH,
        ADVICE_GRAFT_FILE_DEPRECATED,
        ADVICE_IGNORED_HOOK,
        ADVICE_IMPLICIT_IDENTITY,
@@ -32,23 +32,24 @@ struct string_list;
        ADVICE_PUSH_NEEDS_FORCE,
        ADVICE_PUSH_NON_FF_CURRENT,
        ADVICE_PUSH_NON_FF_MATCHING,
+       ADVICE_PUSH_REF_NEEDS_UPDATE,
        ADVICE_PUSH_UNQUALIFIED_REF_NAME,
-       ADVICE_PUSH_UPDATE_REJECTED_ALIAS,
        ADVICE_PUSH_UPDATE_REJECTED,
-       ADVICE_PUSH_REF_NEEDS_UPDATE,
+       ADVICE_PUSH_UPDATE_REJECTED_ALIAS,
        ADVICE_RESET_NO_REFRESH_WARNING,
        ADVICE_RESOLVE_CONFLICT,
        ADVICE_RM_HINTS,
        ADVICE_SEQUENCER_IN_USE,
        ADVICE_SET_UPSTREAM_FAILURE,
+       ADVICE_SKIPPED_CHERRY_PICKS,
        ADVICE_STATUS_AHEAD_BEHIND_WARNING,
        ADVICE_STATUS_HINTS,
        ADVICE_STATUS_U_OPTION,
-       ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE,
        ADVICE_SUBMODULES_NOT_UPDATED,
+       ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE,
+       ADVICE_SUGGEST_DETACHING_HEAD,
        ADVICE_UPDATE_SPARSE_PATH,
        ADVICE_WAITING_FOR_EDITOR,
-       ADVICE_SKIPPED_CHERRY_PICKS,
        ADVICE_WORKTREE_ADD_ORPHAN,
 };
 
diff --git a/apply.c b/apply.c
index 3d69fec836d41fbae10988606ac20ce96dad562d..7608e3301ca0727dcfec0a579ed46390afa41ab0 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -12,7 +12,6 @@
 #include "base85.h"
 #include "config.h"
 #include "object-store-ll.h"
-#include "blob.h"
 #include "delta.h"
 #include "diff.h"
 #include "dir.h"
index 07269968399a5e1b1731fb458d48f4188ed25577..f2a0ed77523b457fa17497498d3ffb4499773a1f 100644 (file)
@@ -9,6 +9,7 @@
 #include "tar.h"
 #include "archive.h"
 #include "object-store-ll.h"
+#include "strbuf.h"
 #include "streaming.h"
 #include "run-command.h"
 #include "write-or-die.h"
index 7229e3e454feb080509d1587a974925ff1318f31..fd1d3f816d30d696456cf1915bb40f2d325f3010 100644 (file)
@@ -10,6 +10,7 @@
 #include "streaming.h"
 #include "utf8.h"
 #include "object-store-ll.h"
+#include "strbuf.h"
 #include "userdiff.h"
 #include "write-or-die.h"
 #include "xdiff-interface.h"
index ca11db185b15a73dd31b239d91a609071faa9a2a..a6730bebfa07d65d003b5594c63081e6ef7d3e88 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -5,6 +5,7 @@
 #include "environment.h"
 #include "gettext.h"
 #include "hex.h"
+#include "object-name.h"
 #include "path.h"
 #include "pretty.h"
 #include "setup.h"
@@ -17,7 +18,6 @@
 #include "archive.h"
 #include "parse-options.h"
 #include "unpack-trees.h"
-#include "dir.h"
 #include "quote.h"
 
 static char const * const archive_usage[] = {
@@ -685,6 +685,8 @@ static int parse_archive_args(int argc, const char **argv,
                base = "";
 
        if (list) {
+               if (argc)
+                       die(_("extra command line parameter '%s'"), *argv);
                for (i = 0; i < nr_archivers; i++)
                        if (!is_remote || archivers[i]->flags & ARCHIVER_REMOTE)
                                printf("%s\n", archivers[i]->name);
index 3a4bdfbd078109680dd54143d03ee2567a0cba31..bbe65ba0f900df1367d1d7607300d732a86c5c0c 100644 (file)
--- a/archive.h
+++ b/archive.h
@@ -1,7 +1,6 @@
 #ifndef ARCHIVE_H
 #define ARCHIVE_H
 
-#include "object-name.h"
 #include "pathspec.h"
 #include "string-list.h"
 
diff --git a/attr.c b/attr.c
index e62876dfd3e9beae50d18da63f15285d5974b8ec..679e42258c2b205bfceb9d065295c47c375442d4 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -17,6 +17,7 @@
 #include "utf8.h"
 #include "quote.h"
 #include "read-cache-ll.h"
+#include "refs.h"
 #include "revision.h"
 #include "object-store-ll.h"
 #include "setup.h"
@@ -183,6 +184,15 @@ static void all_attrs_init(struct attr_hashmap *map, struct attr_check *check)
        }
 }
 
+/*
+ * Atribute name cannot begin with "builtin_" which
+ * is a reserved namespace for built in attributes values.
+ */
+static int attr_name_reserved(const char *name)
+{
+       return starts_with(name, "builtin_");
+}
+
 static int attr_name_valid(const char *name, size_t namelen)
 {
        /*
@@ -315,7 +325,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp,
                        cp++;
                        len--;
                }
-               if (!attr_name_valid(cp, len)) {
+               if (!attr_name_valid(cp, len) || attr_name_reserved(cp)) {
                        report_invalid_attr(cp, len, src, lineno);
                        return NULL;
                }
@@ -379,7 +389,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
                name += strlen(ATTRIBUTE_MACRO_PREFIX);
                name += strspn(name, blank);
                namelen = strcspn(name, blank);
-               if (!attr_name_valid(name, namelen)) {
+               if (!attr_name_valid(name, namelen) || attr_name_reserved(name)) {
                        report_invalid_attr(name, namelen, src, lineno);
                        goto fail_return;
                }
@@ -1240,6 +1250,85 @@ static struct object_id *default_attr_source(void)
        return &attr_source;
 }
 
+static const char *interned_mode_string(unsigned int mode)
+{
+       static struct {
+               unsigned int val;
+               char str[7];
+       } mode_string[] = {
+               { .val = 0040000 },
+               { .val = 0100644 },
+               { .val = 0100755 },
+               { .val = 0120000 },
+               { .val = 0160000 },
+       };
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(mode_string); i++) {
+               if (mode_string[i].val != mode)
+                       continue;
+               if (!*mode_string[i].str)
+                       snprintf(mode_string[i].str, sizeof(mode_string[i].str),
+                                "%06o", mode);
+               return mode_string[i].str;
+       }
+       BUG("Unsupported mode 0%o", mode);
+}
+
+static const char *builtin_object_mode_attr(struct index_state *istate, const char *path)
+{
+       unsigned int mode;
+
+       if (direction == GIT_ATTR_CHECKIN) {
+               struct object_id oid;
+               struct stat st;
+               if (lstat(path, &st))
+                       die_errno(_("unable to stat '%s'"), path);
+               mode = canon_mode(st.st_mode);
+               if (S_ISDIR(mode)) {
+                       /*
+                        *`path` is either a directory or it is a submodule,
+                        * in which case it is already indexed as submodule
+                        * or it does not exist in the index yet and we need to
+                        * check if we can resolve to a ref.
+                       */
+                       int pos = index_name_pos(istate, path, strlen(path));
+                       if (pos >= 0) {
+                                if (S_ISGITLINK(istate->cache[pos]->ce_mode))
+                                        mode = istate->cache[pos]->ce_mode;
+                       } else if (resolve_gitlink_ref(path, "HEAD", &oid) == 0) {
+                               mode = S_IFGITLINK;
+                       }
+               }
+       } else {
+               /*
+                * For GIT_ATTR_CHECKOUT and GIT_ATTR_INDEX we only check
+                * for mode in the index.
+                */
+               int pos = index_name_pos(istate, path, strlen(path));
+               if (pos >= 0)
+                       mode = istate->cache[pos]->ce_mode;
+               else
+                       return ATTR__UNSET;
+       }
+
+       return interned_mode_string(mode);
+}
+
+
+static const char *compute_builtin_attr(struct index_state *istate,
+                                         const char *path,
+                                         const struct git_attr *attr) {
+       static const struct git_attr *object_mode_attr;
+
+       if (!object_mode_attr)
+               object_mode_attr = git_attr("builtin_objectmode");
+
+       if (attr == object_mode_attr)
+               return builtin_object_mode_attr(istate, path);
+       return ATTR__UNSET;
+}
+
 void git_check_attr(struct index_state *istate,
                    const char *path,
                    struct attr_check *check)
@@ -1253,7 +1342,7 @@ void git_check_attr(struct index_state *istate,
                unsigned int n = check->items[i].attr->attr_nr;
                const char *value = check->all_attrs[n].value;
                if (value == ATTR__UNKNOWN)
-                       value = ATTR__UNSET;
+                       value = compute_builtin_attr(istate, path, check->all_attrs[n].attr);
                check->items[i].value = value;
        }
 }
index 1be8e0a2711df9d29c1ba903fd4e8901379ea406..f1273c787d9667d81215ba0dcd32ede3319138ea 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -9,7 +9,6 @@
 #include "refs.h"
 #include "list-objects.h"
 #include "quote.h"
-#include "hash-lookup.h"
 #include "run-command.h"
 #include "log-tree.h"
 #include "bisect.h"
@@ -471,7 +470,6 @@ static int read_bisect_refs(void)
 }
 
 static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
-static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
 static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
 static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
 static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
@@ -707,26 +705,10 @@ static enum bisect_error error_if_skipped_commits(struct commit_list *tried,
 
 static int is_expected_rev(const struct object_id *oid)
 {
-       const char *filename = git_path_bisect_expected_rev();
-       struct stat st;
-       struct strbuf str = STRBUF_INIT;
-       FILE *fp;
-       int res = 0;
-
-       if (stat(filename, &st) || !S_ISREG(st.st_mode))
+       struct object_id expected_oid;
+       if (read_ref("BISECT_EXPECTED_REV", &expected_oid))
                return 0;
-
-       fp = fopen_or_warn(filename, "r");
-       if (!fp)
-               return 0;
-
-       if (strbuf_getline_lf(&str, fp) != EOF)
-               res = !strcmp(str.buf, oid_to_hex(oid));
-
-       strbuf_release(&str);
-       fclose(fp);
-
-       return res;
+       return oideq(oid, &expected_oid);
 }
 
 enum bisect_error bisect_checkout(const struct object_id *bisect_rev,
@@ -1185,10 +1167,10 @@ int bisect_clean_state(void)
        struct string_list refs_for_removal = STRING_LIST_INIT_NODUP;
        for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal);
        string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD"));
+       string_list_append(&refs_for_removal, xstrdup("BISECT_EXPECTED_REV"));
        result = delete_refs("bisect: remove", &refs_for_removal, REF_NO_DEREF);
        refs_for_removal.strdup_strings = 1;
        string_list_clear(&refs_for_removal, 0);
-       unlink_or_warn(git_path_bisect_expected_rev());
        unlink_or_warn(git_path_bisect_ancestors_ok());
        unlink_or_warn(git_path_bisect_log());
        unlink_or_warn(git_path_bisect_names());
diff --git a/blame.c b/blame.c
index 141756975bf5a58a1744eda78c1750d9d949272f..1a16d4eb6a59148325190f8152e13410398177ba 100644 (file)
--- a/blame.c
+++ b/blame.c
@@ -3,6 +3,7 @@
 #include "object-store-ll.h"
 #include "cache-tree.h"
 #include "mergesort.h"
+#include "commit.h"
 #include "convert.h"
 #include "diff.h"
 #include "diffcore.h"
@@ -10,6 +11,7 @@
 #include "hex.h"
 #include "path.h"
 #include "read-cache.h"
+#include "revision.h"
 #include "setup.h"
 #include "tag.h"
 #include "trace2.h"
diff --git a/blame.h b/blame.h
index 31ddc85f19e359908710162ad4aa144784dec16b..5b4e47d44c613e31fc4788c12ee9d8dc2a49d571 100644 (file)
--- a/blame.h
+++ b/blame.h
@@ -1,12 +1,9 @@
 #ifndef BLAME_H
 #define BLAME_H
 
-#include "commit.h"
 #include "oidset.h"
 #include "xdiff-interface.h"
-#include "revision.h"
 #include "prio-queue.h"
-#include "diff.h"
 
 #define PICKAXE_BLAME_MOVE             01
 #define PICKAXE_BLAME_COPY             02
diff --git a/blob.c b/blob.c
index 888e28a5594747bd263df0bb8f2179122bccb131..3fb2922b1ae65a552101d625a806e914b99eb339 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -1,6 +1,5 @@
 #include "git-compat-util.h"
 #include "blob.h"
-#include "repository.h"
 #include "alloc.h"
 
 const char *blob_type = "blob";
diff --git a/bloom.c b/bloom.c
index 1474aa19fa524ff1695c2b518259f61b1c2023b7..e529f7605ca14d3fcb779412fc22e1e20d45ac20 100644 (file)
--- a/bloom.c
+++ b/bloom.c
@@ -2,7 +2,6 @@
 #include "bloom.h"
 #include "diff.h"
 #include "diffcore.h"
-#include "revision.h"
 #include "hashmap.h"
 #include "commit-graph.h"
 #include "commit.h"
index 06f7af9dd47260d87158ba672f90d2b5c37e558e..6719a181bd1f03af21b92d8be71a93142ef700e7 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -420,9 +420,9 @@ static void prepare_checked_out_branches(void)
                wt_status_state_free_buffers(&state);
 
                if (wt_status_check_bisect(wt, &state) &&
-                   state.branch) {
+                   state.bisecting_from) {
                        struct strbuf ref = STRBUF_INIT;
-                       strbuf_addf(&ref, "refs/heads/%s", state.branch);
+                       strbuf_addf(&ref, "refs/heads/%s", state.bisecting_from);
                        old = strmap_put(&current_checked_out_branches,
                                         ref.buf,
                                         xstrdup(wt->path));
@@ -817,8 +817,9 @@ void remove_merge_branch_state(struct repository *r)
        unlink(git_path_merge_rr(r));
        unlink(git_path_merge_msg(r));
        unlink(git_path_merge_mode(r));
-       unlink(git_path_auto_merge(r));
-       save_autostash(git_path_merge_autostash(r));
+       refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
+                       NULL, REF_NO_DEREF);
+       save_autostash_ref(r, "MERGE_AUTOSTASH");
 }
 
 void remove_branch_state(struct repository *r, int verbose)
index d560baa6618ac9e021dc7f957b942466ac6e35b3..28280636da8f52deabea4472d52f574ecb3a4945 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -211,6 +211,7 @@ int cmd_remote(int argc, const char **argv, const char *prefix);
 int cmd_remote_ext(int argc, const char **argv, const char *prefix);
 int cmd_remote_fd(int argc, const char **argv, const char *prefix);
 int cmd_repack(int argc, const char **argv, const char *prefix);
+int cmd_replay(int argc, const char **argv, const char *prefix);
 int cmd_rerere(int argc, const char **argv, const char *prefix);
 int cmd_reset(int argc, const char **argv, const char *prefix);
 int cmd_restore(int argc, const char **argv, const char *prefix);
index 5126d2ede3d7614f764c0cb7cab4b694a792074e..ada7719561f0ec324238e613a9788a5f5a2981eb 100644 (file)
 #include "dir.h"
 #include "gettext.h"
 #include "pathspec.h"
-#include "exec-cmd.h"
-#include "cache-tree.h"
 #include "run-command.h"
 #include "parse-options.h"
 #include "path.h"
 #include "preload-index.h"
 #include "diff.h"
-#include "diffcore.h"
 #include "read-cache.h"
 #include "repository.h"
 #include "revision.h"
@@ -424,7 +421,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
         * Check the "pathspec '%s' did not match any files" block
         * below before enabling new magic.
         */
-       parse_pathspec(&pathspec, PATHSPEC_ATTR,
+       parse_pathspec(&pathspec, 0,
                       PATHSPEC_PREFER_FULL |
                       PATHSPEC_SYMLINK_LEADING_PATH,
                       prefix, argv);
@@ -433,7 +430,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                if (pathspec.nr)
                        die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
 
-               parse_pathspec_file(&pathspec, PATHSPEC_ATTR,
+               parse_pathspec_file(&pathspec, 0,
                                    PATHSPEC_PREFER_FULL |
                                    PATHSPEC_SYMLINK_LEADING_PATH,
                                    prefix, pathspec_from_file, pathspec_file_nul);
@@ -504,7 +501,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                               PATHSPEC_LITERAL |
                               PATHSPEC_GLOB |
                               PATHSPEC_ICASE |
-                              PATHSPEC_EXCLUDE);
+                              PATHSPEC_EXCLUDE |
+                              PATHSPEC_ATTR);
 
                for (i = 0; i < pathspec.nr; i++) {
                        const char *path = pathspec.items[i].match;
index 9f084d58bc705c74d6986541fe81161dcd37df8c..d1990d7edcbe37467c59a420a9b03a5894e6da93 100644 (file)
@@ -10,7 +10,6 @@
 #include "config.h"
 #include "editor.h"
 #include "environment.h"
-#include "exec-cmd.h"
 #include "gettext.h"
 #include "hex.h"
 #include "parse-options.h"
@@ -24,7 +23,6 @@
 #include "refs.h"
 #include "commit.h"
 #include "diff.h"
-#include "diffcore.h"
 #include "unpack-trees.h"
 #include "branch.h"
 #include "object-name.h"
 #include "log-tree.h"
 #include "notes-utils.h"
 #include "rerere.h"
-#include "prompt.h"
 #include "mailinfo.h"
 #include "apply.h"
 #include "string-list.h"
-#include "packfile.h"
 #include "pager.h"
 #include "path.h"
 #include "repository.h"
index c18b7ea5d3da4aa66d82c9fa0e911274308ad598..861a01910ca20bcb9bd18279ebd10f9d94032fe1 100644 (file)
@@ -1,6 +1,5 @@
 #include "builtin.h"
 #include "gettext.h"
-#include "parse-options.h"
 #include "repository.h"
 #include "apply.h"
 
index 90761fdfee0f58f0d2d8cca9c7b0bcd6e0ee7fe9..15ee1ec7bb765fe6307dc3f17cde238242169cc4 100644 (file)
@@ -9,7 +9,6 @@
 #include "parse-options.h"
 #include "pkt-line.h"
 #include "repository.h"
-#include "sideband.h"
 
 static void create_output_file(const char *output_file)
 {
index 35938b05fd1c0b51e64a01e58294f267afc4d758..f69c3f7e43df4623be83509b7c9070d69072dc2a 100644 (file)
@@ -7,7 +7,6 @@
 #include "parse-options.h"
 #include "bisect.h"
 #include "refs.h"
-#include "dir.h"
 #include "strvec.h"
 #include "run-command.h"
 #include "oid-array.h"
@@ -17,7 +16,6 @@
 #include "revision.h"
 
 static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
-static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
 static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
 static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
 static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
@@ -233,11 +231,10 @@ static int bisect_reset(const char *commit)
        struct strbuf branch = STRBUF_INIT;
 
        if (!commit) {
-               if (strbuf_read_file(&branch, git_path_bisect_start(), 0) < 1) {
+               if (!strbuf_read_file(&branch, git_path_bisect_start(), 0))
                        printf(_("We are not bisecting.\n"));
-                       return 0;
-               }
-               strbuf_rtrim(&branch);
+               else
+                       strbuf_rtrim(&branch);
        } else {
                struct object_id oid;
 
@@ -246,7 +243,7 @@ static int bisect_reset(const char *commit)
                strbuf_addstr(&branch, commit);
        }
 
-       if (!ref_exists("BISECT_HEAD")) {
+       if (branch.len && !ref_exists("BISECT_HEAD")) {
                struct child_process cmd = CHILD_PROCESS_INIT;
 
                cmd.git_cmd = 1;
@@ -921,7 +918,6 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
        const char *state;
        int i, verify_expected = 1;
        struct object_id oid, expected;
-       struct strbuf buf = STRBUF_INIT;
        struct oid_array revs = OID_ARRAY_INIT;
 
        if (!argc)
@@ -976,10 +972,8 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
                oid_array_append(&revs, &commit->object.oid);
        }
 
-       if (strbuf_read_file(&buf, git_path_bisect_expected_rev(), 0) < the_hash_algo->hexsz ||
-           get_oid_hex(buf.buf, &expected) < 0)
+       if (read_ref("BISECT_EXPECTED_REV", &expected))
                verify_expected = 0; /* Ignore invalid file contents */
-       strbuf_release(&buf);
 
        for (i = 0; i < revs.nr; i++) {
                if (bisect_write(state, oid_to_hex(&revs.oid[i]), terms, 0)) {
@@ -988,7 +982,7 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, int argc,
                }
                if (verify_expected && !oideq(&revs.oid[i], &expected)) {
                        unlink_or_warn(git_path_bisect_ancestors_ok());
-                       unlink_or_warn(git_path_bisect_expected_rev());
+                       delete_ref(NULL, "BISECT_EXPECTED_REV", NULL, REF_NO_DEREF);
                        verify_expected = 0;
                }
        }
index 9c987d656756e8f436e3b1cb818306097e402e4c..db1f56de61a434c3028869a2f4105c319ac6fd33 100644 (file)
@@ -25,7 +25,6 @@
 #include "userdiff.h"
 #include "line-range.h"
 #include "line-log.h"
-#include "dir.h"
 #include "progress.h"
 #include "object-name.h"
 #include "object-store-ll.h"
@@ -748,6 +747,8 @@ static int git_blame_config(const char *var, const char *value,
        }
 
        if (!strcmp(var, "blame.coloring")) {
+               if (!value)
+                       return config_error_nonbool(var);
                if (!strcmp(value, "repeatedLines")) {
                        coloring_mode |= OUTPUT_COLOR_LINE;
                } else if (!strcmp(value, "highlightRecent")) {
index e7ee9bd0f15028532087132cdc6062e8632568c2..cfb63cce5fb9dff64106907947d0df25a2c25489 100644 (file)
 #include "remote.h"
 #include "parse-options.h"
 #include "branch.h"
-#include "diff.h"
 #include "path.h"
-#include "revision.h"
 #include "string-list.h"
 #include "column.h"
 #include "utf8.h"
-#include "wt-status.h"
 #include "ref-filter.h"
 #include "worktree.h"
 #include "help.h"
+#include "advice.h"
 #include "commit-reach.h"
 
 static const char * const builtin_branch_usage[] = {
@@ -45,7 +43,6 @@ 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] = {
@@ -194,9 +191,10 @@ static int check_branch_commit(const char *branchname, const char *refname,
                return -1;
        }
        if (!force && !branch_merged(kinds, branchname, rev, head_rev)) {
-               error(_("the branch '%s' is not fully merged.\n"
-                     "If you are sure you want to delete it, "
-                     "run 'git branch -D %s'"), branchname, branchname);
+               error(_("the branch '%s' is not fully merged"), branchname);
+               advise_if_enabled(ADVICE_FORCE_DELETE_BRANCH,
+                                 _("If you are sure you want to delete it, "
+                                 "run 'git branch -D %s'"), branchname);
                return -1;
        }
        return 0;
@@ -438,8 +436,6 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
 {
        int i;
        struct ref_array array;
-       struct strbuf out = STRBUF_INIT;
-       struct strbuf err = STRBUF_INIT;
        int maxwidth = 0;
        const char *remote_prefix = "";
        char *to_free = NULL;
@@ -469,24 +465,27 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
        filter_ahead_behind(the_repository, format, &array);
        ref_array_sort(sorting, &array);
 
-       for (i = 0; i < array.nr; i++) {
-               strbuf_reset(&err);
-               strbuf_reset(&out);
-               if (format_ref_array_item(array.items[i], format, &out, &err))
-                       die("%s", err.buf);
-               if (column_active(colopts)) {
-                       assert(!filter->verbose && "--column and --verbose are incompatible");
-                        /* format to a string_list to let print_columns() do its job */
+       if (column_active(colopts)) {
+               struct strbuf out = STRBUF_INIT, err = STRBUF_INIT;
+
+               assert(!filter->verbose && "--column and --verbose are incompatible");
+
+               for (i = 0; i < array.nr; i++) {
+                       strbuf_reset(&err);
+                       strbuf_reset(&out);
+                       if (format_ref_array_item(array.items[i], format, &out, &err))
+                               die("%s", err.buf);
+
+                       /* format to a string_list to let print_columns() do its job */
                        string_list_append(output, out.buf);
-               } else {
-                       fwrite(out.buf, 1, out.len, stdout);
-                       if (out.len || !omit_empty)
-                               putchar('\n');
                }
+
+               strbuf_release(&err);
+               strbuf_release(&out);
+       } else {
+               print_formatted_ref_array(&array, format);
        }
 
-       strbuf_release(&err);
-       strbuf_release(&out);
        ref_array_clear(&array);
        free(to_free);
 }
@@ -737,7 +736,7 @@ 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,
+               OPT_BOOL(0, "omit-empty",  &format.array_opts.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),
@@ -767,7 +766,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage_with_options(builtin_branch_usage, options);
 
+       /*
+        * Try to set sort keys from config. If config does not set any,
+        * fall back on default (refname) sorting.
+        */
        git_config(git_branch_config, &sorting_options);
+       if (!sorting_options.nr)
+               string_list_append(&sorting_options, "refname");
 
        track = git_branch_track;
 
index ea8ad601ecc0b7ee2d7eb9c3da2bb70bcfc55cf8..7d4899348a387b1a07377c2d968d7574a74d219d 100644 (file)
@@ -15,7 +15,6 @@
 #include "parse-options.h"
 #include "userdiff.h"
 #include "streaming.h"
-#include "tree-walk.h"
 #include "oid-array.h"
 #include "packfile.h"
 #include "object-file.h"
index 3b68b47615302473cf3e2c5c70f9ebf1c707c3af..2e086a204dc7b19271cdeb2733c62a64066ab304 100644 (file)
@@ -7,7 +7,6 @@
 #define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "config.h"
-#include "dir.h"
 #include "gettext.h"
 #include "lockfile.h"
 #include "quote.h"
index f02434bc155ba1769a248350afa686322e6110bb..a6e30931b5c809da06c751ba6ef43e47a5a3e0ea 100644 (file)
@@ -1,7 +1,6 @@
 #define USE_THE_INDEX_VARIABLE
 #include "builtin.h"
 #include "advice.h"
-#include "blob.h"
 #include "branch.h"
 #include "cache-tree.h"
 #include "checkout.h"
 #include "remote.h"
 #include "resolve-undo.h"
 #include "revision.h"
-#include "run-command.h"
 #include "setup.h"
 #include "submodule.h"
-#include "submodule-config.h"
 #include "symlinks.h"
 #include "trace2.h"
 #include "tree.h"
@@ -1202,6 +1199,8 @@ static int git_checkout_config(const char *var, const char *value,
        struct checkout_opts *opts = cb;
 
        if (!strcmp(var, "diff.ignoresubmodules")) {
+               if (!value)
+                       return config_error_nonbool(var);
                handle_ignore_submodules_arg(&opts->diff_options, value);
                return 0;
        }
@@ -1516,6 +1515,26 @@ static void die_if_some_operation_in_progress(void)
        wt_status_state_free_buffers(&state);
 }
 
+/*
+ * die if attempting to checkout an existing branch that is in use
+ * in another worktree, unless ignore-other-wortrees option is given.
+ * The check is bypassed when the branch is already the current one,
+ * as it will not make things any worse.
+ */
+static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts,
+                                               const char *full_ref)
+{
+       int flags;
+       char *head_ref;
+
+       if (opts->ignore_other_worktrees)
+               return;
+       head_ref = resolve_refdup("HEAD", 0, NULL, &flags);
+       if (head_ref && (!(flags & REF_ISSYMREF) || strcmp(head_ref, full_ref)))
+               die_if_checked_out(full_ref, 1);
+       free(head_ref);
+}
+
 static int checkout_branch(struct checkout_opts *opts,
                           struct branch_info *new_branch_info)
 {
@@ -1576,14 +1595,15 @@ static int checkout_branch(struct checkout_opts *opts,
        if (!opts->can_switch_when_in_progress)
                die_if_some_operation_in_progress();
 
-       if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
-           !opts->ignore_other_worktrees) {
-               int flag;
-               char *head_ref = resolve_refdup("HEAD", 0, NULL, &flag);
-               if (head_ref &&
-                   (!(flag & REF_ISSYMREF) || strcmp(head_ref, new_branch_info->path)))
-                       die_if_checked_out(new_branch_info->path, 1);
-               free(head_ref);
+       /* "git checkout <branch>" */
+       if (new_branch_info->path && !opts->force_detach && !opts->new_branch)
+               die_if_switching_to_a_branch_in_use(opts, new_branch_info->path);
+
+       /* "git checkout -B <branch>" */
+       if (opts->new_branch_force) {
+               char *full_ref = xstrfmt("refs/heads/%s", opts->new_branch);
+               die_if_switching_to_a_branch_in_use(opts, full_ref);
+               free(full_ref);
        }
 
        if (!new_branch_info->commit && opts->new_branch) {
@@ -1627,7 +1647,7 @@ static struct option *add_common_switch_branch_options(
                        parse_opt_tracking_mode),
                OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
                           PARSE_OPT_NOCOMPLETE),
-               OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
+               OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unborn branch")),
                OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore,
                           N_("update ignored files (default)"),
                           PARSE_OPT_NOCOMPLETE),
index 49c224e626d61819a633252572d3e7f3b32ffd62..d90766cad3a0ba13c41dd0b90c03254ba5eb83ee 100644 (file)
@@ -971,7 +971,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
 
        if (ignored && ignored_only)
-               die(_("-x and -X cannot be used together"));
+               die(_("options '%s' and '%s' cannot be used together"), "-x", "-X");
        if (!ignored)
                setup_standard_excludes(&dir);
        if (ignored_only)
index c6357af949895a688639c83984598931906b2690..bad1b70ce8255156cf4745aca5e120371d39642d 100644 (file)
@@ -19,7 +19,6 @@
 #include "hex.h"
 #include "lockfile.h"
 #include "parse-options.h"
-#include "fetch-pack.h"
 #include "refs.h"
 #include "refspec.h"
 #include "object-file.h"
@@ -72,6 +71,7 @@ static char *remote_name = NULL;
 static char *option_branch = NULL;
 static struct string_list option_not = STRING_LIST_INIT_NODUP;
 static const char *real_git_dir;
+static const char *ref_format;
 static char *option_upload_pack = "git-upload-pack";
 static int option_verbosity;
 static int option_progress = -1;
@@ -157,6 +157,8 @@ static struct option builtin_clone_options[] = {
                    N_("any cloned submodules will be shallow")),
        OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
                   N_("separate git dir from working tree")),
+       OPT_STRING(0, "ref-format", &ref_format, N_("format"),
+                  N_("specify the reference format to use")),
        OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
                        N_("set config inside the new repository")),
        OPT_STRING_LIST(0, "server-option", &server_options,
@@ -791,6 +793,8 @@ static int git_clone_config(const char *k, const char *v,
                            const struct config_context *ctx, void *cb)
 {
        if (!strcmp(k, "clone.defaultremotename")) {
+               if (!v)
+                       return config_error_nonbool(k);
                free(remote_name);
                remote_name = xstrdup(v);
        }
@@ -930,6 +934,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        int submodule_progress;
        int filter_submodules = 0;
        int hash_algo;
+       unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
        const int do_not_override_repo_unix_permissions = -1;
 
        struct transport_ls_refs_options transport_ls_refs_options =
@@ -955,6 +960,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (option_single_branch == -1)
                option_single_branch = deepen ? 1 : 0;
 
+       if (ref_format) {
+               ref_storage_format = ref_storage_format_by_name(ref_format);
+               if (ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)
+                       die(_("unknown ref storage format '%s'"), ref_format);
+       }
+
        if (option_mirror)
                option_bare = 1;
 
@@ -965,7 +976,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        }
 
        if (bundle_uri && deepen)
-               die(_("--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-exclude"));
+               die(_("options '%s' and '%s' cannot be used together"),
+                   "--bundle-uri",
+                   "--depth/--shallow-since/--shallow-exclude");
 
        repo_name = argv[0];
 
@@ -1097,8 +1110,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                }
        }
 
-       init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, NULL,
-               do_not_override_repo_unix_permissions, INIT_DB_QUIET);
+       /*
+        * Initialize the repository, but skip initializing the reference
+        * database. We do not yet know about the object format of the
+        * repository, and reference backends may persist that information into
+        * their on-disk data structures.
+        */
+       init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN,
+               ref_storage_format, NULL,
+               do_not_override_repo_unix_permissions, INIT_DB_QUIET | INIT_DB_SKIP_REFDB);
 
        if (real_git_dir) {
                free((char *)git_dir);
@@ -1185,10 +1205,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (option_required_reference.nr || option_optional_reference.nr)
                setup_reference();
 
-       if (option_sparse_checkout && git_sparse_checkout_init(dir))
-               return 1;
-
-       remote = remote_get(remote_name);
+       remote = remote_get_early(remote_name);
 
        refspec_appendf(&remote->fetch, "+%s*:%s*", src_ref_prefix,
                        branch_top.buf);
@@ -1266,6 +1283,27 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (transport->smart_options && !deepen && !filter_options.choice)
                transport->smart_options->check_self_contained_and_connected = 1;
 
+       strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
+       refspec_ref_prefixes(&remote->fetch,
+                            &transport_ls_refs_options.ref_prefixes);
+       if (option_branch)
+               expand_ref_prefix(&transport_ls_refs_options.ref_prefixes,
+                                 option_branch);
+       if (!option_no_tags)
+               strvec_push(&transport_ls_refs_options.ref_prefixes,
+                           "refs/tags/");
+
+       refs = transport_get_remote_refs(transport, &transport_ls_refs_options);
+
+       /*
+        * Now that we know what algorithm the remote side is using, let's set
+        * ours to the same thing.
+        */
+       hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
+       initialize_repository_version(hash_algo, the_repository->ref_storage_format, 1);
+       repo_set_hash_algo(the_repository, hash_algo);
+       create_reference_database(the_repository->ref_storage_format, NULL, 1);
+
        /*
         * Before fetching from the remote, download and install bundle
         * data from the --bundle-uri option.
@@ -1281,24 +1319,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                                bundle_uri);
                else if (has_heuristic)
                        git_config_set_gently("fetch.bundleuri", bundle_uri);
-       }
-
-       strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
-       refspec_ref_prefixes(&remote->fetch,
-                            &transport_ls_refs_options.ref_prefixes);
-       if (option_branch)
-               expand_ref_prefix(&transport_ls_refs_options.ref_prefixes,
-                                 option_branch);
-       if (!option_no_tags)
-               strvec_push(&transport_ls_refs_options.ref_prefixes,
-                           "refs/tags/");
-
-       refs = transport_get_remote_refs(transport, &transport_ls_refs_options);
-
-       if (refs)
-               mapped_refs = wanted_peer_refs(refs, &remote->fetch);
-
-       if (!bundle_uri) {
+       } else {
                /*
                * Populate transport->got_remote_bundle_uri and
                * transport->bundle_uri. We might get nothing.
@@ -1319,13 +1340,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                }
        }
 
-               /*
-                * Now that we know what algorithm the remote side is using,
-                * let's set ours to the same thing.
-                */
-       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 (refs)
+               mapped_refs = wanted_peer_refs(refs, &remote->fetch);
 
        if (mapped_refs) {
                /*
@@ -1428,6 +1444,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                dissociate_from_references();
        }
 
+       if (option_sparse_checkout && git_sparse_checkout_init(dir))
+               return 1;
+
        junk_mode = JUNK_LEAVE_REPO;
        err = checkout(submodule_progress, filter_submodules);
 
index a83be8bc991a8c9dfca5b64f886d33f952890236..e80218f81f94b5fc7a3e4605527556da336bb6ff 100644 (file)
@@ -56,5 +56,7 @@ int cmd_column(int argc, const char **argv, const char *prefix)
                string_list_append(&list, sb.buf);
 
        print_columns(&list, colopts, &copts);
+       strbuf_release(&sb);
+       string_list_clear(&list, 0);
        return 0;
 }
index 45d035af6007a2a64833015dbe58300493a55f80..666ad574a46b1218def7201e581e969f90e57d48 100644 (file)
@@ -1,17 +1,16 @@
 #include "builtin.h"
 #include "commit.h"
 #include "config.h"
-#include "dir.h"
 #include "environment.h"
 #include "gettext.h"
 #include "hex.h"
-#include "lockfile.h"
 #include "parse-options.h"
 #include "repository.h"
 #include "commit-graph.h"
 #include "object-store-ll.h"
 #include "progress.h"
 #include "replace-object.h"
+#include "strbuf.h"
 #include "tag.h"
 #include "trace2.h"
 
index 02625e71761dbf8d25f063b5ff4835e5eb4bf477..1bb78198392e9e13db117e87a4d421e17d10b26a 100644 (file)
@@ -11,9 +11,6 @@
 #include "object-store-ll.h"
 #include "repository.h"
 #include "commit.h"
-#include "tree.h"
-#include "utf8.h"
-#include "gpg-interface.h"
 #include "parse-options.h"
 
 static const char * const commit_tree_usage[] = {
index 781af2e206c1060213f16dd7cacb5ca68884f6b8..6d1fa71676f735b7ef1c3ab7bd56b767348ed992 100644 (file)
 #include "editor.h"
 #include "environment.h"
 #include "diff.h"
-#include "diffcore.h"
 #include "commit.h"
 #include "gettext.h"
 #include "revision.h"
 #include "wt-status.h"
 #include "run-command.h"
-#include "hook.h"
-#include "refs.h"
-#include "log-tree.h"
 #include "strbuf.h"
-#include "utf8.h"
 #include "object-name.h"
 #include "parse-options.h"
 #include "path.h"
@@ -35,9 +30,6 @@
 #include "string-list.h"
 #include "rerere.h"
 #include "unpack-trees.h"
-#include "quote.h"
-#include "submodule.h"
-#include "gpg-interface.h"
 #include "column.h"
 #include "sequencer.h"
 #include "sparse-index.h"
@@ -900,7 +892,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                strbuf_stripspace(&sb, '\0');
 
        if (signoff)
-               append_signoff(&sb, ignore_non_trailer(sb.buf, sb.len), 0);
+               append_signoff(&sb, ignored_log_message_bytes(sb.buf, sb.len), 0);
 
        if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
                die_errno(_("could not write commit template"));
@@ -1885,7 +1877,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                                     &oid, flags);
        }
 
-       apply_autostash(git_path_merge_autostash(the_repository));
+       apply_autostash_ref(the_repository, "MERGE_AUTOSTASH");
 
 cleanup:
        strbuf_release(&author_ident);
index 11a4d4ef1411222f3750c760b68efd10180148c0..08fe36d4997d896b1378d25bd899d32af3679f64 100644 (file)
@@ -708,30 +708,11 @@ int cmd_config(int argc, const char **argv, const char *prefix)
        }
 
        if (use_global_config) {
-               char *user_config, *xdg_config;
-
-               git_global_config(&user_config, &xdg_config);
-               if (!user_config)
-                       /*
-                        * It is unknown if HOME/.gitconfig exists, so
-                        * we do not know if we should write to XDG
-                        * location; error out even if XDG_CONFIG_HOME
-                        * is set and points at a sane location.
-                        */
+               given_config_source.file = git_global_config();
+               if (!given_config_source.file)
                        die(_("$HOME not set"));
-
                given_config_source.scope = CONFIG_SCOPE_GLOBAL;
-
-               if (access_or_warn(user_config, R_OK, 0) &&
-                   xdg_config && !access_or_warn(xdg_config, R_OK, 0)) {
-                       given_config_source.file = xdg_config;
-                       free(user_config);
-               } else {
-                       given_config_source.file = user_config;
-                       free(xdg_config);
-               }
-       }
-       else if (use_system_config) {
+       } else if (use_system_config) {
                given_config_source.file = git_system_config();
                given_config_source.scope = CONFIG_SCOPE_SYSTEM;
        } else if (use_local_config) {
@@ -760,7 +741,6 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                given_config_source.scope = CONFIG_SCOPE_COMMAND;
        }
 
-
        if (respect_includes_opt == -1)
                config_options.respect_includes = !given_config_source.file;
        else
index 43b9d0e5b16ba5da555a3b16ce53b88dff5dbb4c..bba96d4ffd6f198adb186aaba0c853e34a93dd11 100644 (file)
@@ -7,8 +7,6 @@
 
 #ifndef NO_UNIX_SOCKETS
 
-#include "credential.h"
-#include "string-list.h"
 #include "unix-socket.h"
 #include "run-command.h"
 
index fb6b0508f3212db5d41bc33cfe76de441534ca7c..d6c77a714f46194faca18739d0118294bc233f81 100644 (file)
@@ -7,9 +7,7 @@
 #include "lockfile.h"
 #include "commit.h"
 #include "tag.h"
-#include "blob.h"
 #include "refs.h"
-#include "exec-cmd.h"
 #include "object-name.h"
 #include "parse-options.h"
 #include "read-cache-ll.h"
index f38912cd407bf4e5098d84a928affd849f4123d8..018011f29ea26be24b567e9af380699dd7d893a3 100644 (file)
@@ -11,7 +11,6 @@
 #include "preload-index.h"
 #include "repository.h"
 #include "revision.h"
-#include "submodule.h"
 
 static const char diff_files_usage[] =
 "git diff-files [-q] [-0 | -1 | -2 | -3 | -c | --cc] [<common-diff-options>] [<path>...]"
index 220f341ffa2a138f70d6aa0d343594e6e60d2ebb..3e05260ac0e0401188163aeaf4c4d1aea0ba56d1 100644 (file)
@@ -7,8 +7,6 @@
 #include "repository.h"
 #include "revision.h"
 #include "setup.h"
-#include "sparse-index.h"
-#include "submodule.h"
 
 static const char diff_cache_usage[] =
 "git diff-index [-m] [--cached] [--merge-base] "
index 86be6342861be40da9edbf2f1a2002fa4abc0cda..a8e68ce8ef6275a99e2a4f62545d0e7d962dcf45 100644 (file)
@@ -6,7 +6,6 @@
 #include "gettext.h"
 #include "hex.h"
 #include "log-tree.h"
-#include "submodule.h"
 #include "read-cache-ll.h"
 #include "repository.h"
 #include "revision.h"
index 55e7d21755a09c9754d45fea8689cbcbd32939dd..6e196e0c7d2d055df1557ffd256d87a737053324 100644 (file)
@@ -10,7 +10,6 @@
 #include "lockfile.h"
 #include "color.h"
 #include "commit.h"
-#include "blob.h"
 #include "gettext.h"
 #include "tag.h"
 #include "diff.h"
@@ -21,7 +20,6 @@
 #include "revision.h"
 #include "log-tree.h"
 #include "setup.h"
-#include "submodule.h"
 #include "oid-array.h"
 #include "tree.h"
 
index 0f5eae9cd41b3219b06964011089bb8d389975b4..a3c72b8258e7cb34e6e87e679e6a996d6086ecb0 100644 (file)
@@ -18,7 +18,6 @@
 #include "copy.h"
 #include "run-command.h"
 #include "environment.h"
-#include "exec-cmd.h"
 #include "gettext.h"
 #include "hex.h"
 #include "parse-options.h"
index 70aff515acbe97b20db80cc8b746e28f0149b56f..f18f0809f9c7f78874eaddfd869c5fc5d11831e0 100644 (file)
@@ -25,7 +25,6 @@
 #include "quote.h"
 #include "remote.h"
 #include "blob.h"
-#include "commit-slab.h"
 
 static const char *fast_export_usage[] = {
        N_("git fast-export [<rev-list-opts>]"),
index 444f41cf8ca8e6ba523f54169a3534d384094196..92eda20683ca2d07c24a03f5903d1e604ce54763 100644 (file)
@@ -2809,8 +2809,7 @@ static void parse_new_tag(const char *arg)
        enum object_type type;
        const char *v;
 
-       t = mem_pool_alloc(&fi_mem_pool, sizeof(struct tag));
-       memset(t, 0, sizeof(struct tag));
+       t = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct tag));
        t->name = mem_pool_strdup(&fi_mem_pool, arg);
        if (last_tag)
                last_tag->next_tag = t;
index fd134ba74d9086cfa44ba1c1b5a5a2597d182bee..3aedfd1bb6361c6bbfd651970f9e9767d0595734 100644 (file)
@@ -26,7 +26,6 @@
 #include "connected.h"
 #include "strvec.h"
 #include "utf8.h"
-#include "packfile.h"
 #include "pager.h"
 #include "path.h"
 #include "pkt-line.h"
@@ -38,7 +37,6 @@
 #include "shallow.h"
 #include "trace.h"
 #include "trace2.h"
-#include "worktree.h"
 #include "bundle-uri.h"
 
 #define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
@@ -102,6 +100,7 @@ static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP;
 
 struct fetch_config {
        enum display_format display_format;
+       int all;
        int prune;
        int prune_tags;
        int show_forced_updates;
@@ -115,6 +114,11 @@ static int git_fetch_config(const char *k, const char *v,
 {
        struct fetch_config *fetch_config = cb;
 
+       if (!strcmp(k, "fetch.all")) {
+               fetch_config->all = git_config_bool(k, v);
+               return 0;
+       }
+
        if (!strcmp(k, "fetch.prune")) {
                fetch_config->prune = git_config_bool(k, v);
                return 0;
@@ -1651,7 +1655,7 @@ static int do_fetch(struct transport *transport,
        if (atomic_fetch) {
                transaction = ref_transaction_begin(&err);
                if (!transaction) {
-                       retcode = error("%s", err.buf);
+                       retcode = -1;
                        goto cleanup;
                }
        }
@@ -1711,7 +1715,6 @@ static int do_fetch(struct transport *transport,
 
                retcode = ref_transaction_commit(transaction, &err);
                if (retcode) {
-                       error("%s", err.buf);
                        ref_transaction_free(transaction);
                        transaction = NULL;
                        goto cleanup;
@@ -1775,9 +1778,14 @@ static int do_fetch(struct transport *transport,
        }
 
 cleanup:
-       if (retcode && transaction) {
-               ref_transaction_abort(transaction, &err);
-               error("%s", err.buf);
+       if (retcode) {
+               if (err.len) {
+                       error("%s", err.buf);
+                       strbuf_reset(&err);
+               }
+               if (transaction && ref_transaction_abort(transaction, &err) &&
+                   err.len)
+                       error("%s", err.buf);
        }
 
        display_state_release(&display_state);
@@ -2128,7 +2136,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        const char *bundle_uri;
        struct string_list list = STRING_LIST_INIT_DUP;
        struct remote *remote = NULL;
-       int all = 0, multiple = 0;
+       int all = -1, multiple = 0;
        int result = 0;
        int prune_tags_ok = 1;
        int enable_auto_gc = 1;
@@ -2333,11 +2341,20 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
            fetch_bundle_uri(the_repository, bundle_uri, NULL))
                warning(_("failed to fetch bundles from '%s'"), bundle_uri);
 
+       if (all < 0) {
+               /*
+                * no --[no-]all given;
+                * only use config option if no remote was explicitly specified
+                */
+               all = (!argc) ? config.all : 0;
+       }
+
        if (all) {
                if (argc == 1)
                        die(_("fetch --all does not take a repository argument"));
                else if (argc > 1)
                        die(_("fetch --all does not make sense with refspecs"));
+
                (void) for_each_remote(get_one_remote_for_fetch, &list);
 
                /* do not do fetch_multiple() of one */
index 350bfa6e811b8c53d94ba33083a03bf9d93437dc..3885a9c28e149e5e4133bbf25aa557c38bd4193b 100644 (file)
@@ -1,13 +1,12 @@
 #include "builtin.h"
+#include "commit.h"
 #include "config.h"
 #include "gettext.h"
-#include "refs.h"
 #include "object.h"
 #include "parse-options.h"
 #include "ref-filter.h"
 #include "strbuf.h"
 #include "strvec.h"
-#include "commit-reach.h"
 
 static char const * const for_each_ref_usage[] = {
        N_("git for-each-ref [<options>] [<pattern>]"),
@@ -19,15 +18,11 @@ static char const * const for_each_ref_usage[] = {
 
 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, omit_empty = 0;
-       struct ref_array array;
+       int icase = 0;
        struct ref_filter filter = REF_FILTER_INIT;
        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;
 
@@ -40,11 +35,11 @@ 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,
+               OPT_BOOL(0, "omit-empty",  &format.array_opts.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")),
+               OPT_INTEGER( 0 , "count", &format.array_opts.max_count, N_("show only <n> matched refs")),
                OPT_STRING(  0 , "format", &format.format, N_("format"), N_("format to use for the output")),
                OPT__COLOR(&format.use_color, N_("respect format colors")),
                OPT_REF_FILTER_EXCLUDE(&filter),
@@ -61,15 +56,16 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
                OPT_END(),
        };
 
-       memset(&array, 0, sizeof(array));
-
        format.format = "%(objectname) %(objecttype)\t%(refname)";
 
        git_config(git_default_config, NULL);
 
+       /* Set default (refname) sorting */
+       string_list_append(&sorting_options, "refname");
+
        parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);
-       if (maxcount < 0) {
-               error("invalid --count argument: `%d'", maxcount);
+       if (format.array_opts.max_count < 0) {
+               error("invalid --count argument: `%d'", format.array_opts.max_count);
                usage_with_options(for_each_ref_usage, opts);
        }
        if (HAS_MULTI_BITS(format.quote_style)) {
@@ -101,26 +97,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
        }
 
        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)
-               maxcount = array.nr;
-       for (i = 0; i < maxcount; i++) {
-               strbuf_reset(&err);
-               strbuf_reset(&output);
-               if (format_ref_array_item(array.items[i], &format, &output, &err))
-                       die("%s", err.buf);
-               fwrite(output.buf, 1, output.len, stdout);
-               if (output.len || !omit_empty)
-                       putchar('\n');
-       }
+       filter_and_format_refs(&filter, FILTER_REFS_ALL, sorting, &format);
 
-       strbuf_release(&err);
-       strbuf_release(&output);
-       ref_array_clear(&array);
        ref_filter_clear(&filter);
        ref_sorting_release(sorting);
        strvec_clear(&vec);
index 611925905e4fd1d972565806156059016d2eb1a6..a7cf94f67edf3aaace9fdbbca29778d6857d28af 100644 (file)
 #include "refs.h"
 #include "pack.h"
 #include "cache-tree.h"
-#include "tree-walk.h"
 #include "fsck.h"
 #include "parse-options.h"
-#include "dir.h"
 #include "progress.h"
 #include "streaming.h"
-#include "decorate.h"
 #include "packfile.h"
 #include "object-file.h"
 #include "object-name.h"
index 5d01db5c029e2bf967ab96795bf0b3aef5b6102e..1593713f4cb29fe8e608f4c3c7502fa5a624d1f9 100644 (file)
@@ -1,19 +1,20 @@
 #include "builtin.h"
 #include "abspath.h"
 #include "config.h"
+#include "dir.h"
 #include "environment.h"
 #include "gettext.h"
 #include "parse-options.h"
 #include "fsmonitor-ll.h"
 #include "fsmonitor-ipc.h"
-#include "fsmonitor-path-utils.h"
 #include "fsmonitor-settings.h"
 #include "compat/fsmonitor/fsm-health.h"
 #include "compat/fsmonitor/fsm-listen.h"
 #include "fsmonitor--daemon.h"
+#include "repository.h"
 #include "simple-ipc.h"
 #include "khash.h"
-#include "pkt-line.h"
+#include "run-command.h"
 #include "trace.h"
 #include "trace2.h"
 
index 7c11d5ebef052a46cd06b411960fb1c2b3d6ac08..cb80ced6cb5c65d70859a3a6f57cff3d886084b3 100644 (file)
@@ -1543,19 +1543,18 @@ static int maintenance_register(int argc, const char **argv, const char *prefix)
 
        if (!found) {
                int rc;
-               char *user_config = NULL, *xdg_config = NULL;
+               char *global_config_file = NULL;
 
                if (!config_file) {
-                       git_global_config(&user_config, &xdg_config);
-                       config_file = user_config;
-                       if (!user_config)
-                               die(_("$HOME not set"));
+                       global_config_file = git_global_config();
+                       config_file = global_config_file;
                }
+               if (!config_file)
+                       die(_("$HOME not set"));
                rc = git_config_set_multivar_in_file_gently(
                        config_file, "maintenance.repo", maintpath,
                        CONFIG_REGEX_NONE, 0);
-               free(user_config);
-               free(xdg_config);
+               free(global_config_file);
 
                if (rc)
                        die(_("unable to add '%s' value of '%s'"),
@@ -1612,18 +1611,18 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi
 
        if (found) {
                int rc;
-               char *user_config = NULL, *xdg_config = NULL;
+               char *global_config_file = NULL;
+
                if (!config_file) {
-                       git_global_config(&user_config, &xdg_config);
-                       config_file = user_config;
-                       if (!user_config)
-                               die(_("$HOME not set"));
+                       global_config_file = git_global_config();
+                       config_file = global_config_file;
                }
+               if (!config_file)
+                       die(_("$HOME not set"));
                rc = git_config_set_multivar_in_file_gently(
                        config_file, key, NULL, maintpath,
                        CONFIG_FLAGS_MULTI_REPLACE | CONFIG_FLAGS_FIXED_VALUE);
-               free(user_config);
-               free(xdg_config);
+               free(global_config_file);
 
                if (rc &&
                    (!force || rc == CONFIG_NOTHING_SET))
index 20d0dfe9cf1dfefe6ffacb23bd500701cca71a09..66a7389f9f4a21df975ef12a00d93fd7d8eee46f 100644 (file)
@@ -4,7 +4,6 @@
 #include "builtin.h"
 #include "commit.h"
 #include "tar.h"
-#include "quote.h"
 
 static const char builtin_get_tar_commit_id_usage[] =
 "git get-tar-commit-id";
index fe78d4c98b13b578a09de344d6a56d2316a8cb00..c8e33f977552081fcde8afc12d0aa92bfc4b5e62 100644 (file)
@@ -9,15 +9,11 @@
 #include "hex.h"
 #include "repository.h"
 #include "config.h"
-#include "blob.h"
-#include "tree.h"
-#include "commit.h"
 #include "tag.h"
 #include "tree-walk.h"
 #include "parse-options.h"
 #include "string-list.h"
 #include "run-command.h"
-#include "userdiff.h"
 #include "grep.h"
 #include "quote.h"
 #include "dir.h"
index 5ffec99dceaf5c95d9b57c1f0c6c90358b80507d..82ca6d2bfdc7797a13646ff01271ceb803b0a973 100644 (file)
@@ -14,7 +14,6 @@
 #include "blob.h"
 #include "quote.h"
 #include "parse-options.h"
-#include "exec-cmd.h"
 #include "setup.h"
 #include "strbuf.h"
 #include "write-or-die.h"
index 09b51a6487c3928bc099567109193c32fe82fe30..5234693a94b40e2a6dd7aa4a8d724f0ac7b06a3b 100644 (file)
@@ -3,7 +3,6 @@
 #include "gettext.h"
 #include "hook.h"
 #include "parse-options.h"
-#include "strbuf.h"
 #include "strvec.h"
 
 #define BUILTIN_HOOK_RUN_USAGE \
index dda94a9f46d9a9401c298ee304f57e55db25f217..1ea87e01f2905ed70e3a17a847278ec2b7b68ec3 100644 (file)
@@ -8,11 +8,9 @@
 #include "csum-file.h"
 #include "blob.h"
 #include "commit.h"
-#include "tag.h"
 #include "tree.h"
 #include "progress.h"
 #include "fsck.h"
-#include "exec-cmd.h"
 #include "strbuf.h"
 #include "streaming.h"
 #include "thread-utils.h"
@@ -1257,6 +1255,7 @@ static void resolve_deltas(void)
        base_cache_limit = delta_base_cache_limit * nr_threads;
        if (nr_threads > 1 || getenv("GIT_FORCE_THREADS")) {
                init_thread();
+               work_lock();
                for (i = 0; i < nr_threads; i++) {
                        int ret = pthread_create(&thread_data[i].thread, NULL,
                                                 threaded_second_pass, thread_data + i);
@@ -1264,6 +1263,7 @@ static void resolve_deltas(void)
                                die(_("unable to create thread: %s"),
                                    strerror(ret));
                }
+               work_unlock();
                for (i = 0; i < nr_threads; i++)
                        pthread_join(thread_data[i].thread, NULL);
                cleanup_thread();
index cb727c826f5653ab2827ccfbd575e6d305fad75a..0170469b849e5e5ad52cf8586f4540088b0edd61 100644 (file)
@@ -5,12 +5,13 @@
  */
 #include "builtin.h"
 #include "abspath.h"
-#include "config.h"
 #include "environment.h"
 #include "gettext.h"
 #include "object-file.h"
 #include "parse-options.h"
 #include "path.h"
+#include "refs.h"
+#include "repository.h"
 #include "setup.h"
 #include "strbuf.h"
 
@@ -57,6 +58,7 @@ static int shared_callback(const struct option *opt, const char *arg, int unset)
 static const char *const init_db_usage[] = {
        N_("git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
           "         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+          "         [--ref-format=<format>]\n"
           "         [-b <branch-name> | --initial-branch=<branch-name>]\n"
           "         [--shared[=<permissions>]] [<directory>]"),
        NULL
@@ -76,8 +78,10 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
        const char *template_dir = NULL;
        unsigned int flags = 0;
        const char *object_format = NULL;
+       const char *ref_format = NULL;
        const char *initial_branch = NULL;
        int hash_algo = GIT_HASH_UNKNOWN;
+       unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
        int init_shared_repository = -1;
        const struct option init_db_options[] = {
                OPT_STRING(0, "template", &template_dir, N_("template-directory"),
@@ -95,6 +99,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                           N_("override the name of the initial branch")),
                OPT_STRING(0, "object-format", &object_format, N_("hash"),
                           N_("specify the hash algorithm to use")),
+               OPT_STRING(0, "ref-format", &ref_format, N_("format"),
+                          N_("specify the reference format to use")),
                OPT_END()
        };
 
@@ -158,6 +164,12 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                        die(_("unknown hash algorithm '%s'"), object_format);
        }
 
+       if (ref_format) {
+               ref_storage_format = ref_storage_format_by_name(ref_format);
+               if (ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)
+                       die(_("unknown ref storage format '%s'"), ref_format);
+       }
+
        if (init_shared_repository != -1)
                set_shared_repository(init_shared_repository);
 
@@ -236,5 +248,6 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
 
        flags |= INIT_DB_EXIST_OK;
        return init_db(git_dir, real_git_dir, template_dir, hash_algo,
-                      initial_branch, init_shared_repository, flags);
+                      ref_storage_format, initial_branch,
+                      init_shared_repository, flags);
 }
index ba775d7b5cf886374bd94c9f2e54d5695a3dbaff..db1808d7c13dfd523e31d1da395122b52b6579ab 100644 (file)
@@ -26,7 +26,6 @@
 #include "tag.h"
 #include "reflog-walk.h"
 #include "patch-ids.h"
-#include "run-command.h"
 #include "shortlog.h"
 #include "remote.h"
 #include "string-list.h"
@@ -36,7 +35,6 @@
 #include "streaming.h"
 #include "version.h"
 #include "mailmap.h"
-#include "gpg-interface.h"
 #include "progress.h"
 #include "commit-slab.h"
 #include "repository.h"
@@ -594,8 +592,11 @@ static int git_log_config(const char *var, const char *value,
                        decoration_style = 0; /* maybe warn? */
                return 0;
        }
-       if (!strcmp(var, "log.diffmerges"))
+       if (!strcmp(var, "log.diffmerges")) {
+               if (!value)
+                       return config_error_nonbool(var);
                return diff_merges_config(value);
+       }
        if (!strcmp(var, "log.showroot")) {
                default_show_root = git_config_bool(var, value);
                return 0;
@@ -1364,6 +1365,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
        pp.date_mode.type = DATE_RFC2822;
        pp.rev = rev;
        pp.print_email_subject = 1;
+       pp.encode_email_headers = rev->encode_email_headers;
        pp_user_info(&pp, NULL, &sb, committer, encoding);
        prepare_cover_text(&pp, description_file, branch_name, &sb,
                           encoding, need_8bit_cte);
index a0229c3277874accc9edf0a4a5ac4e8186217903..92f94e65bf065e900e59b9fcf2bc18f5af045975 100644 (file)
 #include "gettext.h"
 #include "object-name.h"
 #include "strbuf.h"
-#include "tree.h"
-#include "cache-tree.h"
 #include "parse-options.h"
 #include "resolve-undo.h"
 #include "string-list.h"
 #include "path.h"
 #include "pathspec.h"
 #include "read-cache.h"
-#include "run-command.h"
 #include "setup.h"
 #include "sparse-index.h"
 #include "submodule.h"
-#include "submodule-config.h"
 #include "object-store.h"
 #include "hex.h"
 
index fc765754305ed7a3f8a2efdcd604dd29c8b3b502..e8d65ebbdc0afc864826d722a7da7514a4f382ea 100644 (file)
@@ -5,7 +5,6 @@
 #include "pkt-line.h"
 #include "ref-filter.h"
 #include "remote.h"
-#include "refs.h"
 #include "parse-options.h"
 #include "wildmatch.h"
 
@@ -58,6 +57,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
        struct transport *transport;
        const struct ref *ref;
        struct ref_array ref_array;
+       struct ref_sorting *sorting;
        struct string_list sorting_options = STRING_LIST_INIT_DUP;
 
        struct option options[] = {
@@ -141,13 +141,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
                item->symref = xstrdup_or_null(ref->symref);
        }
 
-       if (sorting_options.nr) {
-               struct ref_sorting *sorting;
-
-               sorting = ref_sorting_options(&sorting_options);
-               ref_array_sort(sorting, &ref_array);
-               ref_sorting_release(sorting);
-       }
+       sorting = ref_sorting_options(&sorting_options);
+       ref_array_sort(sorting, &ref_array);
 
        for (i = 0; i < ref_array.nr; i++) {
                const struct ref_array_item *ref = ref_array.items[i];
@@ -157,6 +152,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
                status = 0; /* we found something */
        }
 
+       ref_sorting_release(sorting);
        ref_array_clear(&ref_array);
        if (transport_disconnect(transport))
                status = 1;
index 209d2dc0d59af3d0c8860ef4eb397edb34e8b429..e4a891337c3c619112af77bac014139b520e463f 100644 (file)
@@ -9,9 +9,7 @@
 #include "hex.h"
 #include "object-name.h"
 #include "object-store-ll.h"
-#include "blob.h"
 #include "tree.h"
-#include "commit.h"
 #include "path.h"
 #include "quote.h"
 #include "parse-options.h"
index 53b55dd71c0537b149860c4b9c0d505ea305fe57..53a22645da58b8c18bb825ca9012e2f072cbf3a3 100644 (file)
@@ -6,7 +6,6 @@
 #include "abspath.h"
 #include "environment.h"
 #include "gettext.h"
-#include "utf8.h"
 #include "strbuf.h"
 #include "mailinfo.h"
 #include "parse-options.h"
index e68b7fe45d77a9b032dd6031864b876eeeca5e5c..d26e8fbf6f75d9971762afdc0ea5c04e209db387 100644 (file)
@@ -3,9 +3,6 @@
 #include "commit.h"
 #include "gettext.h"
 #include "hex.h"
-#include "refs.h"
-#include "diff.h"
-#include "revision.h"
 #include "object-name.h"
 #include "parse-options.h"
 #include "repository.h"
index 832c93d8d54cc50b8581102be4db7c92d80e738a..1f987334a31b0e19904825297c1bb0d37a1fc2fe 100644 (file)
@@ -1,5 +1,6 @@
 #include "builtin.h"
 #include "abspath.h"
+#include "diff.h"
 #include "hex.h"
 #include "object-name.h"
 #include "object-store.h"
@@ -28,6 +29,30 @@ static int label_cb(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+static int set_diff_algorithm(xpparam_t *xpp,
+                             const char *alg)
+{
+       long diff_algorithm = parse_algorithm_value(alg);
+       if (diff_algorithm < 0)
+               return -1;
+       xpp->flags = (xpp->flags & ~XDF_DIFF_ALGORITHM_MASK) | diff_algorithm;
+       return 0;
+}
+
+static int diff_algorithm_cb(const struct option *opt,
+                               const char *arg, int unset)
+{
+       xpparam_t *xpp = opt->value;
+
+       BUG_ON_OPT_NEG(unset);
+
+       if (set_diff_algorithm(xpp, arg))
+               return error(_("option diff-algorithm accepts \"myers\", "
+                              "\"minimal\", \"patience\" and \"histogram\""));
+
+       return 0;
+}
+
 int cmd_merge_file(int argc, const char **argv, const char *prefix)
 {
        const char *names[3] = { 0 };
@@ -48,6 +73,9 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
                            XDL_MERGE_FAVOR_THEIRS),
                OPT_SET_INT(0, "union", &xmp.favor, N_("for conflicts, use a union version"),
                            XDL_MERGE_FAVOR_UNION),
+               OPT_CALLBACK_F(0, "diff-algorithm", &xmp.xpp, N_("<algorithm>"),
+                            N_("choose a diff algorithm"),
+                            PARSE_OPT_NONEG, diff_algorithm_cb),
                OPT_INTEGER(0, "marker-size", &xmp.marker_size,
                            N_("for conflicts, use this marker size")),
                OPT__QUIET(&quiet, N_("do not warn about conflicts")),
index 3366699657c9c390f850dfcba11be08327834f50..c2ce044a201366d7eea11ab3d2eeed2c15113af4 100644 (file)
@@ -1,13 +1,10 @@
 #include "builtin.h"
 #include "advice.h"
-#include "commit.h"
 #include "gettext.h"
 #include "hash.h"
-#include "tag.h"
 #include "merge-recursive.h"
 #include "object-name.h"
 #include "repository.h"
-#include "xdiff-interface.h"
 
 static const char builtin_merge_recursive_usage[] =
        "git %s <base>... -- <head> <remote> ...";
index a35e0452d6672594da29623e8934256f37784549..3bdec53fbe58bb5edef6e4d425829a58ce87bffe 100644 (file)
@@ -13,7 +13,6 @@
 #include "parse-options.h"
 #include "repository.h"
 #include "blob.h"
-#include "exec-cmd.h"
 #include "merge-blobs.h"
 #include "quote.h"
 #include "tree.h"
@@ -577,7 +576,8 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
                if (o.mode == MODE_TRIVIAL)
                        die(_("--trivial-merge is incompatible with all other options"));
                if (merge_base)
-                       die(_("--merge-base is incompatible with --stdin"));
+                       die(_("options '%s' and '%s' cannot be used together"),
+                           "--merge-base", "--stdin");
                line_termination = '\0';
                while (strbuf_getline_lf(&buf, stdin) != EOF) {
                        struct strbuf **split;
index d748d46e13517afd6908b1f7297bf448557b1a86..8f819781cc34a11dc2c9e3d9b56d0df39f57b9a4 100644 (file)
@@ -31,8 +31,6 @@
 #include "unpack-trees.h"
 #include "cache-tree.h"
 #include "dir.h"
-#include "utf8.h"
-#include "log-tree.h"
 #include "color.h"
 #include "rerere.h"
 #include "help.h"
 #include "resolve-undo.h"
 #include "remote.h"
 #include "fmt-merge-msg.h"
-#include "gpg-interface.h"
 #include "sequencer.h"
 #include "string-list.h"
-#include "packfile.h"
 #include "tag.h"
 #include "alias.h"
 #include "branch.h"
@@ -480,7 +476,7 @@ static void finish(struct commit *head_commit,
        run_hooks_l("post-merge", squash ? "1" : "0", NULL);
 
        if (new_head)
-               apply_autostash(git_path_merge_autostash(the_repository));
+               apply_autostash_ref(the_repository, "MERGE_AUTOSTASH");
        strbuf_release(&reflog_message);
 }
 
@@ -869,7 +865,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)
                                _(no_scissors_editor_comment), comment_line_char);
        }
        if (signoff)
-               append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0);
+               append_signoff(&msg, ignored_log_message_bytes(msg.buf, msg.len), 0);
        write_merge_heads(remoteheads);
        write_file_buf(git_path_merge_msg(the_repository), msg.buf, msg.len);
        if (run_commit_hook(0 < option_edit, get_index_file(), NULL,
@@ -1319,7 +1315,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        if (abort_current_merge) {
                int nargc = 2;
                const char *nargv[] = {"reset", "--merge", NULL};
-               struct strbuf stash_oid = STRBUF_INIT;
+               char stash_oid_hex[GIT_MAX_HEXSZ + 1];
+               struct object_id stash_oid = {0};
 
                if (orig_argc != 2)
                        usage_msg_opt(_("--abort expects no arguments"),
@@ -1328,17 +1325,17 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                if (!file_exists(git_path_merge_head(the_repository)))
                        die(_("There is no merge to abort (MERGE_HEAD missing)."));
 
-               if (read_oneliner(&stash_oid, git_path_merge_autostash(the_repository),
-                   READ_ONELINER_SKIP_IF_EMPTY))
-                       unlink(git_path_merge_autostash(the_repository));
+               if (!read_ref("MERGE_AUTOSTASH", &stash_oid))
+                       delete_ref("", "MERGE_AUTOSTASH", &stash_oid, REF_NO_DEREF);
 
                /* Invoke 'git reset --merge' */
                ret = cmd_reset(nargc, nargv, prefix);
 
-               if (stash_oid.len)
-                       apply_autostash_oid(stash_oid.buf);
+               if (!is_null_oid(&stash_oid)) {
+                       oid_to_hex_r(stash_oid_hex, &stash_oid);
+                       apply_autostash_oid(stash_oid_hex);
+               }
 
-               strbuf_release(&stash_oid);
                goto done;
        }
 
@@ -1567,13 +1564,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                }
 
                if (autostash)
-                       create_autostash(the_repository,
-                                        git_path_merge_autostash(the_repository));
+                       create_autostash_ref(the_repository, "MERGE_AUTOSTASH");
                if (checkout_fast_forward(the_repository,
                                          &head_commit->object.oid,
                                          &commit->object.oid,
                                          overwrite_ignore)) {
-                       apply_autostash(git_path_merge_autostash(the_repository));
+                       apply_autostash_ref(the_repository, "MERGE_AUTOSTASH");
                        ret = 1;
                        goto done;
                }
@@ -1659,8 +1655,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                die_ff_impossible();
 
        if (autostash)
-               create_autostash(the_repository,
-                                git_path_merge_autostash(the_repository));
+               create_autostash_ref(the_repository, "MERGE_AUTOSTASH");
 
        /* We are going to make a new commit. */
        git_committer_info(IDENT_STRICT);
@@ -1745,7 +1740,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                else
                        fprintf(stderr, _("Merge with strategy %s failed.\n"),
                                use_strategies[0]->name);
-               apply_autostash(git_path_merge_autostash(the_repository));
+               apply_autostash_ref(the_repository, "MERGE_AUTOSTASH");
                ret = 2;
                goto done;
        } else if (best_strategy == wt_strategy)
index d8e0b5afc079d2f71383eed6881b733fea251312..4767f1a97e6df25ca09eba0846feaf404e08fe46 100644 (file)
@@ -3,7 +3,6 @@
 #include "hex.h"
 #include "parse-options.h"
 #include "strbuf.h"
-#include "tag.h"
 #include "replace-object.h"
 #include "object-file.h"
 #include "object-store-ll.h"
index c596515ad0536f4b13ce2fdc2a797f6aaa9df70a..22e64fc2900ec8e654aafa6cfaf8a10cdb5d58a7 100644 (file)
@@ -15,7 +15,6 @@
 #include "pathspec.h"
 #include "lockfile.h"
 #include "dir.h"
-#include "cache-tree.h"
 #include "string-list.h"
 #include "parse-options.h"
 #include "read-cache-ll.h"
index 9f38863dd507ff680cf8a006b81a413d75550b1b..e65cae0bcf702bf939dcd3ddfb2af5793e73fda0 100644 (file)
@@ -9,7 +9,6 @@
 
 #include "builtin.h"
 #include "config.h"
-#include "alloc.h"
 #include "editor.h"
 #include "environment.h"
 #include "gettext.h"
@@ -19,7 +18,6 @@
 #include "object-store-ll.h"
 #include "path.h"
 #include "repository.h"
-#include "blob.h"
 #include "pretty.h"
 #include "refs.h"
 #include "exec-cmd.h"
index 89a8b5a9768e42da3edaaddb63a663936ef70576..d8c2128a97928229e93212461027b700d219ec6a 100644 (file)
@@ -6,10 +6,8 @@
 #include "config.h"
 #include "attr.h"
 #include "object.h"
-#include "blob.h"
 #include "commit.h"
 #include "tag.h"
-#include "tree.h"
 #include "delta.h"
 #include "pack.h"
 #include "pack-revindex.h"
@@ -18,7 +16,6 @@
 #include "diff.h"
 #include "revision.h"
 #include "list-objects.h"
-#include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
 #include "pack-objects.h"
 #include "progress.h"
@@ -221,13 +218,19 @@ static int thin;
 static int num_preferred_base;
 static struct progress *progress_state;
 
-static struct packed_git *reuse_packfile;
+static struct bitmapped_pack *reuse_packfiles;
+static size_t reuse_packfiles_nr;
+static size_t reuse_packfiles_used_nr;
 static uint32_t reuse_packfile_objects;
 static struct bitmap *reuse_packfile_bitmap;
 
 static int use_bitmap_index_default = 1;
 static int use_bitmap_index = -1;
-static int allow_pack_reuse = 1;
+static enum {
+       NO_PACK_REUSE = 0,
+       SINGLE_PACK_REUSE,
+       MULTI_PACK_REUSE,
+} allow_pack_reuse = SINGLE_PACK_REUSE;
 static enum {
        WRITE_BITMAP_FALSE = 0,
        WRITE_BITMAP_QUIET,
@@ -1013,7 +1016,9 @@ static off_t find_reused_offset(off_t where)
        return reused_chunks[lo-1].difference;
 }
 
-static void write_reused_pack_one(size_t pos, struct hashfile *out,
+static void write_reused_pack_one(struct packed_git *reuse_packfile,
+                                 size_t pos, struct hashfile *out,
+                                 off_t pack_start,
                                  struct pack_window **w_curs)
 {
        off_t offset, next, cur;
@@ -1023,7 +1028,8 @@ static void write_reused_pack_one(size_t pos, struct hashfile *out,
        offset = pack_pos_to_offset(reuse_packfile, pos);
        next = pack_pos_to_offset(reuse_packfile, pos + 1);
 
-       record_reused_object(offset, offset - hashfile_total(out));
+       record_reused_object(offset,
+                            offset - (hashfile_total(out) - pack_start));
 
        cur = offset;
        type = unpack_object_header(reuse_packfile, w_curs, &cur, &size);
@@ -1091,41 +1097,93 @@ static void write_reused_pack_one(size_t pos, struct hashfile *out,
        copy_pack_data(out, reuse_packfile, w_curs, offset, next - offset);
 }
 
-static size_t write_reused_pack_verbatim(struct hashfile *out,
+static size_t write_reused_pack_verbatim(struct bitmapped_pack *reuse_packfile,
+                                        struct hashfile *out,
+                                        off_t pack_start,
                                         struct pack_window **w_curs)
 {
-       size_t pos = 0;
+       size_t pos = reuse_packfile->bitmap_pos;
+       size_t end;
+
+       if (pos % BITS_IN_EWORD) {
+               size_t word_pos = (pos / BITS_IN_EWORD);
+               size_t offset = pos % BITS_IN_EWORD;
+               size_t last;
+               eword_t word = reuse_packfile_bitmap->words[word_pos];
+
+               if (offset + reuse_packfile->bitmap_nr < BITS_IN_EWORD)
+                       last = offset + reuse_packfile->bitmap_nr;
+               else
+                       last = BITS_IN_EWORD;
+
+               for (; offset < last; offset++) {
+                       if (word >> offset == 0)
+                               return word_pos;
+                       if (!bitmap_get(reuse_packfile_bitmap,
+                                       word_pos * BITS_IN_EWORD + offset))
+                               return word_pos;
+               }
+
+               pos += BITS_IN_EWORD - (pos % BITS_IN_EWORD);
+       }
+
+       /*
+        * Now we're going to copy as many whole eword_t's as possible.
+        * "end" is the index of the last whole eword_t we copy, but
+        * there may be additional bits to process. Those are handled
+        * individually by write_reused_pack().
+        *
+        * Begin by advancing to the first word boundary in range of the
+        * bit positions occupied by objects in "reuse_packfile". Then
+        * pick the last word boundary in the same range. If we have at
+        * least one word's worth of bits to process, continue on.
+        */
+       end = reuse_packfile->bitmap_pos + reuse_packfile->bitmap_nr;
+       if (end % BITS_IN_EWORD)
+               end -= end % BITS_IN_EWORD;
+       if (pos >= end)
+               return reuse_packfile->bitmap_pos / BITS_IN_EWORD;
+
+       while (pos < end &&
+              reuse_packfile_bitmap->words[pos / BITS_IN_EWORD] == (eword_t)~0)
+               pos += BITS_IN_EWORD;
 
-       while (pos < reuse_packfile_bitmap->word_alloc &&
-                       reuse_packfile_bitmap->words[pos] == (eword_t)~0)
-               pos++;
+       if (pos > end)
+               pos = end;
 
-       if (pos) {
-               off_t to_write;
+       if (reuse_packfile->bitmap_pos < pos) {
+               off_t pack_start_off = pack_pos_to_offset(reuse_packfile->p, 0);
+               off_t pack_end_off = pack_pos_to_offset(reuse_packfile->p,
+                                                       pos - reuse_packfile->bitmap_pos);
 
-               written = (pos * BITS_IN_EWORD);
-               to_write = pack_pos_to_offset(reuse_packfile, written)
-                       - sizeof(struct pack_header);
+               written += pos - reuse_packfile->bitmap_pos;
 
                /* We're recording one chunk, not one object. */
-               record_reused_object(sizeof(struct pack_header), 0);
+               record_reused_object(pack_start_off,
+                                    pack_start_off - (hashfile_total(out) - pack_start));
                hashflush(out);
-               copy_pack_data(out, reuse_packfile, w_curs,
-                       sizeof(struct pack_header), to_write);
+               copy_pack_data(out, reuse_packfile->p, w_curs,
+                       pack_start_off, pack_end_off - pack_start_off);
 
                display_progress(progress_state, written);
        }
-       return pos;
+       if (pos % BITS_IN_EWORD)
+               BUG("attempted to jump past a word boundary to %"PRIuMAX,
+                   (uintmax_t)pos);
+       return pos / BITS_IN_EWORD;
 }
 
-static void write_reused_pack(struct hashfile *f)
+static void write_reused_pack(struct bitmapped_pack *reuse_packfile,
+                             struct hashfile *f)
 {
-       size_t i = 0;
+       size_t i = reuse_packfile->bitmap_pos / BITS_IN_EWORD;
        uint32_t offset;
+       off_t pack_start = hashfile_total(f) - sizeof(struct pack_header);
        struct pack_window *w_curs = NULL;
 
        if (allow_ofs_delta)
-               i = write_reused_pack_verbatim(f, &w_curs);
+               i = write_reused_pack_verbatim(reuse_packfile, f, pack_start,
+                                              &w_curs);
 
        for (; i < reuse_packfile_bitmap->word_alloc; ++i) {
                eword_t word = reuse_packfile_bitmap->words[i];
@@ -1136,16 +1194,23 @@ static void write_reused_pack(struct hashfile *f)
                                break;
 
                        offset += ewah_bit_ctz64(word >> offset);
+                       if (pos + offset < reuse_packfile->bitmap_pos)
+                               continue;
+                       if (pos + offset >= reuse_packfile->bitmap_pos + reuse_packfile->bitmap_nr)
+                               goto done;
                        /*
                         * Can use bit positions directly, even for MIDX
                         * bitmaps. See comment in try_partial_reuse()
                         * for why.
                         */
-                       write_reused_pack_one(pos + offset, f, &w_curs);
+                       write_reused_pack_one(reuse_packfile->p,
+                                             pos + offset - reuse_packfile->bitmap_pos,
+                                             f, pack_start, &w_curs);
                        display_progress(progress_state, ++written);
                }
        }
 
+done:
        unuse_pack(&w_curs);
 }
 
@@ -1197,9 +1262,14 @@ static void write_pack_file(void)
 
                offset = write_pack_header(f, nr_remaining);
 
-               if (reuse_packfile) {
+               if (reuse_packfiles_nr) {
                        assert(pack_to_stdout);
-                       write_reused_pack(f);
+                       for (j = 0; j < reuse_packfiles_nr; j++) {
+                               reused_chunks_nr = 0;
+                               write_reused_pack(&reuse_packfiles[j], f);
+                               if (reused_chunks_nr)
+                                       reuse_packfiles_used_nr++;
+                       }
                        offset = hashfile_total(f);
                }
 
@@ -3175,7 +3245,19 @@ static int git_pack_config(const char *k, const char *v,
                return 0;
        }
        if (!strcmp(k, "pack.allowpackreuse")) {
-               allow_pack_reuse = git_config_bool(k, v);
+               int res = git_parse_maybe_bool_text(v);
+               if (res < 0) {
+                       if (!strcasecmp(v, "single"))
+                               allow_pack_reuse = SINGLE_PACK_REUSE;
+                       else if (!strcasecmp(v, "multi"))
+                               allow_pack_reuse = MULTI_PACK_REUSE;
+                       else
+                               die(_("invalid pack.allowPackReuse value: '%s'"), v);
+               } else if (res) {
+                       allow_pack_reuse = SINGLE_PACK_REUSE;
+               } else {
+                       allow_pack_reuse = NO_PACK_REUSE;
+               }
                return 0;
        }
        if (!strcmp(k, "pack.threads")) {
@@ -3204,7 +3286,7 @@ static int git_pack_config(const char *k, const char *v,
                return 0;
        }
        if (!strcmp(k, "uploadpack.blobpackfileuri")) {
-               struct configured_exclusion *ex = xmalloc(sizeof(*ex));
+               struct configured_exclusion *ex;
                const char *oid_end, *pack_end;
                /*
                 * Stores the pack hash. This is not a true object ID, but is
@@ -3212,6 +3294,10 @@ static int git_pack_config(const char *k, const char *v,
                 */
                struct object_id pack_hash;
 
+               if (!v)
+                       return config_error_nonbool(k);
+
+               ex = xmalloc(sizeof(*ex));
                if (parse_oid_hex(v, &ex->e.oid, &oid_end) ||
                    *oid_end != ' ' ||
                    parse_oid_hex(oid_end + 1, &pack_hash, &pack_end) ||
@@ -3930,7 +4016,7 @@ static void loosen_unused_packed_objects(void)
  */
 static int pack_options_allow_reuse(void)
 {
-       return allow_pack_reuse &&
+       return allow_pack_reuse != NO_PACK_REUSE &&
               pack_to_stdout &&
               !ignore_packed_keep_on_disk &&
               !ignore_packed_keep_in_core &&
@@ -3943,13 +4029,18 @@ static int get_object_list_from_bitmap(struct rev_info *revs)
        if (!(bitmap_git = prepare_bitmap_walk(revs, 0)))
                return -1;
 
-       if (pack_options_allow_reuse() &&
-           !reuse_partial_packfile_from_bitmap(
-                       bitmap_git,
-                       &reuse_packfile,
-                       &reuse_packfile_objects,
-                       &reuse_packfile_bitmap)) {
-               assert(reuse_packfile_objects);
+       if (pack_options_allow_reuse())
+               reuse_partial_packfile_from_bitmap(bitmap_git,
+                                                  &reuse_packfiles,
+                                                  &reuse_packfiles_nr,
+                                                  &reuse_packfile_bitmap,
+                                                  allow_pack_reuse == MULTI_PACK_REUSE);
+
+       if (reuse_packfiles) {
+               reuse_packfile_objects = bitmap_popcount(reuse_packfile_bitmap);
+               if (!reuse_packfile_objects)
+                       BUG("expected non-empty reuse bitmap");
+
                nr_result += reuse_packfile_objects;
                nr_seen += reuse_packfile_objects;
                display_progress(progress_state, nr_seen);
@@ -4517,11 +4608,20 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                fprintf_ln(stderr,
                           _("Total %"PRIu32" (delta %"PRIu32"),"
                             " reused %"PRIu32" (delta %"PRIu32"),"
-                            " pack-reused %"PRIu32),
+                            " pack-reused %"PRIu32" (from %"PRIuMAX")"),
                           written, written_delta, reused, reused_delta,
-                          reuse_packfile_objects);
+                          reuse_packfile_objects,
+                          (uintmax_t)reuse_packfiles_used_nr);
+
+       trace2_data_intmax("pack-objects", the_repository, "written", written);
+       trace2_data_intmax("pack-objects", the_repository, "written/delta", written_delta);
+       trace2_data_intmax("pack-objects", the_repository, "reused", reused);
+       trace2_data_intmax("pack-objects", the_repository, "reused/delta", reused_delta);
+       trace2_data_intmax("pack-objects", the_repository, "pack-reused", reuse_packfile_objects);
+       trace2_data_intmax("pack-objects", the_repository, "packs-reused", reuse_packfiles_used_nr);
 
 cleanup:
+       clear_packing_data(&to_pack);
        list_objects_filter_release(&filter_options);
        strvec_clear(&rp);
 
index be2b2c9ebc97b2a1620b93e1766e0ccfa6c7843a..73a68b75b0672487013299f992d382d1957a02a4 100644 (file)
@@ -14,7 +14,6 @@
 #include "merge.h"
 #include "object-name.h"
 #include "parse-options.h"
-#include "exec-cmd.h"
 #include "run-command.h"
 #include "oid-array.h"
 #include "remote.h"
 #include "rebase.h"
 #include "refs.h"
 #include "refspec.h"
-#include "revision.h"
 #include "submodule.h"
 #include "submodule-config.h"
-#include "tempfile.h"
-#include "lockfile.h"
 #include "wt-status.h"
 #include "commit-reach.h"
 #include "sequencer.h"
-#include "packfile.h"
 
 /**
  * Parses the value of --rebase. If value is a false value, returns
index 2e708383c24ba86e07fc14b0139afb28d489c014..2fbb31c3ad8eb9b7a15ffac4e5d9aa65367d298f 100644 (file)
@@ -7,7 +7,6 @@
 #include "config.h"
 #include "environment.h"
 #include "gettext.h"
-#include "refs.h"
 #include "refspec.h"
 #include "run-command.h"
 #include "remote.h"
@@ -392,7 +391,7 @@ static int push_with_options(struct transport *transport, struct refspec *rs,
        if (!is_empty_cas(&cas)) {
                if (!transport->smart_options)
                        die("underlying transport does not support --%s option",
-                           CAS_OPT_NAME);
+                           "force-with-lease");
                transport->smart_options->cas = &cas;
        }
 
@@ -526,26 +525,21 @@ static int git_push_config(const char *k, const char *v,
                        *flags |= TRANSPORT_PUSH_AUTO_UPSTREAM;
                return 0;
        } else if (!strcmp(k, "push.gpgsign")) {
-               const char *value;
-               if (!git_config_get_value("push.gpgsign", &value)) {
-                       switch (git_parse_maybe_bool(value)) {
-                       case 0:
-                               set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_NEVER);
-                               break;
-                       case 1:
-                               set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_ALWAYS);
-                               break;
-                       default:
-                               if (value && !strcasecmp(value, "if-asked"))
-                                       set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_IF_ASKED);
-                               else
-                                       return error(_("invalid value for '%s'"), k);
-                       }
+               switch (git_parse_maybe_bool(v)) {
+               case 0:
+                       set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_NEVER);
+                       break;
+               case 1:
+                       set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_ALWAYS);
+                       break;
+               default:
+                       if (!strcasecmp(v, "if-asked"))
+                               set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_IF_ASKED);
+                       else
+                               return error(_("invalid value for '%s'"), k);
                }
        } else if (!strcmp(k, "push.recursesubmodules")) {
-               const char *value;
-               if (!git_config_get_value("push.recursesubmodules", &value))
-                       recurse_submodules = parse_push_recurse_submodules_arg(k, value);
+               recurse_submodules = parse_push_recurse_submodules_arg(k, v);
        } else if (!strcmp(k, "submodule.recurse")) {
                int val = git_config_bool(k, v) ?
                        RECURSE_SUBMODULES_ON_DEMAND : RECURSE_SUBMODULES_OFF;
@@ -604,7 +598,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                OPT_BIT('n' , "dry-run", &flags, N_("dry run"), TRANSPORT_PUSH_DRY_RUN),
                OPT_BIT( 0,  "porcelain", &flags, N_("machine-readable output"), TRANSPORT_PUSH_PORCELAIN),
                OPT_BIT('f', "force", &flags, N_("force updates"), TRANSPORT_PUSH_FORCE),
-               OPT_CALLBACK_F(0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"),
+               OPT_CALLBACK_F(0, "force-with-lease", &cas, N_("<refname>:<expect>"),
                               N_("require old value of ref to be at this value"),
                               PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP, parseopt_push_cas_option),
                OPT_BIT(0, TRANS_OPT_FORCE_IF_INCLUDES, &flags,
@@ -639,8 +633,10 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                : &push_options_config);
        set_push_cert_flags(&flags, push_cert);
 
-       if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
-               die(_("options '%s' and '%s' cannot be used together"), "--delete", "--all/--branches/--mirror/--tags");
+       die_for_incompatible_opt4(deleterefs, "--delete",
+                                 tags, "--tags",
+                                 flags & TRANSPORT_PUSH_ALL, "--all/--branches",
+                                 flags & TRANSPORT_PUSH_MIRROR, "--mirror");
        if (deleterefs && argc < 2)
                die(_("--delete doesn't make sense without any refs"));
 
@@ -677,19 +673,13 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
 
        if (flags & TRANSPORT_PUSH_ALL) {
-               if (tags)
-                       die(_("options '%s' and '%s' cannot be used together"), "--all", "--tags");
                if (argc >= 2)
                        die(_("--all can't be combined with refspecs"));
        }
        if (flags & TRANSPORT_PUSH_MIRROR) {
-               if (tags)
-                       die(_("options '%s' and '%s' cannot be used together"), "--mirror", "--tags");
                if (argc >= 2)
                        die(_("--mirror can't be combined with refspecs"));
        }
-       if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR))
-               die(_("options '%s' and '%s' cannot be used together"), "--all", "--mirror");
 
        if (!is_empty_cas(&cas) && (flags & TRANSPORT_PUSH_FORCE_IF_INCLUDES))
                cas.use_force_if_includes = 1;
index e455a4795cc8101576a806ee68df71a56c7661ce..f02cbac087db403cedae2e4b5269e5258059677f 100644 (file)
@@ -5,7 +5,6 @@
 #include "range-diff.h"
 #include "config.h"
 #include "repository.h"
-#include "revision.h"
 
 static const char * const builtin_range_diff_usage[] = {
 N_("git range-diff [<options>] <old-base>..<old-tip> <new-base>..<new-tip>"),
index 8196ca9dd85828890cf5e7088d19e51990a00c66..20e7db19737d5a5070068a131797042d37a936a5 100644 (file)
 #include "tree-walk.h"
 #include "cache-tree.h"
 #include "unpack-trees.h"
-#include "dir.h"
 #include "parse-options.h"
 #include "repository.h"
 #include "resolve-undo.h"
 #include "setup.h"
 #include "sparse-index.h"
 #include "submodule.h"
-#include "submodule-config.h"
 
 static int nr_trees;
 static int read_empty;
index 043c65dccd9f19667a1eb6af993dfb9c1f4d8669..5b086f651a6fce7d97132da8ff36c80f00648db1 100644 (file)
 #include "gettext.h"
 #include "hex.h"
 #include "run-command.h"
-#include "exec-cmd.h"
 #include "strvec.h"
 #include "dir.h"
-#include "packfile.h"
 #include "refs.h"
-#include "quote.h"
 #include "config.h"
-#include "cache-tree.h"
 #include "unpack-trees.h"
 #include "lockfile.h"
 #include "object-file.h"
@@ -149,7 +145,6 @@ struct rebase_options {
                .reapply_cherry_picks = -1,             \
                .allow_empty_message = 1,               \
                .autosquash = -1,                       \
-               .config_autosquash = -1,                \
                .rebase_merges = -1,                    \
                .config_rebase_merges = -1,             \
                .update_refs = -1,                      \
@@ -520,7 +515,7 @@ static int finish_rebase(struct rebase_options *opts)
        int ret = 0;
 
        delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
-       unlink(git_path_auto_merge(the_repository));
+       delete_ref(NULL, "AUTO_MERGE", NULL, REF_NO_DEREF);
        apply_autostash(state_dir_path("autostash", opts));
        /*
         * We ignore errors in 'git maintenance run --auto', since the
@@ -583,7 +578,6 @@ static int run_am(struct rebase_options *opts)
 {
        struct child_process am = CHILD_PROCESS_INIT;
        struct child_process format_patch = CHILD_PROCESS_INIT;
-       struct strbuf revisions = STRBUF_INIT;
        int status;
        char *rebased_patches;
 
@@ -616,13 +610,6 @@ static int run_am(struct rebase_options *opts)
                return run_command(&am);
        }
 
-       strbuf_addf(&revisions, "%s...%s",
-                   oid_to_hex(opts->root ?
-                              /* this is now equivalent to !opts->upstream */
-                              &opts->onto->object.oid :
-                              &opts->upstream->object.oid),
-                   oid_to_hex(&opts->orig_head->object.oid));
-
        rebased_patches = xstrdup(git_path("rebased-patches"));
        format_patch.out = open(rebased_patches,
                                O_WRONLY | O_CREAT | O_TRUNC, 0666);
@@ -643,7 +630,12 @@ static int run_am(struct rebase_options *opts)
        if (opts->git_format_patch_opt.len)
                strvec_split(&format_patch.args,
                             opts->git_format_patch_opt.buf);
-       strvec_push(&format_patch.args, revisions.buf);
+       strvec_pushf(&format_patch.args, "%s...%s",
+                    oid_to_hex(opts->root ?
+                               /* this is now equivalent to !opts->upstream */
+                               &opts->onto->object.oid :
+                               &opts->upstream->object.oid),
+                    oid_to_hex(&opts->orig_head->object.oid));
        if (opts->restrict_revision)
                strvec_pushf(&format_patch.args, "^%s",
                             oid_to_hex(&opts->restrict_revision->object.oid));
@@ -666,10 +658,8 @@ static int run_am(struct rebase_options *opts)
                        "As a result, git cannot rebase them."),
                      opts->revisions);
 
-               strbuf_release(&revisions);
                return status;
        }
-       strbuf_release(&revisions);
 
        am.in = open(rebased_patches, O_RDONLY);
        if (am.in < 0) {
@@ -711,10 +701,8 @@ static int run_specific_rebase(struct rebase_options *opts)
        if (opts->type == REBASE_MERGE) {
                /* Run sequencer-based rebase */
                setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1);
-               if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
+               if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT))
                        setenv("GIT_SEQUENCE_EDITOR", ":", 1);
-                       opts->autosquash = 0;
-               }
                if (opts->gpg_sign_opt) {
                        /* remove the leading "-S" */
                        char *tmp = xstrdup(opts->gpg_sign_opt + 2);
@@ -1405,7 +1393,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) ||
            (options.action != ACTION_NONE) ||
            (options.exec.nr > 0) ||
-           (options.autosquash == -1 && options.config_autosquash == 1) ||
            options.autosquash == 1) {
                allow_preemptive_ff = 0;
        }
@@ -1508,8 +1495,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        if (is_merge(&options))
                                die(_("apply options and merge options "
                                          "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)
@@ -1529,10 +1514,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        options.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :
                                ((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);
 
-       if (options.autosquash == 1)
+       if (options.autosquash == 1) {
                imply_merge(&options, "--autosquash");
-       options.autosquash = (options.autosquash >= 0) ? options.autosquash :
-                            ((options.config_autosquash >= 0) ? options.config_autosquash : 0);
+       } else if (options.autosquash == -1) {
+               options.autosquash =
+                       options.config_autosquash &&
+                       (options.flags & REBASE_INTERACTIVE_EXPLICIT);
+       }
 
        if (options.type == REBASE_UNSPECIFIED) {
                if (!strcmp(options.default_backend, "merge"))
index 8c4f0cb90a936b2b24b8b7b9e8b1b789c33bff51..e36b1d619f5c075fc26617fb26972dc21d561e75 100644 (file)
@@ -22,7 +22,6 @@
 #include "connected.h"
 #include "strvec.h"
 #include "version.h"
-#include "tag.h"
 #include "gpg-interface.h"
 #include "sigchain.h"
 #include "fsck.h"
@@ -142,6 +141,7 @@ static enum deny_action parse_deny_action(const char *var, const char *value)
 static int receive_pack_config(const char *var, const char *value,
                               const struct config_context *ctx, void *cb)
 {
+       const char *msg_id;
        int status = parse_hide_refs_config(var, value, "receive", &hidden_refs);
 
        if (status)
@@ -178,12 +178,14 @@ static int receive_pack_config(const char *var, const char *value,
                return 0;
        }
 
-       if (skip_prefix(var, "receive.fsck.", &var)) {
-               if (is_valid_msg_type(var, value))
+       if (skip_prefix(var, "receive.fsck.", &msg_id)) {
+               if (!value)
+                       return config_error_nonbool(var);
+               if (is_valid_msg_type(msg_id, value))
                        strbuf_addf(&fsck_msg_types, "%c%s=%s",
-                               fsck_msg_types.len ? ',' : '=', var, value);
+                               fsck_msg_types.len ? ',' : '=', msg_id, value);
                else
-                       warning("skipping unknown msg id '%s'", var);
+                       warning("skipping unknown msg id '%s'", msg_id);
                return 0;
        }
 
index 6e490f83d543bb2e38af7f737dc7966ef009c35b..a5a4099f61a5f825d6e622572c9ee85ae563de7b 100644 (file)
@@ -248,7 +248,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
        int verbose = 0;
        reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
        const struct option options[] = {
-               OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"),
+               OPT_BIT('n', "dry-run", &flags, N_("do not actually prune any entries"),
                        EXPIRE_REFLOGS_DRY_RUN),
                OPT_BIT(0, "rewrite", &flags,
                        N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"),
@@ -368,7 +368,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
        int verbose = 0;
 
        const struct option options[] = {
-               OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"),
+               OPT_BIT('n', "dry-run", &flags, N_("do not actually prune any entries"),
                        EXPIRE_REFLOGS_DRY_RUN),
                OPT_BIT(0, "rewrite", &flags,
                        N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"),
index edaee4dbec7b01624b5d272a7c402c49f46dba5d..ede36328a3cab9170bc59cb98dda097ea6eb24f7 100644 (file)
@@ -8,7 +8,6 @@
 #include "path.h"
 #include "run-command.h"
 #include "server-info.h"
-#include "sigchain.h"
 #include "strbuf.h"
 #include "string-list.h"
 #include "strvec.h"
@@ -1203,19 +1202,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        if (delete_redundant && repository_format_precious_objects)
                die(_("cannot delete packs in a precious-objects repo"));
 
-       if (keep_unreachable &&
-           (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)))
-               die(_("options '%s' and '%s' cannot be used together"), "--keep-unreachable", "-A");
+       die_for_incompatible_opt3(unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE), "-A",
+                                 keep_unreachable, "-k/--keep-unreachable",
+                                 pack_everything & PACK_CRUFT, "--cruft");
 
-       if (pack_everything & PACK_CRUFT) {
+       if (pack_everything & PACK_CRUFT)
                pack_everything |= ALL_INTO_ONE;
 
-               if (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE))
-                       die(_("options '%s' and '%s' cannot be used together"), "--cruft", "-A");
-               if (keep_unreachable)
-                       die(_("options '%s' and '%s' cannot be used together"), "--cruft", "-k");
-       }
-
        if (write_bitmaps < 0) {
                if (!write_midx &&
                    (!(pack_everything & ALL_INTO_ONE) || !is_bare_repository()))
diff --git a/builtin/replay.c b/builtin/replay.c
new file mode 100644 (file)
index 0000000..6bc4b47
--- /dev/null
@@ -0,0 +1,446 @@
+/*
+ * "git replay" builtin command
+ */
+
+#define USE_THE_INDEX_VARIABLE
+#include "git-compat-util.h"
+
+#include "builtin.h"
+#include "environment.h"
+#include "hex.h"
+#include "lockfile.h"
+#include "merge-ort.h"
+#include "object-name.h"
+#include "parse-options.h"
+#include "refs.h"
+#include "revision.h"
+#include "strmap.h"
+#include <oidset.h>
+#include <tree.h>
+
+static const char *short_commit_name(struct commit *commit)
+{
+       return repo_find_unique_abbrev(the_repository, &commit->object.oid,
+                                      DEFAULT_ABBREV);
+}
+
+static struct commit *peel_committish(const char *name)
+{
+       struct object *obj;
+       struct object_id oid;
+
+       if (repo_get_oid(the_repository, name, &oid))
+               return NULL;
+       obj = parse_object(the_repository, &oid);
+       return (struct commit *)repo_peel_to_type(the_repository, name, 0, obj,
+                                                 OBJ_COMMIT);
+}
+
+static char *get_author(const char *message)
+{
+       size_t len;
+       const char *a;
+
+       a = find_commit_header(message, "author", &len);
+       if (a)
+               return xmemdupz(a, len);
+
+       return NULL;
+}
+
+static struct commit *create_commit(struct tree *tree,
+                                   struct commit *based_on,
+                                   struct commit *parent)
+{
+       struct object_id ret;
+       struct object *obj;
+       struct commit_list *parents = NULL;
+       char *author;
+       char *sign_commit = NULL; /* FIXME: cli users might want to sign again */
+       struct commit_extra_header *extra;
+       struct strbuf msg = STRBUF_INIT;
+       const char *out_enc = get_commit_output_encoding();
+       const char *message = repo_logmsg_reencode(the_repository, based_on,
+                                                  NULL, out_enc);
+       const char *orig_message = NULL;
+       const char *exclude_gpgsig[] = { "gpgsig", NULL };
+
+       commit_list_insert(parent, &parents);
+       extra = read_commit_extra_headers(based_on, exclude_gpgsig);
+       find_commit_subject(message, &orig_message);
+       strbuf_addstr(&msg, orig_message);
+       author = get_author(message);
+       reset_ident_date();
+       if (commit_tree_extended(msg.buf, msg.len, &tree->object.oid, parents,
+                                &ret, author, NULL, sign_commit, extra)) {
+               error(_("failed to write commit object"));
+               return NULL;
+       }
+       free(author);
+       strbuf_release(&msg);
+
+       obj = parse_object(the_repository, &ret);
+       return (struct commit *)obj;
+}
+
+struct ref_info {
+       struct commit *onto;
+       struct strset positive_refs;
+       struct strset negative_refs;
+       int positive_refexprs;
+       int negative_refexprs;
+};
+
+static void get_ref_information(struct rev_cmdline_info *cmd_info,
+                               struct ref_info *ref_info)
+{
+       int i;
+
+       ref_info->onto = NULL;
+       strset_init(&ref_info->positive_refs);
+       strset_init(&ref_info->negative_refs);
+       ref_info->positive_refexprs = 0;
+       ref_info->negative_refexprs = 0;
+
+       /*
+        * When the user specifies e.g.
+        *   git replay origin/main..mybranch
+        *   git replay ^origin/next mybranch1 mybranch2
+        * we want to be able to determine where to replay the commits.  In
+        * these examples, the branches are probably based on an old version
+        * of either origin/main or origin/next, so we want to replay on the
+        * newest version of that branch.  In contrast we would want to error
+        * out if they ran
+        *   git replay ^origin/master ^origin/next mybranch
+        *   git replay mybranch~2..mybranch
+        * the first of those because there's no unique base to choose, and
+        * the second because they'd likely just be replaying commits on top
+        * of the same commit and not making any difference.
+        */
+       for (i = 0; i < cmd_info->nr; i++) {
+               struct rev_cmdline_entry *e = cmd_info->rev + i;
+               struct object_id oid;
+               const char *refexpr = e->name;
+               char *fullname = NULL;
+               int can_uniquely_dwim = 1;
+
+               if (*refexpr == '^')
+                       refexpr++;
+               if (repo_dwim_ref(the_repository, refexpr, strlen(refexpr), &oid, &fullname, 0) != 1)
+                       can_uniquely_dwim = 0;
+
+               if (e->flags & BOTTOM) {
+                       if (can_uniquely_dwim)
+                               strset_add(&ref_info->negative_refs, fullname);
+                       if (!ref_info->negative_refexprs)
+                               ref_info->onto = lookup_commit_reference_gently(the_repository,
+                                                                               &e->item->oid, 1);
+                       ref_info->negative_refexprs++;
+               } else {
+                       if (can_uniquely_dwim)
+                               strset_add(&ref_info->positive_refs, fullname);
+                       ref_info->positive_refexprs++;
+               }
+
+               free(fullname);
+       }
+}
+
+static void determine_replay_mode(struct rev_cmdline_info *cmd_info,
+                                 const char *onto_name,
+                                 const char **advance_name,
+                                 struct commit **onto,
+                                 struct strset **update_refs)
+{
+       struct ref_info rinfo;
+
+       get_ref_information(cmd_info, &rinfo);
+       if (!rinfo.positive_refexprs)
+               die(_("need some commits to replay"));
+       if (onto_name && *advance_name)
+               die(_("--onto and --advance are incompatible"));
+       else if (onto_name) {
+               *onto = peel_committish(onto_name);
+               if (rinfo.positive_refexprs <
+                   strset_get_size(&rinfo.positive_refs))
+                       die(_("all positive revisions given must be references"));
+       } else if (*advance_name) {
+               struct object_id oid;
+               char *fullname = NULL;
+
+               *onto = peel_committish(*advance_name);
+               if (repo_dwim_ref(the_repository, *advance_name, strlen(*advance_name),
+                            &oid, &fullname, 0) == 1) {
+                       *advance_name = fullname;
+               } else {
+                       die(_("argument to --advance must be a reference"));
+               }
+               if (rinfo.positive_refexprs > 1)
+                       die(_("cannot advance target with multiple sources because ordering would be ill-defined"));
+       } else {
+               int positive_refs_complete = (
+                       rinfo.positive_refexprs ==
+                       strset_get_size(&rinfo.positive_refs));
+               int negative_refs_complete = (
+                       rinfo.negative_refexprs ==
+                       strset_get_size(&rinfo.negative_refs));
+               /*
+                * We need either positive_refs_complete or
+                * negative_refs_complete, but not both.
+                */
+               if (rinfo.negative_refexprs > 0 &&
+                   positive_refs_complete == negative_refs_complete)
+                       die(_("cannot implicitly determine whether this is an --advance or --onto operation"));
+               if (negative_refs_complete) {
+                       struct hashmap_iter iter;
+                       struct strmap_entry *entry;
+
+                       if (rinfo.negative_refexprs == 0)
+                               die(_("all positive revisions given must be references"));
+                       else if (rinfo.negative_refexprs > 1)
+                               die(_("cannot implicitly determine whether this is an --advance or --onto operation"));
+                       else if (rinfo.positive_refexprs > 1)
+                               die(_("cannot advance target with multiple source branches because ordering would be ill-defined"));
+
+                       /* Only one entry, but we have to loop to get it */
+                       strset_for_each_entry(&rinfo.negative_refs,
+                                             &iter, entry) {
+                               *advance_name = entry->key;
+                       }
+               } else { /* positive_refs_complete */
+                       if (rinfo.negative_refexprs > 1)
+                               die(_("cannot implicitly determine correct base for --onto"));
+                       if (rinfo.negative_refexprs == 1)
+                               *onto = rinfo.onto;
+               }
+       }
+       if (!*advance_name) {
+               *update_refs = xcalloc(1, sizeof(**update_refs));
+               **update_refs = rinfo.positive_refs;
+               memset(&rinfo.positive_refs, 0, sizeof(**update_refs));
+       }
+       strset_clear(&rinfo.negative_refs);
+       strset_clear(&rinfo.positive_refs);
+}
+
+static struct commit *mapped_commit(kh_oid_map_t *replayed_commits,
+                                   struct commit *commit,
+                                   struct commit *fallback)
+{
+       khint_t pos = kh_get_oid_map(replayed_commits, commit->object.oid);
+       if (pos == kh_end(replayed_commits))
+               return fallback;
+       return kh_value(replayed_commits, pos);
+}
+
+static struct commit *pick_regular_commit(struct commit *pickme,
+                                         kh_oid_map_t *replayed_commits,
+                                         struct commit *onto,
+                                         struct merge_options *merge_opt,
+                                         struct merge_result *result)
+{
+       struct commit *base, *replayed_base;
+       struct tree *pickme_tree, *base_tree;
+
+       base = pickme->parents->item;
+       replayed_base = mapped_commit(replayed_commits, base, onto);
+
+       result->tree = repo_get_commit_tree(the_repository, replayed_base);
+       pickme_tree = repo_get_commit_tree(the_repository, pickme);
+       base_tree = repo_get_commit_tree(the_repository, base);
+
+       merge_opt->branch1 = short_commit_name(replayed_base);
+       merge_opt->branch2 = short_commit_name(pickme);
+       merge_opt->ancestor = xstrfmt("parent of %s", merge_opt->branch2);
+
+       merge_incore_nonrecursive(merge_opt,
+                                 base_tree,
+                                 result->tree,
+                                 pickme_tree,
+                                 result);
+
+       free((char*)merge_opt->ancestor);
+       merge_opt->ancestor = NULL;
+       if (!result->clean)
+               return NULL;
+       return create_commit(result->tree, pickme, replayed_base);
+}
+
+int cmd_replay(int argc, const char **argv, const char *prefix)
+{
+       const char *advance_name = NULL;
+       struct commit *onto = NULL;
+       const char *onto_name = NULL;
+       int contained = 0;
+
+       struct rev_info revs;
+       struct commit *last_commit = NULL;
+       struct commit *commit;
+       struct merge_options merge_opt;
+       struct merge_result result;
+       struct strset *update_refs = NULL;
+       kh_oid_map_t *replayed_commits;
+       int ret = 0;
+
+       const char * const replay_usage[] = {
+               N_("(EXPERIMENTAL!) git replay "
+                  "([--contained] --onto <newbase> | --advance <branch>) "
+                  "<revision-range>..."),
+               NULL
+       };
+       struct option replay_options[] = {
+               OPT_STRING(0, "advance", &advance_name,
+                          N_("branch"),
+                          N_("make replay advance given branch")),
+               OPT_STRING(0, "onto", &onto_name,
+                          N_("revision"),
+                          N_("replay onto given commit")),
+               OPT_BOOL(0, "contained", &contained,
+                        N_("advance all branches contained in revision-range")),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, replay_options, replay_usage,
+                            PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT);
+
+       if (!onto_name && !advance_name) {
+               error(_("option --onto or --advance is mandatory"));
+               usage_with_options(replay_usage, replay_options);
+       }
+
+       if (advance_name && contained)
+               die(_("options '%s' and '%s' cannot be used together"),
+                   "--advance", "--contained");
+
+       repo_init_revisions(the_repository, &revs, prefix);
+
+       /*
+        * Set desired values for rev walking options here. If they
+        * are changed by some user specified option in setup_revisions()
+        * below, we will detect that below and then warn.
+        *
+        * TODO: In the future we might want to either die(), or allow
+        * some options changing these values if we think they could
+        * be useful.
+        */
+       revs.reverse = 1;
+       revs.sort_order = REV_SORT_IN_GRAPH_ORDER;
+       revs.topo_order = 1;
+       revs.simplify_history = 0;
+
+       argc = setup_revisions(argc, argv, &revs, NULL);
+       if (argc > 1) {
+               ret = error(_("unrecognized argument: %s"), argv[1]);
+               goto cleanup;
+       }
+
+       /*
+        * Detect and warn if we override some user specified rev
+        * walking options.
+        */
+       if (revs.reverse != 1) {
+               warning(_("some rev walking options will be overridden as "
+                         "'%s' bit in 'struct rev_info' will be forced"),
+                       "reverse");
+               revs.reverse = 1;
+       }
+       if (revs.sort_order != REV_SORT_IN_GRAPH_ORDER) {
+               warning(_("some rev walking options will be overridden as "
+                         "'%s' bit in 'struct rev_info' will be forced"),
+                       "sort_order");
+               revs.sort_order = REV_SORT_IN_GRAPH_ORDER;
+       }
+       if (revs.topo_order != 1) {
+               warning(_("some rev walking options will be overridden as "
+                         "'%s' bit in 'struct rev_info' will be forced"),
+                       "topo_order");
+               revs.topo_order = 1;
+       }
+       if (revs.simplify_history != 0) {
+               warning(_("some rev walking options will be overridden as "
+                         "'%s' bit in 'struct rev_info' will be forced"),
+                       "simplify_history");
+               revs.simplify_history = 0;
+       }
+
+       determine_replay_mode(&revs.cmdline, onto_name, &advance_name,
+                             &onto, &update_refs);
+
+       if (!onto) /* FIXME: Should handle replaying down to root commit */
+               die("Replaying down to root commit is not supported yet!");
+
+       if (prepare_revision_walk(&revs) < 0) {
+               ret = error(_("error preparing revisions"));
+               goto cleanup;
+       }
+
+       init_merge_options(&merge_opt, the_repository);
+       memset(&result, 0, sizeof(result));
+       merge_opt.show_rename_progress = 0;
+       last_commit = onto;
+       replayed_commits = kh_init_oid_map();
+       while ((commit = get_revision(&revs))) {
+               const struct name_decoration *decoration;
+               khint_t pos;
+               int hr;
+
+               if (!commit->parents)
+                       die(_("replaying down to root commit is not supported yet!"));
+               if (commit->parents->next)
+                       die(_("replaying merge commits is not supported yet!"));
+
+               last_commit = pick_regular_commit(commit, replayed_commits, onto,
+                                                 &merge_opt, &result);
+               if (!last_commit)
+                       break;
+
+               /* Record commit -> last_commit mapping */
+               pos = kh_put_oid_map(replayed_commits, commit->object.oid, &hr);
+               if (hr == 0)
+                       BUG("Duplicate rewritten commit: %s\n",
+                           oid_to_hex(&commit->object.oid));
+               kh_value(replayed_commits, pos) = last_commit;
+
+               /* Update any necessary branches */
+               if (advance_name)
+                       continue;
+               decoration = get_name_decoration(&commit->object);
+               if (!decoration)
+                       continue;
+               while (decoration) {
+                       if (decoration->type == DECORATION_REF_LOCAL &&
+                           (contained || strset_contains(update_refs,
+                                                         decoration->name))) {
+                               printf("update %s %s %s\n",
+                                      decoration->name,
+                                      oid_to_hex(&last_commit->object.oid),
+                                      oid_to_hex(&commit->object.oid));
+                       }
+                       decoration = decoration->next;
+               }
+       }
+
+       /* In --advance mode, advance the target ref */
+       if (result.clean == 1 && advance_name) {
+               printf("update %s %s %s\n",
+                      advance_name,
+                      oid_to_hex(&last_commit->object.oid),
+                      oid_to_hex(&onto->object.oid));
+       }
+
+       merge_finalize(&merge_opt, &result);
+       kh_destroy_oid_map(replayed_commits);
+       if (update_refs) {
+               strset_clear(update_refs);
+               free(update_refs);
+       }
+       ret = result.clean;
+
+cleanup:
+       release_revisions(&revs);
+
+       /* Return */
+       if (ret < 0)
+               exit(128);
+       return ret ? 0 : 1;
+}
index 07a9d37275cbbc52cc8cddbae4a6143df2e49a82..b2efc6f640e18a2f956cdc985ecc2c793120a267 100644 (file)
@@ -1,6 +1,5 @@
 #include "builtin.h"
 #include "config.h"
-#include "dir.h"
 #include "gettext.h"
 #include "parse-options.h"
 #include "repository.h"
index 4b018d20e3b2200e44a04f1f759837a571e4bc4f..8390bfe4c487b0c3170045f0601234c1e8278122 100644 (file)
 #include "hash.h"
 #include "hex.h"
 #include "lockfile.h"
-#include "tag.h"
 #include "object.h"
 #include "pretty.h"
-#include "run-command.h"
 #include "refs.h"
 #include "diff.h"
 #include "diffcore.h"
@@ -33,7 +31,6 @@
 #include "setup.h"
 #include "sparse-index.h"
 #include "submodule.h"
-#include "submodule-config.h"
 #include "trace.h"
 #include "trace2.h"
 #include "dir.h"
index 181353dcf51ba8af7620b8926084a1cbea3163b2..b3f47838580c9dd0ed233a3ac34c19aac6b7fa0f 100644 (file)
@@ -7,13 +7,11 @@
 #include "hex.h"
 #include "revision.h"
 #include "list-objects.h"
-#include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
 #include "object.h"
 #include "object-name.h"
 #include "object-file.h"
 #include "object-store-ll.h"
-#include "pack.h"
 #include "pack-bitmap.h"
 #include "log-tree.h"
 #include "graph.h"
index fde8861ca4e07990078258843890aef6334dd4c9..d08987646a0a533ad81480ae4667b577a335f9f7 100644 (file)
@@ -893,13 +893,15 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                        }
                        if (opt_with_value(arg, "--branches", &arg)) {
                                if (ref_excludes.hidden_refs_configured)
-                                       return error(_("--exclude-hidden cannot be used together with --branches"));
+                                       return error(_("options '%s' and '%s' cannot be used together"),
+                                                    "--exclude-hidden", "--branches");
                                handle_ref_opt(arg, "refs/heads/");
                                continue;
                        }
                        if (opt_with_value(arg, "--tags", &arg)) {
                                if (ref_excludes.hidden_refs_configured)
-                                       return error(_("--exclude-hidden cannot be used together with --tags"));
+                                       return error(_("options '%s' and '%s' cannot be used together"),
+                                                    "--exclude-hidden", "--tags");
                                handle_ref_opt(arg, "refs/tags/");
                                continue;
                        }
@@ -909,7 +911,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                        }
                        if (opt_with_value(arg, "--remotes", &arg)) {
                                if (ref_excludes.hidden_refs_configured)
-                                       return error(_("--exclude-hidden cannot be used together with --remotes"));
+                                       return error(_("options '%s' and '%s' cannot be used together"),
+                                                    "--exclude-hidden", "--remotes");
                                handle_ref_opt(arg, "refs/remotes/");
                                continue;
                        }
@@ -1059,6 +1062,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                puts(the_hash_algo->name);
                                continue;
                        }
+                       if (!strcmp(arg, "--show-ref-format")) {
+                               puts(ref_storage_format_to_name(the_repository->ref_storage_format));
+                               continue;
+                       }
                        if (!strcmp(arg, "--end-of-options")) {
                                seen_end_of_options = 1;
                                if (filter & (DO_FLAGS | DO_REVS))
index e6f9a1ad26721c450258849cb8e415156052e442..89821bab95745a5fca294d00811804799ecd323d 100644 (file)
@@ -1,5 +1,4 @@
 #include "git-compat-util.h"
-#include "config.h"
 #include "builtin.h"
 #include "parse-options.h"
 #include "diff.h"
@@ -7,7 +6,6 @@
 #include "repository.h"
 #include "revision.h"
 #include "rerere.h"
-#include "dir.h"
 #include "sequencer.h"
 #include "branch.h"
 
index dff819ae5098ff2ee0c72bae00b35da39bf4979d..fd130cea2d2592829e3abd991ca6caae4a119897 100644 (file)
@@ -9,7 +9,6 @@
 #include "config.h"
 #include "lockfile.h"
 #include "dir.h"
-#include "cache-tree.h"
 #include "gettext.h"
 #include "hash.h"
 #include "tree-walk.h"
index cd6d9e41129a21d7f9c21cebae0a322812ebcbcd..3df9eaad092babdbbfc47f1c5441fc43a4ccd182 100644 (file)
@@ -1,19 +1,14 @@
 #include "builtin.h"
 #include "config.h"
-#include "commit.h"
 #include "hex.h"
-#include "refs.h"
 #include "pkt-line.h"
-#include "sideband.h"
 #include "run-command.h"
 #include "remote.h"
 #include "connect.h"
 #include "send-pack.h"
 #include "quote.h"
 #include "transport.h"
-#include "version.h"
 #include "oid-array.h"
-#include "gpg-interface.h"
 #include "gettext.h"
 #include "protocol.h"
 #include "parse-options.h"
@@ -135,21 +130,18 @@ static int send_pack_config(const char *k, const char *v,
                            const struct config_context *ctx, void *cb)
 {
        if (!strcmp(k, "push.gpgsign")) {
-               const char *value;
-               if (!git_config_get_value("push.gpgsign", &value)) {
-                       switch (git_parse_maybe_bool(value)) {
-                       case 0:
-                               args.push_cert = SEND_PACK_PUSH_CERT_NEVER;
-                               break;
-                       case 1:
-                               args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
-                               break;
-                       default:
-                               if (value && !strcasecmp(value, "if-asked"))
-                                       args.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;
-                               else
-                                       return error(_("invalid value for '%s'"), k);
-                       }
+               switch (git_parse_maybe_bool(v)) {
+               case 0:
+                       args.push_cert = SEND_PACK_PUSH_CERT_NEVER;
+                       break;
+               case 1:
+                       args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
+                       break;
+               default:
+                       if (!strcasecmp(v, "if-asked"))
+                               args.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;
+                       else
+                               return error(_("invalid value for '%s'"), k);
                }
        }
        return git_default_config(k, v, ctx, cb);
@@ -208,7 +200,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "stateless-rpc", &stateless_rpc, N_("use stateless RPC protocol")),
                OPT_BOOL(0, "stdin", &from_stdin, N_("read refs from stdin")),
                OPT_BOOL(0, "helper-status", &helper_status, N_("print status from remote helper")),
-               OPT_CALLBACK_F(0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"),
+               OPT_CALLBACK_F(0, "force-with-lease", &cas, N_("<refname>:<expect>"),
                  N_("require old value of ref to be at this value"),
                  PARSE_OPT_OPTARG, parseopt_push_cas_option),
                OPT_BOOL(0, TRANS_OPT_FORCE_IF_INCLUDES, &force_if_includes,
@@ -341,6 +333,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
        }
 
        if (!ret && !transport_refs_pushed(remote_refs))
+               /* stable plumbing output; do not modify or localize */
                fprintf(stderr, "Everything up-to-date\n");
 
        return ret;
index 7aac525a878b3b3ae4959a831de4747d379e140f..79955c2856eacea0398a7fd9e90f34c790bbd8cd 100644 (file)
@@ -6,7 +6,6 @@
 #include "object-name.h"
 #include "object-store-ll.h"
 #include "object.h"
-#include "tag.h"
 #include "string-list.h"
 #include "parse-options.h"
 
@@ -239,7 +238,7 @@ static int cmd_show_ref__exists(const char **refs)
        if (refs_read_raw_ref(get_main_ref_store(the_repository), ref,
                              &unused_oid, &unused_referent, &unused_type,
                              &failure_errno)) {
-               if (failure_errno == ENOENT) {
+               if (failure_errno == ENOENT || failure_errno == EISDIR) {
                        error(_("reference does not exist"));
                        ret = 2;
                } else {
@@ -315,9 +314,9 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, show_ref_options,
                             show_ref_usage, 0);
 
-       if ((!!exclude_existing_opts.enabled + !!verify + !!exists) > 1)
-               die(_("only one of '%s', '%s' or '%s' can be given"),
-                   "--exclude-existing", "--verify", "--exists");
+       die_for_incompatible_opt3(exclude_existing_opts.enabled, "--exclude-existing",
+                                 verify, "--verify",
+                                 exists, "--exists");
 
        if (exclude_existing_opts.enabled)
                return cmd_show_ref__exclude_existing(&exclude_existing_opts);
index 5c8ffb1f7598b056fadcdd8fb98182d34928e6f3..0f52e252493d0d56fd0bcf5a7b9f92a416e86348 100644 (file)
@@ -8,14 +8,10 @@
 #include "parse-options.h"
 #include "pathspec.h"
 #include "repository.h"
-#include "run-command.h"
 #include "strbuf.h"
 #include "string-list.h"
-#include "cache-tree.h"
 #include "lockfile.h"
-#include "resolve-undo.h"
 #include "unpack-trees.h"
-#include "wt-status.h"
 #include "quote.h"
 #include "setup.h"
 #include "sparse-index.h"
@@ -777,8 +773,7 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix)
 
        argc = parse_options(argc, argv, prefix,
                             builtin_sparse_checkout_add_options,
-                            builtin_sparse_checkout_add_usage,
-                            PARSE_OPT_KEEP_UNKNOWN_OPT);
+                            builtin_sparse_checkout_add_usage, 0);
 
        sanitize_paths(argc, argv, prefix, add_opts.skip_checks);
 
@@ -824,8 +819,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
 
        argc = parse_options(argc, argv, prefix,
                             builtin_sparse_checkout_set_options,
-                            builtin_sparse_checkout_set_usage,
-                            PARSE_OPT_KEEP_UNKNOWN_OPT);
+                            builtin_sparse_checkout_set_usage, 0);
 
        if (update_modes(&set_opts.cone_mode, &set_opts.sparse_index))
                return 1;
@@ -835,7 +829,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
         * non-cone mode, if nothing is specified, manually select just the
         * top-level directory (much as 'init' would do).
         */
-       if (!core_sparse_checkout_cone && argc == 0) {
+       if (!core_sparse_checkout_cone && !set_opts.use_stdin && argc == 0) {
                argv = default_patterns;
                argc = default_patterns_nr;
        } else {
@@ -996,8 +990,7 @@ static int sparse_checkout_check_rules(int argc, const char **argv, const char *
 
        argc = parse_options(argc, argv, prefix,
                             builtin_sparse_checkout_check_rules_options,
-                            builtin_sparse_checkout_check_rules_usage,
-                            PARSE_OPT_KEEP_UNKNOWN_OPT);
+                            builtin_sparse_checkout_check_rules_usage, 0);
 
        if (check_rules_opts.rules_file && check_rules_opts.cone_mode < 0)
                check_rules_opts.cone_mode = 1;
index 4a6771c9f4c493a5dd41df2bf285f40784fa8cff..b2813c614cb2cf084ab28b48c1099209865e36b7 100644 (file)
@@ -26,7 +26,6 @@
 #include "sparse-index.h"
 #include "log-tree.h"
 #include "diffcore.h"
-#include "exec-cmd.h"
 #include "reflog.h"
 #include "add-interactive.h"
 
index cce46450abe95ea6a24d28026a1686cb930cb75a..fda50f2af1e33277161f819f560b4d8164d284a0 100644 (file)
@@ -22,7 +22,6 @@
 #include "remote.h"
 #include "refs.h"
 #include "refspec.h"
-#include "connect.h"
 #include "revision.h"
 #include "diffcore.h"
 #include "diff.h"
index 3918eacbb57bd9e0d2b90ecbbf6cc07c16fad73b..f036cf32f5fafffba426e7e050f93ca39efc0eb2 100644 (file)
@@ -18,7 +18,6 @@
 #include "object-store-ll.h"
 #include "path.h"
 #include "tag.h"
-#include "run-command.h"
 #include "parse-options.h"
 #include "diff.h"
 #include "revision.h"
@@ -44,18 +43,11 @@ 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)
 {
-       struct ref_array array;
-       struct strbuf output = STRBUF_INIT;
-       struct strbuf err = STRBUF_INIT;
        char *to_free = NULL;
-       int i;
-
-       memset(&array, 0, sizeof(array));
 
        if (filter->lines == -1)
                filter->lines = 0;
@@ -73,23 +65,8 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
        if (verify_ref_format(format))
                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++) {
-               strbuf_reset(&output);
-               strbuf_reset(&err);
-               if (format_ref_array_item(array.items[i], format, &output, &err))
-                       die("%s", err.buf);
-               fwrite(output.buf, 1, output.len, stdout);
-               if (output.len || !omit_empty)
-                       putchar('\n');
-       }
+       filter_and_format_refs(filter, FILTER_REFS_TAGS, sorting, format);
 
-       strbuf_release(&err);
-       strbuf_release(&output);
-       ref_array_clear(&array);
        free(to_free);
 
        return 0;
@@ -481,7 +458,7 @@ 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,
+               OPT_BOOL(0, "omit-empty",  &format.array_opts.omit_empty,
                        N_("do not output a newline after empty formatted refs")),
                OPT_REF_SORT(&sorting_options),
                {
@@ -501,7 +478,13 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 
        setup_ref_filter_porcelain_msg();
 
+       /*
+        * Try to set sort keys from config. If config does not set any,
+        * fall back on default (refname) sorting.
+        */
        git_config(git_tag_config, &sorting_options);
+       if (!sorting_options.nr)
+               string_list_append(&sorting_options, "refname");
 
        memset(&opt, 0, sizeof(opt));
        filter.lines = -1;
index fef7423448804006a04286ce18adedbaaf950229..e0a701f2b383bbbd95fc1e7fef3fec30c3783c37 100644 (file)
 #include "delta.h"
 #include "pack.h"
 #include "blob.h"
-#include "commit.h"
 #include "replace-object.h"
 #include "strbuf.h"
-#include "tag.h"
-#include "tree.h"
-#include "tree-walk.h"
 #include "progress.h"
 #include "decorate.h"
 #include "fsck.h"
index c0c4e65e6fb1986fadf2e98f0d19449e95905fe6..61338a01ecfc0ee4f029215149651bb9ac03da1e 100644 (file)
@@ -7,7 +7,6 @@
 #include "parse-options.h"
 #include "quote.h"
 #include "repository.h"
-#include "strvec.h"
 
 static const char * const git_update_ref_usage[] = {
        N_("git update-ref [<options>] -d <refname> [<old-val>]"),
index 8cf7dd9e2e5cef1d9238c22ed64ea07440d31528..cf5567208a277a62b872a1846919060fb6e276c3 100644 (file)
@@ -90,7 +90,7 @@ static char *git_config_val_global(int ident_flag UNUSED)
        char *user, *xdg;
        size_t unused;
 
-       git_global_config(&user, &xdg);
+       git_global_config_paths(&user, &xdg);
        if (xdg && *xdg) {
                normalize_path_copy(xdg, xdg);
                strbuf_addf(&buf, "%s\n", xdg);
index 9680b5870130615b7b4432a5ce456279db750aa4..0d2b9aea2aedca39b18d15bc7a8719669d63f1d3 100644 (file)
@@ -9,10 +9,8 @@
 #include "config.h"
 #include "gettext.h"
 #include "object-name.h"
-#include "object-store-ll.h"
 #include "repository.h"
 #include "commit.h"
-#include "run-command.h"
 #include "parse-options.h"
 #include "gpg-interface.h"
 
index d8753270ebee4df6eed4b2dd5037f3fee97eb2c6..c731e2f87b4ee35e4910e3a8c11e18b8e68e58c1 100644 (file)
@@ -9,7 +9,6 @@
 #include "config.h"
 #include "gettext.h"
 #include "tag.h"
-#include "run-command.h"
 #include "object-name.h"
 #include "parse-options.h"
 #include "gpg-interface.h"
index 62b7e26f4bdb17f524914ab6e60143915d8f2565..6d7da11746a25ade659a208db13fe8c0bcd4e36c 100644 (file)
        _("No possible source branch, inferring '--orphan'")
 
 #define WORKTREE_ADD_ORPHAN_WITH_DASH_B_HINT_TEXT \
-       _("If you meant to create a worktree containing a new orphan branch\n" \
+       _("If you meant to create a worktree containing a new unborn branch\n" \
        "(branch with no commits) for this repository, you can do so\n" \
        "using the --orphan flag:\n" \
        "\n" \
        "    git worktree add --orphan -b %s %s\n")
 
 #define WORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT \
-       _("If you meant to create a worktree containing a new orphan branch\n" \
+       _("If you meant to create a worktree containing a new unborn branch\n" \
        "(branch with no commits) for this repository, you can do so\n" \
        "using the --orphan flag:\n" \
        "\n" \
@@ -416,7 +416,6 @@ static int add_worktree(const char *path, const char *refname,
        struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
        struct strbuf sb = STRBUF_INIT, realpath = STRBUF_INIT;
        const char *name;
-       struct child_process cp = CHILD_PROCESS_INIT;
        struct strvec child_env = STRVEC_INIT;
        unsigned int counter = 0;
        int len, ret;
@@ -424,7 +423,8 @@ static int add_worktree(const char *path, const char *refname,
        struct commit *commit = NULL;
        int is_branch = 0;
        struct strbuf sb_name = STRBUF_INIT;
-       struct worktree **worktrees;
+       struct worktree **worktrees, *wt = NULL;
+       struct ref_store *wt_refs;
 
        worktrees = get_worktrees();
        check_candidate_path(path, opts->force, worktrees, "add");
@@ -495,20 +495,32 @@ static int add_worktree(const char *path, const char *refname,
        strbuf_realpath(&realpath, get_git_common_dir(), 1);
        write_file(sb_git.buf, "gitdir: %s/worktrees/%s",
                   realpath.buf, name);
-       /*
-        * This is to keep resolve_ref() happy. We need a valid HEAD
-        * or is_git_directory() will reject the directory. Any value which
-        * looks like an object ID will do since it will be immediately
-        * replaced by the symbolic-ref or update-ref invocation in the new
-        * worktree.
-        */
-       strbuf_reset(&sb);
-       strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
-       write_file(sb.buf, "%s", oid_to_hex(null_oid()));
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
        write_file(sb.buf, "../..");
 
+       /*
+        * Set up the ref store of the worktree and create the HEAD reference.
+        */
+       wt = get_linked_worktree(name, 1);
+       if (!wt) {
+               ret = error(_("could not find created worktree '%s'"), name);
+               goto done;
+       }
+       wt_refs = get_worktree_ref_store(wt);
+
+       ret = refs_init_db(wt_refs, REFS_INIT_DB_IS_WORKTREE, &sb);
+       if (ret)
+               goto done;
+
+       if (!is_branch && commit)
+               ret = refs_update_ref(wt_refs, NULL, "HEAD", &commit->object.oid,
+                                     NULL, 0, UPDATE_REFS_MSG_ON_ERR);
+       else
+               ret = refs_create_symref(wt_refs, "HEAD", symref.buf, NULL);
+       if (ret)
+               goto done;
+
        /*
         * If the current worktree has sparse-checkout enabled, then copy
         * the sparse-checkout patterns from the current worktree.
@@ -526,22 +538,6 @@ static int add_worktree(const char *path, const char *refname,
 
        strvec_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
        strvec_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
-       cp.git_cmd = 1;
-
-       if (!is_branch && commit) {
-               strvec_pushl(&cp.args, "update-ref", "HEAD",
-                            oid_to_hex(&commit->object.oid), NULL);
-       } else {
-               strvec_pushl(&cp.args, "symbolic-ref", "HEAD",
-                            symref.buf, NULL);
-               if (opts->quiet)
-                       strvec_push(&cp.args, "--quiet");
-       }
-
-       strvec_pushv(&cp.env, child_env.v);
-       ret = run_command(&cp);
-       if (ret)
-               goto done;
 
        if (opts->orphan &&
            (ret = make_worktree_orphan(refname, opts, &child_env)))
@@ -587,6 +583,7 @@ done:
        strbuf_release(&sb_git);
        strbuf_release(&sb_name);
        strbuf_release(&realpath);
+       free_worktree(wt);
        return ret;
 }
 
@@ -730,11 +727,11 @@ static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote)
        }
 
        if (opt_track) {
-               die(_("'%s' and '%s' cannot be used together"), "--orphan",
-                   "--track");
+               die(_("options '%s' and '%s' cannot be used together"),
+                   "--orphan", "--track");
        } else if (!opts->checkout) {
-               die(_("'%s' and '%s' cannot be used together"), "--orphan",
-                   "--no-checkout");
+               die(_("options '%s' and '%s' cannot be used together"),
+                   "--orphan", "--no-checkout");
        }
        return 1;
 }
@@ -784,7 +781,7 @@ static int add(int ac, const char **av, const char *prefix)
                           N_("create a new branch")),
                OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
                           N_("create or reset a branch")),
-               OPT_BOOL(0, "orphan", &opts.orphan, N_("create unborn/orphaned branch")),
+               OPT_BOOL(0, "orphan", &opts.orphan, N_("create unborn branch")),
                OPT_BOOL('d', "detach", &opts.detach, N_("detach HEAD at named commit")),
                OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
                OPT_BOOL(0, "lock", &keep_locked, N_("keep the new working tree locked")),
@@ -806,16 +803,17 @@ static int add(int ac, const char **av, const char *prefix)
        if (!!opts.detach + !!new_branch + !!new_branch_force > 1)
                die(_("options '%s', '%s', and '%s' cannot be used together"), "-b", "-B", "--detach");
        if (opts.detach && opts.orphan)
-               die(_("options '%s', and '%s' cannot be used together"),
+               die(_("options '%s' and '%s' cannot be used together"),
                    "--orphan", "--detach");
        if (opts.orphan && opt_track)
-               die(_("'%s' and '%s' cannot be used together"), "--orphan", "--track");
+               die(_("options '%s' and '%s' cannot be used together"),
+                   "--orphan", "--track");
        if (opts.orphan && !opts.checkout)
-               die(_("'%s' and '%s' cannot be used together"), "--orphan",
-                   "--no-checkout");
+               die(_("options '%s' and '%s' cannot be used together"),
+                   "--orphan", "--no-checkout");
        if (opts.orphan && ac == 2)
-               die(_("'%s' and '%s' cannot be used together"), "--orphan",
-                   _("<commit-ish>"));
+               die(_("option '%s' and commit-ish cannot be used together"),
+                   "--orphan");
        if (lock_reason && !keep_locked)
                die(_("the option '%s' requires '%s'"), "--reason", "--lock");
        if (lock_reason)
index 6ce62999e58523e20a686801446daaf0d924e9be..eb46b8863793e25b82ca6df27eb435476c4807a5 100644 (file)
@@ -11,7 +11,6 @@
 #include "csum-file.h"
 #include "pack.h"
 #include "strbuf.h"
-#include "string-list.h"
 #include "tmp-objdir.h"
 #include "packfile.h"
 #include "object-file.h"
index 8492fffd2f759f97e750063c4de0eefdc0763d99..ca32050a78fd7570adea76db264c5600339caf82 100644 (file)
@@ -4,7 +4,6 @@
 #include "copy.h"
 #include "environment.h"
 #include "gettext.h"
-#include "object-store-ll.h"
 #include "refs.h"
 #include "run-command.h"
 #include "hashmap.h"
index 641427ed410af39be80be22460a103dbad2d3d35..64678fe19930402b8400cb05d3ccaad5bb5b00a6 100644 (file)
@@ -11,7 +11,6 @@
 #include "read-cache-ll.h"
 #include "replace-object.h"
 #include "promisor-remote.h"
-#include "sparse-index.h"
 #include "trace.h"
 #include "trace2.h"
 
index 4f407530d3057552177883ad6b088f0a65b37b9a..b4e22de3cb9c351098cc57105c12e6242d1a4384 100755 (executable)
@@ -37,15 +37,13 @@ macos-*)
        test -z "$BREW_INSTALL_PACKAGES" ||
        brew install $BREW_INSTALL_PACKAGES
        brew link --force gettext
-       mkdir -p $HOME/bin
-       (
-               cd $HOME/bin
+
+       mkdir -p "$P4_PATH"
+       pushd "$P4_PATH"
                wget -q "$P4WHENCE/bin.macosx1015x86_64/helix-core-server.tgz" &&
                tar -xf helix-core-server.tgz &&
                sudo xattr -d com.apple.quarantine p4 p4d 2>/dev/null || true
-       )
-       PATH="$PATH:${HOME}/bin"
-       export PATH
+       popd
 
        if test -n "$CC_PACKAGE"
        then
index 78b7e326da6d8b6b3d1923007d3f22dea9814b8d..eb2c9e1eca73d3beb19ad0d20087f50af13fc62a 100755 (executable)
@@ -3,6 +3,10 @@
 # Install dependencies required to build and test Git inside container
 #
 
+. ${0%/*}/lib.sh
+
+begin_group "Install dependencies"
+
 case "$jobname" in
 linux32)
        linux32 --32bit i386 sh -c '
@@ -12,11 +16,31 @@ linux32)
        '
        ;;
 linux-musl)
-       apk add --update build-base curl-dev openssl-dev expat-dev gettext \
-               pcre2-dev python3 musl-libintl perl-utils ncurses >/dev/null
+       apk add --update shadow sudo build-base curl-dev openssl-dev expat-dev gettext \
+               pcre2-dev python3 musl-libintl perl-utils ncurses \
+               apache2 apache2-http2 apache2-proxy apache2-ssl apache2-webdav apr-util-dbd_sqlite3 \
+               bash cvs gnupg perl-cgi perl-dbd-sqlite >/dev/null
+       ;;
+linux-*|StaticAnalysis)
+       # Required so that apt doesn't wait for user input on certain packages.
+       export DEBIAN_FRONTEND=noninteractive
+
+       apt update -q &&
+       apt install -q -y sudo git make language-pack-is libsvn-perl apache2 libssl-dev \
+               libcurl4-openssl-dev libexpat-dev tcl tk gettext zlib1g-dev \
+               perl-modules liberror-perl libauthen-sasl-perl libemail-valid-perl \
+               libdbd-sqlite3-perl libio-socket-ssl-perl libnet-smtp-ssl-perl ${CC_PACKAGE:-${CC:-gcc}} \
+               apache2 cvs cvsps gnupg libcgi-pm-perl subversion
+
+       if test "$jobname" = StaticAnalysis
+       then
+               apt install -q -y coccinelle
+       fi
        ;;
 pedantic)
        dnf -yq update >/dev/null &&
        dnf -yq install make gcc findutils diffutils perl python3 gettext zlib-devel expat-devel openssl-devel curl-devel pcre2-devel >/dev/null
        ;;
 esac
+
+end_group "Install dependencies"
index bc0b23099df29e252aefa1c740f83922658e2cbf..d5dd2f269762a239704bf2795c3e3919790ba6a2 100755 (executable)
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -1,16 +1,7 @@
 # Library of functions shared by all CI scripts
 
-if test true != "$GITHUB_ACTIONS"
+if test true = "$GITHUB_ACTIONS"
 then
-       begin_group () { :; }
-       end_group () { :; }
-
-       group () {
-               shift
-               "$@"
-       }
-       set -x
-else
        begin_group () {
                need_to_end_group=t
                echo "::group::$1" >&2
@@ -23,27 +14,50 @@ else
                need_to_end_group=
                echo '::endgroup::' >&2
        }
-       trap end_group EXIT
+elif test true = "$GITLAB_CI"
+then
+       begin_group () {
+               need_to_end_group=t
+               printf "\e[0Ksection_start:$(date +%s):$(echo "$1" | tr ' ' _)\r\e[0K$1\n"
+               trap "end_group '$1'" EXIT
+               set -x
+       }
 
-       group () {
+       end_group () {
+               test -n "$need_to_end_group" || return 0
                set +x
-               begin_group "$1"
-               shift
-               # work around `dash` not supporting `set -o pipefail`
-               (
-                       "$@" 2>&1
-                       echo $? >exit.status
-               ) |
-               sed 's/^\(\([^ ]*\):\([0-9]*\):\([0-9]*:\) \)\(error\|warning\): /::\5 file=\2,line=\3::\1/'
-               res=$(cat exit.status)
-               rm exit.status
-               end_group
-               return $res
+               need_to_end_group=
+               printf "\e[0Ksection_end:$(date +%s):$(echo "$1" | tr ' ' _)\r\e[0K\n"
+               trap - EXIT
        }
+else
+       begin_group () { :; }
+       end_group () { :; }
 
-       begin_group "CI setup"
+       set -x
 fi
 
+group () {
+       group="$1"
+       shift
+       begin_group "$group"
+
+       # work around `dash` not supporting `set -o pipefail`
+       (
+               "$@" 2>&1
+               echo $? >exit.status
+       ) |
+       sed 's/^\(\([^ ]*\):\([0-9]*\):\([0-9]*:\) \)\(error\|warning\): /::\5 file=\2,line=\3::\1/'
+       res=$(cat exit.status)
+       rm exit.status
+
+       end_group "$group"
+       return $res
+}
+
+begin_group "CI setup"
+trap "end_group 'CI setup'" EXIT
+
 # Set 'exit on error' for all CI scripts to let the caller know that
 # something went wrong.
 #
@@ -71,10 +85,32 @@ skip_branch_tip_with_tag () {
        fi
 }
 
+# Check whether we can use the path passed via the first argument as Git
+# repository.
+is_usable_git_repository () {
+       # We require Git in our PATH, otherwise we cannot access repositories
+       # at all.
+       if ! command -v git >/dev/null
+       then
+               return 1
+       fi
+
+       # And the target directory needs to be a proper Git repository.
+       if ! git -C "$1" rev-parse 2>/dev/null
+       then
+               return 1
+       fi
+}
+
 # Save some info about the current commit's tree, so we can skip the build
 # job if we encounter the same tree again and can provide a useful info
 # message.
 save_good_tree () {
+       if ! is_usable_git_repository .
+       then
+               return
+       fi
+
        echo "$(git rev-parse $CI_COMMIT^{tree}) $CI_COMMIT $CI_JOB_NUMBER $CI_JOB_ID" >>"$good_trees_file"
        # limit the file size
        tail -1000 "$good_trees_file" >"$good_trees_file".tmp
@@ -90,6 +126,11 @@ skip_good_tree () {
                return
        fi
 
+       if ! is_usable_git_repository .
+       then
+               return
+       fi
+
        if ! good_tree_info="$(grep "^$(git rev-parse $CI_COMMIT^{tree}) " "$good_trees_file")"
        then
                # Haven't seen this tree yet, or no cached good trees file yet.
@@ -121,6 +162,11 @@ skip_good_tree () {
 }
 
 check_unignored_build_artifacts () {
+       if ! is_usable_git_repository .
+       then
+               return
+       fi
+
        ! git ls-files --other --exclude-standard --error-unmatch \
                -- ':/*' 2>/dev/null ||
        {
@@ -133,6 +179,26 @@ handle_failed_tests () {
        return 1
 }
 
+create_failed_test_artifacts () {
+       mkdir -p t/failed-test-artifacts
+
+       for test_exit in t/test-results/*.exit
+       do
+               test 0 != "$(cat "$test_exit")" || continue
+
+               test_name="${test_exit%.exit}"
+               test_name="${test_name##*/}"
+               printf "\\e[33m\\e[1m=== Failed test: ${test_name} ===\\e[m\\n"
+               echo "The full logs are in the 'print test failures' step below."
+               echo "See also the 'failed-tests-*' artifacts attached to this run."
+               cat "t/test-results/$test_name.markup"
+
+               trash_dir="t/trash directory.$test_name"
+               cp "t/test-results/$test_name.out" t/failed-test-artifacts/
+               tar czf t/failed-test-artifacts/"$test_name".trash.tar.gz "$trash_dir"
+       done
+}
+
 # GitHub Action doesn't set TERM, which is required by tput
 export TERM=${TERM:-dumb}
 
@@ -156,11 +222,8 @@ then
        # among *all* phases)
        cache_dir="$HOME/test-cache/$SYSTEM_PHASENAME"
 
-       export GIT_PROVE_OPTS="--timer --jobs 10 --state=failed,slow,save"
-       export GIT_TEST_OPTS="--verbose-log -x --write-junit-xml"
-       MAKEFLAGS="$MAKEFLAGS --jobs=10"
-       test windows_nt != "$CI_OS_NAME" ||
-       GIT_TEST_OPTS="--no-chain-lint --no-bin-wrappers $GIT_TEST_OPTS"
+       GIT_TEST_OPTS="--write-junit-xml"
+       JOBS=10
 elif test true = "$GITHUB_ACTIONS"
 then
        CI_TYPE=github-actions
@@ -173,40 +236,70 @@ then
        CC="${CC_PACKAGE:-${CC:-gcc}}"
        DONT_SKIP_TAGS=t
        handle_failed_tests () {
-               mkdir -p t/failed-test-artifacts
                echo "FAILED_TEST_ARTIFACTS=t/failed-test-artifacts" >>$GITHUB_ENV
+               create_failed_test_artifacts
+               return 1
+       }
+
+       cache_dir="$HOME/none"
 
-               for test_exit in t/test-results/*.exit
-               do
-                       test 0 != "$(cat "$test_exit")" || continue
-
-                       test_name="${test_exit%.exit}"
-                       test_name="${test_name##*/}"
-                       printf "\\e[33m\\e[1m=== Failed test: ${test_name} ===\\e[m\\n"
-                       echo "The full logs are in the 'print test failures' step below."
-                       echo "See also the 'failed-tests-*' artifacts attached to this run."
-                       cat "t/test-results/$test_name.markup"
-
-                       trash_dir="t/trash directory.$test_name"
-                       cp "t/test-results/$test_name.out" t/failed-test-artifacts/
-                       tar czf t/failed-test-artifacts/"$test_name".trash.tar.gz "$trash_dir"
-               done
+       GIT_TEST_OPTS="--github-workflow-markup"
+       JOBS=10
+elif test true = "$GITLAB_CI"
+then
+       CI_TYPE=gitlab-ci
+       CI_BRANCH="$CI_COMMIT_REF_NAME"
+       CI_COMMIT="$CI_COMMIT_SHA"
+       case "$CI_JOB_IMAGE" in
+       macos-*)
+               # GitLab CI has Python installed via multiple package managers,
+               # most notably via asdf and Homebrew. Ensure that our builds
+               # pick up the Homebrew one by prepending it to our PATH as the
+               # asdf one breaks tests.
+               export PATH="$(brew --prefix)/bin:$PATH"
+
+               CI_OS_NAME=osx
+               ;;
+       alpine:*|fedora:*|ubuntu:*)
+               CI_OS_NAME=linux;;
+       *)
+               echo "Could not identify OS image" >&2
+               env >&2
+               exit 1
+               ;;
+       esac
+       CI_REPO_SLUG="$CI_PROJECT_PATH"
+       CI_JOB_ID="$CI_JOB_ID"
+       CC="${CC_PACKAGE:-${CC:-gcc}}"
+       DONT_SKIP_TAGS=t
+       handle_failed_tests () {
+               create_failed_test_artifacts
                return 1
        }
 
        cache_dir="$HOME/none"
 
-       export GIT_PROVE_OPTS="--timer --jobs 10"
-       export GIT_TEST_OPTS="--verbose-log -x --github-workflow-markup"
-       MAKEFLAGS="$MAKEFLAGS --jobs=10"
-       test windows != "$CI_OS_NAME" ||
-       GIT_TEST_OPTS="--no-chain-lint --no-bin-wrappers $GIT_TEST_OPTS"
+       runs_on_pool=$(echo "$CI_JOB_IMAGE" | tr : -)
+       JOBS=$(nproc)
 else
        echo "Could not identify CI type" >&2
        env >&2
        exit 1
 fi
 
+MAKEFLAGS="$MAKEFLAGS --jobs=$JOBS"
+GIT_PROVE_OPTS="--timer --jobs $JOBS"
+
+GIT_TEST_OPTS="$GIT_TEST_OPTS --verbose-log -x"
+case "$CI_OS_NAME" in
+windows|windows_nt)
+       GIT_TEST_OPTS="$GIT_TEST_OPTS --no-chain-lint --no-bin-wrappers"
+       ;;
+esac
+
+export GIT_TEST_OPTS
+export GIT_PROVE_OPTS
+
 good_trees_file="$cache_dir/good-trees"
 
 mkdir -p "$cache_dir"
@@ -258,6 +351,9 @@ macos-*)
        then
                MAKEFLAGS="$MAKEFLAGS APPLE_COMMON_CRYPTO_SHA1=Yes"
        fi
+
+       P4_PATH="$HOME/custom/p4"
+       export PATH="$P4_PATH:$PATH"
        ;;
 esac
 
@@ -285,5 +381,5 @@ esac
 
 MAKEFLAGS="$MAKEFLAGS CC=${CC:-cc}"
 
-end_group
+end_group "CI setup"
 set -x
index 57277eefcd0c8b6117fd71e9adb3a526baa05ef1..b1f80aeac345dd70746b02b6ca1b5282a0c2a4aa 100755 (executable)
@@ -8,7 +8,7 @@
 # Tracing executed commands would produce too much noise in the loop below.
 set +x
 
-cd t/
+cd "${TEST_OUTPUT_DIRECTORY:-t/}"
 
 if ! ls test-results/*.exit >/dev/null 2>/dev/null
 then
@@ -51,6 +51,12 @@ do
                        tar czf failed-test-artifacts/"$test_name".trash.tar.gz "$trash_dir"
                        continue
                        ;;
+               gitlab-ci)
+                       mkdir -p failed-test-artifacts
+                       cp "${TEST_EXIT%.exit}.out" failed-test-artifacts/
+                       tar czf failed-test-artifacts/"$test_name".trash.tar.gz "$trash_dir"
+                       continue
+                       ;;
                *)
                        echo "Unhandled CI type: $CI_TYPE" >&2
                        exit 1
diff --git a/ci/run-build-and-minimal-fuzzers.sh b/ci/run-build-and-minimal-fuzzers.sh
new file mode 100755 (executable)
index 0000000..8ba486f
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh
+#
+# Build and test Git's fuzzers
+#
+
+. ${0%/*}/lib.sh
+
+group "Build fuzzers" make \
+       CC=clang \
+       CXX=clang++ \
+       CFLAGS="-fsanitize=fuzzer-no-link,address" \
+       LIB_FUZZING_ENGINE="-fsanitize=fuzzer,address" \
+       fuzz-all
+
+for fuzzer in commit-graph date pack-headers pack-idx ; do
+       begin_group "fuzz-$fuzzer"
+       ./oss-fuzz/fuzz-$fuzzer -verbosity=0 -runs=1 || exit 1
+       end_group "fuzz-$fuzzer"
+done
index 2528f25e31d3c860d1f2dc435af320a4f1b4973c..7a1466b8687f6b24ec370f6577c930cf86621cf8 100755 (executable)
@@ -50,6 +50,8 @@ if test -n "$run_tests"
 then
        group "Run tests" make test ||
        handle_failed_tests
+       group "Run unit tests" \
+               make DEFAULT_UNIT_TEST_TARGET=unit-tests-prove unit-tests
 fi
 check_unignored_build_artifacts
 
index a3c67956a8df8f803bc81eee1b86a8f5559db57b..ae8094382fe418dbd807dbc2156e16b814aa2adc 100755 (executable)
@@ -15,4 +15,9 @@ group "Run tests" make --quiet -C t T="$(cd t &&
        tr '\n' ' ')" ||
 handle_failed_tests
 
+# We only have one unit test at the moment, so run it in the first slice
+if [ "$1" == "0" ] ; then
+       group "Run unit tests" make --quiet -C t unit-tests-prove
+fi
+
 check_unignored_build_artifacts
index f90f442482932e618cc7399924c00ddde8c50eaf..db94581f72443c28fd47535db78e39b5ad021a86 100644 (file)
@@ -2,7 +2,6 @@
 #include "object-store-ll.h"
 #include "commit.h"
 #include "convert.h"
-#include "blob.h"
 #include "diff.h"
 #include "diffcore.h"
 #include "environment.h"
index 54b2a50f5f1dfeb2121f73a43417a7855d6fe1f4..c4cd0f352b8833bc7362a5f685f220ec5dd888b0 100644 (file)
@@ -160,6 +160,7 @@ git-reflog                              ancillarymanipulators           complete
 git-remote                              ancillarymanipulators           complete
 git-repack                              ancillarymanipulators           complete
 git-replace                             ancillarymanipulators           complete
+git-replay                              plumbingmanipulators
 git-request-pull                        foreignscminterface             complete
 git-rerere                              ancillaryinterrogators
 git-reset                               mainporcelain           history
index ee66098e077d89f293b9527c7689865904f4a12f..45417d7412202e54d21de0ae49941536eb2c82b4 100644 (file)
@@ -1,14 +1,13 @@
 #include "git-compat-util.h"
 #include "config.h"
+#include "csum-file.h"
 #include "gettext.h"
 #include "hex.h"
 #include "lockfile.h"
-#include "pack.h"
 #include "packfile.h"
 #include "commit.h"
 #include "object.h"
 #include "refs.h"
-#include "revision.h"
 #include "hash-lookup.h"
 #include "commit-graph.h"
 #include "object-file.h"
@@ -275,68 +274,37 @@ struct commit_graph *load_commit_graph_one_fd_st(struct repository *r,
        return ret;
 }
 
-static int verify_commit_graph_lite(struct commit_graph *g)
+static int graph_read_oid_fanout(const unsigned char *chunk_start,
+                                size_t chunk_size, void *data)
 {
+       struct commit_graph *g = data;
        int i;
 
-       /*
-        * Basic validation shared between parse_commit_graph()
-        * which'll be called every time the graph is used, and the
-        * much more expensive verify_commit_graph() used by
-        * "commit-graph verify".
-        *
-        * There should only be very basic checks here to ensure that
-        * we don't e.g. segfault in fill_commit_in_graph(), but
-        * because this is a very hot codepath nothing that e.g. loops
-        * over g->num_commits, or runs a checksum on the commit-graph
-        * itself.
-        */
-       if (!g->chunk_oid_fanout) {
-               error("commit-graph is missing the OID Fanout chunk");
-               return 1;
-       }
-       if (!g->chunk_oid_lookup) {
-               error("commit-graph is missing the OID Lookup chunk");
-               return 1;
-       }
-       if (!g->chunk_commit_data) {
-               error("commit-graph is missing the Commit Data chunk");
-               return 1;
-       }
+       if (chunk_size != 256 * sizeof(uint32_t))
+               return error(_("commit-graph oid fanout chunk is wrong size"));
+       g->chunk_oid_fanout = (const uint32_t *)chunk_start;
+       g->num_commits = ntohl(g->chunk_oid_fanout[255]);
 
        for (i = 0; i < 255; i++) {
                uint32_t oid_fanout1 = ntohl(g->chunk_oid_fanout[i]);
                uint32_t oid_fanout2 = ntohl(g->chunk_oid_fanout[i + 1]);
 
                if (oid_fanout1 > oid_fanout2) {
-                       error("commit-graph fanout values out of order");
+                       error(_("commit-graph fanout values out of order"));
                        return 1;
                }
        }
-       if (ntohl(g->chunk_oid_fanout[255]) != g->num_commits) {
-               error("commit-graph oid table and fanout disagree on size");
-               return 1;
-       }
 
        return 0;
 }
 
-static int graph_read_oid_fanout(const unsigned char *chunk_start,
-                                size_t chunk_size, void *data)
-{
-       struct commit_graph *g = data;
-       if (chunk_size != 256 * sizeof(uint32_t))
-               return error("commit-graph oid fanout chunk is wrong size");
-       g->chunk_oid_fanout = (const uint32_t *)chunk_start;
-       return 0;
-}
-
 static int graph_read_oid_lookup(const unsigned char *chunk_start,
                                 size_t chunk_size, void *data)
 {
        struct commit_graph *g = data;
        g->chunk_oid_lookup = chunk_start;
-       g->num_commits = chunk_size / g->hash_len;
+       if (chunk_size / g->hash_len != g->num_commits)
+               return error(_("commit-graph OID lookup chunk is the wrong size"));
        return 0;
 }
 
@@ -344,8 +312,8 @@ static int graph_read_commit_data(const unsigned char *chunk_start,
                                  size_t chunk_size, void *data)
 {
        struct commit_graph *g = data;
-       if (chunk_size != g->num_commits * GRAPH_DATA_WIDTH)
-               return error("commit-graph commit data chunk is wrong size");
+       if (chunk_size / GRAPH_DATA_WIDTH != g->num_commits)
+               return error(_("commit-graph commit data chunk is wrong size"));
        g->chunk_commit_data = chunk_start;
        return 0;
 }
@@ -354,8 +322,8 @@ static int graph_read_generation_data(const unsigned char *chunk_start,
                                      size_t chunk_size, void *data)
 {
        struct commit_graph *g = data;
-       if (chunk_size != g->num_commits * sizeof(uint32_t))
-               return error("commit-graph generations chunk is wrong size");
+       if (chunk_size / sizeof(uint32_t) != g->num_commits)
+               return error(_("commit-graph generations chunk is wrong size"));
        g->chunk_generation_data = chunk_start;
        return 0;
 }
@@ -364,8 +332,8 @@ static int graph_read_bloom_index(const unsigned char *chunk_start,
                                  size_t chunk_size, void *data)
 {
        struct commit_graph *g = data;
-       if (chunk_size != g->num_commits * 4) {
-               warning("commit-graph changed-path index chunk is too small");
+       if (chunk_size / 4 != g->num_commits) {
+               warning(_("commit-graph changed-path index chunk is too small"));
                return -1;
        }
        g->chunk_bloom_indexes = chunk_start;
@@ -379,8 +347,8 @@ static int graph_read_bloom_data(const unsigned char *chunk_start,
        uint32_t hash_version;
 
        if (chunk_size < BLOOMDATA_CHUNK_HEADER_SIZE) {
-               warning("ignoring too-small changed-path chunk"
-                       " (%"PRIuMAX" < %"PRIuMAX") in commit-graph file",
+               warning(_("ignoring too-small changed-path chunk"
+                       " (%"PRIuMAX" < %"PRIuMAX") in commit-graph file"),
                        (uintmax_t)chunk_size,
                        (uintmax_t)BLOOMDATA_CHUNK_HEADER_SIZE);
                return -1;
@@ -462,9 +430,19 @@ struct commit_graph *parse_commit_graph(struct repo_settings *s,
                                   GRAPH_HEADER_SIZE, graph->num_chunks, 1))
                goto free_and_return;
 
-       read_chunk(cf, GRAPH_CHUNKID_OIDFANOUT, graph_read_oid_fanout, graph);
-       read_chunk(cf, GRAPH_CHUNKID_OIDLOOKUP, graph_read_oid_lookup, graph);
-       read_chunk(cf, GRAPH_CHUNKID_DATA, graph_read_commit_data, graph);
+       if (read_chunk(cf, GRAPH_CHUNKID_OIDFANOUT, graph_read_oid_fanout, graph)) {
+               error(_("commit-graph required OID fanout chunk missing or corrupted"));
+               goto free_and_return;
+       }
+       if (read_chunk(cf, GRAPH_CHUNKID_OIDLOOKUP, graph_read_oid_lookup, graph)) {
+               error(_("commit-graph required OID lookup chunk missing or corrupted"));
+               goto free_and_return;
+       }
+       if (read_chunk(cf, GRAPH_CHUNKID_DATA, graph_read_commit_data, graph)) {
+               error(_("commit-graph required commit data chunk missing or corrupted"));
+               goto free_and_return;
+       }
+
        pair_chunk(cf, GRAPH_CHUNKID_EXTRAEDGES, &graph->chunk_extra_edges,
                   &graph->chunk_extra_edges_size);
        pair_chunk(cf, GRAPH_CHUNKID_BASE, &graph->chunk_base_graphs,
@@ -499,9 +477,6 @@ struct commit_graph *parse_commit_graph(struct repo_settings *s,
 
        oidread(&graph->oid, graph->data + graph->data_len - graph->hash_len);
 
-       if (verify_commit_graph_lite(graph))
-               goto free_and_return;
-
        free_chunkfile(cf);
        return graph;
 
@@ -629,7 +604,7 @@ int open_commit_graph_chain(const char *chain_file,
                        /* treat empty files the same as missing */
                        errno = ENOENT;
                } else {
-                       warning("commit-graph chain file too small");
+                       warning(_("commit-graph chain file too small"));
                        errno = EINVAL;
                }
                return 0;
@@ -831,6 +806,9 @@ struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r)
 
 void close_commit_graph(struct raw_object_store *o)
 {
+       if (!o->commit_graph)
+               return;
+
        clear_commit_graph_data_slab(&commit_graph_data_slab);
        free_commit_graph(o->commit_graph);
        o->commit_graph = NULL;
@@ -970,7 +948,7 @@ static int fill_commit_in_graph(struct repository *r,
        parent_data_pos = edge_value & GRAPH_EDGE_LAST_MASK;
        do {
                if (g->chunk_extra_edges_size / sizeof(uint32_t) <= parent_data_pos) {
-                       error("commit-graph extra-edges pointer out of bounds");
+                       error(_("commit-graph extra-edges pointer out of bounds"));
                        free_commit_list(item->parents);
                        item->parents = NULL;
                        item->object.parsed = 0;
@@ -1029,7 +1007,7 @@ struct commit *lookup_commit_in_graph(struct repository *repo, const struct obje
        uint32_t pos;
 
        if (commit_graph_paranoia == -1)
-               commit_graph_paranoia = git_env_bool(GIT_COMMIT_GRAPH_PARANOIA, 1);
+               commit_graph_paranoia = git_env_bool(GIT_COMMIT_GRAPH_PARANOIA, 0);
 
        if (!prepare_commit_graph(repo))
                return NULL;
@@ -2641,19 +2619,16 @@ cleanup:
        oid_array_clear(&ctx->oids);
        clear_topo_level_slab(&topo_levels);
 
-       if (ctx->commit_graph_filenames_after) {
-               for (i = 0; i < ctx->num_commit_graphs_after; i++) {
-                       free(ctx->commit_graph_filenames_after[i]);
-                       free(ctx->commit_graph_hash_after[i]);
-               }
-
-               for (i = 0; i < ctx->num_commit_graphs_before; i++)
-                       free(ctx->commit_graph_filenames_before[i]);
+       for (i = 0; i < ctx->num_commit_graphs_before; i++)
+               free(ctx->commit_graph_filenames_before[i]);
+       free(ctx->commit_graph_filenames_before);
 
-               free(ctx->commit_graph_filenames_after);
-               free(ctx->commit_graph_filenames_before);
-               free(ctx->commit_graph_hash_after);
+       for (i = 0; i < ctx->num_commit_graphs_after; i++) {
+               free(ctx->commit_graph_filenames_after[i]);
+               free(ctx->commit_graph_hash_after[i]);
        }
+       free(ctx->commit_graph_filenames_after);
+       free(ctx->commit_graph_hash_after);
 
        free(ctx);
 
@@ -2690,10 +2665,6 @@ static int verify_one_commit_graph(struct repository *r,
        struct commit *seen_gen_zero = NULL;
        struct commit *seen_gen_non_zero = NULL;
 
-       verify_commit_graph_error = verify_commit_graph_lite(g);
-       if (verify_commit_graph_error)
-               return verify_commit_graph_error;
-
        if (!commit_graph_checksum_valid(g)) {
                graph_report(_("the commit-graph file has incorrect checksum and is likely corrupt"));
                verify_commit_graph_error = VERIFY_COMMIT_GRAPH_ERROR_HASH;
index a868a575ea1cf8e7ccd331610b9d309a065ad135..ecc913fc99ba9e6b88df675462a2b8f912cbd0ce 100644 (file)
@@ -4,7 +4,6 @@
 #include "decorate.h"
 #include "hex.h"
 #include "prio-queue.h"
-#include "tree.h"
 #include "ref-filter.h"
 #include "revision.h"
 #include "tag.h"
index 8405d7c3fceab23c6eafd16e36d76c7172c44923..ef679a0b939046c4aac15567cdf3c0ae8c079d29 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -8,7 +8,6 @@
 #include "repository.h"
 #include "object-name.h"
 #include "object-store-ll.h"
-#include "pkt-line.h"
 #include "utf8.h"
 #include "diff.h"
 #include "revision.h"
@@ -23,7 +22,6 @@
 #include "advice.h"
 #include "refs.h"
 #include "commit-reach.h"
-#include "run-command.h"
 #include "setup.h"
 #include "shallow.h"
 #include "tree.h"
@@ -577,7 +575,7 @@ int repo_parse_commit_internal(struct repository *r,
                static int commit_graph_paranoia = -1;
 
                if (commit_graph_paranoia == -1)
-                       commit_graph_paranoia = git_env_bool(GIT_COMMIT_GRAPH_PARANOIA, 1);
+                       commit_graph_paranoia = git_env_bool(GIT_COMMIT_GRAPH_PARANOIA, 0);
 
                if (commit_graph_paranoia && !has_object(r, &item->object.oid, 0)) {
                        unparse_commit(r, &item->object.oid);
@@ -1783,7 +1781,7 @@ const char *find_commit_header(const char *msg, const char *key, size_t *out_len
  * Returns the number of bytes from the tail to ignore, to be fed as
  * the second parameter to append_signoff().
  */
-size_t ignore_non_trailer(const char *buf, size_t len)
+size_t ignored_log_message_bytes(const char *buf, size_t len)
 {
        size_t boc = 0;
        size_t bol = 0;
index 28928833c544081f9c08971f7a4450e97154ebb3..1cc872f225f438be7dc03a9b4dc8f207da0deb5b 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -294,8 +294,8 @@ const char *find_header_mem(const char *msg, size_t len,
 const char *find_commit_header(const char *msg, const char *key,
                               size_t *out_len);
 
-/* Find the end of the log message, the right place for a new trailer. */
-size_t ignore_non_trailer(const char *buf, size_t len);
+/* Find the number of bytes to ignore from the end of a log message. */
+size_t ignored_log_message_bytes(const char *buf, size_t len);
 
 typedef int (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra,
                                void *cb_data);
index 2d4e245beb18b8f3573ea33526274d3f8f45b675..2aa8c219acee4def1711e5f84d45571e34be4e16 100644 (file)
@@ -4,6 +4,7 @@
 #include "fsm-health.h"
 #include "fsmonitor--daemon.h"
 #include "gettext.h"
+#include "simple-ipc.h"
 
 /*
  * Every minute wake up and test our health.
index 11b56d3ef12ffb52ef291e95e2b8e39823e77f0f..2fc67442eb5e87e0b006f997fd6b597f9a919b99 100644 (file)
@@ -29,6 +29,7 @@
 #include "fsmonitor--daemon.h"
 #include "fsmonitor-path-utils.h"
 #include "gettext.h"
+#include "simple-ipc.h"
 #include "string-list.h"
 #include "trace.h"
 
index 90a2412284414e0675bc7603e4d13850d640a695..5a21dade7b8659aa4d6e9e3c697a7e124ef31ccb 100644 (file)
@@ -4,6 +4,7 @@
 #include "fsm-listen.h"
 #include "fsmonitor--daemon.h"
 #include "gettext.h"
+#include "simple-ipc.h"
 #include "trace2.h"
 
 /*
index ec5280da160135170e2d778cfcb19cea1752c211..238a84ddbaa1b4c7563f6950eca469949d77fa68 100644 (file)
@@ -255,6 +255,8 @@ int mingw_core_config(const char *var, const char *value,
        }
 
        if (!strcmp(var, "core.unsetenvvars")) {
+               if (!value)
+                       return config_error_nonbool(var);
                free(unset_environment_variables);
                unset_environment_variables = xstrdup(value);
                return 0;
@@ -2682,6 +2684,30 @@ static PSID get_current_user_sid(void)
        return result;
 }
 
+static BOOL user_sid_to_user_name(PSID sid, LPSTR *str)
+{
+       SID_NAME_USE pe_use;
+       DWORD len_user = 0, len_domain = 0;
+       BOOL translate_sid_to_user;
+
+       /*
+        * returns only FALSE, because the string pointers are NULL
+        */
+       LookupAccountSidA(NULL, sid, NULL, &len_user, NULL, &len_domain,
+                         &pe_use);
+       /*
+        * Alloc needed space of the strings
+        */
+       ALLOC_ARRAY((*str), (size_t)len_domain + (size_t)len_user);
+       translate_sid_to_user = LookupAccountSidA(NULL, sid,
+           (*str) + len_domain, &len_user, *str, &len_domain, &pe_use);
+       if (!translate_sid_to_user)
+               FREE_AND_NULL(*str);
+       else
+               (*str)[len_domain] = '/';
+       return translate_sid_to_user;
+}
+
 static int acls_supported(const char *path)
 {
        size_t offset = offset_1st_component(path);
@@ -2763,27 +2789,47 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
                        strbuf_addf(report, "'%s' is on a file system that does "
                                    "not record ownership\n", path);
                } else if (report) {
-                       LPSTR str1, str2, to_free1 = NULL, to_free2 = NULL;
+                       LPSTR str1, str2, str3, str4, to_free1 = NULL,
+                           to_free3 = NULL, to_local_free2 = NULL,
+                           to_local_free4 = NULL;
 
-                       if (ConvertSidToStringSidA(sid, &str1))
+                       if (user_sid_to_user_name(sid, &str1))
                                to_free1 = str1;
                        else
                                str1 = "(inconvertible)";
-
-                       if (!current_user_sid)
-                               str2 = "(none)";
-                       else if (!IsValidSid(current_user_sid))
-                               str2 = "(invalid)";
-                       else if (ConvertSidToStringSidA(current_user_sid, &str2))
-                               to_free2 = str2;
+                       if (ConvertSidToStringSidA(sid, &str2))
+                               to_local_free2 = str2;
                        else
                                str2 = "(inconvertible)";
+
+                       if (!current_user_sid) {
+                               str3 = "(none)";
+                               str4 = "(none)";
+                       }
+                       else if (!IsValidSid(current_user_sid)) {
+                               str3 = "(invalid)";
+                               str4 = "(invalid)";
+                       } else {
+                               if (user_sid_to_user_name(current_user_sid,
+                                                         &str3))
+                                       to_free3 = str3;
+                               else
+                                       str3 = "(inconvertible)";
+                               if (ConvertSidToStringSidA(current_user_sid,
+                                                          &str4))
+                                       to_local_free4 = str4;
+                               else
+                                       str4 = "(inconvertible)";
+                       }
                        strbuf_addf(report,
                                    "'%s' is owned by:\n"
-                                   "\t'%s'\nbut the current user is:\n"
-                                   "\t'%s'\n", path, str1, str2);
-                       LocalFree(to_free1);
-                       LocalFree(to_free2);
+                                   "\t%s (%s)\nbut the current user is:\n"
+                                   "\t%s (%s)\n",
+                                   path, str1, str2, str3, str4);
+                       free(to_free1);
+                       LocalFree(to_local_free2);
+                       free(to_free3);
+                       LocalFree(to_local_free4);
                }
        }
 
index e5e1dda8ccdb60320a05b064479595eb4eb4997c..cb176d966f287d98e44495db1797061c8c1102af 100644 (file)
@@ -1,8 +1,5 @@
 #include "git-compat-util.h"
 #include "simple-ipc.h"
-#include "strbuf.h"
-#include "pkt-line.h"
-#include "thread-utils.h"
 
 #ifndef SUPPORTS_SIMPLE_IPC
 /*
index b2f4f22ce44f51f6f8f18e0d7ca6a2ed22de183b..9b3f2cdf8c9608a5aae4dc3ebfd8fa208c5617de 100644 (file)
@@ -2,7 +2,6 @@
 #include "gettext.h"
 #include "simple-ipc.h"
 #include "strbuf.h"
-#include "pkt-line.h"
 #include "thread-utils.h"
 #include "trace2.h"
 #include "unix-socket.h"
index b330c7adb4a5efc3cac898391d4c722b019d95a1..3cfeb3d8bd99f4ca15d0f3a06cd4b1fe932f7f47 100644 (file)
--- a/config.c
+++ b/config.c
 #include "pager.h"
 #include "path.h"
 #include "utf8.h"
-#include "dir.h"
 #include "color.h"
-#include "replace-object.h"
 #include "refs.h"
 #include "setup.h"
 #include "strvec.h"
 #include "trace2.h"
 #include "wildmatch.h"
-#include "worktree.h"
 #include "ws.h"
 #include "write-or-die.h"
 
@@ -98,7 +95,6 @@ static long config_file_ftell(struct config_source *conf)
        return ftell(conf->u.file);
 }
 
-
 static int config_buf_fgetc(struct config_source *conf)
 {
        if (conf->u.buf.pos < conf->u.buf.len)
@@ -1386,10 +1382,15 @@ static int git_default_core_config(const char *var, const char *value,
                return 0;
        }
        if (!strcmp(var, "core.checkstat")) {
+               if (!value)
+                       return config_error_nonbool(var);
                if (!strcasecmp(value, "default"))
                        check_stat = 1;
                else if (!strcasecmp(value, "minimal"))
                        check_stat = 0;
+               else
+                       return error(_("invalid value for '%s': '%s'"),
+                                    var, value);
        }
 
        if (!strcmp(var, "core.quotepath")) {
@@ -1546,12 +1547,12 @@ static int git_default_core_config(const char *var, const char *value,
                return 0;
        }
 
-       if (!strcmp(var, "core.checkroundtripencoding")) {
-               check_roundtrip_encoding = xstrdup(value);
-               return 0;
-       }
+       if (!strcmp(var, "core.checkroundtripencoding"))
+               return git_config_string(&check_roundtrip_encoding, var, value);
 
        if (!strcmp(var, "core.notesref")) {
+               if (!value)
+                       return config_error_nonbool(var);
                notes_ref_name = xstrdup(value);
                return 0;
        }
@@ -1619,6 +1620,8 @@ static int git_default_core_config(const char *var, const char *value,
        }
 
        if (!strcmp(var, "core.createobject")) {
+               if (!value)
+                       return config_error_nonbool(var);
                if (!strcmp(value, "rename"))
                        object_creation_mode = OBJECT_CREATION_USES_RENAMES;
                else if (!strcmp(value, "link"))
@@ -1984,7 +1987,27 @@ char *git_system_config(void)
        return system_config;
 }
 
-void git_global_config(char **user_out, char **xdg_out)
+char *git_global_config(void)
+{
+       char *user_config, *xdg_config;
+
+       git_global_config_paths(&user_config, &xdg_config);
+       if (!user_config) {
+               free(xdg_config);
+               return NULL;
+       }
+
+       if (access_or_warn(user_config, R_OK, 0) && xdg_config &&
+           !access_or_warn(xdg_config, R_OK, 0)) {
+               free(user_config);
+               return xdg_config;
+       } else {
+               free(xdg_config);
+               return user_config;
+       }
+}
+
+void git_global_config_paths(char **user_out, char **xdg_out)
 {
        char *user_config = xstrdup_or_null(getenv("GIT_CONFIG_GLOBAL"));
        char *xdg_config = NULL;
@@ -2037,7 +2060,7 @@ static int do_git_config_sequence(const struct config_options *opts,
                                                         data, CONFIG_SCOPE_SYSTEM,
                                                         NULL);
 
-       git_global_config(&user_config, &xdg_config);
+       git_global_config_paths(&user_config, &xdg_config);
 
        if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
                ret += git_config_from_file_with_options(fn, xdg_config, data,
@@ -3414,7 +3437,6 @@ out_free:
 write_err_out:
        ret = write_error(get_lock_file_path(&lock));
        goto out_free;
-
 }
 
 void git_config_set_multivar_in_file(const char *config_filename,
index 14f881ecfaf6b264b6d120205ab509f2ee5930f2..5dba984f770e4e96d322874351a70d0f7d0ee8ba 100644 (file)
--- a/config.h
+++ b/config.h
@@ -382,7 +382,8 @@ int config_error_nonbool(const char *);
 #endif
 
 char *git_system_config(void);
-void git_global_config(char **user, char **xdg);
+char *git_global_config(void);
+void git_global_config_paths(char **user, char **xdg);
 
 int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 
index 3bb03f423a08102f837c52dcbe7a3b581104e0cb..dacc95172dcdcbd85086d9dacf57d2be588c90e1 100644 (file)
@@ -158,6 +158,19 @@ ifeq ($(uname_S),Darwin)
                ifeq ($(shell test -x /usr/local/opt/gettext/bin/msgfmt && echo y),y)
                        MSGFMT = /usr/local/opt/gettext/bin/msgfmt
                endif
+       # On newer ARM-based machines the default installation path has changed to
+       # /opt/homebrew. Include it in our search paths so that the user does not
+       # have to configure this manually.
+       #
+       # Note that we do not employ the same workaround as above where we manually
+       # add gettext. The issue was fixed more than three years ago by now, and at
+       # that point there haven't been any ARM-based Macs yet.
+       else ifeq ($(shell test -d /opt/homebrew/ && echo y),y)
+               BASIC_CFLAGS += -I/opt/homebrew/include
+               BASIC_LDFLAGS += -L/opt/homebrew/lib
+               ifeq ($(shell test -x /opt/homebrew/bin/msgfmt && echo y),y)
+                       MSGFMT = /opt/homebrew/bin/msgfmt
+               endif
        endif
 
        # The builtin FSMonitor on MacOS builds upon Simple-IPC.  Both require
index 276593cd9dd935a283620f3b9d25b09987c04bf2..d1a96da14eb56766e1538da80ab97ed8a99b24f6 100644 (file)
@@ -94,7 +94,7 @@ AC_DEFUN([GIT_PARSE_WITH_SET_MAKE_VAR],
 [AC_ARG_WITH([$1],
  [AS_HELP_STRING([--with-$1=VALUE], $3)],
  if test -n "$withval"; then
-  if test "$withval" = "yes" -o "$withval" = "no"; then
+  if test "$withval" = "yes" || test "$withval" = "no"; then
     AC_MSG_WARN([You likely do not want either 'yes' or 'no' as]
                     [a value for $1 ($2).  Maybe you do...?])
   fi
index 6b819e2fbdf357f8401a29b5f8daa29e43284cfe..804629c525b1b1978512c9d15b710bfebeef185e 100644 (file)
@@ -974,6 +974,35 @@ target_link_libraries(test-fake-ssh common-main)
 parse_makefile_for_sources(test-reftable_SOURCES "REFTABLE_TEST_OBJS")
 list(TRANSFORM test-reftable_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
 
+#unit-tests
+add_library(unit-test-lib OBJECT ${CMAKE_SOURCE_DIR}/t/unit-tests/test-lib.c)
+
+parse_makefile_for_scripts(unit_test_PROGRAMS "UNIT_TEST_PROGRAMS" "")
+foreach(unit_test ${unit_test_PROGRAMS})
+       add_executable("${unit_test}" "${CMAKE_SOURCE_DIR}/t/unit-tests/${unit_test}.c")
+       target_link_libraries("${unit_test}" unit-test-lib common-main)
+       set_target_properties("${unit_test}"
+               PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/t/unit-tests/bin)
+       if(MSVC)
+               set_target_properties("${unit_test}"
+                       PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/t/unit-tests/bin)
+               set_target_properties("${unit_test}"
+                       PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/t/unit-tests/bin)
+       endif()
+       list(APPEND PROGRAMS_BUILT "${unit_test}")
+
+       # t-basic intentionally fails tests, to validate the unit-test infrastructure.
+       # Therefore, it should only be run as part of t0080, which verifies that it
+       # fails only in the expected ways.
+       #
+       # All other unit tests should be run.
+       if(NOT ${unit_test} STREQUAL "t-basic")
+               add_test(NAME "t.unit-tests.${unit_test}"
+                       COMMAND "./${unit_test}"
+                       WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/t/unit-tests/bin)
+       endif()
+endforeach()
+
 #test-tool
 parse_makefile_for_sources(test-tool_SOURCES "TEST_BUILTINS_OBJS")
 
@@ -1093,17 +1122,18 @@ if(NOT ${CMAKE_BINARY_DIR}/CMakeCache.txt STREQUAL ${CACHE_PATH})
        file(COPY ${CMAKE_SOURCE_DIR}/contrib/completion/git-completion.bash DESTINATION ${CMAKE_BINARY_DIR}/contrib/completion/)
 endif()
 
-file(GLOB test_scipts "${CMAKE_SOURCE_DIR}/t/t[0-9]*.sh")
+file(GLOB test_scripts "${CMAKE_SOURCE_DIR}/t/t[0-9]*.sh")
 
 #test
-foreach(tsh ${test_scipts})
-       add_test(NAME ${tsh}
+foreach(tsh ${test_scripts})
+       string(REGEX REPLACE ".*/(.*)\\.sh" "\\1" test_name ${tsh})
+       add_test(NAME "t.suite.${test_name}"
                COMMAND ${SH_EXE} ${tsh} --no-bin-wrappers --no-chain-lint -vx
                WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/t)
 endforeach()
 
 # This test script takes an extremely long time and is known to time out even
 # on fast machines because it requires in excess of one hour to run
-set_tests_properties("${CMAKE_SOURCE_DIR}/t/t7112-reset-submodule.sh" PROPERTIES TIMEOUT 4000)
+set_tests_properties("t.suite.t7112-reset-submodule" PROPERTIES TIMEOUT 4000)
 
 endif()#BUILD_TESTING
index 13a39ebd2e73f9b34de743b797577a5e8e98cbae..6662db221df9d5088566a283ca7d784548920ace 100644 (file)
@@ -122,6 +122,40 @@ __git ()
                ${__git_dir:+--git-dir="$__git_dir"} "$@" 2>/dev/null
 }
 
+# Helper function to read the first line of a file into a variable.
+# __git_eread requires 2 arguments, the file path and the name of the
+# variable, in that order.
+#
+# This is taken from git-prompt.sh.
+__git_eread ()
+{
+       test -r "$1" && IFS=$'\r\n' read -r "$2" <"$1"
+}
+
+# Runs git in $__git_repo_path to determine whether a pseudoref exists.
+# 1: The pseudo-ref to search
+__git_pseudoref_exists ()
+{
+       local ref=$1
+       local head
+
+       __git_find_repo_path
+
+       # If the reftable is in use, we have to shell out to 'git rev-parse'
+       # to determine whether the ref exists instead of looking directly in
+       # the filesystem to determine whether the ref exists. Otherwise, use
+       # Bash builtins since executing Git commands are expensive on some
+       # platforms.
+       if __git_eread "$__git_repo_path/HEAD" head; then
+               if [ "$head" == "ref: refs/heads/.invalid" ]; then
+                       __git show-ref --exists "$ref"
+                       return $?
+               fi
+       fi
+
+       [ -f "$__git_repo_path/$ref" ]
+}
+
 # Removes backslash escaping, single quotes and double quotes from a word,
 # stores the result in the variable $dequoted_word.
 # 1: The word to dequote.
@@ -1624,8 +1658,7 @@ __git_cherry_pick_inprogress_options=$__git_sequencer_inprogress_options
 
 _git_cherry_pick ()
 {
-       __git_find_repo_path
-       if [ -f "$__git_repo_path"/CHERRY_PICK_HEAD ]; then
+       if __git_pseudoref_exists CHERRY_PICK_HEAD; then
                __gitcomp "$__git_cherry_pick_inprogress_options"
                return
        fi
@@ -2067,7 +2100,7 @@ _git_log ()
        __git_find_repo_path
 
        local merge=""
-       if [ -f "$__git_repo_path/MERGE_HEAD" ]; then
+       if __git_pseudoref_exists MERGE_HEAD; then
                merge="--merge"
        fi
        case "$prev,$cur" in
@@ -2952,7 +2985,7 @@ _git_restore ()
                __gitcomp_builtin restore
                ;;
        *)
-               if __git rev-parse --verify --quiet HEAD >/dev/null; then
+               if __git_pseudoref_exists HEAD; then
                        __git_complete_index_file "--modified"
                fi
        esac
@@ -2962,8 +2995,7 @@ __git_revert_inprogress_options=$__git_sequencer_inprogress_options
 
 _git_revert ()
 {
-       __git_find_repo_path
-       if [ -f "$__git_repo_path"/REVERT_HEAD ]; then
+       if __git_pseudoref_exists REVERT_HEAD; then
                __gitcomp "$__git_revert_inprogress_options"
                return
        fi
@@ -3084,12 +3116,119 @@ __gitcomp_directories ()
                        COMPREPLY+=("$c/")
                        _found=1
                fi
-       done < <(git ls-tree -z -d --name-only HEAD $_tmp_dir)
+       done < <(__git ls-tree -z -d --name-only HEAD $_tmp_dir)
 
        if [[ $_found == 0 ]] && [[ "$cur" =~ /$ ]]; then
                # No possible further completions any deeper, so assume we're at
                # a leaf directory and just consider it complete
                __gitcomp_direct_append "$cur "
+       elif [[ $_found == 0 ]]; then
+               # No possible completions found.  Avoid falling back to
+               # bash's default file and directory completion, because all
+               # valid completions have already been searched and the
+               # fallbacks can do nothing but mislead.  In fact, they can
+               # mislead in three different ways:
+               #    1) Fallback file completion makes no sense when asking
+               #       for directory completions, as this function does.
+               #    2) Fallback directory completion is bad because
+               #       e.g. "/pro" is invalid and should NOT complete to
+               #       "/proc".
+               #    3) Fallback file/directory completion only completes
+               #       on paths that exist in the current working tree,
+               #       i.e. which are *already* part of their
+               #       sparse-checkout.  Thus, normal file and directory
+               #       completion is always useless for "git
+               #       sparse-checkout add" and is also probelmatic for
+               #       "git sparse-checkout set" unless using it to
+               #       strictly narrow the checkout.
+               COMPREPLY=( "" )
+       fi
+}
+
+# In non-cone mode, the arguments to {set,add} are supposed to be
+# patterns, relative to the toplevel directory.  These can be any kind
+# of general pattern, like 'subdir/*.c' and we can't complete on all
+# of those.  However, if the user presses Tab to get tab completion, we
+# presume that they are trying to provide a pattern that names a specific
+# path.
+__gitcomp_slash_leading_paths ()
+{
+       local dequoted_word pfx="" cur_ toplevel
+
+       # Since we are dealing with a sparse-checkout, subdirectories may not
+       # exist in the local working copy.  Therefore, we want to run all
+       # ls-files commands relative to the repository toplevel.
+       toplevel="$(git rev-parse --show-toplevel)/"
+
+       __git_dequote "$cur"
+
+       # If the paths provided by the user already start with '/', then
+       # they are considered relative to the toplevel of the repository
+       # already.  If they do not start with /, then we need to adjust
+       # them to start with the appropriate prefix.
+       case "$cur" in
+       /*)
+               cur="${cur:1}"
+               ;;
+       *)
+               pfx="$(__git rev-parse --show-prefix)"
+       esac
+
+       # Since sparse-index is limited to cone-mode, in non-cone-mode the
+       # list of valid paths is precisely the cached files in the index.
+       #
+       # NEEDSWORK:
+       #   1) We probably need to take care of cases where ls-files
+       #      responds with special quoting.
+       #   2) We probably need to take care of cases where ${cur} has
+       #      some kind of special quoting.
+       #   3) On top of any quoting from 1 & 2, we have to provide an extra
+       #      level of quoting for any paths that contain a '*', '?', '\',
+       #      '[', ']', or leading '#' or '!' since those will be
+       #      interpreted by sparse-checkout as something other than a
+       #      literal path character.
+       # Since there are two types of quoting here, this might get really
+       # complex.  For now, just punt on all of this...
+       completions="$(__git -C "${toplevel}" -c core.quotePath=false \
+                        ls-files --cached -- "${pfx}${cur}*" \
+                        | sed -e s%^%/% -e 's%$% %')"
+       # Note, above, though that we needed all of the completions to be
+       # prefixed with a '/', and we want to add a space so that bash
+       # completion will actually complete an entry and let us move on to
+       # the next one.
+
+       # Return what we've found.
+       if test -n "$completions"; then
+               # We found some completions; return them
+               local IFS=$'\n'
+               COMPREPLY=($completions)
+       else
+               # Do NOT fall back to bash-style all-local-files-and-dirs
+               # when we find no match.  Such options are worse than
+               # useless:
+               #     1. "git sparse-checkout add" needs paths that are NOT
+               #        currently in the working copy.  "git
+               #        sparse-checkout set" does as well, except in the
+               #        special cases when users are only trying to narrow
+               #        their sparse checkout to a subset of what they
+               #        already have.
+               #
+               #     2. A path like '.config' is ambiguous as to whether
+               #        the user wants all '.config' files throughout the
+               #        tree, or just the one under the current directory.
+               #        It would result in a warning from the
+               #        sparse-checkout command due to this.  As such, all
+               #        completions of paths should be prefixed with a
+               #        '/'.
+               #
+               #     3. We don't want paths prefixed with a '/' to
+               #        complete files in the system root directory, we
+               #        want it to complete on files relative to the
+               #        repository root.
+               #
+               # As such, make sure that NO completions are offered rather
+               # than falling back to bash's default completions.
+               COMPREPLY=( "" )
        fi
 }
 
@@ -3097,6 +3236,7 @@ _git_sparse_checkout ()
 {
        local subcommands="list init set disable add reapply"
        local subcommand="$(__git_find_on_cmdline "$subcommands")"
+       local using_cone=true
        if [ -z "$subcommand" ]; then
                __gitcomp "$subcommands"
                return
@@ -3107,9 +3247,18 @@ _git_sparse_checkout ()
                __gitcomp_builtin sparse-checkout_$subcommand "" "--"
                ;;
        set,*|add,*)
-               if [ "$(__git config core.sparseCheckoutCone)" == "true" ] ||
-               [ -n "$(__git_find_on_cmdline --cone)" ]; then
+               if [[ "$(__git config core.sparseCheckout)" == "true" &&
+                     "$(__git config core.sparseCheckoutCone)" == "false" &&
+                     -z "$(__git_find_on_cmdline --cone)" ]]; then
+                       using_cone=false
+               fi
+               if [[ -n "$(__git_find_on_cmdline --no-cone)" ]]; then
+                       using_cone=false
+               fi
+               if [[ "$using_cone" == "true" ]]; then
                        __gitcomp_directories
+               else
+                        __gitcomp_slash_leading_paths
                fi
        esac
 }
@@ -3592,7 +3741,7 @@ __gitk_main ()
        __git_find_repo_path
 
        local merge=""
-       if [ -f "$__git_repo_path/MERGE_HEAD" ]; then
+       if __git_pseudoref_exists MERGE_HEAD; then
                merge="--merge"
        fi
        case "$cur" in
index 2c030050aea1c67472570a9d86d69d55e31e7714..71f179cba3fbda3bc93de461649cf75bb08c6653 100644 (file)
@@ -408,7 +408,7 @@ __git_ps1 ()
 
        local repo_info rev_parse_exit_code
        repo_info="$(git rev-parse --git-dir --is-inside-git-dir \
-               --is-bare-repository --is-inside-work-tree \
+               --is-bare-repository --is-inside-work-tree --show-ref-format \
                --short HEAD 2>/dev/null)"
        rev_parse_exit_code="$?"
 
@@ -421,6 +421,8 @@ __git_ps1 ()
                short_sha="${repo_info##*$'\n'}"
                repo_info="${repo_info%$'\n'*}"
        fi
+       local ref_format="${repo_info##*$'\n'}"
+       repo_info="${repo_info%$'\n'*}"
        local inside_worktree="${repo_info##*$'\n'}"
        repo_info="${repo_info%$'\n'*}"
        local bare_repo="${repo_info##*$'\n'}"
@@ -479,12 +481,25 @@ __git_ps1 ()
                        b="$(git symbolic-ref HEAD 2>/dev/null)"
                else
                        local head=""
-                       if ! __git_eread "$g/HEAD" head; then
-                               return $exit
-                       fi
-                       # is it a symbolic ref?
-                       b="${head#ref: }"
-                       if [ "$head" = "$b" ]; then
+
+                       case "$ref_format" in
+                       files)
+                               if ! __git_eread "$g/HEAD" head; then
+                                       return $exit
+                               fi
+
+                               if [[ $head == "ref: "* ]]; then
+                                       head="${head#ref: }"
+                               else
+                                       head=""
+                               fi
+                               ;;
+                       *)
+                               head="$(git symbolic-ref HEAD 2>/dev/null)"
+                               ;;
+                       esac
+
+                       if test -z "$head"; then
                                detached=yes
                                b="$(
                                case "${GIT_PS1_DESCRIBE_STYLE-}" in
@@ -502,6 +517,8 @@ __git_ps1 ()
 
                                b="$short_sha..."
                                b="($b)"
+                       else
+                               b="$head"
                        fi
                fi
        fi
index 376f577737591e26a118718a87945500ea621c95..636add6968067537a34e46b92957fbc35acd18e9 100644 (file)
@@ -1,6 +1,6 @@
 package DiffHighlight;
 
-use 5.008;
+use 5.008001;
 use warnings FATAL => 'all';
 use strict;
 
index 917d9e2d3222c12f2d0dc043a1434acae2b398ae..ff7811225ee67166d8e997ce99def0344971f904 100644 (file)
@@ -1,6 +1,6 @@
 package Git::Mediawiki;
 
-use 5.008;
+use 5.008001;
 use strict;
 use POSIX;
 use Git;
index e0c5d3b0de63483113edbcc09003e70aaa0a3d2d..3028029ac2ddf67608dad1f6538b37a347aebe29 100755 (executable)
@@ -373,7 +373,8 @@ try_remove_previous () {
 
 # Usage: process_subtree_split_trailer SPLIT_HASH MAIN_HASH [REPOSITORY]
 process_subtree_split_trailer () {
-       assert test $# = 2 -o $# = 3
+       assert test $# -ge 2
+       assert test $# -le 3
        b="$1"
        sq="$2"
        repository=""
@@ -402,7 +403,8 @@ process_subtree_split_trailer () {
 
 # Usage: find_latest_squash DIR [REPOSITORY]
 find_latest_squash () {
-       assert test $# = 1 -o $# = 2
+       assert test $# -ge 1
+       assert test $# -le 2
        dir="$1"
        repository=""
        if test "$#" = 2
@@ -455,7 +457,8 @@ find_latest_squash () {
 
 # Usage: find_existing_splits DIR REV [REPOSITORY]
 find_existing_splits () {
-       assert test $# = 2 -o $# = 3
+       assert test $# -ge 2
+       assert test $# -le 3
        debug "Looking for prior splits..."
        local indent=$(($indent + 1))
 
@@ -489,13 +492,13 @@ find_existing_splits () {
                        ;;
                END)
                        debug "Main is: '$main'"
-                       if test -z "$main" -a -n "$sub"
+                       if test -z "$main" && test -n "$sub"
                        then
                                # squash commits refer to a subtree
                                debug "  Squash: $sq from $sub"
                                cache_set "$sq" "$sub"
                        fi
-                       if test -n "$main" -a -n "$sub"
+                       if test -n "$main" && test -n "$sub"
                        then
                                debug "  Prior: $main -> $sub"
                                cache_set $main $sub
@@ -638,10 +641,16 @@ subtree_for_commit () {
        while read mode type tree name
        do
                assert test "$name" = "$dir"
-               assert test "$type" = "tree" -o "$type" = "commit"
-               test "$type" = "commit" && continue  # ignore submodules
-               echo $tree
-               break
+
+               case "$type" in
+               commit)
+                       continue;; # ignore submodules
+               tree)
+                       echo $tree
+                       break;;
+               *)
+                       die "fatal: tree entry is of type ${type}, expected tree or commit";;
+               esac
        done || exit $?
 }
 
@@ -916,7 +925,7 @@ cmd_split () {
        if test $# -eq 0
        then
                rev=$(git rev-parse HEAD)
-       elif test $# -eq 1 -o $# -eq 2
+       elif test $# -eq 1 || test $# -eq 2
        then
                rev=$(git rev-parse -q --verify "$1^{commit}") ||
                        die "fatal: '$1' does not refer to a commit"
@@ -1006,8 +1015,11 @@ cmd_split () {
 
 # Usage: cmd_merge REV [REPOSITORY]
 cmd_merge () {
-       test $# -eq 1 -o $# -eq 2 ||
+       if test $# -lt 1 || test $# -gt 2
+       then
                die "fatal: you must provide exactly one revision, and optionally a repository. Got: '$*'"
+       fi
+
        rev=$(git rev-parse -q --verify "$1^{commit}") ||
                die "fatal: '$1' does not refer to a commit"
        repository=""
index d925589444b90a11058abe87286df3d5add592f8..ab8b4fa68d67ba3da1fc7900b616dc70a9967eba 100644 (file)
--- a/convert.h
+++ b/convert.h
@@ -92,7 +92,7 @@ void convert_attrs(struct index_state *istate,
                   struct conv_attrs *ca, const char *path);
 
 extern enum eol core_eol;
-extern char *check_roundtrip_encoding;
+extern const char *check_roundtrip_encoding;
 const char *get_cached_convert_stats_ascii(struct index_state *istate,
                                           const char *path);
 const char *get_wt_convert_stats_ascii(const char *path);
index 5de5759f3f13da13465ec6c9bfa6abeb44ceaee3..ee2318d45a1c7898b39999b1f5fc81be8ec49f00 100644 (file)
@@ -1,18 +1,13 @@
 #include "git-compat-util.h"
-#include "attr.h"
 #include "object.h"
-#include "blob.h"
 #include "commit.h"
 #include "gettext.h"
 #include "hex.h"
 #include "tag.h"
 #include "tree.h"
-#include "delta.h"
 #include "pack.h"
 #include "tree-walk.h"
 #include "diff.h"
-#include "revision.h"
-#include "list-objects.h"
 #include "progress.h"
 #include "refs.h"
 #include "khash.h"
index 0e9ec4f68afb01f23d9b9a0142d2ffb2cd1cc79b..6c8df04273702fa47215dffa263477df347900ee 100644 (file)
@@ -2,7 +2,6 @@
  * Copyright (C) 2005 Junio C Hamano
  */
 #include "git-compat-util.h"
-#include "quote.h"
 #include "commit.h"
 #include "diff.h"
 #include "diffcore.h"
  */
 static int check_removed(const struct cache_entry *ce, struct stat *st)
 {
-       if (lstat(ce->name, st) < 0) {
+       int stat_err;
+
+       if (!(ce->ce_flags & CE_FSMONITOR_VALID))
+               stat_err = lstat(ce->name, st);
+       else
+               stat_err = fake_lstat(ce, st);
+       if (stat_err < 0) {
                if (!is_missing_file_error(errno))
                        return -1;
                return 1;
index e7041b89e38887e04237a00f84366781dbabf739..3a8965672c5e90093c08a9fde12981b922ae8a76 100644 (file)
@@ -8,13 +8,10 @@
 #include "abspath.h"
 #include "color.h"
 #include "commit.h"
-#include "blob.h"
-#include "tag.h"
 #include "diff.h"
 #include "diffcore.h"
 #include "gettext.h"
 #include "revision.h"
-#include "log-tree.h"
 #include "parse-options.h"
 #include "string-list.h"
 #include "dir.h"
diff --git a/diff.c b/diff.c
index 2c602df10a372c7e99402ca5a2dc9cf7aa673c09..a89a6a6128ad68de5d29d47061386f21d91a2337 100644 (file)
--- a/diff.c
+++ b/diff.c
 #include "hex.h"
 #include "xdiff-interface.h"
 #include "color.h"
-#include "attr.h"
 #include "run-command.h"
 #include "utf8.h"
 #include "object-store-ll.h"
 #include "userdiff.h"
-#include "submodule-config.h"
 #include "submodule.h"
 #include "hashmap.h"
 #include "mem-pool.h"
@@ -372,7 +370,10 @@ int git_diff_ui_config(const char *var, const char *value,
                return 0;
        }
        if (!strcmp(var, "diff.colormovedws")) {
-               unsigned cm = parse_color_moved_ws(value);
+               unsigned cm;
+               if (!value)
+                       return config_error_nonbool(var);
+               cm = parse_color_moved_ws(value);
                if (cm & COLOR_MOVED_WS_ERROR)
                        return -1;
                diff_color_moved_ws_default = cm;
@@ -426,10 +427,15 @@ int git_diff_ui_config(const char *var, const char *value,
        if (!strcmp(var, "diff.orderfile"))
                return git_config_pathname(&diff_order_file_cfg, var, value);
 
-       if (!strcmp(var, "diff.ignoresubmodules"))
+       if (!strcmp(var, "diff.ignoresubmodules")) {
+               if (!value)
+                       return config_error_nonbool(var);
                handle_ignore_submodules_arg(&default_diff_options, value);
+       }
 
        if (!strcmp(var, "diff.submodule")) {
+               if (!value)
+                       return config_error_nonbool(var);
                if (parse_submodule_params(&default_diff_options, value))
                        warning(_("Unknown value for 'diff.submodule' config variable: '%s'"),
                                value);
@@ -437,9 +443,12 @@ int git_diff_ui_config(const char *var, const char *value,
        }
 
        if (!strcmp(var, "diff.algorithm")) {
+               if (!value)
+                       return config_error_nonbool(var);
                diff_algorithm = parse_algorithm_value(value);
                if (diff_algorithm < 0)
-                       return -1;
+                       return error(_("unknown value for config '%s': %s"),
+                                    var, value);
                return 0;
        }
 
@@ -473,9 +482,13 @@ int git_diff_basic_config(const char *var, const char *value,
        }
 
        if (!strcmp(var, "diff.wserrorhighlight")) {
-               int val = parse_ws_error_highlight(value);
+               int val;
+               if (!value)
+                       return config_error_nonbool(var);
+               val = parse_ws_error_highlight(value);
                if (val < 0)
-                       return -1;
+                       return error(_("unknown value for config '%s': %s"),
+                                    var, value);
                ws_error_highlight_default = val;
                return 0;
        }
@@ -490,6 +503,8 @@ int git_diff_basic_config(const char *var, const char *value,
 
        if (!strcmp(var, "diff.dirstat")) {
                struct strbuf errmsg = STRBUF_INIT;
+               if (!value)
+                       return config_error_nonbool(var);
                default_diff_options.dirstat_permille = diff_dirstat_permille_default;
                if (parse_dirstat_params(&default_diff_options, value, &errmsg))
                        warning(_("Found errors in 'diff.dirstat' config variable:\n%s"),
index f57ece2757d46ea6dee3af34a476c5fdd9908b0d..49ba38aa7c0a58433380e560f6d84e0cc084a67f 100644 (file)
@@ -2,7 +2,6 @@
  * Copyright (C) 2005 Junio C Hamano
  */
 #include "git-compat-util.h"
-#include "diff.h"
 #include "diffcore.h"
 #include "hash.h"
 #include "object.h"
index c30b56e983bda39e18eda24bb81608ae7a33942c..ba6cbee76ba0180db352d4baf0653af201304046 100644 (file)
@@ -1,5 +1,4 @@
 #include "git-compat-util.h"
-#include "diff.h"
 #include "diffcore.h"
 
 /*
@@ -159,6 +158,10 @@ static struct spanhash_top *hash_chars(struct repository *r,
                n = 0;
                accum1 = accum2 = 0;
        }
+       if (n > 0) {
+               hashval = (accum1 + accum2 * 0x61) % HASHBASE;
+               hash = add_spanhash(hash, hashval, n);
+       }
        QSORT(hash->data, (size_t)1ul << hash->alloc_log2, spanhash_cmp);
        return hash;
 }
diff --git a/dir.c b/dir.c
index 16fdb03f2a51c9505b3cc9da2d9a908f3e4ae119..ac699542302cece1d1aea426cd4cc37940e6fead 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -16,7 +16,6 @@
 #include "object-file.h"
 #include "object-store-ll.h"
 #include "path.h"
-#include "attr.h"
 #include "refs.h"
 #include "wildmatch.h"
 #include "pathspec.h"
@@ -2179,7 +2178,8 @@ static int exclude_matches_pathspec(const char *path, int pathlen,
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
                       PATHSPEC_ICASE |
-                      PATHSPEC_EXCLUDE);
+                      PATHSPEC_EXCLUDE |
+                      PATHSPEC_ATTR);
 
        for (i = 0; i < pathspec->nr; i++) {
                const struct pathspec_item *item = &pathspec->items[i];
diff --git a/entry.c b/entry.c
index 076e97eb89ce15e312b6ff08eebd3c1389245997..f918a3a78e8154fb62de1fb2378d100f9c47e9ca 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -1,5 +1,4 @@
 #include "git-compat-util.h"
-#include "blob.h"
 #include "object-store-ll.h"
 #include "dir.h"
 #include "environment.h"
index 9e37bf58c0c6822f48152e5ab59c25a0cf39a874..90632a39bc995af8bf56166b4d89bba5d6dd5272 100644 (file)
@@ -64,7 +64,7 @@ const char *excludes_file;
 enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
 enum eol core_eol = EOL_UNSET;
 int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
-char *check_roundtrip_encoding = "SHIFT-JIS";
+const char *check_roundtrip_encoding = "SHIFT-JIS";
 enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
 enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
index 7b525b1ecd896e02dfa71cdf3aab75496d998fac..ac7e0af622a8fc74b678b57f464698d1bc9a45e7 100644 (file)
@@ -169,6 +169,15 @@ size_t bitmap_popcount(struct bitmap *self)
        return count;
 }
 
+int bitmap_is_empty(struct bitmap *self)
+{
+       size_t i;
+       for (i = 0; i < self->word_alloc; i++)
+               if (self->words[i])
+                       return 0;
+       return 1;
+}
+
 int bitmap_equals(struct bitmap *self, struct bitmap *other)
 {
        struct bitmap *big, *small;
index 7eb8b9b63013daa70c91687108307d38a9eb1e09..c11d76c6f336930e0ea540a5b40c594bdb9f5be2 100644 (file)
@@ -189,5 +189,6 @@ void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other);
 void bitmap_or(struct bitmap *self, const struct bitmap *other);
 
 size_t bitmap_popcount(struct bitmap *self);
+int bitmap_is_empty(struct bitmap *self);
 
 #endif
index 1d597e84ea7f4c3c8296cbf527bf0c5514037e99..909777f61f4fa3d9d8c51c25db96e8d1f30544ae 100644 (file)
@@ -4,7 +4,6 @@
 #include "exec-cmd.h"
 #include "gettext.h"
 #include "path.h"
-#include "quote.h"
 #include "run-command.h"
 #include "strvec.h"
 #include "trace.h"
index 26999e3b6591313a18df430170aaf935d2e24c14..5b8aa0adc77621504619b831bdd842b83f4867a1 100644 (file)
@@ -10,7 +10,6 @@
 #include "pkt-line.h"
 #include "commit.h"
 #include "tag.h"
-#include "exec-cmd.h"
 #include "pack.h"
 #include "sideband.h"
 #include "fetch-pack.h"
@@ -18,7 +17,6 @@
 #include "run-command.h"
 #include "connect.h"
 #include "trace2.h"
-#include "transport.h"
 #include "version.h"
 #include "oid-array.h"
 #include "oidset.h"
@@ -1862,6 +1860,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
 static int fetch_pack_config_cb(const char *var, const char *value,
                                const struct config_context *ctx, void *cb)
 {
+       const char *msg_id;
+
        if (strcmp(var, "fetch.fsck.skiplist") == 0) {
                const char *path;
 
@@ -1873,12 +1873,14 @@ static int fetch_pack_config_cb(const char *var, const char *value,
                return 0;
        }
 
-       if (skip_prefix(var, "fetch.fsck.", &var)) {
-               if (is_valid_msg_type(var, value))
+       if (skip_prefix(var, "fetch.fsck.", &msg_id)) {
+               if (!value)
+                       return config_error_nonbool(var);
+               if (is_valid_msg_type(msg_id, value))
                        strbuf_addf(&fsck_msg_types, "%c%s=%s",
-                               fsck_msg_types.len ? ',' : '=', var, value);
+                               fsck_msg_types.len ? ',' : '=', msg_id, value);
                else
-                       warning("Skipping unknown msg id '%s'", var);
+                       warning("Skipping unknown msg id '%s'", msg_id);
                return 0;
        }
 
index 8c7752fc8212c7f911f9f462a2b306c48022bc2f..6775d265175748d2fccd2b9571c864a6ec091f20 100644 (file)
@@ -2,7 +2,6 @@
 #define FETCH_PACK_H
 
 #include "string-list.h"
-#include "run-command.h"
 #include "protocol.h"
 #include "list-objects-filter-options.h"
 #include "oidset.h"
diff --git a/fsck.c b/fsck.c
index 6a0bbc50877710ff22db33adec48f4a469143c37..8ded0a473a47fb45c223d8736f58c599b66cbb93 100644 (file)
--- a/fsck.c
+++ b/fsck.c
 #include "refs.h"
 #include "url.h"
 #include "utf8.h"
-#include "decorate.h"
 #include "oidset.h"
 #include "packfile.h"
 #include "submodule-config.h"
 #include "config.h"
-#include "credential.h"
 #include "help.h"
 
 static ssize_t max_tree_entry_len = 4096;
@@ -1048,138 +1046,6 @@ done:
        return ret;
 }
 
-static int starts_with_dot_slash(const char *const path)
-{
-       return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_SLASH |
-                               PATH_MATCH_XPLATFORM);
-}
-
-static int starts_with_dot_dot_slash(const char *const path)
-{
-       return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH |
-                               PATH_MATCH_XPLATFORM);
-}
-
-static int submodule_url_is_relative(const char *url)
-{
-       return starts_with_dot_slash(url) || starts_with_dot_dot_slash(url);
-}
-
-/*
- * Count directory components that a relative submodule URL should chop
- * from the remote_url it is to be resolved against.
- *
- * In other words, this counts "../" components at the start of a
- * submodule URL.
- *
- * Returns the number of directory components to chop and writes a
- * pointer to the next character of url after all leading "./" and
- * "../" components to out.
- */
-static int count_leading_dotdots(const char *url, const char **out)
-{
-       int result = 0;
-       while (1) {
-               if (starts_with_dot_dot_slash(url)) {
-                       result++;
-                       url += strlen("../");
-                       continue;
-               }
-               if (starts_with_dot_slash(url)) {
-                       url += strlen("./");
-                       continue;
-               }
-               *out = url;
-               return result;
-       }
-}
-/*
- * Check whether a transport is implemented by git-remote-curl.
- *
- * If it is, returns 1 and writes the URL that would be passed to
- * git-remote-curl to the "out" parameter.
- *
- * Otherwise, returns 0 and leaves "out" untouched.
- *
- * Examples:
- *   http::https://example.com/repo.git -> 1, https://example.com/repo.git
- *   https://example.com/repo.git -> 1, https://example.com/repo.git
- *   git://example.com/repo.git -> 0
- *
- * This is for use in checking for previously exploitable bugs that
- * required a submodule URL to be passed to git-remote-curl.
- */
-static int url_to_curl_url(const char *url, const char **out)
-{
-       /*
-        * We don't need to check for case-aliases, "http.exe", and so
-        * on because in the default configuration, is_transport_allowed
-        * prevents URLs with those schemes from being cloned
-        * automatically.
-        */
-       if (skip_prefix(url, "http::", out) ||
-           skip_prefix(url, "https::", out) ||
-           skip_prefix(url, "ftp::", out) ||
-           skip_prefix(url, "ftps::", out))
-               return 1;
-       if (starts_with(url, "http://") ||
-           starts_with(url, "https://") ||
-           starts_with(url, "ftp://") ||
-           starts_with(url, "ftps://")) {
-               *out = url;
-               return 1;
-       }
-       return 0;
-}
-
-static int check_submodule_url(const char *url)
-{
-       const char *curl_url;
-
-       if (looks_like_command_line_option(url))
-               return -1;
-
-       if (submodule_url_is_relative(url) || starts_with(url, "git://")) {
-               char *decoded;
-               const char *next;
-               int has_nl;
-
-               /*
-                * This could be appended to an http URL and url-decoded;
-                * check for malicious characters.
-                */
-               decoded = url_decode(url);
-               has_nl = !!strchr(decoded, '\n');
-
-               free(decoded);
-               if (has_nl)
-                       return -1;
-
-               /*
-                * URLs which escape their root via "../" can overwrite
-                * the host field and previous components, resolving to
-                * URLs like https::example.com/submodule.git and
-                * https:///example.com/submodule.git that were
-                * susceptible to CVE-2020-11008.
-                */
-               if (count_leading_dotdots(url, &next) > 0 &&
-                   (*next == ':' || *next == '/'))
-                       return -1;
-       }
-
-       else if (url_to_curl_url(url, &curl_url)) {
-               struct credential c = CREDENTIAL_INIT;
-               int ret = 0;
-               if (credential_from_url_gently(&c, curl_url, 1) ||
-                   !*c.host)
-                       ret = -1;
-               credential_clear(&c);
-               return ret;
-       }
-
-       return 0;
-}
-
 struct fsck_gitmodules_data {
        const struct object_id *oid;
        struct fsck_options *options;
@@ -1403,6 +1269,8 @@ int git_fsck_config(const char *var, const char *value,
                    const struct config_context *ctx, void *cb)
 {
        struct fsck_options *options = cb;
+       const char *msg_id;
+
        if (strcmp(var, "fsck.skiplist") == 0) {
                const char *path;
                struct strbuf sb = STRBUF_INIT;
@@ -1416,8 +1284,10 @@ int git_fsck_config(const char *var, const char *value,
                return 0;
        }
 
-       if (skip_prefix(var, "fsck.", &var)) {
-               fsck_set_msg_type(options, var, value);
+       if (skip_prefix(var, "fsck.", &msg_id)) {
+               if (!value)
+                       return config_error_nonbool(var);
+               fsck_set_msg_type(options, msg_id, value);
                return 0;
        }
 
index 673f80d2aad0d4a2e0dff1f7f114f2fc7b5b653c..5cbbec8d940ba75a541dc0398a930f8f393562b6 100644 (file)
@@ -3,9 +3,7 @@
 
 #ifdef HAVE_FSMONITOR_DAEMON_BACKEND
 
-#include "dir.h"
-#include "run-command.h"
-#include "simple-ipc.h"
+#include "hashmap.h"
 #include "thread-utils.h"
 #include "fsmonitor-path-utils.h"
 
index 153918cf768c48f4b9cad799cd728c45a152a8ad..45471b5b741a19515b5d0caea2dd01dd3e9fce25 100644 (file)
@@ -1,5 +1,4 @@
 #include "git-compat-util.h"
-#include "fsmonitor-ll.h"
 #include "gettext.h"
 #include "simple-ipc.h"
 #include "fsmonitor-ipc.h"
index f27e94407b4c2848eecfbd827ee85525ff8c04ed..57facbc21ec2545e5bdeb1e738445ad403f419b4 100644 (file)
--- a/gettext.c
+++ b/gettext.c
@@ -7,9 +7,7 @@
 #include "environment.h"
 #include "exec-cmd.h"
 #include "gettext.h"
-#include "strbuf.h"
 #include "utf8.h"
-#include "config.h"
 
 #ifndef NO_GETTEXT
 #      include <libintl.h>
index b7c173c345544d61a887cfbebb58703478a29d2d..f5a317b89961ce3bd6ec64afa96ae0e550b7e4d0 100755 (executable)
@@ -54,7 +54,7 @@ and can contain multiple, unrelated branches.
 
 =cut
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 use Getopt::Std;
index 3e7a59b5ff108dddce111cb372b6c846c093b4be..7c2a6538e5afea607f3d9a1c09cc6aea5539d8de 100644 (file)
@@ -225,6 +225,7 @@ struct strbuf;
 #include <stddef.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <string.h>
 #ifdef HAVE_STRINGS_H
 #include <strings.h> /* for strcasecmp() */
@@ -684,11 +685,11 @@ report_fn get_warn_routine(void);
 void set_die_is_recursing_routine(int (*routine)(void));
 
 /*
- * If the string "str" begins with the string found in "prefix", return 1.
+ * If the string "str" begins with the string found in "prefix", return true.
  * The "out" parameter is set to "str + strlen(prefix)" (i.e., to the point in
  * the string right after the prefix).
  *
- * Otherwise, return 0 and leave "out" untouched.
+ * Otherwise, return false and leave "out" untouched.
  *
  * Examples:
  *
@@ -699,57 +700,58 @@ void set_die_is_recursing_routine(int (*routine)(void));
  *   [skip prefix if present, otherwise use whole string]
  *   skip_prefix(name, "refs/heads/", &name);
  */
-static inline int skip_prefix(const char *str, const char *prefix,
-                             const char **out)
+static inline bool skip_prefix(const char *str, const char *prefix,
+                              const char **out)
 {
        do {
                if (!*prefix) {
                        *out = str;
-                       return 1;
+                       return true;
                }
        } while (*str++ == *prefix++);
-       return 0;
+       return false;
 }
 
 /*
  * Like skip_prefix, but promises never to read past "len" bytes of the input
  * buffer, and returns the remaining number of bytes in "out" via "outlen".
  */
-static inline int skip_prefix_mem(const char *buf, size_t len,
-                                 const char *prefix,
-                                 const char **out, size_t *outlen)
+static inline bool skip_prefix_mem(const char *buf, size_t len,
+                                  const char *prefix,
+                                  const char **out, size_t *outlen)
 {
        size_t prefix_len = strlen(prefix);
        if (prefix_len <= len && !memcmp(buf, prefix, prefix_len)) {
                *out = buf + prefix_len;
                *outlen = len - prefix_len;
-               return 1;
+               return true;
        }
-       return 0;
+       return false;
 }
 
 /*
- * If buf ends with suffix, return 1 and subtract the length of the suffix
- * from *len. Otherwise, return 0 and leave *len untouched.
+ * If buf ends with suffix, return true and subtract the length of the suffix
+ * from *len. Otherwise, return false and leave *len untouched.
  */
-static inline int strip_suffix_mem(const char *buf, size_t *len,
-                                  const char *suffix)
+static inline bool strip_suffix_mem(const char *buf, size_t *len,
+                                   const char *suffix)
 {
        size_t suflen = strlen(suffix);
        if (*len < suflen || memcmp(buf + (*len - suflen), suffix, suflen))
-               return 0;
+               return false;
        *len -= suflen;
-       return 1;
+       return true;
 }
 
 /*
- * If str ends with suffix, return 1 and set *len to the size of the string
- * without the suffix. Otherwise, return 0 and set *len to the size of the
+ * If str ends with suffix, return true and set *len to the size of the string
+ * without the suffix. Otherwise, return false and set *len to the size of the
  * string.
  *
  * Note that we do _not_ NUL-terminate str to the new length.
  */
-static inline int strip_suffix(const char *str, const char *suffix, size_t *len)
+static inline bool strip_suffix(const char *str, const char *suffix,
+                               size_t *len)
 {
        *len = strlen(str);
        return strip_suffix_mem(str, len, suffix);
@@ -1013,6 +1015,15 @@ static inline unsigned long cast_size_t_to_ulong(size_t a)
        return (unsigned long)a;
 }
 
+static inline uint32_t cast_size_t_to_uint32_t(size_t a)
+{
+       if (a != (uint32_t)a)
+               die("object too large to read on this platform: %"
+                   PRIuMAX" is cut off to %u",
+                   (uintmax_t)a, (uint32_t)a);
+       return (uint32_t)a;
+}
+
 static inline int cast_size_t_to_int(size_t a)
 {
        if (a > INT_MAX)
index 289d4bc684dc26256f497636ca5c080fc6589226..1e03ba94d1b27196c74636fc643781e2b25577a9 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/perl
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 use Getopt::Std;
index 7bf3c12d6789741fc33a9a9032c697bb89dc2fc0..211ec8459a0b84e95e84b0b67ff3c9a7742ad4be 100755 (executable)
@@ -13,7 +13,7 @@
 # The head revision is on branch "origin" by default.
 # You can change that with the '-o' option.
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 use Getopt::Long;
@@ -329,7 +329,7 @@ sub conn {
                        # Use a HTTP Proxy. Only works for HTTP proxies that
                        # don't require user authentication
                        #
-                       # See: http://www.ietf.org/rfc/rfc2817.txt
+                       # See: https://www.ietf.org/rfc/rfc2817.txt
 
                        $s = IO::Socket::INET->new(PeerHost => $proxyhost, PeerPort => $proxyport);
                        die "Socket to $proxyhost: $!\n" unless defined $s;
index 7b757360e28c012eb26632889f831c7fef465e16..124f598bdc07058b139f8dcbf8245128c52d9176 100755 (executable)
@@ -15,7 +15,7 @@
 ####
 ####
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 use bytes;
index 3e5907a4609b1510aef71ab54f56a82eeb36e071..507fb2b6826cf6ca264290f3a6eb907b8d525c11 100755 (executable)
@@ -24,7 +24,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with this program; if not, see <http://www.gnu.org/licenses/>.}]
+along with this program; if not, see <https://www.gnu.org/licenses/>.}]
 
 ######################################################################
 ##
@@ -2367,7 +2367,7 @@ proc do_quit {{rc {1}}} {
        set ret_code $rc
 
        # Briefly enable send again, working around Tk bug
-       # http://sourceforge.net/tracker/?func=detail&atid=112997&aid=1821174&group_id=12997
+       # https://sourceforge.net/p/tktoolkit/bugs/2343/
        tk appname [appname]
 
        destroy .
@@ -3052,7 +3052,7 @@ if {$doc_path ne {}} {
 if {[file isfile $doc_path]} {
        set doc_url "file:$doc_path"
 } else {
-       set doc_url {http://www.kernel.org/pub/software/scm/git/docs/}
+       set doc_url {https://www.kernel.org/pub/software/scm/git/docs/}
 }
 
 proc start_browser {url} {
index 32668fc9c6debee6de9882719c305392c1e4791a..d2e0fa60c3ba3f770f525a8d01c66f17826aea75 100644 (file)
@@ -3,7 +3,7 @@
 # (Copied from gitk, commit fd8ccbec4f0161)
 
 # This list of encoding names and aliases is distilled from
-# http://www.iana.org/assignments/character-sets.
+# https://www.iana.org/assignments/character-sets.
 # Not all of them are supported by Tcl.
 set encoding_aliases {
     { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
index 2514bc22abf4aedfaa8d641976c96fbf222bb8a7..116233100d781a61303c1f63be153e50c52f90a1 100644 (file)
@@ -39,7 +39,7 @@ in your language?
 If you do not know what your language should be named, you need to find
 it.  This currently follows ISO 639-1 two letter codes:
 
-       http://www.loc.gov/standards/iso639-2/php/code_list.php
+       https://www.loc.gov/standards/iso639-2/php/code_list.php
 
 For example, if you are preparing a translation for Afrikaans, the
 language code is "af".  If there already is a translation for your
index c68f49454cd83f48ce3452df178766606effbad9..994431c887277410e50b8a592febf3a1e855469a 100755 (executable)
@@ -432,7 +432,7 @@ mongoose_conf() {
 # Mongoose web server configuration file.
 # Lines starting with '#' and empty lines are ignored.
 # For detailed description of every option, visit
-# http://code.google.com/p/mongoose/wiki/MongooseManual
+# https://code.google.com/p/mongoose/wiki/MongooseManual
 
 root           $root
 ports          $port
@@ -458,7 +458,7 @@ plackup_conf () {
 #!$PERL
 
 # gitweb - simple web interface to track changes in git repositories
-#          PSGI wrapper and server starter (see http://plackperl.org)
+#          PSGI wrapper and server starter (see https://plackperl.org)
 
 use strict;
 
index 0eb3bb4c47dcf12a9f1d98e1b76a3681197ac52e..3ea1c405e55ac9e0081f4b5c544dbc97d04fe507 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -4251,7 +4251,8 @@ class P4Sync(Command, P4UserMap):
         if self.tempBranches != []:
             for branch in self.tempBranches:
                 read_pipe(["git", "update-ref", "-d", branch])
-            os.rmdir(os.path.join(os.environ.get("GIT_DIR", ".git"), self.tempBranchLocation))
+            if len(read_pipe(["git", "for-each-ref", self.tempBranchLocation])) > 0:
+                   die("There are unexpected temporary branches")
 
         # Create a symbolic ref p4/HEAD pointing to p4/<branch> to allow
         # a convenient shortcut refname "p4".
index d24e981d611116aa2fabac09697a4532c8081c00..821b2b3a135ab6f4ab0085ac6acbea1d4eb1c320 100755 (executable)
@@ -16,7 +16,7 @@
 #    and second line is the subject of the message.
 #
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use Getopt::Long;
@@ -119,13 +119,16 @@ sub completion_helper {
 
        foreach my $key (keys %$original_opts) {
                unless (exists $not_for_completion{$key}) {
-                       $key =~ s/!$//;
+                       my $negatable = ($key =~ s/!$//);
 
                        if ($key =~ /[:=][si]$/) {
                                $key =~ s/[:=][si]$//;
                                push (@send_email_opts, "--$_=") foreach (split (/\|/, $key));
                        } else {
                                push (@send_email_opts, "--$_") foreach (split (/\|/, $key));
+                               if ($negatable) {
+                                       push (@send_email_opts, "--no-$_") foreach (split (/\|/, $key));
+                               }
                        }
                }
        }
@@ -228,7 +231,7 @@ sub system_or_msg {
        my @sprintf_args = ($cmd_name ? $cmd_name : $args->[0], $exit_code);
        if (defined $msg) {
                # Quiet the 'redundant' warning category, except we
-               # need to support down to Perl 5.8, so we can't do a
+               # need to support down to Perl 5.8.1, so we can't do a
                # "no warnings 'redundant'", since that category was
                # introduced in perl 5.22, and asking for it will die
                # on older perls.
@@ -491,7 +494,6 @@ my %options = (
                    "bcc=s" => \@getopt_bcc,
                    "no-bcc" => \$no_bcc,
                    "chain-reply-to!" => \$chain_reply_to,
-                   "no-chain-reply-to" => sub {$chain_reply_to = 0},
                    "sendmail-cmd=s" => \$sendmail_cmd,
                    "smtp-server=s" => \$smtp_server,
                    "smtp-server-option=s" => \@smtp_server_options,
@@ -506,36 +508,27 @@ my %options = (
                    "smtp-auth=s" => \$smtp_auth,
                    "no-smtp-auth" => sub {$smtp_auth = 'none'},
                    "annotate!" => \$annotate,
-                   "no-annotate" => sub {$annotate = 0},
                    "compose" => \$compose,
                    "quiet" => \$quiet,
                    "cc-cmd=s" => \$cc_cmd,
                    "header-cmd=s" => \$header_cmd,
                    "no-header-cmd" => \$no_header_cmd,
                    "suppress-from!" => \$suppress_from,
-                   "no-suppress-from" => sub {$suppress_from = 0},
                    "suppress-cc=s" => \@suppress_cc,
                    "signed-off-cc|signed-off-by-cc!" => \$signed_off_by_cc,
-                   "no-signed-off-cc|no-signed-off-by-cc" => sub {$signed_off_by_cc = 0},
-                   "cc-cover|cc-cover!" => \$cover_cc,
-                   "no-cc-cover" => sub {$cover_cc = 0},
-                   "to-cover|to-cover!" => \$cover_to,
-                   "no-to-cover" => sub {$cover_to = 0},
+                   "cc-cover!" => \$cover_cc,
+                   "to-cover!" => \$cover_to,
                    "confirm=s" => \$confirm,
                    "dry-run" => \$dry_run,
                    "envelope-sender=s" => \$envelope_sender,
                    "thread!" => \$thread,
-                   "no-thread" => sub {$thread = 0},
                    "validate!" => \$validate,
-                   "no-validate" => sub {$validate = 0},
                    "transfer-encoding=s" => \$target_xfer_encoding,
                    "format-patch!" => \$format_patch,
-                   "no-format-patch" => sub {$format_patch = 0},
                    "8bit-encoding=s" => \$auto_8bit_encoding,
                    "compose-encoding=s" => \$compose_encoding,
                    "force" => \$force,
                    "xmailer!" => \$use_xmailer,
-                   "no-xmailer" => sub {$use_xmailer = 0},
                    "batch-size=i" => \$batch_size,
                    "relogin-delay=i" => \$relogin_delay,
                    "git-completion-helper" => \$git_completion_helper,
index 4e8878f0357ce0d98ae0675b0cabf5c6efb098b2..b0d0a50984bcea740f9486deda974aa3e53cbe1c 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/perl
 # Copyright (C) 2006, Eric Wong <normalperson@yhbt.net>
 # License: GPL v2 or later
-use 5.008;
+use 5.008001;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use strict;
 use vars qw/   $AUTHOR $VERSION
diff --git a/git.c b/git.c
index c67e44dd82d2e4fc01fb841c8f863282607f6ac7..7068a184b0a2947c6d62c3331c6f1a418708c7be 100644 (file)
--- a/git.c
+++ b/git.c
@@ -594,6 +594,7 @@ static struct cmd_struct commands[] = {
        { "remote-fd", cmd_remote_fd, NO_PARSEOPT },
        { "repack", cmd_repack, RUN_SETUP },
        { "replace", cmd_replace, RUN_SETUP },
+       { "replay", cmd_replay, RUN_SETUP },
        { "rerere", cmd_rerere, RUN_SETUP },
        { "reset", cmd_reset, RUN_SETUP },
        { "restore", cmd_restore, RUN_SETUP | NEED_WORK_TREE },
index df3ba2ea99b310c3cdcd5c7a8938eef498b3e92e..7a087f123d7563d27a88751f70ead6172441af20 100755 (executable)
@@ -11956,7 +11956,7 @@ proc formatdate {d} {
 }
 
 # This list of encoding names and aliases is distilled from
-# http://www.iana.org/assignments/character-sets.
+# https://www.iana.org/assignments/character-sets.
 # Not all of them are supported by Tcl.
 set encoding_aliases {
     { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
@@ -12472,7 +12472,7 @@ if {[tk windowingsystem] eq "aqua"} {
 
 catch {
     # follow the XDG base directory specification by default. See
-    # http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
+    # https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
     if {[info exists env(XDG_CONFIG_HOME)] && $env(XDG_CONFIG_HOME) ne ""} {
         # XDG_CONFIG_HOME environment variable is set
         set config_file [file join $env(XDG_CONFIG_HOME) git gitk]
index a58e6b3c44b0ef6175df1417a3ed6f7775bd9953..5bfa4968c4a43193c009045dbd6a6f187a319c76 100644 (file)
@@ -29,7 +29,7 @@ Requirements
 ------------
 
  - Core git tools
- - Perl 5.8
+ - Perl 5.8.1
  - Perl modules: CGI, Encode, Fcntl, File::Find, File::Basename.
  - web server
 
@@ -203,7 +203,7 @@ You can specify the following configuration variables when building GIT:
    created.  [Default: /etc/gitweb.conf]
  * HIGHLIGHT_BIN
    Path to the highlight executable to use (must be the one from
-   http://www.andre-simon.de due to assumptions about parameters and output).
+   http://andre-simon.de/zip/download.php due to assumptions about parameters and output).
    Useful if highlight is not installed on your webserver's PATH.
    [Default: highlight]
 
index e66eb3d9bad7cf627d5ed35e13e32dafb556d5cd..ccd14e0e30c14ef1da31d09cf6949e70b75cf5e0 100755 (executable)
@@ -7,7 +7,7 @@
 #
 # This program is licensed under the GPLv2
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 # handle ACL in file access tests
@@ -122,9 +122,9 @@ our $favicon = "++GITWEB_FAVICON++";
 our $javascript = "++GITWEB_JS++";
 
 # URI and label (title) of GIT logo link
-#our $logo_url = "http://www.kernel.org/pub/software/scm/git/docs/";
+#our $logo_url = "https://www.kernel.org/pub/software/scm/git/docs/";
 #our $logo_label = "git documentation";
-our $logo_url = "http://git-scm.com/";
+our $logo_url = "https://git-scm.com/";
 our $logo_label = "git homepage";
 
 # source of projects list
@@ -197,7 +197,7 @@ our @diff_opts = ('-M'); # taken from git_commit
 our $prevent_xss = 0;
 
 # Path to the highlight executable to use (must be the one from
-# http://www.andre-simon.de due to assumptions about parameters and output).
+# http://andre-simon.de/zip/download.php due to assumptions about parameters and output).
 # Useful if highlight is not installed on your webserver's PATH.
 # [Default: highlight]
 our $highlight_bin = "++HIGHLIGHT_BIN++";
@@ -269,7 +269,7 @@ our %avatar_size = (
 # Leave it undefined (or set to 'undef') to turn off load checking.
 our $maxload = 300;
 
-# configuration for 'highlight' (http://www.andre-simon.de/)
+# configuration for 'highlight' (http://andre-simon.de/doku/highlight/en/highlight.php)
 # match by basename
 our %highlight_basename = (
        #'Program' => 'py',
@@ -728,9 +728,11 @@ our $per_request_config = 1;
 sub read_config_file {
        my $filename = shift;
        return unless defined $filename;
-       # die if there are errors parsing config file
        if (-e $filename) {
                do $filename;
+               # die if there is a problem accessing the file
+               die $! if $!;
+               # die if there are errors parsing config file
                die $@ if $@;
                return 1;
        }
@@ -8193,7 +8195,7 @@ sub git_feed {
        my $have_blame = gitweb_check_feature('blame');
 
        # Atom: http://www.atomenabled.org/developers/syndication/
-       # RSS:  http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
+       # RSS:  https://web.archive.org/web/20030729001534/http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
        if ($format ne 'rss' && $format ne 'atom') {
                die_error(400, "Unknown web feed format");
        }
index 3212601032622fef063f06066bd7131061de36d9..48d2e5101542ad96dcac78aace13d3bbee40d303 100644 (file)
@@ -667,7 +667,7 @@ div.remote {
 }
 
 
-/* Style definition generated by highlight 2.4.5, http://www.andre-simon.de/ */
+/* Style definition generated by highlight 2.4.5, http://andre-simon.de/doku/highlight/en/highlight.php */
 
 /* Highlighting theme definition: */
 
index 018bbb7d4cb72eeb786c3309ba318e7789a02b37..99e3eb8c3d97ae8705ff8e656512479d9f84a90e 100644 (file)
@@ -123,8 +123,8 @@ function addCssRule(selector, style) {
  * NOTE that there are limits and differences compared to native
  * getElementsByClassName as defined by e.g.:
  *   https://developer.mozilla.org/en/DOM/document.getElementsByClassName
- *   http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-getelementsbyclassname
- *   http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-document-getelementsbyclassname
+ *   https://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-getelementsbyclassname
+ *   https://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-document-getelementsbyclassname
  *
  * Namely, this implementation supports only single class name as
  * argument and not set of space-separated tokens representing classes,
@@ -133,11 +133,11 @@ function addCssRule(selector, style) {
  * (via getElementsByTagName).
  *
  * Based on
- *   http://code.google.com/p/getelementsbyclassname/
+ *   https://code.google.com/p/getelementsbyclassname/
  *   http://www.dustindiaz.com/getelementsbyclass/
- *   http://stackoverflow.com/questions/1818865/do-we-have-getelementsbyclassname-in-javascript
+ *   https://stackoverflow.com/questions/1818865/do-we-have-getelementsbyclassname-in-javascript
  *
- * See also http://ejohn.org/blog/getelementsbyclassname-speed-comparison/
+ * See also https://johnresig.com/blog/getelementsbyclassname-speed-comparison/
  *
  * @param {String} class: name of _single_ class to find
  * @param {String} [taghint] limit search to given tags
index 48f43c5a21d34569bdc966eefd652da38003d157..f614105033230c5f5356ff18c49bd47ff500cb35 100644 (file)
@@ -12,7 +12,6 @@
 #include "sigchain.h"
 #include "tempfile.h"
 #include "alias.h"
-#include "environment.h"
 
 static int git_gpg_config(const char *, const char *,
                          const struct config_context *, void *);
@@ -762,23 +761,14 @@ static int git_gpg_config(const char *var, const char *value,
                return 0;
        }
 
-       if (!strcmp(var, "gpg.ssh.defaultkeycommand")) {
-               if (!value)
-                       return config_error_nonbool(var);
+       if (!strcmp(var, "gpg.ssh.defaultkeycommand"))
                return git_config_string(&ssh_default_key_command, var, value);
-       }
 
-       if (!strcmp(var, "gpg.ssh.allowedsignersfile")) {
-               if (!value)
-                       return config_error_nonbool(var);
+       if (!strcmp(var, "gpg.ssh.allowedsignersfile"))
                return git_config_pathname(&ssh_allowed_signers, var, value);
-       }
 
-       if (!strcmp(var, "gpg.ssh.revocationfile")) {
-               if (!value)
-                       return config_error_nonbool(var);
+       if (!strcmp(var, "gpg.ssh.revocationfile"))
                return git_config_pathname(&ssh_revocation_file, var, value);
-       }
 
        if (!strcmp(var, "gpg.program") || !strcmp(var, "gpg.openpgp.program"))
                fmtname = "openpgp";
diff --git a/graph.h b/graph.h
index e88632a0140f70ef25535e23120c898c007b8349..3fd1dcb2e94d4399fc40406f9ec71b319cda0d5f 100644 (file)
--- a/graph.h
+++ b/graph.h
@@ -130,7 +130,7 @@ void graph_setup_line_prefix(struct diff_options *diffopt);
  * This functions must be called BEFORE graph_init() is called.
  *
  * NOTE: This function isn't used in Git outside graph.c but it is used
- * by CGit (http://git.zx2c4.com/cgit/) to use HTML for colors.
+ * by CGit (https://git.zx2c4.com/cgit/) to use HTML for colors.
  */
 void graph_set_column_colors(const char **colors, unsigned short colors_max);
 
@@ -196,7 +196,7 @@ int graph_is_commit_finished(struct git_graph const *graph);
  * graph_update() is called.
  *
  * NOTE: This function isn't used in Git outside graph.c but it is used
- * by CGit (http://git.zx2c4.com/cgit/) to wrap HTML around graph lines.
+ * by CGit (https://git.zx2c4.com/cgit/) to wrap HTML around graph lines.
  */
 int graph_next_line(struct git_graph *graph, struct strbuf *sb);
 
diff --git a/grep.c b/grep.c
index fc2d0c837a378d6a14da32fad69c0f3620447a21..5f23d1a50cabb35732f9515355fe0a85a33d2ff8 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -9,7 +9,6 @@
 #include "xdiff-interface.h"
 #include "diff.h"
 #include "diffcore.h"
-#include "commit.h"
 #include "quote.h"
 #include "help.h"
 
diff --git a/help.c b/help.c
index 6d2ebfbd2a45f134bc45c039cb4cf26b402b8f6c..2dbe57b413b2a8f2e8262fb2c022711aa2d60a2a 100644 (file)
--- a/help.c
+++ b/help.c
@@ -464,8 +464,11 @@ static int get_alias(const char *var, const char *value,
 {
        struct string_list *list = data;
 
-       if (skip_prefix(var, "alias.", &var))
+       if (skip_prefix(var, "alias.", &var)) {
+               if (!value)
+                       return config_error_nonbool(var);
                string_list_append(list, var)->util = xstrdup(value);
+       }
 
        return 0;
 }
index fffda592670e667e81eb92af218f9a4d95d7614d..bec94988bbe563a5048c6dd5905f1a9827193489 100644 (file)
@@ -1,12 +1,12 @@
 #include "git-compat-util.h"
 #include "config.h"
-#include "exec-cmd.h"
 #include "gettext.h"
 #include "hex.h"
 #include "http.h"
 #include "walker.h"
 #include "setup.h"
 #include "strvec.h"
+#include "url.h"
 #include "urlmatch.h"
 #include "trace2.h"
 
index a704f490fdb2c2144b47a5629ed9ee0643ab0d83..12d111374107a7a071ac90d035c5172af2b63c79 100644 (file)
@@ -6,10 +6,8 @@
 #include "tag.h"
 #include "blob.h"
 #include "http.h"
-#include "refs.h"
 #include "diff.h"
 #include "revision.h"
-#include "exec-cmd.h"
 #include "remote.h"
 #include "list-objects.h"
 #include "setup.h"
@@ -17,6 +15,7 @@
 #include "strvec.h"
 #include "tree.h"
 #include "tree-walk.h"
+#include "url.h"
 #include "packfile.h"
 #include "object-store-ll.h"
 #include "commit-reach.h"
@@ -1852,6 +1851,7 @@ int cmd_main(int argc, const char **argv)
 
                if (oideq(&ref->old_oid, &ref->peer_ref->new_oid)) {
                        if (push_verbosely)
+                               /* stable plumbing output; do not modify or localize */
                                fprintf(stderr, "'%s': up-to-date\n", ref->name);
                        if (helper_status)
                                printf("ok %s up to date\n", ref->name);
@@ -1872,6 +1872,7 @@ int cmd_main(int argc, const char **argv)
                                 * commits at the remote end and likely
                                 * we were not up to date to begin with.
                                 */
+                               /* stable plumbing output; do not modify or localize */
                                error("remote '%s' is not an ancestor of\n"
                                      "local '%s'.\n"
                                      "Maybe you are not up-to-date and "
index 78d99f7c4b002cb95196800a62a1de8fbcce6cea..b395ef13279eaa3f672e36512a7816fee209abf9 100644 (file)
@@ -1,6 +1,5 @@
 #include "git-compat-util.h"
 #include "repository.h"
-#include "commit.h"
 #include "hex.h"
 #include "walker.h"
 #include "http.h"
diff --git a/http.c b/http.c
index 8f71bf00d8998af44e177a86d73dbc86872c9726..e73b136e5897bd8fce2f874b20f316141ba59c31 100644 (file)
--- a/http.c
+++ b/http.c
@@ -4,7 +4,6 @@
 #include "http.h"
 #include "config.h"
 #include "pack.h"
-#include "sideband.h"
 #include "run-command.h"
 #include "url.h"
 #include "urlmatch.h"
@@ -15,7 +14,6 @@
 #include "trace.h"
 #include "transport.h"
 #include "packfile.h"
-#include "protocol.h"
 #include "string-list.h"
 #include "object-file.h"
 #include "object-store-ll.h"
@@ -1902,7 +1900,7 @@ static void write_accept_language(struct strbuf *buf)
         * MAX_DECIMAL_PLACES must not be larger than 3. If it is larger than
         * that, q-value will be smaller than 0.001, the minimum q-value the
         * HTTP specification allows. See
-        * http://tools.ietf.org/html/rfc7231#section-5.3.1 for q-value.
+        * https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.1 for q-value.
         */
        const int MAX_DECIMAL_PLACES = 3;
        const int MAX_LANGUAGE_TAGS = 1000;
diff --git a/http.h b/http.h
index 3a409bccd4e6197874a97629c36b050ca923b944..3af19a8bf53e0550fa7c877bb661c34463283adf 100644 (file)
--- a/http.h
+++ b/http.h
@@ -10,7 +10,6 @@ struct packed_git;
 
 #include "strbuf.h"
 #include "remote.h"
-#include "url.h"
 
 #define DEFAULT_MAX_REQUESTS 5
 
index 996651e4f80abd5fa2daf570f9d981934d328aeb..d662811ee83a3531299accb59b8acea0e4de67d8 100644 (file)
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *  along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include "git-compat-util.h"
 #include "config.h"
 #include "credential.h"
-#include "exec-cmd.h"
 #include "gettext.h"
 #include "run-command.h"
 #include "parse-options.h"
 #include "setup.h"
-#include "strbuf.h"
 #if defined(NO_OPENSSL) && !defined(HAVE_OPENSSL_CSPRNG)
 typedef void *SSL;
 #endif
@@ -860,7 +858,7 @@ static void imap_close_store(struct imap_store *ctx)
 
 /*
  * hexchar() and cram() functions are based on the code from the isync
- * project (http://isync.sf.net/).
+ * project (https://isync.sourceforge.io/).
  */
 static char hexchar(unsigned int b)
 {
@@ -1346,7 +1344,7 @@ static int git_imap_config(const char *var, const char *val,
                server.port = git_config_int(var, val, ctx->kvi);
        else if (!strcmp("imap.host", var)) {
                if (!val) {
-                       git_die_config("imap.host", "Missing value for 'imap.host'");
+                       return config_error_nonbool(var);
                } else {
                        if (starts_with(val, "imap:"))
                                val += 5;
index 209355e0f12ce296c120b5e5b9f1651a2daf311f..04413bd1afda5d6693d5dfec2e96645557e1bb45 100644 (file)
@@ -3,8 +3,8 @@
 
 /*
  * JSON data structures are defined at:
- * [1] http://www.ietf.org/rfc/rfc7159.txt
- * [2] http://json.org/
+ * [1] https://www.ietf.org/rfc/rfc7159.txt
+ * [2] https://www.json.org/
  *
  * The JSON-writer API allows one to build JSON data structures using a
  * simple wrapper around a "struct strbuf" buffer.  It is intended as a
diff --git a/kwset.c b/kwset.c
index bbfcf815a567b053bd0206ddb7bb38d138731bd1..695e47b7ccfaf1aaa0e866933dffa8b7fcc51868 100644 (file)
--- a/kwset.c
+++ b/kwset.c
@@ -18,7 +18,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, see <http://www.gnu.org/licenses/>. */
+   along with this program; if not, see <https://www.gnu.org/licenses/>. */
 
 /* Written August 1989 by Mike Haertel.
    The author may be reached (Email) at the address mike@ai.mit.edu,
diff --git a/kwset.h b/kwset.h
index d42a793a301cb6d7ba758a5d592516cdf9d8444f..c722664e5a7eeef9900a86738a5553b3eace49fc 100644 (file)
--- a/kwset.h
+++ b/kwset.h
@@ -20,7 +20,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, see <http://www.gnu.org/licenses/>. */
+   along with this program; if not, see <https://www.gnu.org/licenses/>. */
 
 /* Written August 1989 by Mike Haertel.
    The author may be reached (Email) at the address mike@ai.mit.edu,
index 24a1ecb67799c23570ef485bc382b4bbdf2779d2..8ff6ccb77241fd249f58827ecc1add65356648a4 100644 (file)
@@ -1,8 +1,8 @@
 #include "git-compat-util.h"
+#include "diffcore.h"
 #include "line-range.h"
 #include "hex.h"
 #include "tag.h"
-#include "blob.h"
 #include "tree.h"
 #include "diff.h"
 #include "commit.h"
@@ -12,8 +12,6 @@
 #include "xdiff-interface.h"
 #include "strbuf.h"
 #include "log-tree.h"
-#include "graph.h"
-#include "userdiff.h"
 #include "line-log.h"
 #include "setup.h"
 #include "strvec.h"
index 4291da8d79235d1f477742a11a5fdb67a4346e2e..e9dadbc1a58e2c657511b03f289994143b62bbfb 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef LINE_LOG_H
 #define LINE_LOG_H
 
-#include "diffcore.h"
-
 struct rev_info;
 struct commit;
 struct string_list;
index 47bf0d6f1a253a01baf6e3d0f68dfa5d5df6a0f0..60f0e5ada81967d3743ef866438aaf9b8efdc497 100644 (file)
@@ -1,7 +1,6 @@
 #include "git-compat-util.h"
 #include "line-range.h"
 #include "xdiff-interface.h"
-#include "strbuf.h"
 #include "userdiff.h"
 
 /*
index 8a08b7af49c1ff7750296508102bf67807027bea..c5f363ca6f729ace28756467470e56363fad7a1e 100644 (file)
@@ -1,11 +1,6 @@
 #include "git-compat-util.h"
-#include "commit.h"
 #include "config.h"
 #include "gettext.h"
-#include "revision.h"
-#include "strvec.h"
-#include "list-objects.h"
-#include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
 #include "promisor-remote.h"
 #include "trace.h"
index 9327ccd5057cb6fffc9fcd9a92f2939a50b50253..da287cc8e0dd2935cb230dd256d5f268495ed40f 100644 (file)
@@ -2,14 +2,9 @@
 #include "dir.h"
 #include "gettext.h"
 #include "hex.h"
-#include "tag.h"
 #include "commit.h"
-#include "tree.h"
-#include "blob.h"
 #include "diff.h"
-#include "tree-walk.h"
 #include "revision.h"
-#include "list-objects.h"
 #include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
 #include "oidmap.h"
diff --git a/list.h b/list.h
index 362a4cd7f5f10f17f174086911dc30e60e07e6ec..98428010f4d131580f8d3fa640efc6a10651a35b 100644 (file)
--- a/list.h
+++ b/list.h
@@ -19,7 +19,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, see
- * <http://www.gnu.org/licenses/>.
+ * <https://www.gnu.org/licenses/>.
  */
 
 #ifndef LIST_H
index 504da6b519edff523bd542d07684a4c27a6c10bb..337b9334cdbafe0d3ffd2bf2fd36898f6d112410 100644 (file)
@@ -2,6 +2,7 @@
 #include "commit-reach.h"
 #include "config.h"
 #include "diff.h"
+#include "diffcore.h"
 #include "environment.h"
 #include "hex.h"
 #include "object-name.h"
index 0e49b932c308b24a5654910d5e0197cf1388162e..819cbefee3de07c7cb7f8f92e1fd715b1b2f7af6 100644 (file)
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -5,7 +5,6 @@
 #include "hex.h"
 #include "repository.h"
 #include "refs.h"
-#include "remote.h"
 #include "strvec.h"
 #include "ls-refs.h"
 #include "pkt-line.h"
index a07d2da16dedb2779a9797cfa185ec29d9321491..94b9b0abf228b891bc1df5a71bcd96807ed982a8 100644 (file)
@@ -58,12 +58,13 @@ static void parse_bogus_from(struct mailinfo *mi, const struct strbuf *line)
 
 static const char *unquote_comment(struct strbuf *outbuf, const char *in)
 {
-       int c;
        int take_next_literally = 0;
+       int depth = 1;
 
        strbuf_addch(outbuf, '(');
 
-       while ((c = *in++) != 0) {
+       while (*in) {
+               int c = *in++;
                if (take_next_literally == 1) {
                        take_next_literally = 0;
                } else {
@@ -72,11 +73,14 @@ static const char *unquote_comment(struct strbuf *outbuf, const char *in)
                                take_next_literally = 1;
                                continue;
                        case '(':
-                               in = unquote_comment(outbuf, in);
+                               strbuf_addch(outbuf, '(');
+                               depth++;
                                continue;
                        case ')':
                                strbuf_addch(outbuf, ')');
-                               return in;
+                               if (!--depth)
+                                       return in;
+                               continue;
                        }
                }
 
@@ -88,10 +92,10 @@ static const char *unquote_comment(struct strbuf *outbuf, const char *in)
 
 static const char *unquote_quoted_string(struct strbuf *outbuf, const char *in)
 {
-       int c;
        int take_next_literally = 0;
 
-       while ((c = *in++) != 0) {
+       while (*in) {
+               int c = *in++;
                if (take_next_literally == 1) {
                        take_next_literally = 0;
                } else {
@@ -1253,6 +1257,8 @@ static int git_mailinfo_config(const char *var, const char *value,
                return 0;
        }
        if (!strcmp(var, "mailinfo.quotedcr")) {
+               if (!value)
+                       return config_error_nonbool(var);
                if (mailinfo_parse_quoted_cr_action(value, &mi->quoted_cr) != 0)
                        return error(_("bad action '%s' for '%s'"), value, var);
                return 0;
index c34846d176c886ecb9f028ec48bb847402d242f9..c7d62560201984db8efcb675da9b4ede933c5cbd 100644 (file)
@@ -89,9 +89,7 @@ void *mem_pool_alloc(struct mem_pool *pool, size_t len)
        struct mp_block *p = NULL;
        void *r;
 
-       /* round up to a 'GIT_MAX_ALIGNMENT' alignment */
-       if (len & (GIT_MAX_ALIGNMENT - 1))
-               len += GIT_MAX_ALIGNMENT - (len & (GIT_MAX_ALIGNMENT - 1));
+       len = DIV_ROUND_UP(len, GIT_MAX_ALIGNMENT) * GIT_MAX_ALIGNMENT;
 
        if (pool->mp_block &&
            pool->mp_block->end - pool->mp_block->next_free >= len)
@@ -99,9 +97,9 @@ void *mem_pool_alloc(struct mem_pool *pool, size_t len)
 
        if (!p) {
                if (len >= (pool->block_alloc / 2))
-                       return mem_pool_alloc_block(pool, len, pool->mp_block);
-
-               p = mem_pool_alloc_block(pool, pool->block_alloc, NULL);
+                       p = mem_pool_alloc_block(pool, len, pool->mp_block);
+               else
+                       p = mem_pool_alloc_block(pool, pool->block_alloc, NULL);
        }
 
        r = p->next_free;
index 9293cbf75c8ab737a2cf7267d3e31ffd93a48dc8..2f659fd01432d4249d1d2be1876f0edfd8c9cd09 100644 (file)
@@ -1,6 +1,4 @@
 #include "git-compat-util.h"
-#include "run-command.h"
-#include "xdiff-interface.h"
 #include "merge-ll.h"
 #include "blob.h"
 #include "merge-blobs.h"
index 8fcf2d3710ed1a87bd851abe04dd22a32fafdf84..1df58ebaac08582d042268f5a07cf5cbd9d7b1c3 100644 (file)
@@ -301,7 +301,7 @@ static int read_merge_config(const char *var, const char *value,
 
        if (!strcmp("driver", key)) {
                if (!value)
-                       return error("%s: lacks value", var);
+                       return config_error_nonbool(var);
                /*
                 * merge.<name>.driver specifies the command line:
                 *
index 6491070d965835f9202b68fbdae72cb6cd6c987f..d72fd04f581fbb99732d1e5c7b48a1d4ae83efc0 100644 (file)
@@ -19,7 +19,6 @@
 
 #include "alloc.h"
 #include "attr.h"
-#include "blob.h"
 #include "cache-tree.h"
 #include "commit.h"
 #include "commit-reach.h"
 #include "path.h"
 #include "promisor-remote.h"
 #include "read-cache-ll.h"
+#include "refs.h"
 #include "revision.h"
 #include "sparse-index.h"
 #include "strmap.h"
-#include "submodule-config.h"
-#include "submodule.h"
 #include "trace2.h"
 #include "tree.h"
 #include "unpack-trees.h"
@@ -4662,9 +4660,6 @@ void merge_switch_to_result(struct merge_options *opt,
 {
        assert(opt->priv == NULL);
        if (result->clean >= 0 && update_worktree_and_index) {
-               const char *filename;
-               FILE *fp;
-
                trace2_region_enter("merge", "checkout", opt->repo);
                if (checkout(opt, head, result->tree)) {
                        /* failure to function */
@@ -4690,10 +4685,17 @@ void merge_switch_to_result(struct merge_options *opt,
                trace2_region_leave("merge", "record_conflicted", opt->repo);
 
                trace2_region_enter("merge", "write_auto_merge", opt->repo);
-               filename = git_path_auto_merge(opt->repo);
-               fp = xfopen(filename, "w");
-               fprintf(fp, "%s\n", oid_to_hex(&result->tree->object.oid));
-               fclose(fp);
+               if (refs_update_ref(get_main_ref_store(opt->repo), "", "AUTO_MERGE",
+                                   &result->tree->object.oid, NULL, REF_NO_DEREF,
+                                   UPDATE_REFS_MSG_ON_ERR)) {
+                       /* failure to function */
+                       opt->priv = NULL;
+                       result->clean = -1;
+                       merge_finalize(opt, result);
+                       trace2_region_leave("merge", "write_auto_merge",
+                                           opt->repo);
+                       return;
+               }
                trace2_region_leave("merge", "write_auto_merge", opt->repo);
        }
        if (display_update_msgs)
index e3beb0801b115691d84b5da3403bc52abcb97336..a0c3e7a2d9105dd895f0ae2613d0f87f00b8d794 100644 (file)
@@ -6,10 +6,7 @@
 #include "git-compat-util.h"
 #include "merge-recursive.h"
 
-#include "advice.h"
 #include "alloc.h"
-#include "attr.h"
-#include "blob.h"
 #include "cache-tree.h"
 #include "commit.h"
 #include "commit-reach.h"
@@ -32,8 +29,6 @@
 #include "revision.h"
 #include "sparse-index.h"
 #include "string-list.h"
-#include "submodule-config.h"
-#include "submodule.h"
 #include "symlinks.h"
 #include "tag.h"
 #include "tree-walk.h"
diff --git a/merge.c b/merge.c
index b60925459c292bbb4a9daae46534edfbb3eca756..ca89b312d173530f8c8a1e5d07470b9d2404e9d4 100644 (file)
--- a/merge.c
+++ b/merge.c
@@ -1,6 +1,4 @@
 #include "git-compat-util.h"
-#include "diff.h"
-#include "diffcore.h"
 #include "gettext.h"
 #include "hash.h"
 #include "hex.h"
@@ -13,7 +11,6 @@
 #include "tree.h"
 #include "tree-walk.h"
 #include "unpack-trees.h"
-#include "dir.h"
 
 static const char *merge_argument(struct commit *commit)
 {
diff --git a/midx.c b/midx.c
index 2f3863c936a4c1c9035f90ae28b73887cf8f24b2..85e1c2cd1287b34e91d9dcccc74d330c93ff809c 100644 (file)
--- a/midx.c
+++ b/midx.c
@@ -21,6 +21,7 @@
 #include "refs.h"
 #include "revision.h"
 #include "list-objects.h"
+#include "pack-revindex.h"
 
 #define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
 #define MIDX_VERSION 1
@@ -33,6 +34,7 @@
 
 #define MIDX_CHUNK_ALIGNMENT 4
 #define MIDX_CHUNKID_PACKNAMES 0x504e414d /* "PNAM" */
+#define MIDX_CHUNKID_BITMAPPEDPACKS 0x42544d50 /* "BTMP" */
 #define MIDX_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
 #define MIDX_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
 #define MIDX_CHUNKID_OBJECTOFFSETS 0x4f4f4646 /* "OOFF" */
@@ -41,6 +43,7 @@
 #define MIDX_CHUNK_FANOUT_SIZE (sizeof(uint32_t) * 256)
 #define MIDX_CHUNK_OFFSET_WIDTH (2 * sizeof(uint32_t))
 #define MIDX_CHUNK_LARGE_OFFSET_WIDTH (sizeof(uint64_t))
+#define MIDX_CHUNK_BITMAPPED_PACKS_WIDTH (2 * sizeof(uint32_t))
 #define MIDX_LARGE_OFFSET_NEEDED 0x80000000
 
 #define PACK_EXPIRED UINT_MAX
@@ -64,6 +67,7 @@ void get_midx_rev_filename(struct strbuf *out, struct multi_pack_index *m)
 static int midx_read_oid_fanout(const unsigned char *chunk_start,
                                size_t chunk_size, void *data)
 {
+       int i;
        struct multi_pack_index *m = data;
        m->chunk_oid_fanout = (uint32_t *)chunk_start;
 
@@ -71,6 +75,16 @@ static int midx_read_oid_fanout(const unsigned char *chunk_start,
                error(_("multi-pack-index OID fanout is of the wrong size"));
                return 1;
        }
+       for (i = 0; i < 255; i++) {
+               uint32_t oid_fanout1 = ntohl(m->chunk_oid_fanout[i]);
+               uint32_t oid_fanout2 = ntohl(m->chunk_oid_fanout[i+1]);
+
+               if (oid_fanout1 > oid_fanout2) {
+                       error(_("oid fanout out of order: fanout[%d] = %"PRIx32" > %"PRIx32" = fanout[%d]"),
+                             i, oid_fanout1, oid_fanout2, i + 1);
+                       return 1;
+               }
+       }
        m->num_objects = ntohl(m->chunk_oid_fanout[255]);
        return 0;
 }
@@ -164,6 +178,8 @@ struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local
 
        m->num_packs = get_be32(m->data + MIDX_BYTE_NUM_PACKS);
 
+       m->preferred_pack_idx = -1;
+
        cf = init_chunkfile(NULL);
 
        if (read_table_of_contents(cf, m->data, midx_size,
@@ -182,6 +198,9 @@ struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local
 
        pair_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS, &m->chunk_large_offsets,
                   &m->chunk_large_offsets_len);
+       pair_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
+                  (const unsigned char **)&m->chunk_bitmapped_packs,
+                  &m->chunk_bitmapped_packs_len);
 
        if (git_env_bool("GIT_TEST_MIDX_READ_RIDX", 1))
                pair_chunk(cf, MIDX_CHUNKID_REVINDEX, &m->chunk_revindex,
@@ -275,6 +294,26 @@ int prepare_midx_pack(struct repository *r, struct multi_pack_index *m, uint32_t
        return 0;
 }
 
+int nth_bitmapped_pack(struct repository *r, struct multi_pack_index *m,
+                      struct bitmapped_pack *bp, uint32_t pack_int_id)
+{
+       if (!m->chunk_bitmapped_packs)
+               return error(_("MIDX does not contain the BTMP chunk"));
+
+       if (prepare_midx_pack(r, m, pack_int_id))
+               return error(_("could not load bitmapped pack %"PRIu32), pack_int_id);
+
+       bp->p = m->packs[pack_int_id];
+       bp->bitmap_pos = get_be32((char *)m->chunk_bitmapped_packs +
+                                 MIDX_CHUNK_BITMAPPED_PACKS_WIDTH * pack_int_id);
+       bp->bitmap_nr = get_be32((char *)m->chunk_bitmapped_packs +
+                                MIDX_CHUNK_BITMAPPED_PACKS_WIDTH * pack_int_id +
+                                sizeof(uint32_t));
+       bp->pack_int_id = pack_int_id;
+
+       return 0;
+}
+
 int bsearch_midx(const struct object_id *oid, struct multi_pack_index *m, uint32_t *result)
 {
        return bsearch_hash(oid->hash, m->chunk_oid_fanout, m->chunk_oid_lookup,
@@ -392,7 +431,8 @@ static int cmp_idx_or_pack_name(const char *idx_or_pack_name,
        return strcmp(idx_or_pack_name, idx_name);
 }
 
-int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name)
+int midx_locate_pack(struct multi_pack_index *m, const char *idx_or_pack_name,
+                    uint32_t *pos)
 {
        uint32_t first = 0, last = m->num_packs;
 
@@ -403,8 +443,11 @@ int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name)
 
                current = m->pack_names[mid];
                cmp = cmp_idx_or_pack_name(idx_or_pack_name, current);
-               if (!cmp)
+               if (!cmp) {
+                       if (pos)
+                               *pos = mid;
                        return 1;
+               }
                if (cmp > 0) {
                        first = mid + 1;
                        continue;
@@ -415,6 +458,28 @@ int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name)
        return 0;
 }
 
+int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name)
+{
+       return midx_locate_pack(m, idx_or_pack_name, NULL);
+}
+
+int midx_preferred_pack(struct multi_pack_index *m, uint32_t *pack_int_id)
+{
+       if (m->preferred_pack_idx == -1) {
+               if (load_midx_revindex(m) < 0) {
+                       m->preferred_pack_idx = -2;
+                       return -1;
+               }
+
+               m->preferred_pack_idx =
+                       nth_midxed_pack_int_id(m, pack_pos_to_midx(m, 0));
+       } else if (m->preferred_pack_idx == -2)
+               return -1; /* no revindex */
+
+       *pack_int_id = m->preferred_pack_idx;
+       return 0;
+}
+
 int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, int local)
 {
        struct multi_pack_index *m;
@@ -457,13 +522,31 @@ static size_t write_midx_header(struct hashfile *f,
        return MIDX_HEADER_SIZE;
 }
 
+#define BITMAP_POS_UNKNOWN (~((uint32_t)0))
+
 struct pack_info {
        uint32_t orig_pack_int_id;
        char *pack_name;
        struct packed_git *p;
+
+       uint32_t bitmap_pos;
+       uint32_t bitmap_nr;
+
        unsigned expired : 1;
 };
 
+static void fill_pack_info(struct pack_info *info,
+                          struct packed_git *p, const char *pack_name,
+                          uint32_t orig_pack_int_id)
+{
+       memset(info, 0, sizeof(struct pack_info));
+
+       info->orig_pack_int_id = orig_pack_int_id;
+       info->pack_name = xstrdup(pack_name);
+       info->p = p;
+       info->bitmap_pos = BITMAP_POS_UNKNOWN;
+}
+
 static int pack_info_compare(const void *_a, const void *_b)
 {
        struct pack_info *a = (struct pack_info *)_a;
@@ -504,6 +587,7 @@ static void add_pack_to_midx(const char *full_path, size_t full_path_len,
                             const char *file_name, void *data)
 {
        struct write_midx_context *ctx = data;
+       struct packed_git *p;
 
        if (ends_with(file_name, ".idx")) {
                display_progress(ctx->progress, ++ctx->pack_paths_checked);
@@ -530,27 +614,22 @@ static void add_pack_to_midx(const char *full_path, size_t full_path_len,
 
                ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc);
 
-               ctx->info[ctx->nr].p = add_packed_git(full_path,
-                                                     full_path_len,
-                                                     0);
-
-               if (!ctx->info[ctx->nr].p) {
+               p = add_packed_git(full_path, full_path_len, 0);
+               if (!p) {
                        warning(_("failed to add packfile '%s'"),
                                full_path);
                        return;
                }
 
-               if (open_pack_index(ctx->info[ctx->nr].p)) {
+               if (open_pack_index(p)) {
                        warning(_("failed to open pack-index '%s'"),
                                full_path);
-                       close_pack(ctx->info[ctx->nr].p);
-                       FREE_AND_NULL(ctx->info[ctx->nr].p);
+                       close_pack(p);
+                       free(p);
                        return;
                }
 
-               ctx->info[ctx->nr].pack_name = xstrdup(file_name);
-               ctx->info[ctx->nr].orig_pack_int_id = ctx->nr;
-               ctx->info[ctx->nr].expired = 0;
+               fill_pack_info(&ctx->info[ctx->nr], p, file_name, ctx->nr);
                ctx->nr++;
        }
 }
@@ -806,6 +885,26 @@ static int write_midx_pack_names(struct hashfile *f, void *data)
        return 0;
 }
 
+static int write_midx_bitmapped_packs(struct hashfile *f, void *data)
+{
+       struct write_midx_context *ctx = data;
+       size_t i;
+
+       for (i = 0; i < ctx->nr; i++) {
+               struct pack_info *pack = &ctx->info[i];
+               if (pack->expired)
+                       continue;
+
+               if (pack->bitmap_pos == BITMAP_POS_UNKNOWN && pack->bitmap_nr)
+                       BUG("pack '%s' has no bitmap position, but has %d bitmapped object(s)",
+                           pack->pack_name, pack->bitmap_nr);
+
+               hashwrite_be32(f, pack->bitmap_pos);
+               hashwrite_be32(f, pack->bitmap_nr);
+       }
+       return 0;
+}
+
 static int write_midx_oid_fanout(struct hashfile *f,
                                 void *data)
 {
@@ -973,8 +1072,19 @@ static uint32_t *midx_pack_order(struct write_midx_context *ctx)
        QSORT(data, ctx->entries_nr, midx_pack_order_cmp);
 
        ALLOC_ARRAY(pack_order, ctx->entries_nr);
-       for (i = 0; i < ctx->entries_nr; i++)
+       for (i = 0; i < ctx->entries_nr; i++) {
+               struct pack_midx_entry *e = &ctx->entries[data[i].nr];
+               struct pack_info *pack = &ctx->info[ctx->pack_perm[e->pack_int_id]];
+               if (pack->bitmap_pos == BITMAP_POS_UNKNOWN)
+                       pack->bitmap_pos = i;
+               pack->bitmap_nr++;
                pack_order[i] = data[i].nr;
+       }
+       for (i = 0; i < ctx->nr; i++) {
+               struct pack_info *pack = &ctx->info[ctx->pack_perm[i]];
+               if (pack->bitmap_pos == BITMAP_POS_UNKNOWN)
+                       pack->bitmap_pos = 0;
+       }
        free(data);
 
        trace2_region_leave("midx", "midx_pack_order", the_repository);
@@ -1275,6 +1385,7 @@ static int write_midx_internal(const char *object_dir,
        struct hashfile *f = NULL;
        struct lock_file lk;
        struct write_midx_context ctx = { 0 };
+       int bitmapped_packs_concat_len = 0;
        int pack_name_concat_len = 0;
        int dropped_packs = 0;
        int result = 0;
@@ -1310,11 +1421,6 @@ static int write_midx_internal(const char *object_dir,
                for (i = 0; i < ctx.m->num_packs; i++) {
                        ALLOC_GROW(ctx.info, ctx.nr + 1, ctx.alloc);
 
-                       ctx.info[ctx.nr].orig_pack_int_id = i;
-                       ctx.info[ctx.nr].pack_name = xstrdup(ctx.m->pack_names[i]);
-                       ctx.info[ctx.nr].p = ctx.m->packs[i];
-                       ctx.info[ctx.nr].expired = 0;
-
                        if (flags & MIDX_WRITE_REV_INDEX) {
                                /*
                                 * If generating a reverse index, need to have
@@ -1330,10 +1436,10 @@ static int write_midx_internal(const char *object_dir,
                                if (open_pack_index(ctx.m->packs[i]))
                                        die(_("could not open index for %s"),
                                            ctx.m->packs[i]->pack_name);
-                               ctx.info[ctx.nr].p = ctx.m->packs[i];
                        }
 
-                       ctx.nr++;
+                       fill_pack_info(&ctx.info[ctx.nr++], ctx.m->packs[i],
+                                      ctx.m->pack_names[i], i);
                }
        }
 
@@ -1492,8 +1598,10 @@ static int write_midx_internal(const char *object_dir,
        }
 
        for (i = 0; i < ctx.nr; i++) {
-               if (!ctx.info[i].expired)
-                       pack_name_concat_len += strlen(ctx.info[i].pack_name) + 1;
+               if (ctx.info[i].expired)
+                       continue;
+               pack_name_concat_len += strlen(ctx.info[i].pack_name) + 1;
+               bitmapped_packs_concat_len += 2 * sizeof(uint32_t);
        }
 
        /* Check that the preferred pack wasn't expired (if given). */
@@ -1553,6 +1661,9 @@ static int write_midx_internal(const char *object_dir,
                add_chunk(cf, MIDX_CHUNKID_REVINDEX,
                          st_mult(ctx.entries_nr, sizeof(uint32_t)),
                          write_midx_revindex);
+               add_chunk(cf, MIDX_CHUNKID_BITMAPPEDPACKS,
+                         bitmapped_packs_concat_len,
+                         write_midx_bitmapped_packs);
        }
 
        write_midx_header(f, get_num_chunks(cf), ctx.nr - dropped_packs);
@@ -1592,8 +1703,13 @@ static int write_midx_internal(const char *object_dir,
                                      flags) < 0) {
                        error(_("could not write multi-pack bitmap"));
                        result = 1;
+                       clear_packing_data(&pdata);
+                       free(commits);
                        goto cleanup;
                }
+
+               clear_packing_data(&pdata);
+               free(commits);
        }
        /*
         * NOTE: Do not use ctx.entries beyond this point, since it might
@@ -1782,15 +1898,6 @@ int verify_midx_file(struct repository *r, const char *object_dir, unsigned flag
        }
        stop_progress(&progress);
 
-       for (i = 0; i < 255; i++) {
-               uint32_t oid_fanout1 = ntohl(m->chunk_oid_fanout[i]);
-               uint32_t oid_fanout2 = ntohl(m->chunk_oid_fanout[i + 1]);
-
-               if (oid_fanout1 > oid_fanout2)
-                       midx_report(_("oid fanout out of order: fanout[%d] = %"PRIx32" > %"PRIx32" = fanout[%d]"),
-                                   i, oid_fanout1, oid_fanout2, i + 1);
-       }
-
        if (m->num_objects == 0) {
                midx_report(_("the midx contains no oid"));
                /*
diff --git a/midx.h b/midx.h
index a5d98919c857b812789f44d86fb6ad13fb7e0560..b374a7afafb867a9ef9f0fd9913ec2552914859c 100644 (file)
--- a/midx.h
+++ b/midx.h
@@ -1,12 +1,12 @@
 #ifndef MIDX_H
 #define MIDX_H
 
-#include "repository.h"
 #include "string-list.h"
 
 struct object_id;
 struct pack_entry;
 struct repository;
+struct bitmapped_pack;
 
 #define GIT_TEST_MULTI_PACK_INDEX "GIT_TEST_MULTI_PACK_INDEX"
 #define GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP \
@@ -28,11 +28,14 @@ struct multi_pack_index {
        unsigned char num_chunks;
        uint32_t num_packs;
        uint32_t num_objects;
+       int preferred_pack_idx;
 
        int local;
 
        const unsigned char *chunk_pack_names;
        size_t chunk_pack_names_len;
+       const uint32_t *chunk_bitmapped_packs;
+       size_t chunk_bitmapped_packs_len;
        const uint32_t *chunk_oid_fanout;
        const unsigned char *chunk_oid_lookup;
        const unsigned char *chunk_object_offsets;
@@ -58,6 +61,8 @@ void get_midx_rev_filename(struct strbuf *out, struct multi_pack_index *m);
 
 struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local);
 int prepare_midx_pack(struct repository *r, struct multi_pack_index *m, uint32_t pack_int_id);
+int nth_bitmapped_pack(struct repository *r, struct multi_pack_index *m,
+                      struct bitmapped_pack *bp, uint32_t pack_int_id);
 int bsearch_midx(const struct object_id *oid, struct multi_pack_index *m, uint32_t *result);
 off_t nth_midxed_offset(struct multi_pack_index *m, uint32_t pos);
 uint32_t nth_midxed_pack_int_id(struct multi_pack_index *m, uint32_t pos);
@@ -65,7 +70,11 @@ struct object_id *nth_midxed_object_oid(struct object_id *oid,
                                        struct multi_pack_index *m,
                                        uint32_t n);
 int fill_midx_entry(struct repository *r, const struct object_id *oid, struct pack_entry *e, struct multi_pack_index *m);
-int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name);
+int midx_contains_pack(struct multi_pack_index *m,
+                      const char *idx_or_pack_name);
+int midx_locate_pack(struct multi_pack_index *m, const char *idx_or_pack_name,
+                    uint32_t *pos);
+int midx_preferred_pack(struct multi_pack_index *m, uint32_t *pack_int_id);
 int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, int local);
 
 /*
index de39028ab7f88058c87f735a79185da2f3b5e749..65e3c200084aa4a198145e2433ad6055425ae2fa 100644 (file)
@@ -1,6 +1,5 @@
 #include "git-compat-util.h"
 #include "noop.h"
-#include "../commit.h"
 #include "../fetch-negotiator.h"
 
 static void known_common(struct fetch_negotiator *n UNUSED,
diff --git a/neue b/neue
new file mode 100644 (file)
index 0000000..e69de29
index 97c031c26ec7c7c2fdade8478fa039d470820bad..6197a5a4556400603ed7e669d5cccb0db4089e21 100644 (file)
@@ -5,7 +5,6 @@
 #include "gettext.h"
 #include "refs.h"
 #include "notes-utils.h"
-#include "repository.h"
 #include "strbuf.h"
 
 void create_notes_commit(struct repository *r,
@@ -112,6 +111,8 @@ static int notes_rewrite_config(const char *k, const char *v,
                }
                return 0;
        } else if (!c->refs_from_env && !strcmp(k, "notes.rewriteref")) {
+               if (!v)
+                       return config_error_nonbool(k);
                /* note that a refs/ prefix is implied in the
                 * underlying for_each_glob_ref */
                if (starts_with(v, "refs/notes/"))
diff --git a/notes.c b/notes.c
index 1ef2a331ce9302e58cb2078e58b7b40a94972b3e..fed1eda80cd7e41a2e21af26aed81e1dd472b27c 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -5,8 +5,6 @@
 #include "notes.h"
 #include "object-name.h"
 #include "object-store-ll.h"
-#include "blob.h"
-#include "tree.h"
 #include "utf8.h"
 #include "strbuf.h"
 #include "tree-walk.h"
index 7c7afe5793641c06b10dee6f1f6735a1a476c7d9..619f039ebc7ceb52467e4810c5c4cf3afabb907a 100644 (file)
 #include "hex.h"
 #include "string-list.h"
 #include "lockfile.h"
-#include "delta.h"
 #include "pack.h"
-#include "blob.h"
 #include "commit.h"
 #include "run-command.h"
-#include "tag.h"
-#include "tree.h"
-#include "tree-walk.h"
 #include "refs.h"
-#include "pack-revindex.h"
-#include "hash-lookup.h"
 #include "bulk-checkin.h"
 #include "repository.h"
 #include "replace-object.h"
 #include "streaming.h"
 #include "dir.h"
 #include "list.h"
-#include "mergesort.h"
 #include "quote.h"
 #include "packfile.h"
 #include "object-file.h"
index 0bfa29dbbfe9b489e454c03b419092294427425d..3a2ef5d6800173fa669bdfcb2612bf21a7c6417a 100644 (file)
@@ -8,7 +8,6 @@
 #include "tag.h"
 #include "commit.h"
 #include "tree.h"
-#include "blob.h"
 #include "tree-walk.h"
 #include "refs.h"
 #include "remote.h"
@@ -21,7 +20,6 @@
 #include "read-cache-ll.h"
 #include "repository.h"
 #include "setup.h"
-#include "submodule.h"
 #include "midx.h"
 #include "commit-reach.h"
 #include "date.h"
index 9acb74412ef0fc28589262b392c9d56c7d381137..5b954088254b21a6cbc90b41d0dac415a552d6b7 100644 (file)
@@ -1,3 +1,4 @@
 fuzz-commit-graph
+fuzz-date
 fuzz-pack-headers
 fuzz-pack-idx
diff --git a/oss-fuzz/dummy-cmd-main.c b/oss-fuzz/dummy-cmd-main.c
new file mode 100644 (file)
index 0000000..071cb23
--- /dev/null
@@ -0,0 +1,14 @@
+#include "git-compat-util.h"
+
+/*
+ * When linking the fuzzers, we link against common-main.o to pick up some
+ * symbols. However, even though we ignore common-main:main(), we still need to
+ * provide all the symbols it references. In the fuzzers' case, we need to
+ * provide a dummy cmd_main() for the linker to be happy. It will never be
+ * executed.
+ */
+
+int cmd_main(int argc, const char **argv) {
+       BUG("We should not execute cmd_main() from a fuzz target");
+       return 1;
+}
diff --git a/oss-fuzz/fuzz-date.c b/oss-fuzz/fuzz-date.c
new file mode 100644 (file)
index 0000000..036378b
--- /dev/null
@@ -0,0 +1,49 @@
+#include "git-compat-util.h"
+#include "date.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+       int local;
+       int num;
+       char *str;
+       int16_t tz;
+       timestamp_t ts;
+       enum date_mode_type dmtype;
+       struct date_mode *dm;
+
+       if (size <= 4)
+               /*
+                * we use the first byte to fuzz dmtype and the
+                * second byte to fuzz local, then the next two
+                * bytes to fuzz tz offset. The remainder
+                * (at least one byte) is fed as input to
+                * approxidate_careful().
+                */
+               return 0;
+
+       local = !!(*data++ & 0x10);
+       num = *data++ % DATE_UNIX;
+       if (num >= DATE_STRFTIME)
+               num++;
+       dmtype = (enum date_mode_type)num;
+       size -= 2;
+
+       tz = *data++;
+       tz = (tz << 8) | *data++;
+       size -= 2;
+
+       str = xmemdupz(data, size);
+
+       ts = approxidate_careful(str, &num);
+       free(str);
+
+       dm = date_mode_from_type(dmtype);
+       dm->local = local;
+       show_date(ts, (int)tz, dm);
+
+       date_mode_release(dm);
+
+       return 0;
+}
index f4ecdf8b0e3581cb6d92d0b88832c37adf749dc1..990a9498d731942bf2b01580a8ac77d08ba04d5f 100644 (file)
@@ -4,12 +4,9 @@
 #include "hex.h"
 #include "object-store-ll.h"
 #include "commit.h"
-#include "tag.h"
 #include "diff.h"
 #include "revision.h"
-#include "list-objects.h"
 #include "progress.h"
-#include "pack-revindex.h"
 #include "pack.h"
 #include "pack-bitmap.h"
 #include "hash-lookup.h"
@@ -198,6 +195,13 @@ struct bb_commit {
        unsigned idx; /* within selected array */
 };
 
+static void clear_bb_commit(struct bb_commit *commit)
+{
+       free_commit_list(commit->reverse_edges);
+       bitmap_free(commit->commit_mask);
+       bitmap_free(commit->bitmap);
+}
+
 define_commit_slab(bb_data, struct bb_commit);
 
 struct bitmap_builder {
@@ -339,7 +343,7 @@ next:
 
 static void bitmap_builder_clear(struct bitmap_builder *bb)
 {
-       clear_bb_data(&bb->data);
+       deep_clear_bb_data(&bb->data, clear_bb_commit);
        free(bb->commits);
        bb->commits_nr = bb->commits_alloc = 0;
 }
index 0260890341b5a36af3ee1ec248282e2f1f1ebc53..229a11fb00fc20257385aca4cd653c26f8ce6b31 100644 (file)
@@ -338,7 +338,7 @@ static int open_midx_bitmap_1(struct bitmap_index *bitmap_git,
        struct stat st;
        char *bitmap_name = midx_bitmap_filename(midx);
        int fd = git_open(bitmap_name);
-       uint32_t i;
+       uint32_t i, preferred_pack;
        struct packed_git *preferred;
 
        if (fd < 0) {
@@ -393,7 +393,12 @@ static int open_midx_bitmap_1(struct bitmap_index *bitmap_git,
                }
        }
 
-       preferred = bitmap_git->midx->packs[midx_preferred_pack(bitmap_git)];
+       if (midx_preferred_pack(bitmap_git->midx, &preferred_pack) < 0) {
+               warning(_("could not determine MIDX preferred pack"));
+               goto cleanup;
+       }
+
+       preferred = bitmap_git->midx->packs[preferred_pack];
        if (!is_pack_valid(preferred)) {
                warning(_("preferred pack (%s) is invalid"),
                        preferred->pack_name);
@@ -1280,6 +1285,8 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
                base = fill_in_bitmap(bitmap_git, revs, base, seen);
        }
 
+       object_list_free(&not_mapped);
+
        return base;
 }
 
@@ -1834,8 +1841,10 @@ cleanup:
  * -1 means "stop trying further objects"; 0 means we may or may not have
  * reused, but you can keep feeding bits.
  */
-static int try_partial_reuse(struct packed_git *pack,
-                            size_t pos,
+static int try_partial_reuse(struct bitmap_index *bitmap_git,
+                            struct bitmapped_pack *pack,
+                            size_t bitmap_pos,
+                            uint32_t pack_pos,
                             struct bitmap *reuse,
                             struct pack_window **w_curs)
 {
@@ -1843,40 +1852,18 @@ static int try_partial_reuse(struct packed_git *pack,
        enum object_type type;
        unsigned long size;
 
-       /*
-        * try_partial_reuse() is called either on (a) objects in the
-        * bitmapped pack (in the case of a single-pack bitmap) or (b)
-        * objects in the preferred pack of a multi-pack bitmap.
-        * Importantly, the latter can pretend as if only a single pack
-        * exists because:
-        *
-        *   - The first pack->num_objects bits of a MIDX bitmap are
-        *     reserved for the preferred pack, and
-        *
-        *   - Ties due to duplicate objects are always resolved in
-        *     favor of the preferred pack.
-        *
-        * Therefore we do not need to ever ask the MIDX for its copy of
-        * an object by OID, since it will always select it from the
-        * preferred pack. Likewise, the selected copy of the base
-        * object for any deltas will reside in the same pack.
-        *
-        * This means that we can reuse pos when looking up the bit in
-        * the reuse bitmap, too, since bits corresponding to the
-        * preferred pack precede all bits from other packs.
-        */
-
-       if (pos >= pack->num_objects)
-               return -1; /* not actually in the pack or MIDX preferred pack */
+       if (pack_pos >= pack->p->num_objects)
+               return -1; /* not actually in the pack */
 
-       offset = delta_obj_offset = pack_pos_to_offset(packpos);
-       type = unpack_object_header(pack, w_curs, &offset, &size);
+       offset = delta_obj_offset = pack_pos_to_offset(pack->p, pack_pos);
+       type = unpack_object_header(pack->p, w_curs, &offset, &size);
        if (type < 0)
                return -1; /* broken packfile, punt */
 
        if (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA) {
                off_t base_offset;
                uint32_t base_pos;
+               uint32_t base_bitmap_pos;
 
                /*
                 * Find the position of the base object so we can look it up
@@ -1886,24 +1873,48 @@ static int try_partial_reuse(struct packed_git *pack,
                 * and the normal slow path will complain about it in
                 * more detail.
                 */
-               base_offset = get_delta_base(pack, w_curs, &offset, type,
+               base_offset = get_delta_base(pack->p, w_curs, &offset, type,
                                             delta_obj_offset);
                if (!base_offset)
                        return 0;
-               if (offset_to_pack_pos(pack, base_offset, &base_pos) < 0)
-                       return 0;
 
-               /*
-                * We assume delta dependencies always point backwards. This
-                * lets us do a single pass, and is basically always true
-                * due to the way OFS_DELTAs work. You would not typically
-                * find REF_DELTA in a bitmapped pack, since we only bitmap
-                * packs we write fresh, and OFS_DELTA is the default). But
-                * let's double check to make sure the pack wasn't written with
-                * odd parameters.
-                */
-               if (base_pos >= pos)
-                       return 0;
+               offset_to_pack_pos(pack->p, base_offset, &base_pos);
+
+               if (bitmap_is_midx(bitmap_git)) {
+                       /*
+                        * Cross-pack deltas are rejected for now, but could
+                        * theoretically be supported in the future.
+                        *
+                        * We would need to ensure that we're sending both
+                        * halves of the delta/base pair, regardless of whether
+                        * or not the two cross a pack boundary. If they do,
+                        * then we must convert the delta to an REF_DELTA to
+                        * refer back to the base in the other pack.
+                        * */
+                       if (midx_pair_to_pack_pos(bitmap_git->midx,
+                                                 pack->pack_int_id,
+                                                 base_offset,
+                                                 &base_bitmap_pos) < 0) {
+                               return 0;
+                       }
+               } else {
+                       if (offset_to_pack_pos(pack->p, base_offset,
+                                              &base_pos) < 0)
+                               return 0;
+                       /*
+                        * We assume delta dependencies always point backwards.
+                        * This lets us do a single pass, and is basically
+                        * always true due to the way OFS_DELTAs work. You would
+                        * not typically find REF_DELTA in a bitmapped pack,
+                        * since we only bitmap packs we write fresh, and
+                        * OFS_DELTA is the default). But let's double check to
+                        * make sure the pack wasn't written with odd
+                        * parameters.
+                        */
+                       if (base_pos >= pack_pos)
+                               return 0;
+                       base_bitmap_pos = pack->bitmap_pos + base_pos;
+               }
 
                /*
                 * And finally, if we're not sending the base as part of our
@@ -1913,77 +1924,89 @@ static int try_partial_reuse(struct packed_git *pack,
                 * to REF_DELTA on the fly. Better to just let the normal
                 * object_entry code path handle it.
                 */
-               if (!bitmap_get(reuse, base_pos))
+               if (!bitmap_get(reuse, base_bitmap_pos))
                        return 0;
        }
 
        /*
         * If we got here, then the object is OK to reuse. Mark it.
         */
-       bitmap_set(reuse, pos);
+       bitmap_set(reuse, bitmap_pos);
        return 0;
 }
 
-uint32_t midx_preferred_pack(struct bitmap_index *bitmap_git)
+static void reuse_partial_packfile_from_bitmap_1(struct bitmap_index *bitmap_git,
+                                                struct bitmapped_pack *pack,
+                                                struct bitmap *reuse)
 {
-       struct multi_pack_index *m = bitmap_git->midx;
-       if (!m)
-               BUG("midx_preferred_pack: requires non-empty MIDX");
-       return nth_midxed_pack_int_id(m, pack_pos_to_midx(bitmap_git->midx, 0));
-}
-
-int reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
-                                      struct packed_git **packfile_out,
-                                      uint32_t *entries,
-                                      struct bitmap **reuse_out)
-{
-       struct repository *r = the_repository;
-       struct packed_git *pack;
        struct bitmap *result = bitmap_git->result;
-       struct bitmap *reuse;
        struct pack_window *w_curs = NULL;
-       size_t i = 0;
-       uint32_t offset;
-       uint32_t objects_nr;
+       size_t pos = pack->bitmap_pos / BITS_IN_EWORD;
 
-       assert(result);
+       if (!pack->bitmap_pos) {
+               /*
+                * If we're processing the first (in the case of a MIDX, the
+                * preferred pack) or the only (in the case of single-pack
+                * bitmaps) pack, then we can reuse whole words at a time.
+                *
+                * This is because we know that any deltas in this range *must*
+                * have their bases chosen from the same pack, since:
+                *
+                * - In the single pack case, there is no other pack to choose
+                *   them from.
+                *
+                * - In the MIDX case, the first pack is the preferred pack, so
+                *   all ties are broken in favor of that pack (i.e. the one
+                *   we're currently processing). So any duplicate bases will be
+                *   resolved in favor of the pack we're processing.
+                */
+               while (pos < result->word_alloc &&
+                      pos < pack->bitmap_nr / BITS_IN_EWORD &&
+                      result->words[pos] == (eword_t)~0)
+                       pos++;
+               memset(reuse->words, 0xFF, pos * sizeof(eword_t));
+       }
 
-       load_reverse_index(r, bitmap_git);
+       for (; pos < result->word_alloc; pos++) {
+               eword_t word = result->words[pos];
+               size_t offset;
 
-       if (bitmap_is_midx(bitmap_git))
-               pack = bitmap_git->midx->packs[midx_preferred_pack(bitmap_git)];
-       else
-               pack = bitmap_git->pack;
-       objects_nr = pack->num_objects;
+               for (offset = 0; offset < BITS_IN_EWORD; offset++) {
+                       size_t bit_pos;
+                       uint32_t pack_pos;
 
-       while (i < result->word_alloc && result->words[i] == (eword_t)~0)
-               i++;
+                       if (word >> offset == 0)
+                               break;
 
-       /*
-        * Don't mark objects not in the packfile or preferred pack. This bitmap
-        * marks objects eligible for reuse, but the pack-reuse code only
-        * understands how to reuse a single pack. Since the preferred pack is
-        * guaranteed to have all bases for its deltas (in a multi-pack bitmap),
-        * we use it instead of another pack. In single-pack bitmaps, the choice
-        * is made for us.
-        */
-       if (i > objects_nr / BITS_IN_EWORD)
-               i = objects_nr / BITS_IN_EWORD;
+                       offset += ewah_bit_ctz64(word >> offset);
 
-       reuse = bitmap_word_alloc(i);
-       memset(reuse->words, 0xFF, i * sizeof(eword_t));
+                       bit_pos = pos * BITS_IN_EWORD + offset;
+                       if (bit_pos < pack->bitmap_pos)
+                               continue;
+                       if (bit_pos >= pack->bitmap_pos + pack->bitmap_nr)
+                               goto done;
 
-       for (; i < result->word_alloc; ++i) {
-               eword_t word = result->words[i];
-               size_t pos = (i * BITS_IN_EWORD);
+                       if (bitmap_is_midx(bitmap_git)) {
+                               uint32_t midx_pos;
+                               off_t ofs;
 
-               for (offset = 0; offset < BITS_IN_EWORD; ++offset) {
-                       if ((word >> offset) == 0)
-                               break;
+                               midx_pos = pack_pos_to_midx(bitmap_git->midx, bit_pos);
+                               ofs = nth_midxed_offset(bitmap_git->midx, midx_pos);
 
-                       offset += ewah_bit_ctz64(word >> offset);
-                       if (try_partial_reuse(pack, pos + offset,
-                                             reuse, &w_curs) < 0) {
+                               if (offset_to_pack_pos(pack->p, ofs, &pack_pos) < 0)
+                                       BUG("could not find object in pack %s "
+                                           "at offset %"PRIuMAX" in MIDX",
+                                           pack_basename(pack->p), (uintmax_t)ofs);
+                       } else {
+                               pack_pos = cast_size_t_to_uint32_t(st_sub(bit_pos, pack->bitmap_pos));
+                               if (pack_pos >= pack->p->num_objects)
+                                       BUG("advanced beyond the end of pack %s (%"PRIuMAX" > %"PRIu32")",
+                                           pack_basename(pack->p), (uintmax_t)pack_pos,
+                                           pack->p->num_objects);
+                       }
+
+                       if (try_partial_reuse(bitmap_git, pack, bit_pos,
+                                             pack_pos, reuse, &w_curs) < 0) {
                                /*
                                 * try_partial_reuse indicated we couldn't reuse
                                 * any bits, so there is no point in trying more
@@ -2000,11 +2023,97 @@ int reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
 
 done:
        unuse_pack(&w_curs);
+}
 
-       *entries = bitmap_popcount(reuse);
-       if (!*entries) {
-               bitmap_free(reuse);
+static int bitmapped_pack_cmp(const void *va, const void *vb)
+{
+       const struct bitmapped_pack *a = va;
+       const struct bitmapped_pack *b = vb;
+
+       if (a->bitmap_pos < b->bitmap_pos)
                return -1;
+       if (a->bitmap_pos > b->bitmap_pos)
+               return 1;
+       return 0;
+}
+
+void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
+                                       struct bitmapped_pack **packs_out,
+                                       size_t *packs_nr_out,
+                                       struct bitmap **reuse_out,
+                                       int multi_pack_reuse)
+{
+       struct repository *r = the_repository;
+       struct bitmapped_pack *packs = NULL;
+       struct bitmap *result = bitmap_git->result;
+       struct bitmap *reuse;
+       size_t i;
+       size_t packs_nr = 0, packs_alloc = 0;
+       size_t word_alloc;
+       uint32_t objects_nr = 0;
+
+       assert(result);
+
+       load_reverse_index(r, bitmap_git);
+
+       if (bitmap_is_midx(bitmap_git)) {
+               for (i = 0; i < bitmap_git->midx->num_packs; i++) {
+                       struct bitmapped_pack pack;
+                       if (nth_bitmapped_pack(r, bitmap_git->midx, &pack, i) < 0) {
+                               warning(_("unable to load pack: '%s', disabling pack-reuse"),
+                                       bitmap_git->midx->pack_names[i]);
+                               free(packs);
+                               return;
+                       }
+
+                       if (!pack.bitmap_nr)
+                               continue;
+
+                       if (!multi_pack_reuse && pack.bitmap_pos) {
+                               /*
+                                * If we're only reusing a single pack, skip
+                                * over any packs which are not positioned at
+                                * the beginning of the MIDX bitmap.
+                                *
+                                * This is consistent with the existing
+                                * single-pack reuse behavior, which only reuses
+                                * parts of the MIDX's preferred pack.
+                                */
+                               continue;
+                       }
+
+                       ALLOC_GROW(packs, packs_nr + 1, packs_alloc);
+                       memcpy(&packs[packs_nr++], &pack, sizeof(pack));
+
+                       objects_nr += pack.p->num_objects;
+
+                       if (!multi_pack_reuse)
+                               break;
+               }
+
+               QSORT(packs, packs_nr, bitmapped_pack_cmp);
+       } else {
+               ALLOC_GROW(packs, packs_nr + 1, packs_alloc);
+
+               packs[packs_nr].p = bitmap_git->pack;
+               packs[packs_nr].bitmap_nr = bitmap_git->pack->num_objects;
+               packs[packs_nr].bitmap_pos = 0;
+
+               objects_nr = packs[packs_nr++].bitmap_nr;
+       }
+
+       word_alloc = objects_nr / BITS_IN_EWORD;
+       if (objects_nr % BITS_IN_EWORD)
+               word_alloc++;
+       reuse = bitmap_word_alloc(word_alloc);
+
+       for (i = 0; i < packs_nr; i++)
+               reuse_partial_packfile_from_bitmap_1(bitmap_git, &packs[i], reuse);
+
+       if (bitmap_is_empty(reuse)) {
+               free(packs);
+               bitmap_free(reuse);
+               return;
        }
 
        /*
@@ -2012,9 +2121,9 @@ done:
         * need to be handled separately.
         */
        bitmap_and_not(result, reuse);
-       *packfile_out = pack;
+       *packs_out = packs;
+       *packs_nr_out = packs_nr;
        *reuse_out = reuse;
-       return 0;
 }
 
 int bitmap_walk_contains(struct bitmap_index *bitmap_git,
index 5273a6a019708c8295be8e4fa5ca11db342f3a40..c7dea13217a00ef544d16a45d0f78de8f0e8ebc0 100644 (file)
@@ -52,6 +52,15 @@ typedef int (*show_reachable_fn)(
 
 struct bitmap_index;
 
+struct bitmapped_pack {
+       struct packed_git *p;
+
+       uint32_t bitmap_pos;
+       uint32_t bitmap_nr;
+
+       uint32_t pack_int_id; /* MIDX only */
+};
+
 struct bitmap_index *prepare_bitmap_git(struct repository *r);
 struct bitmap_index *prepare_midx_bitmap_git(struct multi_pack_index *midx);
 void count_bitmap_commit_list(struct bitmap_index *, uint32_t *commits,
@@ -68,11 +77,11 @@ int test_bitmap_hashes(struct repository *r);
 
 struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
                                         int filter_provided_objects);
-uint32_t midx_preferred_pack(struct bitmap_index *bitmap_git);
-int reuse_partial_packfile_from_bitmap(struct bitmap_index *,
-                                      struct packed_git **packfile,
-                                      uint32_t *entries,
-                                      struct bitmap **reuse_out);
+void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
+                                       struct bitmapped_pack **packs_out,
+                                       size_t *packs_nr_out,
+                                       struct bitmap **reuse_out,
+                                       int multi_pack_reuse);
 int rebuild_existing_bitmaps(struct bitmap_index *, struct packing_data *mapping,
                             kh_oid_map_t *reused_bitmaps, int show_progress);
 void free_bitmap_index(struct bitmap_index *);
index 977f619618e0a9b97044f0b07e5617d3b3e1839b..25104d5b14c1e8f9a9988f106a1ad1a6823ca31c 100644 (file)
@@ -3,7 +3,6 @@
 #include "hex.h"
 #include "repository.h"
 #include "pack.h"
-#include "pack-revindex.h"
 #include "progress.h"
 #include "packfile.h"
 #include "object-file.h"
index f403ca6986a9d4c68715c1e1b24462d462fb8aa9..a9d9855063aea85f4b1b8f70f301c644c5d5e225 100644 (file)
@@ -151,6 +151,21 @@ void prepare_packing_data(struct repository *r, struct packing_data *pdata)
        init_recursive_mutex(&pdata->odb_lock);
 }
 
+void clear_packing_data(struct packing_data *pdata)
+{
+       if (!pdata)
+               return;
+
+       free(pdata->cruft_mtime);
+       free(pdata->in_pack);
+       free(pdata->in_pack_by_idx);
+       free(pdata->in_pack_pos);
+       free(pdata->index);
+       free(pdata->layer);
+       free(pdata->objects);
+       free(pdata->tree_depth);
+}
+
 struct object_entry *packlist_alloc(struct packing_data *pdata,
                                    const struct object_id *oid)
 {
index 0d78db40cb2f11fcbc4b3c2bbb2d23624ef3f22e..b9898a4e64b8b4d53b21ea776c16f79d9794efef 100644 (file)
@@ -169,6 +169,7 @@ struct packing_data {
 };
 
 void prepare_packing_data(struct repository *r, struct packing_data *pdata);
+void clear_packing_data(struct packing_data *pdata);
 
 /* Protect access to object database */
 static inline void packing_data_lock(struct packing_data *pdata)
index acf1dd9786cd3c2a0cb410804721488b6d1d92b4..a7624d8be8e58e0807cf0b2dc5e3bae52ac53af9 100644 (file)
@@ -520,19 +520,12 @@ static int midx_pack_order_cmp(const void *va, const void *vb)
        return 0;
 }
 
-int midx_to_pack_pos(struct multi_pack_index *m, uint32_t at, uint32_t *pos)
+static int midx_key_to_pack_pos(struct multi_pack_index *m,
+                               struct midx_pack_key *key,
+                               uint32_t *pos)
 {
-       struct midx_pack_key key;
        uint32_t *found;
 
-       if (!m->revindex_data)
-               BUG("midx_to_pack_pos: reverse index not yet loaded");
-       if (m->num_objects <= at)
-               BUG("midx_to_pack_pos: out-of-bounds object at %"PRIu32, at);
-
-       key.pack = nth_midxed_pack_int_id(m, at);
-       key.offset = nth_midxed_offset(m, at);
-       key.midx = m;
        /*
         * The preferred pack sorts first, so determine its identifier by
         * looking at the first object in pseudo-pack order.
@@ -542,14 +535,43 @@ int midx_to_pack_pos(struct multi_pack_index *m, uint32_t at, uint32_t *pos)
         * implicitly is preferred (and includes all its objects, since ties are
         * broken first by pack identifier).
         */
-       key.preferred_pack = nth_midxed_pack_int_id(m, pack_pos_to_midx(m, 0));
+       if (midx_preferred_pack(key->midx, &key->preferred_pack) < 0)
+               return error(_("could not determine preferred pack"));
 
-       found = bsearch(&key, m->revindex_data, m->num_objects,
-                       sizeof(*m->revindex_data), midx_pack_order_cmp);
+       found = bsearch(key, m->revindex_data, m->num_objects,
+                       sizeof(*m->revindex_data),
+                       midx_pack_order_cmp);
 
        if (!found)
-               return error("bad offset for revindex");
+               return -1;
 
        *pos = found - m->revindex_data;
        return 0;
 }
+
+int midx_to_pack_pos(struct multi_pack_index *m, uint32_t at, uint32_t *pos)
+{
+       struct midx_pack_key key;
+
+       if (!m->revindex_data)
+               BUG("midx_to_pack_pos: reverse index not yet loaded");
+       if (m->num_objects <= at)
+               BUG("midx_to_pack_pos: out-of-bounds object at %"PRIu32, at);
+
+       key.pack = nth_midxed_pack_int_id(m, at);
+       key.offset = nth_midxed_offset(m, at);
+       key.midx = m;
+
+       return midx_key_to_pack_pos(m, &key, pos);
+}
+
+int midx_pair_to_pack_pos(struct multi_pack_index *m, uint32_t pack_int_id,
+                         off_t ofs, uint32_t *pos)
+{
+       struct midx_pack_key key = {
+               .pack = pack_int_id,
+               .offset = ofs,
+               .midx = m,
+       };
+       return midx_key_to_pack_pos(m, &key, pos);
+}
index 6dd47efea10ec69a3438f0a804a71bf13cd91c37..422c2487ae32d8a4500f4a151ca673364fed96a0 100644 (file)
@@ -142,4 +142,7 @@ uint32_t pack_pos_to_midx(struct multi_pack_index *m, uint32_t pos);
  */
 int midx_to_pack_pos(struct multi_pack_index *midx, uint32_t at, uint32_t *pos);
 
+int midx_pair_to_pack_pos(struct multi_pack_index *midx, uint32_t pack_id,
+                         off_t ofs, uint32_t *pos);
+
 #endif
index b19ddf15b284868acd7ad9123aed8fba10d08d30..80ecfa544c5d9f7980adfad829e32cabd281ecc4 100644 (file)
@@ -7,7 +7,6 @@
 #include "remote.h"
 #include "chunk-format.h"
 #include "pack-mtimes.h"
-#include "oidmap.h"
 #include "pack-objects.h"
 #include "pack-revindex.h"
 #include "path.h"
index 9cc0a2e37a83dd38c2fe1b26d34cfb5fea983bc7..84a005674d8749407afffc1f6fd670501ce764d4 100644 (file)
@@ -9,7 +9,6 @@
 #include "mergesort.h"
 #include "packfile.h"
 #include "delta.h"
-#include "streaming.h"
 #include "hash-lookup.h"
 #include "commit.h"
 #include "object.h"
index c3692308b8dc866b2b99b2ed9b983db4d6d1352b..28c8fd3e39a23ae997a66a11dfd0d5d281dbc28f 100644 (file)
@@ -54,7 +54,7 @@ const char *pack_basename(struct packed_git *p);
 struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
 
 typedef void each_file_in_pack_dir_fn(const char *full_path, size_t full_path_len,
-                                     const char *file_pach, void *data);
+                                     const char *file_name, void *data);
 void for_each_file_in_pack_dir(const char *objdir,
                               each_file_in_pack_dir_fn fn,
                               void *data);
index e4fda09af49a5db6406dc35bd0fcfe6f2176737e..63a99dea6ef06b19bbb679a3ddd76087abc2a028 100644 (file)
@@ -2,8 +2,6 @@
 #include "parse-options.h"
 #include "abspath.h"
 #include "parse.h"
-#include "commit.h"
-#include "color.h"
 #include "gettext.h"
 #include "strbuf.h"
 #include "string-list.h"
@@ -279,7 +277,8 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
 
        opt_name = optnamearg(opt, arg, flags);
        other_opt_name = optnamearg(elem->opt, elem->arg, elem->flags);
-       error(_("%s is incompatible with %s"), opt_name, other_opt_name);
+       error(_("options '%s' and '%s' cannot be used together"),
+             opt_name, other_opt_name);
        free(opt_name);
        free(other_opt_name);
        return -1;
@@ -930,13 +929,18 @@ enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx,
                        continue;
                }
 
-               if (!arg[2] /* "--" */ ||
-                   !strcmp(arg + 2, "end-of-options")) {
+               if (!arg[2] /* "--" */) {
                        if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {
                                ctx->argc--;
                                ctx->argv++;
                        }
                        break;
+               } else if (!strcmp(arg + 2, "end-of-options")) {
+                       if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN_OPT)) {
+                               ctx->argc--;
+                               ctx->argv++;
+                       }
+                       break;
                }
 
                if (internal_help && !strcmp(arg + 2, "help-all"))
index c3e1a0dd216c80c92c47f0e2a98d74a57c422742..a5683b462c6e7692ceff3218213b9747a9b6cc02 100644 (file)
@@ -2,7 +2,6 @@
 #include "diff.h"
 #include "commit.h"
 #include "hash.h"
-#include "hash-lookup.h"
 #include "hex.h"
 #include "patch-ids.h"
 
diff --git a/path.c b/path.c
index 67e2690efef897b406d46eab6202b52fc65a55d0..0fb527918b77652dbba2e6b0bee651fcc0fddfe9 100644 (file)
--- a/path.c
+++ b/path.c
@@ -1588,7 +1588,5 @@ REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
 REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
 REPO_GIT_PATH_FUNC(merge_mode, "MERGE_MODE")
 REPO_GIT_PATH_FUNC(merge_head, "MERGE_HEAD")
-REPO_GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH")
-REPO_GIT_PATH_FUNC(auto_merge, "AUTO_MERGE")
 REPO_GIT_PATH_FUNC(fetch_head, "FETCH_HEAD")
 REPO_GIT_PATH_FUNC(shallow, "shallow")
diff --git a/path.h b/path.h
index 639372edd9ee36127e3b89084f89ee59d12ff60f..b3233c51fa0f1acdf87368db4a7d9c3f0955c964 100644 (file)
--- a/path.h
+++ b/path.h
@@ -175,8 +175,6 @@ const char *git_path_merge_msg(struct repository *r);
 const char *git_path_merge_rr(struct repository *r);
 const char *git_path_merge_mode(struct repository *r);
 const char *git_path_merge_head(struct repository *r);
-const char *git_path_merge_autostash(struct repository *r);
-const char *git_path_auto_merge(struct repository *r);
 const char *git_path_fetch_head(struct repository *r);
 const char *git_path_shallow(struct repository *r);
 
index bb1efe1f3929f116a216de5cae6fa6b83227569e..2133b9fe60a87c201186dc3f1840276104903f7f 100644 (file)
@@ -109,16 +109,37 @@ static struct pathspec_magic {
        { PATHSPEC_ATTR,    '\0', "attr" },
 };
 
-static void prefix_magic(struct strbuf *sb, int prefixlen, unsigned magic)
+static void prefix_magic(struct strbuf *sb, int prefixlen,
+                        unsigned magic, const char *element)
 {
-       int i;
-       strbuf_addstr(sb, ":(");
-       for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
-               if (magic & pathspec_magic[i].bit) {
-                       if (sb->buf[sb->len - 1] != '(')
-                               strbuf_addch(sb, ',');
-                       strbuf_addstr(sb, pathspec_magic[i].name);
+       /* No magic was found in element, just add prefix magic */
+       if (!magic) {
+               strbuf_addf(sb, ":(prefix:%d)", prefixlen);
+               return;
+       }
+
+       /*
+        * At this point, we know that parse_element_magic() was able
+        * to extract some pathspec magic from element. So we know
+        * element is correctly formatted in either shorthand or
+        * longhand form
+        */
+       if (element[1] != '(') {
+               /* Process an element in shorthand form (e.g. ":!/<match>") */
+               strbuf_addstr(sb, ":(");
+               for (int i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
+                       if ((magic & pathspec_magic[i].bit) &&
+                           pathspec_magic[i].mnemonic) {
+                               if (sb->buf[sb->len - 1] != '(')
+                                       strbuf_addch(sb, ',');
+                               strbuf_addstr(sb, pathspec_magic[i].name);
+                       }
                }
+       } else {
+               /* For the longhand form, we copy everything up to the final ')' */
+               size_t len = strchr(element, ')') - element;
+               strbuf_add(sb, element, len);
+       }
        strbuf_addf(sb, ",prefix:%d)", prefixlen);
 }
 
@@ -493,7 +514,7 @@ static void init_pathspec_item(struct pathspec_item *item, unsigned flags,
                struct strbuf sb = STRBUF_INIT;
 
                /* Preserve the actual prefix length of each pattern */
-               prefix_magic(&sb, prefixlen, element_magic);
+               prefix_magic(&sb, prefixlen, element_magic, elt);
 
                strbuf_addstr(&sb, match);
                item->original = strbuf_detach(&sb, NULL);
index d82b71325c6bd492cd7eb0f703b00fa6634c627d..5b97e0315d6f16c9a215b387fb587dba1074bd23 100644 (file)
@@ -1025,7 +1025,7 @@ C<:warndie> handlers added by Paul Evans <leonerd@leonerd.org.uk>
 
 =head1 MAINTAINER
 
-Shlomi Fish, L<http://www.shlomifish.org/> .
+Shlomi Fish, L<https://www.shlomifish.org/> .
 
 =head1 PAST MAINTAINERS
 
index 117765dc73c4a8c30bfbcf9b3b37bad6b26a9ede..03bf570bf4c852932973f3a6815c2d3dd4802622 100644 (file)
@@ -7,7 +7,7 @@ Git - Perl interface to the Git version control system
 
 package Git;
 
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 
index 895e759c57a9d9c76d677299ff4658796e2930af..5454c3a6d2c433c1456c0d6151c50746e8f2fad4 100644 (file)
@@ -1,5 +1,5 @@
 package Git::I18N;
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 BEGIN {
index 0c360bc799860798ade6b2afce6e05b0a567d21f..8c7fa805f97390a47366b0d0058966ee391b9df1 100644 (file)
@@ -1,5 +1,5 @@
 package Git::LoadCPAN;
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 
index 5d84c202884b7cb7cea576ea1a6f146b2e653ae2..5cecb0fcd6930a1806aafdaacee9312aa7e8212e 100644 (file)
@@ -1,5 +1,5 @@
 package Git::LoadCPAN::Error;
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use Git::LoadCPAN (
index 340e88a7a56be37af8b1f437c92ecb52b1b29089..9f808090a66a16c02eba9c5ed84669ec03f1b944 100644 (file)
@@ -1,5 +1,5 @@
 package Git::LoadCPAN::Mail::Address;
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use Git::LoadCPAN (
index d144f5168f37adaf54a2f9b768f04ad2a1c20196..d896e6952399b0fa92b249144d61fcd58d2aab67 100644 (file)
@@ -1,5 +1,5 @@
 package Git::Packet;
-use 5.008;
+use 5.008001;
 use strict;
 use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 BEGIN {
index 6ce2e283c8d18b3de72cbec12957ebd532d25333..7721708ce5d7f6560ba9f84706e9ad840664020a 100644 (file)
@@ -1752,7 +1752,7 @@ sub tie_for_persistent_memoization {
 END {
        # Force cache writeout explicitly instead of waiting for
        # global destruction to avoid segfault in Storable:
-       # http://rt.cpan.org/Public/Bug/Display.html?id=36087
+       # https://rt.cpan.org/Public/Bug/Display.html?id=36087
        unmemoize_svn_mergeinfo_functions();
 }
 
index af83a19f4df5537da9f1c7a87ea29cb2f5903ea2..24479eae4dbe2a44b15a10f86b15bfc6f865bb34 100644 (file)
@@ -4,6 +4,7 @@
 #include "gettext.h"
 #include "hex.h"
 #include "run-command.h"
+#include "sideband.h"
 #include "trace.h"
 #include "write-or-die.h"
 
@@ -462,8 +463,32 @@ enum packet_read_status packet_read_with_status(int fd, char **src_buffer,
        }
 
        if ((options & PACKET_READ_CHOMP_NEWLINE) &&
-           len && buffer[len-1] == '\n')
-               len--;
+           len && buffer[len-1] == '\n') {
+               if (options & PACKET_READ_USE_SIDEBAND) {
+                       int band = *buffer & 0xff;
+                       switch (band) {
+                       case 1:
+                               /* Chomp newline for payload */
+                               len--;
+                               break;
+                       case 2:
+                       case 3:
+                               /*
+                                * Do not chomp newline for progress and error
+                                * message.
+                                */
+                               break;
+                       default:
+                               /*
+                                * Bad sideband, let's leave it to
+                                * demultiplex_sideband() to catch this error.
+                                */
+                               break;
+                       }
+               } else {
+                       len--;
+               }
+       }
 
        buffer[len] = 0;
        if (options & PACKET_READ_REDACT_URI_PATH &&
@@ -592,17 +617,19 @@ void packet_reader_init(struct packet_reader *reader, int fd,
        reader->options = options;
        reader->me = "git";
        reader->hash_algo = &hash_algos[GIT_HASH_SHA1];
+       strbuf_init(&reader->scratch, 0);
 }
 
 enum packet_read_status packet_reader_read(struct packet_reader *reader)
 {
-       struct strbuf scratch = STRBUF_INIT;
-
        if (reader->line_peeked) {
                reader->line_peeked = 0;
                return reader->status;
        }
 
+       if (reader->use_sideband)
+               reader->options |= PACKET_READ_USE_SIDEBAND;
+
        /*
         * Consume all progress packets until a primary payload packet is
         * received
@@ -620,7 +647,7 @@ enum packet_read_status packet_reader_read(struct packet_reader *reader)
                        break;
                if (demultiplex_sideband(reader->me, reader->status,
                                         reader->buffer, reader->pktlen, 1,
-                                        &scratch, &sideband_type))
+                                        &reader->scratch, &sideband_type))
                        break;
        }
 
index 954eec87197d3e0c50812879d47e0afbb008baa3..3b33cc64f34dcc3447f6a6f42f12451dbc1a77a9 100644 (file)
@@ -2,7 +2,6 @@
 #define PKTLINE_H
 
 #include "strbuf.h"
-#include "sideband.h"
 
 /*
  * Write a packetized stream, where each line is preceded by
@@ -85,6 +84,7 @@ void packet_fflush(FILE *f);
 #define PACKET_READ_DIE_ON_ERR_PACKET    (1u<<2)
 #define PACKET_READ_GENTLE_ON_READ_ERROR (1u<<3)
 #define PACKET_READ_REDACT_URI_PATH      (1u<<4)
+#define PACKET_READ_USE_SIDEBAND         (1u<<5)
 int packet_read(int fd, char *buffer, unsigned size, int options);
 
 /*
@@ -194,6 +194,9 @@ struct packet_reader {
 
        /* hash algorithm in use */
        const struct git_hash_algo *hash_algo;
+
+       /* hold temporary sideband message */
+       struct strbuf scratch;
 };
 
 /*
index 808a68c974ab7715513657c62d238aae8d3af07f..75f4cbb0a70951a85c8e5a4dba853f027ee7cf4b 100644 (file)
@@ -3,7 +3,6 @@
 #include "gettext.h"
 #include "hex.h"
 #include "pkt-line.h"
-#include "strvec.h"
 #include "hash-ll.h"
 #include "hex.h"
 #include "object.h"
index de66bf80f8409370069fed46ce97dadb25519d7d..1e574bbd80be1bf9d9366dfefd24e3016ec87cf0 100644 (file)
@@ -18,7 +18,7 @@
  * with Linus Torvalds <torvalds@osdl.org> as the point of
  * contact. September 2005.
  *
- * See http://www.iana.org/assignments/port-numbers
+ * See https://www.iana.org/assignments/port-numbers
  */
 #define DEFAULT_GIT_PORT 9418
 
index 0ce8f83e56a9b42400b8ec792b19deb3a69ddb6e..f29b06a5d059e23030333f26d5ba0667872251ab 100644 (file)
@@ -2,7 +2,6 @@
 #include "gettext.h"
 #include "hex.h"
 #include "refs.h"
-#include "tag.h"
 #include "commit.h"
 #include "blob.h"
 #include "diff.h"
index 9a1a7edc5a22cf0b0d91f7edd03225ea19607156..2a50a784f0ec6ae4da94fed41345e625c5bbce1c 100644 (file)
@@ -436,6 +436,14 @@ int match_stat_data_racy(const struct index_state *istate,
 
 void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, struct stat *st);
 
+/*
+ * Fill members of st by members of sd enough to convince match_stat()
+ * to consider that they match.  It should be usable as a replacement
+ * for lstat() for a tracked path that is known to be up-to-date via
+ * some out-of-line means (like fsmonitor).
+ */
+int fake_lstat(const struct cache_entry *ce, struct stat *st);
+
 #define REFRESH_REALLY                   (1 << 0) /* ignore_valid */
 #define REFRESH_UNMERGED                 (1 << 1) /* allow unmerged */
 #define REFRESH_QUIET                    (1 << 2) /* be quiet about it */
index 080bd39713bc1782434b7212954e6bc590fa4d6d..f546cf7875cbfefddbec2449e8303786e485a8dd 100644 (file)
@@ -20,7 +20,6 @@
 #include "oid-array.h"
 #include "tree.h"
 #include "commit.h"
-#include "blob.h"
 #include "environment.h"
 #include "gettext.h"
 #include "mem-pool.h"
@@ -31,7 +30,6 @@
 #include "read-cache.h"
 #include "resolve-undo.h"
 #include "revision.h"
-#include "run-command.h"
 #include "strbuf.h"
 #include "trace2.h"
 #include "varint.h"
@@ -197,6 +195,33 @@ void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, st
        }
 }
 
+static unsigned int st_mode_from_ce(const struct cache_entry *ce)
+{
+       extern int trust_executable_bit, has_symlinks;
+
+       switch (ce->ce_mode & S_IFMT) {
+       case S_IFLNK:
+               return has_symlinks ? S_IFLNK : (S_IFREG | 0644);
+       case S_IFREG:
+               return (ce->ce_mode & (trust_executable_bit ? 0755 : 0644)) | S_IFREG;
+       case S_IFGITLINK:
+               return S_IFDIR | 0755;
+       case S_IFDIR:
+               return ce->ce_mode;
+       default:
+               BUG("unsupported ce_mode: %o", ce->ce_mode);
+       }
+}
+
+int fake_lstat(const struct cache_entry *ce, struct stat *st)
+{
+       fake_lstat_data(&ce->ce_stat_data, st);
+       st->st_mode = st_mode_from_ce(ce);
+
+       /* always succeed as lstat() replacement */
+       return 0;
+}
+
 static int ce_compare_data(struct index_state *istate,
                           const struct cache_entry *ce,
                           struct stat *st)
index e4d3510e28e15a7ff967737f08a8c1acadb588e1..35b989e1dfe59e9d274afead3d397f97ed624b26 100644 (file)
 #include "ref-filter.h"
 #include "revision.h"
 #include "utf8.h"
-#include "version.h"
 #include "versioncmp.h"
 #include "trailer.h"
 #include "wt-status.h"
 #include "commit-slab.h"
-#include "commit-graph.h"
 #include "commit-reach.h"
 #include "worktree.h"
 #include "hashmap.h"
-#include "strvec.h"
 
 static struct ref_msg {
        const char *gone;
@@ -2212,7 +2209,7 @@ char *get_head_description(void)
                                    state.detached_from);
        } else if (state.bisect_in_progress)
                strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
-                           state.branch);
+                           state.bisecting_from);
        else if (state.detached_from) {
                if (state.detached_at)
                        strbuf_addf(&desc, _("(HEAD detached at %s)"),
@@ -2508,17 +2505,12 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
                return 0;
 
        /*
-        * If it is a tag object, see if we use a value that derefs
-        * the object, and if we do grab the object it refers to.
+        * If it is a tag object, see if we use the peeled value. If we do,
+        * grab the peeled OID.
         */
-       oi_deref.oid = *get_tagged_oid((struct tag *)obj);
+       if (need_tagged && peel_iterated_oid(&obj->oid, &oi_deref.oid))
+               die("bad tag");
 
-       /*
-        * NEEDSWORK: This derefs tag only once, which
-        * is good to deal with chains of trust, but
-        * is not consistent with what deref_tag() does
-        * which peels the onion to the core.
-        */
        return get_object(ref, 1, &obj, &oi_deref, err);
 }
 
@@ -2716,15 +2708,18 @@ static struct ref_array_item *new_ref_array_item(const char *refname,
        return ref;
 }
 
+static void ref_array_append(struct ref_array *array, struct ref_array_item *ref)
+{
+       ALLOC_GROW(array->items, array->nr + 1, array->alloc);
+       array->items[array->nr++] = ref;
+}
+
 struct ref_array_item *ref_array_push(struct ref_array *array,
                                      const char *refname,
                                      const struct object_id *oid)
 {
        struct ref_array_item *ref = new_ref_array_item(refname, oid);
-
-       ALLOC_GROW(array->items, array->nr + 1, array->alloc);
-       array->items[array->nr++] = ref;
-
+       ref_array_append(array, ref);
        return ref;
 }
 
@@ -2761,48 +2756,36 @@ static int filter_ref_kind(struct ref_filter *filter, const char *refname)
        return ref_kind_from_refname(refname);
 }
 
-struct ref_filter_cbdata {
-       struct ref_array *array;
-       struct ref_filter *filter;
-       struct contains_cache contains_cache;
-       struct contains_cache no_contains_cache;
-};
-
-/*
- * A call-back given to for_each_ref().  Filter refs and keep them for
- * later object processing.
- */
-static int ref_filter_handler(const char *refname, const struct object_id *oid, int flag, void *cb_data)
+static struct ref_array_item *apply_ref_filter(const char *refname, const struct object_id *oid,
+                           int flag, struct ref_filter *filter)
 {
-       struct ref_filter_cbdata *ref_cbdata = cb_data;
-       struct ref_filter *filter = ref_cbdata->filter;
        struct ref_array_item *ref;
        struct commit *commit = NULL;
        unsigned int kind;
 
        if (flag & REF_BAD_NAME) {
                warning(_("ignoring ref with broken name %s"), refname);
-               return 0;
+               return NULL;
        }
 
        if (flag & REF_ISBROKEN) {
                warning(_("ignoring broken ref %s"), refname);
-               return 0;
+               return NULL;
        }
 
        /* Obtain the current ref kind from filter_ref_kind() and ignore unwanted refs. */
        kind = filter_ref_kind(filter, refname);
        if (!(kind & filter->kind))
-               return 0;
+               return NULL;
 
        if (!filter_pattern_match(filter, refname))
-               return 0;
+               return NULL;
 
        if (filter_exclude_match(filter, refname))
-               return 0;
+               return NULL;
 
        if (filter->points_at.nr && !match_points_at(&filter->points_at, oid, refname))
-               return 0;
+               return NULL;
 
        /*
         * A merge filter is applied on refs pointing to commits. Hence
@@ -2813,15 +2796,15 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
            filter->with_commit || filter->no_commit || filter->verbose) {
                commit = lookup_commit_reference_gently(the_repository, oid, 1);
                if (!commit)
-                       return 0;
+                       return NULL;
                /* We perform the filtering for the '--contains' option... */
                if (filter->with_commit &&
-                   !commit_contains(filter, commit, filter->with_commit, &ref_cbdata->contains_cache))
-                       return 0;
+                   !commit_contains(filter, commit, filter->with_commit, &filter->internal.contains_cache))
+                       return NULL;
                /* ...or for the `--no-contains' option */
                if (filter->no_commit &&
-                   commit_contains(filter, commit, filter->no_commit, &ref_cbdata->no_contains_cache))
-                       return 0;
+                   commit_contains(filter, commit, filter->no_commit, &filter->internal.no_contains_cache))
+                       return NULL;
        }
 
        /*
@@ -2829,11 +2812,32 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
         * to do its job and the resulting list may yet to be pruned
         * by maxcount logic.
         */
-       ref = ref_array_push(ref_cbdata->array, refname, oid);
+       ref = new_ref_array_item(refname, oid);
        ref->commit = commit;
        ref->flag = flag;
        ref->kind = kind;
 
+       return ref;
+}
+
+struct ref_filter_cbdata {
+       struct ref_array *array;
+       struct ref_filter *filter;
+};
+
+/*
+ * A call-back given to for_each_ref().  Filter refs and keep them for
+ * later object processing.
+ */
+static int filter_one(const char *refname, const struct object_id *oid, int flag, void *cb_data)
+{
+       struct ref_filter_cbdata *ref_cbdata = cb_data;
+       struct ref_array_item *ref;
+
+       ref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter);
+       if (ref)
+               ref_array_append(ref_cbdata->array, ref);
+
        return 0;
 }
 
@@ -2851,6 +2855,49 @@ static void free_array_item(struct ref_array_item *item)
        free(item);
 }
 
+struct ref_filter_and_format_cbdata {
+       struct ref_filter *filter;
+       struct ref_format *format;
+
+       struct ref_filter_and_format_internal {
+               int count;
+       } internal;
+};
+
+static int filter_and_format_one(const char *refname, const struct object_id *oid, int flag, void *cb_data)
+{
+       struct ref_filter_and_format_cbdata *ref_cbdata = cb_data;
+       struct ref_array_item *ref;
+       struct strbuf output = STRBUF_INIT, err = STRBUF_INIT;
+
+       ref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter);
+       if (!ref)
+               return 0;
+
+       if (format_ref_array_item(ref, ref_cbdata->format, &output, &err))
+               die("%s", err.buf);
+
+       if (output.len || !ref_cbdata->format->array_opts.omit_empty) {
+               fwrite(output.buf, 1, output.len, stdout);
+               putchar('\n');
+       }
+
+       strbuf_release(&output);
+       strbuf_release(&err);
+       free_array_item(ref);
+
+       /*
+        * Increment the running count of refs that match the filter. If
+        * max_count is set and we've reached the max, stop the ref
+        * iteration by returning a nonzero value.
+        */
+       if (ref_cbdata->format->array_opts.max_count &&
+           ++ref_cbdata->internal.count >= ref_cbdata->format->array_opts.max_count)
+               return 1;
+
+       return 0;
+}
+
 /* Free all memory allocated for ref_array */
 void ref_array_clear(struct ref_array *array)
 {
@@ -2969,28 +3016,14 @@ void filter_ahead_behind(struct repository *r,
        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
- * as per the given ref_filter structure and finally store the
- * filtered refs in the ref_array structure.
- */
-int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type)
+static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref_fn fn, void *cb_data)
 {
-       struct ref_filter_cbdata ref_cbdata;
-       int save_commit_buffer_orig;
        int ret = 0;
 
-       ref_cbdata.array = array;
-       ref_cbdata.filter = filter;
-
        filter->kind = type & FILTER_REFS_KIND_MASK;
 
-       save_commit_buffer_orig = save_commit_buffer;
-       save_commit_buffer = 0;
-
-       init_contains_cache(&ref_cbdata.contains_cache);
-       init_contains_cache(&ref_cbdata.no_contains_cache);
+       init_contains_cache(&filter->internal.contains_cache);
+       init_contains_cache(&filter->internal.no_contains_cache);
 
        /*  Simple per-ref filtering */
        if (!filter->kind)
@@ -3003,19 +3036,42 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
                 * of filter_ref_kind().
                 */
                if (filter->kind == FILTER_REFS_BRANCHES)
-                       ret = for_each_fullref_in("refs/heads/", ref_filter_handler, &ref_cbdata);
+                       ret = for_each_fullref_in("refs/heads/", fn, cb_data);
                else if (filter->kind == FILTER_REFS_REMOTES)
-                       ret = for_each_fullref_in("refs/remotes/", ref_filter_handler, &ref_cbdata);
+                       ret = for_each_fullref_in("refs/remotes/", fn, cb_data);
                else if (filter->kind == FILTER_REFS_TAGS)
-                       ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata);
+                       ret = for_each_fullref_in("refs/tags/", fn, cb_data);
                else if (filter->kind & FILTER_REFS_ALL)
-                       ret = for_each_fullref_in_pattern(filter, ref_filter_handler, &ref_cbdata);
+                       ret = for_each_fullref_in_pattern(filter, fn, cb_data);
                if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD))
-                       head_ref(ref_filter_handler, &ref_cbdata);
+                       head_ref(fn, cb_data);
        }
 
-       clear_contains_cache(&ref_cbdata.contains_cache);
-       clear_contains_cache(&ref_cbdata.no_contains_cache);
+       clear_contains_cache(&filter->internal.contains_cache);
+       clear_contains_cache(&filter->internal.no_contains_cache);
+
+       return ret;
+}
+
+/*
+ * 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
+ * as per the given ref_filter structure and finally store the
+ * filtered refs in the ref_array structure.
+ */
+int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type)
+{
+       struct ref_filter_cbdata ref_cbdata;
+       int save_commit_buffer_orig;
+       int ret = 0;
+
+       ref_cbdata.array = array;
+       ref_cbdata.filter = filter;
+
+       save_commit_buffer_orig = save_commit_buffer;
+       save_commit_buffer = 0;
+
+       ret = do_filter_refs(filter, type, filter_one, &ref_cbdata);
 
        /*  Filters that need revision walking */
        reach_filter(array, &filter->reachable_from, INCLUDE_REACHED);
@@ -3025,6 +3081,51 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
        return ret;
 }
 
+static inline int can_do_iterative_format(struct ref_filter *filter,
+                                         struct ref_sorting *sorting,
+                                         struct ref_format *format)
+{
+       /*
+        * Filtering & formatting results within a single ref iteration
+        * callback is not compatible with options that require
+        * post-processing a filtered ref_array. These include:
+        * - filtering on reachability
+        * - sorting the filtered results
+        * - including ahead-behind information in the formatted output
+        */
+       return !(filter->reachable_from ||
+                filter->unreachable_from ||
+                sorting ||
+                format->bases.nr);
+}
+
+void filter_and_format_refs(struct ref_filter *filter, unsigned int type,
+                           struct ref_sorting *sorting,
+                           struct ref_format *format)
+{
+       if (can_do_iterative_format(filter, sorting, format)) {
+               int save_commit_buffer_orig;
+               struct ref_filter_and_format_cbdata ref_cbdata = {
+                       .filter = filter,
+                       .format = format,
+               };
+
+               save_commit_buffer_orig = save_commit_buffer;
+               save_commit_buffer = 0;
+
+               do_filter_refs(filter, type, filter_and_format_one, &ref_cbdata);
+
+               save_commit_buffer = save_commit_buffer_orig;
+       } else {
+               struct ref_array array = { 0 };
+               filter_refs(&array, filter, type);
+               filter_ahead_behind(the_repository, format, &array);
+               ref_array_sort(sorting, &array);
+               print_formatted_ref_array(&array, format);
+               ref_array_clear(&array);
+       }
+}
+
 static int compare_detached_head(struct ref_array_item *a, struct ref_array_item *b)
 {
        if (!(a->kind ^ b->kind))
@@ -3142,7 +3243,8 @@ void ref_sorting_set_sort_flags_all(struct ref_sorting *sorting,
 
 void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)
 {
-       QSORT_S(array->items, array->nr, compare_refs, sorting);
+       if (sorting)
+               QSORT_S(array->items, array->nr, compare_refs, sorting);
 }
 
 static void append_literal(const char *cp, const char *ep, struct ref_formatting_state *state)
@@ -3213,6 +3315,29 @@ int format_ref_array_item(struct ref_array_item *info,
        return 0;
 }
 
+void print_formatted_ref_array(struct ref_array *array, struct ref_format *format)
+{
+       int total;
+       struct strbuf output = STRBUF_INIT, err = STRBUF_INIT;
+
+       total = format->array_opts.max_count;
+       if (!total || array->nr < total)
+               total = array->nr;
+       for (int i = 0; i < total; i++) {
+               strbuf_reset(&err);
+               strbuf_reset(&output);
+               if (format_ref_array_item(array->items[i], format, &output, &err))
+                       die("%s", err.buf);
+               if (output.len || !format->array_opts.omit_empty) {
+                       fwrite(output.buf, 1, output.len, stdout);
+                       putchar('\n');
+               }
+       }
+
+       strbuf_release(&err);
+       strbuf_release(&output);
+}
+
 void pretty_print_ref(const char *name, const struct object_id *oid,
                      struct ref_format *format)
 {
@@ -3248,18 +3373,6 @@ static int parse_sorting_atom(const char *atom)
        return res;
 }
 
-/*  If no sorting option is given, use refname to sort as default */
-static struct ref_sorting *ref_default_sorting(void)
-{
-       static const char cstr_name[] = "refname";
-
-       struct ref_sorting *sorting = xcalloc(1, sizeof(*sorting));
-
-       sorting->next = NULL;
-       sorting->atom = parse_sorting_atom(cstr_name);
-       return sorting;
-}
-
 static void parse_ref_sorting(struct ref_sorting **sorting_tail, const char *arg)
 {
        struct ref_sorting *s;
@@ -3283,9 +3396,7 @@ struct ref_sorting *ref_sorting_options(struct string_list *options)
        struct string_list_item *item;
        struct ref_sorting *sorting = NULL, **tail = &sorting;
 
-       if (!options->nr) {
-               sorting = ref_default_sorting();
-       } else {
+       if (options->nr) {
                for_each_string_list_item(item, options)
                        parse_ref_sorting(tail, item->string);
        }
index 1524bc463a5701d475cb68e50caa884a6daf890a..07cd6f6da3da7e3950dc77538baf0f71950630e1 100644 (file)
@@ -3,10 +3,10 @@
 
 #include "gettext.h"
 #include "oid-array.h"
-#include "refs.h"
 #include "commit.h"
 #include "string-list.h"
 #include "strvec.h"
+#include "commit-reach.h"
 
 /* Quoting styles */
 #define QUOTE_NONE 0
@@ -75,6 +75,11 @@ struct ref_filter {
                lines;
        int abbrev,
                verbose;
+
+       struct {
+               struct contains_cache contains_cache;
+               struct contains_cache no_contains_cache;
+       } internal;
 };
 
 struct ref_format {
@@ -92,6 +97,11 @@ struct ref_format {
 
        /* List of bases for ahead-behind counts. */
        struct string_list bases;
+
+       struct {
+               int max_count;
+               int omit_empty;
+       } array_opts;
 };
 
 #define REF_FILTER_INIT { \
@@ -126,6 +136,14 @@ struct ref_format {
  * filtered refs in the ref_array structure.
  */
 int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type);
+/*
+ * Filter refs using the given ref_filter and type, sort the contents
+ * according to the given ref_sorting, format the filtered refs with the
+ * given ref_format, and print them to stdout.
+ */
+void filter_and_format_refs(struct ref_filter *filter, unsigned int type,
+                           struct ref_sorting *sorting,
+                           struct ref_format *format);
 /*  Clear all memory allocated to ref_array */
 void ref_array_clear(struct ref_array *array);
 /*  Used to verify if the given format is correct and to parse out the used atoms */
@@ -150,6 +168,12 @@ char *get_head_description(void);
 /*  Set up translated strings in the output. */
 void setup_ref_filter_porcelain_msg(void);
 
+/*
+ * Print up to maxcount ref_array elements to stdout using the given
+ * ref_format.
+ */
+void print_formatted_ref_array(struct ref_array *array, struct ref_format *format);
+
 /*
  * Print a single ref, outside of any ref-filter. Note that the
  * name must be a fully qualified refname.
index 9ad50e7d93e40c7a5f9c7ad0cecb0b7d1766105b..0a1bc35e8cd2c82771c0272dcdc08f8b99ba73e7 100644 (file)
--- a/reflog.c
+++ b/reflog.c
@@ -6,7 +6,6 @@
 #include "revision.h"
 #include "tree.h"
 #include "tree-walk.h"
-#include "worktree.h"
 
 /* Remember to update object flag allocation in object.h */
 #define INCOMPLETE     (1u<<10)
diff --git a/refs.c b/refs.c
index fcae5dddc6050627668048e966384b7f9fa70171..c633abf2847cf1b05eec9c9b4d0c817cf78618b6 100644 (file)
--- a/refs.c
+++ b/refs.c
 /*
  * List of all available backends
  */
-static struct ref_storage_be *refs_backends = &refs_be_files;
+static const struct ref_storage_be *refs_backends[] = {
+       [REF_STORAGE_FORMAT_FILES] = &refs_be_files,
+};
 
-static struct ref_storage_be *find_ref_storage_backend(const char *name)
+static const struct ref_storage_be *find_ref_storage_backend(unsigned int ref_storage_format)
 {
-       struct ref_storage_be *be;
-       for (be = refs_backends; be; be = be->next)
-               if (!strcmp(be->name, name))
-                       return be;
+       if (ref_storage_format < ARRAY_SIZE(refs_backends))
+               return refs_backends[ref_storage_format];
        return NULL;
 }
 
+unsigned int ref_storage_format_by_name(const char *name)
+{
+       for (unsigned int i = 0; i < ARRAY_SIZE(refs_backends); i++)
+               if (refs_backends[i] && !strcmp(refs_backends[i]->name, name))
+                       return i;
+       return REF_STORAGE_FORMAT_UNKNOWN;
+}
+
+const char *ref_storage_format_to_name(unsigned int ref_storage_format)
+{
+       const struct ref_storage_be *be = find_ref_storage_backend(ref_storage_format);
+       if (!be)
+               return "unknown";
+       return be->name;
+}
+
 /*
  * How to handle various characters in refnames:
  * 0: An acceptable character for refs
@@ -1806,8 +1822,10 @@ static int refs_read_special_head(struct ref_store *ref_store,
        int result = -1;
        strbuf_addf(&full_path, "%s/%s", ref_store->gitdir, refname);
 
-       if (strbuf_read_file(&content, full_path.buf, 0) < 0)
+       if (strbuf_read_file(&content, full_path.buf, 0) < 0) {
+               *failure_errno = errno;
                goto done;
+       }
 
        result = parse_loose_ref_contents(content.buf, oid, referent, type,
                                          failure_errno);
@@ -1818,15 +1836,45 @@ done:
        return result;
 }
 
+static int is_special_ref(const char *refname)
+{
+       /*
+        * Special references are refs that have different semantics compared
+        * to "normal" refs. These refs can thus not be stored in the ref
+        * backend, but must always be accessed via the filesystem. The
+        * following refs are special:
+        *
+        * - FETCH_HEAD may contain multiple object IDs, and each one of them
+        *   carries additional metadata like where it came from.
+        *
+        * - MERGE_HEAD may contain multiple object IDs when merging multiple
+        *   heads.
+        *
+        * Reading, writing or deleting references must consistently go either
+        * through the filesystem (special refs) or through the reference
+        * backend (normal ones).
+        */
+       static const char * const special_refs[] = {
+               "FETCH_HEAD",
+               "MERGE_HEAD",
+       };
+       size_t i;
+
+       for (i = 0; i < ARRAY_SIZE(special_refs); i++)
+               if (!strcmp(refname, special_refs[i]))
+                       return 1;
+
+       return 0;
+}
+
 int refs_read_raw_ref(struct ref_store *ref_store, const char *refname,
                      struct object_id *oid, struct strbuf *referent,
                      unsigned int *type, int *failure_errno)
 {
        assert(failure_errno);
-       if (!strcmp(refname, "FETCH_HEAD") || !strcmp(refname, "MERGE_HEAD")) {
+       if (is_special_ref(refname))
                return refs_read_special_head(ref_store, refname, oid, referent,
                                              type, failure_errno);
-       }
 
        return ref_store->be->read_raw_ref(ref_store, refname, oid, referent,
                                           type, failure_errno);
@@ -1928,11 +1976,9 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
 }
 
 /* backend functions */
-int refs_init_db(struct strbuf *err)
+int refs_init_db(struct ref_store *refs, int flags, struct strbuf *err)
 {
-       struct ref_store *refs = get_main_ref_store(the_repository);
-
-       return refs->be->init_db(refs, err);
+       return refs->be->init_db(refs, flags, err);
 }
 
 const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
@@ -2029,12 +2075,12 @@ static struct ref_store *ref_store_init(struct repository *repo,
                                        const char *gitdir,
                                        unsigned int flags)
 {
-       const char *be_name = "files";
-       struct ref_storage_be *be = find_ref_storage_backend(be_name);
+       const struct ref_storage_be *be;
        struct ref_store *refs;
 
+       be = find_ref_storage_backend(repo->ref_storage_format);
        if (!be)
-               BUG("reference backend %s is unknown", be_name);
+               BUG("reference backend is unknown");
 
        refs = be->init(repo, gitdir, flags);
        return refs;
@@ -2599,13 +2645,55 @@ void ref_transaction_for_each_queued_update(struct ref_transaction *transaction,
 int refs_delete_refs(struct ref_store *refs, const char *logmsg,
                     struct string_list *refnames, unsigned int flags)
 {
+       struct ref_transaction *transaction;
+       struct strbuf err = STRBUF_INIT;
+       struct string_list_item *item;
+       int ret = 0, failures = 0;
        char *msg;
-       int retval;
+
+       if (!refnames->nr)
+               return 0;
 
        msg = normalize_reflog_message(logmsg);
-       retval = refs->be->delete_refs(refs, msg, refnames, flags);
+
+       /*
+        * Since we don't check the references' old_oids, the
+        * individual updates can't fail, so we can pack all of the
+        * updates into a single transaction.
+        */
+       transaction = ref_store_transaction_begin(refs, &err);
+       if (!transaction) {
+               ret = error("%s", err.buf);
+               goto out;
+       }
+
+       for_each_string_list_item(item, refnames) {
+               ret = ref_transaction_delete(transaction, item->string,
+                                            NULL, flags, msg, &err);
+               if (ret) {
+                       warning(_("could not delete reference %s: %s"),
+                               item->string, err.buf);
+                       strbuf_reset(&err);
+                       failures = 1;
+               }
+       }
+
+       ret = ref_transaction_commit(transaction, &err);
+       if (ret) {
+               if (refnames->nr == 1)
+                       error(_("could not delete reference %s: %s"),
+                             refnames->items[0].string, err.buf);
+               else
+                       error(_("could not delete references: %s"), err.buf);
+       }
+
+out:
+       if (!ret && failures)
+               ret = -1;
+       ref_transaction_free(transaction);
+       strbuf_release(&err);
        free(msg);
-       return retval;
+       return ret;
 }
 
 int delete_refs(const char *msg, struct string_list *refnames,
diff --git a/refs.h b/refs.h
index 23211a5ea1cabbb0a35d091bf2b5dbbd87e28252..303c5fac4d08b9798fafb6d4c667e9a94bf91e72 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -11,6 +11,9 @@ struct string_list;
 struct string_list_item;
 struct worktree;
 
+unsigned int ref_storage_format_by_name(const char *name);
+const char *ref_storage_format_to_name(unsigned int ref_storage_format);
+
 /*
  * Resolve a reference, recursively following symbolic refererences.
  *
@@ -56,7 +59,7 @@ struct worktree;
  * Even with RESOLVE_REF_ALLOW_BAD_NAME, names that escape the refs/
  * directory and do not consist of all caps and underscores cannot be
  * resolved. The function returns NULL for such ref names.
- * Caps and underscores refers to the special refs, such as HEAD,
+ * Caps and underscores refers to the pseudorefs, such as HEAD,
  * FETCH_HEAD and friends, that all live outside of the refs/ directory.
  */
 #define RESOLVE_REF_READING 0x01
@@ -123,7 +126,9 @@ int should_autocreate_reflog(const char *refname);
 
 int is_branch(const char *refname);
 
-int refs_init_db(struct strbuf *err);
+#define REFS_INIT_DB_IS_WORKTREE (1 << 0)
+
+int refs_init_db(struct ref_store *refs, int flags, struct strbuf *err);
 
 /*
  * Return the peeled value of the oid currently being iterated via
index b7ffc4ce67e333321036592b7e53efa30f8871d8..634681ca44e39b86129b2bbd305aa4c1771c3328 100644 (file)
@@ -33,10 +33,10 @@ struct ref_store *maybe_debug_wrap_ref_store(const char *gitdir, struct ref_stor
        return (struct ref_store *)res;
 }
 
-static int debug_init_db(struct ref_store *refs, struct strbuf *err)
+static int debug_init_db(struct ref_store *refs, int flags, struct strbuf *err)
 {
        struct debug_ref_store *drefs = (struct debug_ref_store *)refs;
-       int res = drefs->refs->be->init_db(drefs->refs, err);
+       int res = drefs->refs->be->init_db(drefs->refs, flags, err);
        trace_printf_key(&trace_refs, "init_db: %d\n", res);
        return res;
 }
@@ -143,20 +143,6 @@ static int debug_create_symref(struct ref_store *ref_store,
        return res;
 }
 
-static int debug_delete_refs(struct ref_store *ref_store, const char *msg,
-                            struct string_list *refnames, unsigned int flags)
-{
-       struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
-       int res =
-               drefs->refs->be->delete_refs(drefs->refs, msg, refnames, flags);
-       int i;
-       trace_printf_key(&trace_refs, "delete_refs {\n");
-       for (i = 0; i < refnames->nr; i++)
-               trace_printf_key(&trace_refs, "%s\n", refnames->items[i].string);
-       trace_printf_key(&trace_refs, "}: %d\n", res);
-       return res;
-}
-
 static int debug_rename_ref(struct ref_store *ref_store, const char *oldref,
                            const char *newref, const char *logmsg)
 {
@@ -440,7 +426,6 @@ static int debug_reflog_expire(struct ref_store *ref_store, const char *refname,
 }
 
 struct ref_storage_be refs_be_debug = {
-       .next = NULL,
        .name = "debug",
        .init = NULL,
        .init_db = debug_init_db,
@@ -458,7 +443,6 @@ struct ref_storage_be refs_be_debug = {
 
        .pack_refs = debug_pack_refs,
        .create_symref = debug_create_symref,
-       .delete_refs = debug_delete_refs,
        .rename_ref = debug_rename_ref,
        .copy_ref = debug_copy_ref,
 
index db5c0c7a724d57ad67250cb0be996ae5cfbc0372..75dcc21ecb5ab83cadd37a899a252618a0c66df0 100644 (file)
@@ -1,5 +1,4 @@
 #include "../git-compat-util.h"
-#include "../config.h"
 #include "../copy.h"
 #include "../environment.h"
 #include "../gettext.h"
@@ -19,7 +18,6 @@
 #include "../dir.h"
 #include "../chdir-notify.h"
 #include "../setup.h"
-#include "../worktree.h"
 #include "../wrapper.h"
 #include "../write-or-die.h"
 #include "../revision.h"
@@ -1265,54 +1263,6 @@ static int files_pack_refs(struct ref_store *ref_store,
        return 0;
 }
 
-static int files_delete_refs(struct ref_store *ref_store, const char *msg,
-                            struct string_list *refnames, unsigned int flags)
-{
-       struct files_ref_store *refs =
-               files_downcast(ref_store, REF_STORE_WRITE, "delete_refs");
-       struct strbuf err = STRBUF_INIT;
-       int i, result = 0;
-
-       if (!refnames->nr)
-               return 0;
-
-       if (packed_refs_lock(refs->packed_ref_store, 0, &err))
-               goto error;
-
-       if (refs_delete_refs(refs->packed_ref_store, msg, refnames, flags)) {
-               packed_refs_unlock(refs->packed_ref_store);
-               goto error;
-       }
-
-       packed_refs_unlock(refs->packed_ref_store);
-
-       for (i = 0; i < refnames->nr; i++) {
-               const char *refname = refnames->items[i].string;
-
-               if (refs_delete_ref(&refs->base, msg, refname, NULL, flags))
-                       result |= error(_("could not remove reference %s"), refname);
-       }
-
-       strbuf_release(&err);
-       return result;
-
-error:
-       /*
-        * If we failed to rewrite the packed-refs file, then it is
-        * unsafe to try to remove loose refs, because doing so might
-        * expose an obsolete packed value for a reference that might
-        * even point at an object that has been garbage collected.
-        */
-       if (refnames->nr == 1)
-               error(_("could not delete reference %s: %s"),
-                     refnames->items[0].string, err.buf);
-       else
-               error(_("could not delete references: %s"), err.buf);
-
-       strbuf_release(&err);
-       return -1;
-}
-
 /*
  * People using contrib's git-new-workdir have .git/logs/refs ->
  * /some/other/path/.git/logs/refs, and that may live on another device.
@@ -3268,28 +3218,52 @@ static int files_reflog_expire(struct ref_store *ref_store,
        return -1;
 }
 
-static int files_init_db(struct ref_store *ref_store, struct strbuf *err UNUSED)
+static int files_init_db(struct ref_store *ref_store,
+                        int flags,
+                        struct strbuf *err UNUSED)
 {
        struct files_ref_store *refs =
                files_downcast(ref_store, REF_STORE_WRITE, "init_db");
        struct strbuf sb = STRBUF_INIT;
 
        /*
-        * Create .git/refs/{heads,tags}
+        * We need to create a "refs" dir in any case so that older versions of
+        * Git can tell that this is a repository. This serves two main purposes:
+        *
+        * - Clients will know to stop walking the parent-directory chain when
+        *   detecting the Git repository. Otherwise they may end up detecting
+        *   a Git repository in a parent directory instead.
+        *
+        * - Instead of failing to detect a repository with unknown reference
+        *   format altogether, old clients will print an error saying that
+        *   they do not understand the reference format extension.
         */
-       files_ref_path(refs, &sb, "refs/heads");
+       strbuf_addf(&sb, "%s/refs", ref_store->gitdir);
        safe_create_dir(sb.buf, 1);
+       adjust_shared_perm(sb.buf);
 
-       strbuf_reset(&sb);
-       files_ref_path(refs, &sb, "refs/tags");
-       safe_create_dir(sb.buf, 1);
+       /*
+        * There is no need to create directories for common refs when creating
+        * a worktree ref store.
+        */
+       if (!(flags & REFS_INIT_DB_IS_WORKTREE)) {
+               /*
+                * Create .git/refs/{heads,tags}
+                */
+               strbuf_reset(&sb);
+               files_ref_path(refs, &sb, "refs/heads");
+               safe_create_dir(sb.buf, 1);
+
+               strbuf_reset(&sb);
+               files_ref_path(refs, &sb, "refs/tags");
+               safe_create_dir(sb.buf, 1);
+       }
 
        strbuf_release(&sb);
        return 0;
 }
 
 struct ref_storage_be refs_be_files = {
-       .next = NULL,
        .name = "files",
        .init = files_ref_store_create,
        .init_db = files_init_db,
@@ -3300,7 +3274,6 @@ struct ref_storage_be refs_be_files = {
 
        .pack_refs = files_pack_refs,
        .create_symref = files_create_symref,
-       .delete_refs = files_delete_refs,
        .rename_ref = files_rename_ref,
        .copy_ref = files_copy_ref,
 
index 59c78d7941f8d5e05fec6c895aafeb41803b919d..a499a91c7e0ac94e45a7c28264647802522b65c4 100644 (file)
@@ -1,5 +1,4 @@
 #include "../git-compat-util.h"
-#include "../alloc.h"
 #include "../config.h"
 #include "../gettext.h"
 #include "../hash.h"
@@ -1246,6 +1245,7 @@ static const char PACKED_REFS_HEADER[] =
        "# pack-refs with: peeled fully-peeled sorted \n";
 
 static int packed_init_db(struct ref_store *ref_store UNUSED,
+                         int flags UNUSED,
                          struct strbuf *err UNUSED)
 {
        /* Nothing to do. */
@@ -1688,55 +1688,6 @@ static int packed_initial_transaction_commit(struct ref_store *ref_store UNUSED,
        return ref_transaction_commit(transaction, err);
 }
 
-static int packed_delete_refs(struct ref_store *ref_store, const char *msg,
-                            struct string_list *refnames, unsigned int flags)
-{
-       struct packed_ref_store *refs =
-               packed_downcast(ref_store, REF_STORE_WRITE, "delete_refs");
-       struct strbuf err = STRBUF_INIT;
-       struct ref_transaction *transaction;
-       struct string_list_item *item;
-       int ret;
-
-       (void)refs; /* We need the check above, but don't use the variable */
-
-       if (!refnames->nr)
-               return 0;
-
-       /*
-        * Since we don't check the references' old_oids, the
-        * individual updates can't fail, so we can pack all of the
-        * updates into a single transaction.
-        */
-
-       transaction = ref_store_transaction_begin(ref_store, &err);
-       if (!transaction)
-               return -1;
-
-       for_each_string_list_item(item, refnames) {
-               if (ref_transaction_delete(transaction, item->string, NULL,
-                                          flags, msg, &err)) {
-                       warning(_("could not delete reference %s: %s"),
-                               item->string, err.buf);
-                       strbuf_reset(&err);
-               }
-       }
-
-       ret = ref_transaction_commit(transaction, &err);
-
-       if (ret) {
-               if (refnames->nr == 1)
-                       error(_("could not delete reference %s: %s"),
-                             refnames->items[0].string, err.buf);
-               else
-                       error(_("could not delete references: %s"), err.buf);
-       }
-
-       ref_transaction_free(transaction);
-       strbuf_release(&err);
-       return ret;
-}
-
 static int packed_pack_refs(struct ref_store *ref_store UNUSED,
                            struct pack_refs_opts *pack_opts UNUSED)
 {
@@ -1754,7 +1705,6 @@ static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_s
 }
 
 struct ref_storage_be refs_be_packed = {
-       .next = NULL,
        .name = "packed",
        .init = packed_ref_store_create,
        .init_db = packed_init_db,
@@ -1765,7 +1715,6 @@ struct ref_storage_be refs_be_packed = {
 
        .pack_refs = packed_pack_refs,
        .create_symref = NULL,
-       .delete_refs = packed_delete_refs,
        .rename_ref = NULL,
        .copy_ref = NULL,
 
index 6e3b725245c3e8df1ec666cc23ab51b718be1d1f..a372a00941fda659cc60722452c8766f9c7de455 100644 (file)
@@ -1,5 +1,4 @@
 #include "../git-compat-util.h"
-#include "../alloc.h"
 #include "../hash.h"
 #include "../refs.h"
 #include "../repository.h"
index 9db8aec4da8eef6fb9542a9db5d1cbae01c129e6..82219829b011d12fcb4a27577baa41225ff812f7 100644 (file)
@@ -529,7 +529,9 @@ typedef struct ref_store *ref_store_init_fn(struct repository *repo,
                                            const char *gitdir,
                                            unsigned int flags);
 
-typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);
+typedef int ref_init_db_fn(struct ref_store *refs,
+                          int flags,
+                          struct strbuf *err);
 
 typedef int ref_transaction_prepare_fn(struct ref_store *refs,
                                       struct ref_transaction *transaction,
@@ -553,8 +555,6 @@ typedef int create_symref_fn(struct ref_store *ref_store,
                             const char *ref_target,
                             const char *refs_heads_master,
                             const char *logmsg);
-typedef int delete_refs_fn(struct ref_store *ref_store, const char *msg,
-                          struct string_list *refnames, unsigned int flags);
 typedef int rename_ref_fn(struct ref_store *ref_store,
                          const char *oldref, const char *newref,
                          const char *logmsg);
@@ -665,7 +665,6 @@ typedef int read_symbolic_ref_fn(struct ref_store *ref_store, const char *refnam
                                 struct strbuf *referent);
 
 struct ref_storage_be {
-       struct ref_storage_be *next;
        const char *name;
        ref_store_init_fn *init;
        ref_init_db_fn *init_db;
@@ -677,7 +676,6 @@ struct ref_storage_be {
 
        pack_refs_fn *pack_refs;
        create_symref_fn *create_symref;
-       delete_refs_fn *delete_refs;
        rename_ref_fn *rename_ref;
        copy_ref_fn *copy_ref;
 
index 34d4d073692f9e913d84715d987c8e1c35a7fe8a..1df3d8a0f09671c74143e655cb2963adda872545 100644 (file)
@@ -323,30 +323,28 @@ int block_iter_next(struct block_iter *it, struct reftable_record *rec)
                .len = it->br->block_len - it->next_off,
        };
        struct string_view start = in;
-       struct strbuf key = STRBUF_INIT;
        uint8_t extra = 0;
        int n = 0;
 
        if (it->next_off >= it->br->block_len)
                return 1;
 
-       n = reftable_decode_key(&key, &extra, it->last_key, in);
+       n = reftable_decode_key(&it->key, &extra, it->last_key, in);
        if (n < 0)
                return -1;
 
-       if (!key.len)
+       if (!it->key.len)
                return REFTABLE_FORMAT_ERROR;
 
        string_view_consume(&in, n);
-       n = reftable_record_decode(rec, key, extra, in, it->br->hash_size);
+       n = reftable_record_decode(rec, it->key, extra, in, it->br->hash_size);
        if (n < 0)
                return -1;
        string_view_consume(&in, n);
 
        strbuf_reset(&it->last_key);
-       strbuf_addbuf(&it->last_key, &key);
+       strbuf_addbuf(&it->last_key, &it->key);
        it->next_off += start.len - in.len;
-       strbuf_release(&key);
        return 0;
 }
 
@@ -377,6 +375,7 @@ int block_iter_seek(struct block_iter *it, struct strbuf *want)
 void block_iter_close(struct block_iter *it)
 {
        strbuf_release(&it->last_key);
+       strbuf_release(&it->key);
 }
 
 int block_reader_seek(struct block_reader *br, struct block_iter *it,
@@ -387,11 +386,8 @@ int block_reader_seek(struct block_reader *br, struct block_iter *it,
                .r = br,
        };
        struct reftable_record rec = reftable_new_record(block_reader_type(br));
-       struct strbuf key = STRBUF_INIT;
        int err = 0;
-       struct block_iter next = {
-               .last_key = STRBUF_INIT,
-       };
+       struct block_iter next = BLOCK_ITER_INIT;
 
        int i = binsearch(br->restart_count, &restart_key_less, &args);
        if (args.error) {
@@ -416,8 +412,8 @@ int block_reader_seek(struct block_reader *br, struct block_iter *it,
                if (err < 0)
                        goto done;
 
-               reftable_record_key(&rec, &key);
-               if (err > 0 || strbuf_cmp(&key, want) >= 0) {
+               reftable_record_key(&rec, &it->key);
+               if (err > 0 || strbuf_cmp(&it->key, want) >= 0) {
                        err = 0;
                        goto done;
                }
@@ -426,8 +422,7 @@ int block_reader_seek(struct block_reader *br, struct block_iter *it,
        }
 
 done:
-       strbuf_release(&key);
-       strbuf_release(&next.last_key);
+       block_iter_close(&next);
        reftable_record_release(&rec);
 
        return err;
index 87c77539b5bd6ab0b9909c51801719258507b424..17481e6331979cc31972ee3dad576e9c594a1769 100644 (file)
@@ -84,8 +84,14 @@ struct block_iter {
 
        /* key for last entry we read. */
        struct strbuf last_key;
+       struct strbuf key;
 };
 
+#define BLOCK_ITER_INIT { \
+       .last_key = STRBUF_INIT, \
+       .key = STRBUF_INIT, \
+}
+
 /* initializes a block reader. */
 int block_reader_init(struct block_reader *br, struct reftable_block *bl,
                      uint32_t header_off, uint32_t table_block_size,
index cb88af4a5639258945f569ede758e31809b62ead..dedb05c7d8c0560898b03aba93043c7efa46aeea 100644 (file)
@@ -32,7 +32,7 @@ static void test_block_read_write(void)
        int i = 0;
        int n;
        struct block_reader br = { 0 };
-       struct block_iter it = { .last_key = STRBUF_INIT };
+       struct block_iter it = BLOCK_ITER_INIT;
        int j = 0;
        struct strbuf want = STRBUF_INIT;
 
@@ -49,13 +49,11 @@ static void test_block_read_write(void)
 
        for (i = 0; i < N; i++) {
                char name[100];
-               uint8_t hash[GIT_SHA1_RAWSZ];
                snprintf(name, sizeof(name), "branch%02d", i);
-               memset(hash, i, sizeof(hash));
 
                rec.u.ref.refname = name;
                rec.u.ref.value_type = REFTABLE_REF_VAL1;
-               rec.u.ref.value.val1 = hash;
+               memset(rec.u.ref.value.val1, i, GIT_SHA1_RAWSZ);
 
                names[i] = xstrdup(name);
                n = block_writer_add(&bw, &rec);
@@ -87,7 +85,7 @@ static void test_block_read_write(void)
        block_iter_close(&it);
 
        for (i = 0; i < N; i++) {
-               struct block_iter it = { .last_key = STRBUF_INIT };
+               struct block_iter it = BLOCK_ITER_INIT;
                strbuf_reset(&want);
                strbuf_addstr(&want, names[i]);
 
index 8331b34e82300630b68c574e4f68071f3ebf261a..8c41e3c70f36aa0aab704fd8d296825f6e342a3d 100644 (file)
@@ -76,8 +76,8 @@ struct reftable_block_source malloc_block_source(void)
 }
 
 struct file_block_source {
-       int fd;
        uint64_t size;
+       unsigned char *data;
 };
 
 static uint64_t file_size(void *b)
@@ -87,19 +87,12 @@ static uint64_t file_size(void *b)
 
 static void file_return_block(void *b, struct reftable_block *dest)
 {
-       if (dest->len)
-               memset(dest->data, 0xff, dest->len);
-       reftable_free(dest->data);
 }
 
-static void file_close(void *b)
+static void file_close(void *v)
 {
-       int fd = ((struct file_block_source *)b)->fd;
-       if (fd > 0) {
-               close(fd);
-               ((struct file_block_source *)b)->fd = 0;
-       }
-
+       struct file_block_source *b = v;
+       munmap(b->data, b->size);
        reftable_free(b);
 }
 
@@ -108,9 +101,7 @@ static int file_read_block(void *v, struct reftable_block *dest, uint64_t off,
 {
        struct file_block_source *b = v;
        assert(off + size <= b->size);
-       dest->data = reftable_malloc(size);
-       if (pread(b->fd, dest->data, size, off) != size)
-               return -1;
+       dest->data = b->data + off;
        dest->len = size;
        return size;
 }
@@ -125,26 +116,26 @@ static struct reftable_block_source_vtable file_vtable = {
 int reftable_block_source_from_file(struct reftable_block_source *bs,
                                    const char *name)
 {
-       struct stat st = { 0 };
-       int err = 0;
-       int fd = open(name, O_RDONLY);
-       struct file_block_source *p = NULL;
+       struct file_block_source *p;
+       struct stat st;
+       int fd;
+
+       fd = open(name, O_RDONLY);
        if (fd < 0) {
-               if (errno == ENOENT) {
+               if (errno == ENOENT)
                        return REFTABLE_NOT_EXIST_ERROR;
-               }
                return -1;
        }
 
-       err = fstat(fd, &st);
-       if (err < 0) {
+       if (fstat(fd, &st) < 0) {
                close(fd);
                return REFTABLE_IO_ERROR;
        }
 
-       p = reftable_calloc(sizeof(struct file_block_source));
+       p = reftable_calloc(sizeof(*p));
        p->size = st.st_size;
-       p->fd = fd;
+       p->data = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       close(fd);
 
        assert(!bs->ops);
        bs->ops = &file_vtable;
index ce936b4e1887ca982c9d814a014b469cbb49e52e..26e0393c7db7a4b6677bb3c5a73ce08559956dd7 100644 (file)
@@ -11,14 +11,12 @@ https://developers.google.com/open-source/licenses/bsd
 
 #include "reftable-blocksource.h"
 #include "reftable-error.h"
-#include "reftable-merged.h"
 #include "reftable-record.h"
 #include "reftable-tests.h"
 #include "reftable-writer.h"
 #include "reftable-iterator.h"
 #include "reftable-reader.h"
 #include "reftable-stack.h"
-#include "reftable-generic.h"
 
 #include <stddef.h>
 #include <stdio.h>
index 57f8032db941ca77f5c61d0e6d38268c56958be9..b9f1c7c18a2efc48e91c2526a42bb9433783f8dd 100644 (file)
@@ -6,7 +6,6 @@ license that can be found in the LICENSE file or at
 https://developers.google.com/open-source/licenses/bsd
 */
 
-#include "basics.h"
 #include "constants.h"
 #include "record.h"
 #include "generic.h"
index 09eb0cbfa5997e0eca4f378a8d45025c4af8732d..47d67d84df679c522ce50d1d7aade2d1be683b5a 100644 (file)
@@ -53,10 +53,10 @@ struct indexed_table_ref_iter {
        int is_finished;
 };
 
-#define INDEXED_TABLE_REF_ITER_INIT                                     \
-       {                                                               \
-               .cur = { .last_key = STRBUF_INIT }, .oid = STRBUF_INIT, \
-       }
+#define INDEXED_TABLE_REF_ITER_INIT { \
+       .cur = BLOCK_ITER_INIT, \
+       .oid = STRBUF_INIT, \
+}
 
 void iterator_from_indexed_table_ref_iter(struct reftable_iterator *it,
                                          struct indexed_table_ref_iter *itr);
index 5ded470c086c92760083dc16c08e7bdff821e9bf..c258ce953e81d7df5774a03250b056280beccbf3 100644 (file)
@@ -11,7 +11,6 @@ https://developers.google.com/open-source/licenses/bsd
 #include "constants.h"
 #include "iter.h"
 #include "pq.h"
-#include "reader.h"
 #include "record.h"
 #include "generic.h"
 #include "reftable-merged.h"
@@ -52,6 +51,8 @@ static void merged_iter_close(void *p)
                reftable_iterator_destroy(&mi->stack[i]);
        }
        reftable_free(mi->stack);
+       strbuf_release(&mi->key);
+       strbuf_release(&mi->entry_key);
 }
 
 static int merged_iter_advance_nonnull_subiter(struct merged_iter *mi,
@@ -85,7 +86,6 @@ static int merged_iter_advance_subiter(struct merged_iter *mi, size_t idx)
 static int merged_iter_next_entry(struct merged_iter *mi,
                                  struct reftable_record *rec)
 {
-       struct strbuf entry_key = STRBUF_INIT;
        struct pq_entry entry = { 0 };
        int err = 0;
 
@@ -105,33 +105,31 @@ static int merged_iter_next_entry(struct merged_iter *mi,
          such a deployment, the loop below must be changed to collect all
          entries for the same key, and return new the newest one.
        */
-       reftable_record_key(&entry.rec, &entry_key);
+       reftable_record_key(&entry.rec, &mi->entry_key);
        while (!merged_iter_pqueue_is_empty(mi->pq)) {
                struct pq_entry top = merged_iter_pqueue_top(mi->pq);
-               struct strbuf k = STRBUF_INIT;
-               int err = 0, cmp = 0;
+               int cmp = 0;
 
-               reftable_record_key(&top.rec, &k);
+               reftable_record_key(&top.rec, &mi->key);
 
-               cmp = strbuf_cmp(&k, &entry_key);
-               strbuf_release(&k);
-
-               if (cmp > 0) {
+               cmp = strbuf_cmp(&mi->key, &mi->entry_key);
+               if (cmp > 0)
                        break;
-               }
 
                merged_iter_pqueue_remove(&mi->pq);
                err = merged_iter_advance_subiter(mi, top.index);
-               if (err < 0) {
-                       return err;
-               }
+               if (err < 0)
+                       goto done;
                reftable_record_release(&top.rec);
        }
 
-       reftable_record_copy_from(rec, &entry.rec, hash_size(mi->hash_id));
-       reftable_record_release(&entry.rec);
-       strbuf_release(&entry_key);
-       return 0;
+       reftable_record_release(rec);
+       *rec = entry.rec;
+
+done:
+       if (err)
+               reftable_record_release(&entry.rec);
+       return err;
 }
 
 static int merged_iter_next(struct merged_iter *mi, struct reftable_record *rec)
@@ -248,6 +246,8 @@ static int merged_table_seek_record(struct reftable_merged_table *mt,
                .typ = reftable_record_type(rec),
                .hash_id = mt->hash_id,
                .suppress_deletions = mt->suppress_deletions,
+               .key = STRBUF_INIT,
+               .entry_key = STRBUF_INIT,
        };
        int n = 0;
        int err = 0;
index 7d9f95d27ed0a44c4208e743b6b83ba4cdc5536c..d5b39dfe7f1e3b54b5dc5e7ae46068155794986f 100644 (file)
@@ -31,6 +31,8 @@ struct merged_iter {
        uint8_t typ;
        int suppress_deletions;
        struct merged_iter_pqueue pq;
+       struct strbuf key;
+       struct strbuf entry_key;
 };
 
 void merged_table_release(struct reftable_merged_table *mt);
index d08c16abefbc3d8562c29cf1703d1a6908f71dcd..46908f738f770f4be920a65df85b96178ddbc70d 100644 (file)
@@ -12,7 +12,6 @@ https://developers.google.com/open-source/licenses/bsd
 
 #include "basics.h"
 #include "blocksource.h"
-#include "constants.h"
 #include "reader.h"
 #include "record.h"
 #include "test_framework.h"
@@ -123,13 +122,11 @@ static void readers_destroy(struct reftable_reader **readers, size_t n)
 
 static void test_merged_between(void)
 {
-       uint8_t hash1[GIT_SHA1_RAWSZ] = { 1, 2, 3, 0 };
-
        struct reftable_ref_record r1[] = { {
                .refname = "b",
                .update_index = 1,
                .value_type = REFTABLE_REF_VAL1,
-               .value.val1 = hash1,
+               .value.val1 = { 1, 2, 3, 0 },
        } };
        struct reftable_ref_record r2[] = { {
                .refname = "a",
@@ -165,26 +162,24 @@ static void test_merged_between(void)
 
 static void test_merged(void)
 {
-       uint8_t hash1[GIT_SHA1_RAWSZ] = { 1 };
-       uint8_t hash2[GIT_SHA1_RAWSZ] = { 2 };
        struct reftable_ref_record r1[] = {
                {
                        .refname = "a",
                        .update_index = 1,
                        .value_type = REFTABLE_REF_VAL1,
-                       .value.val1 = hash1,
+                       .value.val1 = { 1 },
                },
                {
                        .refname = "b",
                        .update_index = 1,
                        .value_type = REFTABLE_REF_VAL1,
-                       .value.val1 = hash1,
+                       .value.val1 = { 1 },
                },
                {
                        .refname = "c",
                        .update_index = 1,
                        .value_type = REFTABLE_REF_VAL1,
-                       .value.val1 = hash1,
+                       .value.val1 = { 1 },
                }
        };
        struct reftable_ref_record r2[] = { {
@@ -197,13 +192,13 @@ static void test_merged(void)
                        .refname = "c",
                        .update_index = 3,
                        .value_type = REFTABLE_REF_VAL1,
-                       .value.val1 = hash2,
+                       .value.val1 = { 2 },
                },
                {
                        .refname = "d",
                        .update_index = 3,
                        .value_type = REFTABLE_REF_VAL1,
-                       .value.val1 = hash1,
+                       .value.val1 = { 1 },
                },
        };
 
index b4db23ce1880794a937b841e28ffa27e8869d2c7..64dc366fb15935c55c5377ec9d4f1b74640cc3f6 100644 (file)
@@ -16,7 +16,6 @@ https://developers.google.com/open-source/licenses/bsd
 #include "record.h"
 #include "reftable-error.h"
 #include "reftable-generic.h"
-#include "tree.h"
 
 uint64_t block_source_size(struct reftable_block_source *source)
 {
@@ -224,10 +223,9 @@ struct table_iter {
        struct block_iter bi;
        int is_finished;
 };
-#define TABLE_ITER_INIT                          \
-       {                                        \
-               .bi = {.last_key = STRBUF_INIT } \
-       }
+#define TABLE_ITER_INIT { \
+       .bi = BLOCK_ITER_INIT \
+}
 
 static void table_iter_copy_from(struct table_iter *dest,
                                 struct table_iter *src)
index 469ab79a5adf3dfaba0160e523bdcece5b9875e0..b8a32240164d6a5c29789fb1730a630127f085d5 100644 (file)
@@ -11,7 +11,6 @@ https://developers.google.com/open-source/licenses/bsd
 #include "basics.h"
 #include "block.h"
 #include "blocksource.h"
-#include "constants.h"
 #include "reader.h"
 #include "record.h"
 #include "test_framework.h"
@@ -60,18 +59,15 @@ static void write_table(char ***names, struct strbuf *buf, int N,
        *names = reftable_calloc(sizeof(char *) * (N + 1));
        reftable_writer_set_limits(w, update_index, update_index);
        for (i = 0; i < N; i++) {
-               uint8_t hash[GIT_SHA256_RAWSZ] = { 0 };
                char name[100];
                int n;
 
-               set_test_hash(hash, i);
-
                snprintf(name, sizeof(name), "refs/heads/branch%02d", i);
 
                ref.refname = name;
                ref.update_index = update_index;
                ref.value_type = REFTABLE_REF_VAL1;
-               ref.value.val1 = hash;
+               set_test_hash(ref.value.val1, i);
                (*names)[i] = xstrdup(name);
 
                n = reftable_writer_add_ref(w, &ref);
@@ -141,8 +137,8 @@ static void test_log_buffer_size(void)
        */
        uint8_t hash1[GIT_SHA1_RAWSZ], hash2[GIT_SHA1_RAWSZ];
        for (i = 0; i < GIT_SHA1_RAWSZ; i++) {
-               hash1[i] = (uint8_t)(rand() % 256);
-               hash2[i] = (uint8_t)(rand() % 256);
+               hash1[i] = (uint8_t)(git_rand() % 256);
+               hash2[i] = (uint8_t)(git_rand() % 256);
        }
        log.value.update.old_hash = hash1;
        log.value.update.new_hash = hash2;
@@ -320,7 +316,7 @@ static void test_log_zlib_corruption(void)
        };
 
        for (i = 0; i < sizeof(message) - 1; i++)
-               message[i] = (uint8_t)(rand() % 64 + ' ');
+               message[i] = (uint8_t)(git_rand() % 64 + ' ');
 
        reftable_writer_set_limits(w, 1, 1);
 
@@ -550,8 +546,6 @@ static void test_table_refs_for(int indexed)
                uint8_t hash[GIT_SHA1_RAWSZ];
                char fill[51] = { 0 };
                char name[100];
-               uint8_t hash1[GIT_SHA1_RAWSZ];
-               uint8_t hash2[GIT_SHA1_RAWSZ];
                struct reftable_ref_record ref = { NULL };
 
                memset(hash, i, sizeof(hash));
@@ -561,11 +555,9 @@ static void test_table_refs_for(int indexed)
                name[40] = 0;
                ref.refname = name;
 
-               set_test_hash(hash1, i / 4);
-               set_test_hash(hash2, 3 + i / 4);
                ref.value_type = REFTABLE_REF_VAL2;
-               ref.value.val2.value = hash1;
-               ref.value.val2.target_value = hash2;
+               set_test_hash(ref.value.val2.value, i / 4);
+               set_test_hash(ref.value.val2.target_value, 3 + i / 4);
 
                /* 80 bytes / entry, so 3 entries per block. Yields 17
                 */
@@ -573,8 +565,8 @@ static void test_table_refs_for(int indexed)
                n = reftable_writer_add_ref(w, &ref);
                EXPECT(n == 0);
 
-               if (!memcmp(hash1, want_hash, GIT_SHA1_RAWSZ) ||
-                   !memcmp(hash2, want_hash, GIT_SHA1_RAWSZ)) {
+               if (!memcmp(ref.value.val2.value, want_hash, GIT_SHA1_RAWSZ) ||
+                   !memcmp(ref.value.val2.target_value, want_hash, GIT_SHA1_RAWSZ)) {
                        want_names[want_names_len++] = xstrdup(name);
                }
        }
@@ -675,11 +667,10 @@ static void test_write_object_id_min_length(void)
        struct strbuf buf = STRBUF_INIT;
        struct reftable_writer *w =
                reftable_new_writer(&strbuf_add_void, &buf, &opts);
-       uint8_t hash[GIT_SHA1_RAWSZ] = {42};
        struct reftable_ref_record ref = {
                .update_index = 1,
                .value_type = REFTABLE_REF_VAL1,
-               .value.val1 = hash,
+               .value.val1 = {42},
        };
        int err;
        int i;
@@ -711,11 +702,10 @@ static void test_write_object_id_length(void)
        struct strbuf buf = STRBUF_INIT;
        struct reftable_writer *w =
                reftable_new_writer(&strbuf_add_void, &buf, &opts);
-       uint8_t hash[GIT_SHA1_RAWSZ] = {42};
        struct reftable_ref_record ref = {
                .update_index = 1,
                .value_type = REFTABLE_REF_VAL1,
-               .value.val1 = hash,
+               .value.val1 = {42},
        };
        int err;
        int i;
@@ -798,6 +788,84 @@ static void test_write_key_order(void)
        strbuf_release(&buf);
 }
 
+static void test_write_multiple_indices(void)
+{
+       struct reftable_write_options opts = {
+               .block_size = 100,
+       };
+       struct strbuf writer_buf = STRBUF_INIT, buf = STRBUF_INIT;
+       struct reftable_block_source source = { 0 };
+       struct reftable_iterator it = { 0 };
+       const struct reftable_stats *stats;
+       struct reftable_writer *writer;
+       struct reftable_reader *reader;
+       int err, i;
+
+       writer = reftable_new_writer(&strbuf_add_void, &writer_buf, &opts);
+       reftable_writer_set_limits(writer, 1, 1);
+       for (i = 0; i < 100; i++) {
+               struct reftable_ref_record ref = {
+                       .update_index = 1,
+                       .value_type = REFTABLE_REF_VAL1,
+                       .value.val1 = {i},
+               };
+
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "refs/heads/%04d", i);
+               ref.refname = buf.buf,
+
+               err = reftable_writer_add_ref(writer, &ref);
+               EXPECT_ERR(err);
+       }
+
+       for (i = 0; i < 100; i++) {
+               unsigned char hash[GIT_SHA1_RAWSZ] = {i};
+               struct reftable_log_record log = {
+                       .update_index = 1,
+                       .value_type = REFTABLE_LOG_UPDATE,
+                       .value.update = {
+                               .old_hash = hash,
+                               .new_hash = hash,
+                       },
+               };
+
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "refs/heads/%04d", i);
+               log.refname = buf.buf,
+
+               err = reftable_writer_add_log(writer, &log);
+               EXPECT_ERR(err);
+       }
+
+       reftable_writer_close(writer);
+
+       /*
+        * The written data should be sufficiently large to result in indices
+        * for each of the block types.
+        */
+       stats = reftable_writer_stats(writer);
+       EXPECT(stats->ref_stats.index_offset > 0);
+       EXPECT(stats->obj_stats.index_offset > 0);
+       EXPECT(stats->log_stats.index_offset > 0);
+
+       block_source_from_strbuf(&source, &writer_buf);
+       err = reftable_new_reader(&reader, &source, "filename");
+       EXPECT_ERR(err);
+
+       /*
+        * Seeking the log uses the log index now. In case there is any
+        * confusion regarding indices we would notice here.
+        */
+       err = reftable_reader_seek_log(reader, &it, "");
+       EXPECT_ERR(err);
+
+       reftable_iterator_destroy(&it);
+       reftable_writer_free(writer);
+       reftable_reader_free(reader);
+       strbuf_release(&writer_buf);
+       strbuf_release(&buf);
+}
+
 static void test_corrupt_table_empty(void)
 {
        struct strbuf buf = STRBUF_INIT;
@@ -847,5 +915,6 @@ int readwrite_test_main(int argc, const char *argv[])
        RUN_TEST(test_log_overflow);
        RUN_TEST(test_write_object_id_length);
        RUN_TEST(test_write_object_id_min_length);
+       RUN_TEST(test_write_multiple_indices);
        return 0;
 }
index fbaa1fbef56c52fed8391c7bcef2efd035eb588b..5c3fbb7b2a1e95ad418066df0aa96235116f5329 100644 (file)
@@ -76,7 +76,7 @@ int reftable_is_block_type(uint8_t typ)
        return 0;
 }
 
-uint8_t *reftable_ref_record_val1(const struct reftable_ref_record *rec)
+const unsigned char *reftable_ref_record_val1(const struct reftable_ref_record *rec)
 {
        switch (rec->value_type) {
        case REFTABLE_REF_VAL1:
@@ -88,7 +88,7 @@ uint8_t *reftable_ref_record_val1(const struct reftable_ref_record *rec)
        }
 }
 
-uint8_t *reftable_ref_record_val2(const struct reftable_ref_record *rec)
+const unsigned char *reftable_ref_record_val2(const struct reftable_ref_record *rec)
 {
        switch (rec->value_type) {
        case REFTABLE_REF_VAL2:
@@ -219,13 +219,10 @@ static void reftable_ref_record_copy_from(void *rec, const void *src_rec,
        case REFTABLE_REF_DELETION:
                break;
        case REFTABLE_REF_VAL1:
-               ref->value.val1 = reftable_malloc(hash_size);
                memcpy(ref->value.val1, src->value.val1, hash_size);
                break;
        case REFTABLE_REF_VAL2:
-               ref->value.val2.value = reftable_malloc(hash_size);
                memcpy(ref->value.val2.value, src->value.val2.value, hash_size);
-               ref->value.val2.target_value = reftable_malloc(hash_size);
                memcpy(ref->value.val2.target_value,
                       src->value.val2.target_value, hash_size);
                break;
@@ -242,7 +239,7 @@ static char hexdigit(int c)
        return 'a' + (c - 10);
 }
 
-static void hex_format(char *dest, uint8_t *src, int hash_size)
+static void hex_format(char *dest, const unsigned char *src, int hash_size)
 {
        assert(hash_size > 0);
        if (src) {
@@ -299,11 +296,8 @@ void reftable_ref_record_release(struct reftable_ref_record *ref)
                reftable_free(ref->value.symref);
                break;
        case REFTABLE_REF_VAL2:
-               reftable_free(ref->value.val2.target_value);
-               reftable_free(ref->value.val2.value);
                break;
        case REFTABLE_REF_VAL1:
-               reftable_free(ref->value.val1);
                break;
        case REFTABLE_REF_DELETION:
                break;
@@ -394,7 +388,6 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key,
                        return -1;
                }
 
-               r->value.val1 = reftable_malloc(hash_size);
                memcpy(r->value.val1, in.buf, hash_size);
                string_view_consume(&in, hash_size);
                break;
@@ -404,11 +397,9 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key,
                        return -1;
                }
 
-               r->value.val2.value = reftable_malloc(hash_size);
                memcpy(r->value.val2.value, in.buf, hash_size);
                string_view_consume(&in, hash_size);
 
-               r->value.val2.target_value = reftable_malloc(hash_size);
                memcpy(r->value.val2.target_value, in.buf, hash_size);
                string_view_consume(&in, hash_size);
                break;
@@ -1164,7 +1155,7 @@ int reftable_record_equal(struct reftable_record *a, struct reftable_record *b,
                reftable_record_data(a), reftable_record_data(b), hash_size);
 }
 
-static int hash_equal(uint8_t *a, uint8_t *b, int hash_size)
+static int hash_equal(const unsigned char *a, const unsigned char *b, int hash_size)
 {
        if (a && b)
                return !memcmp(a, b, hash_size);
index 70ae78feca4f11cdb50f4fdcb08d90118bf487df..2876db7d2708aa36d861181775de74c966862c2d 100644 (file)
@@ -119,15 +119,10 @@ static void test_reftable_ref_record_roundtrip(void)
                case REFTABLE_REF_DELETION:
                        break;
                case REFTABLE_REF_VAL1:
-                       in.u.ref.value.val1 = reftable_malloc(GIT_SHA1_RAWSZ);
                        set_hash(in.u.ref.value.val1, 1);
                        break;
                case REFTABLE_REF_VAL2:
-                       in.u.ref.value.val2.value =
-                               reftable_malloc(GIT_SHA1_RAWSZ);
                        set_hash(in.u.ref.value.val2.value, 1);
-                       in.u.ref.value.val2.target_value =
-                               reftable_malloc(GIT_SHA1_RAWSZ);
                        set_hash(in.u.ref.value.val2.target_value, 2);
                        break;
                case REFTABLE_REF_SYMREF:
index 8645cd93bbd8f9b51e05eb9143ed99217bfa1035..699e1aea4122d66a8a00451521f7d902858474c2 100644 (file)
@@ -9,7 +9,6 @@ https://developers.google.com/open-source/licenses/bsd
 #include "basics.h"
 #include "block.h"
 #include "blocksource.h"
-#include "constants.h"
 #include "reader.h"
 #include "record.h"
 #include "refname.h"
index 67104f8fbfecd7984127d29dd1ae3fa9bb263394..bb6e99acd3151ec75a7a2dc60e930064b7322e60 100644 (file)
@@ -9,6 +9,7 @@ https://developers.google.com/open-source/licenses/bsd
 #ifndef REFTABLE_RECORD_H
 #define REFTABLE_RECORD_H
 
+#include "hash-ll.h"
 #include <stdint.h>
 
 /*
@@ -38,10 +39,10 @@ struct reftable_ref_record {
 #define REFTABLE_NR_REF_VALUETYPES 4
        } value_type;
        union {
-               uint8_t *val1; /* malloced hash. */
+               unsigned char val1[GIT_MAX_RAWSZ];
                struct {
-                       uint8_t *value; /* first value, malloced hash  */
-                       uint8_t *target_value; /* second value, malloced hash */
+                       unsigned char value[GIT_MAX_RAWSZ]; /* first hash  */
+                       unsigned char target_value[GIT_MAX_RAWSZ]; /* second hash */
                } val2;
                char *symref; /* referent, malloced 0-terminated string */
        } value;
@@ -49,11 +50,11 @@ struct reftable_ref_record {
 
 /* Returns the first hash, or NULL if `rec` is not of type
  * REFTABLE_REF_VAL1 or REFTABLE_REF_VAL2. */
-uint8_t *reftable_ref_record_val1(const struct reftable_ref_record *rec);
+const unsigned char *reftable_ref_record_val1(const struct reftable_ref_record *rec);
 
 /* Returns the second hash, or NULL if `rec` is not of type
  * REFTABLE_REF_VAL2. */
-uint8_t *reftable_ref_record_val2(const struct reftable_ref_record *rec);
+const unsigned char *reftable_ref_record_val2(const struct reftable_ref_record *rec);
 
 /* returns whether 'ref' represents a deletion */
 int reftable_ref_record_is_deletion(const struct reftable_ref_record *ref);
index ddbdf1b9c8bf4668dfe4f9b7c03cabd43effb660..bf3869ce70dc7af837d29aabb985ee71e3e0c757 100644 (file)
@@ -17,6 +17,8 @@ https://developers.google.com/open-source/licenses/bsd
 #include "reftable-merged.h"
 #include "writer.h"
 
+#include "tempfile.h"
+
 static int stack_try_add(struct reftable_stack *st,
                         int (*write_table)(struct reftable_writer *wr,
                                            void *arg),
@@ -42,7 +44,7 @@ static void stack_filename(struct strbuf *dest, struct reftable_stack *st,
 static ssize_t reftable_fd_write(void *arg, const void *data, size_t sz)
 {
        int *fdp = (int *)arg;
-       return write(*fdp, data, sz);
+       return write_in_full(*fdp, data, sz);
 }
 
 int reftable_new_stack(struct reftable_stack **dest, const char *dir,
@@ -64,6 +66,7 @@ int reftable_new_stack(struct reftable_stack **dest, const char *dir,
        strbuf_addstr(&list_file_name, "/tables.list");
 
        p->list_file = strbuf_detach(&list_file_name, NULL);
+       p->list_fd = -1;
        p->reftable_dir = xstrdup(dir);
        p->config = config;
 
@@ -92,7 +95,7 @@ static int fd_read_lines(int fd, char ***namesp)
        }
 
        buf = reftable_malloc(size + 1);
-       if (read(fd, buf, size) != size) {
+       if (read_in_full(fd, buf, size) != size) {
                err = REFTABLE_IO_ERROR;
                goto done;
        }
@@ -173,6 +176,12 @@ void reftable_stack_destroy(struct reftable_stack *st)
                st->readers_len = 0;
                FREE_AND_NULL(st->readers);
        }
+
+       if (st->list_fd >= 0) {
+               close(st->list_fd);
+               st->list_fd = -1;
+       }
+
        FREE_AND_NULL(st->list_file);
        FREE_AND_NULL(st->reftable_dir);
        reftable_free(st);
@@ -204,6 +213,7 @@ static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
                reftable_calloc(sizeof(struct reftable_table) * names_len);
        int new_readers_len = 0;
        struct reftable_merged_table *new_merged = NULL;
+       struct strbuf table_path = STRBUF_INIT;
        int i;
 
        while (*names) {
@@ -223,13 +233,10 @@ static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
 
                if (!rd) {
                        struct reftable_block_source src = { NULL };
-                       struct strbuf table_path = STRBUF_INIT;
                        stack_filename(&table_path, st, name);
 
                        err = reftable_block_source_from_file(&src,
                                                              table_path.buf);
-                       strbuf_release(&table_path);
-
                        if (err < 0)
                                goto done;
 
@@ -267,16 +274,13 @@ static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
        for (i = 0; i < cur_len; i++) {
                if (cur[i]) {
                        const char *name = reader_name(cur[i]);
-                       struct strbuf filename = STRBUF_INIT;
-                       stack_filename(&filename, st, name);
+                       stack_filename(&table_path, st, name);
 
                        reader_close(cur[i]);
                        reftable_reader_free(cur[i]);
 
                        /* On Windows, can only unlink after closing. */
-                       unlink(filename.buf);
-
-                       strbuf_release(&filename);
+                       unlink(table_path.buf);
                }
        }
 
@@ -288,6 +292,7 @@ done:
        reftable_free(new_readers);
        reftable_free(new_tables);
        reftable_free(cur);
+       strbuf_release(&table_path);
        return err;
 }
 
@@ -306,69 +311,134 @@ static int tv_cmp(struct timeval *a, struct timeval *b)
 static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
                                             int reuse_open)
 {
-       struct timeval deadline = { 0 };
-       int err = gettimeofday(&deadline, NULL);
+       char **names = NULL, **names_after = NULL;
+       struct timeval deadline;
        int64_t delay = 0;
-       int tries = 0;
-       if (err < 0)
-               return err;
+       int tries = 0, err;
+       int fd = -1;
 
+       err = gettimeofday(&deadline, NULL);
+       if (err < 0)
+               goto out;
        deadline.tv_sec += 3;
+
        while (1) {
-               char **names = NULL;
-               char **names_after = NULL;
-               struct timeval now = { 0 };
-               int err = gettimeofday(&now, NULL);
-               int err2 = 0;
-               if (err < 0) {
-                       return err;
-               }
+               struct timeval now;
 
-               /* Only look at deadlines after the first few times. This
-                  simplifies debugging in GDB */
+               err = gettimeofday(&now, NULL);
+               if (err < 0)
+                       goto out;
+
+               /*
+                * Only look at deadlines after the first few times. This
+                * simplifies debugging in GDB.
+                */
                tries++;
-               if (tries > 3 && tv_cmp(&now, &deadline) >= 0) {
-                       break;
-               }
+               if (tries > 3 && tv_cmp(&now, &deadline) >= 0)
+                       goto out;
 
-               err = read_lines(st->list_file, &names);
-               if (err < 0) {
-                       free_names(names);
-                       return err;
-               }
-               err = reftable_stack_reload_once(st, names, reuse_open);
-               if (err == 0) {
-                       free_names(names);
-                       break;
-               }
-               if (err != REFTABLE_NOT_EXIST_ERROR) {
-                       free_names(names);
-                       return err;
-               }
+               fd = open(st->list_file, O_RDONLY);
+               if (fd < 0) {
+                       if (errno != ENOENT) {
+                               err = REFTABLE_IO_ERROR;
+                               goto out;
+                       }
 
-               /* err == REFTABLE_NOT_EXIST_ERROR can be caused by a concurrent
-                  writer. Check if there was one by checking if the name list
-                  changed.
-               */
-               err2 = read_lines(st->list_file, &names_after);
-               if (err2 < 0) {
-                       free_names(names);
-                       return err2;
+                       names = reftable_calloc(sizeof(char *));
+               } else {
+                       err = fd_read_lines(fd, &names);
+                       if (err < 0)
+                               goto out;
                }
 
+               err = reftable_stack_reload_once(st, names, reuse_open);
+               if (!err)
+                       break;
+               if (err != REFTABLE_NOT_EXIST_ERROR)
+                       goto out;
+
+               /*
+                * REFTABLE_NOT_EXIST_ERROR can be caused by a concurrent
+                * writer. Check if there was one by checking if the name list
+                * changed.
+                */
+               err = read_lines(st->list_file, &names_after);
+               if (err < 0)
+                       goto out;
                if (names_equal(names_after, names)) {
-                       free_names(names);
-                       free_names(names_after);
-                       return err;
+                       err = REFTABLE_NOT_EXIST_ERROR;
+                       goto out;
                }
+
                free_names(names);
+               names = NULL;
                free_names(names_after);
+               names_after = NULL;
+               close(fd);
+               fd = -1;
 
                delay = delay + (delay * rand()) / RAND_MAX + 1;
                sleep_millisec(delay);
        }
 
-       return 0;
+out:
+       /*
+        * Invalidate the stat cache. It is sufficient to only close the file
+        * descriptor and keep the cached stat info because we never use the
+        * latter when the former is negative.
+        */
+       if (st->list_fd >= 0) {
+               close(st->list_fd);
+               st->list_fd = -1;
+       }
+
+       /*
+        * Cache stat information in case it provides a useful signal to us.
+        * According to POSIX, "The st_ino and st_dev fields taken together
+        * uniquely identify the file within the system." That being said,
+        * Windows is not POSIX compliant and we do not have these fields
+        * available. So the information we have there is insufficient to
+        * determine whether two file descriptors point to the same file.
+        *
+        * While we could fall back to using other signals like the file's
+        * mtime, those are not sufficient to avoid races. We thus refrain from
+        * using the stat cache on such systems and fall back to the secondary
+        * caching mechanism, which is to check whether contents of the file
+        * have changed.
+        *
+        * On other systems which are POSIX compliant we must keep the file
+        * descriptor open. This is to avoid a race condition where two
+        * processes access the reftable stack at the same point in time:
+        *
+        *   1. A reads the reftable stack and caches its stat info.
+        *
+        *   2. B updates the stack, appending a new table to "tables.list".
+        *      This will both use a new inode and result in a different file
+        *      size, thus invalidating A's cache in theory.
+        *
+        *   3. B decides to auto-compact the stack and merges two tables. The
+        *      file size now matches what A has cached again. Furthermore, the
+        *      filesystem may decide to recycle the inode number of the file
+        *      we have replaced in (2) because it is not in use anymore.
+        *
+        *   4. A reloads the reftable stack. Neither the inode number nor the
+        *      file size changed. If the timestamps did not change either then
+        *      we think the cached copy of our stack is up-to-date.
+        *
+        * By keeping the file descriptor open the inode number cannot be
+        * recycled, mitigating the race.
+        */
+       if (!err && fd >= 0 && !fstat(fd, &st->list_st) &&
+           st->list_st.st_dev && st->list_st.st_ino) {
+               st->list_fd = fd;
+               fd = -1;
+       }
+
+       if (fd >= 0)
+               close(fd);
+       free_names(names);
+       free_names(names_after);
+       return err;
 }
 
 /* -1 = error
@@ -377,8 +447,44 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
 static int stack_uptodate(struct reftable_stack *st)
 {
        char **names = NULL;
-       int err = read_lines(st->list_file, &names);
+       int err;
        int i = 0;
+
+       /*
+        * When we have cached stat information available then we use it to
+        * verify whether the file has been rewritten.
+        *
+        * Note that we explicitly do not want to use `stat_validity_check()`
+        * and friends here because they may end up not comparing the `st_dev`
+        * and `st_ino` fields. These functions thus cannot guarantee that we
+        * indeed still have the same file.
+        */
+       if (st->list_fd >= 0) {
+               struct stat list_st;
+
+               if (stat(st->list_file, &list_st) < 0) {
+                       /*
+                        * It's fine for "tables.list" to not exist. In that
+                        * case, we have to refresh when the loaded stack has
+                        * any readers.
+                        */
+                       if (errno == ENOENT)
+                               return !!st->readers_len;
+                       return REFTABLE_IO_ERROR;
+               }
+
+               /*
+                * When "tables.list" refers to the same file we can assume
+                * that it didn't change. This is because we always use
+                * rename(3P) to update the file and never write to it
+                * directly.
+                */
+               if (st->list_st.st_dev == list_st.st_dev &&
+                   st->list_st.st_ino == list_st.st_ino)
+                       return 0;
+       }
+
+       err = read_lines(st->list_file, &names);
        if (err < 0)
                return err;
 
@@ -427,16 +533,13 @@ int reftable_stack_add(struct reftable_stack *st,
                return err;
        }
 
-       if (!st->disable_auto_compact)
-               return reftable_stack_auto_compact(st);
-
        return 0;
 }
 
 static void format_name(struct strbuf *dest, uint64_t min, uint64_t max)
 {
        char buf[100];
-       uint32_t rnd = (uint32_t)rand();
+       uint32_t rnd = (uint32_t)git_rand();
        snprintf(buf, sizeof(buf), "0x%012" PRIx64 "-0x%012" PRIx64 "-%08x",
                 min, max, rnd);
        strbuf_reset(dest);
@@ -444,8 +547,7 @@ static void format_name(struct strbuf *dest, uint64_t min, uint64_t max)
 }
 
 struct reftable_addition {
-       int lock_file_fd;
-       struct strbuf lock_file_name;
+       struct tempfile *lock_file;
        struct reftable_stack *stack;
 
        char **new_tables;
@@ -453,24 +555,19 @@ struct reftable_addition {
        uint64_t next_update_index;
 };
 
-#define REFTABLE_ADDITION_INIT                \
-       {                                     \
-               .lock_file_name = STRBUF_INIT \
-       }
+#define REFTABLE_ADDITION_INIT {0}
 
 static int reftable_stack_init_addition(struct reftable_addition *add,
                                        struct reftable_stack *st)
 {
+       struct strbuf lock_file_name = STRBUF_INIT;
        int err = 0;
        add->stack = st;
 
-       strbuf_reset(&add->lock_file_name);
-       strbuf_addstr(&add->lock_file_name, st->list_file);
-       strbuf_addstr(&add->lock_file_name, ".lock");
+       strbuf_addf(&lock_file_name, "%s.lock", st->list_file);
 
-       add->lock_file_fd = open(add->lock_file_name.buf,
-                                O_EXCL | O_CREAT | O_WRONLY, 0666);
-       if (add->lock_file_fd < 0) {
+       add->lock_file = create_tempfile(lock_file_name.buf);
+       if (!add->lock_file) {
                if (errno == EEXIST) {
                        err = REFTABLE_LOCK_ERROR;
                } else {
@@ -479,7 +576,7 @@ static int reftable_stack_init_addition(struct reftable_addition *add,
                goto done;
        }
        if (st->config.default_permissions) {
-               if (chmod(add->lock_file_name.buf, st->config.default_permissions) < 0) {
+               if (chmod(add->lock_file->filename.buf, st->config.default_permissions) < 0) {
                        err = REFTABLE_IO_ERROR;
                        goto done;
                }
@@ -499,6 +596,7 @@ done:
        if (err) {
                reftable_addition_close(add);
        }
+       strbuf_release(&lock_file_name);
        return err;
 }
 
@@ -516,15 +614,7 @@ static void reftable_addition_close(struct reftable_addition *add)
        add->new_tables = NULL;
        add->new_tables_len = 0;
 
-       if (add->lock_file_fd > 0) {
-               close(add->lock_file_fd);
-               add->lock_file_fd = 0;
-       }
-       if (add->lock_file_name.len > 0) {
-               unlink(add->lock_file_name.buf);
-               strbuf_release(&add->lock_file_name);
-       }
-
+       delete_tempfile(&add->lock_file);
        strbuf_release(&nm);
 }
 
@@ -540,8 +630,10 @@ void reftable_addition_destroy(struct reftable_addition *add)
 int reftable_addition_commit(struct reftable_addition *add)
 {
        struct strbuf table_list = STRBUF_INIT;
+       int lock_file_fd = get_tempfile_fd(add->lock_file);
        int i = 0;
        int err = 0;
+
        if (add->new_tables_len == 0)
                goto done;
 
@@ -554,28 +646,20 @@ int reftable_addition_commit(struct reftable_addition *add)
                strbuf_addstr(&table_list, "\n");
        }
 
-       err = write(add->lock_file_fd, table_list.buf, table_list.len);
+       err = write_in_full(lock_file_fd, table_list.buf, table_list.len);
        strbuf_release(&table_list);
        if (err < 0) {
                err = REFTABLE_IO_ERROR;
                goto done;
        }
 
-       err = close(add->lock_file_fd);
-       add->lock_file_fd = 0;
-       if (err < 0) {
-               err = REFTABLE_IO_ERROR;
-               goto done;
-       }
-
-       err = rename(add->lock_file_name.buf, add->stack->list_file);
+       err = rename_tempfile(&add->lock_file, add->stack->list_file);
        if (err < 0) {
                err = REFTABLE_IO_ERROR;
                goto done;
        }
 
        /* success, no more state to clean up. */
-       strbuf_release(&add->lock_file_name);
        for (i = 0; i < add->new_tables_len; i++) {
                reftable_free(add->new_tables[i]);
        }
@@ -583,7 +667,13 @@ int reftable_addition_commit(struct reftable_addition *add)
        add->new_tables = NULL;
        add->new_tables_len = 0;
 
-       err = reftable_stack_reload(add->stack);
+       err = reftable_stack_reload_maybe_reuse(add->stack, 1);
+       if (err)
+               goto done;
+
+       if (!add->stack->disable_auto_compact)
+               err = reftable_stack_auto_compact(add->stack);
+
 done:
        reftable_addition_close(add);
        return err;
@@ -816,18 +906,16 @@ static int stack_write_compact(struct reftable_stack *st,
                        err = 0;
                        break;
                }
-               if (err < 0) {
-                       break;
-               }
+               if (err < 0)
+                       goto done;
 
                if (first == 0 && reftable_ref_record_is_deletion(&ref)) {
                        continue;
                }
 
                err = reftable_writer_add_ref(wr, &ref);
-               if (err < 0) {
-                       break;
-               }
+               if (err < 0)
+                       goto done;
                entries++;
        }
        reftable_iterator_destroy(&it);
@@ -842,9 +930,8 @@ static int stack_write_compact(struct reftable_stack *st,
                        err = 0;
                        break;
                }
-               if (err < 0) {
-                       break;
-               }
+               if (err < 0)
+                       goto done;
                if (first == 0 && reftable_log_record_is_deletion(&log)) {
                        continue;
                }
@@ -860,9 +947,8 @@ static int stack_write_compact(struct reftable_stack *st,
                }
 
                err = reftable_writer_add_log(wr, &log);
-               if (err < 0) {
-                       break;
-               }
+               if (err < 0)
+                       goto done;
                entries++;
        }
 
@@ -1024,7 +1110,7 @@ static int stack_compact_range(struct reftable_stack *st, int first, int last,
                strbuf_addstr(&ref_list_contents, "\n");
        }
 
-       err = write(lock_file_fd, ref_list_contents.buf, ref_list_contents.len);
+       err = write_in_full(lock_file_fd, ref_list_contents.buf, ref_list_contents.len);
        if (err < 0) {
                err = REFTABLE_IO_ERROR;
                unlink(new_table_path.buf);
index f57005846e561272054883e856d7a1dd14863a19..c1e3efa89960b124c8560662d5dd97257964a908 100644 (file)
@@ -14,7 +14,10 @@ https://developers.google.com/open-source/licenses/bsd
 #include "reftable-stack.h"
 
 struct reftable_stack {
+       struct stat list_st;
        char *list_file;
+       int list_fd;
+
        char *reftable_dir;
        int disable_auto_compact;
 
index d0b717510fa7d6dd8832bf77490630964e33b23b..289e902146470035e4dd0109af6b8436e97d40d4 100644 (file)
@@ -13,7 +13,6 @@ https://developers.google.com/open-source/licenses/bsd
 #include "reftable-reader.h"
 #include "merged.h"
 #include "basics.h"
-#include "constants.h"
 #include "record.h"
 #include "test_framework.h"
 #include "reftable-tests.h"
@@ -78,7 +77,7 @@ static void test_read_file(void)
        int i = 0;
 
        EXPECT(fd > 0);
-       n = write(fd, out, strlen(out));
+       n = write_in_full(fd, out, strlen(out));
        EXPECT(n == strlen(out));
        err = close(fd);
        EXPECT(err >= 0);
@@ -289,6 +288,61 @@ static void test_reftable_stack_transaction_api(void)
        clear_dir(dir);
 }
 
+static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
+{
+       char *dir = get_tmp_dir(__LINE__);
+       struct reftable_write_options cfg = {0};
+       struct reftable_addition *add = NULL;
+       struct reftable_stack *st = NULL;
+       int i, n = 20, err;
+
+       err = reftable_new_stack(&st, dir, cfg);
+       EXPECT_ERR(err);
+
+       for (i = 0; i <= n; i++) {
+               struct reftable_ref_record ref = {
+                       .update_index = reftable_stack_next_update_index(st),
+                       .value_type = REFTABLE_REF_SYMREF,
+                       .value.symref = "master",
+               };
+               char name[100];
+
+               snprintf(name, sizeof(name), "branch%04d", i);
+               ref.refname = name;
+
+               /*
+                * Disable auto-compaction for all but the last runs. Like this
+                * we can ensure that we indeed honor this setting and have
+                * better control over when exactly auto compaction runs.
+                */
+               st->disable_auto_compact = i != n;
+
+               err = reftable_stack_new_addition(&add, st);
+               EXPECT_ERR(err);
+
+               err = reftable_addition_add(add, &write_test_ref, &ref);
+               EXPECT_ERR(err);
+
+               err = reftable_addition_commit(add);
+               EXPECT_ERR(err);
+
+               reftable_addition_destroy(add);
+
+               /*
+                * The stack length should grow continuously for all runs where
+                * auto compaction is disabled. When enabled, we should merge
+                * all tables in the stack.
+                */
+               if (i != n)
+                       EXPECT(st->merged->stack_len == i + 1);
+               else
+                       EXPECT(st->merged->stack_len == 1);
+       }
+
+       reftable_stack_destroy(st);
+       clear_dir(dir);
+}
+
 static void test_reftable_stack_validate_refname(void)
 {
        struct reftable_write_options cfg = { 0 };
@@ -408,7 +462,6 @@ static void test_reftable_stack_add(void)
                refs[i].refname = xstrdup(buf);
                refs[i].update_index = i + 1;
                refs[i].value_type = REFTABLE_REF_VAL1;
-               refs[i].value.val1 = reftable_malloc(GIT_SHA1_RAWSZ);
                set_test_hash(refs[i].value.val1, i);
 
                logs[i].refname = xstrdup(buf);
@@ -545,7 +598,6 @@ static void test_reftable_stack_tombstone(void)
                refs[i].update_index = i + 1;
                if (i % 2 == 0) {
                        refs[i].value_type = REFTABLE_REF_VAL1;
-                       refs[i].value.val1 = reftable_malloc(GIT_SHA1_RAWSZ);
                        set_test_hash(refs[i].value.val1, i);
                }
 
@@ -850,6 +902,54 @@ static void test_reftable_stack_auto_compaction(void)
        clear_dir(dir);
 }
 
+static void test_reftable_stack_add_performs_auto_compaction(void)
+{
+       struct reftable_write_options cfg = { 0 };
+       struct reftable_stack *st = NULL;
+       struct strbuf refname = STRBUF_INIT;
+       char *dir = get_tmp_dir(__LINE__);
+       int err, i, n = 20;
+
+       err = reftable_new_stack(&st, dir, cfg);
+       EXPECT_ERR(err);
+
+       for (i = 0; i <= n; i++) {
+               struct reftable_ref_record ref = {
+                       .update_index = reftable_stack_next_update_index(st),
+                       .value_type = REFTABLE_REF_SYMREF,
+                       .value.symref = "master",
+               };
+
+               /*
+                * Disable auto-compaction for all but the last runs. Like this
+                * we can ensure that we indeed honor this setting and have
+                * better control over when exactly auto compaction runs.
+                */
+               st->disable_auto_compact = i != n;
+
+               strbuf_reset(&refname);
+               strbuf_addf(&refname, "branch-%04d", i);
+               ref.refname = refname.buf;
+
+               err = reftable_stack_add(st, &write_test_ref, &ref);
+               EXPECT_ERR(err);
+
+               /*
+                * The stack length should grow continuously for all runs where
+                * auto compaction is disabled. When enabled, we should merge
+                * all tables in the stack.
+                */
+               if (i != n)
+                       EXPECT(st->merged->stack_len == i + 1);
+               else
+                       EXPECT(st->merged->stack_len == 1);
+       }
+
+       reftable_stack_destroy(st);
+       strbuf_release(&refname);
+       clear_dir(dir);
+}
+
 static void test_reftable_stack_compaction_concurrent(void)
 {
        struct reftable_write_options cfg = { 0 };
@@ -960,6 +1060,7 @@ int stack_test_main(int argc, const char *argv[])
        RUN_TEST(test_reftable_stack_add);
        RUN_TEST(test_reftable_stack_add_one);
        RUN_TEST(test_reftable_stack_auto_compaction);
+       RUN_TEST(test_reftable_stack_add_performs_auto_compaction);
        RUN_TEST(test_reftable_stack_compaction_concurrent);
        RUN_TEST(test_reftable_stack_compaction_concurrent_clean);
        RUN_TEST(test_reftable_stack_hash_id);
@@ -967,6 +1068,7 @@ int stack_test_main(int argc, const char *argv[])
        RUN_TEST(test_reftable_stack_log_normalize);
        RUN_TEST(test_reftable_stack_tombstone);
        RUN_TEST(test_reftable_stack_transaction_api);
+       RUN_TEST(test_reftable_stack_transaction_api_performs_auto_compaction);
        RUN_TEST(test_reftable_stack_update_index_check);
        RUN_TEST(test_reftable_stack_uptodate);
        RUN_TEST(test_reftable_stack_validate_refname);
index 84ac972cad063a3402731c704d9c7bfde4d24d6e..04044fc1a0fb5ffa143264527b882afb752fc2c6 100644 (file)
@@ -9,7 +9,6 @@ https://developers.google.com/open-source/licenses/bsd
 #include "system.h"
 #include "test_framework.h"
 
-#include "basics.h"
 
 void set_test_hash(uint8_t *p, int i)
 {
index 774cb275bf679440b804d86370c8858c9e0b1bd2..ee44f735aea51d03fc3400a9340ccbd7af881f9e 100644 (file)
@@ -12,32 +12,38 @@ https://developers.google.com/open-source/licenses/bsd
 #include "system.h"
 #include "reftable-error.h"
 
-#define EXPECT_ERR(c)                                                  \
-       if (c != 0) {                                                  \
-               fflush(stderr);                                        \
-               fflush(stdout);                                        \
-               fprintf(stderr, "%s: %d: error == %d (%s), want 0\n",  \
-                       __FILE__, __LINE__, c, reftable_error_str(c)); \
-               abort();                                               \
-       }
-
-#define EXPECT_STREQ(a, b)                                               \
-       if (strcmp(a, b)) {                                              \
-               fflush(stderr);                                          \
-               fflush(stdout);                                          \
-               fprintf(stderr, "%s:%d: %s (%s) != %s (%s)\n", __FILE__, \
-                       __LINE__, #a, a, #b, b);                         \
-               abort();                                                 \
-       }
-
-#define EXPECT(c)                                                          \
-       if (!(c)) {                                                        \
-               fflush(stderr);                                            \
-               fflush(stdout);                                            \
-               fprintf(stderr, "%s: %d: failed assertion %s\n", __FILE__, \
-                       __LINE__, #c);                                     \
-               abort();                                                   \
-       }
+#define EXPECT_ERR(c)                                                          \
+       do {                                                                   \
+               if (c != 0) {                                                  \
+                       fflush(stderr);                                        \
+                       fflush(stdout);                                        \
+                       fprintf(stderr, "%s: %d: error == %d (%s), want 0\n",  \
+                               __FILE__, __LINE__, c, reftable_error_str(c)); \
+                       abort();                                               \
+               }                                                              \
+       } while (0)
+
+#define EXPECT_STREQ(a, b)                                                       \
+       do {                                                                     \
+               if (strcmp(a, b)) {                                              \
+                       fflush(stderr);                                          \
+                       fflush(stdout);                                          \
+                       fprintf(stderr, "%s:%d: %s (%s) != %s (%s)\n", __FILE__, \
+                               __LINE__, #a, a, #b, b);                         \
+                       abort();                                                 \
+               }                                                                \
+       } while (0)
+
+#define EXPECT(c)                                                                  \
+       do {                                                                       \
+               if (!(c)) {                                                        \
+                       fflush(stderr);                                            \
+                       fflush(stdout);                                            \
+                       fprintf(stderr, "%s: %d: failed assertion %s\n", __FILE__, \
+                               __LINE__, #c);                                     \
+                       abort();                                                   \
+               }                                                                  \
+       } while (0)
 
 #define RUN_TEST(f)                          \
        fprintf(stderr, "running %s\n", #f); \
index ac3a045ad4afbc893f4daff8da98c768cabaf8fd..6961a657adb80497676895eedca696d142e5b57c 100644 (file)
@@ -9,8 +9,6 @@ https://developers.google.com/open-source/licenses/bsd
 #include "system.h"
 #include "tree.h"
 
-#include "basics.h"
-#include "record.h"
 #include "test_framework.h"
 #include "reftable-tests.h"
 
index 2e322a5683d081eea60fc85dfc4d792ce89b7a93..ee4590e20f84dd442fde4f7506e27e568f9ad509 100644 (file)
@@ -432,12 +432,12 @@ static int writer_finish_section(struct reftable_writer *w)
                reftable_free(idx);
        }
 
-       writer_clear_index(w);
-
        err = writer_flush_block(w);
        if (err < 0)
                return err;
 
+       writer_clear_index(w);
+
        bstats = writer_reftable_block_stats(w, typ);
        bstats->index_blocks = w->stats.idx_stats.blocks - before_blocks;
        bstats->index_offset = index_start;
index ef05752ca5738ea16d16d38c8ea9c65e2a9ea167..cb0182b582a5cb4d13718d4a47cfb07c078464a0 100644 (file)
@@ -8,11 +8,9 @@
 #include "strbuf.h"
 #include "walker.h"
 #include "http.h"
-#include "exec-cmd.h"
 #include "run-command.h"
 #include "pkt-line.h"
 #include "string-list.h"
-#include "sideband.h"
 #include "strvec.h"
 #include "credential.h"
 #include "oid-array.h"
@@ -22,6 +20,7 @@
 #include "quote.h"
 #include "trace2.h"
 #include "transport.h"
+#include "url.h"
 #include "write-or-die.h"
 
 static struct remote *remote;
@@ -1564,8 +1563,11 @@ int cmd_main(int argc, const char **argv)
                if (buf.len == 0)
                        break;
                if (starts_with(buf.buf, "fetch ")) {
-                       if (nongit)
-                               die(_("remote-curl: fetch attempted without a local repo"));
+                       if (nongit) {
+                               setup_git_directory_gently(&nongit);
+                               if (nongit)
+                                       die(_("remote-curl: fetch attempted without a local repo"));
+                       }
                        parse_fetch(&buf);
 
                } else if (!strcmp(buf.buf, "list") || starts_with(buf.buf, "list ")) {
index abb24822beb2a17187d4afa9474dd1c537f078ba..e07b316eac3f5242f59e8d586f8f8e52711be494 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -15,7 +15,6 @@
 #include "diff.h"
 #include "revision.h"
 #include "dir.h"
-#include "tag.h"
 #include "setup.h"
 #include "string-list.h"
 #include "strvec.h"
@@ -509,7 +508,7 @@ static void alias_all_urls(struct remote_state *remote_state)
        }
 }
 
-static void read_config(struct repository *repo)
+static void read_config(struct repository *repo, int early)
 {
        int flag;
 
@@ -518,7 +517,7 @@ static void read_config(struct repository *repo)
        repo->remote_state->initialized = 1;
 
        repo->remote_state->current_branch = NULL;
-       if (startup_info->have_repository) {
+       if (startup_info->have_repository && !early) {
                const char *head_ref = refs_resolve_ref_unsafe(
                        get_main_ref_store(repo), "HEAD", 0, NULL, &flag);
                if (head_ref && (flag & REF_ISSYMREF) &&
@@ -561,7 +560,7 @@ static const char *remotes_remote_for_branch(struct remote_state *remote_state,
 
 const char *remote_for_branch(struct branch *branch, int *explicit)
 {
-       read_config(the_repository);
+       read_config(the_repository, 0);
        die_on_missing_branch(the_repository, branch);
 
        return remotes_remote_for_branch(the_repository->remote_state, branch,
@@ -587,7 +586,7 @@ remotes_pushremote_for_branch(struct remote_state *remote_state,
 
 const char *pushremote_for_branch(struct branch *branch, int *explicit)
 {
-       read_config(the_repository);
+       read_config(the_repository, 0);
        die_on_missing_branch(the_repository, branch);
 
        return remotes_pushremote_for_branch(the_repository->remote_state,
@@ -599,7 +598,7 @@ static struct remote *remotes_remote_get(struct remote_state *remote_state,
 
 const char *remote_ref_for_branch(struct branch *branch, int for_push)
 {
-       read_config(the_repository);
+       read_config(the_repository, 0);
        die_on_missing_branch(the_repository, branch);
 
        if (branch) {
@@ -709,7 +708,13 @@ remotes_remote_get(struct remote_state *remote_state, const char *name)
 
 struct remote *remote_get(const char *name)
 {
-       read_config(the_repository);
+       read_config(the_repository, 0);
+       return remotes_remote_get(the_repository->remote_state, name);
+}
+
+struct remote *remote_get_early(const char *name)
+{
+       read_config(the_repository, 1);
        return remotes_remote_get(the_repository->remote_state, name);
 }
 
@@ -722,7 +727,7 @@ remotes_pushremote_get(struct remote_state *remote_state, const char *name)
 
 struct remote *pushremote_get(const char *name)
 {
-       read_config(the_repository);
+       read_config(the_repository, 0);
        return remotes_pushremote_get(the_repository->remote_state, name);
 }
 
@@ -738,7 +743,7 @@ int remote_is_configured(struct remote *remote, int in_repo)
 int for_each_remote(each_remote_fn fn, void *priv)
 {
        int i, result = 0;
-       read_config(the_repository);
+       read_config(the_repository, 0);
        for (i = 0; i < the_repository->remote_state->remotes_nr && !result;
             i++) {
                struct remote *remote =
@@ -1831,7 +1836,7 @@ struct branch *branch_get(const char *name)
 {
        struct branch *ret;
 
-       read_config(the_repository);
+       read_config(the_repository, 0);
        if (!name || !*name || !strcmp(name, "HEAD"))
                ret = the_repository->remote_state->current_branch;
        else
@@ -1973,7 +1978,7 @@ static const char *branch_get_push_1(struct remote_state *remote_state,
 
 const char *branch_get_push(struct branch *branch, struct strbuf *err)
 {
-       read_config(the_repository);
+       read_config(the_repository, 0);
        die_on_missing_branch(the_repository, branch);
 
        if (!branch)
index cdc8b1db42c4a836804009a036628aaa94d94b5a..dfd4837e602755806bcfc73a725c04394ef81c6c 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -118,6 +118,7 @@ struct remote {
  * and configuration.
  */
 struct remote *remote_get(const char *name);
+struct remote *remote_get_early(const char *name);
 
 struct remote *pushremote_get(const char *name);
 int remote_is_configured(struct remote *remote, int in_repo);
@@ -400,8 +401,6 @@ struct ref *get_stale_heads(struct refspec *rs, struct ref *fetch_map);
 /*
  * Compare-and-swap
  */
-#define CAS_OPT_NAME "force-with-lease"
-
 struct push_cas_option {
        unsigned use_tracking_for_rest:1;
        unsigned use_force_if_includes:1;
index 525f69c0c7785845cdf8991e56c8d231c2e6d239..30cd4787627b7cacbc2dbceddc0babee1b72cb3e 100644 (file)
@@ -2,7 +2,6 @@
 #include "config.h"
 #include "repository.h"
 #include "midx.h"
-#include "compat/fsmonitor/fsm-listen.h"
 
 static void repo_cfg_bool(struct repository *r, const char *key, int *dest,
                          int def)
index a7679ceeaa45ce7a20094f9a29a82987290ad354..7aacb51b65cca69ec6acd0c879dd0aa5b15978b3 100644 (file)
@@ -104,6 +104,11 @@ void repo_set_hash_algo(struct repository *repo, int hash_algo)
        repo->hash_algo = &hash_algos[hash_algo];
 }
 
+void repo_set_ref_storage_format(struct repository *repo, unsigned int format)
+{
+       repo->ref_storage_format = format;
+}
+
 /*
  * Attempt to resolve and set the provided 'gitdir' for repository 'repo'.
  * Return 0 upon success and a non-zero value upon failure.
@@ -184,6 +189,7 @@ int repo_init(struct repository *repo,
                goto error;
 
        repo_set_hash_algo(repo, format.hash_algo);
+       repo_set_ref_storage_format(repo, format.ref_storage_format);
        repo->repository_format_worktree_config = format.worktree_config;
 
        /* take ownership of format.partial_clone */
@@ -256,8 +262,6 @@ static void repo_clear_path_cache(struct repo_path_cache *cache)
        FREE_AND_NULL(cache->merge_rr);
        FREE_AND_NULL(cache->merge_mode);
        FREE_AND_NULL(cache->merge_head);
-       FREE_AND_NULL(cache->merge_autostash);
-       FREE_AND_NULL(cache->auto_merge);
        FREE_AND_NULL(cache->fetch_head);
        FREE_AND_NULL(cache->shallow);
 }
index 5f18486f6465c44b44abb2ded74bcdda14ba7bd9..7a250a6605cc8e5a3ae661584122bf1f90f16747 100644 (file)
@@ -24,6 +24,9 @@ enum fetch_negotiation_setting {
        FETCH_NEGOTIATION_NOOP,
 };
 
+#define REF_STORAGE_FORMAT_UNKNOWN 0
+#define REF_STORAGE_FORMAT_FILES   1
+
 struct repo_settings {
        int initialized;
 
@@ -64,8 +67,6 @@ struct repo_path_cache {
        char *merge_rr;
        char *merge_mode;
        char *merge_head;
-       char *merge_autostash;
-       char *auto_merge;
        char *fetch_head;
        char *shallow;
 };
@@ -160,6 +161,9 @@ struct repository {
        /* Repository's current hash algorithm, as serialized on disk. */
        const struct git_hash_algo *hash_algo;
 
+       /* Repository's reference storage format, as serialized on disk. */
+       unsigned int ref_storage_format;
+
        /* A unique-id for tracing purposes. */
        int trace2_repo_id;
 
@@ -199,6 +203,7 @@ void repo_set_gitdir(struct repository *repo, const char *root,
                     const struct set_gitdir_args *extra_args);
 void repo_set_worktree(struct repository *repo, const char *path);
 void repo_set_hash_algo(struct repository *repo, int algo);
+void repo_set_ref_storage_format(struct repository *repo, unsigned int format);
 void initialize_the_repository(void);
 RESULT_MUST_BE_USED
 int repo_init(struct repository *r, const char *gitdir, const char *worktree);
index 09e19412859b3edb7be4142947d3967fc76520cc..ca7e77ba68c1ad4aa49f032906ba419098be0d04 100644 (file)
--- a/rerere.c
+++ b/rerere.c
 #include "dir.h"
 #include "resolve-undo.h"
 #include "merge-ll.h"
-#include "attr.h"
 #include "path.h"
 #include "pathspec.h"
 #include "object-file.h"
 #include "object-store-ll.h"
-#include "hash-lookup.h"
 #include "strmap.h"
 
 #define RESOLVED 0
diff --git a/reset.c b/reset.c
index 48da0adf851e1b09edfc22ffbbc0b1128cbbc5ab..0f2ff0fe31531fc5602025cfcd866a9348cfcf63 100644 (file)
--- a/reset.c
+++ b/reset.c
@@ -6,7 +6,6 @@
 #include "object-name.h"
 #include "refs.h"
 #include "reset.h"
-#include "run-command.h"
 #include "tree-walk.h"
 #include "tree.h"
 #include "unpack-trees.h"
index 00d5c29bfce18a1d4a5f5d701a593596ccd65783..2424c9bd674e534909df89e25c21b5eb119fda05 100644 (file)
 #include "reflog-walk.h"
 #include "patch-ids.h"
 #include "decorate.h"
-#include "log-tree.h"
 #include "string-list.h"
 #include "line-log.h"
 #include "mailmap.h"
 #include "commit-slab.h"
-#include "dir.h"
 #include "cache-tree.h"
 #include "bisect.h"
 #include "packfile.h"
@@ -2223,6 +2221,27 @@ static void add_message_grep(struct rev_info *revs, const char *pattern)
        add_grep(revs, pattern, GREP_PATTERN_BODY);
 }
 
+static int parse_count(const char *arg)
+{
+       int count;
+
+       if (strtol_i(arg, 10, &count) < 0)
+               die("'%s': not an integer", arg);
+       return count;
+}
+
+static timestamp_t parse_age(const char *arg)
+{
+       timestamp_t num;
+       char *p;
+
+       errno = 0;
+       num = parse_timestamp(arg, &p, 10);
+       if (errno || *p || p == arg)
+               die("'%s': not a number of seconds since epoch", arg);
+       return num;
+}
+
 static int handle_revision_opt(struct rev_info *revs, int argc, const char **argv,
                               int *unkc, const char **unkv,
                               const struct setup_revision_opt* opt)
@@ -2249,29 +2268,27 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        }
 
        if ((argcount = parse_long_opt("max-count", argv, &optarg))) {
-               revs->max_count = atoi(optarg);
+               revs->max_count = parse_count(optarg);
                revs->no_walk = 0;
                return argcount;
        } else if ((argcount = parse_long_opt("skip", argv, &optarg))) {
-               revs->skip_count = atoi(optarg);
+               revs->skip_count = parse_count(optarg);
                return argcount;
        } else if ((*arg == '-') && isdigit(arg[1])) {
                /* accept -<digit>, like traditional "head" */
-               if (strtol_i(arg + 1, 10, &revs->max_count) < 0 ||
-                   revs->max_count < 0)
-                       die("'%s': not a non-negative integer", arg + 1);
+               revs->max_count = parse_count(arg + 1);
                revs->no_walk = 0;
        } else if (!strcmp(arg, "-n")) {
                if (argc <= 1)
                        return error("-n requires an argument");
-               revs->max_count = atoi(argv[1]);
+               revs->max_count = parse_count(argv[1]);
                revs->no_walk = 0;
                return 2;
        } else if (skip_prefix(arg, "-n", &optarg)) {
-               revs->max_count = atoi(optarg);
+               revs->max_count = parse_count(optarg);
                revs->no_walk = 0;
        } else if ((argcount = parse_long_opt("max-age", argv, &optarg))) {
-               revs->max_age = atoi(optarg);
+               revs->max_age = parse_age(optarg);
                return argcount;
        } else if ((argcount = parse_long_opt("since", argv, &optarg))) {
                revs->max_age = approxidate(optarg);
@@ -2283,7 +2300,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->max_age = approxidate(optarg);
                return argcount;
        } else if ((argcount = parse_long_opt("min-age", argv, &optarg))) {
-               revs->min_age = atoi(optarg);
+               revs->min_age = parse_age(optarg);
                return argcount;
        } else if ((argcount = parse_long_opt("before", argv, &optarg))) {
                revs->min_age = approxidate(optarg);
@@ -2371,11 +2388,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!strcmp(arg, "--no-merges")) {
                revs->max_parents = 1;
        } else if (skip_prefix(arg, "--min-parents=", &optarg)) {
-               revs->min_parents = atoi(optarg);
+               revs->min_parents = parse_count(optarg);
        } else if (!strcmp(arg, "--no-min-parents")) {
                revs->min_parents = 0;
        } else if (skip_prefix(arg, "--max-parents=", &optarg)) {
-               revs->max_parents = atoi(optarg);
+               revs->max_parents = parse_count(optarg);
        } else if (!strcmp(arg, "--no-max-parents")) {
                revs->max_parents = -1;
        } else if (!strcmp(arg, "--boundary")) {
@@ -2384,8 +2401,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->left_right = 1;
        } else if (!strcmp(arg, "--left-only")) {
                if (revs->right_only)
-                       die("--left-only is incompatible with --right-only"
-                           " or --cherry");
+                       die(_("options '%s' and '%s' cannot be used together"),
+                           "--left-only", "--right-only/--cherry");
                revs->left_only = 1;
        } else if (!strcmp(arg, "--right-only")) {
                if (revs->left_only)
@@ -2709,7 +2726,8 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
                clear_ref_exclusions(&revs->ref_excludes);
        } else if (!strcmp(arg, "--branches")) {
                if (revs->ref_excludes.hidden_refs_configured)
-                       return error(_("--exclude-hidden cannot be used together with --branches"));
+                       return error(_("options '%s' and '%s' cannot be used together"),
+                                    "--exclude-hidden", "--branches");
                handle_refs(refs, revs, *flags, refs_for_each_branch_ref);
                clear_ref_exclusions(&revs->ref_excludes);
        } else if (!strcmp(arg, "--bisect")) {
@@ -2720,12 +2738,14 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
                revs->bisect = 1;
        } else if (!strcmp(arg, "--tags")) {
                if (revs->ref_excludes.hidden_refs_configured)
-                       return error(_("--exclude-hidden cannot be used together with --tags"));
+                       return error(_("options '%s' and '%s' cannot be used together"),
+                                    "--exclude-hidden", "--tags");
                handle_refs(refs, revs, *flags, refs_for_each_tag_ref);
                clear_ref_exclusions(&revs->ref_excludes);
        } else if (!strcmp(arg, "--remotes")) {
                if (revs->ref_excludes.hidden_refs_configured)
-                       return error(_("--exclude-hidden cannot be used together with --remotes"));
+                       return error(_("options '%s' and '%s' cannot be used together"),
+                                    "--exclude-hidden", "--remotes");
                handle_refs(refs, revs, *flags, refs_for_each_remote_ref);
                clear_ref_exclusions(&revs->ref_excludes);
        } else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
@@ -2743,21 +2763,24 @@ static int handle_revision_pseudo_opt(struct rev_info *revs,
        } else if (skip_prefix(arg, "--branches=", &optarg)) {
                struct all_refs_cb cb;
                if (revs->ref_excludes.hidden_refs_configured)
-                       return error(_("--exclude-hidden cannot be used together with --branches"));
+                       return error(_("options '%s' and '%s' cannot be used together"),
+                                    "--exclude-hidden", "--branches");
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, optarg, "refs/heads/", &cb);
                clear_ref_exclusions(&revs->ref_excludes);
        } else if (skip_prefix(arg, "--tags=", &optarg)) {
                struct all_refs_cb cb;
                if (revs->ref_excludes.hidden_refs_configured)
-                       return error(_("--exclude-hidden cannot be used together with --tags"));
+                       return error(_("options '%s' and '%s' cannot be used together"),
+                                    "--exclude-hidden", "--tags");
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, optarg, "refs/tags/", &cb);
                clear_ref_exclusions(&revs->ref_excludes);
        } else if (skip_prefix(arg, "--remotes=", &optarg)) {
                struct all_refs_cb cb;
                if (revs->ref_excludes.hidden_refs_configured)
-                       return error(_("--exclude-hidden cannot be used together with --remotes"));
+                       return error(_("options '%s' and '%s' cannot be used together"),
+                                    "--exclude-hidden", "--remotes");
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, optarg, "refs/remotes/", &cb);
                clear_ref_exclusions(&revs->ref_excludes);
@@ -3036,8 +3059,6 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                revs->grep_filter.ignore_locale = 1;
        compile_grep_patterns(&revs->grep_filter);
 
-       if (revs->reverse && revs->reflog_info)
-               die(_("options '%s' and '%s' cannot be used together"), "--reverse", "--walk-reflogs");
        if (revs->reflog_info && revs->limited)
                die("cannot combine --walk-reflogs with history-limiting options");
        if (revs->rewrite_parents && revs->children.name)
@@ -3048,11 +3069,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
        /*
         * Limitations on the graph functionality
         */
-       if (revs->reverse && revs->graph)
-               die(_("options '%s' and '%s' cannot be used together"), "--reverse", "--graph");
+       die_for_incompatible_opt3(!!revs->graph, "--graph",
+                                 !!revs->reverse, "--reverse",
+                                 !!revs->reflog_info, "--walk-reflogs");
 
-       if (revs->reflog_info && revs->graph)
-               die(_("options '%s' and '%s' cannot be used together"), "--walk-reflogs", "--graph");
        if (revs->no_walk && revs->graph)
                die(_("options '%s' and '%s' cannot be used together"), "--no-walk", "--graph");
        if (!revs->reflog_info && revs->grep_filter.use_reflog_filter)
index a558042c876ad2baefcfcc111163bab9691f7f1c..0e7435718a52267a2421a27320b783ee9ce0c1a4 100644 (file)
@@ -14,9 +14,7 @@
 #include "quote.h"
 #include "config.h"
 #include "packfile.h"
-#include "hook.h"
 #include "compat/nonblock.h"
-#include "alloc.h"
 
 void child_process_init(struct child_process *child)
 {
index 89aca9d829ed046f532b87120f39d807c86ebcb9..37f59d4f66bbc2b21bf08288a75de4a159b24625 100644 (file)
@@ -4,7 +4,6 @@
 #include "date.h"
 #include "gettext.h"
 #include "hex.h"
-#include "refs.h"
 #include "object-store-ll.h"
 #include "pkt-line.h"
 #include "sideband.h"
@@ -12,7 +11,6 @@
 #include "remote.h"
 #include "connect.h"
 #include "send-pack.h"
-#include "quote.h"
 #include "transport.h"
 #include "version.h"
 #include "oid-array.h"
index d584cac8ed9307caad0f8a9ede98d1f57ceab874..91de546b323e30022e0428628b594ac9174f2ae5 100644 (file)
 #include "pager.h"
 #include "commit.h"
 #include "sequencer.h"
-#include "tag.h"
 #include "run-command.h"
 #include "hook.h"
-#include "exec-cmd.h"
 #include "utf8.h"
 #include "cache-tree.h"
 #include "diff.h"
@@ -39,7 +37,6 @@
 #include "notes-utils.h"
 #include "sigchain.h"
 #include "unpack-trees.h"
-#include "worktree.h"
 #include "oidmap.h"
 #include "oidset.h"
 #include "commit-slab.h"
@@ -238,34 +235,29 @@ static int git_sequencer_config(const char *k, const char *v,
                                const struct config_context *ctx, void *cb)
 {
        struct replay_opts *opts = cb;
-       int status;
 
        if (!strcmp(k, "commit.cleanup")) {
-               const char *s;
+               if (!v)
+                       return config_error_nonbool(k);
 
-               status = git_config_string(&s, k, v);
-               if (status)
-                       return status;
-
-               if (!strcmp(s, "verbatim")) {
+               if (!strcmp(v, "verbatim")) {
                        opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
                        opts->explicit_cleanup = 1;
-               } else if (!strcmp(s, "whitespace")) {
+               } else if (!strcmp(v, "whitespace")) {
                        opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
                        opts->explicit_cleanup = 1;
-               } else if (!strcmp(s, "strip")) {
+               } else if (!strcmp(v, "strip")) {
                        opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;
                        opts->explicit_cleanup = 1;
-               } else if (!strcmp(s, "scissors")) {
+               } else if (!strcmp(v, "scissors")) {
                        opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SCISSORS;
                        opts->explicit_cleanup = 1;
                } else {
                        warning(_("invalid commit message cleanup mode '%s'"),
-                                 s);
+                                 v);
                }
 
-               free((char *)s);
-               return status;
+               return 0;
        }
 
        if (!strcmp(k, "commit.gpgsign")) {
@@ -345,7 +337,7 @@ static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
        if (ignore_footer)
                sb->buf[sb->len - ignore_footer] = saved_char;
 
-       if (info.trailer_start == info.trailer_end)
+       if (info.trailer_block_start == info.trailer_block_end)
                return 0;
 
        for (i = 0; i < info.trailer_nr; i++)
@@ -482,7 +474,7 @@ static void print_advice(struct repository *r, int show_hint,
                 * of the commit itself so remove CHERRY_PICK_HEAD
                 */
                refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
-                               NULL, 0);
+                               NULL, REF_NO_DEREF);
                return;
        }
 
@@ -1675,7 +1667,7 @@ static int do_commit(struct repository *r,
                strbuf_release(&sb);
                if (!res) {
                        refs_delete_ref(get_main_ref_store(r), "",
-                                       "CHERRY_PICK_HEAD", NULL, 0);
+                                       "CHERRY_PICK_HEAD", NULL, REF_NO_DEREF);
                        unlink(git_path_merge_msg(r));
                        if (!is_rebase_i(opts))
                                print_commit_summary(r, NULL, &oid,
@@ -2414,9 +2406,10 @@ static int do_pick_commit(struct repository *r,
        } else if (allow == 2) {
                drop_commit = 1;
                refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
-                               NULL, 0);
+                               NULL, REF_NO_DEREF);
                unlink(git_path_merge_msg(r));
-               unlink(git_path_auto_merge(r));
+               refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
+                               NULL, REF_NO_DEREF);
                fprintf(stderr,
                        _("dropping %s %s -- patch contents already upstream\n"),
                        oid_to_hex(&commit->object.oid), msg.subject);
@@ -2810,7 +2803,7 @@ void sequencer_post_commit_cleanup(struct repository *r, int verbose)
 
        if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD")) {
                if (!refs_delete_ref(get_main_ref_store(r), "",
-                                    "CHERRY_PICK_HEAD", NULL, 0) &&
+                                    "CHERRY_PICK_HEAD", NULL, REF_NO_DEREF) &&
                    verbose)
                        warning(_("cancelling a cherry picking in progress"));
                opts.action = REPLAY_PICK;
@@ -2819,14 +2812,15 @@ void sequencer_post_commit_cleanup(struct repository *r, int verbose)
 
        if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) {
                if (!refs_delete_ref(get_main_ref_store(r), "", "REVERT_HEAD",
-                                    NULL, 0) &&
+                                    NULL, REF_NO_DEREF) &&
                    verbose)
                        warning(_("cancelling a revert in progress"));
                opts.action = REPLAY_REVERT;
                need_cleanup = 1;
        }
 
-       unlink(git_path_auto_merge(r));
+       refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
+                       NULL, REF_NO_DEREF);
 
        if (!need_cleanup)
                return;
@@ -4124,7 +4118,7 @@ static int do_merge(struct repository *r,
 
                strbuf_release(&ref_name);
                refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
-                               NULL, 0);
+                               NULL, REF_NO_DEREF);
                rollback_lock_file(&lock);
 
                ret = run_command(&cmd);
@@ -4469,12 +4463,17 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
        return -1;
 }
 
-void create_autostash(struct repository *r, const char *path)
+static void create_autostash_internal(struct repository *r,
+                                     const char *path,
+                                     const char *refname)
 {
        struct strbuf buf = STRBUF_INIT;
        struct lock_file lock_file = LOCK_INIT;
        int fd;
 
+       if (path && refname)
+               BUG("can only pass path or refname");
+
        fd = repo_hold_locked_index(r, &lock_file, 0);
        refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
        if (0 <= fd)
@@ -4501,10 +4500,16 @@ void create_autostash(struct repository *r, const char *path)
                strbuf_reset(&buf);
                strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
 
-               if (safe_create_leading_directories_const(path))
-                       die(_("Could not create directory for '%s'"),
-                           path);
-               write_file(path, "%s", oid_to_hex(&oid));
+               if (path) {
+                       if (safe_create_leading_directories_const(path))
+                               die(_("Could not create directory for '%s'"),
+                                   path);
+                       write_file(path, "%s", oid_to_hex(&oid));
+               } else {
+                       refs_update_ref(get_main_ref_store(r), "", refname,
+                                       &oid, null_oid(), 0, UPDATE_REFS_DIE_ON_ERR);
+               }
+
                printf(_("Created autostash: %s\n"), buf.buf);
                if (reset_head(r, &ropts) < 0)
                        die(_("could not reset --hard"));
@@ -4515,6 +4520,16 @@ void create_autostash(struct repository *r, const char *path)
        strbuf_release(&buf);
 }
 
+void create_autostash(struct repository *r, const char *path)
+{
+       create_autostash_internal(r, path, NULL);
+}
+
+void create_autostash_ref(struct repository *r, const char *refname)
+{
+       create_autostash_internal(r, NULL, refname);
+}
+
 static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)
 {
        struct child_process child = CHILD_PROCESS_INIT;
@@ -4592,6 +4607,41 @@ int apply_autostash_oid(const char *stash_oid)
        return apply_save_autostash_oid(stash_oid, 1);
 }
 
+static int apply_save_autostash_ref(struct repository *r, const char *refname,
+                                   int attempt_apply)
+{
+       struct object_id stash_oid;
+       char stash_oid_hex[GIT_MAX_HEXSZ + 1];
+       int flag, ret;
+
+       if (!refs_ref_exists(get_main_ref_store(r), refname))
+               return 0;
+
+       if (!refs_resolve_ref_unsafe(get_main_ref_store(r), refname,
+                                    RESOLVE_REF_READING, &stash_oid, &flag))
+               return -1;
+       if (flag & REF_ISSYMREF)
+               return error(_("autostash reference is a symref"));
+
+       oid_to_hex_r(stash_oid_hex, &stash_oid);
+       ret = apply_save_autostash_oid(stash_oid_hex, attempt_apply);
+
+       refs_delete_ref(get_main_ref_store(r), "", refname,
+                       &stash_oid, REF_NO_DEREF);
+
+       return ret;
+}
+
+int save_autostash_ref(struct repository *r, const char *refname)
+{
+       return apply_save_autostash_ref(r, refname, 0);
+}
+
+int apply_autostash_ref(struct repository *r, const char *refname)
+{
+       return apply_save_autostash_ref(r, refname, 1);
+}
+
 static int checkout_onto(struct repository *r, struct replay_opts *opts,
                         const char *onto_name, const struct object_id *onto,
                         const struct object_id *orig_head)
@@ -4774,8 +4824,10 @@ static int pick_commits(struct repository *r,
                        }
                        unlink(rebase_path_author_script());
                        unlink(git_path_merge_head(r));
-                       unlink(git_path_auto_merge(r));
-                       delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+                       refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
+                                       NULL, REF_NO_DEREF);
+                       refs_delete_ref(get_main_ref_store(r), "", "REBASE_HEAD",
+                                       NULL, REF_NO_DEREF);
 
                        if (item->command == TODO_BREAK) {
                                if (!opts->verbose)
@@ -5116,7 +5168,7 @@ static int commit_staged_changes(struct repository *r,
                if (refs_ref_exists(get_main_ref_store(r),
                                    "CHERRY_PICK_HEAD") &&
                    refs_delete_ref(get_main_ref_store(r), "",
-                                   "CHERRY_PICK_HEAD", NULL, 0))
+                                   "CHERRY_PICK_HEAD", NULL, REF_NO_DEREF))
                        return error(_("could not remove CHERRY_PICK_HEAD"));
                if (unlink(git_path_merge_msg(r)) && errno != ENOENT)
                        return error_errno(_("could not remove '%s'"),
@@ -5130,7 +5182,8 @@ static int commit_staged_changes(struct repository *r,
                return error(_("could not commit staged changes."));
        unlink(rebase_path_amend());
        unlink(git_path_merge_head(r));
-       unlink(git_path_auto_merge(r));
+       refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
+                       NULL, REF_NO_DEREF);
        if (final_fixup) {
                unlink(rebase_path_fixup_msg());
                unlink(rebase_path_squash_msg());
index 913a0f652d9ab356bc066f162fa1e5197f6900eb..dcef7bb99c08b0f8e5a29905f8f3a4d6d2d34a45 100644 (file)
@@ -225,9 +225,12 @@ void commit_post_rewrite(struct repository *r,
                         const struct object_id *new_head);
 
 void create_autostash(struct repository *r, const char *path);
+void create_autostash_ref(struct repository *r, const char *refname);
 int save_autostash(const char *path);
+int save_autostash_ref(struct repository *r, const char *refname);
 int apply_autostash(const char *path);
 int apply_autostash_oid(const char *stash_oid);
+int apply_autostash_ref(struct repository *r, const char *refname);
 
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
 #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
diff --git a/setup.c b/setup.c
index fc592dc6dd5bf3f2ac7ac01ca0d72b7ce0129e77..f4ad53d5454847951f2c55d02e9418484fdadac2 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -13,7 +13,6 @@
 #include "string-list.h"
 #include "chdir-notify.h"
 #include "path.h"
-#include "promisor-remote.h"
 #include "quote.h"
 #include "trace2.h"
 #include "worktree.h"
@@ -559,6 +558,8 @@ static enum extension_result handle_extension_v0(const char *var,
                        data->precious_objects = git_config_bool(var, value);
                        return EXTENSION_OK;
                } else if (!strcmp(ext, "partialclone")) {
+                       if (!value)
+                               return config_error_nonbool(var);
                        data->partial_clone = xstrdup(value);
                        return EXTENSION_OK;
                } else if (!strcmp(ext, "worktreeconfig")) {
@@ -590,6 +591,17 @@ static enum extension_result handle_extension(const char *var,
                                     "extensions.objectformat", value);
                data->hash_algo = format;
                return EXTENSION_OK;
+       } else if (!strcmp(ext, "refstorage")) {
+               unsigned int format;
+
+               if (!value)
+                       return config_error_nonbool(var);
+               format = ref_storage_format_by_name(value);
+               if (format == REF_STORAGE_FORMAT_UNKNOWN)
+                       return error(_("invalid value for '%s': '%s'"),
+                                    "extensions.refstorage", value);
+               data->ref_storage_format = format;
+               return EXTENSION_OK;
        }
        return EXTENSION_UNKNOWN;
 }
@@ -1564,6 +1576,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
                }
                if (startup_info->have_repository) {
                        repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
+                       repo_set_ref_storage_format(the_repository,
+                                                   repo_fmt.ref_storage_format);
                        the_repository->repository_format_worktree_config =
                                repo_fmt.worktree_config;
                        /* take ownership of repo_fmt.partial_clone */
@@ -1657,6 +1671,8 @@ void check_repository_format(struct repository_format *fmt)
        check_repository_format_gently(get_git_dir(), fmt, NULL);
        startup_info->have_repository = 1;
        repo_set_hash_algo(the_repository, fmt->hash_algo);
+       repo_set_ref_storage_format(the_repository,
+                                   fmt->ref_storage_format);
        the_repository->repository_format_worktree_config =
                fmt->worktree_config;
        the_repository->repository_format_partial_clone =
@@ -1865,12 +1881,15 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)
        return 1;
 }
 
-void initialize_repository_version(int hash_algo, int reinit)
+void initialize_repository_version(int hash_algo,
+                                  unsigned int ref_storage_format,
+                                  int reinit)
 {
        char repo_version_string[10];
        int repo_version = GIT_REPO_VERSION;
 
-       if (hash_algo != GIT_HASH_SHA1)
+       if (hash_algo != GIT_HASH_SHA1 ||
+           ref_storage_format != REF_STORAGE_FORMAT_FILES)
                repo_version = GIT_REPO_VERSION_READ;
 
        /* This forces creation of new config file */
@@ -1883,23 +1902,72 @@ void initialize_repository_version(int hash_algo, int reinit)
                               hash_algos[hash_algo].name);
        else if (reinit)
                git_config_set_gently("extensions.objectformat", NULL);
+
+       if (ref_storage_format != REF_STORAGE_FORMAT_FILES)
+               git_config_set("extensions.refstorage",
+                              ref_storage_format_to_name(ref_storage_format));
+}
+
+static int is_reinit(void)
+{
+       struct strbuf buf = STRBUF_INIT;
+       char junk[2];
+       int ret;
+
+       git_path_buf(&buf, "HEAD");
+       ret = !access(buf.buf, R_OK) || readlink(buf.buf, junk, sizeof(junk) - 1) != -1;
+       strbuf_release(&buf);
+       return ret;
+}
+
+void create_reference_database(unsigned int ref_storage_format,
+                              const char *initial_branch, int quiet)
+{
+       struct strbuf err = STRBUF_INIT;
+       int reinit = is_reinit();
+
+       repo_set_ref_storage_format(the_repository, ref_storage_format);
+       if (refs_init_db(get_main_ref_store(the_repository), 0, &err))
+               die("failed to set up refs db: %s", err.buf);
+
+       /*
+        * Point the HEAD symref to the initial branch with if HEAD does
+        * not yet exist.
+        */
+       if (!reinit) {
+               char *ref;
+
+               if (!initial_branch)
+                       initial_branch = git_default_branch_name(quiet);
+
+               ref = xstrfmt("refs/heads/%s", initial_branch);
+               if (check_refname_format(ref, 0) < 0)
+                       die(_("invalid initial branch name: '%s'"),
+                           initial_branch);
+
+               if (create_symref("HEAD", ref, NULL) < 0)
+                       exit(1);
+               free(ref);
+       }
+
+       if (reinit && initial_branch)
+               warning(_("re-init: ignored --initial-branch=%s"),
+                       initial_branch);
+
+       strbuf_release(&err);
 }
 
 static int create_default_files(const char *template_path,
                                const char *original_git_dir,
-                               const char *initial_branch,
                                const struct repository_format *fmt,
                                int prev_bare_repository,
-                               int init_shared_repository,
-                               int quiet)
+                               int init_shared_repository)
 {
        struct stat st1;
        struct strbuf buf = STRBUF_INIT;
        char *path;
-       char junk[2];
        int reinit;
        int filemode;
-       struct strbuf err = STRBUF_INIT;
        const char *init_template_dir = NULL;
        const char *work_tree = get_git_work_tree();
 
@@ -1919,6 +1987,8 @@ static int create_default_files(const char *template_path,
        reset_shared_repository();
        git_config(git_default_config, NULL);
 
+       reinit = is_reinit();
+
        /*
         * We must make sure command-line options continue to override any
         * values we might have just re-read from the config.
@@ -1962,40 +2032,7 @@ static int create_default_files(const char *template_path,
                adjust_shared_perm(get_git_dir());
        }
 
-       /*
-        * We need to create a "refs" dir in any case so that older
-        * versions of git can tell that this is a repository.
-        */
-       safe_create_dir(git_path("refs"), 1);
-       adjust_shared_perm(git_path("refs"));
-
-       if (refs_init_db(&err))
-               die("failed to set up refs db: %s", err.buf);
-
-       /*
-        * Point the HEAD symref to the initial branch with if HEAD does
-        * not yet exist.
-        */
-       path = git_path_buf(&buf, "HEAD");
-       reinit = (!access(path, R_OK)
-                 || readlink(path, junk, sizeof(junk)-1) != -1);
-       if (!reinit) {
-               char *ref;
-
-               if (!initial_branch)
-                       initial_branch = git_default_branch_name(quiet);
-
-               ref = xstrfmt("refs/heads/%s", initial_branch);
-               if (check_refname_format(ref, 0) < 0)
-                       die(_("invalid initial branch name: '%s'"),
-                           initial_branch);
-
-               if (create_symref("HEAD", ref, NULL) < 0)
-                       exit(1);
-               free(ref);
-       }
-
-       initialize_repository_version(fmt->hash_algo, 0);
+       initialize_repository_version(fmt->hash_algo, fmt->ref_storage_format, 0);
 
        /* Check filemode trustability */
        path = git_path_buf(&buf, "config");
@@ -2108,8 +2145,29 @@ static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash
        }
 }
 
+static void validate_ref_storage_format(struct repository_format *repo_fmt,
+                                       unsigned int format)
+{
+       const char *name = getenv("GIT_DEFAULT_REF_FORMAT");
+
+       if (repo_fmt->version >= 0 &&
+           format != REF_STORAGE_FORMAT_UNKNOWN &&
+           format != repo_fmt->ref_storage_format) {
+               die(_("attempt to reinitialize repository with different reference storage format"));
+       } else if (format != REF_STORAGE_FORMAT_UNKNOWN) {
+               repo_fmt->ref_storage_format = format;
+       } else if (name) {
+               format = ref_storage_format_by_name(name);
+               if (format == REF_STORAGE_FORMAT_UNKNOWN)
+                       die(_("unknown ref storage format '%s'"), name);
+               repo_fmt->ref_storage_format = format;
+       }
+}
+
 int init_db(const char *git_dir, const char *real_git_dir,
-           const char *template_dir, int hash, const char *initial_branch,
+           const char *template_dir, int hash,
+           unsigned int ref_storage_format,
+           const char *initial_branch,
            int init_shared_repository, unsigned int flags)
 {
        int reinit;
@@ -2152,16 +2210,22 @@ int init_db(const char *git_dir, const char *real_git_dir,
        check_repository_format(&repo_fmt);
 
        validate_hash_algorithm(&repo_fmt, hash);
+       validate_ref_storage_format(&repo_fmt, ref_storage_format);
 
        reinit = create_default_files(template_dir, original_git_dir,
-                                     initial_branch, &repo_fmt,
-                                     prev_bare_repository,
-                                     init_shared_repository,
-                                     flags & INIT_DB_QUIET);
-       if (reinit && initial_branch)
-               warning(_("re-init: ignored --initial-branch=%s"),
-                       initial_branch);
+                                     &repo_fmt, prev_bare_repository,
+                                     init_shared_repository);
+
+       /*
+        * Now that we have set up both the hash algorithm and the ref storage
+        * format we can update the repository's settings accordingly.
+        */
+       repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
+       repo_set_ref_storage_format(the_repository, repo_fmt.ref_storage_format);
 
+       if (!(flags & INIT_DB_SKIP_REFDB))
+               create_reference_database(repo_fmt.ref_storage_format,
+                                         initial_branch, flags & INIT_DB_QUIET);
        create_object_directory();
 
        if (get_shared_repository()) {
diff --git a/setup.h b/setup.h
index b48cf1c43b5a559057a2e92d2fe4250c15255344..3599aec93c5ac0b72aafaf3cdcb030831cc53e3b 100644 (file)
--- a/setup.h
+++ b/setup.h
@@ -115,6 +115,7 @@ struct repository_format {
        int worktree_config;
        int is_bare;
        int hash_algo;
+       unsigned int ref_storage_format;
        int sparse_index;
        char *work_tree;
        struct string_list unknown_extensions;
@@ -131,6 +132,7 @@ struct repository_format {
        .version = -1, \
        .is_bare = -1, \
        .hash_algo = GIT_HASH_SHA1, \
+       .ref_storage_format = REF_STORAGE_FORMAT_FILES, \
        .unknown_extensions = STRING_LIST_INIT_DUP, \
        .v1_only_extensions = STRING_LIST_INIT_DUP, \
 }
@@ -169,14 +171,20 @@ int verify_repository_format(const struct repository_format *format,
  */
 void check_repository_format(struct repository_format *fmt);
 
-#define INIT_DB_QUIET 0x0001
-#define INIT_DB_EXIST_OK 0x0002
+#define INIT_DB_QUIET      (1 << 0)
+#define INIT_DB_EXIST_OK   (1 << 1)
+#define INIT_DB_SKIP_REFDB (1 << 2)
 
 int init_db(const char *git_dir, const char *real_git_dir,
            const char *template_dir, int hash_algo,
+           unsigned int ref_storage_format,
            const char *initial_branch, int init_shared_repository,
            unsigned int flags);
-void initialize_repository_version(int hash_algo, int reinit);
+void initialize_repository_version(int hash_algo,
+                                  unsigned int ref_storage_format,
+                                  int reinit);
+void create_reference_database(unsigned int ref_storage_format,
+                              const char *initial_branch, int quiet);
 
 /*
  * NOTE NOTE NOTE!!
index 133496bd4d9f2979dfe2765e98a50186463dde1d..f69fd166105a44a8be74fa6cd74841f06aa1a12d 100644 (file)
@@ -31,7 +31,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
 
 /* closeout.c - close standard output and standard error
    Copyright (C) 1998-2007 Free Software Foundation, Inc.
@@ -47,7 +47,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
 
 #include <errno.h>
 #include <stdio.h>
index dede2cbddf94f0c62ef01ba5e48a93026408c5d6..f993ef9c69091740ea2248d6c34452ecb58fba02 100644 (file)
@@ -88,7 +88,7 @@
 /*
  * Should define Big Endian for a whitelist of known processors. See
  * https://sourceforge.net/p/predef/wiki/Endianness/ and
- * http://www.oracle.com/technetwork/server-storage/solaris/portingtosolaris-138514.html
+ * https://web.archive.org/web/20140421151132/http://www.perforce.com/perforce/doc.current/manuals/p4sag/chapter.superuser.html
  */
 #define SHA1DC_BIGENDIAN
 
index ac728cdd77892dab1c323fc81adaf240d988c6d9..7711798127e49efaa18b6403ecf103f05e92f7e7 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -7,7 +7,6 @@
 #include "commit.h"
 #include "tag.h"
 #include "pkt-line.h"
-#include "remote.h"
 #include "refs.h"
 #include "oid-array.h"
 #include "path.h"
diff --git a/shell.c b/shell.c
index 5c67e7bd97e2c825d79c3503267b5b41cd83bb8a..2ece8b16e2e8e1e3473dbc7bae6deedea51ee4e8 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -4,7 +4,6 @@
 #include "strbuf.h"
 #include "run-command.h"
 #include "alias.h"
-#include "prompt.h"
 
 #define COMMAND_DIR "git-shell-commands"
 #define HELP_COMMAND COMMAND_DIR "/help"
index 6cbfd391c47fb531740eb4b963a11b77f8abe29d..266a67342be7245ae3c1dfae5a1a5ad6647f95df 100644 (file)
@@ -69,7 +69,10 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref
  * of the line. This should be called for a single line only, which is
  * passed as the first N characters of the SRC array.
  *
- * NEEDSWORK: use "size_t n" instead for clarity.
+ * It is fine to use "int n" here instead of "size_t n" as all calls to this
+ * function pass an 'int' parameter. Additionally, the buffer involved in
+ * storing these 'int' values takes input from a packet via the pkt-line
+ * interface, which is capable of transferring only 64kB at a time.
  */
 static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
 {
index 9367ca099cd114d56d7b2103b0e56fa4933f5781..3c6bc049c15d44c33c064fecbbd2236571b2dadb 100644 (file)
@@ -31,6 +31,33 @@ void fill_stat_data(struct stat_data *sd, struct stat *st)
        sd->sd_size = munge_st_size(st->st_size);
 }
 
+static void set_times(struct stat *st, const struct stat_data *sd)
+{
+       st->st_ctime = sd->sd_ctime.sec;
+       st->st_mtime = sd->sd_mtime.sec;
+#ifdef NO_NSEC
+       ; /* nothing */
+#else
+#ifdef USE_ST_TIMESPEC
+       st->st_ctimespec.tv_nsec = sd->sd_ctime.nsec;
+       st->st_mtimespec.tv_nsec = sd->sd_mtime.nsec;
+#else
+       st->st_ctim.tv_nsec = sd->sd_ctime.nsec;
+       st->st_mtim.tv_nsec = sd->sd_mtime.nsec;
+#endif
+#endif
+}
+
+void fake_lstat_data(const struct stat_data *sd, struct stat *st)
+{
+       set_times(st, sd);
+       st->st_dev = sd->sd_dev;
+       st->st_ino = sd->sd_ino;
+       st->st_uid = sd->sd_uid;
+       st->st_gid = sd->sd_gid;
+       st->st_size = sd->sd_size;
+}
+
 int match_stat_data(const struct stat_data *sd, struct stat *st)
 {
        int changed = 0;
index 700f502ac0b3cb938c2f376653dc1e91ac4e6d24..5b21a30f90bfd2e1fed39fed0d61b9abb360af33 100644 (file)
@@ -46,6 +46,14 @@ struct stat_validity {
  */
 void fill_stat_data(struct stat_data *sd, struct stat *st);
 
+/*
+ * The inverse of the above.  When we know the cache_entry that
+ * contains sd is up-to-date, but still need to pretend we called
+ * lstat() to learn that fact, this function fills "st" enough to
+ * fool ie_match_stat().
+ */
+void fake_lstat_data(const struct stat_data *sd, struct stat *st);
+
 /*
  * Return 0 if st is consistent with a file not having been changed
  * since sd was filled.  If there are differences, return a
index 9f55c8766ba9de77437275b8796f8e348fe35e03..4715d3e51f873e7b6618a80ebc79f2d2b4ebaab7 100644 (file)
--- a/strvec.h
+++ b/strvec.h
@@ -4,8 +4,8 @@
 /**
  * The strvec API allows one to dynamically build and store
  * NULL-terminated arrays of strings. A strvec maintains the invariant that the
- * `items` member always points to a non-NULL array, and that the array is
- * always NULL-terminated at the element pointed to by `items[nr]`. This
+ * `v` member always points to a non-NULL array, and that the array is
+ * always NULL-terminated at the element pointed to by `v[nr]`. This
  * makes the result suitable for passing to functions expecting to receive
  * argv from main().
  *
@@ -22,7 +22,7 @@ extern const char *empty_strvec[];
 
 /**
  * A single array. This should be initialized by assignment from
- * `STRVEC_INIT`, or by calling `strvec_init`. The `items`
+ * `STRVEC_INIT`, or by calling `strvec_init`. The `v`
  * member contains the actual array; the `nr` member contains the
  * number of elements in the array, not including the terminating
  * NULL.
@@ -80,7 +80,7 @@ void strvec_split(struct strvec *, const char *);
 void strvec_clear(struct strvec *);
 
 /**
- * Disconnect the `items` member from the `strvec` struct and
+ * Disconnect the `v` member from the `strvec` struct and
  * return it. The caller is responsible for freeing the memory used
  * by the array, and by the strings it references. After detaching,
  * the `strvec` is in a reinitialized state and can be pushed
index 6a48fd12f66f93d132aab6745d67effd482a168f..54130f6a38572b613d4b7ee8ae1cf3bc6035055d 100644 (file)
@@ -14,6 +14,8 @@
 #include "parse-options.h"
 #include "thread-utils.h"
 #include "tree-walk.h"
+#include "url.h"
+#include "urlmatch.h"
 
 /*
  * submodule cache lookup structure
@@ -228,6 +230,144 @@ in_component:
        return 0;
 }
 
+static int starts_with_dot_slash(const char *const path)
+{
+       return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_SLASH |
+                               PATH_MATCH_XPLATFORM);
+}
+
+static int starts_with_dot_dot_slash(const char *const path)
+{
+       return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH |
+                               PATH_MATCH_XPLATFORM);
+}
+
+static int submodule_url_is_relative(const char *url)
+{
+       return starts_with_dot_slash(url) || starts_with_dot_dot_slash(url);
+}
+
+/*
+ * Count directory components that a relative submodule URL should chop
+ * from the remote_url it is to be resolved against.
+ *
+ * In other words, this counts "../" components at the start of a
+ * submodule URL.
+ *
+ * Returns the number of directory components to chop and writes a
+ * pointer to the next character of url after all leading "./" and
+ * "../" components to out.
+ */
+static int count_leading_dotdots(const char *url, const char **out)
+{
+       int result = 0;
+       while (1) {
+               if (starts_with_dot_dot_slash(url)) {
+                       result++;
+                       url += strlen("../");
+                       continue;
+               }
+               if (starts_with_dot_slash(url)) {
+                       url += strlen("./");
+                       continue;
+               }
+               *out = url;
+               return result;
+       }
+}
+/*
+ * Check whether a transport is implemented by git-remote-curl.
+ *
+ * If it is, returns 1 and writes the URL that would be passed to
+ * git-remote-curl to the "out" parameter.
+ *
+ * Otherwise, returns 0 and leaves "out" untouched.
+ *
+ * Examples:
+ *   http::https://example.com/repo.git -> 1, https://example.com/repo.git
+ *   https://example.com/repo.git -> 1, https://example.com/repo.git
+ *   git://example.com/repo.git -> 0
+ *
+ * This is for use in checking for previously exploitable bugs that
+ * required a submodule URL to be passed to git-remote-curl.
+ */
+static int url_to_curl_url(const char *url, const char **out)
+{
+       /*
+        * We don't need to check for case-aliases, "http.exe", and so
+        * on because in the default configuration, is_transport_allowed
+        * prevents URLs with those schemes from being cloned
+        * automatically.
+        */
+       if (skip_prefix(url, "http::", out) ||
+           skip_prefix(url, "https::", out) ||
+           skip_prefix(url, "ftp::", out) ||
+           skip_prefix(url, "ftps::", out))
+               return 1;
+       if (starts_with(url, "http://") ||
+           starts_with(url, "https://") ||
+           starts_with(url, "ftp://") ||
+           starts_with(url, "ftps://")) {
+               *out = url;
+               return 1;
+       }
+       return 0;
+}
+
+int check_submodule_url(const char *url)
+{
+       const char *curl_url;
+
+       if (looks_like_command_line_option(url))
+               return -1;
+
+       if (submodule_url_is_relative(url) || starts_with(url, "git://")) {
+               char *decoded;
+               const char *next;
+               int has_nl;
+
+               /*
+                * This could be appended to an http URL and url-decoded;
+                * check for malicious characters.
+                */
+               decoded = url_decode(url);
+               has_nl = !!strchr(decoded, '\n');
+
+               free(decoded);
+               if (has_nl)
+                       return -1;
+
+               /*
+                * URLs which escape their root via "../" can overwrite
+                * the host field and previous components, resolving to
+                * URLs like https::example.com/submodule.git and
+                * https:///example.com/submodule.git that were
+                * susceptible to CVE-2020-11008.
+                */
+               if (count_leading_dotdots(url, &next) > 0 &&
+                   (*next == ':' || *next == '/'))
+                       return -1;
+       }
+
+       else if (url_to_curl_url(url, &curl_url)) {
+               int ret = 0;
+               char *normalized = url_normalize(curl_url, NULL);
+               if (normalized) {
+                       char *decoded = url_decode(normalized);
+                       if (strchr(decoded, '\n'))
+                               ret = -1;
+                       free(normalized);
+                       free(decoded);
+               } else {
+                       ret = -1;
+               }
+
+               return ret;
+       }
+
+       return 0;
+}
+
 static int name_and_item_from_var(const char *var, struct strbuf *name,
                                  struct strbuf *item)
 {
@@ -516,7 +656,9 @@ static int parse_config(const char *var, const char *value,
                        submodule->recommend_shallow =
                                git_config_bool(var, value);
        } else if (!strcmp(item.buf, "branch")) {
-               if (!me->overwrite && submodule->branch)
+               if (!value)
+                       ret = config_error_nonbool(var);
+               else if (!me->overwrite && submodule->branch)
                        warn_multiple_config(me->treeish_name, submodule->name,
                                             "branch");
                else {
index 2a37689cc272e30fa6f5dc9cfbbde044a52068c3..b6133af71b00d61abdafc3aa736fe4f4018695cf 100644 (file)
@@ -2,9 +2,7 @@
 #define SUBMODULE_CONFIG_CACHE_H
 
 #include "config.h"
-#include "hashmap.h"
 #include "submodule.h"
-#include "strbuf.h"
 #include "tree-walk.h"
 
 /**
@@ -91,6 +89,9 @@ int config_set_in_gitmodules_file_gently(const char *key, const char *value);
  */
 int check_submodule_name(const char *name);
 
+/* Returns 0 if the URL valid per RFC3986 and -1 otherwise. */
+int check_submodule_url(const char *url);
+
 /*
  * Note: these helper functions exist solely to maintain backward
  * compatibility with 'fetch' and 'update_clone' storing configuration in
index e603a19a876c88c59f18da33caa1541b15ded9f3..213da79f66116f3db835036140affb98406a934a 100644 (file)
 #include "string-list.h"
 #include "oid-array.h"
 #include "strvec.h"
-#include "blob.h"
 #include "thread-utils.h"
 #include "path.h"
-#include "quote.h"
 #include "remote.h"
 #include "worktree.h"
 #include "parse-options.h"
@@ -30,7 +28,6 @@
 #include "commit-reach.h"
 #include "read-cache-ll.h"
 #include "setup.h"
-#include "shallow.h"
 #include "trace2.h"
 
 static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
index 3e00cdd801d637388edf1a546f9613a99cd3c737..b7a6fefe28d614830a63b0b13e6c597cfd6f9f52 100644 (file)
@@ -17,6 +17,7 @@ TAR ?= $(TAR)
 RM ?= rm -f
 PROVE ?= prove
 DEFAULT_TEST_TARGET ?= test
+DEFAULT_UNIT_TEST_TARGET ?= unit-tests-raw
 TEST_LINT ?= test-lint
 
 ifdef TEST_OUTPUT_DIRECTORY
@@ -41,6 +42,7 @@ TPERF = $(sort $(wildcard perf/p[0-9][0-9][0-9][0-9]-*.sh))
 TINTEROP = $(sort $(wildcard interop/i[0-9][0-9][0-9][0-9]-*.sh))
 CHAINLINTTESTS = $(sort $(patsubst chainlint/%.test,%,$(wildcard chainlint/*.test)))
 CHAINLINT = '$(PERL_PATH_SQ)' chainlint.pl
+UNIT_TESTS = $(sort $(filter-out %.pdb unit-tests/bin/t-basic%,$(wildcard unit-tests/bin/t-*)))
 
 # `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
@@ -65,6 +67,17 @@ prove: pre-clean check-chainlint $(TEST_LINT)
 $(T):
        @echo "*** $@ ***"; '$(TEST_SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
 
+$(UNIT_TESTS):
+       @echo "*** $@ ***"; $@
+
+.PHONY: unit-tests unit-tests-raw unit-tests-prove
+unit-tests: $(DEFAULT_UNIT_TEST_TARGET)
+
+unit-tests-raw: $(UNIT_TESTS)
+
+unit-tests-prove:
+       @echo "*** prove - unit tests ***"; $(PROVE) $(GIT_PROVE_OPTS) $(UNIT_TESTS)
+
 pre-clean:
        $(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'
 
@@ -90,20 +103,12 @@ check-chainlint:
                echo "# chainlint: $(CHAINLINTTMP_SQ)/tests" && \
                for i in $(CHAINLINTTESTS); do \
                        echo "# chainlint: $$i" && \
-                       sed -e '/^[     ]*$$/d' chainlint/$$i.expect; \
+                       cat chainlint/$$i.expect; \
                done \
        } >'$(CHAINLINTTMP_SQ)'/expect && \
        $(CHAINLINT) --emit-all '$(CHAINLINTTMP_SQ)'/tests | \
-               sed -e 's/^[1-9][0-9]* //;/^[   ]*$$/d' >'$(CHAINLINTTMP_SQ)'/actual && \
-       if test -f ../GIT-BUILD-OPTIONS; then \
-               . ../GIT-BUILD-OPTIONS; \
-       fi && \
-       if test -x ../git$$X; then \
-               DIFFW="../git$$X --no-pager diff -w --no-index"; \
-       else \
-               DIFFW="diff -w -u"; \
-       fi && \
-       $$DIFFW '$(CHAINLINTTMP_SQ)'/expect '$(CHAINLINTTMP_SQ)'/actual
+               sed -e 's/^[1-9][0-9]* //' >'$(CHAINLINTTMP_SQ)'/actual && \
+       diff -u '$(CHAINLINTTMP_SQ)'/expect '$(CHAINLINTTMP_SQ)'/actual
 
 test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax \
        test-lint-filenames
@@ -149,4 +154,4 @@ perf:
        $(MAKE) -C perf/ all
 
 .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \
-       check-chainlint clean-chainlint test-chainlint
+       check-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)
index a0ebe294848ddc3ea6abdd7b9308edce34f7a876..621d3b8c095441a8a8985b7f12363e26f8ab4d98 100644 (file)
--- a/t/README
+++ b/t/README
@@ -32,7 +32,7 @@ the tests.
     ok 2 - plain with GIT_WORK_TREE
     ok 3 - plain bare
 
-Since the tests all output TAP (see http://testanything.org) they can
+Since the tests all output TAP (see https://testanything.org) they can
 be run with any TAP harness. Here's an example of parallel testing
 powered by a recent version of prove(1):
 
@@ -479,6 +479,9 @@ GIT_TEST_DEFAULT_HASH=<hash-algo> specifies which hash algorithm to
 use in the test scripts. Recognized values for <hash-algo> are "sha1"
 and "sha256".
 
+GIT_TEST_DEFAULT_REF_FORMAT=<format> specifies which ref storage format
+to use in the test scripts. Recognized values for <format> are "files".
+
 GIT_TEST_NO_WRITE_REV_INDEX=<boolean>, when true disables the
 'pack.writeReverseIndex' setting.
 
@@ -1278,7 +1281,7 @@ Devel::Cover module. To install it do:
    sudo aptitude install libdevel-cover-perl
 
    # From the CPAN with cpanminus
-   curl -L http://cpanmin.us | perl - --sudo --self-upgrade
+   curl -L https://cpanmin.us/ | perl - --sudo --self-upgrade
    cpanm --sudo Devel::Cover
 
 Then, at the top-level:
index 48ed4eb1246efc67b0064b03ea531ea3fad3a320..056e03003d3e7847ead4f14e47b69de630cb507e 100644 (file)
@@ -1,11 +1,11 @@
-test_done ( ) {
+test_done () {
        case "$test_failure" in
-       0 )
+       0)
                test_at_end_hook_
 
                exit 0 ;;
 
-       * )
+       *)
                if test $test_external_has_tap -eq 0
                then
                        say_color error "# failed $test_failure among $msg"
@@ -14,5 +14,5 @@ test_done ( ) {
 
                exit 1 ;;
 
-               esac
+       esac
 }
index f76fde1ffba91d7becf17c0990c39ac25a7083f0..b47827d7499f607289cc6ec78a7b0030ee2449c0 100644 (file)
@@ -1,4 +1,8 @@
 (
+
        nothing &&
+
        something
+
+
 )
index a3bcea492a915f62cc6870a3e194fd86c19344a7..1c873263647907de91d461b4bf19b48ea5e85c3a 100644 (file)
@@ -12,9 +12,9 @@
 ) &&
 
 {
-       echo a ; ?!AMP?! echo b
+       echo a; ?!AMP?! echo b
 } &&
-{ echo a ; ?!AMP?! echo b ; } &&
+{ echo a; ?!AMP?! echo b; } &&
 
 {
        echo "${var}9" &&
index 28f9114f42de6b4dd6682ba7fdea5cd2bd3de263..20d0bb5333083208285e87408e5f16fb7263594c 100644 (file)
@@ -1,9 +1,9 @@
 JGIT_DAEMON_PID= &&
 git init --bare empty.git &&
-> empty.git/git-daemon-export-ok &&
+>empty.git/git-daemon-export-ok &&
 mkfifo jgit_daemon_output &&
 {
-       jgit daemon --port="$JGIT_DAEMON_PORT" . > jgit_daemon_output &
+       jgit daemon --port="$JGIT_DAEMON_PORT" . >jgit_daemon_output &
        JGIT_DAEMON_PID=$!
 } &&
 test_expect_code 2 git ls-remote --exit-code git://localhost:$JGIT_DAEMON_PORT/empty.git
index 1732d221c32e98bf40438167769869cb597ed4a1..4cd18e2edfc80a4bcb30ea409da0e6afac0d341b 100644 (file)
@@ -1,16 +1,16 @@
 case "$(git ls-files)" in
-one ) echo pass one ;;
-* ) echo bad one ; return 1 ;;
+one) echo pass one ;;
+*) echo bad one; return 1 ;;
 esac &&
 (
        case "$(git ls-files)" in
-       two ) echo pass two ;;
-       * ) echo bad two ; exit 1 ;;
-esac
+       two) echo pass two ;;
+       *) echo bad two; exit 1 ;;
+       esac
 ) &&
 case "$(git ls-files)" in
-dir/two"$LF"one ) echo pass both ;;
-* ) echo bad ; return 1 ;;
+dir/two"$LF"one) echo pass both ;;
+*) echo bad; return 1 ;;
 esac &&
 
 for i in 1 2 3 4 ; do
index f4bada946322a6bee2634efc27b1e67f684c1c5e..e6b3b2193e869136e222ec582853ba3f17254e53 100644 (file)
@@ -1,7 +1,7 @@
-OUT=$(( ( large_git ; echo $? 1 >& 3 ) | : ) 3 >& 1) &&
+OUT=$( ((large_git; echo $? 1>&3) | :) 3>&1 ) &&
 test_match_signal 13 "$OUT" &&
 
-{ test-tool sigchain > actual ; ret=$? ; } &&
+{ test-tool sigchain >actual; ret=$?; } &&
 {
        test_match_signal 15 "$ret" ||
        test "$ret" = 3
index af0369d3285b177dac4d2ccbf6528aa6b76ec2f8..83810ea7ec7d34d439c69b12bbfe2d70be16e3df 100644 (file)
@@ -4,7 +4,7 @@ mkdir sub && (
        nuff said
 ) &&
 
-cut "-d " -f actual | ( read s1 s2 s3 &&
+cut "-d " -f actual | (read s1 s2 s3 &&
 test -f $s1 ?!AMP?!
 test $(cat $s2) = tree2path1 &&
-test $(cat $s3) = tree3path1 )
+test $(cat $s3) = tree3path1)
index ab2f79e845703731124315bee2d90b6eff329126..ec42f2c30c98630ef0538936bbaa5f96c0fa7626 100644 (file)
@@ -1,2 +1,2 @@
-OUT=$(( ( large_git 1 >& 3 ) | : ) 3 >& 1) &&
+OUT=$( ((large_git 1>&3) | :) 3>&1 ) &&
 test_match_signal 13 "$OUT"
index bf9ced60d4c4a360266fc942430bbe79f6689345..37eab80738e4018e48b966cb9933ca332ae71f5e 100644 (file)
@@ -1,3 +1,5 @@
-echo 'fatal: reword option of --fixup is mutually exclusive with' '--patch/--interactive/--all/--include/--only' > expect &&
-test_must_fail git commit --fixup=reword:HEAD~ $1 2 > actual &&
+
+echo 'fatal: reword option of --fixup is mutually exclusive with'      '--patch/--interactive/--all/--include/--only' >expect &&
+test_must_fail git commit --fixup=reword:HEAD~ $1 2>actual &&
 test_cmp expect actual
+
index 10724987a5fbb6ba19ba0972c77b5689ca52ab28..087eda15e49144a9cbe24f9fc526f489b63aa068 100644 (file)
@@ -6,6 +6,7 @@ grep "^\.git$" output.txt &&
 (
        cd client$version &&
        GIT_TEST_PROTOCOL_VERSION=$version git fetch-pack --no-progress .. $(cat ../input)
-) > output &&
-       cut -d ' ' -f 2 < output | sort > actual &&
+) >output &&
+       cut -d ' ' -f 2 <output | sort >actual &&
        test_cmp expect actual
+
index e8733c97c645afce31a1253e90a69cfb017e4d7d..8507721192aeb4b5486ca7e7ddd2ecd7a484ebe0 100644 (file)
@@ -1,4 +1,4 @@
-git ls-tree $tree path > current &&
-cat > expected <<\EOF &&
+git ls-tree $tree path >current &&
+cat >expected <<\EOF &&
 EOF
 test_output
index 2d961a58c6676cee9db9ecfb573d4bac51f2d071..765a35bb4c47e31d369e481a3531ab7eaec2b097 100644 (file)
@@ -1,4 +1,4 @@
-if ! condition ; then echo nope ; else yep ; fi &&
+if ! condition; then echo nope; else yep; fi &&
 test_prerequisite !MINGW &&
 mail uucp!address &&
 echo !whatever!
index a21007a63f171c92482e47c381a759f7e6f20e03..02c0d15cca5cd42d7909f9fccbb95a932e217953 100644 (file)
@@ -1,5 +1,5 @@
 for it
 do
-       path=$(expr "$it" : ( [^:]*) ) &&
+       path=$(expr "$it" : ([^:]*)) &&
        git update-index --add "$path" || exit
 done
index d65c82129a68b7c3e2088ba9a95971e03a6952ee..d2237f1e38fad73938ff6335dac9de538e337caa 100644 (file)
@@ -6,6 +6,7 @@
                bar
                EOF
        done ?!AMP?!
+
        for i in a b c; do
                echo $i &&
                cat $i ?!LOOP?!
index a14388e6b9faeb67cdc79eea38a0cb3f94b240f8..dd7c997a3c340d633d9cbf7a78f4b29727a22ce4 100644 (file)
@@ -1,8 +1,8 @@
-sha1_file ( ) {
+sha1_file() {
        echo "$*" | sed "s#..#.git/objects/&/#"
 } &&
 
-remove_object ( ) {
+remove_object() {
        file=$(sha1_file "$*") &&
        test -e "$file" ?!AMP?!
        rm -f "$file"
index 1df3f782821b6a7221767fbdd76ad2c26b5d67c9..91b961242a1cabb7cc3f5edecaa6d99eb3eeef9e 100644 (file)
@@ -1,6 +1,6 @@
 boodle wobba \
-       gorgo snoot \
-       wafta snurb <<EOF &&
+       gorgo snoot \
+       wafta snurb <<EOF &&
 quoth the raven,
 nevermore...
 EOF
index 24da9e86d596b5d068b149242e213ba0224051d3..7ce3a348060dcfaf91299282eb4eee2b471f58bc 100644 (file)
@@ -1,18 +1,18 @@
-( while test $i -le $blobcount
-do
-       printf "Generating blob $i/$blobcount\r" >& 2 &&
+(while test $i -le $blobcount
+ do
+       printf "Generating blob $i/$blobcount\r" >&2 &&
        printf "blob\nmark :$i\ndata $blobsize\n" &&
        #test-tool genrandom $i $blobsize &&
        printf "%-${blobsize}s" $i &&
        echo "M 100644 :$i $i" >> commit &&
        i=$(($i+1)) ||
        echo $? > exit-status
-done &&
-echo "commit refs/heads/main" &&
-echo "author A U Thor <author@email.com> 123456789 +0000" &&
-echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
-echo "data 5" &&
-echo ">2gb" &&
-cat commit ) |
+ done &&
+ echo "commit refs/heads/main" &&
+ echo "author A U Thor <author@email.com> 123456789 +0000" &&
+ echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
+ echo "data 5" &&
+ echo ">2gb" &&
+ cat commit) |
 git fast-import --big-file-threshold=2 &&
 test ! -f exit-status
index 2a86885ee6a330450a76591248b60b89e601816f..3836049cc4190e4a37c35511279cd3fd36040b65 100644 (file)
@@ -2,18 +2,24 @@
        (cd foo &&
                bar
        ) &&
+
        (cd foo &&
                bar
        ) ?!AMP?!
+
        (
                cd foo &&
                bar) &&
+
        (
                cd foo &&
                bar) ?!AMP?!
+
        (cd foo &&
                bar) &&
+
        (cd foo &&
                bar) ?!AMP?!
+
        foobar
 )
index 4793a0e8e12aeb0f4d4c4497a2e77a87a2d6d2b6..3461df40e5129423ab58d92f84518d145f2f8b5d 100644 (file)
@@ -1,31 +1,31 @@
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+for i in 0 1 2 3 4 5 6 7 8 9;
 do
-       for j in 0 1 2 3 4 5 6 7 8 9 ;
+       for j in 0 1 2 3 4 5 6 7 8 9;
        do
-               echo "$i$j" > "path$i$j" ?!LOOP?!
+               echo "$i$j" >"path$i$j" ?!LOOP?!
        done ?!LOOP?!
 done &&
 
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+for i in 0 1 2 3 4 5 6 7 8 9;
 do
-       for j in 0 1 2 3 4 5 6 7 8 9 ;
+       for j in 0 1 2 3 4 5 6 7 8 9;
        do
-               echo "$i$j" > "path$i$j" || return 1
+               echo "$i$j" >"path$i$j" || return 1
        done
 done &&
 
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+for i in 0 1 2 3 4 5 6 7 8 9;
 do
-       for j in 0 1 2 3 4 5 6 7 8 9 ;
+       for j in 0 1 2 3 4 5 6 7 8 9;
        do
-               echo "$i$j" > "path$i$j" ?!LOOP?!
+               echo "$i$j" >"path$i$j" ?!LOOP?!
        done || return 1
 done &&
 
-for i in 0 1 2 3 4 5 6 7 8 9 ;
+for i in 0 1 2 3 4 5 6 7 8 9;
 do
-       for j in 0 1 2 3 4 5 6 7 8 9 ;
+       for j in 0 1 2 3 4 5 6 7 8 9;
        do
-               echo "$i$j" > "path$i$j" || return 1
+               echo "$i$j" >"path$i$j" || return 1
        done || return 1
 done
index 02e0a9f1bb5f302a2903817b5d8ef7ff25b2efa7..73ff28546ae720bd7ce8e15cd9597e27e6afae41 100644 (file)
@@ -4,6 +4,7 @@
                echo a &&
                echo b
        ) >file &&
+
        cd foo &&
        (
                echo a ?!AMP?!
index 2cfc0282970db02dd37eaf1c0c079e22a233cef1..811971b1a3c495da2adf0064c24007c6a7bf913d 100644 (file)
@@ -2,7 +2,9 @@
        foo |
        bar |
        baz &&
+
        fish |
        cow ?!AMP?!
+
        sunder
 )
index 52789278d13b7605a63c0c92c09c518990ab316f..75d6f607e295638e9e0c5029711357bc2e9d2fda 100644 (file)
@@ -1,7 +1,7 @@
 (
        echo wobba \
-               gorgo snoot \
-               wafta snurb <<-EOF &&
+              gorgo snoot \
+              wafta snurb <<-EOF &&
        quoth the raven,
        nevermore...
        EOF
index b7015361bfe6a3555d02e97d2bdc0413b8f8c432..8f694990e8d9f1ce1205399f54b61436cfe4100c 100644 (file)
@@ -2,13 +2,18 @@
        (foo && bar) &&
        (foo && bar) |
        (foo && bar) >baz &&
+
        (foo; ?!AMP?! bar) &&
        (foo; ?!AMP?! bar) |
        (foo; ?!AMP?! bar) >baz &&
+
        (foo || exit 1) &&
        (foo || exit 1) |
        (foo || exit 1) >baz &&
+
        (foo && bar) ?!AMP?!
+
        (foo && bar; ?!AMP?! baz) ?!AMP?!
+
        foobar
 )
index 71b3b3bc20ed1d6718f8d0ee6efe35b147557b8c..02f3129232a0d114bf90211b9a6508385d0115bd 100644 (file)
@@ -15,6 +15,7 @@ main-sub4" &&
 $chkms
 TXT
 ) &&
+
        subfiles=$(git ls-files) &&
        check_equal "$subfiles" "$chkms
 $chks"
index 342360bcd05941c55c924e041a4715e9fe201361..6a387917a7af18bc39b70c91b091b91eb6a7ee82 100644 (file)
@@ -4,22 +4,22 @@ git config filter.rot13.clean ./rot13.sh &&
 {
     echo "*.t filter=rot13" ?!AMP?!
     echo "*.i ident"
-} > .gitattributes &&
+} >.gitattributes &&
 
 {
     echo a b c d e f g h i j k l m ?!AMP?!
     echo n o p q r s t u v w x y z ?!AMP?!
     echo '$Id$'
-} > test &&
-cat test > test.t &&
-cat test > test.o &&
-cat test > test.i &&
+} >test &&
+cat test >test.t &&
+cat test >test.o &&
+cat test >test.i &&
 git add test test.t test.i &&
 rm -f test test.t test.i &&
 git checkout -- test test.t test.i &&
 
-echo "content-test2" > test2.o &&
-echo "content-test3 - filename with special characters" > "test3 'sq',$x=.o" ?!AMP?!
+echo "content-test2" >test2.o &&
+echo "content-test3 - filename with special characters" >"test3 'sq',$x=.o" ?!AMP?!
 
 downstream_url_for_sed=$(
        printf "%sn" "$downstream_url" |
index 1f5eaea0fd59757ea78b7dc0b24ec0971c2faa36..06c1567f481e8cbc21cf50fdf58bfb9a502d439e 100644 (file)
@@ -6,6 +6,7 @@
                bar
                EOF
        done ?!AMP?!
+
        while true; do
                echo foo &&
                cat bar ?!LOOP?!
index 475058592d157da39a40ff32231a91f62a5d186a..09dc78733c009d386a7da4981aee1f3de4c57dda 100644 (file)
@@ -5,9 +5,7 @@
 #include "strbuf.h"
 #include "string-list.h"
 #include "transport.h"
-#include "ref-filter.h"
 #include "remote.h"
-#include "refs.h"
 
 enum input_mode {
        KEY_VALUE_PAIRS,
diff --git a/t/helper/test-ctype.c b/t/helper/test-ctype.c
deleted file mode 100644 (file)
index e5659df..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "test-tool.h"
-
-static int rc;
-
-static void report_error(const char *class, int ch)
-{
-       printf("%s classifies char %d (0x%02x) wrongly\n", class, ch, ch);
-       rc = 1;
-}
-
-static int is_in(const char *s, int ch)
-{
-       /*
-        * We can't find NUL using strchr. Accept it as the first
-        * character in the spec -- there are no empty classes.
-        */
-       if (ch == '\0')
-               return ch == *s;
-       if (*s == '\0')
-               s++;
-       return !!strchr(s, ch);
-}
-
-#define TEST_CLASS(t,s) {                      \
-       int i;                                  \
-       for (i = 0; i < 256; i++) {             \
-               if (is_in(s, i) != t(i))        \
-                       report_error(#t, i);    \
-       }                                       \
-       if (t(EOF))                             \
-               report_error(#t, EOF);          \
-}
-
-#define DIGIT "0123456789"
-#define LOWER "abcdefghijklmnopqrstuvwxyz"
-#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-#define PUNCT "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
-#define ASCII \
-       "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
-       "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
-       "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" \
-       "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" \
-       "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" \
-       "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" \
-       "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" \
-       "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
-#define CNTRL \
-       "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
-       "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
-       "\x7f"
-
-int cmd__ctype(int argc UNUSED, const char **argv UNUSED)
-{
-       TEST_CLASS(isdigit, DIGIT);
-       TEST_CLASS(isspace, " \n\r\t");
-       TEST_CLASS(isalpha, LOWER UPPER);
-       TEST_CLASS(isalnum, LOWER UPPER DIGIT);
-       TEST_CLASS(is_glob_special, "*?[\\");
-       TEST_CLASS(is_regex_special, "$()*+.?[\\^{|");
-       TEST_CLASS(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~");
-       TEST_CLASS(isascii, ASCII);
-       TEST_CLASS(islower, LOWER);
-       TEST_CLASS(isupper, UPPER);
-       TEST_CLASS(iscntrl, CNTRL);
-       TEST_CLASS(ispunct, PUNCT);
-       TEST_CLASS(isxdigit, DIGIT "abcdefABCDEF");
-       TEST_CLASS(isprint, LOWER UPPER DIGIT PUNCT " ");
-
-       return rc;
-}
diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
deleted file mode 100644 (file)
index cac20a7..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * "git fast-rebase" builtin command
- *
- * FAST: Forking Any Subprocesses (is) Taboo
- *
- * This is meant SOLELY as a demo of what is possible.  sequencer.c and
- * rebase.c should be refactored to use the ideas here, rather than attempting
- * to extend this file to replace those (unless Phillip or Dscho say that
- * refactoring is too hard and we need a clean slate, but I'm guessing that
- * refactoring is the better route).
- */
-
-#define USE_THE_INDEX_VARIABLE
-#include "test-tool.h"
-#include "cache-tree.h"
-#include "commit.h"
-#include "environment.h"
-#include "gettext.h"
-#include "hash.h"
-#include "hex.h"
-#include "lockfile.h"
-#include "merge-ort.h"
-#include "object-name.h"
-#include "read-cache-ll.h"
-#include "refs.h"
-#include "revision.h"
-#include "sequencer.h"
-#include "setup.h"
-#include "strvec.h"
-#include "tree.h"
-
-static const char *short_commit_name(struct commit *commit)
-{
-       return repo_find_unique_abbrev(the_repository, &commit->object.oid,
-                                      DEFAULT_ABBREV);
-}
-
-static struct commit *peel_committish(const char *name)
-{
-       struct object *obj;
-       struct object_id oid;
-
-       if (repo_get_oid(the_repository, name, &oid))
-               return NULL;
-       obj = parse_object(the_repository, &oid);
-       return (struct commit *)repo_peel_to_type(the_repository, name, 0, obj,
-                                                 OBJ_COMMIT);
-}
-
-static char *get_author(const char *message)
-{
-       size_t len;
-       const char *a;
-
-       a = find_commit_header(message, "author", &len);
-       if (a)
-               return xmemdupz(a, len);
-
-       return NULL;
-}
-
-static struct commit *create_commit(struct tree *tree,
-                                   struct commit *based_on,
-                                   struct commit *parent)
-{
-       struct object_id ret;
-       struct object *obj;
-       struct commit_list *parents = NULL;
-       char *author;
-       char *sign_commit = NULL;
-       struct commit_extra_header *extra;
-       struct strbuf msg = STRBUF_INIT;
-       const char *out_enc = get_commit_output_encoding();
-       const char *message = repo_logmsg_reencode(the_repository, based_on,
-                                                  NULL, out_enc);
-       const char *orig_message = NULL;
-       const char *exclude_gpgsig[] = { "gpgsig", NULL };
-
-       commit_list_insert(parent, &parents);
-       extra = read_commit_extra_headers(based_on, exclude_gpgsig);
-       find_commit_subject(message, &orig_message);
-       strbuf_addstr(&msg, orig_message);
-       author = get_author(message);
-       reset_ident_date();
-       if (commit_tree_extended(msg.buf, msg.len, &tree->object.oid, parents,
-                                &ret, author, NULL, sign_commit, extra)) {
-               error(_("failed to write commit object"));
-               return NULL;
-       }
-       free(author);
-       strbuf_release(&msg);
-
-       obj = parse_object(the_repository, &ret);
-       return (struct commit *)obj;
-}
-
-int cmd__fast_rebase(int argc, const char **argv)
-{
-       struct commit *onto;
-       struct commit *last_commit = NULL, *last_picked_commit = NULL;
-       struct object_id head;
-       struct lock_file lock = LOCK_INIT;
-       struct strvec rev_walk_args = STRVEC_INIT;
-       struct rev_info revs;
-       struct commit *commit;
-       struct merge_options merge_opt;
-       struct tree *next_tree, *base_tree, *head_tree;
-       struct merge_result result;
-       struct strbuf reflog_msg = STRBUF_INIT;
-       struct strbuf branch_name = STRBUF_INIT;
-       int ret = 0;
-
-       /*
-        * test-tool stuff doesn't set up the git directory by default; need to
-        * do that manually.
-        */
-       setup_git_directory();
-
-       if (argc == 2 && !strcmp(argv[1], "-h")) {
-               printf("Sorry, I am not a psychiatrist; I can not give you the help you need.  Oh, you meant usage...\n");
-               exit(129);
-       }
-
-       if (argc != 5 || strcmp(argv[1], "--onto"))
-               die("usage: read the code, figure out how to use it, then do so");
-
-       onto = peel_committish(argv[2]);
-       strbuf_addf(&branch_name, "refs/heads/%s", argv[4]);
-
-       /* Sanity check */
-       if (repo_get_oid(the_repository, "HEAD", &head))
-               die(_("Cannot read HEAD"));
-       assert(oideq(&onto->object.oid, &head));
-
-       repo_hold_locked_index(the_repository, &lock, LOCK_DIE_ON_ERROR);
-       if (repo_read_index(the_repository) < 0)
-               BUG("Could not read index");
-
-       repo_init_revisions(the_repository, &revs, NULL);
-       revs.verbose_header = 1;
-       revs.max_parents = 1;
-       revs.cherry_mark = 1;
-       revs.limited = 1;
-       revs.reverse = 1;
-       revs.right_only = 1;
-       revs.sort_order = REV_SORT_IN_GRAPH_ORDER;
-       revs.topo_order = 1;
-       strvec_pushl(&rev_walk_args, "", argv[4], "--not", argv[3], NULL);
-
-       if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) {
-               ret = error(_("unhandled options"));
-               goto cleanup;
-       }
-
-       strvec_clear(&rev_walk_args);
-
-       if (prepare_revision_walk(&revs) < 0) {
-               ret = error(_("error preparing revisions"));
-               goto cleanup;
-       }
-
-       init_merge_options(&merge_opt, the_repository);
-       memset(&result, 0, sizeof(result));
-       merge_opt.show_rename_progress = 1;
-       merge_opt.branch1 = "HEAD";
-       head_tree = repo_get_commit_tree(the_repository, onto);
-       result.tree = head_tree;
-       last_commit = onto;
-       while ((commit = get_revision(&revs))) {
-               struct commit *base;
-
-               fprintf(stderr, "Rebasing %s...\r",
-                       oid_to_hex(&commit->object.oid));
-               assert(commit->parents && !commit->parents->next);
-               base = commit->parents->item;
-
-               next_tree = repo_get_commit_tree(the_repository, commit);
-               base_tree = repo_get_commit_tree(the_repository, base);
-
-               merge_opt.branch2 = short_commit_name(commit);
-               merge_opt.ancestor = xstrfmt("parent of %s", merge_opt.branch2);
-
-               merge_incore_nonrecursive(&merge_opt,
-                                         base_tree,
-                                         result.tree,
-                                         next_tree,
-                                         &result);
-
-               free((char*)merge_opt.ancestor);
-               merge_opt.ancestor = NULL;
-               if (!result.clean)
-                       break;
-               last_picked_commit = commit;
-               last_commit = create_commit(result.tree, commit, last_commit);
-       }
-
-       merge_switch_to_result(&merge_opt, head_tree, &result, 1, !result.clean);
-
-       if (result.clean < 0)
-               exit(128);
-
-       if (result.clean) {
-               fprintf(stderr, "\nDone.\n");
-               strbuf_addf(&reflog_msg, "finish rebase %s onto %s",
-                           oid_to_hex(&last_picked_commit->object.oid),
-                           oid_to_hex(&last_commit->object.oid));
-               if (update_ref(reflog_msg.buf, branch_name.buf,
-                              &last_commit->object.oid,
-                              &last_picked_commit->object.oid,
-                              REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
-                       error(_("could not update %s"), argv[4]);
-                       die("Failed to update %s", argv[4]);
-               }
-               if (create_symref("HEAD", branch_name.buf, reflog_msg.buf) < 0)
-                       die(_("unable to update HEAD"));
-
-               prime_cache_tree(the_repository, the_repository->index,
-                                result.tree);
-       } else {
-               fprintf(stderr, "\nAborting: Hit a conflict.\n");
-               strbuf_addf(&reflog_msg, "rebase progress up to %s",
-                           oid_to_hex(&last_picked_commit->object.oid));
-               if (update_ref(reflog_msg.buf, "HEAD",
-                              &last_commit->object.oid,
-                              &head,
-                              REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
-                       error(_("could not update %s"), argv[4]);
-                       die("Failed to update %s", argv[4]);
-               }
-       }
-       if (write_locked_index(&the_index, &lock,
-                              COMMIT_LOCK | SKIP_IF_UNCHANGED))
-               die(_("unable to write %s"), get_index_file());
-
-       ret = (result.clean == 0);
-cleanup:
-       strbuf_release(&reflog_msg);
-       strbuf_release(&branch_name);
-       release_revisions(&revs);
-       return ret;
-}
index f4d134a145214f3964e781db949c05cf51f32fc5..4daa82f00fcb5f0b178aa912033c906fe14c7718 100644 (file)
@@ -1,7 +1,9 @@
 #include "git-compat-util.h"
 #include "test-tool.h"
 #include "pkt-line.h"
+#include "sideband.h"
 #include "write-or-die.h"
+#include "parse-options.h"
 
 static void pack_line(const char *line)
 {
@@ -64,12 +66,33 @@ static void unpack(void)
        }
 }
 
-static void unpack_sideband(void)
+static void unpack_sideband(int argc, const char **argv)
 {
        struct packet_reader reader;
-       packet_reader_init(&reader, 0, NULL, 0,
-                          PACKET_READ_GENTLE_ON_EOF |
-                          PACKET_READ_CHOMP_NEWLINE);
+       int options = PACKET_READ_GENTLE_ON_EOF;
+       int chomp_newline = 1;
+       int reader_use_sideband = 0;
+       const char *const unpack_sideband_usage[] = {
+               "test_tool unpack_sideband [options...]", NULL
+       };
+       struct option cmd_options[] = {
+               OPT_BOOL(0, "reader-use-sideband", &reader_use_sideband,
+                        "set use_sideband bit for packet reader (Default: off)"),
+               OPT_BOOL(0, "chomp-newline", &chomp_newline,
+                        "chomp newline in packet (Default: on)"),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, "", cmd_options, unpack_sideband_usage,
+                            0);
+       if (argc > 0)
+               usage_msg_opt(_("too many arguments"), unpack_sideband_usage,
+                             cmd_options);
+
+       if (chomp_newline)
+               options |= PACKET_READ_CHOMP_NEWLINE;
+       packet_reader_init(&reader, 0, NULL, 0, options);
+       reader.use_sideband = reader_use_sideband;
 
        while (packet_reader_read(&reader) != PACKET_READ_EOF) {
                int band;
@@ -79,6 +102,17 @@ static void unpack_sideband(void)
                case PACKET_READ_EOF:
                        break;
                case PACKET_READ_NORMAL:
+                       /*
+                        * When the "use_sideband" field of the reader is turned
+                        * on, sideband packets other than the payload have been
+                        * parsed and consumed in packet_reader_read(), and only
+                        * the payload arrives here.
+                        */
+                       if (reader.use_sideband) {
+                               write_or_die(1, reader.line, reader.pktlen - 1);
+                               break;
+                       }
+
                        band = reader.line[0] & 0xff;
                        if (band < 1 || band > 2)
                                continue; /* skip non-sideband packets */
@@ -97,15 +131,31 @@ static void unpack_sideband(void)
 
 static int send_split_sideband(void)
 {
+       const char *foo = "Foo.\n";
+       const char *bar = "Bar.\n";
        const char *part1 = "Hello,";
        const char *primary = "\001primary: regular output\n";
        const char *part2 = " world!\n";
 
+       /* Each sideband message has a trailing newline character. */
+       send_sideband(1, 2, foo, strlen(foo), LARGE_PACKET_MAX);
+       send_sideband(1, 2, bar, strlen(bar), LARGE_PACKET_MAX);
+
+       /*
+        * One sideband message is divided into part1 and part2
+        * by the primary message.
+        */
        send_sideband(1, 2, part1, strlen(part1), LARGE_PACKET_MAX);
        packet_write(1, primary, strlen(primary));
        send_sideband(1, 2, part2, strlen(part2), LARGE_PACKET_MAX);
        packet_response_end(1);
 
+       /*
+        * We use unpack_sideband() to consume packets. A flush packet
+        * is required to end parsing.
+        */
+       packet_flush(1);
+
        return 0;
 }
 
@@ -126,7 +176,7 @@ int cmd__pkt_line(int argc, const char **argv)
        else if (!strcmp(argv[1], "unpack"))
                unpack();
        else if (!strcmp(argv[1], "unpack-sideband"))
-               unpack_sideband();
+               unpack_sideband(argc - 1, argv + 1);
        else if (!strcmp(argv[1], "send-split-sideband"))
                send_split_sideband();
        else if (!strcmp(argv[1], "receive-sideband"))
index 3e173399a00f5296a70aa293107dfb4db94b1633..1e159a754db6db5d02eaa81072b0ab19fa863004 100644 (file)
@@ -1,11 +1,9 @@
 #include "test-tool.h"
 #include "commit.h"
 #include "commit-reach.h"
-#include "config.h"
 #include "gettext.h"
 #include "hex.h"
 #include "object-name.h"
-#include "parse-options.h"
 #include "ref-filter.h"
 #include "setup.h"
 #include "string-list.h"
index e9a444ddba55b49c9c47ceb3b27a31d3e022a8dc..4acae41bb993c883970227f394a61603a6e7152d 100644 (file)
@@ -6,6 +6,7 @@
 #include "pack-bitmap.h"
 #include "packfile.h"
 #include "setup.h"
+#include "gettext.h"
 
 static int read_midx_file(const char *object_dir, int show_objects)
 {
@@ -79,7 +80,7 @@ static int read_midx_checksum(const char *object_dir)
 static int read_midx_preferred_pack(const char *object_dir)
 {
        struct multi_pack_index *midx = NULL;
-       struct bitmap_index *bitmap = NULL;
+       uint32_t preferred_pack;
 
        setup_git_directory();
 
@@ -87,23 +88,45 @@ static int read_midx_preferred_pack(const char *object_dir)
        if (!midx)
                return 1;
 
-       bitmap = prepare_bitmap_git(the_repository);
-       if (!bitmap)
+       if (midx_preferred_pack(midx, &preferred_pack) < 0) {
+               warning(_("could not determine MIDX preferred pack"));
                return 1;
-       if (!bitmap_is_midx(bitmap)) {
-               free_bitmap_index(bitmap);
+       }
+
+       printf("%s\n", midx->pack_names[preferred_pack]);
+       return 0;
+}
+
+static int read_midx_bitmapped_packs(const char *object_dir)
+{
+       struct multi_pack_index *midx = NULL;
+       struct bitmapped_pack pack;
+       uint32_t i;
+
+       setup_git_directory();
+
+       midx = load_multi_pack_index(object_dir, 1);
+       if (!midx)
                return 1;
+
+       for (i = 0; i < midx->num_packs; i++) {
+               if (nth_bitmapped_pack(the_repository, midx, &pack, i) < 0)
+                       return 1;
+
+               printf("%s\n", pack_basename(pack.p));
+               printf("  bitmap_pos: %"PRIuMAX"\n", (uintmax_t)pack.bitmap_pos);
+               printf("  bitmap_nr: %"PRIuMAX"\n", (uintmax_t)pack.bitmap_nr);
        }
 
-       printf("%s\n", midx->pack_names[midx_preferred_pack(bitmap)]);
-       free_bitmap_index(bitmap);
+       close_midx(midx);
+
        return 0;
 }
 
 int cmd__read_midx(int argc, const char **argv)
 {
        if (!(argc == 2 || argc == 3))
-               usage("read-midx [--show-objects|--checksum|--preferred-pack] <object-dir>");
+               usage("read-midx [--show-objects|--checksum|--preferred-pack|--bitmap] <object-dir>");
 
        if (!strcmp(argv[1], "--show-objects"))
                return read_midx_file(argv[2], 1);
@@ -111,5 +134,7 @@ int cmd__read_midx(int argc, const char **argv)
                return read_midx_checksum(argv[2]);
        else if (!strcmp(argv[1], "--preferred-pack"))
                return read_midx_preferred_pack(argv[2]);
+       else if (!strcmp(argv[1], "--bitmap"))
+               return read_midx_bitmapped_packs(argv[2]);
        return read_midx_file(argv[1], 0);
 }
index 48552e6a9e0049365185499c02d9b1c7d1069d0c..702ec1f128ad42443bc093614dee971c7106af77 100644 (file)
@@ -298,16 +298,19 @@ static int cmd_update_ref(struct ref_store *refs, const char **argv)
        const char *new_sha1_buf = notnull(*argv++, "new-sha1");
        const char *old_sha1_buf = notnull(*argv++, "old-sha1");
        unsigned int flags = arg_flags(*argv++, "flags", transaction_flags);
-       struct object_id old_oid;
+       struct object_id old_oid, *old_oid_ptr = NULL;
        struct object_id new_oid;
 
-       if (get_oid_hex(old_sha1_buf, &old_oid))
-               die("cannot parse %s as %s", old_sha1_buf, the_hash_algo->name);
+       if (*old_sha1_buf) {
+               if (get_oid_hex(old_sha1_buf, &old_oid))
+                       die("cannot parse %s as %s", old_sha1_buf, the_hash_algo->name);
+               old_oid_ptr = &old_oid;
+       }
        if (get_oid_hex(new_sha1_buf, &new_oid))
                die("cannot parse %s as %s", new_sha1_buf, the_hash_algo->name);
 
        return refs_update_ref(refs, msg, refname,
-                              &new_oid, &old_oid,
+                              &new_oid, old_oid_ptr,
                               flags, UPDATE_REFS_DIE_ON_ERR);
 }
 
index bd871a735b4fbb19ed99e9bfab56f13d138a0567..80042eafc20603e0c84cb83d3ba400cd316010c7 100644 (file)
@@ -30,7 +30,7 @@ static int test_regex_bug(void)
        if (regexec(&r, str, 1, m, 0))
                die("no match of pattern '%s' to string '%s'", pat, str);
 
-       /* http://sourceware.org/bugzilla/show_bug.cgi?id=3957  */
+       /* https://sourceware.org/bugzilla/show_bug.cgi?id=3957 */
        if (m[0].rm_so == 3) /* matches '\n' when it should not */
                die("regex bug confirmed: re-build git with NO_REGEX=1");
 
index 4cd8a952e5c6f55fbb3cfdbffe8c3be07bf008d7..0c7c5aa4dd7641a72b930b57eeff17dbf90c4c16 100644 (file)
@@ -1,10 +1,8 @@
 #include "test-tool.h"
 #include "commit-graph.h"
 #include "commit.h"
-#include "config.h"
 #include "environment.h"
 #include "hex.h"
-#include "object-store-ll.h"
 #include "object.h"
 #include "repository.h"
 #include "setup.h"
index 941ae7e3bcf3eda6e88148d9ff9d923aea707ada..fb5927775daef80040ea9675742aa2cb47d46d4e 100644 (file)
@@ -4,7 +4,6 @@
 
 #include "test-tool.h"
 #include "gettext.h"
-#include "strbuf.h"
 #include "simple-ipc.h"
 #include "parse-options.h"
 #include "thread-utils.h"
index 356e0a26c5a0b87714d76f608a917807f69599f5..7197969a08149699a14c758ceef1223e494b7c35 100644 (file)
@@ -4,16 +4,24 @@
 #include "remote.h"
 #include "repository.h"
 #include "setup.h"
+#include "strbuf.h"
 #include "submodule-config.h"
 #include "submodule.h"
 
 #define TEST_TOOL_CHECK_NAME_USAGE \
-       "test-tool submodule check-name <name>"
+       "test-tool submodule check-name"
 static const char *submodule_check_name_usage[] = {
        TEST_TOOL_CHECK_NAME_USAGE,
        NULL
 };
 
+#define TEST_TOOL_CHECK_URL_USAGE \
+       "test-tool submodule check-url"
+static const char *submodule_check_url_usage[] = {
+       TEST_TOOL_CHECK_URL_USAGE,
+       NULL
+};
+
 #define TEST_TOOL_IS_ACTIVE_USAGE \
        "test-tool submodule is-active <name>"
 static const char *submodule_is_active_usage[] = {
@@ -30,31 +38,26 @@ static const char *submodule_resolve_relative_url_usage[] = {
 
 static const char *submodule_usage[] = {
        TEST_TOOL_CHECK_NAME_USAGE,
+       TEST_TOOL_CHECK_URL_USAGE,
        TEST_TOOL_IS_ACTIVE_USAGE,
        TEST_TOOL_RESOLVE_RELATIVE_URL_USAGE,
        NULL
 };
 
+typedef int (*check_fn_t)(const char *);
+
 /*
- * Exit non-zero if any of the submodule names given on the command line is
- * invalid. If no names are given, filter stdin to print only valid names
- * (which is primarily intended for testing).
+ * Apply 'check_fn' to each line of stdin, printing values that pass the check
+ * to stdout.
  */
-static int check_name(int argc, const char **argv)
+static int check_submodule(check_fn_t check_fn)
 {
-       if (argc > 1) {
-               while (*++argv) {
-                       if (check_submodule_name(*argv) < 0)
-                               return 1;
-               }
-       } else {
-               struct strbuf buf = STRBUF_INIT;
-               while (strbuf_getline(&buf, stdin) != EOF) {
-                       if (!check_submodule_name(buf.buf))
-                               printf("%s\n", buf.buf);
-               }
-               strbuf_release(&buf);
+       struct strbuf buf = STRBUF_INIT;
+       while (strbuf_getline(&buf, stdin) != EOF) {
+               if (!check_fn(buf.buf))
+                       printf("%s\n", buf.buf);
        }
+       strbuf_release(&buf);
        return 0;
 }
 
@@ -68,7 +71,20 @@ static int cmd__submodule_check_name(int argc, const char **argv)
        if (argc)
                usage_with_options(submodule_check_name_usage, options);
 
-       return check_name(argc, argv);
+       return check_submodule(check_submodule_name);
+}
+
+static int cmd__submodule_check_url(int argc, const char **argv)
+{
+       struct option options[] = {
+               OPT_END()
+       };
+       argc = parse_options(argc, argv, "test-tools", options,
+                            submodule_check_url_usage, 0);
+       if (argc)
+               usage_with_options(submodule_check_url_usage, options);
+
+       return check_submodule(check_submodule_url);
 }
 
 static int cmd__submodule_is_active(int argc, const char **argv)
@@ -194,6 +210,7 @@ static int cmd__submodule_config_writeable(int argc, const char **argv UNUSED)
 
 static struct test_cmd cmds[] = {
        { "check-name", cmd__submodule_check_name },
+       { "check-url", cmd__submodule_check_url },
        { "is-active", cmd__submodule_is_active },
        { "resolve-relative-url", cmd__submodule_resolve_relative_url},
        { "config-list", cmd__submodule_config_list },
index 876cd2dc313a73e48b55ba79488dbf2ea5887fa1..33b9501c2112a758c1f662890d31fcbce93b854f 100644 (file)
@@ -19,7 +19,6 @@ static struct test_cmd cmds[] = {
        { "config", cmd__config },
        { "crontab", cmd__crontab },
        { "csprng", cmd__csprng },
-       { "ctype", cmd__ctype },
        { "date", cmd__date },
        { "delta", cmd__delta },
        { "dir-iterator", cmd__dir_iterator },
@@ -30,7 +29,6 @@ static struct test_cmd cmds[] = {
        { "dump-untracked-cache", cmd__dump_untracked_cache },
        { "env-helper", cmd__env_helper },
        { "example-decorate", cmd__example_decorate },
-       { "fast-rebase", cmd__fast_rebase },
        { "find-pack", cmd__find_pack },
        { "fsmonitor-client", cmd__fsmonitor_client },
        { "genrandom", cmd__genrandom },
index 70dd4eba11905083f325409955b1b0f8ec18a883..b72f07ded9946b207b0c20da9886d7842cc5ac5c 100644 (file)
@@ -12,7 +12,6 @@ int cmd__chmtime(int argc, const char **argv);
 int cmd__config(int argc, const char **argv);
 int cmd__crontab(int argc, const char **argv);
 int cmd__csprng(int argc, const char **argv);
-int cmd__ctype(int argc, const char **argv);
 int cmd__date(int argc, const char **argv);
 int cmd__delta(int argc, const char **argv);
 int cmd__dir_iterator(int argc, const char **argv);
@@ -24,7 +23,6 @@ int cmd__dump_untracked_cache(int argc, const char **argv);
 int cmd__dump_reftable(int argc, const char **argv);
 int cmd__env_helper(int argc, const char **argv);
 int cmd__example_decorate(int argc, const char **argv);
-int cmd__fast_rebase(int argc, const char **argv);
 int cmd__find_pack(int argc, const char **argv);
 int cmd__fsmonitor_client(int argc, const char **argv);
 int cmd__genrandom(int argc, const char **argv);
index d5ca0046c89fd434e5b4c5c4f28cb24d6a076bd2..1adac29a575254492f206bf4c4e4b6cab08f999d 100644 (file)
@@ -412,6 +412,56 @@ static int ut_201counter(int argc, const char **argv)
        return 0;
 }
 
+static int ut_300redact_start(int argc, const char **argv)
+{
+       if (!argc)
+               die("expect <argv...>");
+
+       trace2_cmd_start(argv);
+
+       return 0;
+}
+
+static int ut_301redact_child_start(int argc, const char **argv)
+{
+       struct child_process cmd = CHILD_PROCESS_INIT;
+       int k;
+
+       if (!argc)
+               die("expect <argv...>");
+
+       for (k = 0; argv[k]; k++)
+               strvec_push(&cmd.args, argv[k]);
+
+       trace2_child_start(&cmd);
+
+       strvec_clear(&cmd.args);
+
+       return 0;
+}
+
+static int ut_302redact_exec(int argc, const char **argv)
+{
+       if (!argc)
+               die("expect <exe> <argv...>");
+
+       trace2_exec(argv[0], &argv[1]);
+
+       return 0;
+}
+
+static int ut_303redact_def_param(int argc, const char **argv)
+{
+       struct key_value_info kvi = KVI_INIT;
+
+       if (argc < 2)
+               die("expect <key> <value>");
+
+       trace2_def_param(argv[0], argv[1], &kvi);
+
+       return 0;
+}
+
 /*
  * Usage:
  *     test-tool trace2 <ut_name_1> <ut_usage_1>
@@ -438,6 +488,11 @@ static struct unit_test ut_table[] = {
 
        { ut_200counter,  "200counter", "<v1> [<v2> [<v3> [...]]]" },
        { ut_201counter,  "201counter", "<v1> <v2> <threads>" },
+
+       { ut_300redact_start,       "300redact_start",       "<argv...>" },
+       { ut_301redact_child_start, "301redact_child_start", "<argv...>" },
+       { ut_302redact_exec,        "302redact_exec",        "<exe> <argv...>" },
+       { ut_303redact_def_param,   "303redact_def_param",   "<key> <value>" },
 };
 /* clang-format on */
 
index 83b83c9abb5089186adfd4ac0b647b6f4c673820..add11e88fc00d7c452f62c0f6da0835fd12f53a2 100644 (file)
@@ -13,7 +13,7 @@ test_lazy_prereq GPG '
        gpg_version=$(gpg --version 2>&1)
        test $? != 127 || exit 1
 
-       # As said here: http://www.gnupg.org/documentation/faqs.html#q6.19
+       # As said here: https://web.archive.org/web/20130212022238/https://www.gnupg.org/faq/gnupg-faq.html#why-does-gnupg-1.0.6-bail-out-on-keyrings-used-with-1.0.7
        # the gpg version 1.0.6 did not parse trust packets correctly, so for
        # that version, creation of signed tags using the generated key fails.
        case "$gpg_version" in
index 5fe3c8ab69d166b79f6c3ebcfe6e1d497ba17d15..d83bafeab32d40d6fd373e084757221ac7181d06 100644 (file)
@@ -55,21 +55,31 @@ fi
 
 HTTPD_PARA=""
 
-for DEFAULT_HTTPD_PATH in '/usr/sbin/httpd' '/usr/sbin/apache2'
+for DEFAULT_HTTPD_PATH in '/usr/sbin/httpd' \
+                         '/usr/sbin/apache2' \
+                         "$(command -v httpd)" \
+                         "$(command -v apache2)"
 do
-       if test -x "$DEFAULT_HTTPD_PATH"
+       if test -n "$DEFAULT_HTTPD_PATH" && test -x "$DEFAULT_HTTPD_PATH"
        then
                break
        fi
 done
 
+if test -x "$DEFAULT_HTTPD_PATH"
+then
+       DETECTED_HTTPD_ROOT="$("$DEFAULT_HTTPD_PATH" -V 2>/dev/null | sed -n 's/^ -D HTTPD_ROOT="\(.*\)"$/\1/p')"
+fi
+
 for DEFAULT_HTTPD_MODULE_PATH in '/usr/libexec/apache2' \
                                 '/usr/lib/apache2/modules' \
                                 '/usr/lib64/httpd/modules' \
                                 '/usr/lib/httpd/modules' \
-                                '/usr/libexec/httpd'
+                                '/usr/libexec/httpd' \
+                                '/usr/lib/apache2' \
+                                "${DETECTED_HTTPD_ROOT:+${DETECTED_HTTPD_ROOT}/modules}"
 do
-       if test -d "$DEFAULT_HTTPD_MODULE_PATH"
+       if test -n "$DEFAULT_HTTPD_MODULE_PATH" && test -d "$DEFAULT_HTTPD_MODULE_PATH"
        then
                break
        fi
@@ -127,6 +137,20 @@ else
                "Could not identify web server at '$LIB_HTTPD_PATH'"
 fi
 
+if test -n "$LIB_HTTPD_DAV" && test -f /etc/os-release
+then
+       case "$(grep "^ID=" /etc/os-release | cut -d= -f2-)" in
+       alpine)
+               # The WebDAV module in Alpine Linux is broken at least up to
+               # Alpine v3.16 as the default DBM driver is missing.
+               #
+               # https://gitlab.alpinelinux.org/alpine/aports/-/issues/13112
+               test_skip_or_die GIT_TEST_HTTPD \
+                       "Apache WebDAV module does not have default DBM backend driver"
+               ;;
+       esac
+fi
+
 install_script () {
        write_script "$HTTPD_ROOT_PATH/$1" <"$TEST_PATH/$1"
 }
index 99a34d648742f669e7b501298a92fab401bc1971..d9c122f34828919da09284529b9e5a70b4ffeec1 100644 (file)
@@ -1 +1 @@
-user@host:xb4E8pqD81KQs
+user@host:$apr1$LGPmCZWj$9vxEwj5Z5GzQLBMxp3mCx1
index 77c25138e074005dbfd21fc726f230be615cd27f..2ad7705d9a3946f92487a8553526af945ce0bc71 100644 (file)
@@ -1 +1 @@
-proxuser:2x7tAukjAED5M
+proxuser:$apr1$RxS6MLkD$DYsqQdflheq4GPNxzJpx5.
diff --git a/t/perf/p5332-multi-pack-reuse.sh b/t/perf/p5332-multi-pack-reuse.sh
new file mode 100755 (executable)
index 0000000..5c6c575
--- /dev/null
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+test_description='tests pack performance with multi-pack reuse'
+
+. ./perf-lib.sh
+. "${TEST_DIRECTORY}/perf/lib-pack.sh"
+
+packdir=.git/objects/pack
+
+test_perf_large_repo
+
+find_pack () {
+       for idx in $packdir/pack-*.idx
+       do
+               if git show-index <$idx | grep -q "$1"
+               then
+                       basename $idx
+               fi || return 1
+       done
+}
+
+repack_into_n_chunks () {
+       git repack -adk &&
+
+       test "$1" -eq 1 && return ||
+
+       find $packdir -type f | sort >packs.before &&
+
+       # partition the repository into $1 chunks of consecutive commits, and
+       # then create $1 packs with the objects reachable from each chunk
+       # (excluding any objects reachable from the previous chunks)
+       sz="$(($(git rev-list --count --all) / $1))"
+       for rev in $(git rev-list --all | awk "NR % $sz == 0" | tac)
+       do
+               pack="$(echo "$rev" | git pack-objects --revs \
+                       --honor-pack-keep --delta-base-offset $packdir/pack)" &&
+               touch $packdir/pack-$pack.keep || return 1
+       done
+
+       # grab any remaining objects not packed by the previous step(s)
+       git pack-objects --revs --all --honor-pack-keep --delta-base-offset \
+               $packdir/pack &&
+
+       find $packdir -type f | sort >packs.after &&
+
+       # and install the whole thing
+       for f in $(comm -12 packs.before packs.after)
+       do
+               rm -f "$f" || return 1
+       done
+       rm -fr $packdir/*.keep
+}
+
+for nr_packs in 1 10 100
+do
+       test_expect_success "create $nr_packs-pack scenario" '
+               repack_into_n_chunks $nr_packs
+       '
+
+       test_expect_success "setup bitmaps for $nr_packs-pack scenario" '
+               find $packdir -type f -name "*.idx" | sed -e "s/.*\/\(.*\)$/+\1/g" |
+               git multi-pack-index write --stdin-packs --bitmap \
+                       --preferred-pack="$(find_pack $(git rev-parse HEAD))"
+       '
+
+       for reuse in single multi
+       do
+               test_perf "clone for $nr_packs-pack scenario ($reuse-pack reuse)" "
+                       git for-each-ref --format='%(objectname)' refs/heads refs/tags >in &&
+                       git -c pack.allowPackReuse=$reuse pack-objects \
+                               --revs --delta-base-offset --use-bitmap-index \
+                               --stdout <in >result
+               "
+
+               test_size "clone size for $nr_packs-pack scenario ($reuse-pack reuse)" '
+                       wc -c <result
+               '
+       done
+done
+
+test_done
diff --git a/t/perf/p6300-for-each-ref.sh b/t/perf/p6300-for-each-ref.sh
new file mode 100755 (executable)
index 0000000..fa7289c
--- /dev/null
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='performance of for-each-ref'
+. ./perf-lib.sh
+
+test_perf_fresh_repo
+
+ref_count_per_type=10000
+test_iteration_count=10
+
+test_expect_success "setup" '
+       test_commit_bulk $(( 1 + $ref_count_per_type )) &&
+
+       # Create refs
+       test_seq $ref_count_per_type |
+               sed "s,.*,update refs/heads/branch_& HEAD~&\nupdate refs/custom/special_& HEAD~&," |
+               git update-ref --stdin &&
+
+       # Create annotated tags
+       for i in $(test_seq $ref_count_per_type)
+       do
+               # Base tags
+               echo "tag tag_$i" &&
+               echo "mark :$i" &&
+               echo "from HEAD~$i" &&
+               printf "tagger %s <%s> %s\n" \
+                       "$GIT_COMMITTER_NAME" \
+                       "$GIT_COMMITTER_EMAIL" \
+                       "$GIT_COMMITTER_DATE" &&
+               echo "data <<EOF" &&
+               echo "tag $i" &&
+               echo "EOF" &&
+
+               # Nested tags
+               echo "tag nested_$i" &&
+               echo "from :$i" &&
+               printf "tagger %s <%s> %s\n" \
+                       "$GIT_COMMITTER_NAME" \
+                       "$GIT_COMMITTER_EMAIL" \
+                       "$GIT_COMMITTER_DATE" &&
+               echo "data <<EOF" &&
+               echo "nested tag $i" &&
+               echo "EOF" || return 1
+       done | git fast-import
+'
+
+test_for_each_ref () {
+       title="for-each-ref"
+       if test $# -gt 0; then
+               title="$title ($1)"
+               shift
+       fi
+       args="$@"
+
+       test_perf "$title" "
+               for i in \$(test_seq $test_iteration_count); do
+                       git for-each-ref $args >/dev/null
+               done
+       "
+}
+
+run_tests () {
+       test_for_each_ref "$1"
+       test_for_each_ref "$1, no sort" --no-sort
+       test_for_each_ref "$1, --count=1" --count=1
+       test_for_each_ref "$1, --count=1, no sort" --no-sort --count=1
+       test_for_each_ref "$1, tags" refs/tags/
+       test_for_each_ref "$1, tags, no sort" --no-sort refs/tags/
+       test_for_each_ref "$1, tags, dereferenced" '--format="%(refname) %(objectname) %(*objectname)"' refs/tags/
+       test_for_each_ref "$1, tags, dereferenced, no sort" --no-sort '--format="%(refname) %(objectname) %(*objectname)"' refs/tags/
+
+       test_perf "for-each-ref ($1, tags) + cat-file --batch-check (dereferenced)" "
+               for i in \$(test_seq $test_iteration_count); do
+                       git for-each-ref --format='%(objectname)^{} %(refname) %(objectname)' refs/tags/ | \
+                               git cat-file --batch-check='%(objectname) %(rest)' >/dev/null
+               done
+       "
+}
+
+run_tests "loose"
+
+test_expect_success 'pack refs' '
+       git pack-refs --all
+'
+run_tests "packed"
+
+test_done
index e7786775a9016151e063335047256bfea93d2f63..ab0c7634119282e6b2914f3ea50064b4daa92574 100644 (file)
@@ -15,7 +15,7 @@
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see http://www.gnu.org/licenses/ .
+# along with this program.  If not, see https://www.gnu.org/licenses/ .
 
 # These variables must be set before the inclusion of test-lib.sh below,
 # because it will change our working directory.
@@ -31,7 +31,7 @@ unset GIT_CONFIG_NOSYSTEM
 GIT_CONFIG_SYSTEM="$TEST_DIRECTORY/perf/config"
 export GIT_CONFIG_SYSTEM
 
-if test -n "$GIT_TEST_INSTALLED" -a -z "$PERF_SET_GIT_TEST_INSTALLED"
+if test -n "$GIT_TEST_INSTALLED" && test -z "$PERF_SET_GIT_TEST_INSTALLED"
 then
        error "Do not use GIT_TEST_INSTALLED with the perf tests.
 
index 34115edec356831d4863a3ca891be0d885bf46fc..486ead21980ec27de640745fe2b67fe869a08b4c 100755 (executable)
@@ -91,10 +91,10 @@ set_git_test_installed () {
 run_dirs_helper () {
        mydir=${1%/}
        shift
-       while test $# -gt 0 -a "$1" != -- -a ! -f "$1"; do
+       while test $# -gt 0 && test "$1" != -- && test ! -f "$1"; do
                shift
        done
-       if test $# -gt 0 -a "$1" = --; then
+       if test $# -gt 0 && test "$1" = --; then
                shift
        fi
 
@@ -124,7 +124,7 @@ run_dirs_helper () {
 }
 
 run_dirs () {
-       while test $# -gt 0 -a "$1" != -- -a ! -f "$1"; do
+       while test $# -gt 0 && test "$1" != -- && test ! -f "$1"; do
                run_dirs_helper "$@"
                shift
        done
@@ -180,7 +180,8 @@ run_subsection () {
        GIT_PERF_AGGREGATING_LATER=t
        export GIT_PERF_AGGREGATING_LATER
 
-       if test $# = 0 -o "$1" = -- -o -f "$1"; then
+       if test $# = 0 || test "$1" = -- || test -f "$1"
+       then
                set -- . "$@"
        fi
 
index 2b78e3be4795096aa3d9eeb5f7b5c3dc3a24f717..b131d665db40e9858d4a3f9c13ab71eb77d6cc5f 100755 (executable)
@@ -532,6 +532,76 @@ test_expect_success 'init rejects attempts to initialize with different hash' '
        test_must_fail git -C sha256 init --object-format=sha1
 '
 
+test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage is not allowed with repo version 0' '
+       test_when_finished "rm -rf refstorage" &&
+       git init refstorage &&
+       git -C refstorage config extensions.refStorage files &&
+       test_must_fail git -C refstorage rev-parse 2>err &&
+       grep "repo version is 0, but v1-only extension found" err
+'
+
+test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage with files backend' '
+       test_when_finished "rm -rf refstorage" &&
+       git init refstorage &&
+       git -C refstorage config core.repositoryformatversion 1 &&
+       git -C refstorage config extensions.refStorage files &&
+       test_commit -C refstorage A &&
+       git -C refstorage rev-parse --verify HEAD
+'
+
+test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage with unknown backend' '
+       test_when_finished "rm -rf refstorage" &&
+       git init refstorage &&
+       git -C refstorage config core.repositoryformatversion 1 &&
+       git -C refstorage config extensions.refStorage garbage &&
+       test_must_fail git -C refstorage rev-parse 2>err &&
+       grep "invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}garbage${SQ}" err
+'
+
+test_expect_success DEFAULT_REPO_FORMAT 'init with GIT_DEFAULT_REF_FORMAT=files' '
+       test_when_finished "rm -rf refformat" &&
+       GIT_DEFAULT_REF_FORMAT=files git init refformat &&
+       echo 0 >expect &&
+       git -C refformat config core.repositoryformatversion >actual &&
+       test_cmp expect actual &&
+       test_must_fail git -C refformat config extensions.refstorage
+'
+
+test_expect_success 'init with GIT_DEFAULT_REF_FORMAT=garbage' '
+       test_when_finished "rm -rf refformat" &&
+       cat >expect <<-EOF &&
+       fatal: unknown ref storage format ${SQ}garbage${SQ}
+       EOF
+       test_must_fail env GIT_DEFAULT_REF_FORMAT=garbage git init refformat 2>err &&
+       test_cmp expect err
+'
+
+test_expect_success 'init with --ref-format=files' '
+       test_when_finished "rm -rf refformat" &&
+       git init --ref-format=files refformat &&
+       echo files >expect &&
+       git -C refformat rev-parse --show-ref-format >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 're-init with same format' '
+       test_when_finished "rm -rf refformat" &&
+       git init --ref-format=files refformat &&
+       git init --ref-format=files refformat &&
+       echo files >expect &&
+       git -C refformat rev-parse --show-ref-format >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'init with --ref-format=garbage' '
+       test_when_finished "rm -rf refformat" &&
+       cat >expect <<-EOF &&
+       fatal: unknown ref storage format ${SQ}garbage${SQ}
+       EOF
+       test_must_fail git init --ref-format=garbage refformat 2>err &&
+       test_cmp expect err
+'
+
 test_expect_success MINGW 'core.hidedotfiles = false' '
        git config --global core.hidedotfiles false &&
        rm -rf newdir &&
index aee2298f01331acdf3d73c1657f1a29ed1c8616b..774b52c2980ca10e6183cf74ad10427f00c2514b 100755 (executable)
@@ -19,6 +19,20 @@ attr_check () {
        test_must_be_empty err
 }
 
+attr_check_object_mode_basic () {
+       path="$1" &&
+       expect="$2" &&
+       check_opts="$3" &&
+       git check-attr $check_opts builtin_objectmode -- "$path" >actual 2>err &&
+       echo "$path: builtin_objectmode: $expect" >expect &&
+       test_cmp expect actual
+}
+
+attr_check_object_mode () {
+       attr_check_object_mode_basic "$@" &&
+       test_must_be_empty err
+}
+
 attr_check_quote () {
        path="$1" quoted_path="$2" expect="$3" &&
 
@@ -558,4 +572,66 @@ test_expect_success EXPENSIVE 'large attributes file ignored in index' '
        test_cmp expect err
 '
 
+test_expect_success 'builtin object mode attributes work (dir and regular paths)' '
+       >normal &&
+       attr_check_object_mode normal 100644 &&
+       mkdir dir &&
+       attr_check_object_mode dir 040000
+'
+
+test_expect_success POSIXPERM 'builtin object mode attributes work (executable)' '
+       >exec &&
+       chmod +x exec &&
+       attr_check_object_mode exec 100755
+'
+
+test_expect_success SYMLINKS 'builtin object mode attributes work (symlinks)' '
+       ln -s to_sym sym &&
+       attr_check_object_mode sym 120000
+'
+
+test_expect_success 'native object mode attributes work with --cached' '
+       >normal &&
+       git add normal &&
+       empty_blob=$(git rev-parse :normal) &&
+       git update-index --index-info <<-EOF &&
+       100755 $empty_blob 0    exec
+       120000 $empty_blob 0    symlink
+       EOF
+       attr_check_object_mode normal 100644 --cached &&
+       attr_check_object_mode exec 100755 --cached &&
+       attr_check_object_mode symlink 120000 --cached
+'
+
+test_expect_success 'check object mode attributes work for submodules' '
+       mkdir sub &&
+       (
+               cd sub &&
+               git init &&
+               mv .git .real &&
+               echo "gitdir: .real" >.git &&
+               test_commit first
+       ) &&
+       attr_check_object_mode sub 160000 &&
+       attr_check_object_mode sub unspecified --cached &&
+       git add sub &&
+       attr_check_object_mode sub 160000 --cached
+'
+
+test_expect_success 'we do not allow user defined builtin_* attributes' '
+       echo "foo* builtin_foo" >.gitattributes &&
+       git add .gitattributes 2>actual &&
+       echo "builtin_foo is not a valid attribute name: .gitattributes:1" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'user defined builtin_objectmode values are ignored' '
+       echo "foo* builtin_objectmode=12345" >.gitattributes &&
+       git add .gitattributes &&
+       >foo_1 &&
+       attr_check_object_mode_basic foo_1 100644 &&
+       echo "builtin_objectmode is not a valid attribute name: .gitattributes:1" >expect &&
+       test_cmp expect err
+'
+
 test_done
index a34de5642073d1323b37b7c82f56edfe798fcfdd..a7f4de4a43ffa022ae56ebf14999cbe81900a4a9 100755 (executable)
@@ -9,7 +9,7 @@ test_expect_success setup '
 
        git config core.autocrlf true &&
 
-       printf "CRLF line ending\r\nAnd another\r\n" > sample &&
+       printf "CRLF line ending\r\nAnd another\r\n" >sample &&
        git add sample &&
 
        test_tick &&
@@ -19,8 +19,9 @@ test_expect_success setup '
 
 test_expect_success 'tar archive' '
 
-       git archive --format=tar HEAD |
-       ( mkdir untarred && cd untarred && "$TAR" -xf - ) &&
+       git archive --format=tar HEAD >test.tar &&
+       mkdir untarred &&
+       "$TAR" xf test.tar -C untarred &&
 
        test_cmp sample untarred/sample
 
@@ -30,7 +31,11 @@ test_expect_success UNZIP 'zip archive' '
 
        git archive --format=zip HEAD >test.zip &&
 
-       ( mkdir unzipped && cd unzipped && "$GIT_UNZIP" ../test.zip ) &&
+       mkdir unzipped &&
+       (
+               cd unzipped &&
+               "$GIT_UNZIP" ../test.zip
+       ) &&
 
        test_cmp sample unzipped/sample
 
index 8fdef88b65e77bd03b9db8fc287914022ab10848..ec974867e4337ca503cb817fd79926193c518710 100755 (executable)
@@ -376,7 +376,7 @@ test_expect_success 'OPT_CMDMODE() detects incompatibility (1)' '
        test_must_be_empty output &&
        test_grep "mode1" output.err &&
        test_grep "mode2" output.err &&
-       test_grep "is incompatible with" output.err
+       test_grep "cannot be used together" output.err
 '
 
 test_expect_success 'OPT_CMDMODE() detects incompatibility (2)' '
@@ -384,7 +384,7 @@ test_expect_success 'OPT_CMDMODE() detects incompatibility (2)' '
        test_must_be_empty output &&
        test_grep "mode2" output.err &&
        test_grep "set23" output.err &&
-       test_grep "is incompatible with" output.err
+       test_grep "cannot be used together" output.err
 '
 
 test_expect_success 'OPT_CMDMODE() detects incompatibility (3)' '
@@ -392,7 +392,7 @@ test_expect_success 'OPT_CMDMODE() detects incompatibility (3)' '
        test_must_be_empty output &&
        test_grep "mode2" output.err &&
        test_grep "set23" output.err &&
-       test_grep "is incompatible with" output.err
+       test_grep "cannot be used together" output.err
 '
 
 test_expect_success 'OPT_CMDMODE() detects incompatibility (4)' '
@@ -401,7 +401,7 @@ test_expect_success 'OPT_CMDMODE() detects incompatibility (4)' '
        test_must_be_empty output &&
        test_grep "mode2" output.err &&
        test_grep "mode34.3" output.err &&
-       test_grep "is incompatible with" output.err
+       test_grep "cannot be used together" output.err
 '
 
 test_expect_success 'OPT_COUNTUP() with PARSE_OPT_NODASH works' '
index 487bc8d9054c908f97bc376005c3fd1eedd107b9..0ecec2ba71116959fd2efceac7fc46683d1a3e91 100755 (executable)
@@ -9,10 +9,6 @@ Verify wrappers and compatibility functions.
 TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
-test_expect_success 'character classes (isspace, isalpha etc.)' '
-       test-tool ctype
-'
-
 test_expect_success 'mktemp to nonexistent directory prints filename' '
        test_must_fail test-tool mktemp doesnotexist/testXXXXXX 2>err &&
        grep "doesnotexist/test" err
@@ -53,4 +49,62 @@ test_expect_success 'missing sideband designator is reported' '
        test_grep "missing sideband" err
 '
 
+test_expect_success 'unpack-sideband: --no-chomp-newline' '
+       test_when_finished "rm -f expect-out expect-err" &&
+       test-tool pkt-line send-split-sideband >split-sideband &&
+       test-tool pkt-line unpack-sideband \
+               --no-chomp-newline <split-sideband >out 2>err &&
+       cat >expect-out <<-EOF &&
+               primary: regular output
+       EOF
+       cat >expect-err <<-EOF &&
+               Foo.
+               Bar.
+               Hello, world!
+       EOF
+       test_cmp expect-out out &&
+       test_cmp expect-err err
+'
+
+test_expect_success 'unpack-sideband: --chomp-newline (default)' '
+       test_when_finished "rm -f expect-out expect-err" &&
+       test-tool pkt-line send-split-sideband >split-sideband &&
+       test-tool pkt-line unpack-sideband \
+               --chomp-newline <split-sideband >out 2>err &&
+       printf "primary: regular output" >expect-out &&
+       printf "Foo.Bar.Hello, world!" >expect-err &&
+       test_cmp expect-out out &&
+       test_cmp expect-err err
+'
+
+test_expect_success 'unpack-sideband: packet_reader_read() consumes sideband, no chomp payload' '
+       test_when_finished "rm -f expect-out expect-err" &&
+       test-tool pkt-line send-split-sideband >split-sideband &&
+       test-tool pkt-line unpack-sideband \
+               --reader-use-sideband \
+               --no-chomp-newline <split-sideband >out 2>err &&
+       cat >expect-out <<-EOF &&
+               primary: regular output
+       EOF
+       printf "remote: Foo.        \n"           >expect-err &&
+       printf "remote: Bar.        \n"          >>expect-err &&
+       printf "remote: Hello, world!        \n" >>expect-err &&
+       test_cmp expect-out out &&
+       test_cmp expect-err err
+'
+
+test_expect_success 'unpack-sideband: packet_reader_read() consumes sideband, chomp payload' '
+       test_when_finished "rm -f expect-out expect-err" &&
+       test-tool pkt-line send-split-sideband >split-sideband &&
+       test-tool pkt-line unpack-sideband \
+               --reader-use-sideband \
+               --chomp-newline <split-sideband >out 2>err &&
+       printf "primary: regular output" >expect-out &&
+       printf "remote: Foo.        \n"           >expect-err &&
+       printf "remote: Bar.        \n"          >>expect-err &&
+       printf "remote: Hello, world!        \n" >>expect-err &&
+       test_cmp expect-out out &&
+       test_cmp expect-err err
+'
+
 test_done
diff --git a/t/t0080-unit-test-output.sh b/t/t0080-unit-test-output.sh
new file mode 100755 (executable)
index 0000000..961b54b
--- /dev/null
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+test_description='Test the output of the unit test framework'
+
+. ./test-lib.sh
+
+test_expect_success 'TAP output from unit tests' '
+       cat >expect <<-EOF &&
+       ok 1 - passing test
+       ok 2 - passing test and assertion return 1
+       # check "1 == 2" failed at t/unit-tests/t-basic.c:76
+       #    left: 1
+       #   right: 2
+       not ok 3 - failing test
+       ok 4 - failing test and assertion return 0
+       not ok 5 - passing TEST_TODO() # TODO
+       ok 6 - passing TEST_TODO() returns 1
+       # todo check ${SQ}check(x)${SQ} succeeded at t/unit-tests/t-basic.c:25
+       not ok 7 - failing TEST_TODO()
+       ok 8 - failing TEST_TODO() returns 0
+       # check "0" failed at t/unit-tests/t-basic.c:30
+       # skipping test - missing prerequisite
+       # skipping check ${SQ}1${SQ} at t/unit-tests/t-basic.c:32
+       ok 9 - test_skip() # SKIP
+       ok 10 - skipped test returns 1
+       # skipping test - missing prerequisite
+       ok 11 - test_skip() inside TEST_TODO() # SKIP
+       ok 12 - test_skip() inside TEST_TODO() returns 1
+       # check "0" failed at t/unit-tests/t-basic.c:48
+       not ok 13 - TEST_TODO() after failing check
+       ok 14 - TEST_TODO() after failing check returns 0
+       # check "0" failed at t/unit-tests/t-basic.c:56
+       not ok 15 - failing check after TEST_TODO()
+       ok 16 - failing check after TEST_TODO() returns 0
+       # check "!strcmp("\thello\\\\", "there\"\n")" failed at t/unit-tests/t-basic.c:61
+       #    left: "\011hello\\\\"
+       #   right: "there\"\012"
+       # check "!strcmp("NULL", NULL)" failed at t/unit-tests/t-basic.c:62
+       #    left: "NULL"
+       #   right: NULL
+       # check "${SQ}a${SQ} == ${SQ}\n${SQ}" failed at t/unit-tests/t-basic.c:63
+       #    left: ${SQ}a${SQ}
+       #   right: ${SQ}\012${SQ}
+       # check "${SQ}\\\\${SQ} == ${SQ}\\${SQ}${SQ}" failed at t/unit-tests/t-basic.c:64
+       #    left: ${SQ}\\\\${SQ}
+       #   right: ${SQ}\\${SQ}${SQ}
+       not ok 17 - messages from failing string and char comparison
+       # BUG: test has no checks at t/unit-tests/t-basic.c:91
+       not ok 18 - test with no checks
+       ok 19 - test with no checks returns 0
+       1..19
+       EOF
+
+       ! "$GIT_BUILD_DIR"/t/unit-tests/bin/t-basic >actual &&
+       test_cmp expect actual
+'
+
+test_done
index 2cbf7b95907384b4b4b5ac91f801026810b85f0a..47d96a2a13f93b235f6f3d0c9f8f06922f6ee872 100755 (executable)
@@ -1,5 +1,5 @@
 #!/usr/bin/perl
-use 5.008;
+use 5.008001;
 use lib (split(/:/, $ENV{GITPERLLIB}));
 use strict;
 use warnings;
index 80e76a4695ed8d5abbaf8deb1ea2a55d2f535d41..c312657a12cd34334ce58361c7f0a73f384daff6 100755 (executable)
@@ -2,7 +2,7 @@
 
 test_description='test trace2 facility (normal target)'
 
-TEST_PASSES_SANITIZE_LEAK=true
+TEST_PASSES_SANITIZE_LEAK=false
 . ./test-lib.sh
 
 # Turn off any inherited trace2 settings for this test.
@@ -283,4 +283,22 @@ test_expect_success 'using global config with include' '
        test_cmp expect actual
 '
 
+test_expect_success 'unsafe URLs are redacted by default' '
+       test_when_finished \
+               "rm -r trace.normal unredacted.normal clone clone2" &&
+
+       test_config_global \
+               "url.$(pwd).insteadOf" https://user:pwd@example.com/ &&
+       test_config_global trace2.configParams "core.*,remote.*.url" &&
+
+       GIT_TRACE2="$(pwd)/trace.normal" \
+               git clone https://user:pwd@example.com/ clone &&
+       ! grep user:pwd trace.normal &&
+
+       GIT_TRACE2_REDACT=0 GIT_TRACE2="$(pwd)/unredacted.normal" \
+               git clone https://user:pwd@example.com/ clone2 &&
+       grep "start .* clone https://user:pwd@example.com" unredacted.normal &&
+       grep "remote.origin.url=https://user:pwd@example.com" unredacted.normal
+'
+
 test_done
index cfba6861322e373c64897e4c543838b7879cf3e0..290b6eaaab16052b84ca3c4e5527c8910f67ebf0 100755 (executable)
@@ -2,7 +2,7 @@
 
 test_description='test trace2 facility (perf target)'
 
-TEST_PASSES_SANITIZE_LEAK=true
+TEST_PASSES_SANITIZE_LEAK=false
 . ./test-lib.sh
 
 # Turn off any inherited trace2 settings for this test.
@@ -268,4 +268,23 @@ test_expect_success PTHREADS 'global counter test/test2' '
        have_counter_event "main" "counter" "test" "test2" 60 actual
 '
 
+test_expect_success 'unsafe URLs are redacted by default' '
+       test_when_finished \
+               "rm -r actual trace.perf unredacted.perf clone clone2" &&
+
+       test_config_global \
+               "url.$(pwd).insteadOf" https://user:pwd@example.com/ &&
+       test_config_global trace2.configParams "core.*,remote.*.url" &&
+
+       GIT_TRACE2_PERF="$(pwd)/trace.perf" \
+               git clone https://user:pwd@example.com/ clone &&
+       ! grep user:pwd trace.perf &&
+
+       GIT_TRACE2_REDACT=0 GIT_TRACE2_PERF="$(pwd)/unredacted.perf" \
+               git clone https://user:pwd@example.com/ clone2 &&
+       perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <unredacted.perf >actual &&
+       grep "d0|main|start|.* clone https://user:pwd@example.com" actual &&
+       grep "d0|main|def_param|.*|remote.origin.url:https://user:pwd@example.com" actual
+'
+
 test_done
index 6d3374ff773c1ef3b612b2a0df9aa9df4525790e..147643d582643efebf794000fa48e3d460c76173 100755 (executable)
@@ -323,4 +323,44 @@ test_expect_success 'discard traces when there are too many files' '
        head -n2 trace_target_dir/git-trace2-discard | tail -n1 | grep \"event\":\"too_many_files\"
 '
 
+# In the following "...redact..." tests, skip testing the GIT_TRACE2_REDACT=0
+# case because we would need to exactly model the full JSON event stream like
+# we did in the basic tests above and I do not think it is worth it.
+
+test_expect_success 'unsafe URLs are redacted by default in cmd_start events' '
+       test_when_finished \
+               "rm -r trace.event" &&
+
+       GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+               test-tool trace2 300redact_start git clone https://user:pwd@example.com/ clone2 &&
+       ! grep user:pwd trace.event
+'
+
+test_expect_success 'unsafe URLs are redacted by default in child_start events' '
+       test_when_finished \
+               "rm -r trace.event" &&
+
+       GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+               test-tool trace2 301redact_child_start git clone https://user:pwd@example.com/ clone2 &&
+       ! grep user:pwd trace.event
+'
+
+test_expect_success 'unsafe URLs are redacted by default in exec events' '
+       test_when_finished \
+               "rm -r trace.event" &&
+
+       GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+               test-tool trace2 302redact_exec git clone https://user:pwd@example.com/ clone2 &&
+       ! grep user:pwd trace.event
+'
+
+test_expect_success 'unsafe URLs are redacted by default in def_param events' '
+       test_when_finished \
+               "rm -r trace.event" &&
+
+       GIT_TRACE2_EVENT="$(pwd)/trace.event" \
+               test-tool trace2 303redact_def_param url https://user:pwd@example.com/ &&
+       ! grep user:pwd trace.event
+'
+
 test_done
index 5b7bee888d567b5b55e2d4e7c53849db8945fdcb..6b6424b3df17135666b332b7857ec8382c813ece 100755 (executable)
@@ -49,7 +49,7 @@ test_expect_success 'convert shallow clone to partial clone' '
        test_cmp_config -C client 1 core.repositoryformatversion
 '
 
-test_expect_success SHA1 'convert to partial clone with noop extension' '
+test_expect_success SHA1,REFFILES 'convert to partial clone with noop extension' '
        rm -fr server client &&
        test_create_repo server &&
        test_commit -C server my_commit 1 &&
@@ -60,7 +60,7 @@ test_expect_success SHA1 'convert to partial clone with noop extension' '
        git -C client fetch --unshallow --filter="blob:none"
 '
 
-test_expect_success SHA1 'converting to partial clone fails with unrecognized extension' '
+test_expect_success SHA1,REFFILES 'converting to partial clone fails with unrecognized extension' '
        rm -fr server client &&
        test_create_repo server &&
        test_commit -C server my_commit 1 &&
index d73a0be1b9d1bfa3e0f1fd96e69c0e78ad2dec06..e0c6482797e1203a9e97ec27714d5ea48aaf1b6c 100755 (executable)
@@ -6,7 +6,7 @@ test_description='git cat-file'
 
 test_cmdmode_usage () {
        test_expect_code 129 "$@" 2>err &&
-       grep "^error:.*is incompatible with" err
+       grep "^error: .* cannot be used together" err
 }
 
 for switches in \
@@ -1100,6 +1100,42 @@ test_expect_success 'cat-file --batch="batman" with --batch-all-objects will wor
        cmp expect actual
 '
 
+test_expect_success 'cat-file %(objectsize:disk) with --batch-all-objects' '
+       # our state has both loose and packed objects,
+       # so find both for our expected output
+       {
+               find .git/objects/?? -type f |
+               awk -F/ "{ print \$0, \$3\$4 }" |
+               while read path oid
+               do
+                       size=$(test_file_size "$path") &&
+                       echo "$oid $size" ||
+                       return 1
+               done &&
+               rawsz=$(test_oid rawsz) &&
+               find .git/objects/pack -name "*.idx" |
+               while read idx
+               do
+                       git show-index <"$idx" >idx.raw &&
+                       sort -nr <idx.raw >idx.sorted &&
+                       packsz=$(test_file_size "${idx%.idx}.pack") &&
+                       end=$((packsz - rawsz)) &&
+                       while read start oid rest
+                       do
+                               size=$((end - start)) &&
+                               end=$start &&
+                               echo "$oid $size" ||
+                               return 1
+                       done <idx.sorted ||
+                       return 1
+               done
+       } >expect.raw &&
+       sort <expect.raw >expect &&
+       git cat-file --batch-all-objects \
+               --batch-check="%(objectname) %(objectsize:disk)" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'set up replacement object' '
        orig=$(git rev-parse HEAD) &&
        git cat-file commit $orig >orig &&
index f67611da28e6e27125008e5e7073cec8a2fd8752..e49b8024ac53659e17f22eb39e252aba73e56a6b 100755 (executable)
@@ -334,7 +334,7 @@ test_expect_success 'cone mode: set with nested folders' '
 
 test_expect_success 'cone mode: add independent path' '
        git -C repo sparse-checkout set deep/deeper1 &&
-       git -C repo sparse-checkout add folder1 &&
+       git -C repo sparse-checkout add --end-of-options folder1 &&
        cat >expect <<-\EOF &&
        /*
        !/*/
@@ -886,6 +886,12 @@ test_expect_success 'by default, cone mode will error out when passed files' '
        grep ".gitignore.*is not a directory" error
 '
 
+test_expect_success 'error on mistyped command line options' '
+       test_must_fail git -C repo sparse-checkout add --sikp-checks .gitignore 2>error &&
+
+       grep "unknown option.*sikp-checks" error
+'
+
 test_expect_success 'by default, non-cone mode will warn on individual files' '
        git -C repo sparse-checkout reapply --no-cone &&
        git -C repo sparse-checkout add .gitignore 2>warning &&
index 9ac4b7036bf7ec8fe0befca6f08797c83a72c4b0..f18843bf7aa293d26ef1aabc7fbb8f2c1a8dca03 100755 (executable)
@@ -9,8 +9,6 @@ test_description='Test git update-ref and basic ref logging'
 Z=$ZERO_OID
 
 m=refs/heads/main
-n_dir=refs/heads/gu
-n=$n_dir/fixes
 outside=refs/foo
 bare=bare-repo
 
@@ -62,10 +60,10 @@ test_expect_success "delete $m without oldvalue verification" '
        test_must_fail git show-ref --verify -q $m
 '
 
-test_expect_success "fail to create $n" '
-       test_when_finished "rm -f .git/$n_dir" &&
-       touch .git/$n_dir &&
-       test_must_fail git update-ref $n $A
+test_expect_success "fail to create $n due to file/directory conflict" '
+       test_when_finished "git update-ref -d refs/heads/gu" &&
+       git update-ref refs/heads/gu $A &&
+       test_must_fail git update-ref refs/heads/gu/fixes $A
 '
 
 test_expect_success "create $m (by HEAD)" '
@@ -92,7 +90,8 @@ test_expect_success "deleting current branch adds message to HEAD's log" '
        git symbolic-ref HEAD $m &&
        git update-ref -m delete-$m -d $m &&
        test_must_fail git show-ref --verify -q $m &&
-       grep "delete-$m$" .git/logs/HEAD
+       test-tool ref-store main for-each-reflog-ent HEAD >actual &&
+       grep "delete-$m$" actual
 '
 
 test_expect_success "deleting by HEAD adds message to HEAD's log" '
@@ -101,7 +100,8 @@ test_expect_success "deleting by HEAD adds message to HEAD's log" '
        git symbolic-ref HEAD $m &&
        git update-ref -m delete-by-head -d HEAD &&
        test_must_fail git show-ref --verify -q $m &&
-       grep "delete-by-head$" .git/logs/HEAD
+       test-tool ref-store main for-each-reflog-ent HEAD >actual &&
+       grep "delete-by-head$" actual
 '
 
 test_expect_success 'update-ref does not create reflogs by default' '
@@ -132,7 +132,7 @@ test_expect_success 'creates no reflog in bare repository' '
 
 test_expect_success 'core.logAllRefUpdates=true creates reflog in bare repository' '
        test_when_finished "git -C $bare config --unset core.logAllRefUpdates && \
-               rm $bare/logs/$m" &&
+               test-tool ref-store main delete-reflog $m" &&
        git -C $bare config core.logAllRefUpdates true &&
        git -C $bare update-ref $m $bareB &&
        git -C $bare rev-parse $bareB >expect &&
@@ -221,27 +221,27 @@ test_expect_success 'delete symref without dereference when the referred ref is
 '
 
 test_expect_success 'update-ref -d is not confused by self-reference' '
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self" &&
        git symbolic-ref refs/heads/self refs/heads/self &&
-       test_when_finished "rm -f .git/refs/heads/self" &&
-       test_path_is_file .git/refs/heads/self &&
+       git symbolic-ref --no-recurse refs/heads/self &&
        test_must_fail git update-ref -d refs/heads/self &&
-       test_path_is_file .git/refs/heads/self
+       git symbolic-ref --no-recurse refs/heads/self
 '
 
 test_expect_success 'update-ref --no-deref -d can delete self-reference' '
+       test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF refs/heads/self" &&
        git symbolic-ref refs/heads/self refs/heads/self &&
-       test_when_finished "rm -f .git/refs/heads/self" &&
-       test_path_is_file .git/refs/heads/self &&
+       git symbolic-ref --no-recurse refs/heads/self &&
        git update-ref --no-deref -d refs/heads/self &&
        test_must_fail git show-ref --verify -q refs/heads/self
 '
 
-test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' '
+test_expect_success REFFILES 'update-ref --no-deref -d can delete reference to bad ref' '
        >.git/refs/heads/bad &&
        test_when_finished "rm -f .git/refs/heads/bad" &&
        git symbolic-ref refs/heads/ref-to-bad refs/heads/bad &&
        test_when_finished "git update-ref -d refs/heads/ref-to-bad" &&
-       test_path_is_file .git/refs/heads/ref-to-bad &&
+       git symbolic-ref --no-recurse refs/heads/ref-to-bad &&
        git update-ref --no-deref -d refs/heads/ref-to-bad &&
        test_must_fail git show-ref --verify -q refs/heads/ref-to-bad
 '
@@ -265,7 +265,10 @@ test_expect_success "(not) changed .git/$m" '
        ! test $B = $(git show-ref -s --verify $m)
 '
 
-rm -f .git/logs/refs/heads/main
+test_expect_success "clean up reflog" '
+       test-tool ref-store main delete-reflog $m
+'
+
 test_expect_success "create $m (logged by touch)" '
        test_config core.logAllRefUpdates false &&
        GIT_COMMITTER_DATE="2005-05-26 23:30" \
@@ -285,7 +288,7 @@ test_expect_success "set $m (logged by touch)" '
        test $A = $(git show-ref -s --verify $m)
 '
 
-test_expect_success 'empty directory removal' '
+test_expect_success REFFILES 'empty directory removal' '
        git branch d1/d2/r1 HEAD &&
        git branch d1/r2 HEAD &&
        test_path_is_file .git/refs/heads/d1/d2/r1 &&
@@ -297,7 +300,7 @@ test_expect_success 'empty directory removal' '
        test_path_is_file .git/logs/refs/heads/d1/r2
 '
 
-test_expect_success 'symref empty directory removal' '
+test_expect_success REFFILES 'symref empty directory removal' '
        git branch e1/e2/r1 HEAD &&
        git branch e1/r2 HEAD &&
        git checkout e1/e2/r1 &&
@@ -318,7 +321,7 @@ $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000   Switch
 $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000
 EOF
 test_expect_success "verifying $m's log (logged by touch)" '
-       test_when_finished "git update-ref -d $m && rm -rf .git/logs actual expect" &&
+       test_when_finished "git update-ref -d $m && git reflog expire --expire=all --all && rm -rf actual expect" &&
        test-tool ref-store main for-each-reflog-ent $m >actual &&
        test_cmp actual expect
 '
@@ -348,20 +351,34 @@ $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 +0000 Switch
 $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000
 EOF
 test_expect_success "verifying $m's log (logged by config)" '
-       test_when_finished "git update-ref -d $m && rm -rf .git/logs actual expect" &&
+       test_when_finished "git update-ref -d $m && git reflog expire --expire=all --all && rm -rf actual expect" &&
        test-tool ref-store main for-each-reflog-ent $m >actual &&
        test_cmp actual expect
 '
 
 test_expect_success 'set up for querying the reflog' '
+       git update-ref -d $m &&
+       test-tool ref-store main delete-reflog $m &&
+
+       GIT_COMMITTER_DATE="1117150320 -0500" git update-ref $m $C &&
+       GIT_COMMITTER_DATE="1117150350 -0500" git update-ref $m $A &&
+       GIT_COMMITTER_DATE="1117150380 -0500" git update-ref $m $B &&
+       GIT_COMMITTER_DATE="1117150680 -0500" git update-ref $m $F &&
+       GIT_COMMITTER_DATE="1117150980 -0500" git update-ref $m $E &&
        git update-ref $m $D &&
-       cat >.git/logs/$m <<-EOF
+       # Delete the last reflog entry so that the tip of m and the reflog for
+       # it disagree.
+       git reflog delete $m@{0} &&
+
+       cat >expect <<-EOF &&
        $Z $C $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 -0500
        $C $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150350 -0500
        $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 -0500
-       $F $Z $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500
-       $Z $E $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 -0500
+       $B $F $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500
+       $F $E $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 -0500
        EOF
+       test-tool ref-store main for-each-reflog-ent $m >actual &&
+       test_cmp expect actual
 '
 
 ed="Thu, 26 May 2005 18:32:00 -0500"
@@ -409,13 +426,12 @@ test_expect_success 'Query "main@{2005-05-26 23:33:01}" (middle of history with
        test_when_finished "rm -f o e" &&
        git rev-parse --verify "main@{2005-05-26 23:33:01}" >o 2>e &&
        echo "$B" >expect &&
-       test_cmp expect o &&
-       test_grep -F "warning: log for ref $m has gap after $gd" e
+       test_cmp expect o
 '
 test_expect_success 'Query "main@{2005-05-26 23:38:00}" (middle of history)' '
        test_when_finished "rm -f o e" &&
        git rev-parse --verify "main@{2005-05-26 23:38:00}" >o 2>e &&
-       echo "$Z" >expect &&
+       echo "$F" >expect &&
        test_cmp expect o &&
        test_must_be_empty e
 '
@@ -434,7 +450,24 @@ test_expect_success 'Query "main@{2005-05-28}" (past end of history)' '
        test_grep -F "warning: log for ref $m unexpectedly ended on $ld" e
 '
 
-rm -f .git/$m .git/logs/$m expect
+rm -f expect
+git update-ref -d $m
+
+test_expect_success REFFILES 'query reflog with gap' '
+       test_when_finished "git update-ref -d $m" &&
+
+       git update-ref $m $F &&
+       cat >.git/logs/$m <<-EOF &&
+       $Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 -0500
+       $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 -0500
+       $D $F $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500
+       EOF
+
+       git rev-parse --verify "main@{2005-05-26 23:33:01}" >actual 2>stderr &&
+       echo "$B" >expect &&
+       test_cmp expect actual &&
+       test_grep -F "warning: log for ref $m has gap after $gd" stderr
+'
 
 test_expect_success 'creating initial files' '
        test_when_finished rm -f M &&
@@ -1635,7 +1668,7 @@ test_expect_success PIPE 'transaction flushes status updates' '
        test_cmp expected actual
 '
 
-test_expect_success 'directory not created deleting packed ref' '
+test_expect_success REFFILES 'directory not created deleting packed ref' '
        git branch d1/d2/r1 HEAD &&
        git pack-refs --all &&
        test_path_is_missing .git/refs/heads/d1/d2 &&
index c7745e1bf69e910c2d66a61bb8bef36b45473a56..5c60d6f812dc20c1c86f5125b5feabb4ce1df9ce 100755 (executable)
@@ -106,9 +106,8 @@ test_expect_success LONG_REF 'we can parse long symbolic ref' '
 '
 
 test_expect_success 'symbolic-ref reports failure in exit code' '
-       test_when_finished "rm -f .git/HEAD.lock" &&
-       >.git/HEAD.lock &&
-       test_must_fail git symbolic-ref HEAD refs/heads/whatever
+       # Create d/f conflict to simulate failure.
+       test_must_fail git symbolic-ref refs/heads refs/heads/foo
 '
 
 test_expect_success 'symbolic-ref writes reflog entry' '
@@ -171,8 +170,8 @@ test_expect_success 'symbolic-ref refuses invalid target for non-HEAD' '
 '
 
 test_expect_success 'symbolic-ref allows top-level target for non-HEAD' '
-       git symbolic-ref refs/heads/top-level FETCH_HEAD &&
-       git update-ref FETCH_HEAD HEAD &&
+       git symbolic-ref refs/heads/top-level ORIG_HEAD &&
+       git update-ref ORIG_HEAD HEAD &&
        test_cmp_rev top-level HEAD
 '
 
index b50ae6fcf115abe384444db1ef0ddbf04ce247d3..d0a8f7b121cd874562fbf0759670520f4b275b30 100755 (executable)
@@ -197,18 +197,20 @@ test_expect_success 'show-ref --verify with dangling ref' '
 '
 
 test_expect_success 'show-ref sub-modes are mutually exclusive' '
-       cat >expect <<-EOF &&
-       fatal: only one of ${SQ}--exclude-existing${SQ}, ${SQ}--verify${SQ} or ${SQ}--exists${SQ} can be given
-       EOF
-
        test_must_fail git show-ref --verify --exclude-existing 2>err &&
-       test_cmp expect err &&
+       grep "verify" err &&
+       grep "exclude-existing" err &&
+       grep "cannot be used together" err &&
 
        test_must_fail git show-ref --verify --exists 2>err &&
-       test_cmp expect err &&
+       grep "verify" err &&
+       grep "exists" err &&
+       grep "cannot be used together" err &&
 
        test_must_fail git show-ref --exclude-existing --exists 2>err &&
-       test_cmp expect err
+       grep "exclude-existing" err &&
+       grep "exists" err &&
+       grep "cannot be used together" err
 '
 
 test_expect_success '--exists with existing reference' '
@@ -260,10 +262,20 @@ test_expect_success '--exists with non-commit object' '
 
 test_expect_success '--exists with directory fails with generic error' '
        cat >expect <<-EOF &&
-       error: failed to look up reference: Is a directory
+       error: reference does not exist
        EOF
-       test_expect_code 1 git show-ref --exists refs/heads 2>err &&
+       test_expect_code 2 git show-ref --exists refs/heads 2>err &&
        test_cmp expect err
 '
 
+test_expect_success '--exists with non-existent special ref' '
+       test_expect_code 2 git show-ref --exists FETCH_HEAD
+'
+
+test_expect_success '--exists with existing special ref' '
+       test_when_finished "rm .git/FETCH_HEAD" &&
+       git rev-parse HEAD >.git/FETCH_HEAD &&
+       git show-ref --exists FETCH_HEAD
+'
+
 test_done
index aeddc2fb3f69516921733c49ad6211c00bf066d3..a0ff8d51f04b2d9ad34060a4f42290577d6673f7 100755 (executable)
@@ -469,11 +469,11 @@ test_expect_success 'expire one of multiple worktrees' '
        )
 '
 
-test_expect_success REFFILES 'empty reflog' '
+test_expect_success 'empty reflog' '
        test_when_finished "rm -rf empty" &&
        git init empty &&
        test_commit -C empty A &&
-       >empty/.git/logs/refs/heads/foo &&
+       test-tool ref-store main create-reflog refs/heads/foo &&
        git -C empty reflog expire --all 2>err &&
        test_must_be_empty err
 '
index 14f13b57c6d2017d199a5d91d6908665c7f2671d..0eb5e674bc1f0dbc688523dd3f94a5e5bb32b6d7 100755 (executable)
@@ -14,9 +14,13 @@ test_expect_success 'setup' '
                test_commit B &&
                test_commit C &&
 
-               cp .git/logs/HEAD HEAD.old &&
+               git reflog HEAD >expect &&
                git reset --hard HEAD~ &&
-               cp HEAD.old .git/logs/HEAD
+               # Make sure that the reflog does not point to the same commit
+               # as HEAD.
+               git reflog delete HEAD@{0} &&
+               git reflog HEAD >actual &&
+               test_cmp expect actual
        )
 '
 
@@ -25,7 +29,7 @@ test_reflog_updateref () {
        shift
        args="$@"
 
-       test_expect_success REFFILES "get '$exp' with '$args'"  '
+       test_expect_success "get '$exp' with '$args'"  '
                test_when_finished "rm -rf copy" &&
                cp -R repo copy &&
 
index 68cc9e73d0c01bb7b9642b25d33155406662116c..0c00118c2b3f798777bad27a7082e5c0a2f29ea4 100755 (executable)
@@ -164,9 +164,9 @@ test_expect_success 'rev-parse skips symref pointing to broken name' '
 test_expect_success 'for-each-ref emits warnings for broken names' '
        test-tool ref-store main update-ref msg "refs/heads/broken...ref" $main_sha1 $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...ref" &&
-       printf "ref: refs/heads/broken...ref\n" >.git/refs/heads/badname &&
+       test-tool ref-store main create-symref refs/heads/badname refs/heads/broken...ref &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/badname" &&
-       printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
+       test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
        git for-each-ref >output 2>error &&
        ! grep -e "broken\.\.\.ref" output &&
@@ -257,7 +257,7 @@ test_expect_success 'update-ref -d can delete broken name through symref' '
 '
 
 test_expect_success 'update-ref --no-deref -d can delete symref with broken name' '
-       printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
+       test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
        test_ref_exists refs/heads/broken...symref &&
        git update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&
@@ -267,7 +267,7 @@ test_expect_success 'update-ref --no-deref -d can delete symref with broken name
 '
 
 test_expect_success 'branch -d can delete symref with broken name' '
-       printf "ref: refs/heads/main\n" >.git/refs/heads/broken...symref &&
+       test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/main &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
        test_ref_exists refs/heads/broken...symref &&
        git branch -d broken...symref >output 2>error &&
@@ -277,7 +277,7 @@ test_expect_success 'branch -d can delete symref with broken name' '
 '
 
 test_expect_success 'update-ref --no-deref -d can delete dangling symref with broken name' '
-       printf "ref: refs/heads/idonotexist\n" >.git/refs/heads/broken...symref &&
+       test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/idonotexist &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
        test_ref_exists refs/heads/broken...symref &&
        git update-ref --no-deref -d refs/heads/broken...symref >output 2>error &&
@@ -287,7 +287,7 @@ test_expect_success 'update-ref --no-deref -d can delete dangling symref with br
 '
 
 test_expect_success 'branch -d can delete dangling symref with broken name' '
-       printf "ref: refs/heads/idonotexist\n" >.git/refs/heads/broken...symref &&
+       test-tool ref-store main create-symref refs/heads/broken...symref refs/heads/idonotexist &&
        test_when_finished "test-tool ref-store main delete-refs REF_NO_DEREF msg refs/heads/broken...symref" &&
        test_ref_exists refs/heads/broken...symref &&
        git branch -d broken...symref >output 2>error &&
index 0e3e87d37a1d52417e91ff1fc51fc86a468f0c89..8a456b1142d1cc463f97c5e8809bcfc83ffa1e96 100755 (executable)
@@ -15,6 +15,7 @@ test_expect_success setup '
        git config --unset i18n.commitencoding &&
        git checkout HEAD^0 &&
        test_commit B fileB two &&
+       orig_head=$(git rev-parse HEAD) &&
        git tag -d A B &&
        git reflog expire --expire=now --all
 '
@@ -115,15 +116,15 @@ test_expect_success 'zlib corrupt loose object output ' '
 '
 
 test_expect_success 'branch pointing to non-commit' '
-       git rev-parse HEAD^{tree} >.git/refs/heads/invalid &&
+       tree_oid=$(git rev-parse --verify HEAD^{tree}) &&
        test_when_finished "git update-ref -d refs/heads/invalid" &&
+       test-tool ref-store main update-ref msg refs/heads/invalid $tree_oid $ZERO_OID REF_SKIP_OID_VERIFICATION &&
        test_must_fail git fsck 2>out &&
        test_grep "not a commit" out
 '
 
-test_expect_success 'HEAD link pointing at a funny object' '
-       test_when_finished "mv .git/SAVED_HEAD .git/HEAD" &&
-       mv .git/HEAD .git/SAVED_HEAD &&
+test_expect_success REFFILES 'HEAD link pointing at a funny object' '
+       test_when_finished "git update-ref HEAD $orig_head" &&
        echo $ZERO_OID >.git/HEAD &&
        # avoid corrupt/broken HEAD from interfering with repo discovery
        test_must_fail env GIT_DIR=.git git fsck 2>out &&
@@ -131,27 +132,25 @@ test_expect_success 'HEAD link pointing at a funny object' '
 '
 
 test_expect_success 'HEAD link pointing at a funny place' '
-       test_when_finished "mv .git/SAVED_HEAD .git/HEAD" &&
-       mv .git/HEAD .git/SAVED_HEAD &&
-       echo "ref: refs/funny/place" >.git/HEAD &&
+       test_when_finished "git update-ref --no-deref HEAD $orig_head" &&
+       test-tool ref-store main create-symref HEAD refs/funny/place &&
        # avoid corrupt/broken HEAD from interfering with repo discovery
        test_must_fail env GIT_DIR=.git git fsck 2>out &&
        test_grep "HEAD points to something strange" out
 '
 
-test_expect_success 'HEAD link pointing at a funny object (from different wt)' '
-       test_when_finished "mv .git/SAVED_HEAD .git/HEAD" &&
-       test_when_finished "rm -rf .git/worktrees wt" &&
+test_expect_success REFFILES 'HEAD link pointing at a funny object (from different wt)' '
+       test_when_finished "git update-ref HEAD $orig_head" &&
+       test_when_finished "git worktree remove -f wt" &&
        git worktree add wt &&
-       mv .git/HEAD .git/SAVED_HEAD &&
        echo $ZERO_OID >.git/HEAD &&
        # avoid corrupt/broken HEAD from interfering with repo discovery
        test_must_fail git -C wt fsck 2>out &&
        test_grep "main-worktree/HEAD: detached HEAD points" out
 '
 
-test_expect_success 'other worktree HEAD link pointing at a funny object' '
-       test_when_finished "rm -rf .git/worktrees other" &&
+test_expect_success REFFILES 'other worktree HEAD link pointing at a funny object' '
+       test_when_finished "git worktree remove -f other" &&
        git worktree add other &&
        echo $ZERO_OID >.git/worktrees/other/HEAD &&
        test_must_fail git fsck 2>out &&
@@ -159,17 +158,18 @@ test_expect_success 'other worktree HEAD link pointing at a funny object' '
 '
 
 test_expect_success 'other worktree HEAD link pointing at missing object' '
-       test_when_finished "rm -rf .git/worktrees other" &&
+       test_when_finished "git worktree remove -f other" &&
        git worktree add other &&
-       echo "Contents missing from repo" | git hash-object --stdin >.git/worktrees/other/HEAD &&
+       object_id=$(echo "Contents missing from repo" | git hash-object --stdin) &&
+       test-tool -C other ref-store main update-ref msg HEAD $object_id "" REF_NO_DEREF,REF_SKIP_OID_VERIFICATION &&
        test_must_fail git fsck 2>out &&
        test_grep "worktrees/other/HEAD: invalid sha1 pointer" out
 '
 
 test_expect_success 'other worktree HEAD link pointing at a funny place' '
-       test_when_finished "rm -rf .git/worktrees other" &&
+       test_when_finished "git worktree remove -f other" &&
        git worktree add other &&
-       echo "ref: refs/funny/place" >.git/worktrees/other/HEAD &&
+       git -C other symbolic-ref HEAD refs/funny/place &&
        test_must_fail git fsck 2>out &&
        test_grep "worktrees/other/HEAD points to something strange" out
 '
@@ -391,7 +391,7 @@ test_expect_success 'tag pointing to nonexistent' '
 
        tag=$(git hash-object -t tag -w --stdin <invalid-tag) &&
        test_when_finished "remove_object $tag" &&
-       echo $tag >.git/refs/tags/invalid &&
+       git update-ref refs/tags/invalid $tag &&
        test_when_finished "git update-ref -d refs/tags/invalid" &&
        test_must_fail git fsck --tags >out &&
        test_grep "broken link" out
@@ -411,7 +411,7 @@ test_expect_success 'tag pointing to something else than its type' '
 
        tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
        test_when_finished "remove_object $tag" &&
-       echo $tag >.git/refs/tags/wrong &&
+       git update-ref refs/tags/wrong $tag &&
        test_when_finished "git update-ref -d refs/tags/wrong" &&
        test_must_fail git fsck --tags
 '
@@ -428,7 +428,7 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '
 
        tag=$(git hash-object --literally -t tag -w --stdin <wrong-tag) &&
        test_when_finished "remove_object $tag" &&
-       echo $tag >.git/refs/tags/wrong &&
+       git update-ref refs/tags/wrong $tag &&
        test_when_finished "git update-ref -d refs/tags/wrong" &&
        git fsck --tags 2>out &&
 
@@ -452,7 +452,7 @@ test_expect_success 'tag with bad tagger' '
 
        tag=$(git hash-object --literally -t tag -w --stdin <wrong-tag) &&
        test_when_finished "remove_object $tag" &&
-       echo $tag >.git/refs/tags/wrong &&
+       git update-ref refs/tags/wrong $tag &&
        test_when_finished "git update-ref -d refs/tags/wrong" &&
        test_must_fail git fsck --tags 2>out &&
        test_grep "error in tag .*: invalid author/committer" out
@@ -471,7 +471,7 @@ test_expect_success 'tag with NUL in header' '
 
        tag=$(git hash-object --literally -t tag -w --stdin <tag-NUL-header) &&
        test_when_finished "remove_object $tag" &&
-       echo $tag >.git/refs/tags/wrong &&
+       git update-ref refs/tags/wrong $tag &&
        test_when_finished "git update-ref -d refs/tags/wrong" &&
        test_must_fail git fsck --tags 2>out &&
        test_grep "error in tag $tag.*unterminated header: NUL at offset" out
index 3f9e7f62e458c4db06a686700a15c5e08f47fad9..a669e592f1d95ca7e0dc575697342326a7a6654f 100755 (executable)
@@ -208,6 +208,23 @@ test_expect_success 'rev-parse --show-object-format in repo' '
        grep "unknown mode for --show-object-format: squeamish-ossifrage" err
 '
 
+test_expect_success 'rev-parse --show-ref-format' '
+       test_detect_ref_format >expect &&
+       git rev-parse --show-ref-format >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rev-parse --show-ref-format with invalid storage' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       (
+               cd repo &&
+               git config extensions.refstorage broken &&
+               test_must_fail git rev-parse --show-ref-format 2>err &&
+               grep "error: invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}broken${SQ}" err
+       )
+'
+
 test_expect_success '--show-toplevel from subdir of working tree' '
        pwd >expect &&
        git -C sub/dir rev-parse --show-toplevel >actual &&
index d9997e7b6b41a19537eed0bd512be1e8c78dc3d6..3c8135831b827d592c67c8d1316b1d768e87dc26 100755 (executable)
@@ -18,18 +18,18 @@ test_expect_success 'checkout should not start branch from a tree' '
        test_must_fail git checkout -b newbranch main^{tree}
 '
 
-test_expect_success 'checkout main from invalid HEAD' '
+test_expect_success REFFILES 'checkout main from invalid HEAD' '
        echo $ZERO_OID >.git/HEAD &&
        git checkout main --
 '
 
-test_expect_success 'checkout notices failure to lock HEAD' '
+test_expect_success REFFILES 'checkout notices failure to lock HEAD' '
        test_when_finished "rm -f .git/HEAD.lock" &&
        >.git/HEAD.lock &&
        test_must_fail git checkout -b other
 '
 
-test_expect_success 'create ref directory/file conflict scenario' '
+test_expect_success REFFILES 'create ref directory/file conflict scenario' '
        git update-ref refs/heads/outer/inner main &&
 
        # do not rely on symbolic-ref to get a known state,
@@ -39,26 +39,26 @@ test_expect_success 'create ref directory/file conflict scenario' '
        }
 '
 
-test_expect_success 'checkout away from d/f HEAD (unpacked, to branch)' '
+test_expect_success REFFILES 'checkout away from d/f HEAD (unpacked, to branch)' '
        reset_to_df &&
        git checkout main
 '
 
-test_expect_success 'checkout away from d/f HEAD (unpacked, to detached)' '
+test_expect_success REFFILES 'checkout away from d/f HEAD (unpacked, to detached)' '
        reset_to_df &&
        git checkout --detach main
 '
 
-test_expect_success 'pack refs' '
+test_expect_success REFFILES 'pack refs' '
        git pack-refs --all --prune
 '
 
-test_expect_success 'checkout away from d/f HEAD (packed, to branch)' '
+test_expect_success REFFILES 'checkout away from d/f HEAD (packed, to branch)' '
        reset_to_df &&
        git checkout main
 '
 
-test_expect_success 'checkout away from d/f HEAD (packed, to detached)' '
+test_expect_success REFFILES 'checkout away from d/f HEAD (packed, to detached)' '
        reset_to_df &&
        git checkout --detach main
 '
index e247a4735bbc26b01fecd600ed3e529bea16f464..c91c4db9361133e4e790f2c0201c5f41c14eaff1 100755 (executable)
@@ -170,8 +170,10 @@ test_expect_success 'switch back when temporarily detached and checked out elsew
        # we test in both worktrees to ensure that works
        # as expected with "first" and "next" worktrees
        test_must_fail git -C wt1 switch shared &&
+       test_must_fail git -C wt1 switch -C shared &&
        git -C wt1 switch --ignore-other-worktrees shared &&
        test_must_fail git -C wt2 switch shared &&
+       test_must_fail git -C wt2 switch -C shared &&
        git -C wt2 switch --ignore-other-worktrees shared
 '
 
index df4aff7825c9f8430d7449d1bdf2b0d5045d397a..c28c04133c8a1c953406c5bdea0e83c8cb82870f 100755 (executable)
@@ -126,6 +126,28 @@ test_expect_success 'die the same branch is already checked out' '
        )
 '
 
+test_expect_success 'refuse to reset a branch in use elsewhere' '
+       (
+               cd here &&
+
+               # we know we are on detached HEAD but just in case ...
+               git checkout --detach HEAD &&
+               git rev-parse --verify HEAD >old.head &&
+
+               git rev-parse --verify refs/heads/newmain >old.branch &&
+               test_must_fail git checkout -B newmain 2>error &&
+               git rev-parse --verify refs/heads/newmain >new.branch &&
+               git rev-parse --verify HEAD >new.head &&
+
+               grep "already used by worktree at" error &&
+               test_cmp old.branch new.branch &&
+               test_cmp old.head new.head &&
+
+               # and we must be still on the same detached HEAD state
+               test_must_fail git symbolic-ref HEAD
+       )
+'
+
 test_expect_success SYMLINKS 'die the same branch is already checked out (symlink)' '
        head=$(git -C there rev-parse --git-path HEAD) &&
        ref=$(git -C there symbolic-ref HEAD) &&
@@ -415,7 +437,7 @@ test_wt_add_orphan_hint () {
                git -C repo switch --orphan noref &&
                test_must_fail git -C repo worktree add $opts foobar/ 2>actual &&
                ! grep "error: unknown switch" actual &&
-               grep "hint: If you meant to create a worktree containing a new orphan branch" actual &&
+               grep "hint: If you meant to create a worktree containing a new unborn branch" actual &&
                if [ $use_branch -eq 1 ]
                then
                        grep -E "^hint: +git worktree add --orphan -b [^ ]+ [^ ]+$" actual
@@ -436,7 +458,7 @@ test_expect_success "'worktree add' doesn't show orphan hint in bad/orphan HEAD
        (cd repo && test_commit commit) &&
        test_must_fail git -C repo worktree add --quiet foobar_branch foobar/ 2>actual &&
        ! grep "error: unknown switch" actual &&
-       ! grep "hint: If you meant to create a worktree containing a new orphan branch" actual
+       ! grep "hint: If you meant to create a worktree containing a new unborn branch" actual
 '
 
 test_expect_success 'local clone from linked checkout' '
@@ -468,7 +490,8 @@ test_expect_success 'put a worktree under rebase' '
                cd under-rebase &&
                set_fake_editor &&
                FAKE_LINES="edit 1" git rebase -i HEAD^ &&
-               git worktree list | grep "under-rebase.*detached HEAD"
+               git worktree list >actual &&
+               grep "under-rebase.*detached HEAD" actual
        )
 '
 
@@ -509,7 +532,8 @@ test_expect_success 'checkout a branch under bisect' '
                git bisect start &&
                git bisect bad &&
                git bisect good HEAD~2 &&
-               git worktree list | grep "under-bisect.*detached HEAD" &&
+               git worktree list >actual &&
+               grep "under-bisect.*detached HEAD" actual &&
                test_must_fail git worktree add new-bisect under-bisect &&
                ! test -d new-bisect
        )
@@ -709,9 +733,9 @@ test_expect_success 'git worktree --no-guess-remote option overrides config' '
 test_dwim_orphan () {
        local info_text="No possible source branch, inferring '--orphan'" &&
        local fetch_error_text="fatal: No local or remote refs exist despite at least one remote" &&
-       local orphan_hint="hint: If you meant to create a worktree containing a new orphan branch" &&
+       local orphan_hint="hint: If you meant to create a worktree containing a new unborn branch" &&
        local invalid_ref_regex="^fatal: invalid reference: " &&
-       local bad_combo_regex="^fatal: '[-a-z]*' and '[-a-z]*' cannot be used together" &&
+       local bad_combo_regex="^fatal: options '[-a-z]*' and '[-a-z]*' cannot be used together" &&
 
        local git_ns="repo" &&
        local dashc_args="-C $git_ns" &&
index c54fd9ea06e6cce4c11729f109718454f5132261..de7d3014e4fb914012db343d9efd85a2818677b4 100755 (executable)
@@ -28,7 +28,7 @@ test_expect_success 'git branch --help should not have created a bogus branch' '
        test_ref_missing refs/heads/--help
 '
 
-test_expect_success 'branch -h in broken repository' '
+test_expect_success REFFILES 'branch -h in broken repository' '
        mkdir broken &&
        (
                cd broken &&
@@ -76,14 +76,14 @@ test_expect_success 'git branch HEAD should fail' '
 '
 
 cat >expect <<EOF
-$ZERO_OID $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000    branch: Created from main
+$HEAD refs/heads/d/e/f@{0}: branch: Created from main
 EOF
 test_expect_success 'git branch --create-reflog d/e/f should create a branch and a log' '
        GIT_COMMITTER_DATE="2005-05-26 23:30" \
        git -c core.logallrefupdates=false branch --create-reflog d/e/f &&
        test_ref_exists refs/heads/d/e/f &&
-       test_path_is_file .git/logs/refs/heads/d/e/f &&
-       test_cmp expect .git/logs/refs/heads/d/e/f
+       git reflog show --no-abbrev-commit refs/heads/d/e/f >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'git branch -d d/e/f should delete a branch and a log' '
@@ -203,10 +203,9 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou
        test $(git rev-parse --abbrev-ref HEAD) = bam
 '
 
-test_expect_success 'git branch -M baz bam should add entries to .git/logs/HEAD' '
-       msg="Branch: renamed refs/heads/baz to refs/heads/bam" &&
-       grep " $ZERO_OID.*$msg$" .git/logs/HEAD &&
-       grep "^$ZERO_OID.*$msg$" .git/logs/HEAD
+test_expect_success 'git branch -M baz bam should add entries to HEAD reflog' '
+       git reflog show HEAD >actual &&
+       grep "HEAD@{0}: Branch: renamed refs/heads/baz to refs/heads/bam" actual
 '
 
 test_expect_success 'git branch -M should leave orphaned HEAD alone' '
@@ -215,17 +214,20 @@ test_expect_success 'git branch -M should leave orphaned HEAD alone' '
                cd orphan &&
                test_commit initial &&
                git checkout --orphan lonely &&
-               grep lonely .git/HEAD &&
+               git symbolic-ref HEAD >expect &&
+               echo refs/heads/lonely >actual &&
+               test_cmp expect actual &&
                test_ref_missing refs/head/lonely &&
                git branch -M main mistress &&
-               grep lonely .git/HEAD
+               git symbolic-ref HEAD >expect &&
+               test_cmp expect actual
        )
 '
 
 test_expect_success 'resulting reflog can be shown by log -g' '
        oid=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
-       HEAD@{0} $oid $msg
+       HEAD@{0} $oid Branch: renamed refs/heads/baz to refs/heads/bam
        HEAD@{2} $oid checkout: moving from foo to baz
        EOF
        git log -g --format="%gd %H %gs" -2 HEAD >actual &&
@@ -243,7 +245,7 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou
        git worktree prune
 '
 
-test_expect_success 'git branch -M fails if updating any linked working tree fails' '
+test_expect_success REFFILES 'git branch -M fails if updating any linked working tree fails' '
        git worktree add -b baz bazdir1 &&
        git worktree add -f bazdir2 baz &&
        touch .git/worktrees/bazdir1/HEAD.lock &&
@@ -517,7 +519,7 @@ EOF
 
 mv .git/config .git/config-saved
 
-test_expect_success SHA1 'git branch -m q q2 without config should succeed' '
+test_expect_success DEFAULT_REPO_FORMAT 'git branch -m q q2 without config should succeed' '
        git branch -m q q2 &&
        git branch -m q2 q
 '
@@ -699,7 +701,8 @@ test_expect_success 'git branch -C c1 c2 should succeed when c1 is checked out'
 
 test_expect_success 'git branch -C c1 c2 should never touch HEAD' '
        msg="Branch: copied refs/heads/c1 to refs/heads/c2" &&
-       ! grep "$msg$" .git/logs/HEAD
+       git reflog HEAD >actual &&
+       ! grep "$msg$" actual
 '
 
 test_expect_success 'git branch -C main should work when main is checked out' '
@@ -809,7 +812,7 @@ test_expect_success 'deleting a symref' '
 
 test_expect_success 'deleting a dangling symref' '
        git symbolic-ref refs/heads/dangling-symref nowhere &&
-       test_path_is_file .git/refs/heads/dangling-symref &&
+       git symbolic-ref --no-recurse refs/heads/dangling-symref &&
        echo "Deleted branch dangling-symref (was nowhere)." >expect &&
        git branch -d dangling-symref >actual &&
        test_ref_missing refs/heads/dangling-symref &&
@@ -833,14 +836,14 @@ test_expect_success 'renaming a symref is not allowed' '
        test_ref_missing refs/heads/new-topic
 '
 
-test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' '
+test_expect_success SYMLINKS,REFFILES 'git branch -m u v should fail when the reflog for u is a symlink' '
        git branch --create-reflog u &&
        mv .git/logs/refs/heads/u real-u &&
        ln -s real-u .git/logs/refs/heads/u &&
        test_must_fail git branch -m u v
 '
 
-test_expect_success SYMLINKS 'git branch -m with symlinked .git/refs' '
+test_expect_success SYMLINKS,REFFILES 'git branch -m with symlinked .git/refs' '
        test_when_finished "rm -rf subdir" &&
        git init --bare subdir &&
 
@@ -1140,14 +1143,14 @@ test_expect_success '--set-upstream-to notices an error to set branch as own ups
 
 # Keep this test last, as it changes the current branch
 cat >expect <<EOF
-$ZERO_OID $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000    branch: Created from main
+$HEAD refs/heads/g/h/i@{0}: branch: Created from main
 EOF
 test_expect_success 'git checkout -b g/h/i -l should create a branch and a log' '
        GIT_COMMITTER_DATE="2005-05-26 23:30" \
        git checkout -b g/h/i -l main &&
        test_ref_exists refs/heads/g/h/i &&
-       test_path_is_file .git/logs/refs/heads/g/h/i &&
-       test_cmp expect .git/logs/refs/heads/g/h/i
+       git reflog show --no-abbrev-commit refs/heads/g/h/i >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'checkout -b makes reflog by default' '
@@ -1573,9 +1576,10 @@ test_expect_success 'tracking with unexpected .fetch refspec' '
 
 test_expect_success 'configured committerdate sort' '
        git init -b main sort &&
+       test_config -C sort branch.sort "committerdate" &&
+
        (
                cd sort &&
-               git config branch.sort committerdate &&
                test_commit initial &&
                git checkout -b a &&
                test_commit a &&
@@ -1595,9 +1599,10 @@ test_expect_success 'configured committerdate sort' '
 '
 
 test_expect_success 'option override configured sort' '
+       test_config -C sort branch.sort "committerdate" &&
+
        (
                cd sort &&
-               git config branch.sort committerdate &&
                git branch --sort=refname >actual &&
                cat >expect <<-\EOF &&
                  a
@@ -1609,10 +1614,70 @@ test_expect_success 'option override configured sort' '
        )
 '
 
+test_expect_success '--no-sort cancels config sort keys' '
+       test_config -C sort branch.sort "-refname" &&
+
+       (
+               cd sort &&
+
+               # objecttype is identical for all of them, so sort falls back on
+               # default (ascending refname)
+               git branch \
+                       --no-sort \
+                       --sort="objecttype" >actual &&
+               cat >expect <<-\EOF &&
+                 a
+               * b
+                 c
+                 main
+               EOF
+               test_cmp expect actual
+       )
+
+'
+
+test_expect_success '--no-sort cancels command line sort keys' '
+       (
+               cd sort &&
+
+               # objecttype is identical for all of them, so sort falls back on
+               # default (ascending refname)
+               git branch \
+                       --sort="-refname" \
+                       --no-sort \
+                       --sort="objecttype" >actual &&
+               cat >expect <<-\EOF &&
+                 a
+               * b
+                 c
+                 main
+               EOF
+               test_cmp expect actual
+       )
+'
+
+test_expect_success '--no-sort without subsequent --sort prints expected branches' '
+       (
+               cd sort &&
+
+               # Sort the results with `sort` for a consistent comparison
+               # against expected
+               git branch --no-sort | sort >actual &&
+               cat >expect <<-\EOF &&
+                 a
+                 c
+                 main
+               * b
+               EOF
+               test_cmp expect actual
+       )
+'
+
 test_expect_success 'invalid sort parameter in configuration' '
+       test_config -C sort branch.sort "v:notvalid" &&
+
        (
                cd sort &&
-               git config branch.sort "v:notvalid" &&
 
                # this works in the "listing" mode, so bad sort key
                # is a dying offence.
index 60d6ed2dc8438a22439d55432d5664ea2080b9f0..597df5ebc0a582018d30c018e734d1154c364ed6 100755 (executable)
@@ -561,9 +561,9 @@ y and z notes on 4th commit
 EOF
        # Fail to finalize merge
        test_must_fail git notes merge --commit >output 2>&1 &&
-       # .git/NOTES_MERGE_* must remain
-       test -f .git/NOTES_MERGE_PARTIAL &&
-       test -f .git/NOTES_MERGE_REF &&
+       # NOTES_MERGE_* refs and .git/NOTES_MERGE_* state files must remain
+       git rev-parse --verify NOTES_MERGE_PARTIAL &&
+       git rev-parse --verify NOTES_MERGE_REF &&
        test -f .git/NOTES_MERGE_WORKTREE/$commit_sha1 &&
        test -f .git/NOTES_MERGE_WORKTREE/$commit_sha2 &&
        test -f .git/NOTES_MERGE_WORKTREE/$commit_sha3 &&
index 24a539c662f6c1f941e4f8c2949cfdc07d3cf2e5..57f13929260e70865fbb8f31e89ba7ff197afeaa 100755 (executable)
@@ -424,7 +424,7 @@ test_expect_success 'refuse to switch to branch checked out elsewhere' '
        test_grep "already used by worktree at" err
 '
 
-test_expect_success MINGW,SYMLINKS_WINDOWS 'rebase when .git/logs is a symlink' '
+test_expect_success REFFILES,MINGW,SYMLINKS_WINDOWS 'rebase when .git/logs is a symlink' '
        git checkout main &&
        mv .git/logs actual_logs &&
        cmd //c "mklink /D .git\logs ..\actual_logs" &&
index c5f30554c6f356742732f8979a55c872441fb159..64b641002e4fdb049d7f958cdc54335f9c0f34ce 100755 (executable)
@@ -2160,7 +2160,7 @@ test_expect_success '--update-refs: check failed ref update' '
        # recorded in the update-refs file. We will force-update the
        # "second" ref, but "git branch -f" will not work because of
        # the lock in the update-refs file.
-       git rev-parse third >.git/refs/heads/second &&
+       git update-ref refs/heads/second third &&
 
        test_must_fail git rebase --continue 2>err &&
        grep "update_ref failed for ref '\''refs/heads/second'\''" err &&
index a364530d7629fc0bb47c30211935b878e1aa9805..fcc40d6fe1fd5b2a9c4f2a5e4ede15e73c247790 100755 (executable)
@@ -43,7 +43,7 @@ test_auto_fixup () {
 
        git tag $1 &&
        test_tick &&
-       git rebase $2 -i HEAD^^^ &&
+       git rebase $2 HEAD^^^ &&
        git log --oneline >actual &&
        if test -n "$no_squash"
        then
@@ -61,15 +61,24 @@ test_auto_fixup () {
 }
 
 test_expect_success 'auto fixup (option)' '
-       test_auto_fixup final-fixup-option --autosquash
+       test_auto_fixup fixup-option --autosquash &&
+       test_auto_fixup fixup-option-i "--autosquash -i"
 '
 
-test_expect_success 'auto fixup (config)' '
+test_expect_success 'auto fixup (config true)' '
        git config rebase.autosquash true &&
-       test_auto_fixup final-fixup-config-true &&
+       test_auto_fixup ! fixup-config-true &&
+       test_auto_fixup fixup-config-true-i -i &&
        test_auto_fixup ! fixup-config-true-no --no-autosquash &&
+       test_auto_fixup ! fixup-config-true-i-no "-i --no-autosquash"
+'
+
+test_expect_success 'auto fixup (config false)' '
        git config rebase.autosquash false &&
-       test_auto_fixup ! final-fixup-config-false
+       test_auto_fixup ! fixup-config-false &&
+       test_auto_fixup ! fixup-config-false-i -i &&
+       test_auto_fixup fixup-config-false-yes --autosquash &&
+       test_auto_fixup fixup-config-false-i-yes "-i --autosquash"
 '
 
 test_auto_squash () {
@@ -87,7 +96,7 @@ test_auto_squash () {
        git commit -m "squash! first" -m "extra para for first" &&
        git tag $1 &&
        test_tick &&
-       git rebase $2 -i HEAD^^^ &&
+       git rebase $2 HEAD^^^ &&
        git log --oneline >actual &&
        if test -n "$no_squash"
        then
@@ -105,15 +114,24 @@ test_auto_squash () {
 }
 
 test_expect_success 'auto squash (option)' '
-       test_auto_squash final-squash --autosquash
+       test_auto_squash squash-option --autosquash &&
+       test_auto_squash squash-option-i "--autosquash -i"
 '
 
-test_expect_success 'auto squash (config)' '
+test_expect_success 'auto squash (config true)' '
        git config rebase.autosquash true &&
-       test_auto_squash final-squash-config-true &&
+       test_auto_squash ! squash-config-true &&
+       test_auto_squash squash-config-true-i -i &&
        test_auto_squash ! squash-config-true-no --no-autosquash &&
+       test_auto_squash ! squash-config-true-i-no "-i --no-autosquash"
+'
+
+test_expect_success 'auto squash (config false)' '
        git config rebase.autosquash false &&
-       test_auto_squash ! final-squash-config-false
+       test_auto_squash ! squash-config-false &&
+       test_auto_squash ! squash-config-false-i -i &&
+       test_auto_squash squash-config-false-yes --autosquash &&
+       test_auto_squash squash-config-false-i-yes "-i --autosquash"
 '
 
 test_expect_success 'misspelled auto squash' '
index 2eba00bdf5898531744c8f77429a8f4d1ad1b91d..b40f26250b7e3d8ce95b149589822bd57d84dbbe 100755 (executable)
@@ -100,12 +100,6 @@ test_rebase_am_only () {
                test_must_fail git rebase $opt --root A
        "
 
-       test_expect_success "$opt incompatible with rebase.autosquash" "
-               git checkout B^0 &&
-               test_must_fail git -c rebase.autosquash=true rebase $opt A 2>err &&
-               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 &&
@@ -118,12 +112,6 @@ test_rebase_am_only () {
                grep -e --no-update-refs err
        "
 
-       test_expect_success "$opt okay with overridden rebase.autosquash" "
-               test_when_finished \"git reset --hard B^0\" &&
-               git checkout B^0 &&
-               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 &&
diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh
new file mode 100755 (executable)
index 0000000..3896702
--- /dev/null
@@ -0,0 +1,198 @@
+#!/bin/sh
+
+test_description='basic git replay tests'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+GIT_AUTHOR_NAME=author@name
+GIT_AUTHOR_EMAIL=bogus@email@address
+export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
+
+test_expect_success 'setup' '
+       test_commit A &&
+       test_commit B &&
+
+       git switch -c topic1 &&
+       test_commit C &&
+       git switch -c topic2 &&
+       test_commit D &&
+       test_commit E &&
+       git switch topic1 &&
+       test_commit F &&
+       git switch -c topic3 &&
+       test_commit G &&
+       test_commit H &&
+       git switch -c topic4 main &&
+       test_commit I &&
+       test_commit J &&
+
+       git switch -c next main &&
+       test_commit K &&
+       git merge -m "Merge topic1" topic1 &&
+       git merge -m "Merge topic2" topic2 &&
+       git merge -m "Merge topic3" topic3 &&
+       >evil &&
+       git add evil &&
+       git commit --amend &&
+       git merge -m "Merge topic4" topic4 &&
+
+       git switch main &&
+       test_commit L &&
+       test_commit M &&
+
+       git switch -c conflict B &&
+       test_commit C.conflict C.t conflict
+'
+
+test_expect_success 'setup bare' '
+       git clone --bare . bare
+'
+
+test_expect_success 'using replay to rebase two branches, one on top of other' '
+       git replay --onto main topic1..topic2 >result &&
+
+       test_line_count = 1 result &&
+
+       git log --format=%s $(cut -f 3 -d " " result) >actual &&
+       test_write_lines E D M L B A >expect &&
+       test_cmp expect actual &&
+
+       printf "update refs/heads/topic2 " >expect &&
+       printf "%s " $(cut -f 3 -d " " result) >>expect &&
+       git rev-parse topic2 >>expect &&
+
+       test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to rebase two branches, one on top of other' '
+       git -C bare replay --onto main topic1..topic2 >result-bare &&
+       test_cmp expect result-bare
+'
+
+test_expect_success 'using replay to rebase with a conflict' '
+       test_expect_code 1 git replay --onto topic1 B..conflict
+'
+
+test_expect_success 'using replay on bare repo to rebase with a conflict' '
+       test_expect_code 1 git -C bare replay --onto topic1 B..conflict
+'
+
+test_expect_success 'using replay to perform basic cherry-pick' '
+       # The differences between this test and previous ones are:
+       #   --advance vs --onto
+       # 2nd field of result is refs/heads/main vs. refs/heads/topic2
+       # 4th field of result is hash for main instead of hash for topic2
+
+       git replay --advance main topic1..topic2 >result &&
+
+       test_line_count = 1 result &&
+
+       git log --format=%s $(cut -f 3 -d " " result) >actual &&
+       test_write_lines E D M L B A >expect &&
+       test_cmp expect actual &&
+
+       printf "update refs/heads/main " >expect &&
+       printf "%s " $(cut -f 3 -d " " result) >>expect &&
+       git rev-parse main >>expect &&
+
+       test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to perform basic cherry-pick' '
+       git -C bare replay --advance main topic1..topic2 >result-bare &&
+       test_cmp expect result-bare
+'
+
+test_expect_success 'replay on bare repo fails with both --advance and --onto' '
+       test_must_fail git -C bare replay --advance main --onto main topic1..topic2 >result-bare
+'
+
+test_expect_success 'replay fails when both --advance and --onto are omitted' '
+       test_must_fail git replay topic1..topic2 >result
+'
+
+test_expect_success 'using replay to also rebase a contained branch' '
+       git replay --contained --onto main main..topic3 >result &&
+
+       test_line_count = 2 result &&
+       cut -f 3 -d " " result >new-branch-tips &&
+
+       git log --format=%s $(head -n 1 new-branch-tips) >actual &&
+       test_write_lines F C M L B A >expect &&
+       test_cmp expect actual &&
+
+       git log --format=%s $(tail -n 1 new-branch-tips) >actual &&
+       test_write_lines H G F C M L B A >expect &&
+       test_cmp expect actual &&
+
+       printf "update refs/heads/topic1 " >expect &&
+       printf "%s " $(head -n 1 new-branch-tips) >>expect &&
+       git rev-parse topic1 >>expect &&
+       printf "update refs/heads/topic3 " >>expect &&
+       printf "%s " $(tail -n 1 new-branch-tips) >>expect &&
+       git rev-parse topic3 >>expect &&
+
+       test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to also rebase a contained branch' '
+       git -C bare replay --contained --onto main main..topic3 >result-bare &&
+       test_cmp expect result-bare
+'
+
+test_expect_success 'using replay to rebase multiple divergent branches' '
+       git replay --onto main ^topic1 topic2 topic4 >result &&
+
+       test_line_count = 2 result &&
+       cut -f 3 -d " " result >new-branch-tips &&
+
+       git log --format=%s $(head -n 1 new-branch-tips) >actual &&
+       test_write_lines E D M L B A >expect &&
+       test_cmp expect actual &&
+
+       git log --format=%s $(tail -n 1 new-branch-tips) >actual &&
+       test_write_lines J I M L B A >expect &&
+       test_cmp expect actual &&
+
+       printf "update refs/heads/topic2 " >expect &&
+       printf "%s " $(head -n 1 new-branch-tips) >>expect &&
+       git rev-parse topic2 >>expect &&
+       printf "update refs/heads/topic4 " >>expect &&
+       printf "%s " $(tail -n 1 new-branch-tips) >>expect &&
+       git rev-parse topic4 >>expect &&
+
+       test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to rebase multiple divergent branches, including contained ones' '
+       git -C bare replay --contained --onto main ^main topic2 topic3 topic4 >result &&
+
+       test_line_count = 4 result &&
+       cut -f 3 -d " " result >new-branch-tips &&
+
+       >expect &&
+       for i in 2 1 3 4
+       do
+               printf "update refs/heads/topic$i " >>expect &&
+               printf "%s " $(grep topic$i result | cut -f 3 -d " ") >>expect &&
+               git -C bare rev-parse topic$i >>expect || return 1
+       done &&
+
+       test_cmp expect result &&
+
+       test_write_lines F C M L B A >expect1 &&
+       test_write_lines E D C M L B A >expect2 &&
+       test_write_lines H G F C M L B A >expect3 &&
+       test_write_lines J I M L B A >expect4 &&
+
+       for i in 1 2 3 4
+       do
+               git -C bare log --format=%s $(grep topic$i result | cut -f 3 -d " ") >actual &&
+               test_cmp expect$i actual || return 1
+       done
+'
+
+test_done
index 85be1367de6a3f73e1162ffc03563ec7b9a9eb5a..49c042a38ae987fa95f201bd3a00f55e4adeffab 100755 (executable)
@@ -286,4 +286,28 @@ test_expect_success 'basename similarity vs best similarity' '
        test_cmp expected actual
 '
 
+test_expect_success 'last line matters too' '
+       {
+               test_write_lines a 0 1 2 3 4 5 6 7 8 9 &&
+               printf "git ignores final up to 63 characters if not newline terminated"
+       } >no-final-lf &&
+       git add no-final-lf &&
+       git commit -m "original version of file with no final newline" &&
+
+       # Change ONLY the first character of the whole file
+       {
+               test_write_lines b 0 1 2 3 4 5 6 7 8 9 &&
+               printf "git ignores final up to 63 characters if not newline terminated"
+       } >no-final-lf &&
+       git add no-final-lf &&
+       git mv no-final-lf still-absent-final-lf &&
+       git commit -a -m "rename no-final-lf -> still-absent-final-lf" &&
+       git diff-tree -r -M --name-status HEAD^ HEAD >actual &&
+       sed -e "s/^R[0-9]*      /R      /" actual >actual.munged &&
+       cat >expected <<-\EOF &&
+       R       no-final-lf     still-absent-final-lf
+       EOF
+       test_cmp expected actual.munged
+'
+
 test_done
index 663368d4115038dcda261fe861316b7451608f09..1e3b2dbea48488ecb3a68a8dba82d57b6fbca2a7 100755 (executable)
@@ -178,32 +178,29 @@ process_diffs () {
 V=$(git version | sed -e 's/^git version //' -e 's/\./\\./g')
 while read magic cmd
 do
-       status=success
        case "$magic" in
        '' | '#'*)
                continue ;;
-       :*)
-               magic=${magic#:}
+       :noellipses)
+               magic=noellipses
                label="$magic-$cmd"
-               case "$magic" in
-               noellipses) ;;
-               failure)
-                       status=failure
-                       magic=
-                       label="$cmd" ;;
-               *)
-                       BUG "unknown magic $magic" ;;
-               esac ;;
+               ;;
+       :*)
+               BUG "unknown magic $magic"
+               ;;
        *)
-               cmd="$magic $cmd" magic=
-               label="$cmd" ;;
+               cmd="$magic $cmd"
+               magic=
+               label="$cmd"
+               ;;
        esac
+
        test=$(echo "$label" | sed -e 's|[/ ][/ ]*|_|g')
        pfx=$(printf "%04d" $test_count)
        expect="$TEST_DIRECTORY/t4013/diff.$test"
        actual="$pfx-diff.$test"
 
-       test_expect_$status "git $cmd # magic is ${magic:-(not used)}" '
+       test_expect_success "git $cmd # magic is ${magic:-(not used)}" '
                {
                        echo "$ git $cmd"
                        case "$magic" in
@@ -522,7 +519,7 @@ test_expect_success 'log -S requires an argument' '
 '
 
 test_expect_success 'diff --cached on unborn branch' '
-       echo ref: refs/heads/unborn >.git/HEAD &&
+       git symbolic-ref HEAD refs/heads/unborn &&
        git diff --cached >result &&
        process_diffs result >actual &&
        process_diffs "$TEST_DIRECTORY/t4013/diff.diff_--cached" >expected &&
index 5ced27ed45715ce337d82261b53f1c5c8b02a551..e37a1411ee247cc76083367870e0e0c2e16fbb84 100755 (executable)
@@ -1906,6 +1906,16 @@ body" &&
        grep "^body$" actual
 '
 
+test_expect_success 'cover letter with --cover-from-description subject (UTF-8 subject line)' '
+       test_config branch.rebuild-1.description "Café?
+
+body" &&
+       git checkout rebuild-1 &&
+       git format-patch --stdout --cover-letter --cover-from-description subject --encode-email-headers main >actual &&
+       grep "^Subject: \[PATCH 0/2\] =?UTF-8?q?Caf=C3=A9=3F?=$" actual &&
+       ! grep "Café" actual
+'
+
 test_expect_success 'cover letter with format.coverFromDescription = auto (short subject line)' '
        test_config branch.rebuild-1.description "config subject
 
index e7a7295f1b687d1ed916e80e431e78c4c67657b7..2775bfadd8619b2a5e2440209c46c0658205ddcd 100755 (executable)
@@ -41,7 +41,8 @@ test_expect_success FILEMODE 'same mode (index only)' '
        chmod +x file &&
        git add file &&
        git apply --cached patch-0.txt &&
-       git ls-files -s file | grep "^100755"
+       git ls-files -s file >ls-files-output &&
+       test_grep "^100755" ls-files-output
 '
 
 test_expect_success FILEMODE 'mode update (no index)' '
@@ -60,7 +61,8 @@ test_expect_success FILEMODE 'mode update (with index)' '
 test_expect_success FILEMODE 'mode update (index only)' '
        git reset --hard &&
        git apply --cached patch-1.txt &&
-       git ls-files -s file | grep "^100755"
+       git ls-files -s file >ls-files-output &&
+       test_grep "^100755" ls-files-output
 '
 
 test_expect_success FILEMODE 'empty mode is rejected' '
index 708636671a3fd1dd8b63b34335dbe4535d7c58bb..ddd205f98abf7f8d58555761eaf20006bb8433e8 100755 (executable)
@@ -2265,7 +2265,7 @@ test_expect_success REFFILES 'log diagnoses bogus HEAD hash' '
 
 test_expect_success REFFILES 'log diagnoses bogus HEAD symref' '
        git init empty &&
-       echo "ref: refs/heads/invalid.lock" > empty/.git/HEAD &&
+       test-tool -C empty ref-store main create-symref HEAD refs/heads/invalid.lock &&
        test_must_fail git -C empty log 2>stderr &&
        test_grep broken stderr &&
        test_must_fail git -C empty log --default totally-bogus 2>stderr &&
index 21986a866df65c9ed9025f7ab48dc596e6df81ca..73ea9e515503baf7776aaba04a4b6d9de7e20c92 100755 (executable)
@@ -70,8 +70,14 @@ ${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A
        cmp_filtered_decorations
 '
 
+remove_replace_refs () {
+       git for-each-ref 'refs/replace*/**' --format='delete %(refname)' >in &&
+       git update-ref --stdin <in &&
+       rm in
+}
+
 test_expect_success 'test coloring with replace-objects' '
-       test_when_finished rm -rf .git/refs/replace* &&
+       test_when_finished remove_replace_refs &&
        test_commit C &&
        test_commit D &&
 
@@ -99,7 +105,7 @@ EOF
 '
 
 test_expect_success 'test coloring with grafted commit' '
-       test_when_finished rm -rf .git/refs/replace* &&
+       test_when_finished remove_replace_refs &&
 
        git replace --graft HEAD HEAD~2 &&
 
index b2c8a43fce311570e358d77a3b062228adcbbf68..12ac43687366d72f2fa2870c72a9d5563a05fc66 100755 (executable)
@@ -887,7 +887,7 @@ test_expect_success '--stdin with both a successful and a conflicted merge' '
 test_expect_success '--merge-base is incompatible with --stdin' '
        test_must_fail git merge-tree --merge-base=side1 --stdin 2>expect &&
 
-       grep "^fatal: --merge-base is incompatible with --stdin" expect
+       grep "^fatal: .*merge-base.*stdin.* cannot be used together" expect
 '
 
 # specify merge-base as parent of branch2
index 4b4c3315d885f02fdcf2be65885ea9107f65b86d..72b8d0ff02e34be0606747c850b0815be31196d4 100755 (executable)
@@ -124,6 +124,16 @@ test_expect_success 'setup' '
        EOF
 '
 
+test_expect_success '--list notices extra parameters' '
+       test_must_fail git archive --list blah &&
+       test_must_fail git archive --remote=. --list blah
+'
+
+test_expect_success 'end-of-options is correctly eaten' '
+       git archive --list --end-of-options &&
+       git archive --remote=. --list --end-of-options
+'
+
 test_expect_success 'populate workdir' '
        mkdir a &&
        echo simple textfile >a/a &&
index db11cababd310f9773dcd0a3bb7039a4df8d4f59..654d8cf3ee003ee4e8d66b6e88df06312014cb47 100755 (executable)
@@ -268,4 +268,26 @@ test_expect_success 'mailinfo warn CR in base64 encoded email' '
        test_must_be_empty quoted-cr/0002.err
 '
 
+test_expect_success 'from line with unterminated quoted string' '
+       echo "From: bob \"unterminated string smith <bob@example.com>" >in &&
+       git mailinfo /dev/null /dev/null <in >actual &&
+       cat >expect <<-\EOF &&
+       Author: bob unterminated string smith
+       Email: bob@example.com
+
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'from line with unterminated comment' '
+       echo "From: bob (unterminated comment smith <bob@example.com>" >in &&
+       git mailinfo /dev/null /dev/null <in >actual &&
+       cat >expect <<-\EOF &&
+       Author: bob (unterminated comment smith
+       Email: bob@example.com
+
+       EOF
+       test_cmp expect actual
+'
+
 test_done
index 72281779843fb270762b5352b8dc213bd3b805e1..bd71956a474531775de56ac26757484a76025676 100644 (file)
@@ -1,4 +1,4 @@
-Author: A U Thor (this is (really) a comment (honestly))
+Author: (this is (really) a "comment" (honestly)) A U Thor
 Email: somebody@example.com
 Subject: testing comments
 Date: Sun, 25 May 2008 00:38:18 -0700
index c53a192dfeac51a32000d80f4f8052eef6e44486..0b7e903b061131b30a258d30b11cd9cc22acae5d 100644 (file)
@@ -1,5 +1,5 @@
 From 1234567890123456789012345678901234567890 Mon Sep 17 00:00:00 2001
-From: "A U Thor" <somebody@example.com> (this is \(really\) a comment (honestly))
+From: (this is \(really\) a "comment" (honestly)) "A U Thor" <somebody@example.com>
 Date: Sun, 25 May 2008 00:38:18 -0700
 Subject: [PATCH] testing comments
 
index d4fc65e078e4bf395ad1c83bba285f672d0785cd..a2b44426609fe5c52347510e8b82c9484897dea8 100755 (executable)
@@ -540,17 +540,17 @@ test_expect_success 'detect low chunk count' '
 
 test_expect_success 'detect missing OID fanout chunk' '
        corrupt_graph_and_verify $GRAPH_BYTE_OID_FANOUT_ID "\0" \
-               "missing the OID Fanout chunk"
+               "commit-graph required OID fanout chunk missing or corrupted"
 '
 
 test_expect_success 'detect missing OID lookup chunk' '
        corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_ID "\0" \
-               "missing the OID Lookup chunk"
+               "commit-graph required OID lookup chunk missing or corrupted"
 '
 
 test_expect_success 'detect missing commit data chunk' '
        corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_DATA_ID "\0" \
-               "missing the Commit Data chunk"
+               "commit-graph required commit data chunk missing or corrupted"
 '
 
 test_expect_success 'detect incorrect fanout' '
@@ -560,7 +560,7 @@ test_expect_success 'detect incorrect fanout' '
 
 test_expect_success 'detect incorrect fanout final value' '
        corrupt_graph_and_verify $GRAPH_BYTE_FANOUT2 "\01" \
-               "oid table and fanout disagree on size"
+               "OID lookup chunk is the wrong size"
 '
 
 test_expect_success 'detect incorrect OID order' '
@@ -842,7 +842,7 @@ test_expect_success 'reader notices too-small oid fanout chunk' '
        check_corrupt_chunk OIDF clear $(printf "000000%02x" $(test_seq 250)) &&
        cat >expect.err <<-\EOF &&
        error: commit-graph oid fanout chunk is wrong size
-       error: commit-graph is missing the OID Fanout chunk
+       error: commit-graph required OID fanout chunk missing or corrupted
        EOF
        test_cmp expect.err err
 '
@@ -850,7 +850,8 @@ test_expect_success 'reader notices too-small oid fanout chunk' '
 test_expect_success 'reader notices fanout/lookup table mismatch' '
        check_corrupt_chunk OIDF 1020 "FFFFFFFF" &&
        cat >expect.err <<-\EOF &&
-       error: commit-graph oid table and fanout disagree on size
+       error: commit-graph OID lookup chunk is the wrong size
+       error: commit-graph required OID lookup chunk missing or corrupted
        EOF
        test_cmp expect.err err
 '
@@ -866,6 +867,7 @@ test_expect_success 'reader notices out-of-bounds fanout' '
        check_corrupt_chunk OIDF 0 $(printf "%02x000000" $(test_seq 0 254)) &&
        cat >expect.err <<-\EOF &&
        error: commit-graph fanout values out of order
+       error: commit-graph required OID fanout chunk missing or corrupted
        EOF
        test_cmp expect.err err
 '
@@ -874,7 +876,7 @@ test_expect_success 'reader notices too-small commit data chunk' '
        check_corrupt_chunk CDAT clear 00000000 &&
        cat >expect.err <<-\EOF &&
        error: commit-graph commit data chunk is wrong size
-       error: commit-graph is missing the Commit Data chunk
+       error: commit-graph required commit data chunk missing or corrupted
        EOF
        test_cmp expect.err err
 '
@@ -909,10 +911,10 @@ test_expect_success 'stale commit cannot be parsed when given directly' '
 
                # Verify that it is possible to read the commit from the
                # commit graph when not being paranoid, ...
-               GIT_COMMIT_GRAPH_PARANOIA=false git rev-list B &&
+               git rev-list B &&
                # ... but parsing the commit when double checking that
                # it actually exists in the object database should fail.
-               test_must_fail git rev-list -1 B
+               test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git rev-list -1 B
        )
 '
 
@@ -936,9 +938,9 @@ test_expect_success 'stale commit cannot be parsed when traversing graph' '
 
                # Again, we should be able to parse the commit when not
                # being paranoid about commit graph staleness...
-               GIT_COMMIT_GRAPH_PARANOIA=false git rev-parse HEAD~2 &&
+               git rev-parse HEAD~2 &&
                # ... but fail when we are paranoid.
-               test_must_fail git rev-parse HEAD~2 2>error &&
+               test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git rev-parse HEAD~2 2>error &&
                grep "error: commit $oid exists in commit-graph but not in the object database" error
        )
 '
index c4c6060cee432ed088d4d87338a3d311204ab5c8..dd09134db03638fd01e73d89acbac45cceaa6e46 100755 (executable)
@@ -1157,4 +1157,53 @@ test_expect_success 'reader notices too-small revindex chunk' '
        test_cmp expect.err err
 '
 
+test_expect_success 'reader notices out-of-bounds fanout' '
+       # This is similar to the out-of-bounds fanout test in t5318. The values
+       # in adjacent entries should be large but not identical (they
+       # are used as hi/lo starts for a binary search, which would then abort
+       # immediately).
+       corrupt_chunk OIDF 0 $(printf "%02x000000" $(test_seq 0 254)) &&
+       test_must_fail git log 2>err &&
+       cat >expect <<-\EOF &&
+       error: oid fanout out of order: fanout[254] = fe000000 > 5c = fanout[255]
+       fatal: multi-pack-index required OID fanout chunk missing or corrupted
+       EOF
+       test_cmp expect err
+'
+
+test_expect_success 'bitmapped packs are stored via the BTMP chunk' '
+       test_when_finished "rm -fr repo" &&
+       git init repo &&
+       (
+               cd repo &&
+
+               for i in 1 2 3 4 5
+               do
+                       test_commit "$i" &&
+                       git repack -d || return 1
+               done &&
+
+               find $objdir/pack -type f -name "*.idx" | xargs -n 1 basename |
+               sort >packs &&
+
+               git multi-pack-index write --stdin-packs <packs &&
+               test_must_fail test-tool read-midx --bitmap $objdir 2>err &&
+               cat >expect <<-\EOF &&
+               error: MIDX does not contain the BTMP chunk
+               EOF
+               test_cmp expect err &&
+
+               git multi-pack-index write --stdin-packs --bitmap \
+                       --preferred-pack="$(head -n1 <packs)" <packs  &&
+               test-tool read-midx --bitmap $objdir >actual &&
+               for i in $(test_seq $(wc -l <packs))
+               do
+                       sed -ne "${i}s/\.idx$/\.pack/p" packs &&
+                       echo "  bitmap_pos: $((($i - 1) * 3))" &&
+                       echo "  bitmap_nr: 3" || return 1
+               done >expect &&
+               test_cmp expect actual
+       )
+'
+
 test_done
diff --git a/t/t5332-multi-pack-reuse.sh b/t/t5332-multi-pack-reuse.sh
new file mode 100755 (executable)
index 0000000..2ba788b
--- /dev/null
@@ -0,0 +1,203 @@
+#!/bin/sh
+
+test_description='pack-objects multi-pack reuse'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-bitmap.sh
+
+objdir=.git/objects
+packdir=$objdir/pack
+
+test_pack_reused () {
+       test_trace2_data pack-objects pack-reused "$1"
+}
+
+test_packs_reused () {
+       test_trace2_data pack-objects packs-reused "$1"
+}
+
+
+# pack_position <object> </path/to/pack.idx
+pack_position () {
+       git show-index >objects &&
+       grep "$1" objects | cut -d" " -f1
+}
+
+test_expect_success 'preferred pack is reused for single-pack reuse' '
+       test_config pack.allowPackReuse single &&
+
+       for i in A B
+       do
+               test_commit "$i" &&
+               git repack -d || return 1
+       done &&
+
+       git multi-pack-index write --bitmap &&
+
+       : >trace2.txt &&
+       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+               git pack-objects --stdout --revs --all >/dev/null &&
+
+       test_pack_reused 3 <trace2.txt &&
+       test_packs_reused 1 <trace2.txt
+'
+
+test_expect_success 'enable multi-pack reuse' '
+       git config pack.allowPackReuse multi
+'
+
+test_expect_success 'reuse all objects from subset of bitmapped packs' '
+       test_commit C &&
+       git repack -d &&
+
+       git multi-pack-index write --bitmap &&
+
+       cat >in <<-EOF &&
+       $(git rev-parse C)
+       ^$(git rev-parse A)
+       EOF
+
+       : >trace2.txt &&
+       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+               git pack-objects --stdout --revs <in >/dev/null &&
+
+       test_pack_reused 6 <trace2.txt &&
+       test_packs_reused 2 <trace2.txt
+'
+
+test_expect_success 'reuse all objects from all packs' '
+       : >trace2.txt &&
+       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+               git pack-objects --stdout --revs --all >/dev/null &&
+
+       test_pack_reused 9 <trace2.txt &&
+       test_packs_reused 3 <trace2.txt
+'
+
+test_expect_success 'reuse objects from first pack with middle gap' '
+       for i in D E F
+       do
+               test_commit "$i" || return 1
+       done &&
+
+       # Set "pack.window" to zero to ensure that we do not create any
+       # deltas, which could alter the amount of pack reuse we perform
+       # (if, for e.g., we are not sending one or more bases).
+       D="$(git -c pack.window=0 pack-objects --all --unpacked $packdir/pack)" &&
+
+       d_pos="$(pack_position $(git rev-parse D) <$packdir/pack-$D.idx)" &&
+       e_pos="$(pack_position $(git rev-parse E) <$packdir/pack-$D.idx)" &&
+       f_pos="$(pack_position $(git rev-parse F) <$packdir/pack-$D.idx)" &&
+
+       # commits F, E, and D, should appear in that order at the
+       # beginning of the pack
+       test $f_pos -lt $e_pos &&
+       test $e_pos -lt $d_pos &&
+
+       # Ensure that the pack we are constructing sorts ahead of any
+       # other packs in lexical/bitmap order by choosing it as the
+       # preferred pack.
+       git multi-pack-index write --bitmap --preferred-pack="pack-$D.idx" &&
+
+       cat >in <<-EOF &&
+       $(git rev-parse E)
+       ^$(git rev-parse D)
+       EOF
+
+       : >trace2.txt &&
+       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+               git pack-objects --stdout --delta-base-offset --revs <in >/dev/null &&
+
+       test_pack_reused 3 <trace2.txt &&
+       test_packs_reused 1 <trace2.txt
+'
+
+test_expect_success 'reuse objects from middle pack with middle gap' '
+       rm -fr $packdir/multi-pack-index* &&
+
+       # Ensure that the pack we are constructing sort into any
+       # position *but* the first one, by choosing a different pack as
+       # the preferred one.
+       git multi-pack-index write --bitmap --preferred-pack="pack-$A.idx" &&
+
+       cat >in <<-EOF &&
+       $(git rev-parse E)
+       ^$(git rev-parse D)
+       EOF
+
+       : >trace2.txt &&
+       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+               git pack-objects --stdout --delta-base-offset --revs <in >/dev/null &&
+
+       test_pack_reused 3 <trace2.txt &&
+       test_packs_reused 1 <trace2.txt
+'
+
+test_expect_success 'omit delta with uninteresting base (same pack)' '
+       git repack -adk &&
+
+       test_seq 32 >f &&
+       git add f &&
+       test_tick &&
+       git commit -m "delta" &&
+       delta="$(git rev-parse HEAD)" &&
+
+       test_seq 64 >f &&
+       test_tick &&
+       git commit -a -m "base" &&
+       base="$(git rev-parse HEAD)" &&
+
+       test_commit other &&
+
+       git repack -d &&
+
+       have_delta "$(git rev-parse $delta:f)" "$(git rev-parse $base:f)" &&
+
+       git multi-pack-index write --bitmap &&
+
+       cat >in <<-EOF &&
+       $(git rev-parse other)
+       ^$base
+       EOF
+
+       : >trace2.txt &&
+       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+               git pack-objects --stdout --delta-base-offset --revs <in >/dev/null &&
+
+       # We can only reuse the 3 objects corresponding to "other" from
+       # the latest pack.
+       #
+       # This is because even though we want "delta", we do not want
+       # "base", meaning that we have to inflate the delta/base-pair
+       # corresponding to the blob in commit "delta", which bypasses
+       # the pack-reuse mechanism.
+       #
+       # The remaining objects from the other pack are similarly not
+       # reused because their objects are on the uninteresting side of
+       # the query.
+       test_pack_reused 3 <trace2.txt &&
+       test_packs_reused 1 <trace2.txt
+'
+
+test_expect_success 'omit delta from uninteresting base (cross pack)' '
+       cat >in <<-EOF &&
+       $(git rev-parse $base)
+       ^$(git rev-parse $delta)
+       EOF
+
+       P="$(git pack-objects --revs $packdir/pack <in)" &&
+
+       git multi-pack-index write --bitmap --preferred-pack="pack-$P.idx" &&
+
+       : >trace2.txt &&
+       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
+               git pack-objects --stdout --delta-base-offset --all >/dev/null &&
+
+       packs_nr="$(find $packdir -type f -name "pack-*.pack" | wc -l)" &&
+       objects_nr="$(git rev-list --count --all --objects)" &&
+
+       test_pack_reused $(($objects_nr - 1)) <trace2.txt &&
+       test_packs_reused $packs_nr <trace2.txt
+'
+
+test_done
index 001b7a17ad2bb93610198ebf134ccbedc2781f00..8b8bc47dc0b9d6a8d90928c628bbe122d5d04f31 100755 (executable)
@@ -133,10 +133,8 @@ test_expect_success 'pre-receive hook that forgets to read its input' '
        EOF
        rm -f victim.git/hooks/update victim.git/hooks/post-update &&
 
-       for v in $(test_seq 100 999)
-       do
-               git branch branch_$v main || return
-       done &&
+       printf "create refs/heads/branch_%d main\n" $(test_seq 100 999) >input &&
+       git update-ref --stdin <input &&
        git push ./victim.git "+refs/heads/*:refs/heads/*"
 '
 
index dcadd56d3a6f473fc2fc11f5a3e55261d8c0cc13..33d34d5ae9e953e29f6dba8badb16922072cd76c 100755 (executable)
@@ -169,6 +169,7 @@ test_expect_success REFFILES 'fetch --prune fails to delete branches' '
        git clone . prune-fail &&
        cd prune-fail &&
        git update-ref refs/remotes/origin/extrabranch main &&
+       git pack-refs --all &&
        : this will prevent --prune from locking packed-refs for deleting refs, but adding loose refs still succeeds  &&
        >.git/packed-refs.new &&
 
@@ -802,7 +803,8 @@ test_expect_success 'fetch.writeCommitGraph with submodules' '
                cd super-clone &&
                rm -rf .git/objects/info &&
                git -c fetch.writeCommitGraph=true fetch origin &&
-               test_path_is_file .git/objects/info/commit-graphs/commit-graph-chain
+               test_path_is_file .git/objects/info/commit-graphs/commit-graph-chain &&
+               git -c fetch.writeCommitGraph=true fetch --recurse-submodules origin
        )
 '
 
index a95841dc368329286a798982ffac30b7321e02df..25772c85c5a9a89bf247cfa41cf9d8b127572cd8 100755 (executable)
@@ -24,6 +24,15 @@ setup_repository () {
        )
 }
 
+setup_test_clone () {
+       test_dir="$1" &&
+       git clone one "$test_dir" &&
+       for r in one two three
+       do
+               git -C "$test_dir" remote add "$r" "../$r" || return 1
+       done
+}
+
 test_expect_success setup '
        setup_repository one &&
        setup_repository two &&
@@ -209,4 +218,156 @@ test_expect_success 'git fetch --multiple --jobs=0 picks a default' '
         git fetch --multiple --jobs=0)
 '
 
+create_fetch_all_expect () {
+       cat >expect <<-\EOF
+         one/main
+         one/side
+         origin/HEAD -> origin/main
+         origin/main
+         origin/side
+         three/another
+         three/main
+         three/side
+         two/another
+         two/main
+         two/side
+       EOF
+}
+
+for fetch_all in true false
+do
+       test_expect_success "git fetch --all (works with fetch.all = $fetch_all)" '
+               test_dir="test_fetch_all_$fetch_all" &&
+               setup_test_clone "$test_dir" &&
+               (
+                       cd "$test_dir" &&
+                       git config fetch.all $fetch_all &&
+                       git fetch --all &&
+                       create_fetch_all_expect &&
+                       git branch -r >actual &&
+                       test_cmp expect actual
+               )
+       '
+done
+
+test_expect_success 'git fetch (fetch all remotes with fetch.all = true)' '
+       setup_test_clone test9 &&
+       (
+               cd test9 &&
+               git config fetch.all true &&
+               git fetch &&
+               git branch -r >actual &&
+               create_fetch_all_expect &&
+               test_cmp expect actual
+       )
+'
+
+create_fetch_one_expect () {
+       cat >expect <<-\EOF
+         one/main
+         one/side
+         origin/HEAD -> origin/main
+         origin/main
+         origin/side
+       EOF
+}
+
+test_expect_success 'git fetch one (explicit remote overrides fetch.all)' '
+       setup_test_clone test10 &&
+       (
+               cd test10 &&
+               git config fetch.all true &&
+               git fetch one &&
+               create_fetch_one_expect &&
+               git branch -r >actual &&
+               test_cmp expect actual
+       )
+'
+
+create_fetch_two_as_origin_expect () {
+       cat >expect <<-\EOF
+         origin/HEAD -> origin/main
+         origin/another
+         origin/main
+         origin/side
+       EOF
+}
+
+test_expect_success 'git config fetch.all false (fetch only default remote)' '
+       setup_test_clone test11 &&
+       (
+               cd test11 &&
+               git config fetch.all false &&
+               git remote set-url origin ../two &&
+               git fetch &&
+               create_fetch_two_as_origin_expect &&
+               git branch -r >actual &&
+               test_cmp expect actual
+       )
+'
+
+for fetch_all in true false
+do
+       test_expect_success "git fetch --no-all (fetch only default remote with fetch.all = $fetch_all)" '
+               test_dir="test_no_all_fetch_all_$fetch_all" &&
+               setup_test_clone "$test_dir" &&
+               (
+                       cd "$test_dir" &&
+                       git config fetch.all $fetch_all &&
+                       git remote set-url origin ../two &&
+                       git fetch --no-all &&
+                       create_fetch_two_as_origin_expect &&
+                       git branch -r >actual &&
+                       test_cmp expect actual
+               )
+       '
+done
+
+test_expect_success 'git fetch --no-all (fetch only default remote without fetch.all)' '
+       setup_test_clone test12 &&
+       (
+               cd test12 &&
+               git config --unset-all fetch.all || true &&
+               git remote set-url origin ../two &&
+               git fetch --no-all &&
+               create_fetch_two_as_origin_expect &&
+               git branch -r >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'git fetch --all --no-all (fetch only default remote)' '
+       setup_test_clone test13 &&
+       (
+               cd test13 &&
+               git remote set-url origin ../two &&
+               git fetch --all --no-all &&
+               create_fetch_two_as_origin_expect &&
+               git branch -r >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'git fetch --no-all one (fetch only explicit remote)' '
+       setup_test_clone test14 &&
+       (
+               cd test14 &&
+               git fetch --no-all one &&
+               create_fetch_one_expect &&
+               git branch -r >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'git fetch --no-all --all (fetch all remotes)' '
+       setup_test_clone test15 &&
+       (
+               cd test15 &&
+               git fetch --no-all --all &&
+               create_fetch_all_expect &&
+               git branch -r >actual &&
+               test_cmp expect actual
+       )
+'
+
 test_done
index 26e933f93ae7f72261046495f25f63af8c31349d..7ab220fa3135a1ecbaa8f69a5db8a9b1298f4ce1 100755 (executable)
@@ -771,7 +771,7 @@ test_expect_success 'fetching submodule into a broken repository' '
        git -C dst fetch --recurse-submodules &&
 
        # Break the receiving submodule
-       rm -f dst/sub/.git/HEAD &&
+       test-tool -C dst/sub ref-store main delete-refs REF_NO_DEREF msg HEAD &&
 
        # NOTE: without the fix the following tests will recurse forever!
        # They should terminate with an error.
index df758e187dfdc2fc869ad5cb747d895a145d5e91..71428f3d5c760a9bdca7524e15cb9747e92cfd17 100755 (executable)
@@ -232,8 +232,9 @@ test_expect_success 'push --atomic fails on server-side errors' '
        test_config -C "$d" http.receivepack true &&
        up="$HTTPD_URL"/smart/atomic-branches.git &&
 
-       # break ref updates for other on the remote site
-       mkdir "$d/refs/heads/other.lock" &&
+       # Create d/f conflict to break ref updates for other on the remote site.
+       git -C "$d" update-ref -d refs/heads/other &&
+       git -C "$d" update-ref refs/heads/other/conflict HEAD &&
 
        # add the new commit to other
        git branch -f other collateral &&
@@ -241,18 +242,9 @@ test_expect_success 'push --atomic fails on server-side errors' '
        # --atomic should cause entire push to be rejected
        test_must_fail git push --atomic "$up" atomic other 2>output  &&
 
-       # the new branch should not have been created upstream
-       test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
-
-       # upstream should still reflect atomic2, the last thing we pushed
-       # successfully
-       git rev-parse atomic2 >expected &&
-       # ...to other.
-       git -C "$d" rev-parse refs/heads/other >actual &&
-       test_cmp expected actual &&
-
-       # the new branch should not have been created upstream
+       # The atomic and other branches should not be created upstream.
        test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
+       test_must_fail git -C "$d" show-ref --verify refs/heads/other &&
 
        # the failed refs should be indicated to the user
        grep "^ ! .*rejected.* other -> other .*atomic transaction failed" output &&
index e444b30bf6156817fa624a9a3c9ed7127e61c3e1..4c3b32785d580fb32846306df720629aee9b3a4b 100755 (executable)
@@ -66,11 +66,11 @@ test_expect_success 'create empty remote repository' '
        setup_post_update_server_info_hook "$HTTPD_DOCUMENT_ROOT_PATH/empty.git"
 '
 
-test_expect_success 'empty dumb HTTP repository has default hash algorithm' '
+test_expect_success 'empty dumb HTTP repository falls back to SHA1' '
        test_when_finished "rm -fr clone-empty" &&
        git clone $HTTPD_URL/dumb/empty.git clone-empty &&
        git -C clone-empty rev-parse --show-object-format >empty-format &&
-       test "$(cat empty-format)" = "$(test_oid algo)"
+       test "$(cat empty-format)" = sha1
 '
 
 setup_askpass_helper
index 8a41adf1e1f078712a423b0f41541afb1b5f045d..e069737b80b7bd906afa1963f7b818ffac8ed2ca 100755 (executable)
@@ -359,7 +359,9 @@ create_tags () {
 
        # now assign tags to all the dangling commits we created above
        tag=$(perl -e "print \"bla\" x 30") &&
-       sed -e "s|^:\([^ ]*\) \(.*\)$|\2 refs/tags/$tag-\1|" <marks >>packed-refs
+       sed -e "s|^:\([^ ]*\) \(.*\)$|create refs/tags/$tag-\1 \2|" <marks >input &&
+       git update-ref --stdin <input &&
+       rm input
 }
 
 test_expect_success 'create 2,000 tags in the repo' '
index 996a08e90c9c2c5b2c37405b1b3082e82d29a7e8..1ca5f745e73bd30deb9fc2dfa79baa10d63345bf 100755 (executable)
@@ -33,6 +33,15 @@ test_expect_success 'clone with path bundle' '
        test_cmp expect actual
 '
 
+test_expect_success 'clone with path bundle and non-default hash' '
+       test_when_finished "rm -rf clone-path-non-default-hash" &&
+       GIT_DEFAULT_HASH=sha256 git clone --bundle-uri="clone-from/B.bundle" \
+               clone-from clone-path-non-default-hash &&
+       git -C clone-path-non-default-hash rev-parse refs/bundles/topic >actual &&
+       git -C clone-from rev-parse topic >expect &&
+       test_cmp expect actual
+'
+
 test_expect_success 'clone with file:// bundle' '
        git clone --bundle-uri="file://$(pwd)/clone-from/B.bundle" \
                clone-from clone-file &&
@@ -284,6 +293,15 @@ test_expect_success 'clone HTTP bundle' '
        test_config -C clone-http log.excludedecoration refs/bundle/
 '
 
+test_expect_success 'clone HTTP bundle with non-default hash' '
+       test_when_finished "rm -rf clone-http-non-default-hash" &&
+       GIT_DEFAULT_HASH=sha256 git clone --bundle-uri="$HTTPD_URL/B.bundle" \
+               "$HTTPD_URL/smart/fetch.git" clone-http-non-default-hash &&
+       git -C clone-http-non-default-hash rev-parse refs/bundles/topic >actual &&
+       git -C clone-from rev-parse topic >expect &&
+       test_cmp expect actual
+'
+
 test_expect_success 'clone bundle list (HTTP, no heuristic)' '
        test_when_finished rm -f trace*.txt &&
 
index 718dd9b49d493ec55e2503dcb14aa5ddcc26cc6f..9babb9a375e5fb2a66f216d1968312ec094a6d93 100644 (file)
@@ -1,4 +1,4 @@
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 
index a9b06b22510ebe58c0faafd4ac05830c1bf64f3a..5883839a04e991d6ab93a965698662ced064fe3d 100755 (executable)
@@ -61,11 +61,10 @@ test_expect_success 'fetch compact output' '
        test_cmp expect actual
 '
 
-test_expect_success 'fetch porcelain output' '
-       test_when_finished "rm -rf porcelain" &&
-
+test_expect_success 'setup for fetch porcelain output' '
        # Set up a bunch of references that we can use to demonstrate different
        # kinds of flag symbols in the output format.
+       test_commit commit-for-porcelain-output &&
        MAIN_OLD=$(git rev-parse HEAD) &&
        git branch "fast-forward" &&
        git branch "deleted-branch" &&
@@ -74,15 +73,10 @@ test_expect_success 'fetch porcelain output' '
        FORCE_UPDATED_OLD=$(git rev-parse HEAD) &&
        git checkout main &&
 
-       # Clone and pre-seed the repositories. We fetch references into two
-       # namespaces so that we can test that rejected and force-updated
-       # references are reported properly.
-       refspecs="refs/heads/*:refs/unforced/* +refs/heads/*:refs/forced/*" &&
-       git clone . porcelain &&
-       git -C porcelain fetch origin $refspecs &&
+       # Backup to preseed.git
+       git clone --mirror . preseed.git &&
 
-       # Now that we have set up the client repositories we can change our
-       # local references.
+       # Continue changing our local references.
        git branch new-branch &&
        git branch -d deleted-branch &&
        git checkout fast-forward &&
@@ -91,36 +85,53 @@ test_expect_success 'fetch porcelain output' '
        git checkout force-updated &&
        git reset --hard HEAD~ &&
        test_commit --no-tag force-update-new &&
-       FORCE_UPDATED_NEW=$(git rev-parse HEAD) &&
-
-       cat >expect <<-EOF &&
-       - $MAIN_OLD $ZERO_OID refs/forced/deleted-branch
-       - $MAIN_OLD $ZERO_OID refs/unforced/deleted-branch
-         $MAIN_OLD $FAST_FORWARD_NEW refs/unforced/fast-forward
-       ! $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/unforced/force-updated
-       * $ZERO_OID $MAIN_OLD refs/unforced/new-branch
-         $MAIN_OLD $FAST_FORWARD_NEW refs/forced/fast-forward
-       + $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/forced/force-updated
-       * $ZERO_OID $MAIN_OLD refs/forced/new-branch
-         $MAIN_OLD $FAST_FORWARD_NEW refs/remotes/origin/fast-forward
-       + $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/remotes/origin/force-updated
-       * $ZERO_OID $MAIN_OLD refs/remotes/origin/new-branch
-       EOF
-
-       # Execute a dry-run fetch first. We do this to assert that the dry-run
-       # and non-dry-run fetches produces the same output. Execution of the
-       # fetch is expected to fail as we have a rejected reference update.
-       test_must_fail git -C porcelain fetch \
-               --porcelain --dry-run --prune origin $refspecs >actual &&
-       test_cmp expect actual &&
-
-       # And now we perform a non-dry-run fetch.
-       test_must_fail git -C porcelain fetch \
-               --porcelain --prune origin $refspecs >actual 2>stderr &&
-       test_cmp expect actual &&
-       test_must_be_empty stderr
+       FORCE_UPDATED_NEW=$(git rev-parse HEAD)
 '
 
+for opt in "" "--atomic"
+do
+       test_expect_success "fetch porcelain output ${opt:+(atomic)}" '
+               test_when_finished "rm -rf porcelain" &&
+
+               # Clone and pre-seed the repositories. We fetch references into two
+               # namespaces so that we can test that rejected and force-updated
+               # references are reported properly.
+               refspecs="refs/heads/*:refs/unforced/* +refs/heads/*:refs/forced/*" &&
+               git clone preseed.git porcelain &&
+               git -C porcelain fetch origin $opt $refspecs &&
+
+               cat >expect <<-EOF &&
+               - $MAIN_OLD $ZERO_OID refs/forced/deleted-branch
+               - $MAIN_OLD $ZERO_OID refs/unforced/deleted-branch
+                 $MAIN_OLD $FAST_FORWARD_NEW refs/unforced/fast-forward
+               ! $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/unforced/force-updated
+               * $ZERO_OID $MAIN_OLD refs/unforced/new-branch
+                 $MAIN_OLD $FAST_FORWARD_NEW refs/forced/fast-forward
+               + $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/forced/force-updated
+               * $ZERO_OID $MAIN_OLD refs/forced/new-branch
+                 $MAIN_OLD $FAST_FORWARD_NEW refs/remotes/origin/fast-forward
+               + $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/remotes/origin/force-updated
+               * $ZERO_OID $MAIN_OLD refs/remotes/origin/new-branch
+               EOF
+
+               # Change the URL of the repository to fetch different references.
+               git -C porcelain remote set-url origin .. &&
+
+               # Execute a dry-run fetch first. We do this to assert that the dry-run
+               # and non-dry-run fetches produces the same output. Execution of the
+               # fetch is expected to fail as we have a rejected reference update.
+               test_must_fail git -C porcelain fetch $opt \
+                       --porcelain --dry-run --prune origin $refspecs >actual &&
+               test_cmp expect actual &&
+
+               # And now we perform a non-dry-run fetch.
+               test_must_fail git -C porcelain fetch $opt \
+                       --porcelain --prune origin $refspecs >actual 2>stderr &&
+               test_cmp expect actual &&
+               test_must_be_empty stderr
+       '
+done
+
 test_expect_success 'fetch porcelain with multiple remotes' '
        test_when_finished "rm -rf porcelain" &&
 
index 47eae641f018055f2200ef6e2581d8ac60364cdb..fb1b9c686db24a30f9dbd2f6f38f29bfc4624676 100755 (executable)
@@ -157,6 +157,23 @@ test_expect_success 'clone --mirror does not repeat tags' '
 
 '
 
+test_expect_success 'clone with files ref format' '
+       test_when_finished "rm -rf ref-storage" &&
+       git clone --ref-format=files --mirror src ref-storage &&
+       echo files >expect &&
+       git -C ref-storage rev-parse --show-ref-format >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone with garbage ref format' '
+       cat >expect <<-EOF &&
+       fatal: unknown ref storage format ${SQ}garbage${SQ}
+       EOF
+       test_must_fail git clone --ref-format=garbage --mirror src ref-storage 2>err &&
+       test_cmp expect err &&
+       test_path_is_missing ref-storage
+'
+
 test_expect_success 'clone to destination with trailing /' '
 
        git clone src target-1/ &&
index 946c5751885671859ae309458c34106b79d67966..a3055869bc7bc182e2e4af736b06785ee28bed85 100755 (executable)
@@ -65,7 +65,7 @@ test_expect_success 'Even without -l, local will make a hardlink' '
 '
 
 test_expect_success 'local clone of repo with nonexistent ref in HEAD' '
-       echo "ref: refs/heads/nonexistent" > a.git/HEAD &&
+       git -C a.git symbolic-ref HEAD refs/heads/nonexistent &&
        git clone a d &&
        (cd d &&
        git fetch &&
@@ -157,7 +157,7 @@ test_expect_success 'cloning locally respects "-u" for fetching refs' '
        test_must_fail git clone --bare -u false a should_not_work.git
 '
 
-test_expect_success 'local clone from repo with corrupt refs fails gracefully' '
+test_expect_success REFFILES 'local clone from repo with corrupt refs fails gracefully' '
        git init corrupt &&
        test_commit -C corrupt one &&
        echo a >corrupt/.git/refs/heads/topic &&
index fc4bbd9daf4fd3bf1777699f05af9ca39ac5a954..a400bcca622f464f13fe4f0a57c4d44ec8dd827b 100755 (executable)
@@ -64,7 +64,7 @@ test_expect_success 'disallows --bundle-uri with shallow options' '
        for option in --depth=1 --shallow-since=01-01-2000 --shallow-exclude=HEAD
        do
                test_must_fail git clone --bundle-uri=bundle $option from to 2>err &&
-               grep "bundle-uri is incompatible" err || return 1
+               grep "bundle-uri.* cannot be used together" err || return 1
        done
 '
 
index 3c0c6047d5778cb478fa7b8a4903f4457107f30e..6ef4971845fb4407977e98cb62b3468abfca5838 100755 (executable)
@@ -221,7 +221,9 @@ test_expect_success 'clone of empty repo propagates name of default branch' '
        GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
        git -c init.defaultBranch=main -c protocol.version=2 \
                clone "file://$(pwd)/file_empty_parent" file_empty_child &&
-       grep "refs/heads/mydefaultbranch" file_empty_child/.git/HEAD
+       echo refs/heads/mydefaultbranch >expect &&
+       git -C file_empty_child symbolic-ref HEAD >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success '...but not if explicitly forbidden by config' '
@@ -234,7 +236,9 @@ test_expect_success '...but not if explicitly forbidden by config' '
        GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
        git -c init.defaultBranch=main -c protocol.version=2 \
                clone "file://$(pwd)/file_empty_parent" file_empty_child &&
-       ! grep "refs/heads/mydefaultbranch" file_empty_child/.git/HEAD
+       echo refs/heads/main >expect &&
+       git -C file_empty_child symbolic-ref HEAD >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'bare clone propagates empty default branch' '
@@ -247,7 +251,9 @@ test_expect_success 'bare clone propagates empty default branch' '
        git -c init.defaultBranch=main -c protocol.version=2 \
                clone --bare \
                "file://$(pwd)/file_empty_parent" file_empty_child.git &&
-       grep "refs/heads/mydefaultbranch" file_empty_child.git/HEAD
+       echo "refs/heads/mydefaultbranch" >expect &&
+       git -C file_empty_child.git symbolic-ref HEAD >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'clone propagates unborn HEAD from non-empty repo' '
@@ -265,7 +271,9 @@ test_expect_success 'clone propagates unborn HEAD from non-empty repo' '
        git -c init.defaultBranch=main -c protocol.version=2 \
                clone "file://$(pwd)/file_unborn_parent" \
                file_unborn_child 2>stderr &&
-       grep "refs/heads/mydefaultbranch" file_unborn_child/.git/HEAD &&
+       echo "refs/heads/mydefaultbranch" >expect &&
+       git -C file_unborn_child symbolic-ref HEAD >actual &&
+       test_cmp expect actual &&
        grep "warning: remote HEAD refers to nonexistent ref" stderr
 '
 
@@ -295,7 +303,9 @@ test_expect_success 'bare clone propagates unborn HEAD from non-empty repo' '
        git -c init.defaultBranch=main -c protocol.version=2 \
                clone --bare "file://$(pwd)/file_unborn_parent" \
                file_unborn_child.git 2>stderr &&
-       grep "refs/heads/mydefaultbranch" file_unborn_child.git/HEAD &&
+       echo "refs/heads/mydefaultbranch" >expect &&
+       git -C file_unborn_child.git symbolic-ref HEAD >actual &&
+       test_cmp expect actual &&
        ! grep "warning:" stderr
 '
 
@@ -315,7 +325,9 @@ test_expect_success 'defaulted HEAD uses remote branch if available' '
        git -c init.defaultBranch=branchwithstuff -c protocol.version=2 \
                clone "file://$(pwd)/file_unborn_parent" \
                file_unborn_child 2>stderr &&
-       grep "refs/heads/branchwithstuff" file_unborn_child/.git/HEAD &&
+       echo "refs/heads/branchwithstuff" >expect &&
+       git -C file_unborn_child symbolic-ref HEAD >actual &&
+       test_cmp expect actual &&
        test_path_is_file file_unborn_child/stuff.t &&
        ! grep "warning:" stderr
 '
index 0729f800c3c95790946d68296240799c2dbbb5ad..ee0306aeec0b6e60fc9b62e7efbc5b659fbc1c72 100755 (executable)
@@ -18,20 +18,34 @@ test_expect_success 'no options' '
 '
 
 test_expect_success '--max-count' '
+       test_must_fail git rev-list --max-count=1q HEAD 2>error &&
+       grep "not an integer" error &&
+
        test_stdout_line_count = 0 git rev-list HEAD --max-count=0 &&
        test_stdout_line_count = 3 git rev-list HEAD --max-count=3 &&
        test_stdout_line_count = 5 git rev-list HEAD --max-count=5 &&
-       test_stdout_line_count = 5 git rev-list HEAD --max-count=10
+       test_stdout_line_count = 5 git rev-list HEAD --max-count=10 &&
+       test_stdout_line_count = 5 git rev-list HEAD --max-count=-1
 '
 
 test_expect_success '--max-count all forms' '
+       test_must_fail git rev-list -1q HEAD 2>error &&
+       grep "not an integer" error &&
+       test_must_fail git rev-list --1 HEAD &&
+       test_must_fail git rev-list -n 1q HEAD 2>error &&
+       grep "not an integer" error &&
+
        test_stdout_line_count = 1 git rev-list HEAD --max-count=1 &&
        test_stdout_line_count = 1 git rev-list HEAD -1 &&
        test_stdout_line_count = 1 git rev-list HEAD -n1 &&
-       test_stdout_line_count = 1 git rev-list HEAD -n 1
+       test_stdout_line_count = 1 git rev-list HEAD -n 1 &&
+       test_stdout_line_count = 5 git rev-list HEAD -n -1
 '
 
 test_expect_success '--skip' '
+       test_must_fail git rev-list --skip 1q HEAD 2>error &&
+       grep "not an integer" error &&
+
        test_stdout_line_count = 5 git rev-list HEAD --skip=0 &&
        test_stdout_line_count = 2 git rev-list HEAD --skip=3 &&
        test_stdout_line_count = 0 git rev-list HEAD --skip=5 &&
index ced40157ed68c077c17cb4758d1ebbc529e67d9c..91db8fafe83e3883923b71e36bbd8bc11bfde1f6 100755 (executable)
@@ -63,6 +63,17 @@ test_expect_success 'setup roots, merges and octopuses' '
        git checkout main
 '
 
+test_expect_success 'parse --max-parents & --min-parents' '
+       test_must_fail git rev-list --max-parents=1q HEAD 2>error &&
+       grep "not an integer" error &&
+
+       test_must_fail git rev-list --min-parents=1q HEAD 2>error &&
+       grep "not an integer" error &&
+
+       git rev-list --max-parents=1 --min-parents=1 HEAD &&
+       git rev-list --max-parents=-1 --min-parents=-1 HEAD
+'
+
 test_expect_success 'rev-list roots' '
 
        check_revlist "--max-parents=0" one five
index 67d523d40571b89b50cc5ca655ec77198c1a3ae3..3b181f771c25a3d8a074f7d0472bf1f0422f582b 100755 (executable)
@@ -214,15 +214,13 @@ do
        for pseudoopt in branches tags remotes
        do
                test_expect_success "rev-parse --exclude-hidden=$section fails with --$pseudoopt" '
-                       echo "error: --exclude-hidden cannot be used together with --$pseudoopt" >expected &&
                        test_must_fail git rev-parse --exclude-hidden=$section --$pseudoopt 2>err &&
-                       test_cmp expected err
+                       test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
                '
 
                test_expect_success "rev-parse --exclude-hidden=$section fails with --$pseudoopt=pattern" '
-                       echo "error: --exclude-hidden cannot be used together with --$pseudoopt" >expected &&
                        test_must_fail git rev-parse --exclude-hidden=$section --$pseudoopt=pattern 2>err &&
-                       test_cmp expected err
+                       test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
                '
        done
 done
index cdf7aa94276cb9e993dcad825994828322e5b3ea..51df02105d7b83c3b0b1c85f95d419ac9a4bbfe1 100755 (executable)
@@ -151,12 +151,12 @@ do
        do
                test_expect_success "$section: fails with --$pseudoopt" '
                        test_must_fail git rev-list --exclude-hidden=$section --$pseudoopt 2>err &&
-                       test_grep "error: --exclude-hidden cannot be used together with --$pseudoopt" err
+                       test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
                '
 
                test_expect_success "$section: fails with --$pseudoopt=pattern" '
                        test_must_fail git rev-list --exclude-hidden=$section --$pseudoopt=pattern 2>err &&
-                       test_grep "error: --exclude-hidden cannot be used together with --$pseudoopt" err
+                       test_grep "error: options .--exclude-hidden. and .--$pseudoopt. cannot be used together" err
                '
        done
 done
index 40265a4f66f996501207e6a6e950f45551453cf6..211672759a2260e5a1bb572f96aad93b3d3f91e5 100755 (executable)
@@ -13,6 +13,12 @@ test_expect_success 'create repository and alternate directory' '
        test_commit 3
 '
 
+# We manually corrupt the repository, which means that the commit-graph may
+# contain references to already-deleted objects. We thus need to enable
+# commit-graph paranoia to not returned these deleted commits from the graph.
+GIT_COMMIT_GRAPH_PARANOIA=true
+export GIT_COMMIT_GRAPH_PARANOIA
+
 for obj in "HEAD~1" "HEAD~1^{tree}" "HEAD:1.t"
 do
        test_expect_success "rev-list --missing=error fails with missing object $obj" '
index 2a5b7d8379c2a9ced13f7eb85e29d0c895450e2f..561080bf240d6d5de7b557c6332995846f60e56e 100755 (executable)
@@ -170,6 +170,12 @@ test_expect_success 'bisect reset when not bisecting' '
        cmp branch.expect branch.output
 '
 
+test_expect_success 'bisect reset cleans up even when not bisecting' '
+       echo garbage >.git/BISECT_LOG &&
+       git bisect reset &&
+       test_path_is_missing .git/BISECT_LOG
+'
+
 test_expect_success 'bisect reset removes packed refs' '
        git bisect reset &&
        git bisect start &&
@@ -1176,7 +1182,7 @@ test_expect_success 'git bisect reset cleans bisection state properly' '
        git bisect bad $HASH4 &&
        git bisect reset &&
        test -z "$(git for-each-ref "refs/bisect/*")" &&
-       test_path_is_missing ".git/BISECT_EXPECTED_REV" &&
+       test_ref_missing BISECT_EXPECTED_REV &&
        test_path_is_missing ".git/BISECT_ANCESTORS_OK" &&
        test_path_is_missing ".git/BISECT_LOG" &&
        test_path_is_missing ".git/BISECT_RUN" &&
index 86c70521f1db7184e70c1eb6baf17883ecc6609f..459f0d741225522276b84a9a65caaa2e2a985bde 100755 (executable)
@@ -4,6 +4,8 @@ test_description='rev-list combining bitmaps and filters'
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-bitmap.sh
 
+TEST_PASSES_SANITIZE_LEAK=true
+
 test_expect_success 'set up bitmapped repo' '
        # one commit will have bitmaps, the other will not
        test_commit one &&
index a9c1e4e0ecd0edf0269d3c1506ea1c8715c58d07..120dcd74a51434303b8bd7e71d10a9cb120bf574 100755 (executable)
@@ -64,12 +64,24 @@ test_expect_success 'setup .gitattributes' '
        fileSetLabel label
        fileValue label=foo
        fileWrongLabel label☺
+       newFileA* labelA
+       newFileB* labelB
        EOF
        echo fileSetLabel label1 >sub/.gitattributes &&
        git add .gitattributes sub/.gitattributes &&
        git commit -m "add attributes"
 '
 
+test_expect_success 'setup .gitignore' '
+       cat <<-\EOF >.gitignore &&
+       actual
+       expect
+       pathspec_file
+       EOF
+       git add .gitignore &&
+       git commit -m "add gitignore"
+'
+
 test_expect_success 'check specific set attr' '
        cat <<-\EOF >expect &&
        fileSetLabel
@@ -150,6 +162,7 @@ test_expect_success 'check specific value attr (2)' '
 test_expect_success 'check unspecified attr' '
        cat <<-\EOF >expect &&
        .gitattributes
+       .gitignore
        fileA
        fileAB
        fileAC
@@ -175,6 +188,7 @@ test_expect_success 'check unspecified attr' '
 test_expect_success 'check unspecified attr (2)' '
        cat <<-\EOF >expect &&
        HEAD:.gitattributes
+       HEAD:.gitignore
        HEAD:fileA
        HEAD:fileAB
        HEAD:fileAC
@@ -200,6 +214,7 @@ test_expect_success 'check unspecified attr (2)' '
 test_expect_success 'check multiple unspecified attr' '
        cat <<-\EOF >expect &&
        .gitattributes
+       .gitignore
        fileC
        fileNoLabel
        fileWrongLabel
@@ -239,16 +254,99 @@ test_expect_success 'fail on multiple attr specifiers in one pathspec item' '
        test_grep "Only one" actual
 '
 
-test_expect_success 'fail if attr magic is used places not implemented' '
+test_expect_success 'fail if attr magic is used in places not implemented' '
        # The main purpose of this test is to check that we actually fail
        # when you attempt to use attr magic in commands that do not implement
-       # attr magic. This test does not advocate git-add to stay that way,
-       # though, but git-add is convenient as it has its own internal pathspec
-       # parsing.
-       test_must_fail git add ":(attr:labelB)" 2>actual &&
+       # attr magic. This test does not advocate check-ignore to stay that way.
+       # When you teach the command to grok the pathspec, you need to find
+       # another command to replace it for the test.
+       test_must_fail git check-ignore ":(attr:labelB)" 2>actual &&
        test_grep "magic not supported" actual
 '
 
+test_expect_success 'check that attr magic works for git stash push' '
+       cat <<-\EOF >expect &&
+       A       sub/newFileA-foo
+       EOF
+       >sub/newFileA-foo &&
+       >sub/newFileB-foo &&
+       git stash push --include-untracked -- ":(exclude,attr:labelB)" &&
+       git stash show --include-untracked --name-status >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git add --all' '
+       cat <<-\EOF >expect &&
+       sub/newFileA-foo
+       EOF
+       >sub/newFileA-foo &&
+       >sub/newFileB-foo &&
+       git add --all ":(exclude,attr:labelB)" &&
+       git diff --name-only --cached >actual &&
+       git restore -W -S . &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git add -u' '
+       cat <<-\EOF >expect &&
+       sub/fileA
+       EOF
+       >sub/newFileA-foo &&
+       >sub/newFileB-foo &&
+       >sub/fileA &&
+       >sub/fileB &&
+       git add -u ":(exclude,attr:labelB)" &&
+       git diff --name-only --cached  >actual &&
+       git restore -S -W . && rm sub/new* &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git add <path>' '
+       cat <<-\EOF >expect &&
+       fileA
+       fileB
+       sub/fileA
+       EOF
+       >fileA &&
+       >fileB &&
+       >sub/fileA &&
+       >sub/fileB &&
+       git add ":(exclude,attr:labelB)sub/*" &&
+       git diff --name-only --cached >actual &&
+       git restore -S -W . &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git -add .' '
+       cat <<-\EOF >expect &&
+       sub/fileA
+       EOF
+       >fileA &&
+       >fileB &&
+       >sub/fileA &&
+       >sub/fileB &&
+       cd sub &&
+       git add . ":(exclude,attr:labelB)" &&
+       cd .. &&
+       git diff --name-only --cached >actual &&
+       git restore -S -W . &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check that attr magic works for git add --pathspec-from-file' '
+       cat <<-\EOF >pathspec_file &&
+       :(exclude,attr:labelB)
+       EOF
+       cat <<-\EOF >expect &&
+       sub/newFileA-foo
+       EOF
+       >sub/newFileA-foo &&
+       >sub/newFileB-foo &&
+       git add --all --pathspec-from-file=pathspec_file &&
+       git diff --name-only --cached >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'abort on giving invalid label on the command line' '
        test_must_fail git ls-files . ":(attr:☺)"
 '
@@ -295,4 +393,31 @@ test_expect_success 'reading from .gitattributes in a subdirectory (3)' '
        test_cmp expect actual
 '
 
+test_expect_success POSIXPERM 'pathspec with builtin_objectmode attr can be used' '
+       >mode_exec_file_1 &&
+
+       git status -s ":(attr:builtin_objectmode=100644)mode_exec_*" >actual &&
+       echo ?? mode_exec_file_1 >expect &&
+       test_cmp expect actual &&
+
+       git add mode_exec_file_1 &&
+       chmod +x mode_exec_file_1 &&
+       git status -s ":(attr:builtin_objectmode=100755)mode_exec_*" >actual &&
+       echo AM mode_exec_file_1 >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success POSIXPERM 'builtin_objectmode attr can be excluded' '
+       >mode_1_regular &&
+       >mode_1_exec  &&
+       chmod +x mode_1_exec &&
+       git status -s ":(exclude,attr:builtin_objectmode=100644)" "mode_1_*" >actual &&
+       echo ?? mode_1_exec >expect &&
+       test_cmp expect actual &&
+
+       git status -s ":(exclude,attr:builtin_objectmode=100755)" "mode_1_*" >actual &&
+       echo ?? mode_1_regular >expect &&
+       test_cmp expect actual
+'
+
 test_done
index 00a060df0b5e81cc1c47758240a7c790384b758e..843a7fe14313b52d1bb3267d9b1f82152f3ccb96 100755 (executable)
@@ -20,12 +20,13 @@ setdate_and_increment () {
     export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
 }
 
-test_expect_success setup '
-       test_oid_cache <<-EOF &&
-       disklen sha1:138
-       disklen sha256:154
-       EOF
+test_object_file_size () {
+       oid=$(git rev-parse "$1")
+       path=".git/objects/$(test_oid_to_path $oid)"
+       test_file_size "$path"
+}
 
+test_expect_success setup '
        # setup .mailmap
        cat >.mailmap <<-EOF &&
        A Thor <athor@example.com> A U Thor <author@example.com>
@@ -94,7 +95,6 @@ test_atom () {
 }
 
 hexlen=$(test_oid hexsz)
-disklen=$(test_oid disklen)
 
 test_atom head refname refs/heads/main
 test_atom head refname: refs/heads/main
@@ -129,7 +129,7 @@ test_atom head push:strip=1 remotes/myfork/main
 test_atom head push:strip=-1 main
 test_atom head objecttype commit
 test_atom head objectsize $((131 + hexlen))
-test_atom head objectsize:disk $disklen
+test_atom head objectsize:disk $(test_object_file_size refs/heads/main)
 test_atom head deltabase $ZERO_OID
 test_atom head objectname $(git rev-parse refs/heads/main)
 test_atom head objectname:short $(git rev-parse --short refs/heads/main)
@@ -203,8 +203,8 @@ test_atom tag upstream ''
 test_atom tag push ''
 test_atom tag objecttype tag
 test_atom tag objectsize $((114 + hexlen))
-test_atom tag objectsize:disk $disklen
-test_atom tag '*objectsize:disk' $disklen
+test_atom tag objectsize:disk $(test_object_file_size refs/tags/testtag)
+test_atom tag '*objectsize:disk' $(test_object_file_size refs/heads/main)
 test_atom tag deltabase $ZERO_OID
 test_atom tag '*deltabase' $ZERO_OID
 test_atom tag objectname $(git rev-parse refs/tags/testtag)
@@ -1335,6 +1335,27 @@ test_expect_success '--no-sort cancels the previous sort keys' '
        test_cmp expected actual
 '
 
+test_expect_success '--no-sort without subsequent --sort prints expected refs' '
+       cat >expected <<-\EOF &&
+       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
+       EOF
+
+       # Sort the results with `sort` for a consistent comparison against
+       # expected
+       git for-each-ref \
+               --format="%(refname)" \
+               --no-sort \
+               "refs/tags/multi-*" | sort >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '
        test_when_finished "git checkout main" &&
        git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual &&
@@ -1818,6 +1839,28 @@ test_expect_success 'git for-each-ref with non-existing refs' '
        test_must_be_empty actual
 '
 
+test_expect_success 'git for-each-ref with nested tags' '
+       git tag -am "Normal tag" nested/base HEAD &&
+       git tag -am "Nested tag" nested/nest1 refs/tags/nested/base &&
+       git tag -am "Double nested tag" nested/nest2 refs/tags/nested/nest1 &&
+
+       head_oid="$(git rev-parse HEAD)" &&
+       base_tag_oid="$(git rev-parse refs/tags/nested/base)" &&
+       nest1_tag_oid="$(git rev-parse refs/tags/nested/nest1)" &&
+       nest2_tag_oid="$(git rev-parse refs/tags/nested/nest2)" &&
+
+       cat >expect <<-EOF &&
+       refs/tags/nested/base $base_tag_oid tag $head_oid commit
+       refs/tags/nested/nest1 $nest1_tag_oid tag $head_oid commit
+       refs/tags/nested/nest2 $nest2_tag_oid tag $head_oid commit
+       EOF
+
+       git for-each-ref \
+               --format="%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)" \
+               refs/tags/nested/ >actual &&
+       test_cmp expect actual
+'
+
 GRADE_FORMAT="%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)"
 TRUSTLEVEL_FORMAT="%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)"
 
index 2667dd13fe33893086e2bdf14a94a6f3c8648d84..83b8a19d94176dcf66d2aca1d8a4b9519ee020fe 100755 (executable)
@@ -15,7 +15,7 @@ test_expect_success setup '
        git for-each-ref --format="%(objectname) %(refname)" >brief-list
 '
 
-test_expect_success 'Broken refs are reported correctly' '
+test_expect_success REFFILES 'Broken refs are reported correctly' '
        r=refs/heads/bogus &&
        : >.git/$r &&
        test_when_finished "rm -f .git/$r" &&
@@ -25,7 +25,7 @@ test_expect_success 'Broken refs are reported correctly' '
        test_cmp broken-err err
 '
 
-test_expect_success 'NULL_SHA1 refs are reported correctly' '
+test_expect_success REFFILES 'NULL_SHA1 refs are reported correctly' '
        r=refs/heads/zeros &&
        echo $ZEROS >.git/$r &&
        test_when_finished "rm -f .git/$r" &&
@@ -39,15 +39,14 @@ test_expect_success 'NULL_SHA1 refs are reported correctly' '
 '
 
 test_expect_success 'Missing objects are reported correctly' '
-       r=refs/heads/missing &&
-       echo $MISSING >.git/$r &&
-       test_when_finished "rm -f .git/$r" &&
-       echo "fatal: missing object $MISSING for $r" >missing-err &&
+       test_when_finished "git update-ref -d refs/heads/missing" &&
+       test-tool ref-store main update-ref msg refs/heads/missing "$MISSING" "$ZERO_OID" REF_SKIP_OID_VERIFICATION &&
+       echo "fatal: missing object $MISSING for refs/heads/missing" >missing-err &&
        test_must_fail git for-each-ref 2>err &&
        test_cmp missing-err err &&
        (
                cat brief-list &&
-               echo "$MISSING $r"
+               echo "$MISSING refs/heads/missing"
        ) | sort -k 2 >missing-brief-expected &&
        git for-each-ref --format="%(objectname) %(refname)" >brief-out 2>brief-err &&
        test_cmp missing-brief-expected brief-out &&
index af223e44d679571b3148d265ebb09049275cef3a..82f3d1ea0f25ed60900b09a454e3c98965db574f 100755 (executable)
@@ -45,8 +45,8 @@ test_expect_success 'check signed tags with --points-at' '
        sed -e "s/Z$//" >expect <<-\EOF &&
        refs/heads/side Z
        refs/tags/annotated-tag four
-       refs/tags/doubly-annotated-tag An annotated tag
-       refs/tags/doubly-signed-tag A signed tag
+       refs/tags/doubly-annotated-tag four
+       refs/tags/doubly-signed-tag four
        refs/tags/four Z
        refs/tags/signed-tag four
        EOF
index 2c92209ecabf5b17477522a328e64ac0c5e62ee1..fb872c5a1136fc523dacdf6f9778b37441905980 100755 (executable)
@@ -56,7 +56,67 @@ test_expect_success 'setup' '
        deduxit me super semitas jusitiae,
        EOF
 
-       printf "propter nomen suum." >>new4.txt
+       printf "propter nomen suum." >>new4.txt &&
+
+       cat >base.c <<-\EOF &&
+       int f(int x, int y)
+       {
+               if (x == 0)
+               {
+                       return y;
+               }
+               return x;
+       }
+
+       int g(size_t u)
+       {
+               while (u < 30)
+               {
+                       u++;
+               }
+               return u;
+       }
+       EOF
+
+       cat >ours.c <<-\EOF &&
+       int g(size_t u)
+       {
+               while (u < 30)
+               {
+                       u++;
+               }
+               return u;
+       }
+
+       int h(int x, int y, int z)
+       {
+               if (z == 0)
+               {
+                       return x;
+               }
+               return y;
+       }
+       EOF
+
+       cat >theirs.c <<-\EOF
+       int f(int x, int y)
+       {
+               if (x == 0)
+               {
+                       return y;
+               }
+               return x;
+       }
+
+       int g(size_t u)
+       {
+               while (u > 34)
+               {
+                       u--;
+               }
+               return u;
+       }
+       EOF
 '
 
 test_expect_success 'merge with no changes' '
@@ -447,4 +507,66 @@ test_expect_success '--object-id fails without repository' '
        grep "not a git repository" err
 '
 
+test_expect_success 'merging C files with "myers" diff algorithm creates some spurious conflicts' '
+       cat >expect.c <<-\EOF &&
+       int g(size_t u)
+       {
+               while (u < 30)
+               {
+                       u++;
+               }
+               return u;
+       }
+
+       int h(int x, int y, int z)
+       {
+       <<<<<<< ours.c
+               if (z == 0)
+       ||||||| base.c
+               while (u < 30)
+       =======
+               while (u > 34)
+       >>>>>>> theirs.c
+               {
+       <<<<<<< ours.c
+                       return x;
+       ||||||| base.c
+                       u++;
+       =======
+                       u--;
+       >>>>>>> theirs.c
+               }
+               return y;
+       }
+       EOF
+
+       test_must_fail git merge-file -p --diff3 --diff-algorithm myers ours.c base.c theirs.c >myers_output.c &&
+       test_cmp expect.c myers_output.c
+'
+
+test_expect_success 'merging C files with "histogram" diff algorithm avoids some spurious conflicts' '
+       cat >expect.c <<-\EOF &&
+       int g(size_t u)
+       {
+               while (u > 34)
+               {
+                       u--;
+               }
+               return u;
+       }
+
+       int h(int x, int y, int z)
+       {
+               if (z == 0)
+               {
+                       return x;
+               }
+               return y;
+       }
+       EOF
+
+       git merge-file -p --diff3 --diff-algorithm histogram ours.c base.c theirs.c >histogram_output.c &&
+       test_cmp expect.c histogram_output.c
+'
+
 test_done
index d02fa16614e0622585a7732942ee9d5b515e6afa..0f39ed0d08a34230c7c42f307992c20cc8b06d02 100755 (executable)
@@ -71,8 +71,9 @@ test_expect_success 'caching renames does not preclude finding new ones' '
 
                git switch upstream &&
 
-               test-tool fast-rebase --onto HEAD upstream~1 topic &&
-               #git cherry-pick upstream~1..topic
+               git replay --onto HEAD upstream~1..topic >out &&
+               git update-ref --stdin <out &&
+               git checkout topic &&
 
                git ls-files >tracked-files &&
                test_line_count = 2 tracked-files &&
@@ -140,8 +141,9 @@ test_expect_success 'cherry-pick both a commit and its immediate revert' '
                GIT_TRACE2_PERF="$(pwd)/trace.output" &&
                export GIT_TRACE2_PERF &&
 
-               test-tool fast-rebase --onto HEAD upstream~1 topic &&
-               #git cherry-pick upstream~1..topic &&
+               git replay --onto HEAD upstream~1..topic >out &&
+               git update-ref --stdin <out &&
+               git checkout topic &&
 
                grep region_enter.*diffcore_rename trace.output >calls &&
                test_line_count = 1 calls
@@ -199,8 +201,9 @@ test_expect_success 'rename same file identically, then reintroduce it' '
                GIT_TRACE2_PERF="$(pwd)/trace.output" &&
                export GIT_TRACE2_PERF &&
 
-               test-tool fast-rebase --onto HEAD upstream~1 topic &&
-               #git cherry-pick upstream~1..topic &&
+               git replay --onto HEAD upstream~1..topic >out &&
+               git update-ref --stdin <out &&
+               git checkout topic &&
 
                git ls-files >tracked &&
                test_line_count = 2 tracked &&
@@ -276,8 +279,9 @@ test_expect_success 'rename same file identically, then add file to old dir' '
                GIT_TRACE2_PERF="$(pwd)/trace.output" &&
                export GIT_TRACE2_PERF &&
 
-               test-tool fast-rebase --onto HEAD upstream~1 topic &&
-               #git cherry-pick upstream~1..topic &&
+               git replay --onto HEAD upstream~1..topic >out &&
+               git update-ref --stdin <out &&
+               git checkout topic &&
 
                git ls-files >tracked &&
                test_line_count = 4 tracked &&
@@ -353,10 +357,7 @@ test_expect_success 'cached dir rename does not prevent noticing later conflict'
                GIT_TRACE2_PERF="$(pwd)/trace.output" &&
                export GIT_TRACE2_PERF &&
 
-               test_must_fail test-tool fast-rebase --onto HEAD upstream~1 topic >output &&
-               #git cherry-pick upstream..topic &&
-
-               grep CONFLICT..rename/rename output &&
+               test_must_fail git replay --onto HEAD upstream~1..topic >output &&
 
                grep region_enter.*diffcore_rename trace.output >calls &&
                test_line_count = 2 calls
@@ -455,8 +456,9 @@ test_expect_success 'dir rename unneeded, then add new file to old dir' '
                GIT_TRACE2_PERF="$(pwd)/trace.output" &&
                export GIT_TRACE2_PERF &&
 
-               test-tool fast-rebase --onto HEAD upstream~1 topic &&
-               #git cherry-pick upstream..topic &&
+               git replay --onto HEAD upstream~1..topic >out &&
+               git update-ref --stdin <out &&
+               git checkout topic &&
 
                grep region_enter.*diffcore_rename trace.output >calls &&
                test_line_count = 2 calls &&
@@ -521,8 +523,9 @@ test_expect_success 'dir rename unneeded, then rename existing file into old dir
                GIT_TRACE2_PERF="$(pwd)/trace.output" &&
                export GIT_TRACE2_PERF &&
 
-               test-tool fast-rebase --onto HEAD upstream~1 topic &&
-               #git cherry-pick upstream..topic &&
+               git replay --onto HEAD upstream~1..topic >out &&
+               git update-ref --stdin <out &&
+               git checkout topic &&
 
                grep region_enter.*diffcore_rename trace.output >calls &&
                test_line_count = 3 calls &&
@@ -623,8 +626,9 @@ test_expect_success 'caching renames only on upstream side, part 1' '
                GIT_TRACE2_PERF="$(pwd)/trace.output" &&
                export GIT_TRACE2_PERF &&
 
-               test-tool fast-rebase --onto HEAD upstream~1 topic &&
-               #git cherry-pick upstream..topic &&
+               git replay --onto HEAD upstream~1..topic >out &&
+               git update-ref --stdin <out &&
+               git checkout topic &&
 
                grep region_enter.*diffcore_rename trace.output >calls &&
                test_line_count = 1 calls &&
@@ -681,8 +685,9 @@ test_expect_success 'caching renames only on upstream side, part 2' '
                GIT_TRACE2_PERF="$(pwd)/trace.output" &&
                export GIT_TRACE2_PERF &&
 
-               test-tool fast-rebase --onto HEAD upstream~1 topic &&
-               #git cherry-pick upstream..topic &&
+               git replay --onto HEAD upstream~1..topic >out &&
+               git update-ref --stdin <out &&
+               git checkout topic &&
 
                grep region_enter.*diffcore_rename trace.output >calls &&
                test_line_count = 2 calls &&
index e689db429292e60162d4f8f89905cc8489d7a3e0..b41a47eb943a03b1588bdc87802b0645944ce2ec 100755 (executable)
@@ -1862,6 +1862,51 @@ test_expect_success 'option override configured sort' '
        test_cmp expect actual
 '
 
+test_expect_success '--no-sort cancels config sort keys' '
+       test_config tag.sort "-refname" &&
+
+       # objecttype is identical for all of them, so sort falls back on
+       # default (ascending refname)
+       git tag -l \
+               --no-sort \
+               --sort="objecttype" \
+               "foo*" >actual &&
+       cat >expect <<-\EOF &&
+       foo1.10
+       foo1.3
+       foo1.6
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success '--no-sort cancels command line sort keys' '
+       # objecttype is identical for all of them, so sort falls back on
+       # default (ascending refname)
+       git tag -l \
+               --sort="-refname" \
+               --no-sort \
+               --sort="objecttype" \
+               "foo*" >actual &&
+       cat >expect <<-\EOF &&
+       foo1.10
+       foo1.3
+       foo1.6
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success '--no-sort without subsequent --sort prints expected tags' '
+       # Sort the results with `sort` for a consistent comparison against
+       # expected
+       git tag -l --no-sort "foo*" | sort >actual &&
+       cat >expect <<-\EOF &&
+       foo1.10
+       foo1.3
+       foo1.6
+       EOF
+       test_cmp expect actual
+'
+
 test_expect_success 'invalid sort parameter on command line' '
        test_must_fail git tag -l --sort=notvalid "foo*" >actual
 '
index 4287863ae6cab9a67564c9328effd68d2c186ab6..62d9f846ce86c54745eb87619d14fefc005d6fe3 100755 (executable)
@@ -616,4 +616,12 @@ test_expect_success 'reset --mixed sets up work tree' '
        test_must_be_empty actual
 '
 
+test_expect_success 'reset handles --end-of-options' '
+       git update-ref refs/heads/--foo HEAD^ &&
+       git log -1 --format=%s refs/heads/--foo >expect &&
+       git reset --hard --end-of-options --foo &&
+       git log -1 --format=%s HEAD >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 1a310a45fd35765167c8f2c06ea85dca8cab9749..611b3dd3aedb44c9b5d4657f3058c72a60258cb0 100755 (executable)
@@ -517,8 +517,12 @@ test_expect_success 'nested (empty) git should be kept' '
        git init empty_repo &&
        mkdir to_clean &&
        >to_clean/should_clean.this &&
+       # Note that we put the expect file in the .git directory so that it
+       # does not get cleaned.
+       find empty_repo | sort >.git/expect &&
        git clean -f -d &&
-       test_path_is_file empty_repo/.git/HEAD &&
+       find empty_repo | sort >actual &&
+       test_cmp .git/expect actual &&
        test_path_is_missing to_clean
 '
 
@@ -559,10 +563,10 @@ test_expect_success 'giving path in nested git work tree will NOT remove it' '
                mkdir -p bar/baz &&
                test_commit msg bar/baz/hello.world
        ) &&
+       find repo | sort >expect &&
        git clean -f -d repo/bar/baz &&
-       test_path_is_file repo/.git/HEAD &&
-       test_path_is_dir repo/bar/ &&
-       test_path_is_file repo/bar/baz/hello.world
+       find repo | sort >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'giving path to nested .git will not remove it' '
@@ -573,10 +577,10 @@ test_expect_success 'giving path to nested .git will not remove it' '
                git init &&
                test_commit msg hello.world
        ) &&
+       find repo | sort >expect &&
        git clean -f -d repo/.git &&
-       test_path_is_file repo/.git/HEAD &&
-       test_path_is_dir repo/.git/refs &&
-       test_path_is_dir repo/.git/objects &&
+       find repo | sort >actual &&
+       test_cmp expect actual &&
        test_path_is_dir untracked/
 '
 
@@ -588,9 +592,10 @@ test_expect_success 'giving path to nested .git/ will NOT remove contents' '
                git init &&
                test_commit msg hello.world
        ) &&
+       find repo | sort >expect &&
        git clean -f -d repo/.git/ &&
-       test_path_is_dir repo/.git &&
-       test_path_is_file repo/.git/HEAD &&
+       find repo | sort >actual &&
+       test_cmp expect actual &&
        test_path_is_dir untracked/
 '
 
index 35a31acd4d7e26bc3bf8f42853d80593488c9227..46d4fb0354b13d585260587d78d27d1e9c545f1c 100755 (executable)
@@ -45,6 +45,32 @@ test_expect_success 'check names' '
        test_cmp expect actual
 '
 
+test_expect_success 'check urls' '
+       cat >expect <<-\EOF &&
+       ./bar/baz/foo.git
+       https://example.com/foo.git
+       http://example.com:80/deeper/foo.git
+       EOF
+
+       test-tool submodule check-url >actual <<-\EOF &&
+       ./bar/baz/foo.git
+       https://example.com/foo.git
+       http://example.com:80/deeper/foo.git
+       -a./foo
+       ../../..//test/foo.git
+       ../../../../../:localhost:8080/foo.git
+       ..\../.\../:example.com/foo.git
+       ./%0ahost=example.com/foo.git
+       https://one.example.com/evil?%0ahost=two.example.com
+       https:///example.com/foo.git
+       http://example.com:test/foo.git
+       https::example.com/foo.git
+       http:::example.com/foo.git
+       EOF
+
+       test_cmp expect actual
+'
+
 test_expect_success 'create innocent subrepo' '
        git init innocent &&
        git -C innocent commit --allow-empty -m foo
index 3d8500a52e50c004544c6df62cf123b8aeb62a99..bced44a0fc915f430ccc41d54fbd8bc48df2cef8 100755 (executable)
@@ -3,8 +3,7 @@
 # Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>
 #
 
-# FIXME: Test the various index usages, -i and -o, test reflog,
-# signoff
+# FIXME: Test the various index usages, test reflog
 
 test_description='git commit'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
@@ -92,6 +91,34 @@ test_expect_success '--long fails with nothing to commit' '
        test_must_fail git commit -m initial --long
 '
 
+test_expect_success 'fail to commit untracked file (even with --include/--only)' '
+       echo content >baz &&
+       error="error: pathspec .baz. did not match any file(s) known to git" &&
+
+       test_must_fail git commit -m "baz" baz 2>err &&
+       test_grep -e "$error" err &&
+
+       test_must_fail git commit --only -m "baz" baz 2>err &&
+       test_grep -e "$error" err &&
+
+       # TODO: as for --include, the below command will fail because
+       # nothing is staged. If something was staged, it would not fail
+       # even though the provided pathspec does not match any tracked
+       # path. (However, the untracked paths that match the pathspec are
+       # not committed and only the staged changes get committed.)
+       # In either cases, no error is returned to stderr like in (--only
+       # and without --only/--include) cases. In a similar manner,
+       # "git add -u baz" also does not error out.
+       #
+       # Therefore, the below test is just to document the current behavior
+       # and is not an endorsement to the current behavior, and we may
+       # want to fix this. And when that happens, this test should be
+       # updated accordingly.
+
+       test_must_fail git commit --include -m "baz" baz 2>err &&
+       test_must_be_empty err
+'
+
 test_expect_success 'setup: non-initial commit' '
        echo bongo bongo bongo >file &&
        git commit -m next -a
@@ -117,6 +144,51 @@ test_expect_success '--long with stuff to commit returns ok' '
        git commit -m next -a --long
 '
 
+for opt in "" "-o" "--only"
+do
+       test_expect_success 'exclude additional staged changes when given pathspec' '
+               echo content >>file &&
+               echo content >>baz &&
+               git add baz &&
+               git commit $opt -m "file" file &&
+
+               git diff --name-only >actual &&
+               test_must_be_empty actual &&
+
+               test_write_lines baz >expect &&
+               git diff --name-only --cached >actual &&
+               test_cmp expect actual &&
+
+               test_write_lines file >expect &&
+               git diff --name-only HEAD^ HEAD >actual &&
+               test_cmp expect actual
+       '
+done
+
+test_expect_success '-i/--include includes staged changes' '
+       echo content >>file &&
+       echo content >>baz &&
+       git add file &&
+
+       # baz is in the index, therefore, it will be committed
+       git commit --include -m "file and baz" baz  &&
+
+       git diff --name-only HEAD >remaining &&
+       test_must_be_empty remaining &&
+
+       test_write_lines baz file >expect &&
+       git diff --name-only HEAD^ HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--include and --only do not mix' '
+       test_when_finished "git reset --hard" &&
+       echo content >>file &&
+       echo content >>baz &&
+       test_must_fail git commit --include --only -m "file baz" file baz 2>actual &&
+       test_grep -e "fatal: options .-i/--include. and .-o/--only. cannot be used together" actual
+'
+
 test_expect_success 'commit message from non-existing file' '
        echo more bongo: bongo bongo bongo bongo >file &&
        test_must_fail git commit -F gah -a
@@ -389,6 +461,28 @@ test_expect_success 'amend commit to fix date' '
 
 '
 
+test_expect_success 'amend commit to add signoff' '
+
+       test_commit "msg" file content &&
+       git commit --amend --signoff &&
+       test_commit_message HEAD <<-EOF
+       msg
+
+       Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+       EOF
+'
+
+test_expect_success 'amend does not add signoff if it already exists' '
+
+       test_commit --signoff "tenor" file newcontent &&
+       git commit --amend --signoff &&
+       test_commit_message HEAD <<-EOF
+       tenor
+
+       Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+       EOF
+'
+
 test_expect_success 'commit mentions forced date in output' '
        git commit --amend --date=2010-01-02T03:04:05 >output &&
        grep "Date: *Sat Jan 2 03:04:05 2010" output
index c2ab8a444a832513b9365a2a6eca11535e2ec8fe..802f8f704c62eb11192bcc82ecd35f0ae53b5a6a 100755 (executable)
@@ -692,6 +692,34 @@ EOF
 '
 
 
+test_expect_success 'status when bisecting while rebasing' '
+       git reset --hard main &&
+       test_when_finished "git rebase --abort" &&
+       ONTO=$(git rev-parse --short HEAD^) &&
+       FAKE_LINES="break" git rebase -i HEAD^ &&
+       test_when_finished "git checkout -" &&
+       git checkout -b bisect_while_rebasing &&
+       test_when_finished "git bisect reset" &&
+       git bisect start &&
+       cat >expected <<EOF &&
+On branch bisect_while_rebasing
+Last command done (1 command done):
+   break
+No commands remaining.
+You are currently editing a commit while rebasing branch '\''bisect'\'' on '\''$ONTO'\''.
+  (use "git commit --amend" to amend the current commit)
+  (use "git rebase --continue" once you are satisfied with your changes)
+
+You are currently bisecting, started from branch '\''bisect_while_rebasing'\''.
+  (use "git bisect reset" to get back to the original branch)
+
+nothing to commit (use -u to show untracked files)
+EOF
+       git status --untracked-files=no >actual &&
+       test_cmp expected actual
+'
+
+
 test_expect_success 'status when rebase --apply conflicts with statushints disabled' '
        git reset --hard main &&
        git checkout -b statushints_disabled &&
index 78503158fd699d2df75e8fbbbaf164a129f2578b..363f9dc0e41b2686aa9892d5e21e153ca54727b9 100755 (executable)
@@ -978,7 +978,7 @@ test_expect_success !UNICODE_COMPOSITION_SENSITIVE 'Unicode nfc/nfd' '
        mkdir test_unicode/nfd &&
        mkdir test_unicode/nfd/d_${utf8_nfd} &&
 
-       git -C test_unicode fsmonitor--daemon stop &&
+       test-tool -C test_unicode fsmonitor-client query --token 0 &&
 
        if test_have_prereq UNICODE_NFC_PRESERVED
        then
index d2975e6c93a07480e8da4e89bb1bd48faf397cba..94f9f4a1dac5621fbd340b6425d2f2ea7f042016 100755 (executable)
@@ -271,7 +271,7 @@ test_expect_success 'repacking fails when missing .pack actually means missing o
                ls .git/objects/pack/*.pack >before-pack-dir &&
 
                test_must_fail git fsck &&
-               test_must_fail git repack --cruft -d 2>err &&
+               test_must_fail env GIT_COMMIT_GRAPH_PARANOIA=true git repack --cruft -d 2>err &&
                grep "bad object" err &&
 
                # Before failing, the repack did not modify the
index 935df6a1dbb00e486e217c26f518b6e8e862a78c..0943dfa18a3f97b7e351346d089efdd878f6ca68 100755 (executable)
@@ -67,6 +67,51 @@ test_expect_success 'maintenance.auto config option' '
        test_subcommand ! git maintenance run --auto --quiet  <false
 '
 
+test_expect_success 'register uses XDG_CONFIG_HOME config if it exists' '
+       test_when_finished rm -r .config/git/config &&
+       (
+               XDG_CONFIG_HOME=.config &&
+               export XDG_CONFIG_HOME &&
+               mkdir -p $XDG_CONFIG_HOME/git &&
+               >$XDG_CONFIG_HOME/git/config &&
+               git maintenance register &&
+               git config --file=$XDG_CONFIG_HOME/git/config --get maintenance.repo >actual &&
+               pwd >expect &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'register does not need XDG_CONFIG_HOME config to exist' '
+       test_when_finished git maintenance unregister &&
+       test_path_is_missing $XDG_CONFIG_HOME/git/config &&
+       git maintenance register &&
+       git config --global --get maintenance.repo >actual &&
+       pwd >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'unregister uses XDG_CONFIG_HOME config if it exists' '
+       test_when_finished rm -r .config/git/config &&
+       (
+               XDG_CONFIG_HOME=.config &&
+               export XDG_CONFIG_HOME &&
+               mkdir -p $XDG_CONFIG_HOME/git &&
+               >$XDG_CONFIG_HOME/git/config &&
+               git maintenance register &&
+               git maintenance unregister &&
+               test_must_fail git config --file=$XDG_CONFIG_HOME/git/config --get maintenance.repo >actual &&
+               test_must_be_empty actual
+       )
+'
+
+test_expect_success 'unregister does not need XDG_CONFIG_HOME config to exist' '
+       test_path_is_missing $XDG_CONFIG_HOME/git/config &&
+       git maintenance register &&
+       git maintenance unregister &&
+       test_must_fail git config --global --get maintenance.repo >actual &&
+       test_must_be_empty actual
+'
+
 test_expect_success 'maintenance.<task>.enabled' '
        git config maintenance.gc.enabled false &&
        git config maintenance.commit-graph.enabled true &&
@@ -157,7 +202,8 @@ test_expect_success 'prefetch multiple remotes' '
        fetchargs="--prefetch --prune --no-tags --no-write-fetch-head --recurse-submodules=no --quiet" &&
        test_subcommand git fetch remote1 $fetchargs <run-prefetch.txt &&
        test_subcommand git fetch remote2 $fetchargs <run-prefetch.txt &&
-       test_path_is_missing .git/refs/remotes &&
+       git for-each-ref refs/remotes >actual &&
+       test_must_be_empty actual &&
        git log prefetch/remotes/remote1/one &&
        git log prefetch/remotes/remote2/two &&
        git fetch --all &&
index 6d3dbde3feb9a41dc59a42f75ef578b1ebc5b183..348cc40658235239c7e68671738cd030871451ad 100755 (executable)
@@ -1,6 +1,7 @@
 #!/bin/sh
 
 test_description='git column'
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 32317d6bca5f45314a46082ecd9aa68061853904..e06538b1c854815c40aa43915d844089671cb2bd 100755 (executable)
@@ -27,7 +27,7 @@ cat << EOF
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program; if not, see <http://www.gnu.org/licenses/>.
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
 #
 EOF
 }
index d8d536269cf73d64d9d2df593667c17c826479c5..8ca24670acb5a9280a163b11682e9a0dd52eaaa5 100755 (executable)
@@ -11,7 +11,7 @@ test_expect_success 'setup repo with a git repo inside it' '
        (
                cd s &&
                git init &&
-               test -f .git/HEAD &&
+               git symbolic-ref HEAD &&
                > .git/a &&
                echo a > a &&
                svn_cmd add .git a &&
index c8e6c0733f41cf90a41941a3ad3550efb7ea56f1..d1dec89c3b7f6c46f18a2838c3d8cdba640146ea 100755 (executable)
@@ -46,6 +46,14 @@ setup_hook()
                        "passed to setup_hook" >&2 ; return 1; }
        echo "cnt=$skip_revs" > "$hook_type-counter"
        rm -f "$rawsvnrepo/hooks/"*-commit # drop previous hooks
+
+       # Subversion hooks run with an empty environment by default. We thus
+       # need to propagate PATH so that we can find executables.
+       cat >"$rawsvnrepo/conf/hooks-env" <<-EOF
+       [default]
+       PATH = ${PATH}
+       EOF
+
        hook="$rawsvnrepo/hooks/$hook_type"
        cat > "$hook" <<- 'EOF1'
                #!/bin/sh
@@ -63,7 +71,6 @@ EOF1
        if [ "$hook_type" = "pre-commit" ]; then
                echo "echo 'commit disallowed' >&2; exit 1" >>"$hook"
        else
-               echo "PATH=\"$PATH\"; export PATH" >>"$hook"
                echo "svnconf=\"$svnconf\"" >>"$hook"
                cat >>"$hook" <<- 'EOF2'
                        cd work-auto-commits.svn
index 26c25c0eb2ba57fa90c682950fda4899329474f8..e9a12c18bbd3f8bd43659d0b4eee0e4dfbad30ab 100755 (executable)
@@ -791,4 +791,14 @@ test_expect_success 'fast-export --first-parent outputs all revisions output by
        )
 '
 
+test_expect_success 'fast-export handles --end-of-options' '
+       git update-ref refs/heads/nodash HEAD &&
+       git update-ref refs/heads/--dashes HEAD &&
+       git fast-export --end-of-options nodash >expect &&
+       git fast-export --end-of-options --dashes >actual.raw &&
+       # fix up lines which mention the ref for comparison
+       sed s/--dashes/nodash/ <actual.raw >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 0333065d4d60a0113cb5ac1de391a4a997d2eaa8..7679780fb87b4f14e9c0f5a22708b1e8925a4d53 100755 (executable)
@@ -627,6 +627,7 @@ test_expect_success \
 test_expect_success 'setup' '
        version=$(git config core.repositoryformatversion) &&
        algo=$(test_might_fail git config extensions.objectformat) &&
+       refstorage=$(test_might_fail git config extensions.refstorage) &&
        cat >.git/config <<-\EOF &&
        # testing noval and alternate separator
        [gitweb]
@@ -637,6 +638,10 @@ test_expect_success 'setup' '
        if test -n "$algo"
        then
                git config extensions.objectformat "$algo"
+       fi &&
+       if test -n "$refstorage"
+       then
+               git config extensions.refstorage "$refstorage"
        fi
 '
 
index 6d753708d2acb6c7bcf47e5a057d99845c1006a3..d8e85482ab2ba7b28cb1e899f07dc1379969d1c1 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/perl
 use lib (split(/:/, $ENV{GITPERLLIB}));
 
-use 5.008;
+use 5.008001;
 use warnings;
 use strict;
 
index 73cca0d143d41f219fea625865480dced9df1094..c598011635ac2f44d0764acaf1ba7c53f569514c 100755 (executable)
@@ -466,7 +466,7 @@ test_expect_success 'git p4 clone complex branches with excluded files' '
        )
 '
 
-# From a report in http://stackoverflow.com/questions/11893688
+# From a report in https://stackoverflow.com/questions/11893688
 # where --use-client-spec caused branch prefixes not to be removed;
 # every file in git appeared into a subdirectory of the branch name.
 test_expect_success 'use-client-spec detect-branches setup' '
index 932841003cfc4e3f6273087f8567d6c6a660bb0b..5e904ac80d85226a74296774ef4279369da15d4a 100755 (executable)
@@ -9,7 +9,7 @@ test_expect_success 'start p4d' '
 '
 
 # See
-# http://www.perforce.com/perforce/doc.current/manuals/p4sag/03_superuser.html#1088563
+# https://web.archive.org/web/20150602090517/http://www.perforce.com/perforce/doc.current/manuals/p4sag/chapter.superuser.html#superuser.basic.typemap_locking
 # for suggestions on how to configure "sitewide pessimistic locking"
 # where only one person can have a file open for edit at a time.
 test_expect_success 'init depot' '
index a7c3b4eb63abfe251dcb9edc4b14b136dcfc91c6..35eb534fdda2735786faf1fa62bb399ddcc52f27 100755 (executable)
@@ -5,6 +5,12 @@
 
 test_description='test bash completion'
 
+# The Bash completion scripts must not print anything to either stdout or
+# stderr, which we try to verify. When tracing is enabled without support for
+# BASH_XTRACEFD this assertion will fail, so we have to mark the test as
+# untraceable with such ancient Bash versions.
+test_untraceable=UnfortunatelyYes
+
 . ./lib-bash.sh
 
 complete ()
@@ -87,9 +93,11 @@ test_completion ()
        else
                sed -e 's/Z$//' |sort >expected
        fi &&
-       run_completion "$1" &&
+       run_completion "$1" >"$TRASH_DIRECTORY"/bash-completion-output 2>&1 &&
        sort out >out_sorted &&
-       test_cmp expected out_sorted
+       test_cmp expected out_sorted &&
+       test_must_be_empty "$TRASH_DIRECTORY"/bash-completion-output &&
+       rm "$TRASH_DIRECTORY"/bash-completion-output
 }
 
 # Test __gitcomp.
@@ -1571,7 +1579,7 @@ test_expect_success FUNNYNAMES,!CYGWIN 'cone mode sparse-checkout completes dire
        )
 '
 
-test_expect_success 'non-cone mode sparse-checkout uses bash completion' '
+test_expect_success 'non-cone mode sparse-checkout gives rooted paths' '
        # reset sparse-checkout repo to non-cone mode
        git -C sparse-checkout sparse-checkout disable &&
        git -C sparse-checkout sparse-checkout set --no-cone &&
@@ -1581,7 +1589,12 @@ test_expect_success 'non-cone mode sparse-checkout uses bash completion' '
                # expected to be empty since we have not configured
                # custom completion for non-cone mode
                test_completion "git sparse-checkout set f" <<-\EOF
-
+               /folder1/0/1/t.txt Z
+               /folder1/expected Z
+               /folder1/out Z
+               /folder1/out_sorted Z
+               /folder2/0/t.txt Z
+               /folder3/t.txt Z
                EOF
        )
 '
@@ -1920,6 +1933,14 @@ test_expect_success 'git checkout - --orphan with branch already provided comple
        EOF
 '
 
+test_expect_success 'git restore completes modified files' '
+       test_commit A a.file &&
+       echo B >a.file &&
+       test_completion "git restore a." <<-\EOF
+       a.file
+       EOF
+'
+
 test_expect_success 'teardown after ref completion' '
        git branch -d matching-branch &&
        git tag -d matching-tag &&
@@ -2715,4 +2736,31 @@ test_expect_success '__git_complete' '
        test_must_fail __git_complete ga missing
 '
 
+test_expect_success '__git_pseudoref_exists' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       (
+               cd repo &&
+               sane_unset __git_repo_path &&
+
+               # HEAD should exist, even if it points to an unborn branch.
+               __git_pseudoref_exists HEAD >output 2>&1 &&
+               test_must_be_empty output &&
+
+               # HEAD points to an existing branch, so it should exist.
+               test_commit A &&
+               __git_pseudoref_exists HEAD >output 2>&1 &&
+               test_must_be_empty output &&
+
+               # CHERRY_PICK_HEAD does not exist, so the existence check should fail.
+               ! __git_pseudoref_exists CHERRY_PICK_HEAD >output 2>&1 &&
+               test_must_be_empty output &&
+
+               # CHERRY_PICK_HEAD points to a commit, so it should exist.
+               git update-ref CHERRY_PICK_HEAD A &&
+               __git_pseudoref_exists CHERRY_PICK_HEAD >output 2>&1 &&
+               test_must_be_empty output
+       )
+'
+
 test_done
index 9c3cf12b268ab5457f2abaf7e9181b1332a5ba96..b5eaf7fdc1186d4420388c355e794bfdba783f2f 100644 (file)
@@ -14,7 +14,7 @@
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see http://www.gnu.org/licenses/ .
+# along with this program.  If not, see https://www.gnu.org/licenses/ .
 
 # The semantics of the editor variables are that of invoking
 # sh -c "$EDITOR \"$@\"" files ...
@@ -1277,7 +1277,7 @@ test_grep () {
        if test $# -lt 2 ||
           { test "x!" = "x$1" && test $# -lt 3 ; }
        then
-               BUG "too few parameters to test_i18ngrep"
+               BUG "too few parameters to test_grep"
        fi
 
        if test "x!" = "x$1"
@@ -1659,6 +1659,11 @@ test_detect_hash () {
        test_hash_algo="${GIT_TEST_DEFAULT_HASH:-sha1}"
 }
 
+# Detect the hash algorithm in use.
+test_detect_ref_format () {
+       echo "${GIT_TEST_DEFAULT_REF_FORMAT:-files}"
+}
+
 # Load common hash metadata and common placeholder object IDs for use with
 # test_oid.
 test_oid_init () {
@@ -1874,6 +1879,20 @@ test_region () {
        return 0
 }
 
+# Check that the given data fragment was included as part of the
+# trace2-format trace on stdin.
+#
+#      test_trace2_data <category> <key> <value>
+#
+# For example, to look for trace2_data_intmax("pack-objects", repo,
+# "reused", N) in an invocation of "git pack-objects", run:
+#
+#      GIT_TRACE2_EVENT="$(pwd)/trace.txt" git pack-objects ... &&
+#      test_trace2_data pack-objects reused N <trace2.txt
+test_trace2_data () {
+       grep -e '"category":"'"$1"'","key":"'"$2"'","value":"'"$3"'"'
+}
+
 # Given a GIT_TRACE2_EVENT log over stdin, writes to stdout a list of URLs
 # sent to git-remote-https child processes.
 test_remote_https_urls() {
index 9c5339c577ac3fd50090baf0d84caf6d62df0697..33405c90d740d4c4e4fac93cfa8a713939a39348 100644 (file)
@@ -14,7 +14,7 @@
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see http://www.gnu.org/licenses/ .
+# along with this program.  If not, see https://www.gnu.org/licenses/ .
 #
 # The idea is for `test-lib.sh` to source this file when run in GitHub
 # workflows; these functions will then override (empty) functions
@@ -42,8 +42,8 @@ finalize_test_case_output () {
        fixed)
                echo >>$github_markup_output "::notice::fixed: $this_test.$test_count $1"
                ;;
-       ok)
-               # Exit without printing the "ok" tests
+       ok|broken)
+               # Exit without printing the "ok" or ""broken" tests
                return
                ;;
        esac
index 79c31c788b921baa3118c918f731ed46b450393b..76cbbd3299d64a82276c0aa30241479388014af8 100644 (file)
@@ -15,7 +15,7 @@
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see http://www.gnu.org/licenses/ .
+# along with this program.  If not, see https://www.gnu.org/licenses/ .
 #
 # The idea is for `test-lib.sh` to source this file when the user asks
 # for JUnit XML; these functions will then override (empty) functions
index 1656c9eed006bdce115ed6e7673246847319783a..fc93aa57e6116d5602b17a63f41bd4b8746289e2 100644 (file)
@@ -13,7 +13,7 @@
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program.  If not, see http://www.gnu.org/licenses/ .
+# along with this program.  If not, see https://www.gnu.org/licenses/ .
 
 # Test the binaries we have just built.  The tests are kept in
 # t/ subdirectory and are run in 'trash directory' subdirectory.
@@ -542,6 +542,8 @@ export EDITOR
 
 GIT_DEFAULT_HASH="${GIT_TEST_DEFAULT_HASH:-sha1}"
 export GIT_DEFAULT_HASH
+GIT_DEFAULT_REF_FORMAT="${GIT_TEST_DEFAULT_REF_FORMAT:-files}"
+export GIT_DEFAULT_REF_FORMAT
 GIT_TEST_MERGE_ALGORITHM="${GIT_TEST_MERGE_ALGORITHM:-ort}"
 export GIT_TEST_MERGE_ALGORITHM
 
@@ -1745,7 +1747,14 @@ parisc* | hppa*)
        ;;
 esac
 
-test_set_prereq REFFILES
+case "$GIT_DEFAULT_REF_FORMAT" in
+files)
+       test_set_prereq REFFILES;;
+*)
+       echo 2>&1 "error: unknown ref format $GIT_DEFAULT_REF_FORMAT"
+       exit 1
+       ;;
+esac
 
 ( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1
 test -z "$NO_CURL" && test_set_prereq LIBCURL
@@ -1936,6 +1945,10 @@ test_lazy_prereq SHA1 '
        esac
 '
 
+test_lazy_prereq DEFAULT_REPO_FORMAT '
+       test_have_prereq SHA1,REFFILES
+'
+
 # Ensure that no test accidentally triggers a Git command
 # that runs the actual maintenance scheduler, affecting a user's
 # system permanently.
index 1bcf01a9a42a6147a85db9bb4d82523740a95ad7..3810e9bb43190322cd932d520ace6ab1e874be82 100755 (executable)
@@ -1,5 +1,5 @@
 #!/usr/bin/perl
-use 5.008;
+use 5.008001;
 use strict;
 use warnings;
 use IO::Pty;
diff --git a/t/unit-tests/.gitignore b/t/unit-tests/.gitignore
new file mode 100644 (file)
index 0000000..5e56e04
--- /dev/null
@@ -0,0 +1 @@
+/bin
diff --git a/t/unit-tests/t-basic.c b/t/unit-tests/t-basic.c
new file mode 100644 (file)
index 0000000..fda1ae5
--- /dev/null
@@ -0,0 +1,95 @@
+#include "test-lib.h"
+
+/*
+ * The purpose of this "unit test" is to verify a few invariants of the unit
+ * test framework itself, as well as to provide examples of output from actually
+ * failing tests. As such, it is intended that this test fails, and thus it
+ * should not be run as part of `make unit-tests`. Instead, we verify it behaves
+ * as expected in the integration test t0080-unit-test-output.sh
+ */
+
+/* Used to store the return value of check_int(). */
+static int check_res;
+
+/* Used to store the return value of TEST(). */
+static int test_res;
+
+static void t_res(int expect)
+{
+       check_int(check_res, ==, expect);
+       check_int(test_res, ==, expect);
+}
+
+static void t_todo(int x)
+{
+       check_res = TEST_TODO(check(x));
+}
+
+static void t_skip(void)
+{
+       check(0);
+       test_skip("missing prerequisite");
+       check(1);
+}
+
+static int do_skip(void)
+{
+       test_skip("missing prerequisite");
+       return 1;
+}
+
+static void t_skip_todo(void)
+{
+       check_res = TEST_TODO(do_skip());
+}
+
+static void t_todo_after_fail(void)
+{
+       check(0);
+       TEST_TODO(check(0));
+}
+
+static void t_fail_after_todo(void)
+{
+       check(1);
+       TEST_TODO(check(0));
+       check(0);
+}
+
+static void t_messages(void)
+{
+       check_str("\thello\\", "there\"\n");
+       check_str("NULL", NULL);
+       check_char('a', ==, '\n');
+       check_char('\\', ==, '\'');
+}
+
+static void t_empty(void)
+{
+       ; /* empty */
+}
+
+int cmd_main(int argc, const char **argv)
+{
+       test_res = TEST(check_res = check_int(1, ==, 1), "passing test");
+       TEST(t_res(1), "passing test and assertion return 1");
+       test_res = TEST(check_res = check_int(1, ==, 2), "failing test");
+       TEST(t_res(0), "failing test and assertion return 0");
+       test_res = TEST(t_todo(0), "passing TEST_TODO()");
+       TEST(t_res(1), "passing TEST_TODO() returns 1");
+       test_res = TEST(t_todo(1), "failing TEST_TODO()");
+       TEST(t_res(0), "failing TEST_TODO() returns 0");
+       test_res = TEST(t_skip(), "test_skip()");
+       TEST(check_int(test_res, ==, 1), "skipped test returns 1");
+       test_res = TEST(t_skip_todo(), "test_skip() inside TEST_TODO()");
+       TEST(t_res(1), "test_skip() inside TEST_TODO() returns 1");
+       test_res = TEST(t_todo_after_fail(), "TEST_TODO() after failing check");
+       TEST(check_int(test_res, ==, 0), "TEST_TODO() after failing check returns 0");
+       test_res = TEST(t_fail_after_todo(), "failing check after TEST_TODO()");
+       TEST(check_int(test_res, ==, 0), "failing check after TEST_TODO() returns 0");
+       TEST(t_messages(), "messages from failing string and char comparison");
+       test_res = TEST(t_empty(), "test with no checks");
+       TEST(check_int(test_res, ==, 0), "test with no checks returns 0");
+
+       return test_done();
+}
diff --git a/t/unit-tests/t-ctype.c b/t/unit-tests/t-ctype.c
new file mode 100644 (file)
index 0000000..f315489
--- /dev/null
@@ -0,0 +1,80 @@
+#include "test-lib.h"
+
+static int is_in(const char *s, int ch)
+{
+       /*
+        * We can't find NUL using strchr. Accept it as the first
+        * character in the spec -- there are no empty classes.
+        */
+       if (ch == '\0')
+               return ch == *s;
+       if (*s == '\0')
+               s++;
+       return !!strchr(s, ch);
+}
+
+/* Macro to test a character type */
+#define TEST_CTYPE_FUNC(func, string) \
+static void test_ctype_##func(void) { \
+       for (int i = 0; i < 256; i++) { \
+               if (!check_int(func(i), ==, is_in(string, i))) \
+                       test_msg("       i: 0x%02x", i); \
+       } \
+       if (!check(!func(EOF))) \
+                       test_msg("      i: 0x%02x (EOF)", EOF); \
+}
+
+#define TEST_CHAR_CLASS(class) TEST(test_ctype_##class(), #class " works")
+
+#define DIGIT "0123456789"
+#define LOWER "abcdefghijklmnopqrstuvwxyz"
+#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define PUNCT "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
+#define ASCII \
+       "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
+       "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
+       "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" \
+       "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" \
+       "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" \
+       "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" \
+       "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" \
+       "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
+#define CNTRL \
+       "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
+       "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
+       "\x7f"
+
+TEST_CTYPE_FUNC(isdigit, DIGIT)
+TEST_CTYPE_FUNC(isspace, " \n\r\t")
+TEST_CTYPE_FUNC(isalpha, LOWER UPPER)
+TEST_CTYPE_FUNC(isalnum, LOWER UPPER DIGIT)
+TEST_CTYPE_FUNC(is_glob_special, "*?[\\")
+TEST_CTYPE_FUNC(is_regex_special, "$()*+.?[\\^{|")
+TEST_CTYPE_FUNC(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~")
+TEST_CTYPE_FUNC(isascii, ASCII)
+TEST_CTYPE_FUNC(islower, LOWER)
+TEST_CTYPE_FUNC(isupper, UPPER)
+TEST_CTYPE_FUNC(iscntrl, CNTRL)
+TEST_CTYPE_FUNC(ispunct, PUNCT)
+TEST_CTYPE_FUNC(isxdigit, DIGIT "abcdefABCDEF")
+TEST_CTYPE_FUNC(isprint, LOWER UPPER DIGIT PUNCT " ")
+
+int cmd_main(int argc, const char **argv) {
+       /* Run all character type tests */
+       TEST_CHAR_CLASS(isspace);
+       TEST_CHAR_CLASS(isdigit);
+       TEST_CHAR_CLASS(isalpha);
+       TEST_CHAR_CLASS(isalnum);
+       TEST_CHAR_CLASS(is_glob_special);
+       TEST_CHAR_CLASS(is_regex_special);
+       TEST_CHAR_CLASS(is_pathspec_magic);
+       TEST_CHAR_CLASS(isascii);
+       TEST_CHAR_CLASS(islower);
+       TEST_CHAR_CLASS(isupper);
+       TEST_CHAR_CLASS(iscntrl);
+       TEST_CHAR_CLASS(ispunct);
+       TEST_CHAR_CLASS(isxdigit);
+       TEST_CHAR_CLASS(isprint);
+
+       return test_done();
+}
diff --git a/t/unit-tests/t-mem-pool.c b/t/unit-tests/t-mem-pool.c
new file mode 100644 (file)
index 0000000..a0d57df
--- /dev/null
@@ -0,0 +1,31 @@
+#include "test-lib.h"
+#include "mem-pool.h"
+
+static void setup_static(void (*f)(struct mem_pool *), size_t block_alloc)
+{
+       struct mem_pool pool = { .block_alloc = block_alloc };
+       f(&pool);
+       mem_pool_discard(&pool, 0);
+}
+
+static void t_calloc_100(struct mem_pool *pool)
+{
+       size_t size = 100;
+       char *buffer = mem_pool_calloc(pool, 1, size);
+       for (size_t i = 0; i < size; i++)
+               check_int(buffer[i], ==, 0);
+       if (!check(pool->mp_block != NULL))
+               return;
+       check(pool->mp_block->next_free != NULL);
+       check(pool->mp_block->end != NULL);
+}
+
+int cmd_main(int argc, const char **argv)
+{
+       TEST(setup_static(t_calloc_100, 1024 * 1024),
+            "mem_pool_calloc returns 100 zeroed bytes with big block");
+       TEST(setup_static(t_calloc_100, 1),
+            "mem_pool_calloc returns 100 zeroed bytes with tiny block");
+
+       return test_done();
+}
diff --git a/t/unit-tests/t-strbuf.c b/t/unit-tests/t-strbuf.c
new file mode 100644 (file)
index 0000000..de434a4
--- /dev/null
@@ -0,0 +1,120 @@
+#include "test-lib.h"
+#include "strbuf.h"
+
+/* wrapper that supplies tests with an empty, initialized strbuf */
+static void setup(void (*f)(struct strbuf*, void*), void *data)
+{
+       struct strbuf buf = STRBUF_INIT;
+
+       f(&buf, data);
+       strbuf_release(&buf);
+       check_uint(buf.len, ==, 0);
+       check_uint(buf.alloc, ==, 0);
+}
+
+/* wrapper that supplies tests with a populated, initialized strbuf */
+static void setup_populated(void (*f)(struct strbuf*, void*), char *init_str, void *data)
+{
+       struct strbuf buf = STRBUF_INIT;
+
+       strbuf_addstr(&buf, init_str);
+       check_uint(buf.len, ==, strlen(init_str));
+       f(&buf, data);
+       strbuf_release(&buf);
+       check_uint(buf.len, ==, 0);
+       check_uint(buf.alloc, ==, 0);
+}
+
+static int assert_sane_strbuf(struct strbuf *buf)
+{
+       /* Initialized strbufs should always have a non-NULL buffer */
+       if (!check(!!buf->buf))
+               return 0;
+       /* Buffers should always be NUL-terminated */
+       if (!check_char(buf->buf[buf->len], ==, '\0'))
+               return 0;
+       /*
+        * Freshly-initialized strbufs may not have a dynamically allocated
+        * buffer
+        */
+       if (buf->len == 0 && buf->alloc == 0)
+               return 1;
+       /* alloc must be at least one byte larger than len */
+       return check_uint(buf->len, <, buf->alloc);
+}
+
+static void t_static_init(void)
+{
+       struct strbuf buf = STRBUF_INIT;
+
+       check_uint(buf.len, ==, 0);
+       check_uint(buf.alloc, ==, 0);
+       check_char(buf.buf[0], ==, '\0');
+}
+
+static void t_dynamic_init(void)
+{
+       struct strbuf buf;
+
+       strbuf_init(&buf, 1024);
+       check(assert_sane_strbuf(&buf));
+       check_uint(buf.len, ==, 0);
+       check_uint(buf.alloc, >=, 1024);
+       check_char(buf.buf[0], ==, '\0');
+       strbuf_release(&buf);
+}
+
+static void t_addch(struct strbuf *buf, void *data)
+{
+       const char *p_ch = data;
+       const char ch = *p_ch;
+       size_t orig_alloc = buf->alloc;
+       size_t orig_len = buf->len;
+
+       if (!check(assert_sane_strbuf(buf)))
+               return;
+       strbuf_addch(buf, ch);
+       if (!check(assert_sane_strbuf(buf)))
+               return;
+       if (!(check_uint(buf->len, ==, orig_len + 1) &&
+             check_uint(buf->alloc, >=, orig_alloc)))
+               return; /* avoid de-referencing buf->buf */
+       check_char(buf->buf[buf->len - 1], ==, ch);
+       check_char(buf->buf[buf->len], ==, '\0');
+}
+
+static void t_addstr(struct strbuf *buf, void *data)
+{
+       const char *text = data;
+       size_t len = strlen(text);
+       size_t orig_alloc = buf->alloc;
+       size_t orig_len = buf->len;
+
+       if (!check(assert_sane_strbuf(buf)))
+               return;
+       strbuf_addstr(buf, text);
+       if (!check(assert_sane_strbuf(buf)))
+               return;
+       if (!(check_uint(buf->len, ==, orig_len + len) &&
+             check_uint(buf->alloc, >=, orig_alloc) &&
+             check_uint(buf->alloc, >, orig_len + len) &&
+             check_char(buf->buf[orig_len + len], ==, '\0')))
+           return;
+       check_str(buf->buf + orig_len, text);
+}
+
+int cmd_main(int argc, const char **argv)
+{
+       if (!TEST(t_static_init(), "static initialization works"))
+               test_skip_all("STRBUF_INIT is broken");
+       TEST(t_dynamic_init(), "dynamic initialization works");
+       TEST(setup(t_addch, "a"), "strbuf_addch adds char");
+       TEST(setup(t_addch, ""), "strbuf_addch adds NUL char");
+       TEST(setup_populated(t_addch, "initial value", "a"),
+            "strbuf_addch appends to initial value");
+       TEST(setup(t_addstr, "hello there"), "strbuf_addstr adds string");
+       TEST(setup_populated(t_addstr, "initial value", "hello there"),
+            "strbuf_addstr appends string to initial value");
+
+       return test_done();
+}
diff --git a/t/unit-tests/test-lib.c b/t/unit-tests/test-lib.c
new file mode 100644 (file)
index 0000000..7bf9dfd
--- /dev/null
@@ -0,0 +1,374 @@
+#include "test-lib.h"
+
+enum result {
+       RESULT_NONE,
+       RESULT_FAILURE,
+       RESULT_SKIP,
+       RESULT_SUCCESS,
+       RESULT_TODO
+};
+
+static struct {
+       enum result result;
+       int count;
+       unsigned failed :1;
+       unsigned lazy_plan :1;
+       unsigned running :1;
+       unsigned skip_all :1;
+       unsigned todo :1;
+} ctx = {
+       .lazy_plan = 1,
+       .result = RESULT_NONE,
+};
+
+#ifndef _MSC_VER
+#define make_relative(location) location
+#else
+/*
+ * Visual C interpolates the absolute Windows path for `__FILE__`,
+ * but we want to see relative paths, as verified by t0080.
+ */
+#include "dir.h"
+
+static const char *make_relative(const char *location)
+{
+       static char prefix[] = __FILE__, buf[PATH_MAX], *p;
+       static size_t prefix_len;
+
+       if (!prefix_len) {
+               size_t len = strlen(prefix);
+               const char *needle = "\\t\\unit-tests\\test-lib.c";
+               size_t needle_len = strlen(needle);
+
+               if (len < needle_len || strcmp(needle, prefix + len - needle_len))
+                       die("unexpected suffix of '%s'", prefix);
+
+               /* let it end in a directory separator */
+               prefix_len = len - needle_len + 1;
+       }
+
+       /* Does it not start with the expected prefix? */
+       if (fspathncmp(location, prefix, prefix_len))
+               return location;
+
+       strlcpy(buf, location + prefix_len, sizeof(buf));
+       /* convert backslashes to forward slashes */
+       for (p = buf; *p; p++)
+               if (*p == '\\')
+                       *p = '/';
+
+       return buf;
+}
+#endif
+
+static void msg_with_prefix(const char *prefix, const char *format, va_list ap)
+{
+       fflush(stderr);
+       if (prefix)
+               fprintf(stdout, "%s", prefix);
+       vprintf(format, ap); /* TODO: handle newlines */
+       putc('\n', stdout);
+       fflush(stdout);
+}
+
+void test_msg(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+       msg_with_prefix("# ", format, ap);
+       va_end(ap);
+}
+
+void test_plan(int count)
+{
+       assert(!ctx.running);
+
+       fflush(stderr);
+       printf("1..%d\n", count);
+       fflush(stdout);
+       ctx.lazy_plan = 0;
+}
+
+int test_done(void)
+{
+       assert(!ctx.running);
+
+       if (ctx.lazy_plan)
+               test_plan(ctx.count);
+
+       return ctx.failed;
+}
+
+void test_skip(const char *format, ...)
+{
+       va_list ap;
+
+       assert(ctx.running);
+
+       ctx.result = RESULT_SKIP;
+       va_start(ap, format);
+       if (format)
+               msg_with_prefix("# skipping test - ", format, ap);
+       va_end(ap);
+}
+
+void test_skip_all(const char *format, ...)
+{
+       va_list ap;
+       const char *prefix;
+
+       if (!ctx.count && ctx.lazy_plan) {
+               /* We have not printed a test plan yet */
+               prefix = "1..0 # SKIP ";
+               ctx.lazy_plan = 0;
+       } else {
+               /* We have already printed a test plan */
+               prefix = "Bail out! # ";
+               ctx.failed = 1;
+       }
+       ctx.skip_all = 1;
+       ctx.result = RESULT_SKIP;
+       va_start(ap, format);
+       msg_with_prefix(prefix, format, ap);
+       va_end(ap);
+}
+
+int test__run_begin(void)
+{
+       assert(!ctx.running);
+
+       ctx.count++;
+       ctx.result = RESULT_NONE;
+       ctx.running = 1;
+
+       return ctx.skip_all;
+}
+
+static void print_description(const char *format, va_list ap)
+{
+       if (format) {
+               fputs(" - ", stdout);
+               vprintf(format, ap);
+       }
+}
+
+int test__run_end(int was_run UNUSED, const char *location, const char *format, ...)
+{
+       va_list ap;
+
+       assert(ctx.running);
+       assert(!ctx.todo);
+
+       fflush(stderr);
+       va_start(ap, format);
+       if (!ctx.skip_all) {
+               switch (ctx.result) {
+               case RESULT_SUCCESS:
+                       printf("ok %d", ctx.count);
+                       print_description(format, ap);
+                       break;
+
+               case RESULT_FAILURE:
+                       printf("not ok %d", ctx.count);
+                       print_description(format, ap);
+                       break;
+
+               case RESULT_TODO:
+                       printf("not ok %d", ctx.count);
+                       print_description(format, ap);
+                       printf(" # TODO");
+                       break;
+
+               case RESULT_SKIP:
+                       printf("ok %d", ctx.count);
+                       print_description(format, ap);
+                       printf(" # SKIP");
+                       break;
+
+               case RESULT_NONE:
+                       test_msg("BUG: test has no checks at %s",
+                                make_relative(location));
+                       printf("not ok %d", ctx.count);
+                       print_description(format, ap);
+                       ctx.result = RESULT_FAILURE;
+                       break;
+               }
+       }
+       va_end(ap);
+       ctx.running = 0;
+       if (ctx.skip_all)
+               return 1;
+       putc('\n', stdout);
+       fflush(stdout);
+       ctx.failed |= ctx.result == RESULT_FAILURE;
+
+       return ctx.result != RESULT_FAILURE;
+}
+
+static void test_fail(void)
+{
+       assert(ctx.result != RESULT_SKIP);
+
+       ctx.result = RESULT_FAILURE;
+}
+
+static void test_pass(void)
+{
+       assert(ctx.result != RESULT_SKIP);
+
+       if (ctx.result == RESULT_NONE)
+               ctx.result = RESULT_SUCCESS;
+}
+
+static void test_todo(void)
+{
+       assert(ctx.result != RESULT_SKIP);
+
+       if (ctx.result != RESULT_FAILURE)
+               ctx.result = RESULT_TODO;
+}
+
+int test_assert(const char *location, const char *check, int ok)
+{
+       assert(ctx.running);
+
+       if (ctx.result == RESULT_SKIP) {
+               test_msg("skipping check '%s' at %s", check,
+                        make_relative(location));
+               return 1;
+       }
+       if (!ctx.todo) {
+               if (ok) {
+                       test_pass();
+               } else {
+                       test_msg("check \"%s\" failed at %s", check,
+                                make_relative(location));
+                       test_fail();
+               }
+       }
+
+       return !!ok;
+}
+
+void test__todo_begin(void)
+{
+       assert(ctx.running);
+       assert(!ctx.todo);
+
+       ctx.todo = 1;
+}
+
+int test__todo_end(const char *location, const char *check, int res)
+{
+       assert(ctx.running);
+       assert(ctx.todo);
+
+       ctx.todo = 0;
+       if (ctx.result == RESULT_SKIP)
+               return 1;
+       if (res) {
+               test_msg("todo check '%s' succeeded at %s", check,
+                        make_relative(location));
+               test_fail();
+       } else {
+               test_todo();
+       }
+
+       return !res;
+}
+
+int check_bool_loc(const char *loc, const char *check, int ok)
+{
+       return test_assert(loc, check, ok);
+}
+
+union test__tmp test__tmp[2];
+
+int check_int_loc(const char *loc, const char *check, int ok,
+                 intmax_t a, intmax_t b)
+{
+       int ret = test_assert(loc, check, ok);
+
+       if (!ret) {
+               test_msg("   left: %"PRIdMAX, a);
+               test_msg("  right: %"PRIdMAX, b);
+       }
+
+       return ret;
+}
+
+int check_uint_loc(const char *loc, const char *check, int ok,
+                  uintmax_t a, uintmax_t b)
+{
+       int ret = test_assert(loc, check, ok);
+
+       if (!ret) {
+               test_msg("   left: %"PRIuMAX, a);
+               test_msg("  right: %"PRIuMAX, b);
+       }
+
+       return ret;
+}
+
+static void print_one_char(char ch, char quote)
+{
+       if ((unsigned char)ch < 0x20u || ch == 0x7f) {
+               /* TODO: improve handling of \a, \b, \f ... */
+               printf("\\%03o", (unsigned char)ch);
+       } else {
+               if (ch == '\\' || ch == quote)
+                       putc('\\', stdout);
+               putc(ch, stdout);
+       }
+}
+
+static void print_char(const char *prefix, char ch)
+{
+       printf("# %s: '", prefix);
+       print_one_char(ch, '\'');
+       fputs("'\n", stdout);
+}
+
+int check_char_loc(const char *loc, const char *check, int ok, char a, char b)
+{
+       int ret = test_assert(loc, check, ok);
+
+       if (!ret) {
+               fflush(stderr);
+               print_char("   left", a);
+               print_char("  right", b);
+               fflush(stdout);
+       }
+
+       return ret;
+}
+
+static void print_str(const char *prefix, const char *str)
+{
+       printf("# %s: ", prefix);
+       if (!str) {
+               fputs("NULL\n", stdout);
+       } else {
+               putc('"', stdout);
+               while (*str)
+                       print_one_char(*str++, '"');
+               fputs("\"\n", stdout);
+       }
+}
+
+int check_str_loc(const char *loc, const char *check,
+                 const char *a, const char *b)
+{
+       int ok = (!a && !b) || (a && b && !strcmp(a, b));
+       int ret = test_assert(loc, check, ok);
+
+       if (!ret) {
+               fflush(stderr);
+               print_str("   left", a);
+               print_str("  right", b);
+               fflush(stdout);
+       }
+
+       return ret;
+}
diff --git a/t/unit-tests/test-lib.h b/t/unit-tests/test-lib.h
new file mode 100644 (file)
index 0000000..a8f07ae
--- /dev/null
@@ -0,0 +1,149 @@
+#ifndef TEST_LIB_H
+#define TEST_LIB_H
+
+#include "git-compat-util.h"
+
+/*
+ * Run a test function, returns 1 if the test succeeds, 0 if it
+ * fails. If test_skip_all() has been called then the test will not be
+ * run. The description for each test should be unique. For example:
+ *
+ *  TEST(test_something(arg1, arg2), "something %d %d", arg1, arg2)
+ */
+#define TEST(t, ...)                                   \
+       test__run_end(test__run_begin() ? 0 : (t, 1),   \
+                     TEST_LOCATION(),  __VA_ARGS__)
+
+/*
+ * Print a test plan, should be called before any tests. If the number
+ * of tests is not known in advance test_done() will automatically
+ * print a plan at the end of the test program.
+ */
+void test_plan(int count);
+
+/*
+ * test_done() must be called at the end of main(). It will print the
+ * plan if plan() was not called at the beginning of the test program
+ * and returns the exit code for the test program.
+ */
+int test_done(void);
+
+/* Skip the current test. */
+__attribute__((format (printf, 1, 2)))
+void test_skip(const char *format, ...);
+
+/* Skip all remaining tests. */
+__attribute__((format (printf, 1, 2)))
+void test_skip_all(const char *format, ...);
+
+/* Print a diagnostic message to stdout. */
+__attribute__((format (printf, 1, 2)))
+void test_msg(const char *format, ...);
+
+/*
+ * Test checks are built around test_assert(). checks return 1 on
+ * success, 0 on failure. If any check fails then the test will fail. To
+ * create a custom check define a function that wraps test_assert() and
+ * a macro to wrap that function to provide a source location and
+ * stringified arguments. Custom checks that take pointer arguments
+ * should be careful to check that they are non-NULL before
+ * dereferencing them. For example:
+ *
+ *  static int check_oid_loc(const char *loc, const char *check,
+ *                          struct object_id *a, struct object_id *b)
+ *  {
+ *         int res = test_assert(loc, check, a && b && oideq(a, b));
+ *
+ *         if (!res) {
+ *                 test_msg("   left: %s", a ? oid_to_hex(a) : "NULL";
+ *                 test_msg("  right: %s", b ? oid_to_hex(a) : "NULL";
+ *
+ *         }
+ *         return res;
+ *  }
+ *
+ *  #define check_oid(a, b) \
+ *         check_oid_loc(TEST_LOCATION(), "oideq("#a", "#b")", a, b)
+ */
+int test_assert(const char *location, const char *check, int ok);
+
+/* Helper macro to pass the location to checks */
+#define TEST_LOCATION() TEST__MAKE_LOCATION(__LINE__)
+
+/* Check a boolean condition. */
+#define check(x)                               \
+       check_bool_loc(TEST_LOCATION(), #x, x)
+int check_bool_loc(const char *loc, const char *check, int ok);
+
+/*
+ * Compare two integers. Prints a message with the two values if the
+ * comparison fails. NB this is not thread safe.
+ */
+#define check_int(a, op, b)                                            \
+       (test__tmp[0].i = (a), test__tmp[1].i = (b),                    \
+        check_int_loc(TEST_LOCATION(), #a" "#op" "#b,                  \
+                      test__tmp[0].i op test__tmp[1].i,                \
+                      test__tmp[0].i, test__tmp[1].i))
+int check_int_loc(const char *loc, const char *check, int ok,
+                 intmax_t a, intmax_t b);
+
+/*
+ * Compare two unsigned integers. Prints a message with the two values
+ * if the comparison fails. NB this is not thread safe.
+ */
+#define check_uint(a, op, b)                                           \
+       (test__tmp[0].u = (a), test__tmp[1].u = (b),                    \
+        check_uint_loc(TEST_LOCATION(), #a" "#op" "#b,                 \
+                       test__tmp[0].u op test__tmp[1].u,               \
+                       test__tmp[0].u, test__tmp[1].u))
+int check_uint_loc(const char *loc, const char *check, int ok,
+                  uintmax_t a, uintmax_t b);
+
+/*
+ * Compare two chars. Prints a message with the two values if the
+ * comparison fails. NB this is not thread safe.
+ */
+#define check_char(a, op, b)                                           \
+       (test__tmp[0].c = (a), test__tmp[1].c = (b),                    \
+        check_char_loc(TEST_LOCATION(), #a" "#op" "#b,                 \
+                       test__tmp[0].c op test__tmp[1].c,               \
+                       test__tmp[0].c, test__tmp[1].c))
+int check_char_loc(const char *loc, const char *check, int ok,
+                  char a, char b);
+
+/* Check whether two strings are equal. */
+#define check_str(a, b)                                                        \
+       check_str_loc(TEST_LOCATION(), "!strcmp("#a", "#b")", a, b)
+int check_str_loc(const char *loc, const char *check,
+                 const char *a, const char *b);
+
+/*
+ * Wrap a check that is known to fail. If the check succeeds then the
+ * test will fail. Returns 1 if the check fails, 0 if it
+ * succeeds. For example:
+ *
+ *  TEST_TODO(check(0));
+ */
+#define TEST_TODO(check) \
+       (test__todo_begin(), test__todo_end(TEST_LOCATION(), #check, check))
+
+/* Private helpers */
+
+#define TEST__STR(x) #x
+#define TEST__MAKE_LOCATION(line) __FILE__ ":" TEST__STR(line)
+
+union test__tmp {
+       intmax_t i;
+       uintmax_t u;
+       char c;
+};
+
+extern union test__tmp test__tmp[2];
+
+int test__run_begin(void);
+__attribute__((format (printf, 3, 4)))
+int test__run_end(int, const char *, const char *, ...);
+void test__todo_begin(void);
+int test__todo_end(const char *, const char *, int);
+
+#endif /* TEST_LIB_H */
index 669ebaf68be006ee53911f02da72e56cb5a100f0..3c8ee19975bb74d755fd40d3ee8d27fbaecc0e58 100755 (executable)
@@ -23,7 +23,7 @@ memcheck)
        VALGRIND_MAJOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*\([0-9]*\)')
        VALGRIND_MINOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*[0-9]*\.\([0-9]*\)')
        test 3 -gt "$VALGRIND_MAJOR" ||
-       test 3 -eq "$VALGRIND_MAJOR" -a 4 -gt "$VALGRIND_MINOR" ||
+       { test 3 -eq "$VALGRIND_MAJOR" && test 4 -gt "$VALGRIND_MINOR"; } ||
        TOOL_OPTIONS="$TOOL_OPTIONS --track-origins=yes"
        ;;
 *)
index e144712c85c055bcf3248ab342592b440a477062..29ed5ee486a4f07c3f0558101ef8efc46f3d6ab7 100755 (executable)
@@ -28,7 +28,7 @@ if [ "$allownonascii" != "true" ] &&
        # Note that the use of brackets around a tr range is ok here, (it's
        # even required, for portability to Solaris 10's /usr/bin/tr), since
        # the square bracket bytes happen to fall in the designated range.
-       test $(git diff --cached --name-only --diff-filter=A -z $against |
+       test $(git diff-index --cached --name-only --diff-filter=A -z $against |
          LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
 then
        cat <<\EOF
index 5f9074ad1c0b063a0cb906c2dc6182357adcd5ee..3509258be53644973ca2b2b04cc0ec5b27ea96c6 100644 (file)
@@ -6,7 +6,6 @@
 #include "environment.h"
 #include "object-file.h"
 #include "path.h"
-#include "sigchain.h"
 #include "string-list.h"
 #include "strbuf.h"
 #include "strvec.h"
diff --git a/trace.c b/trace.c
index 971a68abe84bf02656bf9cd5eab584833f3da9f7..8669ddfca25e8fe91b57d96875f63214e5700a9e 100644 (file)
--- a/trace.c
+++ b/trace.c
@@ -18,7 +18,7 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *  along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include "git-compat-util.h"
index 6dc74dff4c73205d332a227b0caf2497b71f7538..f1e268bd159ecb3d491721e67c34eabd397a66ea 100644 (file)
--- a/trace2.c
+++ b/trace2.c
@@ -1,12 +1,9 @@
 #include "git-compat-util.h"
 #include "config.h"
-#include "json-writer.h"
-#include "quote.h"
 #include "repository.h"
 #include "run-command.h"
 #include "sigchain.h"
 #include "thread-utils.h"
-#include "version.h"
 #include "trace.h"
 #include "trace2.h"
 #include "trace2/tr2_cfg.h"
@@ -20,6 +17,7 @@
 #include "trace2/tr2_tmr.h"
 
 static int trace2_enabled;
+static int trace2_redact = 1;
 
 static int tr2_next_child_id; /* modify under lock */
 static int tr2_next_exec_id; /* modify under lock */
@@ -227,6 +225,8 @@ void trace2_initialize_fl(const char *file, int line)
        if (!tr2_tgt_want_builtins())
                return;
        trace2_enabled = 1;
+       if (!git_env_bool("GIT_TRACE2_REDACT", 1))
+               trace2_redact = 0;
 
        tr2_sid_get();
 
@@ -247,12 +247,93 @@ int trace2_is_enabled(void)
        return trace2_enabled;
 }
 
+/*
+ * Redacts an argument, i.e. ensures that no password in
+ * https://user:password@host/-style URLs is logged.
+ *
+ * Returns the original if nothing needed to be redacted.
+ * Returns a pointer that needs to be `free()`d otherwise.
+ */
+static const char *redact_arg(const char *arg)
+{
+       const char *p, *colon;
+       size_t at;
+
+       if (!trace2_redact ||
+           (!skip_prefix(arg, "https://", &p) &&
+            !skip_prefix(arg, "http://", &p)))
+               return arg;
+
+       at = strcspn(p, "@/");
+       if (p[at] != '@')
+               return arg;
+
+       colon = memchr(p, ':', at);
+       if (!colon)
+               return arg;
+
+       return xstrfmt("%.*s:<REDACTED>%s", (int)(colon - arg), arg, p + at);
+}
+
+/*
+ * Redacts arguments in an argument list.
+ *
+ * Returns the original if nothing needed to be redacted.
+ * Otherwise, returns a new array that needs to be released
+ * via `free_redacted_argv()`.
+ */
+static const char **redact_argv(const char **argv)
+{
+       int i, j;
+       const char *redacted = NULL;
+       const char **ret;
+
+       if (!trace2_redact)
+               return argv;
+
+       for (i = 0; argv[i]; i++)
+               if ((redacted = redact_arg(argv[i])) != argv[i])
+                       break;
+
+       if (!argv[i])
+               return argv;
+
+       for (j = 0; argv[j]; j++)
+               ; /* keep counting */
+
+       ALLOC_ARRAY(ret, j + 1);
+       ret[j] = NULL;
+
+       for (j = 0; j < i; j++)
+               ret[j] = argv[j];
+       ret[i] = redacted;
+       for (++i; argv[i]; i++) {
+               redacted = redact_arg(argv[i]);
+               ret[i] = redacted ? redacted : argv[i];
+       }
+
+       return ret;
+}
+
+static void free_redacted_argv(const char **redacted, const char **argv)
+{
+       int i;
+
+       if (redacted != argv) {
+               for (i = 0; argv[i]; i++)
+                       if (redacted[i] != argv[i])
+                               free((void *)redacted[i]);
+               free((void *)redacted);
+       }
+}
+
 void trace2_cmd_start_fl(const char *file, int line, const char **argv)
 {
        struct tr2_tgt *tgt_j;
        int j;
        uint64_t us_now;
        uint64_t us_elapsed_absolute;
+       const char **redacted;
 
        if (!trace2_enabled)
                return;
@@ -260,10 +341,14 @@ void trace2_cmd_start_fl(const char *file, int line, const char **argv)
        us_now = getnanotime() / 1000;
        us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
 
+       redacted = redact_argv(argv);
+
        for_each_wanted_builtin (j, tgt_j)
                if (tgt_j->pfn_start_fl)
                        tgt_j->pfn_start_fl(file, line, us_elapsed_absolute,
-                                           argv);
+                                           redacted);
+
+       free_redacted_argv(redacted, argv);
 }
 
 void trace2_cmd_exit_fl(const char *file, int line, int code)
@@ -409,6 +494,7 @@ void trace2_child_start_fl(const char *file, int line,
        int j;
        uint64_t us_now;
        uint64_t us_elapsed_absolute;
+       const char **orig_argv = cmd->args.v;
 
        if (!trace2_enabled)
                return;
@@ -419,10 +505,24 @@ void trace2_child_start_fl(const char *file, int line,
        cmd->trace2_child_id = tr2tls_locked_increment(&tr2_next_child_id);
        cmd->trace2_child_us_start = us_now;
 
+       /*
+        * The `pfn_child_start_fl` API takes a `struct child_process`
+        * rather than a simple `argv` for the child because some
+        * targets make use of the additional context bits/values. So
+        * temporarily replace the original argv (inside the `strvec`)
+        * with a possibly redacted version.
+        */
+       cmd->args.v = redact_argv(orig_argv);
+
        for_each_wanted_builtin (j, tgt_j)
                if (tgt_j->pfn_child_start_fl)
                        tgt_j->pfn_child_start_fl(file, line,
                                                  us_elapsed_absolute, cmd);
+
+       if (cmd->args.v != orig_argv) {
+               free_redacted_argv(cmd->args.v, orig_argv);
+               cmd->args.v = orig_argv;
+       }
 }
 
 void trace2_child_exit_fl(const char *file, int line, struct child_process *cmd,
@@ -493,6 +593,7 @@ int trace2_exec_fl(const char *file, int line, const char *exe,
        int exec_id;
        uint64_t us_now;
        uint64_t us_elapsed_absolute;
+       const char **redacted;
 
        if (!trace2_enabled)
                return -1;
@@ -502,10 +603,14 @@ int trace2_exec_fl(const char *file, int line, const char *exe,
 
        exec_id = tr2tls_locked_increment(&tr2_next_exec_id);
 
+       redacted = redact_argv(argv);
+
        for_each_wanted_builtin (j, tgt_j)
                if (tgt_j->pfn_exec_fl)
                        tgt_j->pfn_exec_fl(file, line, us_elapsed_absolute,
-                                          exec_id, exe, argv);
+                                          exec_id, exe, redacted);
+
+       free_redacted_argv(redacted, argv);
 
        return exec_id;
 }
@@ -637,13 +742,19 @@ void trace2_def_param_fl(const char *file, int line, const char *param,
 {
        struct tr2_tgt *tgt_j;
        int j;
+       const char *redacted;
 
        if (!trace2_enabled)
                return;
 
+       redacted = redact_arg(value);
+
        for_each_wanted_builtin (j, tgt_j)
                if (tgt_j->pfn_param_fl)
-                       tgt_j->pfn_param_fl(file, line, param, value, kvi);
+                       tgt_j->pfn_param_fl(file, line, param, redacted, kvi);
+
+       if (redacted != value)
+               free((void *)redacted);
 }
 
 void trace2_def_repo_fl(const char *file, int line, struct repository *repo)
index 40d8c2e02a5e50ce4f24bc4bd656498773e0853e..1f0669bbd2d4f0ade2b39960ad89d8e5397cb6b1 100644 (file)
--- a/trace2.h
+++ b/trace2.h
@@ -337,8 +337,8 @@ struct key_value_info;
 void trace2_def_param_fl(const char *file, int line, const char *param,
                         const char *value, const struct key_value_info *kvi);
 
-#define trace2_def_param(param, value) \
-       trace2_def_param_fl(__FILE__, __LINE__, (param), (value))
+#define trace2_def_param(param, value, kvi) \
+       trace2_def_param_fl(__FILE__, __LINE__, (param), (value), (kvi))
 
 /*
  * Tell trace2 about a newly instantiated repo object and assign
index 87cf9034fba4b91b4d569ea4013e13f9d19b4aa9..d3a33715c14b9c83647cc4940da4d1abad30dc63 100644 (file)
@@ -1,5 +1,4 @@
 #include "git-compat-util.h"
-#include "thread-utils.h"
 #include "trace2/tr2_tgt.h"
 #include "trace2/tr2_tls.h"
 #include "trace2/tr2_ctr.h"
index d3ecac27728efe8c06dd78f6e394d789d4c1bc7e..048cdd543830418bfe53957b278ab654ecfdcb66 100644 (file)
@@ -68,6 +68,8 @@ static int tr2_sysenv_cb(const char *key, const char *value,
 
        for (k = 0; k < ARRAY_SIZE(tr2_sysenv_settings); k++) {
                if (!strcmp(key, tr2_sysenv_settings[k].git_config_name)) {
+                       if (!value)
+                               return config_error_nonbool(key);
                        free(tr2_sysenv_settings[k].value);
                        tr2_sysenv_settings[k].value = xstrdup(value);
                        return 0;
index 38d5ebddf65f8061e9d9dbdf18f35c274bc6a31c..baef48aa6989cede1d8bf62837388ab3379e9604 100644 (file)
@@ -2,6 +2,7 @@
 #include "config.h"
 #include "repository.h"
 #include "run-command.h"
+#include "strbuf.h"
 #include "quote.h"
 #include "version.h"
 #include "trace2/tr2_dst.h"
index 601c9e5036fb27a7f88957559f03821c2fffa4a2..4f75392952b6a429682d8989fe7146487ce0cb6a 100644 (file)
@@ -1,4 +1,5 @@
 #include "git-compat-util.h"
+#include "strbuf.h"
 #include "thread-utils.h"
 #include "trace.h"
 #include "trace2/tr2_tls.h"
index f9049805d4dcd6eea6550042a1887357ad85d6fd..3dfe6557fc46129edfa7a121522e9a6d54973f28 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef TR2_TLS_H
 #define TR2_TLS_H
 
-#include "strbuf.h"
 #include "trace2/tr2_ctr.h"
 #include "trace2/tr2_tmr.h"
 
index 31d0e4d1bd1220ea0c9d9b204223766235d81e07..51f564b07a4091a24db71b3a756b4730173cae98 100644 (file)
@@ -1,5 +1,4 @@
 #include "git-compat-util.h"
-#include "thread-utils.h"
 #include "trace2/tr2_tgt.h"
 #include "trace2/tr2_tls.h"
 #include "trace2/tr2_tmr.h"
index b6de5d9cb2dab2c07c26775e62e5bfc40241c9fc..3a0710a4583a4a11fa5a87cda88ef6a3d0610870 100644 (file)
--- a/trailer.c
+++ b/trailer.c
@@ -507,6 +507,8 @@ static int git_trailer_default_config(const char *conf_key, const char *value,
                                warning(_("unknown value '%s' for key '%s'"),
                                        value, conf_key);
                } else if (!strcmp(trailer_item, "separators")) {
+                       if (!value)
+                               return config_error_nonbool(conf_key);
                        separators = xstrdup(value);
                }
        }
@@ -551,16 +553,22 @@ static int git_trailer_config(const char *conf_key, const char *value,
        case TRAILER_KEY:
                if (conf->key)
                        warning(_("more than one %s"), conf_key);
+               if (!value)
+                       return config_error_nonbool(conf_key);
                conf->key = xstrdup(value);
                break;
        case TRAILER_COMMAND:
                if (conf->command)
                        warning(_("more than one %s"), conf_key);
+               if (!value)
+                       return config_error_nonbool(conf_key);
                conf->command = xstrdup(value);
                break;
        case TRAILER_CMD:
                if (conf->cmd)
                        warning(_("more than one %s"), conf_key);
+               if (!value)
+                       return config_error_nonbool(conf_key);
                conf->cmd = xstrdup(value);
                break;
        case TRAILER_WHERE:
@@ -809,28 +817,56 @@ static ssize_t last_line(const char *buf, size_t len)
 }
 
 /*
- * Return the position of the start of the patch or the length of str if there
- * is no patch in the message.
+ * Find the end of the log message as an offset from the start of the input
+ * (where callers of this function are interested in looking for a trailers
+ * block in the same input). We have to consider two categories of content that
+ * can come at the end of the input which we want to ignore (because they don't
+ * belong in the log message):
+ *
+ * (1) the "patch part" which begins with a "---" divider and has patch
+ * information (like the output of git-format-patch), and
+ *
+ * (2) any trailing comment lines, blank lines like in the output of "git
+ * commit -v", or stuff below the "cut" (scissor) line.
+ *
+ * As a formula, the situation looks like this:
+ *
+ *     INPUT = LOG MESSAGE + IGNORED
+ *
+ * where IGNORED can be either of the two categories described above. It may be
+ * that there is nothing to ignore. Now it may be the case that the LOG MESSAGE
+ * contains a trailer block, but that's not the concern of this function.
  */
-static size_t find_patch_start(const char *str)
+static size_t find_end_of_log_message(const char *input, int no_divider)
 {
+       size_t end;
        const char *s;
 
-       for (s = str; *s; s = next_line(s)) {
+       /* Assume the naive end of the input is already what we want. */
+       end = strlen(input);
+
+       if (no_divider)
+               return end;
+
+       /* Optionally skip over any patch part ("---" line and below). */
+       for (s = input; *s; s = next_line(s)) {
                const char *v;
 
-               if (skip_prefix(s, "---", &v) && isspace(*v))
-                       return s - str;
+               if (skip_prefix(s, "---", &v) && isspace(*v)) {
+                       end = s - input;
+                       break;
+               }
        }
 
-       return s - str;
+       /* Skip over other ignorable bits. */
+       return end - ignored_log_message_bytes(input, end);
 }
 
 /*
  * Return the position of the first trailer line or len if there are no
  * trailers.
  */
-static size_t find_trailer_start(const char *buf, size_t len)
+static size_t find_trailer_block_start(const char *buf, size_t len)
 {
        const char *s;
        ssize_t end_of_title, l;
@@ -925,12 +961,6 @@ continue_outer_loop:
        return len;
 }
 
-/* Return the position of the end of the trailers. */
-static size_t find_trailer_end(const char *buf, size_t len)
-{
-       return len - ignore_non_trailer(buf, len);
-}
-
 static int ends_with_blank_line(const char *buf, size_t len)
 {
        ssize_t ll = last_line(buf, len);
@@ -1052,7 +1082,6 @@ void process_trailers(const char *file,
        LIST_HEAD(head);
        struct strbuf sb = STRBUF_INIT;
        struct trailer_info info;
-       size_t trailer_end;
        FILE *outfile = stdout;
 
        ensure_configured();
@@ -1063,11 +1092,10 @@ void process_trailers(const char *file,
                outfile = create_in_place_tempfile(file);
 
        parse_trailers(&info, sb.buf, &head, opts);
-       trailer_end = info.trailer_end - sb.buf;
 
        /* Print the lines before the trailers */
        if (!opts->only_trailers)
-               fwrite(sb.buf, 1, info.trailer_start - sb.buf, outfile);
+               fwrite(sb.buf, 1, info.trailer_block_start, outfile);
 
        if (!opts->only_trailers && !info.blank_line_before_trailer)
                fprintf(outfile, "\n");
@@ -1089,7 +1117,7 @@ void process_trailers(const char *file,
 
        /* Print the lines after the trailers as is */
        if (!opts->only_trailers)
-               fwrite(sb.buf + trailer_end, 1, sb.len - trailer_end, outfile);
+               fwrite(sb.buf + info.trailer_block_end, 1, sb.len - info.trailer_block_end, outfile);
 
        if (opts->in_place)
                if (rename_tempfile(&trailers_tempfile, file))
@@ -1101,7 +1129,7 @@ void process_trailers(const char *file,
 void trailer_info_get(struct trailer_info *info, const char *str,
                      const struct process_trailer_options *opts)
 {
-       int patch_start, trailer_end, trailer_start;
+       size_t end_of_log_message = 0, trailer_block_start = 0;
        struct strbuf **trailer_lines, **ptr;
        char **trailer_strings = NULL;
        size_t nr = 0, alloc = 0;
@@ -1109,16 +1137,11 @@ void trailer_info_get(struct trailer_info *info, const char *str,
 
        ensure_configured();
 
-       if (opts->no_divider)
-               patch_start = strlen(str);
-       else
-               patch_start = find_patch_start(str);
-
-       trailer_end = find_trailer_end(str, patch_start);
-       trailer_start = find_trailer_start(str, trailer_end);
+       end_of_log_message = find_end_of_log_message(str, opts->no_divider);
+       trailer_block_start = find_trailer_block_start(str, end_of_log_message);
 
-       trailer_lines = strbuf_split_buf(str + trailer_start,
-                                        trailer_end - trailer_start,
+       trailer_lines = strbuf_split_buf(str + trailer_block_start,
+                                        end_of_log_message - trailer_block_start,
                                         '\n',
                                         0);
        for (ptr = trailer_lines; *ptr; ptr++) {
@@ -1139,9 +1162,9 @@ void trailer_info_get(struct trailer_info *info, const char *str,
        strbuf_list_free(trailer_lines);
 
        info->blank_line_before_trailer = ends_with_blank_line(str,
-                                                              trailer_start);
-       info->trailer_start = str + trailer_start;
-       info->trailer_end = str + trailer_end;
+                                                              trailer_block_start);
+       info->trailer_block_start = trailer_block_start;
+       info->trailer_block_end = end_of_log_message;
        info->trailers = trailer_strings;
        info->trailer_nr = nr;
 }
@@ -1156,6 +1179,7 @@ void trailer_info_release(struct trailer_info *info)
 
 static void format_trailer_info(struct strbuf *out,
                                const struct trailer_info *info,
+                               const char *msg,
                                const struct process_trailer_options *opts)
 {
        size_t origlen = out->len;
@@ -1165,8 +1189,8 @@ static void format_trailer_info(struct strbuf *out,
        if (!opts->only_trailers && !opts->unfold && !opts->filter &&
            !opts->separator && !opts->key_only && !opts->value_only &&
            !opts->key_value_separator) {
-               strbuf_add(out, info->trailer_start,
-                          info->trailer_end - info->trailer_start);
+               strbuf_add(out, msg + info->trailer_block_start,
+                          info->trailer_block_end - info->trailer_block_start);
                return;
        }
 
@@ -1220,7 +1244,7 @@ void format_trailers_from_commit(struct strbuf *out, const char *msg,
        struct trailer_info info;
 
        trailer_info_get(&info, msg, opts);
-       format_trailer_info(out, &info, opts);
+       format_trailer_info(out, &info, msg, opts);
        trailer_info_release(&info);
 }
 
index ab2cd017567b3f782fa9b342ad03e7e93bc17a37..1644cd05f60d9f3ceb89ffa66fb5c5b3da9e5cd4 100644 (file)
--- a/trailer.h
+++ b/trailer.h
@@ -32,16 +32,16 @@ int trailer_set_if_missing(enum trailer_if_missing *item, const char *value);
 struct trailer_info {
        /*
         * True if there is a blank line before the location pointed to by
-        * trailer_start.
+        * trailer_block_start.
         */
        int blank_line_before_trailer;
 
        /*
-        * Pointers to the start and end of the trailer block found. If there
-        * is no trailer block found, these 2 pointers point to the end of the
-        * input string.
+        * Offsets to the trailer block start and end positions in the input
+        * string. If no trailer block is found, these are both set to the
+        * "true" end of the input (find_end_of_log_message()).
         */
-       const char *trailer_start, *trailer_end;
+       size_t trailer_block_start, trailer_block_end;
 
        /*
         * Array of trailers found.
index 49811ef176dbc5af5e33fe47cf93cf1f57719f5e..e34a8f47cfbf5cba150e1aa7c6267fc2b1c080c0 100644 (file)
@@ -3,13 +3,11 @@
 #include "quote.h"
 #include "run-command.h"
 #include "commit.h"
-#include "diff.h"
 #include "environment.h"
 #include "gettext.h"
 #include "hex.h"
 #include "object-name.h"
 #include "repository.h"
-#include "revision.h"
 #include "remote.h"
 #include "string-list.h"
 #include "thread-utils.h"
index 219af8fd50ed57c0fc0f954965e481b51e7aae72..df518ead70c387dd354dd69a5ee4af8a3ab0d560 100644 (file)
@@ -10,9 +10,7 @@
 #include "remote.h"
 #include "connect.h"
 #include "send-pack.h"
-#include "walker.h"
 #include "bundle.h"
-#include "dir.h"
 #include "gettext.h"
 #include "refs.h"
 #include "refspec.h"
@@ -26,7 +24,6 @@
 #include "transport-internal.h"
 #include "protocol.h"
 #include "object-name.h"
-#include "object-store-ll.h"
 #include "color.h"
 #include "bundle-uri.h"
 
@@ -1470,6 +1467,7 @@ int transport_push(struct repository *r,
        if (porcelain && !push_ret)
                puts("Done");
        else if (!quiet && !ret && !transport_refs_pushed(remote_refs))
+               /* stable plumbing output; do not modify or localize */
                fprintf(stderr, "Everything up-to-date\n");
 
 done:
diff --git a/tree.c b/tree.c
index 990f9c9854e6a1a957ed01f74b20e694af07f278..508e5fd76fd5bb588796bb9c51a78557880ba074 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -1,12 +1,9 @@
 #include "git-compat-util.h"
-#include "cache-tree.h"
 #include "hex.h"
 #include "tree.h"
 #include "object-name.h"
 #include "object-store-ll.h"
-#include "blob.h"
 #include "commit.h"
-#include "tag.h"
 #include "alloc.h"
 #include "tree-walk.h"
 #include "repository.h"
index ea234ab6a4522489c7a70a3cfa6d6449c46013c5..2537affa90762228a19d305be749f410d5afb5dc 100644 (file)
@@ -9,13 +9,10 @@
 #include "repository.h"
 #include "object-store-ll.h"
 #include "oid-array.h"
-#include "tag.h"
 #include "object.h"
 #include "commit.h"
 #include "diff.h"
 #include "revision.h"
-#include "list-objects.h"
-#include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
 #include "run-command.h"
 #include "connect.h"
 #include "string-list.h"
 #include "strvec.h"
 #include "trace2.h"
-#include "prio-queue.h"
 #include "protocol.h"
-#include "quote.h"
 #include "upload-pack.h"
-#include "serve.h"
 #include "commit-graph.h"
 #include "commit-reach.h"
 #include "shallow.h"
diff --git a/utf8.c b/utf8.c
index 6a0dd25b0fe414764f89ae48384f68c959dcafe3..6bfaefa28ebbbfd145b30b58fe0e937ea921ec91 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -2,7 +2,7 @@
 #include "strbuf.h"
 #include "utf8.h"
 
-/* This code is originally from http://www.cl.cam.ac.uk/~mgk25/ucs/ */
+/* This code is originally from https://www.cl.cam.ac.uk/~mgk25/ucs/ */
 
 static const char utf16_be_bom[] = {'\xFE', '\xFF'};
 static const char utf16_le_bom[] = {'\xFF', '\xFE'};
diff --git a/utf8.h b/utf8.h
index b68efef6f4398dae9617e6c5eaa81b0ddcebcb6e..35df76086a6c91a414190f7af8d05f0f0042ecda 100644 (file)
--- a/utf8.h
+++ b/utf8.h
@@ -83,7 +83,7 @@ void strbuf_utf8_align(struct strbuf *buf, align_type position, unsigned int wid
  * BOM must not be used [1]. The same applies for the UTF-32 equivalents.
  * The function returns true if this rule is violated.
  *
- * [1] http://unicode.org/faq/utf_bom.html#bom10
+ * [1] https://unicode.org/faq/utf_bom.html#bom10
  */
 int has_prohibited_utf_bom(const char *enc, const char *data, size_t len);
 
@@ -99,8 +99,8 @@ int has_prohibited_utf_bom(const char *enc, const char *data, size_t len);
  * Therefore, strictly requiring a BOM seems to be the safest option for
  * content in Git.
  *
- * [1] http://unicode.org/faq/utf_bom.html#gen6
- * [2] http://www.unicode.org/versions/Unicode10.0.0/ch03.pdf
+ * [1] https://unicode.org/faq/utf_bom.html#gen6
+ * [2] https://www.unicode.org/versions/Unicode10.0.0/ch03.pdf
  *     Section 3.10, D98, page 132
  * [3] https://encoding.spec.whatwg.org/#utf-16le
  */
index a56a6c2a3d136d31019520fdae68fc46c428194d..b02a05a74a341157fa8dff7da22936127bebf18e 100644 (file)
 #include "wt-status.h"
 #include "config.h"
 
+void free_worktree(struct worktree *worktree)
+{
+       if (!worktree)
+               return;
+       free(worktree->path);
+       free(worktree->id);
+       free(worktree->head_ref);
+       free(worktree->lock_reason);
+       free(worktree->prune_reason);
+       free(worktree);
+}
+
 void free_worktrees(struct worktree **worktrees)
 {
        int i = 0;
-
-       for (i = 0; worktrees[i]; i++) {
-               free(worktrees[i]->path);
-               free(worktrees[i]->id);
-               free(worktrees[i]->head_ref);
-               free(worktrees[i]->lock_reason);
-               free(worktrees[i]->prune_reason);
-               free(worktrees[i]);
-       }
+       for (i = 0; worktrees[i]; i++)
+               free_worktree(worktrees[i]);
        free (worktrees);
 }
 
@@ -51,7 +56,7 @@ static void add_head_info(struct worktree *wt)
 /**
  * get the main worktree
  */
-static struct worktree *get_main_worktree(void)
+static struct worktree *get_main_worktree(int skip_reading_head)
 {
        struct worktree *worktree = NULL;
        struct strbuf worktree_path = STRBUF_INIT;
@@ -70,11 +75,13 @@ static struct worktree *get_main_worktree(void)
         */
        worktree->is_bare = (is_bare_repository_cfg == 1) ||
                is_bare_repository();
-       add_head_info(worktree);
+       if (!skip_reading_head)
+               add_head_info(worktree);
        return worktree;
 }
 
-static struct worktree *get_linked_worktree(const char *id)
+struct worktree *get_linked_worktree(const char *id,
+                                    int skip_reading_head)
 {
        struct worktree *worktree = NULL;
        struct strbuf path = STRBUF_INIT;
@@ -93,7 +100,8 @@ static struct worktree *get_linked_worktree(const char *id)
        CALLOC_ARRAY(worktree, 1);
        worktree->path = strbuf_detach(&worktree_path, NULL);
        worktree->id = xstrdup(id);
-       add_head_info(worktree);
+       if (!skip_reading_head)
+               add_head_info(worktree);
 
 done:
        strbuf_release(&path);
@@ -118,7 +126,14 @@ static void mark_current_worktree(struct worktree **worktrees)
        free(git_dir);
 }
 
-struct worktree **get_worktrees(void)
+/*
+ * NEEDSWORK: This function exists so that we can look up metadata of a
+ * worktree without trying to access any of its internals like the refdb. It
+ * would be preferable to instead have a corruption-tolerant function for
+ * retrieving worktree metadata that could be used when the worktree is known
+ * to not be in a healthy state, e.g. when creating or repairing it.
+ */
+static struct worktree **get_worktrees_internal(int skip_reading_head)
 {
        struct worktree **list = NULL;
        struct strbuf path = STRBUF_INIT;
@@ -128,7 +143,7 @@ struct worktree **get_worktrees(void)
 
        ALLOC_ARRAY(list, alloc);
 
-       list[counter++] = get_main_worktree();
+       list[counter++] = get_main_worktree(skip_reading_head);
 
        strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
        dir = opendir(path.buf);
@@ -137,7 +152,7 @@ struct worktree **get_worktrees(void)
                while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
                        struct worktree *linked = NULL;
 
-                       if ((linked = get_linked_worktree(d->d_name))) {
+                       if ((linked = get_linked_worktree(d->d_name, skip_reading_head))) {
                                ALLOC_GROW(list, counter + 1, alloc);
                                list[counter++] = linked;
                        }
@@ -151,6 +166,11 @@ struct worktree **get_worktrees(void)
        return list;
 }
 
+struct worktree **get_worktrees(void)
+{
+       return get_worktrees_internal(0);
+}
+
 const char *get_worktree_git_dir(const struct worktree *wt)
 {
        if (!wt)
@@ -395,9 +415,9 @@ int is_worktree_being_bisected(const struct worktree *wt,
 
        memset(&state, 0, sizeof(state));
        found_bisect = wt_status_check_bisect(wt, &state) &&
-                      state.branch &&
+                      state.bisecting_from &&
                       skip_prefix(target, "refs/heads/", &target) &&
-                      !strcmp(state.branch, target);
+                      !strcmp(state.bisecting_from, target);
        wt_status_state_free_buffers(&state);
        return found_bisect;
 }
@@ -591,7 +611,7 @@ static void repair_noop(int iserr UNUSED,
 
 void repair_worktrees(worktree_repair_fn fn, void *cb_data)
 {
-       struct worktree **worktrees = get_worktrees();
+       struct worktree **worktrees = get_worktrees_internal(1);
        struct worktree **wt = worktrees + 1; /* +1 skips main worktree */
 
        if (!fn)
index ce45b66de9e82d4d619b6f74e0e77c8bd5ab3f53..f14784a2ff871c69e5b5e28466439201721407b2 100644 (file)
@@ -57,6 +57,13 @@ struct worktree *find_worktree(struct worktree **list,
                               const char *prefix,
                               const char *arg);
 
+/*
+ * Look up the worktree corresponding to `id`, or NULL of no such worktree
+ * exists.
+ */
+struct worktree *get_linked_worktree(const char *id,
+                                    int skip_reading_head);
+
 /*
  * Return the worktree corresponding to `path`, or NULL if no such worktree
  * exists.
@@ -134,6 +141,11 @@ void repair_worktrees(worktree_repair_fn, void *cb_data);
  */
 void repair_worktree_at_path(const char *, worktree_repair_fn, void *cb_data);
 
+/*
+ * Free up the memory for a worktree.
+ */
+void free_worktree(struct worktree *);
+
 /*
  * Free up the memory for worktree(s)
  */
index 7da15a56da614ce82ca1d6a5c6e2277b444f1063..eeac3741cf1a4e90ceab3bef07028aad632ebfb6 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -5,7 +5,6 @@
 #include "abspath.h"
 #include "parse.h"
 #include "gettext.h"
-#include "repository.h"
 #include "strbuf.h"
 #include "trace2.h"
 
index 42a2dc73cd3f18638445780e7fa9aa9b67ba871c..39421528653e29cbd724153edf94d2613ea1d6b4 100644 (file)
 void maybe_flush_or_die(FILE *f, const char *desc)
 {
        static int skip_stdout_flush = -1;
-       struct stat st;
-       char *cp;
 
        if (f == stdout) {
                if (skip_stdout_flush < 0) {
-                       /* NEEDSWORK: make this a normal Boolean */
-                       cp = getenv("GIT_FLUSH");
-                       if (cp)
-                               skip_stdout_flush = (atoi(cp) == 0);
-                       else if ((fstat(fileno(stdout), &st) == 0) &&
-                                S_ISREG(st.st_mode))
-                               skip_stdout_flush = 1;
-                       else
-                               skip_stdout_flush = 0;
+                       skip_stdout_flush = git_env_bool("GIT_FLUSH", -1);
+                       if (skip_stdout_flush < 0) {
+                               struct stat st;
+                               if (fstat(fileno(stdout), &st))
+                                       skip_stdout_flush = 0;
+                               else
+                                       skip_stdout_flush = S_ISREG(st.st_mode);
+                       }
                }
                if (skip_stdout_flush && !ferror(f))
                        return;
index 9f45bf69490e6fa4939948aadb63e7201d0e868e..b5a29083df503e3adfb6f2e4a74e55d4f44514b6 100644 (file)
@@ -861,6 +861,7 @@ void wt_status_state_free_buffers(struct wt_status_state *state)
        FREE_AND_NULL(state->branch);
        FREE_AND_NULL(state->onto);
        FREE_AND_NULL(state->detached_from);
+       FREE_AND_NULL(state->bisecting_from);
 }
 
 static void wt_longstatus_print_unmerged(struct wt_status *s)
@@ -1295,26 +1296,32 @@ static char *read_line_from_git_path(const char *filename)
 static int split_commit_in_progress(struct wt_status *s)
 {
        int split_in_progress = 0;
-       char *head, *orig_head, *rebase_amend, *rebase_orig_head;
+       struct object_id head_oid, orig_head_oid;
+       char *rebase_amend, *rebase_orig_head;
+       int head_flags, orig_head_flags;
 
        if ((!s->amend && !s->nowarn && !s->workdir_dirty) ||
            !s->branch || strcmp(s->branch, "HEAD"))
                return 0;
 
-       head = read_line_from_git_path("HEAD");
-       orig_head = read_line_from_git_path("ORIG_HEAD");
+       if (read_ref_full("HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+                         &head_oid, &head_flags) ||
+           read_ref_full("ORIG_HEAD", RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+                         &orig_head_oid, &orig_head_flags))
+               return 0;
+       if (head_flags & REF_ISSYMREF || orig_head_flags & REF_ISSYMREF)
+               return 0;
+
        rebase_amend = read_line_from_git_path("rebase-merge/amend");
        rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head");
 
-       if (!head || !orig_head || !rebase_amend || !rebase_orig_head)
+       if (!rebase_amend || !rebase_orig_head)
                ; /* fall through, no split in progress */
        else if (!strcmp(rebase_amend, rebase_orig_head))
-               split_in_progress = !!strcmp(head, rebase_amend);
-       else if (strcmp(orig_head, rebase_orig_head))
+               split_in_progress = !!strcmp(oid_to_hex(&head_oid), rebase_amend);
+       else if (strcmp(oid_to_hex(&orig_head_oid), rebase_orig_head))
                split_in_progress = 1;
 
-       free(head);
-       free(orig_head);
        free(rebase_amend);
        free(rebase_orig_head);
 
@@ -1569,10 +1576,10 @@ static void show_revert_in_progress(struct wt_status *s,
 static void show_bisect_in_progress(struct wt_status *s,
                                    const char *color)
 {
-       if (s->state.branch)
+       if (s->state.bisecting_from)
                status_printf_ln(s, color,
                                 _("You are currently bisecting, started from branch '%s'."),
-                                s->state.branch);
+                                s->state.bisecting_from);
        else
                status_printf_ln(s, color,
                                 _("You are currently bisecting."));
@@ -1733,7 +1740,7 @@ int wt_status_check_bisect(const struct worktree *wt,
 
        if (!stat(worktree_git_path(wt, "BISECT_LOG"), &st)) {
                state->bisect_in_progress = 1;
-               state->branch = get_branch(wt, "BISECT_START");
+               state->bisecting_from = get_branch(wt, "BISECT_START");
                return 1;
        }
        return 0;
index ab9cc9d8f032b77fbf6bfd5882c45a801a28fa09..819dcad72300c56900ac05f3943d27dd2b9ed7a4 100644 (file)
@@ -94,6 +94,7 @@ struct wt_status_state {
        char *branch;
        char *onto;
        char *detached_from;
+       char *bisecting_from;
        struct object_id detached_oid;
        struct object_id revert_head_oid;
        struct object_id cherry_pick_head_oid;
index adcea109fa9a54eaa96c67c1b5e8a1689af253af..3162f517434353609e1fa45ab67ea3cf4623f8a3 100644 (file)
@@ -1,4 +1,5 @@
 #include "git-compat-util.h"
+#include "gettext.h"
 #include "config.h"
 #include "hex.h"
 #include "object-store-ll.h"
@@ -6,8 +7,6 @@
 #include "xdiff-interface.h"
 #include "xdiff/xtypes.h"
 #include "xdiff/xdiffi.h"
-#include "xdiff/xemit.h"
-#include "xdiff/xmacros.h"
 #include "xdiff/xutils.h"
 
 struct xdiff_emit_state {
@@ -313,7 +312,7 @@ int git_xmerge_config(const char *var, const char *value,
 {
        if (!strcmp(var, "merge.conflictstyle")) {
                if (!value)
-                       die("'%s' is not a boolean", var);
+                       return config_error_nonbool(var);
                if (!strcmp(value, "diff3"))
                        git_xmerge_style = XDL_MERGE_DIFF3;
                else if (!strcmp(value, "zdiff3"))
@@ -325,8 +324,8 @@ int git_xmerge_config(const char *var, const char *value,
                 * git-completion.bash when you add new merge config
                 */
                else
-                       die("unknown style '%s' given for '%s'",
-                           value, var);
+                       return error(_("unknown style '%s' given for '%s'"),
+                                    value, var);
                return 0;
        }
        return git_default_config(var, value, ctx, cb);