aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitattributes1
-rw-r--r--.github/workflows/main.yml17
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--Documentation/BreakingChanges.adoc6
-rw-r--r--Documentation/CodingGuidelines3
-rw-r--r--Documentation/Makefile1
-rw-r--r--Documentation/RelNotes/2.50.0.adoc168
-rw-r--r--Documentation/asciidoc.conf.in8
-rw-r--r--Documentation/asciidoctor-extensions.rb.in12
-rw-r--r--Documentation/blame-options.adoc3
-rw-r--r--Documentation/config/branch.adoc105
-rw-r--r--Documentation/config/core.adoc5
-rw-r--r--Documentation/config/http.adoc18
-rw-r--r--Documentation/config/maintenance.adoc14
-rw-r--r--Documentation/config/promisor.adoc4
-rw-r--r--Documentation/config/remote.adoc3
-rw-r--r--Documentation/git-blame.adoc9
-rw-r--r--Documentation/git-branch.adoc287
-rw-r--r--Documentation/git-cat-file.adoc26
-rw-r--r--Documentation/git-maintenance.adoc22
-rw-r--r--Documentation/git-mv.adoc33
-rw-r--r--Documentation/git-pack-refs.adoc4
-rw-r--r--Documentation/git-reflog.adoc23
-rw-r--r--Documentation/git-repack.adoc21
-rw-r--r--Documentation/git-reset.adoc98
-rw-r--r--Documentation/git-restore.adoc3
-rw-r--r--Documentation/git-rm.adoc56
-rw-r--r--Documentation/git-update-ref.adoc14
-rw-r--r--Documentation/git-version.adoc8
-rw-r--r--Documentation/merge-strategies.adoc51
-rw-r--r--Documentation/mergetools/vimdiff.adoc2
-rw-r--r--Documentation/meson.build13
-rw-r--r--Documentation/rev-list-options.adoc24
-rw-r--r--Documentation/technical/multi-pack-index.adoc82
-rw-r--r--Documentation/technical/sparse-checkout.adoc2
-rw-r--r--Makefile17
-rw-r--r--advice.c1
-rw-r--r--advice.h1
-rw-r--r--apply.c2
-rw-r--r--archive-tar.c2
-rw-r--r--archive-zip.c2
-rw-r--r--archive.c6
-rw-r--r--attr.c2
-rw-r--r--bisect.c2
-rw-r--r--blame.c6
-rw-r--r--branch.c2
-rw-r--r--builtin/backfill.c2
-rw-r--r--builtin/blame.c17
-rw-r--r--builtin/bugreport.c4
-rw-r--r--builtin/cat-file.c258
-rw-r--r--builtin/checkout.c19
-rw-r--r--builtin/clone.c15
-rw-r--r--builtin/commit-graph.c2
-rw-r--r--builtin/commit-tree.c2
-rw-r--r--builtin/count-objects.c2
-rw-r--r--builtin/credential-cache--daemon.c4
-rw-r--r--builtin/describe.c4
-rw-r--r--builtin/diagnose.c4
-rw-r--r--builtin/diff.c5
-rw-r--r--builtin/difftool.c31
-rw-r--r--builtin/fast-export.c12
-rw-r--r--builtin/fast-import.c12
-rw-r--r--builtin/fetch.c53
-rw-r--r--builtin/fsck.c10
-rw-r--r--builtin/gc.c101
-rw-r--r--builtin/grep.c6
-rw-r--r--builtin/hash-object.c25
-rw-r--r--builtin/index-pack.c19
-rw-r--r--builtin/init-db.c3
-rw-r--r--builtin/log.c8
-rw-r--r--builtin/ls-files.c3
-rw-r--r--builtin/ls-tree.c2
-rw-r--r--builtin/merge-file.c1
-rw-r--r--builtin/merge-recursive.c4
-rw-r--r--builtin/merge-tree.c2
-rw-r--r--builtin/merge.c23
-rw-r--r--builtin/mktag.c2
-rw-r--r--builtin/mktree.c3
-rw-r--r--builtin/multi-pack-index.c2
-rw-r--r--builtin/mv.c6
-rw-r--r--builtin/name-rev.c14
-rw-r--r--builtin/notes.c3
-rw-r--r--builtin/pack-objects.c25
-rw-r--r--builtin/pack-redundant.c2
-rw-r--r--builtin/prune.c4
-rw-r--r--builtin/pull.c3
-rw-r--r--builtin/rebase.c12
-rw-r--r--builtin/receive-pack.c5
-rw-r--r--builtin/reflog.c221
-rw-r--r--builtin/remote.c4
-rw-r--r--builtin/repack.c64
-rw-r--r--builtin/replace.c4
-rw-r--r--builtin/rev-list.c98
-rw-r--r--builtin/revert.c2
-rw-r--r--builtin/rm.c21
-rw-r--r--builtin/show-ref.c2
-rw-r--r--builtin/sparse-checkout.c5
-rw-r--r--builtin/submodule--helper.c42
-rw-r--r--builtin/tag.c5
-rw-r--r--builtin/unpack-file.c3
-rw-r--r--builtin/unpack-objects.c9
-rw-r--r--builtin/update-index.c2
-rw-r--r--builtin/update-ref.c68
-rw-r--r--builtin/update-server-info.c4
-rw-r--r--builtin/worktree.c10
-rw-r--r--bulk-checkin.c28
-rw-r--r--bulk-checkin.h15
-rw-r--r--bundle-uri.c2
-rw-r--r--bundle.c10
-rw-r--r--cache-tree.c4
-rwxr-xr-xci/check-unsafe-assertions.sh18
-rwxr-xr-xci/install-dependencies.sh35
-rwxr-xr-xci/run-build-and-tests.sh1
-rwxr-xr-xci/run-static-analysis.sh2
-rwxr-xr-xci/test-documentation.sh27
-rw-r--r--combine-diff.c4
-rw-r--r--commit-graph.c14
-rw-r--r--commit-graph.h2
-rw-r--r--commit.c21
-rw-r--r--compat/mingw-posix.h435
-rw-r--r--compat/mingw.c64
-rw-r--r--compat/mingw.h426
-rw-r--r--compat/msvc-posix.h33
-rw-r--r--compat/msvc.h30
-rw-r--r--compat/open.c29
-rw-r--r--compat/posix.h541
-rw-r--r--compat/regex/regex_internal.c5
-rw-r--r--compat/regex/regexec.c2
-rw-r--r--config.c7
-rw-r--r--config.mak.dev4
-rw-r--r--connected.c2
-rw-r--r--contrib/coccinelle/meson.build7
-rw-r--r--contrib/completion/git-completion.bash49
-rw-r--r--contrib/credential/netrc/meson.build22
-rw-r--r--contrib/subtree/meson.build20
-rw-r--r--convert.c2
-rw-r--r--csum-file.c28
-rw-r--r--csum-file.h12
-rw-r--r--delta-islands.c14
-rw-r--r--delta-islands.h2
-rwxr-xr-xdetect-compiler2
-rw-r--r--diagnose.c2
-rw-r--r--diff-delta.c38
-rw-r--r--diff-lib.c10
-rw-r--r--diff-no-index.c28
-rw-r--r--diff.c16
-rw-r--r--diff.h2
-rw-r--r--diffcore-rename.c4
-rw-r--r--dir.c9
-rw-r--r--entry.c2
-rw-r--r--environment.c1
-rw-r--r--environment.h3
-rw-r--r--ewah/ewah_bitmap.c33
-rw-r--r--ewah/ewok.h12
-rw-r--r--fetch-pack.c2
-rw-r--r--fmt-merge-msg.c2
-rw-r--r--fsck.c2
-rwxr-xr-xgenerate-configlist.sh16
-rw-r--r--git-compat-util.h541
-rw-r--r--git-curl-compat.h7
-rwxr-xr-xgit-send-email.perl67
-rw-r--r--git-zlib.c27
-rw-r--r--gitweb/Makefile2
-rw-r--r--gitweb/meson.build2
-rw-r--r--grep.c4
-rw-r--r--hash.c277
-rw-r--r--hash.h15
-rw-r--r--help.c7
-rw-r--r--http-backend.c2
-rw-r--r--http-push.c3
-rw-r--r--http-walker.c2
-rw-r--r--http.c77
-rw-r--r--imap-send.c2
-rw-r--r--kwset.c54
-rw-r--r--list-objects-filter.c2
-rw-r--r--list-objects.c2
-rw-r--r--log-tree.c4
-rw-r--r--mailmap.c2
-rw-r--r--match-trees.c3
-rw-r--r--merge-blobs.c2
-rw-r--r--merge-ort-wrappers.h2
-rw-r--r--merge-ort.c195
-rw-r--r--merge-ort.h60
-rw-r--r--merge-recursive.c4079
-rw-r--r--merge-recursive.h132
-rw-r--r--mergetools/vimdiff14
-rw-r--r--meson.build79
-rw-r--r--midx-write.c71
-rw-r--r--midx.c4
-rw-r--r--notes-cache.c3
-rw-r--r--notes-merge.c10
-rw-r--r--notes.c5
-rw-r--r--object-file-convert.c29
-rw-r--r--object-file-convert.h3
-rw-r--r--object-file.c1560
-rw-r--r--object-file.h121
-rw-r--r--object-name.c2
-rw-r--r--object-store-ll.h556
-rw-r--r--object-store.c1050
-rw-r--r--object-store.h516
-rw-r--r--object.c121
-rw-r--r--object.h16
-rw-r--r--oss-fuzz/fuzz-pack-idx.c2
-rw-r--r--pack-bitmap-write.c103
-rw-r--r--pack-bitmap.c443
-rw-r--r--pack-bitmap.h27
-rw-r--r--pack-check.c14
-rw-r--r--pack-mtimes.c3
-rw-r--r--pack-objects.h2
-rw-r--r--pack-revindex.c72
-rw-r--r--pack-write.c55
-rw-r--r--pack.h11
-rw-r--r--packfile.c2
-rw-r--r--parallel-checkout.c2
-rw-r--r--parse-options-cb.c2
-rw-r--r--path.c111
-rw-r--r--path.h45
-rw-r--r--pathspec.c32
-rw-r--r--promisor-remote.c29
-rw-r--r--protocol-caps.c2
-rw-r--r--prune-packed.c2
-rw-r--r--pseudo-merge.h2
-rw-r--r--range-diff.c2
-rw-r--r--reachable.c11
-rw-r--r--read-cache.c14
-rw-r--r--ref-filter.c2
-rw-r--r--reflog.c139
-rw-r--r--reflog.h35
-rw-r--r--refs.c192
-rw-r--r--refs.h70
-rw-r--r--refs/debug.c2
-rw-r--r--refs/files-backend.c320
-rw-r--r--refs/packed-backend.c73
-rw-r--r--refs/refs-internal.h51
-rw-r--r--refs/reftable-backend.c502
-rw-r--r--refspec.c38
-rw-r--r--refspec.h18
-rw-r--r--reftable/basics.c19
-rw-r--r--reftable/basics.h123
-rw-r--r--reftable/block.c29
-rw-r--r--reftable/block.h2
-rw-r--r--reftable/blocksource.c17
-rw-r--r--reftable/iter.c13
-rw-r--r--reftable/merged.c27
-rw-r--r--reftable/pq.c40
-rw-r--r--reftable/pq.h2
-rw-r--r--reftable/reader.c33
-rw-r--r--reftable/record.c137
-rw-r--r--reftable/record.h6
-rw-r--r--reftable/stack.c52
-rw-r--r--reftable/system.c7
-rw-r--r--reftable/system.h9
-rw-r--r--reftable/writer.c61
-rw-r--r--remote-curl.c4
-rw-r--r--remote.c16
-rw-r--r--remote.h11
-rw-r--r--replace-object.c2
-rw-r--r--replace-object.h2
-rw-r--r--repo-settings.c20
-rw-r--r--repo-settings.h5
-rw-r--r--repository.c2
-rw-r--r--rerere.c7
-rw-r--r--reset.c2
-rw-r--r--revision.c15
-rw-r--r--revision.h2
-rw-r--r--scalar.c4
-rw-r--r--send-pack.c2
-rw-r--r--sequencer.c76
-rw-r--r--server-info.c4
-rw-r--r--shallow.c12
-rw-r--r--streaming.c5
-rw-r--r--submodule-config.c4
-rw-r--r--submodule.c32
-rw-r--r--t/Makefile16
-rw-r--r--t/README25
-rw-r--r--t/helper/test-pack-mtimes.c2
-rw-r--r--t/helper/test-partial-clone.c2
-rw-r--r--t/helper/test-path-utils.c32
-rw-r--r--t/helper/test-read-graph.c2
-rw-r--r--t/helper/test-read-midx.c2
-rw-r--r--t/helper/test-ref-store.c4
-rwxr-xr-xt/helper/test-sha1.sh4
-rw-r--r--t/helper/test-submodule-nested-repo-config.c2
-rw-r--r--t/lib-diff.sh4
-rw-r--r--t/lib-gpg.sh6
-rw-r--r--t/lib-httpd.sh2
-rw-r--r--t/lib-httpd/apache.conf6
-rw-r--r--t/lib-httpd/apply-one-time-perl.sh27
-rw-r--r--t/lib-httpd/apply-one-time-script.sh26
-rw-r--r--t/lib-merge.sh13
-rw-r--r--t/lib-t6000.sh13
-rw-r--r--t/meson.build4
-rwxr-xr-xt/perf/p7821-grep-engines-fixed.sh6
-rwxr-xr-xt/perf/p9210-scalar.sh3
-rwxr-xr-xt/t0001-init.sh8
-rwxr-xr-xt/t0008-ignores.sh4
-rwxr-xr-xt/t0021-conversion.sh13
-rwxr-xr-xt/t0090-cache-tree.sh4
-rwxr-xr-xt/t0210-trace2-normal.sh55
-rw-r--r--t/t0210/scrub_normal.perl54
-rwxr-xr-xt/t0211-trace2-perf.sh6
-rwxr-xr-xt/t0610-reftable-basics.sh5
-rwxr-xr-xt/t0613-reftable-write-options.sh2
-rwxr-xr-xt/t1006-cat-file.sh168
-rwxr-xr-xt/t1007-hash-object.sh6
-rwxr-xr-xt/t1010-mktree.sh4
-rwxr-xr-xt/t1050-large.sh3
-rwxr-xr-xt/t1092-sparse-checkout-compatibility.sh2
-rwxr-xr-xt/t1400-update-ref.sh233
-rwxr-xr-xt/t1403-show-ref.sh2
-rwxr-xr-xt/t1408-packed-refs.sh15
-rwxr-xr-xt/t1410-reflog.sh126
-rwxr-xr-xt/t1450-fsck.sh6
-rwxr-xr-xt/t1517-outside-repo.sh7
-rwxr-xr-xt/t2501-cwd-empty.sh2
-rwxr-xr-xt/t3300-funny-names.sh6
-rwxr-xr-xt/t3512-cherry-pick-submodule.sh5
-rwxr-xr-xt/t3513-revert-submodule.sh4
-rwxr-xr-xt/t4013-diff-various.sh6
-rwxr-xr-xt/t4014-format-patch.sh30
-rw-r--r--t/t4018/ini-section5
-rw-r--r--t/t4018/ini-section-noindent5
-rw-r--r--t/t4018/ini-section-same-line4
-rw-r--r--t/t4018/ini-subsection12
-rw-r--r--t/t4018/ini-subsection-noindent12
-rwxr-xr-xt/t4020-diff-external.sh2
-rwxr-xr-xt/t4029-diff-trailing-space.sh3
-rwxr-xr-xt/t4030-diff-textconv.sh9
-rwxr-xr-xt/t4031-diff-rewrite-binary.sh17
-rwxr-xr-xt/t4058-diff-duplicates.sh6
-rwxr-xr-xt/t4069-remerge-diff.sh7
-rwxr-xr-xt/t4103-apply-binary.sh6
-rwxr-xr-xt/t4116-apply-reverse.sh4
-rwxr-xr-xt/t4150-am.sh8
-rwxr-xr-xt/t4200-rerere.sh8
-rwxr-xr-xt/t4205-log-pretty-formats.sh6
-rwxr-xr-xt/t4216-log-bloom.sh8
-rwxr-xr-xt/t4301-merge-tree-write-tree.sh7
-rwxr-xr-xt/t5004-archive-corner-cases.sh6
-rwxr-xr-xt/t5300-pack-object.sh10
-rwxr-xr-xt/t5303-pack-corruption-resilience.sh6
-rwxr-xr-xt/t5310-pack-bitmaps.sh2
-rwxr-xr-xt/t5316-pack-delta-depth.sh10
-rwxr-xr-xt/t5318-commit-graph.sh12
-rwxr-xr-xt/t5319-multi-pack-index.sh16
-rwxr-xr-xt/t5323-pack-redundant.sh2
-rwxr-xr-xt/t5324-split-commit-graph.sh2
-rwxr-xr-xt/t5326-multi-pack-bitmaps.sh4
-rwxr-xr-xt/t5328-commit-graph-64bit-time.sh2
-rwxr-xr-xt/t5329-pack-objects-cruft.sh302
-rwxr-xr-xt/t5333-pseudo-merge-bitmaps.sh12
-rwxr-xr-xt/t5334-incremental-multi-pack-index.sh87
-rwxr-xr-xt/t5400-send-pack.sh2
-rwxr-xr-xt/t5410-receive-pack-alternates.sh2
-rwxr-xr-xt/t5503-tagfollow.sh6
-rwxr-xr-xt/t5504-fetch-receive-strict.sh2
-rwxr-xr-xt/t5505-remote.sh8
-rwxr-xr-xt/t5510-fetch.sh38
-rwxr-xr-xt/t5515-fetch-merge-logic.sh2
-rwxr-xr-xt/t5516-fetch-push.sh8
-rwxr-xr-xt/t5532-fetch-proxy.sh6
-rwxr-xr-xt/t5534-push-signed.sh2
-rwxr-xr-xt/t5537-fetch-shallow.sh15
-rwxr-xr-xt/t5551-http-fetch-smart.sh7
-rwxr-xr-xt/t5562-http-backend-content-length.sh6
-rwxr-xr-xt/t5572-pull-submodule.sh5
-rwxr-xr-xt/t5601-clone.sh4
-rwxr-xr-xt/t5605-clone-local.sh5
-rwxr-xr-xt/t5607-clone-bundle.sh12
-rwxr-xr-xt/t5616-partial-clone.sh46
-rwxr-xr-xt/t5701-git-serve.sh5
-rwxr-xr-xt/t5702-protocol-v2.sh21
-rwxr-xr-xt/t5703-upload-pack-ref-in-want.sh29
-rwxr-xr-xt/t5710-promisor-remote-capability.sh81
-rwxr-xr-xt/t6000-rev-list-misc.sh66
-rwxr-xr-xt/t6011-rev-list-with-bad-commit.sh14
-rwxr-xr-xt/t6013-rev-list-reverse-parents.sh10
-rwxr-xr-xt/t6020-bundle-misc.sh53
-rwxr-xr-xt/t6022-rev-list-missing.sh31
-rwxr-xr-xt/t6102-rev-list-unexpected-objects.sh6
-rwxr-xr-xt/t6115-rev-list-du.sh2
-rwxr-xr-xt/t6120-describe.sh18
-rwxr-xr-xt/t6300-for-each-ref.sh15
-rwxr-xr-xt/t6400-merge-df.sh14
-rwxr-xr-xt/t6402-merge-rename.sh125
-rwxr-xr-xt/t6404-recursive-merge.sh21
-rwxr-xr-xt/t6406-merge-attr.sh7
-rwxr-xr-xt/t6416-recursive-corner-cases.sh194
-rwxr-xr-xt/t6421-merge-partial-clone.sh7
-rwxr-xr-xt/t6422-merge-rename-corner-cases.sh31
-rwxr-xr-xt/t6423-merge-rename-directories.sh517
-rwxr-xr-xt/t6424-merge-unrelated-index-changes.sh8
-rwxr-xr-xt/t6426-merge-skip-unneeded-updates.sh4
-rwxr-xr-xt/t6428-merge-conflicts-sparse.sh2
-rwxr-xr-xt/t6430-merge-recursive.sh46
-rwxr-xr-xt/t6434-merge-recursive-rename-options.sh16
-rwxr-xr-xt/t6436-merge-overwrite.sh17
-rwxr-xr-xt/t6437-submodule-merge.sh65
-rwxr-xr-xt/t6438-submodule-directory-file-conflicts.sh5
-rwxr-xr-xt/t6439-merge-co-error-msgs.sh2
-rwxr-xr-xt/t7006-pager.sh6
-rwxr-xr-xt/t7402-submodule-rebase.sh7
-rwxr-xr-xt/t7416-submodule-dash-url.sh3
-rwxr-xr-xt/t7501-commit-basic-functionality.sh6
-rwxr-xr-xt/t7508-status.sh2
-rwxr-xr-xt/t7602-merge-octopus-many.sh9
-rwxr-xr-xt/t7610-mergetool.sh40
-rwxr-xr-xt/t7615-diff-algo-with-mergy-operations.sh2
-rwxr-xr-xt/t7704-repack-cruft.sh293
-rwxr-xr-xt/t7815-grep-binary.sh9
-rwxr-xr-xt/t7900-maintenance.sh46
-rwxr-xr-xt/t8001-annotate.sh6
-rwxr-xr-xt/t8002-blame.sh8
-rwxr-xr-xt/t8006-blame-textconv.sh2
-rwxr-xr-xt/t8011-blame-split-file.sh6
-rwxr-xr-xt/t8012-blame-colors.sh6
-rwxr-xr-xt/t8013-blame-ignore-revs.sh38
-rwxr-xr-xt/t9137-git-svn-dcommit-clobber-series.sh10
-rwxr-xr-xt/t9350-fast-export.sh2
-rwxr-xr-xt/t9811-git-p4-label-import.sh8
-rwxr-xr-xt/t9850-shell.sh2
-rwxr-xr-xt/t9902-completion.sh206
-rw-r--r--t/test-lib-functions.sh25
-rw-r--r--t/test-lib.sh58
-rw-r--r--t/unit-tests/clar/clar/fs.h10
-rw-r--r--t/unit-tests/t-reftable-basics.c28
-rw-r--r--t/unit-tests/t-reftable-pq.c22
-rw-r--r--t/unit-tests/t-reftable-record.c42
-rw-r--r--t/unit-tests/t-trailer.c317
-rw-r--r--t/unit-tests/u-trailer.c320
-rw-r--r--t/unit-tests/u-urlmatch-normalization.c (renamed from t/unit-tests/t-urlmatch-normalization.c)60
-rw-r--r--tag.c2
-rw-r--r--templates/meson.build4
-rw-r--r--tmp-objdir.c2
-rw-r--r--transport-helper.c2
-rw-r--r--tree-diff.c4
-rw-r--r--tree-walk.c2
-rw-r--r--tree.c2
-rw-r--r--unpack-trees.c2
-rw-r--r--upload-pack.c16
-rw-r--r--userdiff.c4
-rw-r--r--walker.c2
-rw-r--r--wildmatch.c7
-rw-r--r--wrapper.c48
-rw-r--r--wt-status.c4
-rw-r--r--xdiff-interface.c4
-rw-r--r--xdiff/xdiffi.c12
447 files changed, 10559 insertions, 11900 deletions
diff --git a/.gitattributes b/.gitattributes
index c6a0b35116..32583149c2 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -13,6 +13,7 @@ CODE_OF_CONDUCT.md -whitespace
/mergetools/* text eol=lf
/t/oid-info/* text eol=lf
/Documentation/git-merge.adoc conflict-marker-size=32
+/Documentation/git-merge-file.adoc conflict-marker-size=32
/Documentation/gitk.adoc conflict-marker-size=32
/Documentation/user-manual.adoc conflict-marker-size=32
/t/t????-*.sh conflict-marker-size=32
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 9959b61ece..83ca8e4182 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -265,7 +265,7 @@ jobs:
run: pip install meson ninja
- name: Setup
shell: pwsh
- run: meson setup build -Dperl=disabled -Dcredential_helpers=wincred
+ run: meson setup build --vsenv -Dperl=disabled -Dcredential_helpers=wincred
- name: Compile
shell: pwsh
run: meson compile -C build
@@ -349,6 +349,7 @@ jobs:
if: needs.ci-config.outputs.enabled == 'yes'
env:
CC: clang
+ CI_JOB_IMAGE: ubuntu-latest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -432,6 +433,7 @@ jobs:
if: needs.ci-config.outputs.enabled == 'yes'
env:
jobname: StaticAnalysis
+ CI_JOB_IMAGE: ubuntu-22.04
runs-on: ubuntu-22.04
concurrency:
group: static-analysis-${{ github.ref }}
@@ -446,20 +448,12 @@ jobs:
if: needs.ci-config.outputs.enabled == 'yes'
env:
jobname: sparse
- runs-on: ubuntu-20.04
+ CI_JOB_IMAGE: ubuntu-22.04
+ runs-on: ubuntu-22.04
concurrency:
group: sparse-${{ github.ref }}
cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
steps:
- - name: Download a current `sparse` package
- # Ubuntu's `sparse` version is too old for us
- uses: git-for-windows/get-azure-pipelines-artifact@v0
- with:
- repository: git/git
- definitionId: 10
- artifact: sparse-20.04
- - name: Install the current `sparse` package
- run: sudo dpkg -i sparse-20.04/sparse_*.deb
- uses: actions/checkout@v4
- name: Install other dependencies
run: ci/install-dependencies.sh
@@ -473,6 +467,7 @@ jobs:
cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
env:
jobname: Documentation
+ CI_JOB_IMAGE: ubuntu-latest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2805cdeecb..4798b28374 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -164,7 +164,7 @@ build:msvc-meson:
extends: .msvc-meson
stage: build
script:
- - meson setup build -Dperl=disabled -Dbackend_max_links=1 -Dcredential_helpers=wincred
+ - meson setup build --vsenv -Dperl=disabled -Dbackend_max_links=1 -Dcredential_helpers=wincred
- meson compile -C build
artifacts:
paths:
diff --git a/Documentation/BreakingChanges.adoc b/Documentation/BreakingChanges.adoc
index bdfad29d8a..61bdd586b9 100644
--- a/Documentation/BreakingChanges.adoc
+++ b/Documentation/BreakingChanges.adoc
@@ -178,6 +178,12 @@ references.
+
These features will be removed.
+* Support for "--stdin" option in the "name-rev" command was
+ deprecated (and hidden from the documentation) in the Git 2.40
+ timeframe, in preference to its synonym "--annotate-stdin". Git 3.0
+ removes the support for "--stdin" altogether.
+
+
== Superseded features that will not be deprecated
Some features have gained newer replacements that aim to improve the design in
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index a0e7041c54..c1046abfb7 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -861,6 +861,9 @@ Markup:
_<git-dir>_
_<key-id>_
+Characters are also surrounded by underscores:
+ _LF_, _CR_, _CR_/_LF_, _NUL_, _EOF_
+
Git's Asciidoc processor has been tailored to treat backticked text
as complex synopsis. When literal and placeholders are mixed, you can
use the backtick notation which will take care of correctly typesetting
diff --git a/Documentation/Makefile b/Documentation/Makefile
index e6b20c021f..0d3a2c6bfe 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -109,6 +109,7 @@ SP_ARTICLES += howto/coordinate-embargoed-releases
API_DOCS = $(patsubst %.adoc,%,$(filter-out technical/api-index-skel.adoc technical/api-index.adoc, $(wildcard technical/api-*.adoc)))
SP_ARTICLES += $(API_DOCS)
+TECH_DOCS += BreakingChanges
TECH_DOCS += DecisionMaking
TECH_DOCS += ReviewingGuidelines
TECH_DOCS += MyFirstContribution
diff --git a/Documentation/RelNotes/2.50.0.adoc b/Documentation/RelNotes/2.50.0.adoc
index 1905c61e9e..e4b731e61d 100644
--- a/Documentation/RelNotes/2.50.0.adoc
+++ b/Documentation/RelNotes/2.50.0.adoc
@@ -7,6 +7,50 @@ UI, Workflows & Features
* A post-processing filter for "diff --raw" output has been
introduced.
+ * "git repack" learned "--combine-cruft-below-size" option that
+ controls how cruft-packs are combined.
+
+ * TCP keepalive behaviour on http transports can now be configured by
+ calling cURL library.
+
+ * Incrementally updating multi-pack index files.
+
+ * "git reflog" learns "drop" subcommand, that discards the entire
+ reflog data for a ref.
+
+ * A new userdiff driver for ".ini" format configuration files has
+ been added.
+
+ * The job to coalesce loose objects into packfiles in "git
+ maintenance" now has configurable batch size.
+
+ * "git clone" still gave the message about the default branch name;
+ this message has been turned into an advice message that can be
+ turned off.
+
+ * "git rev-list" learns machine-parsable output format that delimits
+ each field with NUL.
+
+ * "git maintenance" learns a new task to expire reflog entries.
+
+ * Auth-related (and unrelated) error handling in send-email has been
+ made more robust.
+
+ * Updating multiple references have only been possible in all-or-none
+ fashion with transactions, but it can be more efficient to batch
+ multiple updates even when some of them are allowed to fail in a
+ best-effort manner. A new "best effort batches of updates" mode
+ has been introduced.
+
+ * "git help --build-options" reports SHA-1 and SHA-256 backends used
+ in the build.
+
+ * "git cat-file --batch" and friends learned to allow "--filter=" to
+ omit certain objects, just like the transport layer does.
+
+ * "git blame --porcelain" mode now talks about unblamable lines and
+ lines that are blamed to an ignored commit.
+
Performance, Internal Implementation, Development Support etc.
--------------------------------------------------------------
@@ -34,8 +78,39 @@ Performance, Internal Implementation, Development Support etc.
* Enable -Wunreachable-code for developer builds.
- * Build update.
- (merge 7c8cd9c158 es/meson-building-docs-requires-perl later to maint).
+ * Ensure what we write in assert() does not have side effects,
+ and introduce ASSERT() macro to mark those that cannot be
+ mechanically checked for lack of side effects.
+
+ * Give more meaningful error return values from block writer layer of
+ the reftable ref-API backend.
+
+ * Make the code in reftable library less reliant on the service
+ routines it used to borrow from Git proper, to make it easier to
+ use by external users of the library.
+
+ * CI update.
+
+ * The object layer has been updated to take an explicit repository
+ instance as a parameter in more code paths.
+
+ * Some warnings from "-Wsign-compare" for builtin/rm.c have been
+ squelched.
+
+ * A few traditional unit tests have been rewritten to use the clar
+ framework.
+
+ * Some warnings from "-Wsign-compare" for pathspec.c have been
+ squelched.
+
+ * "make test" used to have a hard dependency on (basic) Perl; tests
+ have been rewritten help environment with NO_PERL test the build as
+ much as possible.
+
+ * Remove remnants of the recursive merge strategy backend, which was
+ superseded by the ort merge strategy.
+
+ * Optimize the code to dedup references recorded in a bundle file.
Fixes since v2.49
@@ -59,6 +134,82 @@ Fixes since v2.49
context size is given, which has been corrected.
(merge d39e28e68c rs/xdiff-context-length-fix later to maint).
+ * GitHub Actions CI switched on a CI/CD variable that does not exist
+ when choosing what packages to install etc., which has been
+ corrected.
+ (merge ee89f7c79d kn/ci-meson-check-build-docs-fix later to maint).
+
+ * Using "git name-rev --stdin" as an example, improve the framework to
+ prepare tests to pretend to be in the future where the breaking
+ changes have already happened.
+ (merge de3dec1187 jc/name-rev-stdin later to maint).
+
+ * An earlier code refactoring of the hash machinery missed a few
+ required calls to init_fn.
+ (merge d39f04b638 jh/hash-init-fixes later to maint).
+
+ * A documentation page was left out from formatting and installation,
+ which has been corrected.
+ (merge ae85116f18 pw/build-breaking-changes-doc later to maint).
+
+ * The bash command line completion script (in contrib/) has been
+ updated to cope with remote repository nicknames with slashes in
+ them.
+ (merge 778d2f1760 dm/completion-remote-names-fix later to maint).
+
+ * "Dubious ownership" checks on Windows has been tightened up.
+ (merge 5bb88e89ef js/mingw-admins-are-special later to maint).
+
+ * Layout configuration in vimdiff backend didn't work as advertised,
+ which has been corrected.
+ (merge 93bab2d04b fr/vimdiff-layout-fixes later to maint).
+
+ * Fix our use of zlib corner cases.
+ (merge 1cb2f293f5 jk/zlib-inflate-fixes later to maint).
+
+ * Fix lockfile contention in reftable code on Windows.
+ (merge 0a3dceabf1 ps/mingw-creat-excl-fix later to maint).
+
+ * "git-merge-file" documentation source, which has lines that look
+ like conflict markers, lacked custom conflict marker size defined,
+ which has been corrected..
+ (merge d3b5832381 pw/custom-conflict-marker-size-for-merge-related-docs later to maint).
+
+ * Squelch false-positive from sparse.
+ (merge da87b58014 dd/sparse-glibc-workaround later to maint).
+
+ * Adjust to the deprecation of use of Ubuntu 20.04 GitHub Actions CI.
+ (merge 832d9f6d0b js/ci-github-update-ubuntu later to maint).
+
+ * Work around CI breakage due to fedora base image getting updated.
+ (merge 8a471a663b js/ci-fedora-gawk later to maint).
+
+ * A ref transaction corner case fix.
+ (merge b9fadeead7 jt/ref-transaction-abort-fix later to maint).
+
+ * Random build fixes.
+ (merge 85e1d6819f ps/misc-build-fixes later to maint).
+
+ * "git fetch [<remote>]" with only the configured fetch refspec
+ should be the only thing to update refs/remotes/<remote>/HEAD,
+ but the code was overly eager to do so in other cases.
+
+ * Incorrect sorting of refs with bytes with high-bit set on platforms
+ with signed char led to a BUG, which has been corrected.
+
+ * "make perf" fixes.
+ (merge 1665f12fa0 pb/perf-test-fixes later to maint).
+
+ * Doc mark-up updates.
+ (merge 5a5565ec44 ja/doc-reset-mv-rm-markup-updates later to maint).
+
+ * Work around false positive from CodeQL checker.
+ (merge 0f558141ed js/range-check-codeql-workaround later to maint).
+
+ * "git log --{left,right}-only A...B", when A and B does not share
+ any common ancestor, now behaves as expected.
+ (merge e7ef4be7c2 mh/left-right-limited later to maint).
+
* Other code cleanup, docfix, build fix, etc.
(merge 227c4f33a0 ja/doc-block-delimiter-markup-fix later to maint).
(merge 2bfd3b3685 ab/decorate-code-cleanup later to maint).
@@ -66,3 +217,16 @@ Fixes since v2.49
(merge 554051d691 en/diff-rename-follow-fix later to maint).
(merge a18c18b470 en/random-cleanups later to maint).
(merge 5af21c9acb hj/doc-rev-list-ancestry-fix later to maint).
+ (merge 26d76ca284 aj/doc-restore-p-update later to maint).
+ (merge 2c0dcb9754 cc/lop-remote later to maint).
+ (merge 7b399322a2 ja/doc-branch-markup later to maint).
+ (merge ee434e1807 pw/doc-pack-refs-markup-fix later to maint).
+ (merge c000918eb7 tb/bitamp-typofix later to maint).
+ (merge fa8cd29676 js/imap-send-peer-cert-verify later to maint).
+ (merge 98b423bc1c rs/clear-commit-marks-simplify later to maint).
+ (merge 133d065dd6 ta/bulk-checkin-signed-compare-false-warning-fix later to maint).
+ (merge d2827dc31e es/meson-build-skip-coccinelle later to maint).
+ (merge ee8edb7156 dk/vimdiff-doc-fix later to maint).
+ (merge 107d889303 md/t1403-path-is-file later to maint).
+ (merge abd4192b07 js/comma-semicolon-confusion later to maint).
+ (merge 27b7264206 ab/environment-clean-header later to maint).
diff --git a/Documentation/asciidoc.conf.in b/Documentation/asciidoc.conf.in
index f2aef6cb79..9d9139306e 100644
--- a/Documentation/asciidoc.conf.in
+++ b/Documentation/asciidoc.conf.in
@@ -43,7 +43,7 @@ ifdef::doctype-book[]
endif::doctype-book[]
[literal-inlinemacro]
-{eval:re.sub(r'(&lt;[-a-zA-Z0-9.]+&gt;)', r'<emphasis>\1</emphasis>', re.sub(r'([\[\s|()>]|^|\]|&gt;)(\.?([-a-zA-Z0-9:+=~@,\/_^\$]+\.?)+)',r'\1<literal>\2</literal>', re.sub(r'(\.\.\.?)([^\]$.])', r'<literal>\1</literal>\2', macros.passthroughs[int(attrs['passtext'][1:-1])] if attrs['passtext'][1:-1].isnumeric() else attrs['passtext'][1:-1])))}
+{eval:re.sub(r'(&lt;[-a-zA-Z0-9.]+&gt;)', r'<emphasis>\1</emphasis>', re.sub(r'([\[\s|()>]|^|\]|&gt;)(\.?([-a-zA-Z0-9:+=~@\\\*\/_^\$]+\.?)+|,)',r'\1<literal>\2</literal>', re.sub(r'(\.\.\.?)([^\]$.])', r'<literal>\1</literal>\2', macros.passthroughs[int(attrs['passtext'][1:-1])] if attrs['passtext'][1:-1].isnumeric() else attrs['passtext'][1:-1])))}
endif::backend-docbook[]
@@ -75,18 +75,18 @@ git-relative-html-prefix=
<a href="{git-relative-html-prefix}{target}.html">{target}{0?({0})}</a>
[literal-inlinemacro]
-{eval:re.sub(r'(&lt;[-a-zA-Z0-9.]+&gt;)', r'<em>\1</em>', re.sub(r'([\[\s|()>]|^|\]|&gt;)(\.?([-a-zA-Z0-9:+=~@,\/_^\$]+\.?)+)',r'\1<code>\2</code>', re.sub(r'(\.\.\.?)([^\]$.])', r'<code>\1</code>\2', macros.passthroughs[int(attrs['passtext'][1:-1])] if attrs['passtext'][1:-1].isnumeric() else attrs['passtext'][1:-1])))}
+{eval:re.sub(r'(&lt;[-a-zA-Z0-9.]+&gt;)', r'<em>\1</em>', re.sub(r'([\[\s|()>]|^|\]|&gt;)(\.?([-a-zA-Z0-9:+=~@,\\\*\/_^\$]+\.?)+)',r'\1<code>\2</code>', re.sub(r'(\.\.\.?)([^\]$.])', r'<code>\1</code>\2', macros.passthroughs[int(attrs['passtext'][1:-1])] if attrs['passtext'][1:-1].isnumeric() else attrs['passtext'][1:-1])))}
endif::backend-xhtml11[]
ifdef::backend-docbook[]
ifdef::doctype-manpage[]
[paradef-default]
-synopsis-style=template="verseparagraph",filter="sed 's!&#8230;\\(\\]\\|$\\)!<phrase>\\0</phrase>!g;s!\\([\\[ |()]\\|^\\|\\]\\|&gt;\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.]\\+\\|&#8230;\\)!\\1<literal>\\2</literal>!g;s!&lt;[-a-zA-Z0-9.]\\+&gt;!<emphasis>\\0</emphasis>!g'"
+synopsis-style=template="verseparagraph",filter="sed 's!&#8230;\\(\\]\\|$\\)!<phrase>\\0</phrase>!g;s!\\([\\[ |()]\\|^\\|\\]\\|&gt;\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|&#8230;\\)!\\1<literal>\\2</literal>!g;s!&lt;[-a-zA-Z0-9.]\\+&gt;!<emphasis>\\0</emphasis>!g'"
endif::doctype-manpage[]
endif::backend-docbook[]
ifdef::backend-xhtml11[]
[paradef-default]
-synopsis-style=template="verseparagraph",filter="sed 's!&#8230;\\(\\]\\|$\\)!<span>\\0</span>!g;s!\\([\\[ |()]\\|^\\|\\]\\|&gt;\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.]\\+\\|&#8230;\\)!\\1<code>\\2</code>!g;s!&lt;[-a-zA-Z0-9.]\\+&gt;!<em>\\0</em>!g'"
+synopsis-style=template="verseparagraph",filter="sed 's!&#8230;\\(\\]\\|$\\)!<span>\\0</span>!g;s!\\([\\[ |()]\\|^\\|\\]\\|&gt;\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|&#8230;\\)!\\1<code>\\2</code>!g;s!&lt;[-a-zA-Z0-9.]\\+&gt;!<em>\\0</em>!g'"
endif::backend-xhtml11[]
diff --git a/Documentation/asciidoctor-extensions.rb.in b/Documentation/asciidoctor-extensions.rb.in
index 2494f17a51..8b7b161349 100644
--- a/Documentation/asciidoctor-extensions.rb.in
+++ b/Documentation/asciidoctor-extensions.rb.in
@@ -49,8 +49,8 @@ module Git
def process parent, reader, attrs
outlines = reader.lines.map do |l|
- l.gsub(/(\.\.\.?)([^\]$.])/, '`\1`\2')
- .gsub(%r{([\[\] |()>]|^)([-a-zA-Z0-9:+=~@,/_^\$]+)}, '\1{empty}`\2`{empty}')
+ l.gsub(/(\.\.\.?)([^\]$\. ])/, '{empty}`\1`{empty}\2')
+ .gsub(%r{([\[\] |()>]|^)([-a-zA-Z0-9:+=~@,/_^\$\\\*]+)}, '\1{empty}`\2`{empty}')
.gsub(/(<[-a-zA-Z0-9.]+>)/, '__\\1__')
.gsub(']', ']{empty}')
end
@@ -71,8 +71,9 @@ module Git
# unhandled math; pass source to alt and required mathphrase element; dblatex will process alt as LaTeX math
%(<inlineequation><alt><![CDATA[#{equation = node.text}]]></alt><mathphrase><![CDATA[#{equation}]]></mathphrase></inlineequation>)
elsif type == :monospaced
- node.text.gsub(/(\.\.\.?)([^\]$.])/, '<literal>\1</literal>\2')
- .gsub(%r{([\[\s|()>.]|^|\]|&gt;)(\.?([-a-zA-Z0-9:+=~@,/_^\$]+\.{0,2})+)}, '\1<literal>\2</literal>')
+ node.text.gsub(/(\.\.\.?)([^\]$\.])/, '<literal>\1</literal>\2')
+ .gsub(/^\.\.\.?$/, '<literal>\0</literal>')
+ .gsub(%r{([\[\s|()>.]|^|\]|&gt;)(\.?([-a-zA-Z0-9:+=~@/_^\$\\\*]+\.{0,2})+|,)}, '\1<literal>\2</literal>')
.gsub(/(&lt;[-a-zA-Z0-9.]+&gt;)/, '<emphasis>\1</emphasis>')
else
open, close, supports_phrase = QUOTE_TAGS[type]
@@ -100,7 +101,8 @@ module Git
def convert_inline_quoted node
if node.type == :monospaced
node.text.gsub(/(\.\.\.?)([^\]$.])/, '<code>\1</code>\2')
- .gsub(%r{([\[\s|()>.]|^|\]|&gt;)(\.?([-a-zA-Z0-9:+=~@,/_^\$]+\.{0,2})+)}, '\1<code>\2</code>')
+ .gsub(/^\.\.\.?$/, '<code>\0</code>')
+ .gsub(%r{([\[\s|()>.]|^|\]|&gt;)(\.?([-a-zA-Z0-9:+=~@,/_^\$\\\*]+\.{0,2})+)}, '\1<code>\2</code>')
.gsub(/(&lt;[-a-zA-Z0-9.]+&gt;)/, '<em>\1</em>')
else
diff --git a/Documentation/blame-options.adoc b/Documentation/blame-options.adoc
index aa77406d4e..19ea187238 100644
--- a/Documentation/blame-options.adoc
+++ b/Documentation/blame-options.adoc
@@ -125,7 +125,8 @@ take effect.
another commit will be marked with a `?` in the blame output. If the
`blame.markUnblamableLines` config option is set, then those lines touched
by an ignored commit that we could not attribute to another revision are
- marked with a '*'.
+ marked with a '*'. In the porcelain modes, we print 'ignored' and
+ 'unblamable' on a newline respectively.
--ignore-revs-file <file>::
Ignore revisions listed in `file`, which must be in the same format as an
diff --git a/Documentation/config/branch.adoc b/Documentation/config/branch.adoc
index 432b9cd2c0..e35ea7ac64 100644
--- a/Documentation/config/branch.adoc
+++ b/Documentation/config/branch.adoc
@@ -1,41 +1,42 @@
-branch.autoSetupMerge::
- Tells 'git branch', 'git switch' and 'git checkout' to set up new branches
+`branch.autoSetupMerge`::
+ Tells `git branch`, `git switch` and `git checkout` to set up new branches
so that linkgit:git-pull[1] will appropriately merge from the
starting point branch. Note that even if this option is not set,
this behavior can be chosen per-branch using the `--track`
- and `--no-track` options. The valid settings are: `false` -- no
- automatic setup is done; `true` -- automatic setup is done when the
- starting point is a remote-tracking branch; `always` --
- automatic setup is done when the starting point is either a
- local branch or remote-tracking branch; `inherit` -- if the starting point
- has a tracking configuration, it is copied to the new
- branch; `simple` -- automatic setup is done only when the starting point
+ and `--no-track` options. This option defaults to `true`. The valid settings
+ are:
+`false`;; no automatic setup is done
+`true`;; automatic setup is done when the starting point is a remote-tracking branch
+`always`;; automatic setup is done when the starting point is either a
+ local branch or remote-tracking branch
+`inherit`;; if the starting point has a tracking configuration, it is copied to the new
+ branch
+`simple`;; automatic setup is done only when the starting point
is a remote-tracking branch and the new branch has the same name as the
- remote branch. This option defaults to true.
+ remote branch.
-branch.autoSetupRebase::
- When a new branch is created with 'git branch', 'git switch' or 'git checkout'
+`branch.autoSetupRebase`::
+ When a new branch is created with `git branch`, `git switch` or `git checkout`
that tracks another branch, this variable tells Git to set
- up pull to rebase instead of merge (see "branch.<name>.rebase").
- When `never`, rebase is never automatically set to true.
- When `local`, rebase is set to true for tracked branches of
- other local branches.
- When `remote`, rebase is set to true for tracked branches of
- remote-tracking branches.
- When `always`, rebase will be set to true for all tracking
- branches.
- See "branch.autoSetupMerge" for details on how to set up a
- branch to track another branch.
- This option defaults to never.
+ up pull to rebase instead of merge (see `branch.<name>.rebase`).
+ The valid settings are:
+`never`;; rebase is never automatically set to true.
+`local`;; rebase is set to true for tracked branches of other local branches.
+`remote`;; rebase is set to true for tracked branches of remote-tracking branches.
+`always`;; rebase will be set to true for all tracking branches.
-branch.sort::
++
+See `branch.autoSetupMerge` for details on how to set up a branch to track another branch.
+This option defaults to `never`.
+
+`branch.sort`::
This variable controls the sort ordering of branches when displayed by
- linkgit:git-branch[1]. Without the "--sort=<value>" option provided, the
+ linkgit:git-branch[1]. Without the `--sort=<value>` option provided, the
value of this variable will be used as the default.
See linkgit:git-for-each-ref[1] field names for valid values.
-branch.<name>.remote::
- When on branch <name>, it tells 'git fetch' and 'git push'
+`branch.<name>.remote`::
+ When on branch _<name>_, it tells `git fetch` and `git push`
which remote to fetch from or push to. The remote to push to
may be overridden with `remote.pushDefault` (for all branches).
The remote to push to, for the current branch, may be further
@@ -46,58 +47,58 @@ branch.<name>.remote::
Additionally, `.` (a period) is the current local repository
(a dot-repository), see `branch.<name>.merge`'s final note below.
-branch.<name>.pushRemote::
- When on branch <name>, it overrides `branch.<name>.remote` for
+`branch.<name>.pushRemote`::
+ When on branch _<name>_, it overrides `branch.<name>.remote` for
pushing. It also overrides `remote.pushDefault` for pushing
- from branch <name>. When you pull from one place (e.g. your
+ from branch _<name>_. When you pull from one place (e.g. your
upstream) and push to another place (e.g. your own publishing
repository), you would want to set `remote.pushDefault` to
specify the remote to push to for all branches, and use this
option to override it for a specific branch.
-branch.<name>.merge::
- Defines, together with branch.<name>.remote, the upstream branch
- for the given branch. It tells 'git fetch'/'git pull'/'git rebase' which
- branch to merge and can also affect 'git push' (see push.default).
- When in branch <name>, it tells 'git fetch' the default
- refspec to be marked for merging in FETCH_HEAD. The value is
+`branch.<name>.merge`::
+ Defines, together with `branch.<name>.remote`, the upstream branch
+ for the given branch. It tells `git fetch`/`git pull`/`git rebase` which
+ branch to merge and can also affect `git push` (see `push.default`).
+ When in branch _<name>_, it tells `git fetch` the default
+ refspec to be marked for merging in `FETCH_HEAD`. The value is
handled like the remote part of a refspec, and must match a
ref which is fetched from the remote given by
- "branch.<name>.remote".
- The merge information is used by 'git pull' (which first calls
- 'git fetch') to lookup the default branch for merging. Without
- this option, 'git pull' defaults to merge the first refspec fetched.
+ `branch.<name>.remote`.
+ The merge information is used by `git pull` (which first calls
+ `git fetch`) to lookup the default branch for merging. Without
+ this option, `git pull` defaults to merge the first refspec fetched.
Specify multiple values to get an octopus merge.
- If you wish to setup 'git pull' so that it merges into <name> from
+ If you wish to setup `git pull` so that it merges into <name> from
another branch in the local repository, you can point
branch.<name>.merge to the desired branch, and use the relative path
- setting `.` (a period) for branch.<name>.remote.
+ setting `.` (a period) for `branch.<name>.remote`.
-branch.<name>.mergeOptions::
- Sets default options for merging into branch <name>. The syntax and
+`branch.<name>.mergeOptions`::
+ Sets default options for merging into branch _<name>_. The syntax and
supported options are the same as those of linkgit:git-merge[1], but
option values containing whitespace characters are currently not
supported.
-branch.<name>.rebase::
- When true, rebase the branch <name> on top of the fetched branch,
+`branch.<name>.rebase`::
+ When true, rebase the branch _<name>_ on top of the fetched branch,
instead of merging the default branch from the default remote when
- "git pull" is run. See "pull.rebase" for doing this in a non
+ `git pull` is run. See `pull.rebase` for doing this in a non
branch-specific manner.
+
-When `merges` (or just 'm'), pass the `--rebase-merges` option to 'git rebase'
+When `merges` (or just `m`), pass the `--rebase-merges` option to `git rebase`
so that the local merge commits are included in the rebase (see
linkgit:git-rebase[1] for details).
+
-When the value is `interactive` (or just 'i'), the rebase is run in interactive
+When the value is `interactive` (or just `i`), the rebase is run in interactive
mode.
+
*NOTE*: this is a possibly dangerous operation; do *not* use
it unless you understand the implications (see linkgit:git-rebase[1]
for details).
-branch.<name>.description::
+`branch.<name>.description`::
Branch description, can be edited with
`git branch --edit-description`. Branch description is
- automatically added to the format-patch cover letter or
- request-pull summary.
+ automatically added to the `format-patch` cover letter or
+ `request-pull` summary.
diff --git a/Documentation/config/core.adoc b/Documentation/config/core.adoc
index 8f6d8e7754..9fde1ab63a 100644
--- a/Documentation/config/core.adoc
+++ b/Documentation/config/core.adoc
@@ -512,6 +512,11 @@ centrally configure your Git hooks instead of configuring them on a
per-repository basis, or as a more flexible and centralized
alternative to having an `init.templateDir` where you've changed
default hooks.
++
+You can also disable all hooks entirely by setting `core.hooksPath`
+to `/dev/null`. This is usually only advisable for expert users and
+on a per-command basis using configuration parameters of the form
+`git -c core.hooksPath=/dev/null ...`.
core.editor::
Commands such as `commit` and `tag` that let you edit
diff --git a/Documentation/config/http.adoc b/Documentation/config/http.adoc
index 22a8803dea..67393282fa 100644
--- a/Documentation/config/http.adoc
+++ b/Documentation/config/http.adoc
@@ -296,6 +296,24 @@ http.lowSpeedLimit, http.lowSpeedTime::
Can be overridden by the `GIT_HTTP_LOW_SPEED_LIMIT` and
`GIT_HTTP_LOW_SPEED_TIME` environment variables.
+http.keepAliveIdle::
+ Specifies how long in seconds to wait on an idle connection
+ before sending TCP keepalive probes (if supported by the OS). If
+ unset, curl's default value is used. Can be overridden by the
+ `GIT_HTTP_KEEPALIVE_IDLE` environment variable.
+
+http.keepAliveInterval::
+ Specifies how long in seconds to wait between TCP keepalive
+ probes (if supported by the OS). If unset, curl's default value
+ is used. Can be overridden by the `GIT_HTTP_KEEPALIVE_INTERVAL`
+ environment variable.
+
+http.keepAliveCount::
+ Specifies how many TCP keepalive probes to send before giving up
+ and terminating the connection (if supported by the OS). If
+ unset, curl's default value is used. Can be overridden by the
+ `GIT_HTTP_KEEPALIVE_COUNT` environment variable.
+
http.noEPSV::
A boolean which disables using of EPSV ftp command by curl.
This can be helpful with some "poor" ftp servers which don't
diff --git a/Documentation/config/maintenance.adoc b/Documentation/config/maintenance.adoc
index 72a9d6cf81..41536162a7 100644
--- a/Documentation/config/maintenance.adoc
+++ b/Documentation/config/maintenance.adoc
@@ -61,6 +61,11 @@ maintenance.loose-objects.auto::
loose objects is at least the value of `maintenance.loose-objects.auto`.
The default value is 100.
+maintenance.loose-objects.batchSize::
+ This integer config option controls the maximum number of loose objects
+ written into a packfile during the `loose-objects` task. The default is
+ fifty thousand. Use value `0` to indicate no limit.
+
maintenance.incremental-repack.auto::
This integer config option controls how often the `incremental-repack`
task should be run as part of `git maintenance run --auto`. If zero,
@@ -69,3 +74,12 @@ maintenance.incremental-repack.auto::
Otherwise, a positive value implies the command should run when the
number of pack-files not in the multi-pack-index is at least the value
of `maintenance.incremental-repack.auto`. The default value is 10.
+
+maintenance.reflog-expire.auto::
+ This integer config option controls how often the `reflog-expire` task
+ should be run as part of `git maintenance run --auto`. If zero, then
+ the `reflog-expire` task will not run with the `--auto` option. A
+ negative value will force the task to run every time. Otherwise, a
+ positive value implies the command should run when the number of
+ expired reflog entries in the "HEAD" reflog is at least the value of
+ `maintenance.loose-objects.auto`. The default value is 100.
diff --git a/Documentation/config/promisor.adoc b/Documentation/config/promisor.adoc
index 9192acfd24..2638b01f83 100644
--- a/Documentation/config/promisor.adoc
+++ b/Documentation/config/promisor.adoc
@@ -26,5 +26,5 @@ promisor.acceptFromServer::
server will be accepted. By accepting a promisor remote, the
client agrees that the server might omit objects that are
lazily fetchable from this promisor remote from its responses
- to "fetch" and "clone" requests from the client. See
- linkgit:gitprotocol-v2[5].
+ to "fetch" and "clone" requests from the client. Name and URL
+ comparisons are case sensitive. See linkgit:gitprotocol-v2[5].
diff --git a/Documentation/config/remote.adoc b/Documentation/config/remote.adoc
index 25fe219d10..91e46f66f5 100644
--- a/Documentation/config/remote.adoc
+++ b/Documentation/config/remote.adoc
@@ -108,7 +108,8 @@ the values inherited from a lower priority configuration files (e.g.
`$HOME/.gitconfig`).
remote.<name>.followRemoteHEAD::
- How linkgit:git-fetch[1] should handle updates to `remotes/<name>/HEAD`.
+ How linkgit:git-fetch[1] should handle updates to `remotes/<name>/HEAD`
+ when fetching using the configured refspecs of a remote.
The default value is "create", which will create `remotes/<name>/HEAD`
if it exists on the remote, but not locally; this will not touch an
already existing local reference. Setting it to "warn" will print
diff --git a/Documentation/git-blame.adoc b/Documentation/git-blame.adoc
index f75ed44790..e438d28625 100644
--- a/Documentation/git-blame.adoc
+++ b/Documentation/git-blame.adoc
@@ -135,10 +135,11 @@ header elements later.
The porcelain format generally suppresses commit information that has
already been seen. For example, two lines that are blamed to the same
commit will both be shown, but the details for that commit will be shown
-only once. This is more efficient, but may require more state be kept by
-the reader. The `--line-porcelain` option can be used to output full
-commit information for each line, allowing simpler (but less efficient)
-usage like:
+only once. Information which is specific to individual lines will not be
+grouped together, like revs to be marked 'ignored' or 'unblamable'. This
+is more efficient, but may require more state be kept by the reader. The
+`--line-porcelain` option can be used to output full commit information
+for each line, allowing simpler (but less efficient) usage like:
# count the number of lines attributed to each author
git blame --line-porcelain file |
diff --git a/Documentation/git-branch.adoc b/Documentation/git-branch.adoc
index 7a073a36d6..50a1e13e1f 100644
--- a/Documentation/git-branch.adoc
+++ b/Documentation/git-branch.adoc
@@ -7,23 +7,23 @@ git-branch - List, create, or delete branches
SYNOPSIS
--------
-[verse]
-'git branch' [--color[=<when>] | --no-color] [--show-current]
- [-v [--abbrev=<n> | --no-abbrev]]
- [--column[=<options>] | --no-column] [--sort=<key>]
- [--merged [<commit>]] [--no-merged [<commit>]]
- [--contains [<commit>]] [--no-contains [<commit>]]
- [--points-at <object>] [--format=<format>]
- [(-r | --remotes) | (-a | --all)]
- [--list] [<pattern>...]
-'git branch' [--track[=(direct|inherit)] | --no-track] [-f]
- [--recurse-submodules] <branchname> [<start-point>]
-'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
-'git branch' --unset-upstream [<branchname>]
-'git branch' (-m | -M) [<oldbranch>] <newbranch>
-'git branch' (-c | -C) [<oldbranch>] <newbranch>
-'git branch' (-d | -D) [-r] <branchname>...
-'git branch' --edit-description [<branchname>]
+[synopsis]
+git branch [--color[=<when>] | --no-color] [--show-current]
+ [-v [--abbrev=<n> | --no-abbrev]]
+ [--column[=<options>] | --no-column] [--sort=<key>]
+ [--merged [<commit>]] [--no-merged [<commit>]]
+ [--contains [<commit>]] [--no-contains [<commit>]]
+ [--points-at <object>] [--format=<format>]
+ [(-r|--remotes) | (-a|--all)]
+ [--list] [<pattern>...]
+git branch [--track[=(direct|inherit)] | --no-track] [-f]
+ [--recurse-submodules] <branch-name> [<start-point>]
+git branch (--set-upstream-to=<upstream>|-u <upstream>) [<branch-name>]
+git branch --unset-upstream [<branch-name>]
+git branch (-m|-M) [<old-branch>] <new-branch>
+git branch (-c|-C) [<old-branch>] <new-branch>
+git branch (-d|-D) [-r] <branch-name>...
+git branch --edit-description [<branch-name>]
DESCRIPTION
-----------
@@ -49,173 +49,184 @@ With `--contains`, shows only the branches that contain the named commit
named commit), `--no-contains` inverts it. With `--merged`, only branches
merged into the named commit (i.e. the branches whose tip commits are
reachable from the named commit) will be listed. With `--no-merged` only
-branches not merged into the named commit will be listed. If the <commit>
+branches not merged into the named commit will be listed. If the _<commit>_
argument is missing it defaults to `HEAD` (i.e. the tip of the current
branch).
-The command's second form creates a new branch head named <branchname>
-which points to the current `HEAD`, or <start-point> if given. As a
-special case, for <start-point>, you may use `"A...B"` as a shortcut for
-the merge base of `A` and `B` if there is exactly one merge base. You
-can leave out at most one of `A` and `B`, in which case it defaults to
-`HEAD`.
+The command's second form creates a new branch head named _<branch-name>_
+which points to the current `HEAD`, or _<start-point>_ if given. As a
+special case, for _<start-point>_, you may use `<rev-A>...<rev-B>` as a
+shortcut for the merge base of _<rev-A>_ and _<rev-B>_ if there is exactly
+one merge base. You can leave out at most one of _<rev-A>_ and _<rev-B>_,
+in which case it defaults to `HEAD`.
Note that this will create the new branch, but it will not switch the
-working tree to it; use "git switch <newbranch>" to switch to the
+working tree to it; use `git switch <new-branch>` to switch to the
new branch.
When a local branch is started off a remote-tracking branch, Git sets up the
branch (specifically the `branch.<name>.remote` and `branch.<name>.merge`
-configuration entries) so that 'git pull' will appropriately merge from
+configuration entries) so that `git pull` will appropriately merge from
the remote-tracking branch. This behavior may be changed via the global
`branch.autoSetupMerge` configuration flag. That setting can be
overridden by using the `--track` and `--no-track` options, and
changed later using `git branch --set-upstream-to`.
-With a `-m` or `-M` option, <oldbranch> will be renamed to <newbranch>.
-If <oldbranch> had a corresponding reflog, it is renamed to match
-<newbranch>, and a reflog entry is created to remember the branch
-renaming. If <newbranch> exists, -M must be used to force the rename
+With a `-m` or `-M` option, _<old-branch>_ will be renamed to _<new-branch>_.
+If _<old-branch>_ had a corresponding reflog, it is renamed to match
+_<new-branch>_, and a reflog entry is created to remember the branch
+renaming. If _<new-branch>_ exists, `-M` must be used to force the rename
to happen.
The `-c` and `-C` options have the exact same semantics as `-m` and
`-M`, except instead of the branch being renamed, it will be copied to a
new name, along with its config and reflog.
-With a `-d` or `-D` option, `<branchname>` will be deleted. You may
+With a `-d` or `-D` option, _<branch-name>_ will be deleted. You may
specify more than one branch for deletion. If the branch currently
has a reflog then the reflog will also be deleted.
Use `-r` together with `-d` to delete remote-tracking branches. Note, that it
only makes sense to delete remote-tracking branches if they no longer exist
-in the remote repository or if 'git fetch' was configured not to fetch
-them again. See also the 'prune' subcommand of linkgit:git-remote[1] for a
+in the remote repository or if `git fetch` was configured not to fetch
+them again. See also the `prune` subcommand of linkgit:git-remote[1] for a
way to clean up all obsolete remote-tracking branches.
OPTIONS
-------
--d::
---delete::
+`-d`::
+`--delete`::
Delete a branch. The branch must be fully merged in its
upstream branch, or in `HEAD` if no upstream was set with
`--track` or `--set-upstream-to`.
--D::
+`-D`::
Shortcut for `--delete --force`.
---create-reflog::
+`--create-reflog`::
Create the branch's reflog. This activates recording of
all changes made to the branch ref, enabling use of date
- based sha1 expressions such as "<branchname>@\{yesterday}".
+ based sha1 expressions such as `<branch-name>@{yesterday}`.
Note that in non-bare repositories, reflogs are usually
enabled by default by the `core.logAllRefUpdates` config option.
The negated form `--no-create-reflog` only overrides an earlier
`--create-reflog`, but currently does not negate the setting of
`core.logAllRefUpdates`.
--f::
---force::
- Reset <branchname> to <start-point>, even if <branchname> exists
- already. Without `-f`, 'git branch' refuses to change an existing branch.
+`-f`::
+`--force`::
+ Reset _<branch-name>_ to _<start-point>_, even if _<branch-name>_ exists
+ already. Without `-f`, `git branch` refuses to change an existing branch.
In combination with `-d` (or `--delete`), allow deleting the
branch irrespective of its merged status, or whether it even
points to a valid commit. In combination with
`-m` (or `--move`), allow renaming the branch even if the new
branch name already exists, the same applies for `-c` (or `--copy`).
+
-Note that 'git branch -f <branchname> [<start-point>]', even with '-f',
-refuses to change an existing branch `<branchname>` that is checked out
+Note that `git branch -f <branch-name> [<start-point>]`, even with `-f`,
+refuses to change an existing branch _<branch-name>_ that is checked out
in another worktree linked to the same repository.
--m::
---move::
+`-m`::
+`--move`::
Move/rename a branch, together with its config and reflog.
--M::
+`-M`::
Shortcut for `--move --force`.
--c::
---copy::
+`-c`::
+`--copy`::
Copy a branch, together with its config and reflog.
--C::
+`-C`::
Shortcut for `--copy --force`.
---color[=<when>]::
+`--color[=<when>]`::
Color branches to highlight current, local, and
remote-tracking branches.
- The value must be always (the default), never, or auto.
+ The value must be `always` (the default), `never`, or `auto`.
---no-color::
+`--no-color`::
Turn off branch colors, even when the configuration file gives the
default to color output.
Same as `--color=never`.
--i::
---ignore-case::
+`-i`::
+`--ignore-case`::
Sorting and filtering branches are case insensitive.
---omit-empty::
+`--omit-empty`::
Do not print a newline after formatted refs where the format expands
to the empty string.
---column[=<options>]::
---no-column::
+`--column[=<options>]`::
+`--no-column`::
Display branch listing in columns. See configuration variable
`column.branch` for option syntax. `--column` and `--no-column`
- without options are equivalent to 'always' and 'never' respectively.
+ without options are equivalent to `always` and `never` respectively.
+
This option is only applicable in non-verbose mode.
--r::
---remotes::
- List or delete (if used with -d) the remote-tracking branches.
+`--sort=<key>`::
+ Sort based on _<key>_. Prefix `-` to sort in descending
+ order of the value. You may use the `--sort=<key>` option
+ multiple times, in which case the last key becomes the primary
+ key. The keys supported are the same as those in linkgit:git-for-each-ref[1].
+ Sort order defaults to the value configured for the
+ `branch.sort` variable if it exists, or to sorting based on the
+ full refname (including `refs/...` prefix). This lists
+ detached `HEAD` (if present) first, then local branches and
+ finally remote-tracking branches. See linkgit:git-config[1].
+
+`-r`::
+`--remotes`::
+ List or delete (if used with `-d`) the remote-tracking branches.
Combine with `--list` to match the optional pattern(s).
--a::
---all::
+`-a`::
+`--all`::
List both remote-tracking branches and local branches.
Combine with `--list` to match optional pattern(s).
--l::
---list::
+`-l`::
+`--list`::
List branches. With optional `<pattern>...`, e.g. `git
branch --list 'maint-*'`, list only the branches that match
the pattern(s).
---show-current::
- Print the name of the current branch. In detached HEAD state,
+`--show-current`::
+ Print the name of the current branch. In detached `HEAD` state,
nothing is printed.
--v::
--vv::
---verbose::
+`-v`::
+`-vv`::
+`--verbose`::
When in list mode,
show sha1 and commit subject line for each head, along with
relationship to upstream branch (if any). If given twice, print
the path of the linked worktree (if any) and the name of the upstream
branch, as well (see also `git remote show <remote>`). Note that the
- current worktree's HEAD will not have its path printed (it will always
+ current worktree's `HEAD` will not have its path printed (it will always
be your current directory).
--q::
---quiet::
+`-q`::
+`--quiet`::
Be more quiet when creating or deleting a branch, suppressing
non-error messages.
---abbrev=<n>::
+`--abbrev=<n>`::
In the verbose listing that show the commit object name,
- show the shortest prefix that is at least '<n>' hexdigits
+ show the shortest prefix that is at least _<n>_ hexdigits
long that uniquely refers the object.
The default value is 7 and can be overridden by the `core.abbrev`
config option.
---no-abbrev::
+`--no-abbrev`::
Display the full sha1s in the output listing rather than abbreviating them.
--t::
---track[=(direct|inherit)]::
+`-t`::
+`--track[=(direct|inherit)]`::
When creating a new branch, set up `branch.<name>.remote` and
`branch.<name>.merge` configuration entries to set "upstream" tracking
configuration for the new branch. This
@@ -229,7 +240,7 @@ The exact upstream branch is chosen depending on the optional argument:
itself as the upstream; `--track=inherit` means to copy the upstream
configuration of the start-point branch.
+
-The branch.autoSetupMerge configuration variable specifies how `git switch`,
+The `branch.autoSetupMerge` configuration variable specifies how `git switch`,
`git checkout` and `git branch` should behave when neither `--track` nor
`--no-track` are specified:
+
@@ -238,106 +249,94 @@ were given whenever the start-point is a remote-tracking branch.
`false` behaves as if `--no-track` were given. `always` behaves as though
`--track=direct` were given. `inherit` behaves as though `--track=inherit`
were given. `simple` behaves as though `--track=direct` were given only when
-the start-point is a remote-tracking branch and the new branch has the same
+the _<start-point>_ is a remote-tracking branch and the new branch has the same
name as the remote branch.
+
See linkgit:git-pull[1] and linkgit:git-config[1] for additional discussion on
how the `branch.<name>.remote` and `branch.<name>.merge` options are used.
---no-track::
+`--no-track`::
Do not set up "upstream" configuration, even if the
- branch.autoSetupMerge configuration variable is set.
+ `branch.autoSetupMerge` configuration variable is set.
---recurse-submodules::
- THIS OPTION IS EXPERIMENTAL! Causes the current command to
+`--recurse-submodules`::
+ THIS OPTION IS EXPERIMENTAL! Cause the current command to
recurse into submodules if `submodule.propagateBranches` is
enabled. See `submodule.propagateBranches` in
linkgit:git-config[1]. Currently, only branch creation is
supported.
+
-When used in branch creation, a new branch <branchname> will be created
+When used in branch creation, a new branch _<branch-name>_ will be created
in the superproject and all of the submodules in the superproject's
-<start-point>. In submodules, the branch will point to the submodule
-commit in the superproject's <start-point> but the branch's tracking
+_<start-point>_. In submodules, the branch will point to the submodule
+commit in the superproject's _<start-point>_ but the branch's tracking
information will be set up based on the submodule's branches and remotes
e.g. `git branch --recurse-submodules topic origin/main` will create the
submodule branch "topic" that points to the submodule commit in the
superproject's "origin/main", but tracks the submodule's "origin/main".
---set-upstream::
+`--set-upstream`::
As this option had confusing syntax, it is no longer supported.
Please use `--track` or `--set-upstream-to` instead.
--u <upstream>::
---set-upstream-to=<upstream>::
- Set up <branchname>'s tracking information so <upstream> is
- considered <branchname>'s upstream branch. If no <branchname>
+`-u <upstream>`::
+`--set-upstream-to=<upstream>`::
+ Set up _<branch-name>_'s tracking information so _<upstream>_ is
+ considered _<branch-name>_'s upstream branch. If no _<branch-name>_
is specified, then it defaults to the current branch.
---unset-upstream::
- Remove the upstream information for <branchname>. If no branch
+`--unset-upstream`::
+ Remove the upstream information for _<branch-name>_. If no branch
is specified it defaults to the current branch.
---edit-description::
+`--edit-description`::
Open an editor and edit the text to explain what the branch is
for, to be used by various other commands (e.g. `format-patch`,
`request-pull`, and `merge` (if enabled)). Multi-line explanations
may be used.
---contains [<commit>]::
- Only list branches which contain the specified commit (HEAD
+`--contains [<commit>]`::
+ Only list branches which contain _<commit>_ (`HEAD`
if not specified). Implies `--list`.
---no-contains [<commit>]::
- Only list branches which don't contain the specified commit
- (HEAD if not specified). Implies `--list`.
+`--no-contains [<commit>]`::
+ Only list branches which don't contain _<commit>_
+ (`HEAD` if not specified). Implies `--list`.
---merged [<commit>]::
- Only list branches whose tips are reachable from the
- specified commit (HEAD if not specified). Implies `--list`.
+`--merged [<commit>]`::
+ Only list branches whose tips are reachable from
+ _<commit>_ (`HEAD` if not specified). Implies `--list`.
---no-merged [<commit>]::
- Only list branches whose tips are not reachable from the
- specified commit (HEAD if not specified). Implies `--list`.
+`--no-merged [<commit>]`::
+ Only list branches whose tips are not reachable from
+ _<commit>_ (`HEAD` if not specified). Implies `--list`.
-<branchname>::
+`--points-at <object>`::
+ Only list branches of _<object>_.
+
+`--format <format>`::
+ A string that interpolates `%(fieldname)` from a branch ref being shown
+ and the object it points at. _<format>_ is the same as
+ that of linkgit:git-for-each-ref[1].
+
+_<branch-name>_::
The name of the branch to create or delete.
The new branch name must pass all checks defined by
linkgit:git-check-ref-format[1]. Some of these checks
may restrict the characters allowed in a branch name.
-<start-point>::
+_<start-point>_::
The new branch head will point to this commit. It may be
given as a branch name, a commit-id, or a tag. If this
- option is omitted, the current HEAD will be used instead.
+ option is omitted, the current `HEAD` will be used instead.
-<oldbranch>::
+_<old-branch>_::
The name of an existing branch. If this option is omitted,
the name of the current branch will be used instead.
-<newbranch>::
+_<new-branch>_::
The new name for an existing branch. The same restrictions as for
- <branchname> apply.
-
---sort=<key>::
- Sort based on the key given. Prefix `-` to sort in descending
- order of the value. You may use the --sort=<key> option
- multiple times, in which case the last key becomes the primary
- key. The keys supported are the same as those in `git
- for-each-ref`. Sort order defaults to the value configured for the
- `branch.sort` variable if it exists, or to sorting based on the
- full refname (including `refs/...` prefix). This lists
- detached HEAD (if present) first, then local branches and
- finally remote-tracking branches. See linkgit:git-config[1].
-
-
---points-at <object>::
- Only list branches of the given object.
-
---format <format>::
- A string that interpolates `%(fieldname)` from a branch ref being shown
- and the object it points at. The format is the same as
- that of linkgit:git-for-each-ref[1].
+ _<branch-name>_ apply.
CONFIGURATION
-------------
@@ -374,7 +373,7 @@ $ git branch -D test <2>
------------
+
<1> Delete the remote-tracking branches "todo", "html" and "man". The next
- 'fetch' or 'pull' will create them again unless you configure them not to.
+ `git fetch` or `git pullè will create them again unless you configure them not to.
See linkgit:git-fetch[1].
<2> Delete the "test" branch even if the "master" branch (or whichever branch
is currently checked out) does not have all commits from the test branch.
@@ -386,8 +385,8 @@ $ git branch -r -l '<remote>/<pattern>' <1>
$ git for-each-ref 'refs/remotes/<remote>/<pattern>' <2>
------------
+
-<1> Using `-a` would conflate <remote> with any local branches you happen to
- have been prefixed with the same <remote> pattern.
+<1> Using `-a` would conflate _<remote>_ with any local branches you happen to
+ have been prefixed with the same _<remote>_ pattern.
<2> `for-each-ref` can take a wide range of options. See linkgit:git-for-each-ref[1]
Patterns will normally need quoting.
@@ -396,24 +395,24 @@ NOTES
-----
If you are creating a branch that you want to switch to immediately,
-it is easier to use the "git switch" command with its `-c` option to
+it is easier to use the `git switch` command with its `-c` option to
do the same thing with a single command.
The options `--contains`, `--no-contains`, `--merged` and `--no-merged`
serve four related but different purposes:
- `--contains <commit>` is used to find all branches which will need
- special attention if <commit> were to be rebased or amended, since those
- branches contain the specified <commit>.
+ special attention if _<commit>_ were to be rebased or amended, since those
+ branches contain the specified _<commit>_.
- `--no-contains <commit>` is the inverse of that, i.e. branches that don't
- contain the specified <commit>.
+ contain the specified _<commit>_.
- `--merged` is used to find all branches which can be safely deleted,
- since those branches are fully contained by HEAD.
+ since those branches are fully contained by `HEAD`.
- `--no-merged` is used to find branches which are candidates for merging
- into HEAD, since those branches are not fully contained by HEAD.
+ into `HEAD`, since those branches are not fully contained by `HEAD`.
include::ref-reachability-filters.adoc[]
@@ -422,8 +421,8 @@ SEE ALSO
linkgit:git-check-ref-format[1],
linkgit:git-fetch[1],
linkgit:git-remote[1],
-link:user-manual.html#what-is-a-branch[``Understanding history: What is
-a branch?''] in the Git User's Manual.
+link:user-manual.html#what-is-a-branch["Understanding history: What is
+a branch?"] in the Git User's Manual.
GIT
---
diff --git a/Documentation/git-cat-file.adoc b/Documentation/git-cat-file.adoc
index 30359f5dbd..fc4b92f104 100644
--- a/Documentation/git-cat-file.adoc
+++ b/Documentation/git-cat-file.adoc
@@ -81,6 +81,25 @@ OPTIONS
end-of-line conversion, etc). In this case, `<object>` has to be of
the form `<tree-ish>:<path>`, or `:<path>`.
+--filter=<filter-spec>::
+--no-filter::
+ Omit objects from the list of printed objects. This can only be used in
+ combination with one of the batched modes. Excluded objects that have
+ been explicitly requested via any of the batch modes that read objects
+ via standard input (`--batch`, `--batch-check`) will be reported as
+ "filtered". Excluded objects in `--batch-all-objects` mode will not be
+ printed at all. The '<filter-spec>' may be one of the following:
++
+The form '--filter=blob:none' omits all blobs.
++
+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.
+
--path=<path>::
For use with `--textconv` or `--filters`, to allow specifying an object
name and a path separately, e.g. when it is difficult to figure out
@@ -340,6 +359,13 @@ the repository, then `cat-file` will ignore any custom format and print:
<object> SP missing LF
------------
+If a name is specified on stdin that is filtered out via `--filter=`,
+then `cat-file` will ignore any custom format and print:
+
+------------
+<object> SP excluded LF
+------------
+
If a name is specified that might refer to more than one object (an ambiguous short sha), then `cat-file` will ignore any custom format and print:
------------
diff --git a/Documentation/git-maintenance.adoc b/Documentation/git-maintenance.adoc
index 0450d74aff..3a1e2a69b6 100644
--- a/Documentation/git-maintenance.adoc
+++ b/Documentation/git-maintenance.adoc
@@ -126,13 +126,17 @@ loose-objects::
objects that already exist in a pack-file; concurrent Git processes
will examine the pack-file for the object data instead of the loose
object. Second, it creates a new pack-file (starting with "loose-")
- containing a batch of loose objects. The batch size is limited to 50
- thousand objects to prevent the job from taking too long on a
- repository with many loose objects. The `gc` task writes unreachable
- objects as loose objects to be cleaned up by a later step only if
- they are not re-added to a pack-file; for this reason it is not
- advisable to enable both the `loose-objects` and `gc` tasks at the
- same time.
+ containing a batch of loose objects.
++
+The batch size defaults to fifty thousand objects to prevent the job from
+taking too long on a repository with many loose objects. Use the
+`maintenance.loose-objects.batchSize` config option to adjust this size,
+including a value of `0` to remove the limit.
++
+The `gc` task writes unreachable objects as loose objects to be cleaned up
+by a later step only if they are not re-added to a pack-file; for this
+reason it is not advisable to enable both the `loose-objects` and `gc`
+tasks at the same time.
incremental-repack::
The `incremental-repack` job repacks the object directory
@@ -158,6 +162,10 @@ pack-refs::
need to iterate across many references. See linkgit:git-pack-refs[1]
for more information.
+reflog-expire::
+ The `reflog-expire` task deletes any entries in the reflog older than the
+ expiry threshold. See linkgit:git-reflog[1] for more information.
+
OPTIONS
-------
--auto::
diff --git a/Documentation/git-mv.adoc b/Documentation/git-mv.adoc
index dc1bf61534..f707e998f7 100644
--- a/Documentation/git-mv.adoc
+++ b/Documentation/git-mv.adoc
@@ -8,19 +8,18 @@ git-mv - Move or rename a file, a directory, or a symlink
SYNOPSIS
--------
-[verse]
-'git mv' [<options>] <source>... <destination>
+
+[synopsis]
+git mv [-v] [-f] [-n] [-k] <source> <destination>
+git mv [-v] [-f] [-n] [-k] <source>... <destination-directory>
DESCRIPTION
-----------
Move or rename a file, directory, or symlink.
- git mv [-v] [-f] [-n] [-k] <source> <destination>
- git mv [-v] [-f] [-n] [-k] <source> ... <destination-directory>
-
-In the first form, it renames <source>, which must exist and be either
-a file, symlink or directory, to <destination>.
-In the second form, the last argument has to be an existing
+In the first form, it renames _<source>_, which must exist and be either
+a file, symlink or directory, to _<destination>_.
+In the second form, _<destination-directory>_ has to be an existing
directory; the given sources will be moved into this directory.
The index is updated after successful completion, but the change must still be
@@ -28,20 +27,20 @@ committed.
OPTIONS
-------
--f::
---force::
+`-f`::
+`--force`::
Force renaming or moving of a file even if the <destination> exists.
--k::
+`-k`::
Skip move or rename actions which would lead to an error
condition. An error happens when a source is neither existing nor
controlled by Git, or when it would overwrite an existing
file unless `-f` is given.
--n::
---dry-run::
+`-n`::
+`--dry-run`::
Do nothing; only show what would happen
--v::
---verbose::
+`-v`::
+`--verbose`::
Report the names of files as they are moved.
SUBMODULES
@@ -49,8 +48,8 @@ SUBMODULES
Moving a submodule using a gitfile (which means they were cloned
with a Git version 1.7.8 or newer) will update the gitfile and
core.worktree setting to make the submodule work in the new location.
-It also will attempt to update the submodule.<name>.path setting in
-the linkgit:gitmodules[5] file and stage that file (unless -n is used).
+It also will attempt to update the `submodule.<name>.path` setting in
+the linkgit:gitmodules[5] file and stage that file (unless `-n` is used).
BUGS
----
diff --git a/Documentation/git-pack-refs.adoc b/Documentation/git-pack-refs.adoc
index 2dcabaf74c..652c549771 100644
--- a/Documentation/git-pack-refs.adoc
+++ b/Documentation/git-pack-refs.adoc
@@ -88,10 +88,10 @@ Do not pack refs matching the given `glob(7)` pattern. Repetitions of this optio
accumulate exclusion patterns. Use `--no-exclude` to clear and reset the list of
patterns. If a ref is already packed, including it with `--exclude` will not
unpack it.
-
++
When used with `--all`, pack only loose refs which do not match any of
the provided `--exclude` patterns.
-
++
When used with `--include`, refs provided to `--include`, minus refs that are
provided to `--exclude` will be packed.
diff --git a/Documentation/git-reflog.adoc b/Documentation/git-reflog.adoc
index a929c52982..b55c060569 100644
--- a/Documentation/git-reflog.adoc
+++ b/Documentation/git-reflog.adoc
@@ -16,6 +16,7 @@ SYNOPSIS
[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]
'git reflog delete' [--rewrite] [--updateref]
[--dry-run | -n] [--verbose] <ref>@{<specifier>}...
+'git reflog drop' [--all [--single-worktree] | <refs>...]
'git reflog exists' <ref>
DESCRIPTION
@@ -48,10 +49,14 @@ and not reachable from the current tip, are removed from the reflog.
This is typically not used directly by end users -- instead, see
linkgit:git-gc[1].
-The "delete" subcommand deletes single entries from the reflog. Its
-argument must be an _exact_ entry (e.g. "`git reflog delete
-master@{2}`"). This subcommand is also typically not used directly by
-end users.
+The "delete" subcommand deletes single entries from the reflog, but
+not the reflog itself. Its argument must be an _exact_ entry (e.g. "`git
+reflog delete master@{2}`"). This subcommand is also typically not used
+directly by end users.
+
+The "drop" subcommand completely removes the reflog for the specified
+references. This is in contrast to "expire" and "delete", both of which
+can be used to delete reflog entries, but not the reflog itself.
The "exists" subcommand checks whether a ref has a reflog. It exits
with zero status if the reflog exists, and non-zero status if it does
@@ -132,6 +137,16 @@ Options for `delete`
`--dry-run`, and `--verbose`, with the same meanings as when they are
used with `expire`.
+Options for `drop`
+~~~~~~~~~~~~~~~~~~~~
+
+--all::
+ Drop the reflogs of all references from all worktrees.
+
+--single-worktree::
+ By default when `--all` is specified, reflogs from all working
+ trees are dropped. This option limits the processing to reflogs
+ from the current working tree only.
GIT
---
diff --git a/Documentation/git-repack.adoc b/Documentation/git-repack.adoc
index 5852a5c973..e1cd75eebe 100644
--- a/Documentation/git-repack.adoc
+++ b/Documentation/git-repack.adoc
@@ -77,15 +77,18 @@ to the new separate pack will be written.
Only useful with `--cruft -d`.
--max-cruft-size=<n>::
- Repack cruft objects into packs as large as `<n>` bytes before
- creating new packs. As long as there are enough cruft packs
- smaller than `<n>`, repacking will cause a new cruft pack to
- be created containing objects from any combined cruft packs,
- along with any new unreachable objects. Cruft packs larger than
- `<n>` will not be modified. When the new cruft pack is larger
- than `<n>` bytes, it will be split into multiple packs, all of
- which are guaranteed to be at most `<n>` bytes in size. Only
- useful with `--cruft -d`.
+ Overrides `--max-pack-size` for cruft packs. Inherits the value of
+ `--max-pack-size` (if any) by default. See the documentation for
+ `--max-pack-size` for more details.
+
+--combine-cruft-below-size=<n>::
+ When generating cruft packs without pruning, only repack
+ existing cruft packs whose size is strictly less than `<n>`,
+ where `<n>` represents a number of bytes, which can optionally
+ be suffixed with "k", "m", or "g". Cruft packs whose size is
+ greater than or equal to `<n>` are left as-is and not repacked.
+ Useful when you want to avoid repacking large cruft pack(s) in
+ repositories that have many and/or large unreachable objects.
--expire-to=<dir>::
Write a cruft pack containing pruned objects (if any) to the
diff --git a/Documentation/git-reset.adoc b/Documentation/git-reset.adoc
index 79ad5643ee..53ab88c545 100644
--- a/Documentation/git-reset.adoc
+++ b/Documentation/git-reset.adoc
@@ -7,23 +7,23 @@ git-reset - Reset current HEAD to the specified state
SYNOPSIS
--------
-[verse]
-'git reset' [-q] [<tree-ish>] [--] <pathspec>...
-'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]
-'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]
-'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
+[synopsis]
+git reset [-q] [<tree-ish>] [--] <pathspec>...
+git reset [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]
+git reset (--patch | -p) [<tree-ish>] [--] [<pathspec>...]
+git reset [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
DESCRIPTION
-----------
-In the first three forms, copy entries from `<tree-ish>` to the index.
-In the last form, set the current branch head (`HEAD`) to `<commit>`,
+In the first three forms, copy entries from _<tree-ish>_ to the index.
+In the last form, set the current branch head (`HEAD`) to _<commit>_,
optionally modifying index and working tree to match.
-The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
+The _<tree-ish>_/_<commit>_ defaults to `HEAD` in all forms.
-'git reset' [-q] [<tree-ish>] [--] <pathspec>...::
-'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]::
+`git reset [-q] [<tree-ish>] [--] <pathspec>...`::
+`git reset [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]`::
These forms reset the index entries for all paths that match the
- `<pathspec>` to their state at `<tree-ish>`. (It does not affect
+ _<pathspec>_ to their state at _<tree-ish>_. (It does not affect
the working tree or the current branch.)
+
This means that `git reset <pathspec>` is the opposite of `git add
@@ -37,30 +37,30 @@ and specifying a commit with `--source`, you
can copy the contents of a path out of a commit to the index and to the
working tree in one go.
-'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]::
+`git reset (--patch | -p) [<tree-ish>] [--] [<pathspec>...]`::
Interactively select hunks in the difference between the index
- and `<tree-ish>` (defaults to `HEAD`). The chosen hunks are applied
+ and _<tree-ish>_ (defaults to `HEAD`). The chosen hunks are applied
in reverse to the index.
+
This means that `git reset -p` is the opposite of `git add -p`, i.e.
-you can use it to selectively reset hunks. See the ``Interactive Mode''
+you can use it to selectively reset hunks. See the "Interactive Mode"
section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
-'git reset' [<mode>] [<commit>]::
- This form resets the current branch head to `<commit>` and
- possibly updates the index (resetting it to the tree of `<commit>`) and
- the working tree depending on `<mode>`. Before the operation, `ORIG_HEAD`
- is set to the tip of the current branch. If `<mode>` is omitted,
- defaults to `--mixed`. The `<mode>` must be one of the following:
+`git reset [<mode>] [<commit>]`::
+ This form resets the current branch head to _<commit>_ and
+ possibly updates the index (resetting it to the tree of _<commit>_) and
+ the working tree depending on _<mode>_. Before the operation, `ORIG_HEAD`
+ is set to the tip of the current branch. If _<mode>_ is omitted,
+ defaults to `--mixed`. The _<mode>_ must be one of the following:
+
--
---soft::
+`--soft`::
Does not touch the index file or the working tree at all (but
- resets the head to `<commit>`, just like all modes do). This leaves
+ resets the head to _<commit>_, just like all modes do). This leaves
all your changed files "Changes to be committed", as `git status`
would put it.
---mixed::
+`--mixed`::
Resets the index but not the working tree (i.e., the changed files
are preserved but not marked for commit) and reports what has not
been updated. This is the default action.
@@ -68,33 +68,33 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
If `-N` is specified, removed paths are marked as intent-to-add (see
linkgit:git-add[1]).
---hard::
+`--hard`::
Resets the index and working tree. Any changes to tracked files in the
- working tree since `<commit>` are discarded. Any untracked files or
+ working tree since _<commit>_ are discarded. Any untracked files or
directories in the way of writing any tracked files are simply deleted.
---merge::
+`--merge`::
Resets the index and updates the files in the working tree that are
- different between `<commit>` and `HEAD`, but keeps those which are
+ different between _<commit>_ and `HEAD`, but keeps those which are
different between the index and working tree (i.e. which have changes
which have not been added).
- If a file that is different between `<commit>` and the index has
+ If a file that is different between _<commit>_ and the index has
unstaged changes, reset is aborted.
+
In other words, `--merge` does something like a `git read-tree -u -m <commit>`,
but carries forward unmerged index entries.
---keep::
+`--keep`::
Resets index entries and updates files in the working tree that are
- different between `<commit>` and `HEAD`.
- If a file that is different between `<commit>` and `HEAD` has local
+ different between _<commit>_ and `HEAD`.
+ If a file that is different between _<commit>_ and `HEAD` has local
changes, reset is aborted.
---[no-]recurse-submodules::
- When the working tree is updated, using --recurse-submodules will
+`--[no-]recurse-submodules`::
+ When the working tree is updated, using `--recurse-submodules` will
also recursively reset the working tree of all active submodules
according to the commit recorded in the superproject, also setting
- the submodules' HEAD to be detached at that commit.
+ the submodules' `HEAD` to be detached at that commit.
--
See "Reset, restore and revert" in linkgit:git[1] for the differences
@@ -104,31 +104,31 @@ between the three commands.
OPTIONS
-------
--q::
---quiet::
+`-q`::
+`--quiet`::
Be quiet, only report errors.
---refresh::
---no-refresh::
+`--refresh`::
+`--no-refresh`::
Refresh the index after a mixed reset. Enabled by default.
---pathspec-from-file=<file>::
- Pathspec is passed in `<file>` instead of commandline args. If
- `<file>` is exactly `-` then standard input is used. Pathspec
- elements are separated by LF or CR/LF. Pathspec elements can be
+`--pathspec-from-file=<file>`::
+ Pathspec is passed in _<file>_ instead of commandline args. If
+ _<file>_ is exactly `-` then standard input is used. Pathspec
+ elements are separated by _LF_ or _CR_/_LF_. Pathspec elements can be
quoted as explained for the configuration variable `core.quotePath`
(see linkgit:git-config[1]). See also `--pathspec-file-nul` and
global `--literal-pathspecs`.
---pathspec-file-nul::
+`--pathspec-file-nul`::
Only meaningful with `--pathspec-from-file`. Pathspec elements are
- separated with NUL character and all other characters are taken
+ separated with _NUL_ character and all other characters are taken
literally (including newlines and quotes).
-\--::
+`--`::
Do not interpret any more arguments as options.
-<pathspec>...::
+`<pathspec>...`::
Limits the paths affected by the operation.
+
For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
@@ -348,7 +348,7 @@ $ git commit ... <8>
------------
+
<1> First, reset the history back one commit so that we remove the original
- commit, but leave the working tree with all the changes. The -N ensures
+ commit, but leave the working tree with all the changes. The `-N` ensures
that any new files added with `HEAD` are still marked so that `git add -p`
will find them.
<2> Next, we interactively select diff hunks to add using the `git add -p`
@@ -458,7 +458,7 @@ working index HEAD target working index HEAD
--keep B C C
....
-`reset --merge` is meant to be used when resetting out of a conflicted
+`git reset --merge` is meant to be used when resetting out of a conflicted
merge. Any mergy operation guarantees that the working tree file that is
involved in the merge does not have a local change with respect to the index
before it starts, and that it writes the result out to the working tree. So if
@@ -467,7 +467,7 @@ between the index and the working tree, then it means that we are not
resetting out from a state that a mergy operation left after failing
with a conflict. That is why we disallow `--merge` option in this case.
-`reset --keep` is meant to be used when removing some of the last
+`git reset --keep` is meant to be used when removing some of the last
commits in the current branch while keeping changes in the working
tree. If there could be conflicts between the changes in the commit we
want to remove and the changes in the working tree we want to keep,
diff --git a/Documentation/git-restore.adoc b/Documentation/git-restore.adoc
index 751f01b441..877b7772e6 100644
--- a/Documentation/git-restore.adoc
+++ b/Documentation/git-restore.adoc
@@ -51,9 +51,6 @@ leave out at most one of _<rev-A>__ and _<rev-B>_, in which case it defaults to
restore source and the restore location. See the "Interactive
Mode" section of linkgit:git-add[1] to learn how to operate
the `--patch` mode.
-+
-Note that `--patch` can accept no pathspec and will prompt to restore
-all modified paths.
`-W`::
`--worktree`::
diff --git a/Documentation/git-rm.adoc b/Documentation/git-rm.adoc
index 363a26934f..b5ead86796 100644
--- a/Documentation/git-rm.adoc
+++ b/Documentation/git-rm.adoc
@@ -7,10 +7,10 @@ git-rm - Remove files from the working tree and from the index
SYNOPSIS
--------
-[verse]
-'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]
- [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]
- [--] [<pathspec>...]
+[synopsis]
+git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]
+ [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]
+ [--] [<pathspec>...]
DESCRIPTION
-----------
@@ -30,7 +30,7 @@ sparse-checkouts are in use (see linkgit:git-sparse-checkout[1]),
OPTIONS
-------
-<pathspec>...::
+`<pathspec>...`::
Files to remove. A leading directory name (e.g. `dir` to remove
`dir/file1` and `dir/file2`) can be given to remove all files in
the directory, and recursively all sub-directories, but this
@@ -43,57 +43,57 @@ directories `d` and `d2`, there is a difference between using
`git rm 'd*'` and `git rm 'd/*'`, as the former will also remove all
of directory `d2`.
+
-For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
+For more details, see the _<pathspec>_ entry in linkgit:gitglossary[7].
--f::
---force::
+`-f`::
+`--force`::
Override the up-to-date check.
--n::
---dry-run::
+`-n`::
+`--dry-run`::
Don't actually remove any file(s). Instead, just show
if they exist in the index and would otherwise be removed
by the command.
--r::
+`-r`::
Allow recursive removal when a leading directory name is
given.
-\--::
+`--`::
This option can be used to separate command-line options from
the list of files, (useful when filenames might be mistaken
for command-line options).
---cached::
+`--cached`::
Use this option to unstage and remove paths only from the index.
Working tree files, whether modified or not, will be
left alone.
---ignore-unmatch::
+`--ignore-unmatch`::
Exit with a zero status even if no files matched.
---sparse::
+`--sparse`::
Allow updating index entries outside of the sparse-checkout cone.
Normally, `git rm` refuses to update index entries whose paths do
not fit within the sparse-checkout cone. See
linkgit:git-sparse-checkout[1] for more.
--q::
---quiet::
+`-q`::
+`--quiet`::
`git rm` normally outputs one line (in the form of an `rm` command)
for each file removed. This option suppresses that output.
---pathspec-from-file=<file>::
- Pathspec is passed in `<file>` instead of commandline args. If
- `<file>` is exactly `-` then standard input is used. Pathspec
- elements are separated by LF or CR/LF. Pathspec elements can be
+`--pathspec-from-file=<file>`::
+ Pathspec is passed in _<file>_ instead of args. If
+ _<file>_ is exactly `-` then standard input is used. Pathspec
+ elements are separated by _LF_ or _CR_/_LF_. Pathspec elements can be
quoted as explained for the configuration variable `core.quotePath`
(see linkgit:git-config[1]). See also `--pathspec-file-nul` and
global `--literal-pathspecs`.
---pathspec-file-nul::
+`--pathspec-file-nul`::
Only meaningful with `--pathspec-from-file`. Pathspec elements are
- separated with NUL character and all other characters are taken
+ separated with _NUL_ character and all other characters are taken
literally (including newlines and quotes).
@@ -153,15 +153,15 @@ SUBMODULES
----------
Only submodules using a gitfile (which means they were cloned
with a Git version 1.7.8 or newer) will be removed from the work
-tree, as their repository lives inside the .git directory of the
+tree, as their repository lives inside the `.git` directory of the
superproject. If a submodule (or one of those nested inside it)
-still uses a .git directory, `git rm` will move the submodules
+still uses a `.git` directory, `git rm` moves the submodules
git directory into the superprojects git directory to protect
-the submodule's history. If it exists the submodule.<name> section
+the submodule's history. If it exists the `submodule.<name>` section
in the linkgit:gitmodules[5] file will also be removed and that file
-will be staged (unless --cached or -n are used).
+will be staged (unless `--cached` or `-n` are used).
-A submodule is considered up to date when the HEAD is the same as
+A submodule is considered up to date when the `HEAD` is the same as
recorded in the index, no tracked files are modified and no untracked
files that aren't ignored are present in the submodule's work tree.
Ignored files are deemed expendable and won't stop a submodule's work
diff --git a/Documentation/git-update-ref.adoc b/Documentation/git-update-ref.adoc
index 9e6935d38d..9310ce9768 100644
--- a/Documentation/git-update-ref.adoc
+++ b/Documentation/git-update-ref.adoc
@@ -7,8 +7,10 @@ git-update-ref - Update the object name stored in a ref safely
SYNOPSIS
--------
-[verse]
-'git update-ref' [-m <reason>] [--no-deref] (-d <ref> [<old-oid>] | [--create-reflog] <ref> <new-oid> [<old-oid>] | --stdin [-z])
+[synopsis]
+git update-ref [-m <reason>] [--no-deref] -d <ref> [<old-oid>]
+git update-ref [-m <reason>] [--no-deref] [--create-reflog] <ref> <new-oid> [<old-oid>]
+git update-ref [-m <reason>] [--no-deref] --stdin [-z] [--batch-updates]
DESCRIPTION
-----------
@@ -57,6 +59,14 @@ performs all modifications together. Specify commands of the form:
With `--create-reflog`, update-ref will create a reflog for each ref
even if one would not ordinarily be created.
+With `--batch-updates`, update-ref executes the updates in a batch but allows
+individual updates to fail due to invalid or incorrect user input, applying only
+the successful updates. However, system-related errors—such as I/O failures or
+memory issues—will result in a full failure of all batched updates. Any failed
+updates will be reported in the following format:
+
+ rejected SP (<old-oid> | <old-target>) SP (<new-oid> | <new-target>) SP <rejection-reason> LF
+
Quote fields containing whitespace as if they were strings in C source
code; i.e., surrounded by double-quotes and with backslash escapes.
Use 40 "0" characters or the empty string to specify a zero value. To
diff --git a/Documentation/git-version.adoc b/Documentation/git-version.adoc
index 80fa7754a6..9462043a14 100644
--- a/Documentation/git-version.adoc
+++ b/Documentation/git-version.adoc
@@ -22,6 +22,14 @@ OPTIONS
--build-options::
Include additional information about how git was built for diagnostic
purposes.
++
+The libraries used to implement the SHA-1 and SHA-256 algorithms are displayed
+in the form `SHA-1: <option>` and `SHA-256: <option>`. Note that the SHA-1
+options `SHA1_APPLE`, `SHA1_OPENSSL`, and `SHA1_BLK` do not use a collision
+detection algorithm and thus may be vulnerable to known SHA-1 collision
+attacks. When a faster SHA-1 implementation without collision detection is used
+for only non-cryptographic purposes, the algorithm is displayed in the form
+`non-collision-detecting-SHA-1: <option>`.
GIT
---
diff --git a/Documentation/merge-strategies.adoc b/Documentation/merge-strategies.adoc
index 59f5ae36cc..9e034f447e 100644
--- a/Documentation/merge-strategies.adoc
+++ b/Documentation/merge-strategies.adoc
@@ -87,44 +87,33 @@ no-renames;;
configuration variable.
See also linkgit:git-diff[1] `--no-renames`.
-subtree[=<path>];;
- This option is a more advanced form of 'subtree' strategy, where
- the strategy makes a guess on how two trees must be shifted to
- match with each other when merging. Instead, the specified path
- is prefixed (or stripped from the beginning) to make the shape of
- two trees to match.
-
-recursive::
- This can only resolve two heads using a 3-way merge
- algorithm. When there is more than one common
- ancestor that can be used for 3-way merge, it creates a
- merged tree of the common ancestors and uses that as
- the reference tree for the 3-way merge. This has been
- reported to result in fewer merge conflicts without
- causing mismerges by tests done on actual merge commits
- taken from Linux 2.6 kernel development history.
- Additionally this can detect and handle merges involving
- renames. It does not make use of detected copies. This was
- the default strategy for resolving two heads from Git v0.99.9k
- until v2.33.0.
-+
-For a path that is a submodule, the same caution as 'ort' applies to this
-strategy.
-+
-The 'recursive' strategy takes the same options as 'ort'. However,
-there are two additional options that 'ort' ignores (not documented
-above) that are potentially useful with the 'recursive' strategy:
+histogram;;
+ Deprecated synonym for `diff-algorithm=histogram`.
patience;;
Deprecated synonym for `diff-algorithm=patience`.
-diff-algorithm=[patience|minimal|histogram|myers];;
+diff-algorithm=[histogram|minimal|myers|patience];;
Use a different diff algorithm while merging, which 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`. Note that `ort`
- specifically uses `diff-algorithm=histogram`, while `recursive`
- defaults to the `diff.algorithm` config setting.
+ defaults to `diff-algorithm=histogram`, while regular diffs
+ currently default to the `diff.algorithm` config setting.
+
+subtree[=<path>];;
+ This option is a more advanced form of 'subtree' strategy, where
+ the strategy makes a guess on how two trees must be shifted to
+ match with each other when merging. Instead, the specified path
+ is prefixed (or stripped from the beginning) to make the shape of
+ two trees to match.
+
+recursive::
+ This is now a synonym for `ort`. It was an alternative
+ implementation until v2.49.0, but was redirected to mean `ort`
+ in v2.50.0. The previous recursive strategy was the default
+ strategy for resolving two heads from Git v0.99.9k until
+ v2.33.0.
resolve::
This can only resolve two heads (i.e. the current branch
@@ -145,7 +134,7 @@ ours::
ignoring all changes from all other branches. It is meant to
be used to supersede old development history of side
branches. Note that this is different from the -Xours option to
- the 'recursive' merge strategy.
+ the 'ort' merge strategy.
subtree::
This is a modified `ort` strategy. When merging trees A and
diff --git a/Documentation/mergetools/vimdiff.adoc b/Documentation/mergetools/vimdiff.adoc
index befa86d692..ab915df408 100644
--- a/Documentation/mergetools/vimdiff.adoc
+++ b/Documentation/mergetools/vimdiff.adoc
@@ -86,7 +86,7 @@ command.
+
--
When `MERGED` is not present in the layout, you must "mark" one of the
-buffers with an asterisk. That will become the buffer you need to edit and
+buffers with an arobase (`@`). That will become the buffer you need to edit and
save after resolving the conflicts.
....
------------------------------------------
diff --git a/Documentation/meson.build b/Documentation/meson.build
index 5e556ce360..8d9cd98548 100644
--- a/Documentation/meson.build
+++ b/Documentation/meson.build
@@ -215,9 +215,9 @@ endif
docs_backend = get_option('docs_backend')
if docs_backend == 'auto'
- if find_program('asciidoc', dirs: program_path, required: false).found()
+ if find_program('asciidoc', dirs: program_path, native: true, required: false).found()
docs_backend = 'asciidoc'
- elif find_program('asciidoctor', dirs: program_path, required: false).found()
+ elif find_program('asciidoctor', dirs: program_path, native: true, required: false).found()
docs_backend = 'asciidoctor'
else
error('Neither asciidoc nor asciidoctor were found.')
@@ -225,7 +225,7 @@ if docs_backend == 'auto'
endif
if docs_backend == 'asciidoc'
- asciidoc = find_program('asciidoc', dirs: program_path)
+ asciidoc = find_program('asciidoc', dirs: program_path, native: true)
asciidoc_html = 'xhtml11'
asciidoc_docbook = 'docbook'
xmlto_extra = [ ]
@@ -254,7 +254,7 @@ if docs_backend == 'asciidoc'
asciidoc_conf,
]
elif docs_backend == 'asciidoctor'
- asciidoctor = find_program('asciidoctor', dirs: program_path)
+ asciidoctor = find_program('asciidoctor', dirs: program_path, native: true)
asciidoc_html = 'xhtml5'
asciidoc_docbook = 'docbook5'
xmlto_extra = [
@@ -296,7 +296,7 @@ if get_option('breaking_changes')
asciidoc_common_options += ['--attribute', 'with-breaking-changes']
endif
-xmlto = find_program('xmlto', dirs: program_path)
+xmlto = find_program('xmlto', dirs: program_path, native: true)
cmd_lists = [
'cmds-ancillaryinterrogators.adoc',
@@ -417,7 +417,7 @@ if get_option('docs').contains('html')
pointing_to: 'git.html',
)
- xsltproc = find_program('xsltproc', dirs: program_path)
+ xsltproc = find_program('xsltproc', dirs: program_path, native: true)
user_manual_xml = custom_target(
command: asciidoc_common_options + [
@@ -448,6 +448,7 @@ if get_option('docs').contains('html')
)
articles = [
+ 'BreakingChanges.adoc',
'DecisionMaking.adoc',
'MyFirstContribution.adoc',
'MyFirstObjectWalk.adoc',
diff --git a/Documentation/rev-list-options.adoc b/Documentation/rev-list-options.adoc
index 1c403c1e40..d38875efda 100644
--- a/Documentation/rev-list-options.adoc
+++ b/Documentation/rev-list-options.adoc
@@ -361,6 +361,30 @@ ifdef::git-rev-list[]
--progress=<header>::
Show progress reports on stderr as objects are considered. The
`<header>` text will be printed with each progress update.
+
+-z::
+ Instead of being newline-delimited, each outputted object and its
+ accompanying metadata is delimited using NUL bytes. Output is printed
+ in the following form:
++
+-----------------------------------------------------------------------
+<OID> NUL [<token>=<value> NUL]...
+-----------------------------------------------------------------------
++
+Additional object metadata, such as object paths or boundary objects, is
+printed using the `<token>=<value>` form. Token values are printed as-is
+without any encoding/truncation. An OID entry never contains a '=' character
+and thus is used to signal the start of a new object record. Examples:
++
+-----------------------------------------------------------------------
+<OID> NUL
+<OID> NUL path=<path> NUL
+<OID> NUL boundary=yes NUL
+<OID> NUL missing=yes NUL [<token>=<value> NUL]...
+-----------------------------------------------------------------------
++
+This mode is only compatible with the `--objects`, `--boundary`, and
+`--missing` output options.
endif::git-rev-list[]
History Simplification
diff --git a/Documentation/technical/multi-pack-index.adoc b/Documentation/technical/multi-pack-index.adoc
index cc063b30be..ffda70aa13 100644
--- a/Documentation/technical/multi-pack-index.adoc
+++ b/Documentation/technical/multi-pack-index.adoc
@@ -164,19 +164,81 @@ objects_nr($H2) + objects_nr($H1) + i
(in the C implementation, this is often computed as `i +
m->num_objects_in_base`).
+=== Pseudo-pack order for incremental MIDXs
+
+The original implementation of multi-pack reachability bitmaps defined
+the pseudo-pack order in linkgit:gitformat-pack[5] (see the section
+titled "multi-pack-index reverse indexes") roughly as follows:
+
+____
+In short, a MIDX's pseudo-pack is the de-duplicated concatenation of
+objects in packs stored by the MIDX, laid out in pack order, and the
+packs arranged in MIDX order (with the preferred pack coming first).
+____
+
+In the incremental MIDX design, we extend this definition to include
+objects from multiple layers of the MIDX chain. The pseudo-pack order
+for incremental MIDXs is determined by concatenating the pseudo-pack
+ordering for each layer of the MIDX chain in order. Formally two objects
+`o1` and `o2` are compared as follows:
+
+1. If `o1` appears in an earlier layer of the MIDX chain than `o2`, then
+ `o1` sorts ahead of `o2`.
+
+2. Otherwise, if `o1` and `o2` appear in the same MIDX layer, and that
+ MIDX layer has no base, then if one of `pack(o1)` and `pack(o2)` is
+ preferred and the other is not, then the preferred one sorts ahead of
+ the non-preferred one. If there is a base layer (i.e. the MIDX layer
+ is not the first layer in the chain), then if `pack(o1)` appears
+ earlier in that MIDX layer's pack order, then `o1` sorts ahead of
+ `o2`. Likewise if `pack(o2)` appears earlier, then the opposite is
+ true.
+
+3. Otherwise, `o1` and `o2` appear in the same pack, and thus in the
+ same MIDX layer. Sort `o1` and `o2` by their offset within their
+ containing packfile.
+
+Note that the preferred pack is a property of the MIDX chain, not the
+individual layers themselves. Fundamentally we could introduce a
+per-layer preferred pack, but this is less relevant now that we can
+perform multi-pack reuse across the set of packs in a MIDX.
+
+=== Reachability bitmaps and incremental MIDXs
+
+Each layer of an incremental MIDX chain may have its objects (and the
+objects from any previous layer in the same MIDX chain) represented in
+its own `*.bitmap` file.
+
+The structure of a `*.bitmap` file belonging to an incremental MIDX
+chain is identical to that of a non-incremental MIDX bitmap, or a
+classic single-pack bitmap. Since objects are added to the end of the
+incremental MIDX's pseudo-pack order (see above), it is possible to
+extend a bitmap when appending to the end of a MIDX chain.
+
+(Note: it is possible likewise to compress a contiguous sequence of MIDX
+incremental layers, and their `*.bitmap` files into a single layer and
+`*.bitmap`, but this is not yet implemented.)
+
+The object positions used are global within the pseudo-pack order, so
+subsequent layers will have, for example, `m->num_objects_in_base`
+number of `0` bits in each of their four type bitmaps. This follows from
+the fact that we only write type bitmap entries for objects present in
+the layer immediately corresponding to the bitmap).
+
+Note also that only the bitmap pertaining to the most recent layer in an
+incremental MIDX chain is used to store reachability information about
+the interesting and uninteresting objects in a reachability query.
+Earlier bitmap layers are only used to look up commit and pseudo-merge
+bitmaps from that layer, as well as the type-level bitmaps for objects
+in that layer.
+
+To simplify the implementation, type-level bitmaps are iterated
+simultaneously, and their results are OR'd together to avoid recursively
+calling internal bitmap functions.
+
Future Work
-----------
-- The multi-pack-index allows many packfiles, especially in a context
- where repacking is expensive (such as a very large repo), or
- unexpected maintenance time is unacceptable (such as a high-demand
- build machine). However, the multi-pack-index needs to be rewritten
- in full every time. We can extend the format to be incremental, so
- writes are fast. By storing a small "tip" multi-pack-index that
- points to large "base" MIDX files, we can keep writes fast while
- still reducing the number of binary searches required for object
- lookups.
-
- If the multi-pack-index is extended to store a "stable object order"
(a function Order(hash) = integer that is constant for a given hash,
even as the multi-pack-index is updated) then MIDX bitmaps could be
diff --git a/Documentation/technical/sparse-checkout.adoc b/Documentation/technical/sparse-checkout.adoc
index d968659354..dc2e763bbe 100644
--- a/Documentation/technical/sparse-checkout.adoc
+++ b/Documentation/technical/sparse-checkout.adoc
@@ -356,8 +356,6 @@ understanding these differences can be beneficial.
The behavior for these commands somewhat depends upon the merge
strategy being used:
* `ort` behaves as described above
- * `recursive` tries to not vivify files unnecessarily, but does sometimes
- vivify files without conflicts.
* `octopus` and `resolve` will always vivify any file changed in the merge
relative to the first parent, which is rather suboptimal.
diff --git a/Makefile b/Makefile
index ac32d2d0bd..0b46dee0da 100644
--- a/Makefile
+++ b/Makefile
@@ -955,7 +955,7 @@ FOUND_SOURCE_FILES := $(filter-out $(GENERATED_H),$(shell $(SOURCES_CMD)))
FOUND_C_SOURCES = $(filter %.c,$(FOUND_SOURCE_FILES))
FOUND_H_SOURCES = $(filter %.h,$(FOUND_SOURCE_FILES))
-COCCI_SOURCES = $(filter-out $(THIRD_PARTY_SOURCES),$(FOUND_C_SOURCES))
+COCCI_SOURCES = $(filter-out $(THIRD_PARTY_SOURCES) reftable/%,$(FOUND_C_SOURCES))
LIB_H = $(FOUND_H_SOURCES)
@@ -994,6 +994,7 @@ LIB_OBJS += common-exit.o
LIB_OBJS += common-init.o
LIB_OBJS += compat/nonblock.o
LIB_OBJS += compat/obstack.o
+LIB_OBJS += compat/open.o
LIB_OBJS += compat/terminal.o
LIB_OBJS += compiler-tricks/not-constant.o
LIB_OBJS += config.o
@@ -1042,6 +1043,7 @@ LIB_OBJS += gpg-interface.o
LIB_OBJS += graph.o
LIB_OBJS += grep.o
LIB_OBJS += hash-lookup.o
+LIB_OBJS += hash.o
LIB_OBJS += hashmap.o
LIB_OBJS += help.o
LIB_OBJS += hex.o
@@ -1069,7 +1071,6 @@ LIB_OBJS += merge-blobs.o
LIB_OBJS += merge-ll.o
LIB_OBJS += merge-ort.o
LIB_OBJS += merge-ort-wrappers.o
-LIB_OBJS += merge-recursive.o
LIB_OBJS += merge.o
LIB_OBJS += midx.o
LIB_OBJS += midx-write.o
@@ -1084,6 +1085,7 @@ LIB_OBJS += notes.o
LIB_OBJS += object-file-convert.o
LIB_OBJS += object-file.o
LIB_OBJS += object-name.o
+LIB_OBJS += object-store.o
LIB_OBJS += object.o
LIB_OBJS += oid-array.o
LIB_OBJS += oidmap.o
@@ -1366,6 +1368,8 @@ CLAR_TEST_SUITES += u-reftable-tree
CLAR_TEST_SUITES += u-strbuf
CLAR_TEST_SUITES += u-strcmp-offset
CLAR_TEST_SUITES += u-strvec
+CLAR_TEST_SUITES += u-trailer
+CLAR_TEST_SUITES += u-urlmatch-normalization
CLAR_TEST_PROG = $(UNIT_TEST_BIN)/unit-tests$(X)
CLAR_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(CLAR_TEST_SUITES))
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
@@ -1380,8 +1384,6 @@ UNIT_TEST_PROGRAMS += t-reftable-reader
UNIT_TEST_PROGRAMS += t-reftable-readwrite
UNIT_TEST_PROGRAMS += t-reftable-record
UNIT_TEST_PROGRAMS += t-reftable-stack
-UNIT_TEST_PROGRAMS += t-trailer
-UNIT_TEST_PROGRAMS += t-urlmatch-normalization
UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/lib-reftable.o
@@ -1411,7 +1413,7 @@ ARFLAGS = rcs
PTHREAD_CFLAGS =
# For the 'sparse' target
-SPARSE_FLAGS ?= -std=gnu99
+SPARSE_FLAGS ?= -std=gnu99 -D__STDC_NO_VLA__
SP_EXTRA_FLAGS =
# For informing GIT-BUILD-OPTIONS of the SANITIZE=leak,address targets
@@ -1811,7 +1813,6 @@ ifdef FREAD_READS_DIRECTORIES
endif
ifdef OPEN_RETURNS_EINTR
COMPAT_CFLAGS += -DOPEN_RETURNS_EINTR
- COMPAT_OBJS += compat/open.o
endif
ifdef NO_SYMLINK_HEAD
BASIC_CFLAGS += -DNO_SYMLINK_HEAD
@@ -2263,6 +2264,10 @@ ifdef WITH_BREAKING_CHANGES
BASIC_CFLAGS += -DWITH_BREAKING_CHANGES
endif
+ifdef CHECK_ASSERTION_SIDE_EFFECTS
+ BASIC_CFLAGS += -DCHECK_ASSERTION_SIDE_EFFECTS
+endif
+
ifdef INCLUDE_LIBGIT_RS
# Enable symbol hiding in contrib/libgit-sys/libgitpub.a without making
# us rebuild the whole tree every time we run a Rust build.
diff --git a/advice.c b/advice.c
index 1df43b7536..e5f0ff8449 100644
--- a/advice.c
+++ b/advice.c
@@ -51,6 +51,7 @@ static struct {
[ADVICE_AM_WORK_DIR] = { "amWorkDir" },
[ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME] = { "checkoutAmbiguousRemoteBranchName" },
[ADVICE_COMMIT_BEFORE_MERGE] = { "commitBeforeMerge" },
+ [ADVICE_DEFAULT_BRANCH_NAME] = { "defaultBranchName" },
[ADVICE_DETACHED_HEAD] = { "detachedHead" },
[ADVICE_DIVERGING] = { "diverging" },
[ADVICE_FETCH_SET_HEAD_WARN] = { "fetchRemoteHEADWarn" },
diff --git a/advice.h b/advice.h
index d233cfc693..727dcecf4a 100644
--- a/advice.h
+++ b/advice.h
@@ -18,6 +18,7 @@ enum advice_type {
ADVICE_AM_WORK_DIR,
ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME,
ADVICE_COMMIT_BEFORE_MERGE,
+ ADVICE_DEFAULT_BRANCH_NAME,
ADVICE_DETACHED_HEAD,
ADVICE_DIVERGING,
ADVICE_FETCH_SET_HEAD_WARN,
diff --git a/apply.c b/apply.c
index a850c7d75f..381d2e3652 100644
--- a/apply.c
+++ b/apply.c
@@ -14,7 +14,7 @@
#include "abspath.h"
#include "base85.h"
#include "config.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "delta.h"
#include "diff.h"
#include "dir.h"
diff --git a/archive-tar.c b/archive-tar.c
index 0edf13fba7..282b48196f 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -11,7 +11,7 @@
#include "hex.h"
#include "tar.h"
#include "archive.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "strbuf.h"
#include "streaming.h"
#include "run-command.h"
diff --git a/archive-zip.c b/archive-zip.c
index 9f32730181..405da6f3d8 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -12,7 +12,7 @@
#include "hex.h"
#include "streaming.h"
#include "utf8.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "strbuf.h"
#include "userdiff.h"
#include "write-or-die.h"
diff --git a/archive.c b/archive.c
index 67bba3cd30..8309ea213e 100644
--- a/archive.c
+++ b/archive.c
@@ -14,7 +14,7 @@
#include "pretty.h"
#include "setup.h"
#include "refs.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "commit.h"
#include "tree.h"
#include "tree-walk.h"
@@ -216,7 +216,7 @@ static int write_archive_entry(const struct object_id *oid, const char *base,
/* Stream it? */
if (S_ISREG(mode) && !args->convert &&
oid_object_info(args->repo, oid, &size) == OBJ_BLOB &&
- size > big_file_threshold)
+ size > repo_settings_get_big_file_threshold(the_repository))
return write_entry(args, oid, path.buf, path.len, mode, NULL, size);
buffer = object_file_to_archive(args, path.buf, oid, mode, &type, &size);
@@ -312,7 +312,7 @@ int write_archive_entries(struct archiver_args *args,
struct object_id fake_oid;
int i;
- oidcpy(&fake_oid, null_oid());
+ oidcpy(&fake_oid, null_oid(the_hash_algo));
if (args->baselen > 0 && args->base[args->baselen - 1] == '/') {
size_t len = args->baselen;
diff --git a/attr.c b/attr.c
index 0bd2750528..86b6109fc4 100644
--- a/attr.c
+++ b/attr.c
@@ -22,7 +22,7 @@
#include "read-cache-ll.h"
#include "refs.h"
#include "revision.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "setup.h"
#include "thread-utils.h"
#include "tree-walk.h"
diff --git a/bisect.c b/bisect.c
index 269a98bf97..a327468c75 100644
--- a/bisect.c
+++ b/bisect.c
@@ -20,7 +20,7 @@
#include "commit-slab.h"
#include "commit-reach.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "path.h"
#include "dir.h"
diff --git a/blame.c b/blame.c
index a15ddf9333..57daa45e89 100644
--- a/blame.c
+++ b/blame.c
@@ -3,7 +3,7 @@
#include "git-compat-util.h"
#include "refs.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "cache-tree.h"
#include "mergesort.h"
#include "commit.h"
@@ -255,7 +255,7 @@ static struct commit *fake_working_tree_commit(struct repository *r,
switch (st.st_mode & S_IFMT) {
case S_IFREG:
if (opt->flags.allow_textconv &&
- textconv_object(r, read_from, mode, null_oid(), 0, &buf_ptr, &buf_len))
+ textconv_object(r, read_from, mode, null_oid(the_hash_algo), 0, &buf_ptr, &buf_len))
strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
die_errno("cannot open or read '%s'", read_from);
@@ -277,7 +277,7 @@ static struct commit *fake_working_tree_commit(struct repository *r,
convert_to_git(r->index, path, buf.buf, buf.len, &buf, 0);
origin->file.ptr = buf.buf;
origin->file.size = buf.len;
- pretend_object_file(buf.buf, buf.len, OBJ_BLOB, &origin->blob_oid);
+ pretend_object_file(the_repository, buf.buf, buf.len, OBJ_BLOB, &origin->blob_oid);
/*
* Read the current index, replace the path entry with
diff --git a/branch.c b/branch.c
index 91297d55ac..6d01d7d6bd 100644
--- a/branch.c
+++ b/branch.c
@@ -633,7 +633,7 @@ void create_branch(struct repository *r,
0, &err);
if (!transaction ||
ref_transaction_update(transaction, ref.buf,
- &oid, forcing ? NULL : null_oid(),
+ &oid, forcing ? NULL : null_oid(the_hash_algo),
NULL, NULL, flags, msg, &err) ||
ref_transaction_commit(transaction, &err))
die("%s", err.buf);
diff --git a/builtin/backfill.c b/builtin/backfill.c
index d95d7a2d4d..fa82ad2f6f 100644
--- a/builtin/backfill.c
+++ b/builtin/backfill.c
@@ -13,7 +13,7 @@
#include "tree.h"
#include "tree-walk.h"
#include "object.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "oid-array.h"
#include "oidset.h"
#include "promisor-remote.h"
diff --git a/builtin/blame.c b/builtin/blame.c
index c470654c7e..5bf11c2a3c 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -28,7 +28,7 @@
#include "line-log.h"
#include "progress.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "pager.h"
#include "blame.h"
#include "refs.h"
@@ -351,6 +351,19 @@ static void emit_porcelain_details(struct blame_origin *suspect, int repeat)
write_filename_info(suspect);
}
+/*
+ * Information which needs to be printed per-line goes here. Any
+ * information which can be clubbed on a commit/file level, should
+ * be printed via 'emit_one_suspect_detail()'.
+ */
+static void emit_porcelain_per_line_details(struct blame_entry *ent)
+{
+ if (mark_unblamable_lines && ent->unblamable)
+ puts("unblamable");
+ if (mark_ignored_lines && ent->ignored)
+ puts("ignored");
+}
+
static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent,
int opt)
{
@@ -367,6 +380,7 @@ static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent,
ent->lno + 1,
ent->num_lines);
emit_porcelain_details(suspect, repeat);
+ emit_porcelain_per_line_details(ent);
cp = blame_nth_line(sb, ent->lno);
for (cnt = 0; cnt < ent->num_lines; cnt++) {
@@ -377,6 +391,7 @@ static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent,
ent->lno + 1 + cnt);
if (repeat)
emit_porcelain_details(suspect, 1);
+ emit_porcelain_per_line_details(ent);
}
putchar('\t');
do {
diff --git a/builtin/bugreport.c b/builtin/bugreport.c
index 66d64bfd5a..f78c3f2aed 100644
--- a/builtin/bugreport.c
+++ b/builtin/bugreport.c
@@ -4,13 +4,13 @@
#include "editor.h"
#include "gettext.h"
#include "parse-options.h"
+#include "path.h"
#include "strbuf.h"
#include "help.h"
#include "compat/compiler.h"
#include "hook.h"
#include "hook-list.h"
#include "diagnose.h"
-#include "object-file.h"
#include "setup.h"
#include "version.h"
@@ -141,7 +141,7 @@ int cmd_bugreport(int argc,
}
strbuf_addstr(&report_path, ".txt");
- switch (safe_create_leading_directories(report_path.buf)) {
+ switch (safe_create_leading_directories(the_repository, report_path.buf)) {
case SCLD_OK:
case SCLD_EXISTS:
break;
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index b13561cf73..0e3f10a946 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -15,14 +15,16 @@
#include "gettext.h"
#include "hex.h"
#include "ident.h"
+#include "list-objects-filter-options.h"
#include "parse-options.h"
#include "userdiff.h"
#include "streaming.h"
#include "oid-array.h"
#include "packfile.h"
+#include "pack-bitmap.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "replace-object.h"
#include "promisor-remote.h"
#include "mailmap.h"
@@ -35,6 +37,7 @@ enum batch_mode {
};
struct batch_options {
+ struct list_objects_filter_options objects_filter;
int enabled;
int follow_symlinks;
enum batch_mode batch_mode;
@@ -455,6 +458,16 @@ static void print_default_format(struct strbuf *scratch, struct expand_data *dat
(uintmax_t)data->size, opt->output_delim);
}
+static void report_object_status(struct batch_options *opt,
+ const char *obj_name,
+ const struct object_id *oid,
+ const char *status)
+{
+ printf("%s %s%c", obj_name ? obj_name : oid_to_hex(oid),
+ status, opt->output_delim);
+ fflush(stdout);
+}
+
/*
* If "pack" is non-NULL, then "offset" is the byte offset within the pack from
* which the object may be accessed (though note that we may also rely on
@@ -470,8 +483,13 @@ static void batch_object_write(const char *obj_name,
if (!data->skip_object_info) {
int ret;
- if (use_mailmap)
+ if (use_mailmap ||
+ opt->objects_filter.choice == LOFC_BLOB_NONE ||
+ opt->objects_filter.choice == LOFC_BLOB_LIMIT ||
+ opt->objects_filter.choice == LOFC_OBJECT_TYPE)
data->info.typep = &data->type;
+ if (opt->objects_filter.choice == LOFC_BLOB_LIMIT)
+ data->info.sizep = &data->size;
if (pack)
ret = packed_object_info(the_repository, pack, offset,
@@ -481,12 +499,42 @@ static void batch_object_write(const char *obj_name,
&data->oid, &data->info,
OBJECT_INFO_LOOKUP_REPLACE);
if (ret < 0) {
- printf("%s missing%c",
- obj_name ? obj_name : oid_to_hex(&data->oid), opt->output_delim);
- fflush(stdout);
+ report_object_status(opt, obj_name, &data->oid, "missing");
return;
}
+ switch (opt->objects_filter.choice) {
+ case LOFC_DISABLED:
+ break;
+ case LOFC_BLOB_NONE:
+ if (data->type == OBJ_BLOB) {
+ if (!opt->all_objects)
+ report_object_status(opt, obj_name,
+ &data->oid, "excluded");
+ return;
+ }
+ break;
+ case LOFC_BLOB_LIMIT:
+ if (data->type == OBJ_BLOB &&
+ data->size >= opt->objects_filter.blob_limit_value) {
+ if (!opt->all_objects)
+ report_object_status(opt, obj_name,
+ &data->oid, "excluded");
+ return;
+ }
+ break;
+ case LOFC_OBJECT_TYPE:
+ if (data->type != opt->objects_filter.object_type) {
+ if (!opt->all_objects)
+ report_object_status(opt, obj_name,
+ &data->oid, "excluded");
+ return;
+ }
+ break;
+ default:
+ BUG("unsupported objects filter");
+ }
+
if (use_mailmap && (data->type == OBJ_COMMIT || data->type == OBJ_TAG)) {
size_t s = data->size;
char *buf = NULL;
@@ -535,10 +583,10 @@ static void batch_one_object(const char *obj_name,
if (result != FOUND) {
switch (result) {
case MISSING_OBJECT:
- printf("%s missing%c", obj_name, opt->output_delim);
+ report_object_status(opt, obj_name, &data->oid, "missing");
break;
case SHORT_NAME_AMBIGUOUS:
- printf("%s ambiguous%c", obj_name, opt->output_delim);
+ report_object_status(opt, obj_name, &data->oid, "ambiguous");
break;
case DANGLING_SYMLINK:
printf("dangling %"PRIuMAX"%c%s%c",
@@ -595,25 +643,18 @@ static int batch_object_cb(const struct object_id *oid, void *vdata)
return 0;
}
-static int collect_loose_object(const struct object_id *oid,
- const char *path UNUSED,
- void *data)
-{
- oid_array_append(data, oid);
- return 0;
-}
-
-static int collect_packed_object(const struct object_id *oid,
- struct packed_git *pack UNUSED,
- uint32_t pos UNUSED,
- void *data)
+static int collect_object(const struct object_id *oid,
+ struct packed_git *pack UNUSED,
+ off_t offset UNUSED,
+ void *data)
{
oid_array_append(data, oid);
return 0;
}
static int batch_unordered_object(const struct object_id *oid,
- struct packed_git *pack, off_t offset,
+ struct packed_git *pack,
+ off_t offset,
void *vdata)
{
struct object_cb_data *data = vdata;
@@ -627,23 +668,6 @@ static int batch_unordered_object(const struct object_id *oid,
return 0;
}
-static int batch_unordered_loose(const struct object_id *oid,
- const char *path UNUSED,
- void *data)
-{
- return batch_unordered_object(oid, NULL, 0, data);
-}
-
-static int batch_unordered_packed(const struct object_id *oid,
- struct packed_git *pack,
- uint32_t pos,
- void *data)
-{
- return batch_unordered_object(oid, pack,
- nth_packed_object_offset(pack, pos),
- data);
-}
-
typedef void (*parse_cmd_fn_t)(struct batch_options *, const char *,
struct strbuf *, struct expand_data *);
@@ -776,6 +800,76 @@ static void batch_objects_command(struct batch_options *opt,
#define DEFAULT_FORMAT "%(objectname) %(objecttype) %(objectsize)"
+typedef int (*for_each_object_fn)(const struct object_id *oid, struct packed_git *pack,
+ off_t offset, void *data);
+
+struct for_each_object_payload {
+ for_each_object_fn callback;
+ void *payload;
+};
+
+static int batch_one_object_loose(const struct object_id *oid,
+ const char *path UNUSED,
+ void *_payload)
+{
+ struct for_each_object_payload *payload = _payload;
+ return payload->callback(oid, NULL, 0, payload->payload);
+}
+
+static int batch_one_object_packed(const struct object_id *oid,
+ struct packed_git *pack,
+ uint32_t pos,
+ void *_payload)
+{
+ struct for_each_object_payload *payload = _payload;
+ return payload->callback(oid, pack, nth_packed_object_offset(pack, pos),
+ payload->payload);
+}
+
+static int batch_one_object_bitmapped(const struct object_id *oid,
+ enum object_type type UNUSED,
+ int flags UNUSED,
+ uint32_t hash UNUSED,
+ struct packed_git *pack,
+ off_t offset,
+ void *_payload)
+{
+ struct for_each_object_payload *payload = _payload;
+ return payload->callback(oid, pack, offset, payload->payload);
+}
+
+static void batch_each_object(struct batch_options *opt,
+ for_each_object_fn callback,
+ unsigned flags,
+ void *_payload)
+{
+ struct for_each_object_payload payload = {
+ .callback = callback,
+ .payload = _payload,
+ };
+ struct bitmap_index *bitmap = prepare_bitmap_git(the_repository);
+
+ for_each_loose_object(batch_one_object_loose, &payload, 0);
+
+ if (bitmap && !for_each_bitmapped_object(bitmap, &opt->objects_filter,
+ batch_one_object_bitmapped, &payload)) {
+ struct packed_git *pack;
+
+ for (pack = get_all_packs(the_repository); pack; pack = pack->next) {
+ if (bitmap_index_contains_pack(bitmap, pack) ||
+ open_pack_index(pack))
+ continue;
+ for_each_object_in_pack(pack, batch_one_object_packed,
+ &payload, flags);
+ }
+ } else {
+ for_each_packed_object(the_repository, batch_one_object_packed,
+ &payload, flags);
+ }
+
+ free_bitmap_index(bitmap);
+}
+
static int batch_objects(struct batch_options *opt)
{
struct strbuf input = STRBUF_INIT;
@@ -812,7 +906,8 @@ static int batch_objects(struct batch_options *opt)
struct object_cb_data cb;
struct object_info empty = OBJECT_INFO_INIT;
- if (!memcmp(&data.info, &empty, sizeof(empty)))
+ if (!memcmp(&data.info, &empty, sizeof(empty)) &&
+ opt->objects_filter.choice == LOFC_DISABLED)
data.skip_object_info = 1;
if (repo_has_promisor_remote(the_repository))
@@ -829,18 +924,14 @@ static int batch_objects(struct batch_options *opt)
cb.seen = &seen;
- for_each_loose_object(batch_unordered_loose, &cb, 0);
- for_each_packed_object(the_repository, batch_unordered_packed,
- &cb, FOR_EACH_OBJECT_PACK_ORDER);
+ batch_each_object(opt, batch_unordered_object,
+ FOR_EACH_OBJECT_PACK_ORDER, &cb);
oidset_clear(&seen);
} else {
struct oid_array sa = OID_ARRAY_INIT;
- for_each_loose_object(collect_loose_object, &sa, 0);
- for_each_packed_object(the_repository, collect_packed_object,
- &sa, 0);
-
+ batch_each_object(opt, collect_object, 0, &sa);
oid_array_for_each_unique(&sa, batch_object_cb, &cb);
oid_array_clear(&sa);
@@ -936,12 +1027,15 @@ int cmd_cat_file(int argc,
int opt_cw = 0;
int opt_epts = 0;
const char *exp_type = NULL, *obj_name = NULL;
- struct batch_options batch = {0};
+ struct batch_options batch = {
+ .objects_filter = LIST_OBJECTS_FILTER_INIT,
+ };
int unknown_type = 0;
int input_nul_terminated = 0;
int nul_terminated = 0;
+ int ret;
- const char * const usage[] = {
+ const char * const builtin_catfile_usage[] = {
N_("git cat-file <type> <object>"),
N_("git cat-file (-e | -p) <object>"),
N_("git cat-file (-t | -s) [--allow-unknown-type] <object>"),
@@ -1000,6 +1094,7 @@ int cmd_cat_file(int argc,
N_("run filters on object's content"), 'w'),
OPT_STRING(0, "path", &force_path, N_("blob|tree"),
N_("use a <path> for (--textconv | --filters); Not with 'batch'")),
+ OPT_PARSE_LIST_OBJECTS_FILTER(&batch.objects_filter),
OPT_END()
};
@@ -1007,13 +1102,27 @@ int cmd_cat_file(int argc,
batch.buffer_output = -1;
- argc = parse_options(argc, argv, prefix, options, usage, 0);
+ argc = parse_options(argc, argv, prefix, options, builtin_catfile_usage, 0);
opt_cw = (opt == 'c' || opt == 'w');
opt_epts = (opt == 'e' || opt == 'p' || opt == 't' || opt == 's');
if (use_mailmap)
read_mailmap(&mailmap);
+ switch (batch.objects_filter.choice) {
+ case LOFC_DISABLED:
+ break;
+ case LOFC_BLOB_NONE:
+ case LOFC_BLOB_LIMIT:
+ case LOFC_OBJECT_TYPE:
+ if (!batch.enabled)
+ usage(_("objects filter only supported in batch mode"));
+ break;
+ default:
+ usagef(_("objects filter not supported: '%s'"),
+ list_object_filter_config_name(batch.objects_filter.choice));
+ }
+
/* --batch-all-objects? */
if (opt == 'b')
batch.all_objects = 1;
@@ -1021,7 +1130,7 @@ int cmd_cat_file(int argc,
/* Option compatibility */
if (force_path && !opt_cw)
usage_msg_optf(_("'%s=<%s>' needs '%s' or '%s'"),
- usage, options,
+ builtin_catfile_usage, options,
"--path", _("path|tree-ish"), "--filters",
"--textconv");
@@ -1029,20 +1138,20 @@ int cmd_cat_file(int argc,
if (batch.enabled)
;
else if (batch.follow_symlinks)
- usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
- "--follow-symlinks");
+ usage_msg_optf(_("'%s' requires a batch mode"), builtin_catfile_usage,
+ options, "--follow-symlinks");
else if (batch.buffer_output >= 0)
- usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
- "--buffer");
+ usage_msg_optf(_("'%s' requires a batch mode"), builtin_catfile_usage,
+ options, "--buffer");
else if (batch.all_objects)
- usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
- "--batch-all-objects");
+ usage_msg_optf(_("'%s' requires a batch mode"), builtin_catfile_usage,
+ options, "--batch-all-objects");
else if (input_nul_terminated)
- usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
- "-z");
+ usage_msg_optf(_("'%s' requires a batch mode"), builtin_catfile_usage,
+ options, "-z");
else if (nul_terminated)
- usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
- "-Z");
+ usage_msg_optf(_("'%s' requires a batch mode"), builtin_catfile_usage,
+ options, "-Z");
batch.input_delim = batch.output_delim = '\n';
if (input_nul_terminated)
@@ -1063,33 +1172,37 @@ int cmd_cat_file(int argc,
batch.transform_mode = opt;
else if (opt && opt != 'b')
usage_msg_optf(_("'-%c' is incompatible with batch mode"),
- usage, options, opt);
+ builtin_catfile_usage, options, opt);
else if (argc)
- usage_msg_opt(_("batch modes take no arguments"), usage,
- options);
+ usage_msg_opt(_("batch modes take no arguments"),
+ builtin_catfile_usage, options);
- return batch_objects(&batch);
+ ret = batch_objects(&batch);
+ goto out;
}
if (opt) {
if (!argc && opt == 'c')
usage_msg_optf(_("<rev> required with '%s'"),
- usage, options, "--textconv");
+ builtin_catfile_usage, options,
+ "--textconv");
else if (!argc && opt == 'w')
usage_msg_optf(_("<rev> required with '%s'"),
- usage, options, "--filters");
+ builtin_catfile_usage, options,
+ "--filters");
else if (!argc && opt_epts)
usage_msg_optf(_("<object> required with '-%c'"),
- usage, options, opt);
+ builtin_catfile_usage, options, opt);
else if (argc == 1)
obj_name = argv[0];
else
- usage_msg_opt(_("too many arguments"), usage, options);
+ usage_msg_opt(_("too many arguments"), builtin_catfile_usage,
+ options);
} else if (!argc) {
- usage_with_options(usage, options);
+ usage_with_options(builtin_catfile_usage, options);
} else if (argc != 2) {
usage_msg_optf(_("only two arguments allowed in <type> <object> mode, not %d"),
- usage, options, argc);
+ builtin_catfile_usage, options, argc);
} else if (argc) {
exp_type = argv[0];
obj_name = argv[1];
@@ -1097,5 +1210,10 @@ int cmd_cat_file(int argc,
if (unknown_type && opt != 't' && opt != 's')
die("git cat-file --allow-unknown-type: use with -s or -t");
- return cat_one_file(opt, exp_type, obj_name, unknown_type);
+
+ ret = cat_one_file(opt, exp_type, obj_name, unknown_type);
+
+out:
+ list_objects_filter_release(&batch.objects_filter);
+ return ret;
}
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 01ea9ff8b2..d185982f3a 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -17,9 +17,10 @@
#include "merge-ll.h"
#include "lockfile.h"
#include "mem-pool.h"
-#include "merge-recursive.h"
+#include "merge-ort-wrappers.h"
+#include "object-file.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "parse-options.h"
#include "path.h"
#include "preload-index.h"
@@ -130,8 +131,8 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm
int changed)
{
return run_hooks_l(the_repository, "post-checkout",
- oid_to_hex(old_commit ? &old_commit->object.oid : null_oid()),
- oid_to_hex(new_commit ? &new_commit->object.oid : null_oid()),
+ oid_to_hex(old_commit ? &old_commit->object.oid : null_oid(the_hash_algo)),
+ oid_to_hex(new_commit ? &new_commit->object.oid : null_oid(the_hash_algo)),
changed ? "1" : "0", NULL);
/* "new_commit" can be NULL when checking out from the index before
a commit exists. */
@@ -710,7 +711,7 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
opts.src_index = the_repository->index;
opts.dst_index = the_repository->index;
init_checkout_metadata(&opts.meta, info->refname,
- info->commit ? &info->commit->object.oid : null_oid(),
+ info->commit ? &info->commit->object.oid : null_oid(the_hash_algo),
NULL);
if (parse_tree(tree) < 0)
return 128;
@@ -907,10 +908,10 @@ static int merge_working_tree(const struct checkout_opts *opts,
o.branch1 = new_branch_info->name;
o.branch2 = "local";
o.conflict_style = opts->conflict_style;
- ret = merge_trees(&o,
- new_tree,
- work,
- old_tree);
+ ret = merge_ort_nonrecursive(&o,
+ new_tree,
+ work,
+ old_tree);
if (ret < 0)
exit(128);
ret = reset_tree(new_tree,
diff --git a/builtin/clone.c b/builtin/clone.c
index 9c3547f41e..b6f378c438 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -25,7 +25,7 @@
#include "refs.h"
#include "refspec.h"
#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "tree.h"
#include "tree-walk.h"
#include "unpack-trees.h"
@@ -452,7 +452,9 @@ static struct ref *wanted_peer_refs(struct clone_opts *opts,
if (head)
tail_link_ref(head, &tail);
if (option_single_branch)
- refs = to_free = guess_remote_head(head, refs, 0);
+ refs = to_free =
+ guess_remote_head(head, refs,
+ REMOTE_GUESS_HEAD_QUIET);
} else if (option_single_branch) {
local_refs = NULL;
tail = &local_refs;
@@ -692,7 +694,7 @@ static int checkout(int submodule_progress, int filter_submodules,
if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
- err |= run_hooks_l(the_repository, "post-checkout", oid_to_hex(null_oid()),
+ err |= run_hooks_l(the_repository, "post-checkout", oid_to_hex(null_oid(the_hash_algo)),
oid_to_hex(&oid), "1", NULL);
if (!err && (option_recurse_submodules.nr > 0)) {
@@ -1097,7 +1099,7 @@ int cmd_clone(int argc,
sigchain_push_common(remove_junk_on_signal);
if (!option_bare) {
- if (safe_create_leading_directories_const(work_tree) < 0)
+ if (safe_create_leading_directories_const(the_repository, work_tree) < 0)
die_errno(_("could not create leading directories of '%s'"),
work_tree);
if (dest_exists)
@@ -1118,7 +1120,7 @@ int cmd_clone(int argc,
junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
junk_git_dir = git_dir;
}
- if (safe_create_leading_directories_const(git_dir) < 0)
+ if (safe_create_leading_directories_const(the_repository, git_dir) < 0)
die(_("could not create leading directories of '%s'"), git_dir);
if (0 <= option_verbosity) {
@@ -1532,7 +1534,8 @@ int cmd_clone(int argc,
}
remote_head = find_ref_by_name(refs, "HEAD");
- remote_head_points_at = guess_remote_head(remote_head, mapped_refs, 0);
+ remote_head_points_at = guess_remote_head(remote_head, mapped_refs,
+ REMOTE_GUESS_HEAD_QUIET);
if (option_branch) {
our_head_points_at = find_remote_branch(mapped_refs, option_branch);
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index 8ca75262c5..be06d0a811 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -6,7 +6,7 @@
#include "hex.h"
#include "parse-options.h"
#include "commit-graph.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "progress.h"
#include "replace-object.h"
#include "strbuf.h"
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index c787133d00..ad6b2c9320 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -9,7 +9,7 @@
#include "gettext.h"
#include "hex.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "commit.h"
#include "parse-options.h"
diff --git a/builtin/count-objects.c b/builtin/count-objects.c
index 1e89148ed7..0bb5360b2f 100644
--- a/builtin/count-objects.c
+++ b/builtin/count-objects.c
@@ -12,7 +12,7 @@
#include "parse-options.h"
#include "quote.h"
#include "packfile.h"
-#include "object-store-ll.h"
+#include "object-store.h"
static unsigned long garbage;
static off_t size_garbage;
diff --git a/builtin/credential-cache--daemon.c b/builtin/credential-cache--daemon.c
index e707618e74..5065ff4660 100644
--- a/builtin/credential-cache--daemon.c
+++ b/builtin/credential-cache--daemon.c
@@ -2,8 +2,8 @@
#include "builtin.h"
#include "abspath.h"
#include "gettext.h"
-#include "object-file.h"
#include "parse-options.h"
+#include "path.h"
#ifndef NO_UNIX_SOCKETS
@@ -271,7 +271,7 @@ static void init_socket_directory(const char *path)
* condition in which somebody can chdir to it, sleep, then try to open
* our protected socket.
*/
- if (safe_create_leading_directories_const(dir) < 0)
+ if (safe_create_leading_directories_const(the_repository, dir) < 0)
die_errno("unable to create directories for '%s'", dir);
if (mkdir(dir, 0700) < 0)
die_errno("unable to mkdir '%s'", dir);
diff --git a/builtin/describe.c b/builtin/describe.c
index 2da9f4fed0..2d50883b72 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -19,7 +19,7 @@
#include "setup.h"
#include "strvec.h"
#include "run-command.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "list-objects.h"
#include "commit-slab.h"
#include "wildmatch.h"
@@ -518,7 +518,7 @@ static void describe_blob(struct object_id oid, struct strbuf *dst)
{
struct rev_info revs;
struct strvec args = STRVEC_INIT;
- struct process_commit_data pcd = { *null_oid(), oid, dst, &revs};
+ struct process_commit_data pcd = { *null_oid(the_hash_algo), oid, dst, &revs};
strvec_pushl(&args, "internal: The first arg is not parsed",
"--objects", "--in-commit-order", "--reverse", "HEAD",
diff --git a/builtin/diagnose.c b/builtin/diagnose.c
index 33c39bd598..ec86d66389 100644
--- a/builtin/diagnose.c
+++ b/builtin/diagnose.c
@@ -3,8 +3,8 @@
#include "builtin.h"
#include "abspath.h"
#include "gettext.h"
-#include "object-file.h"
#include "parse-options.h"
+#include "path.h"
#include "diagnose.h"
static const char * const diagnose_usage[] = {
@@ -50,7 +50,7 @@ int cmd_diagnose(int argc,
strbuf_addftime(&zip_path, option_suffix, localtime_r(&now, &tm), 0, 0);
strbuf_addstr(&zip_path, ".zip");
- switch (safe_create_leading_directories(zip_path.buf)) {
+ switch (safe_create_leading_directories(the_repository, zip_path.buf)) {
case SCLD_OK:
case SCLD_EXISTS:
break;
diff --git a/builtin/diff.c b/builtin/diff.c
index a4fffee42c..fa963808c3 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -104,7 +104,7 @@ static void builtin_diff_b_f(struct rev_info *revs,
stuff_change(&revs->diffopt,
blob[0]->mode, canon_mode(st.st_mode),
- &blob[0]->item->oid, null_oid(),
+ &blob[0]->item->oid, null_oid(the_hash_algo),
1, 0,
blob[0]->path ? blob[0]->path : path,
path);
@@ -498,7 +498,8 @@ int cmd_diff(int argc,
/* If this is a no-index diff, just run it and exit there. */
if (no_index)
- exit(diff_no_index(&rev, no_index == DIFF_NO_INDEX_IMPLICIT,
+ exit(diff_no_index(&rev, the_repository->hash_algo,
+ no_index == DIFF_NO_INDEX_IMPLICIT,
argc, argv));
diff --git a/builtin/difftool.c b/builtin/difftool.c
index 41cd00066c..f17a55b3cf 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -22,6 +22,7 @@
#include "gettext.h"
#include "hex.h"
#include "parse-options.h"
+#include "path.h"
#include "read-cache-ll.h"
#include "repository.h"
#include "sparse-index.h"
@@ -29,7 +30,7 @@
#include "strbuf.h"
#include "lockfile.h"
#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "dir.h"
#include "entry.h"
#include "setup.h"
@@ -271,9 +272,9 @@ static void changed_files(struct repository *repo,
strbuf_release(&buf);
}
-static int ensure_leading_directories(char *path)
+static int ensure_leading_directories(struct repository *repo, char *path)
{
- switch (safe_create_leading_directories(path)) {
+ switch (safe_create_leading_directories(repo, path)) {
case SCLD_OK:
case SCLD_EXISTS:
return 0;
@@ -341,11 +342,12 @@ static int checkout_path(unsigned mode, struct object_id *oid,
return ret;
}
-static void write_file_in_directory(struct strbuf *dir, size_t dir_len,
- const char *path, const char *content)
+static void write_file_in_directory(struct repository *repo,
+ struct strbuf *dir, size_t dir_len,
+ const char *path, const char *content)
{
add_path(dir, dir_len, path);
- ensure_leading_directories(dir->buf);
+ ensure_leading_directories(repo, dir->buf);
unlink(dir->buf);
write_file(dir->buf, "%s", content);
}
@@ -356,14 +358,15 @@ static void write_file_in_directory(struct strbuf *dir, size_t dir_len,
* as text files, resulting in behavior that is analogous to what "git diff"
* displays for symlink and submodule diffs.
*/
-static void write_standin_files(struct pair_entry *entry,
- struct strbuf *ldir, size_t ldir_len,
- struct strbuf *rdir, size_t rdir_len)
+static void write_standin_files(struct repository *repo,
+ struct pair_entry *entry,
+ struct strbuf *ldir, size_t ldir_len,
+ struct strbuf *rdir, size_t rdir_len)
{
if (*entry->left)
- write_file_in_directory(ldir, ldir_len, entry->path, entry->left);
+ write_file_in_directory(repo, ldir, ldir_len, entry->path, entry->left);
if (*entry->right)
- write_file_in_directory(rdir, rdir_len, entry->path, entry->right);
+ write_file_in_directory(repo, rdir, rdir_len, entry->path, entry->right);
}
static int run_dir_diff(struct repository *repo,
@@ -533,7 +536,7 @@ static int run_dir_diff(struct repository *repo,
ADD_CACHE_JUST_APPEND);
add_path(&rdir, rdir_len, dst_path);
- if (ensure_leading_directories(rdir.buf)) {
+ if (ensure_leading_directories(repo, rdir.buf)) {
ret = error("could not create "
"directory for '%s'",
dst_path);
@@ -576,7 +579,7 @@ static int run_dir_diff(struct repository *repo,
*/
hashmap_for_each_entry(&submodules, &iter, entry,
entry /* member name */) {
- write_standin_files(entry, &ldir, ldir_len, &rdir, rdir_len);
+ write_standin_files(repo, entry, &ldir, ldir_len, &rdir, rdir_len);
}
/*
@@ -587,7 +590,7 @@ static int run_dir_diff(struct repository *repo,
hashmap_for_each_entry(&symlinks2, &iter, entry,
entry /* member name */) {
- write_standin_files(entry, &ldir, ldir_len, &rdir, rdir_len);
+ write_standin_files(repo, entry, &ldir, ldir_len, &rdir, rdir_len);
}
strbuf_setlen(&ldir, ldir_len);
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 126980f724..afacd228b5 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -14,7 +14,7 @@
#include "refs.h"
#include "refspec.h"
#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "commit.h"
#include "object.h"
#include "tag.h"
@@ -949,7 +949,7 @@ static void handle_tag(const char *name, struct tag *tag)
p = rewrite_commit((struct commit *)tagged);
if (!p) {
printf("reset %s\nfrom %s\n\n",
- name, oid_to_hex(null_oid()));
+ name, oid_to_hex(null_oid(the_hash_algo)));
free(buf);
return;
}
@@ -963,7 +963,7 @@ static void handle_tag(const char *name, struct tag *tag)
if (tagged->type == OBJ_TAG) {
printf("reset %s\nfrom %s\n\n",
- name, oid_to_hex(null_oid()));
+ name, oid_to_hex(null_oid(the_hash_algo)));
}
skip_prefix(name, "refs/tags/", &name);
printf("tag %s\n", name);
@@ -1103,7 +1103,7 @@ static void handle_tags_and_duplicates(struct string_list *extras)
* it.
*/
printf("reset %s\nfrom %s\n\n",
- name, oid_to_hex(null_oid()));
+ name, oid_to_hex(null_oid(the_hash_algo)));
continue;
}
@@ -1122,7 +1122,7 @@ static void handle_tags_and_duplicates(struct string_list *extras)
if (!reference_excluded_commits) {
/* delete the ref */
printf("reset %s\nfrom %s\n\n",
- name, oid_to_hex(null_oid()));
+ name, oid_to_hex(null_oid(the_hash_algo)));
continue;
}
/* set ref to commit using oid, not mark */
@@ -1233,7 +1233,7 @@ static void handle_deletes(void)
continue;
printf("reset %s\nfrom %s\n\n",
- refspec->dst, oid_to_hex(null_oid()));
+ refspec->dst, oid_to_hex(null_oid(the_hash_algo)));
}
}
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index e432e8d5a1..c1e198f4e3 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -24,7 +24,7 @@
#include "packfile.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "mem-pool.h"
#include "commit-reach.h"
#include "khash.h"
@@ -770,7 +770,7 @@ static void start_packfile(void)
p->pack_fd = pack_fd;
p->do_not_close = 1;
p->repo = the_repository;
- pack_file = hashfd(pack_fd, p->pack_name);
+ pack_file = hashfd(the_repository->hash_algo, pack_fd, p->pack_name);
pack_data = p;
pack_size = write_pack_header(pack_file, 0);
@@ -798,7 +798,7 @@ static const char *create_index(void)
if (c != last)
die("internal consistency error creating the index");
- tmpfile = write_idx_file(the_hash_algo, NULL, idx, object_count,
+ tmpfile = write_idx_file(the_repository, NULL, idx, object_count,
&pack_idx_opts, pack_data->hash);
free(idx);
return tmpfile;
@@ -1720,7 +1720,7 @@ static void dump_marks(void)
if (!export_marks_file || (import_marks_file && !import_marks_file_done))
return;
- if (safe_create_leading_directories_const(export_marks_file)) {
+ if (safe_create_leading_directories_const(the_repository, export_marks_file)) {
failure |= error_errno("unable to create leading directories of %s",
export_marks_file);
return;
@@ -2021,7 +2021,7 @@ static void parse_and_store_blob(
static struct strbuf buf = STRBUF_INIT;
uintmax_t len;
- if (parse_data(&buf, big_file_threshold, &len))
+ if (parse_data(&buf, repo_settings_get_big_file_threshold(the_repository), &len))
store_object(OBJ_BLOB, &buf, last, oidout, mark);
else {
if (last) {
@@ -3425,7 +3425,7 @@ static int parse_one_option(const char *option)
unsigned long v;
if (!git_parse_ulong(option, &v))
return 0;
- big_file_threshold = v;
+ repo_settings_set_big_file_threshold(the_repository, v);
} else if (skip_prefix(option, "depth=", &option)) {
option_depth(option);
} else if (skip_prefix(option, "active-branches=", &option)) {
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 3a5159d9e6..5279997c96 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -14,7 +14,7 @@
#include "refs.h"
#include "refspec.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "oidset.h"
#include "oid-array.h"
#include "commit.h"
@@ -586,7 +586,7 @@ static struct ref *get_ref_map(struct remote *remote,
struct refspec_item tag_refspec;
/* also fetch all tags */
- refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
+ refspec_item_init_push(&tag_refspec, TAG_REFSPEC);
get_fetch_map(remote_refs, &tag_refspec, &tail, 0);
refspec_item_clear(&tag_refspec);
} else if (tags == TAGS_DEFAULT && *autotags) {
@@ -687,7 +687,7 @@ static int s_update_ref(const char *action,
switch (ref_transaction_commit(our_transaction, &err)) {
case 0:
break;
- case TRANSACTION_NAME_CONFLICT:
+ case REF_TRANSACTION_ERROR_NAME_CONFLICT:
ret = STORE_REF_ERROR_DF_CONFLICT;
goto out;
default:
@@ -1638,14 +1638,11 @@ static int set_head(const struct ref *remote_refs, struct remote *remote)
get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
- fetch_map, 1);
+ fetch_map, REMOTE_GUESS_HEAD_ALL);
for (ref = matches; ref; ref = ref->next) {
string_list_append(&heads, strip_refshead(ref->name));
}
- if (follow_remote_head == FOLLOW_REMOTE_NEVER)
- goto cleanup;
-
if (!heads.nr)
result = 1;
else if (heads.nr > 1)
@@ -1691,21 +1688,6 @@ cleanup:
return result;
}
-static int uses_remote_tracking(struct transport *transport, struct refspec *rs)
-{
- if (!remote_is_configured(transport->remote, 0))
- return 0;
-
- if (!rs->nr)
- rs = &transport->remote->fetch;
-
- for (int i = 0; i < rs->nr; i++)
- if (rs->items[i].dst)
- return 1;
-
- return 0;
-}
-
static int do_fetch(struct transport *transport,
struct refspec *rs,
const struct fetch_config *config)
@@ -1720,6 +1702,7 @@ static int do_fetch(struct transport *transport,
TRANSPORT_LS_REFS_OPTIONS_INIT;
struct fetch_head fetch_head = { 0 };
struct strbuf err = STRBUF_INIT;
+ int do_set_head = 0;
if (tags == TAGS_DEFAULT) {
if (transport->remote->fetch_tags == 2)
@@ -1740,9 +1723,12 @@ static int do_fetch(struct transport *transport,
} else {
struct branch *branch = branch_get(NULL);
- if (transport->remote->fetch.nr)
+ if (transport->remote->fetch.nr) {
refspec_ref_prefixes(&transport->remote->fetch,
&transport_ls_refs_options.ref_prefixes);
+ if (transport->remote->follow_remote_head != FOLLOW_REMOTE_NEVER)
+ do_set_head = 1;
+ }
if (branch_has_merge_config(branch) &&
!strcmp(branch->remote_name, transport->remote->name)) {
int i;
@@ -1765,8 +1751,7 @@ static int do_fetch(struct transport *transport,
strvec_push(&transport_ls_refs_options.ref_prefixes,
"refs/tags/");
- if (transport_ls_refs_options.ref_prefixes.nr &&
- uses_remote_tracking(transport, rs))
+ if (do_set_head)
strvec_push(&transport_ls_refs_options.ref_prefixes,
"HEAD");
@@ -1859,8 +1844,15 @@ static int do_fetch(struct transport *transport,
goto cleanup;
retcode = ref_transaction_commit(transaction, &err);
- if (retcode)
+ if (retcode) {
+ /*
+ * Explicitly handle transaction cleanup to avoid
+ * aborting an already closed transaction.
+ */
+ ref_transaction_free(transaction);
+ transaction = NULL;
goto cleanup;
+ }
}
commit_fetch_head(&fetch_head);
@@ -1918,12 +1910,13 @@ static int do_fetch(struct transport *transport,
"you need to specify exactly one branch with the --set-upstream option"));
}
}
- if (set_head(remote_refs, transport->remote))
- ;
+ if (do_set_head) {
/*
- * Way too many cases where this can go wrong
- * so let's just fail silently for now.
+ * Way too many cases where this can go wrong so let's just
+ * ignore errors and fail silently for now.
*/
+ set_head(remote_refs, transport->remote);
+ }
cleanup:
if (retcode) {
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 8fbd1d6334..6cac28356c 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -17,7 +17,7 @@
#include "packfile.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "path.h"
#include "read-cache-ll.h"
#include "replace-object.h"
@@ -332,7 +332,7 @@ static void check_unreachable_object(struct object *obj)
describe_object(&obj->oid));
FILE *f;
- if (safe_create_leading_directories_const(filename)) {
+ if (safe_create_leading_directories_const(the_repository, filename)) {
error(_("could not create lost-found"));
free(filename);
return;
@@ -400,12 +400,12 @@ static void check_connectivity(void)
}
/* Look up all the requirements, warn about missing objects.. */
- max = get_max_object_index();
+ max = get_max_object_index(the_repository);
if (verbose)
fprintf_ln(stderr, _("Checking connectivity (%d objects)"), max);
for (i = 0; i < max; i++) {
- struct object *obj = get_indexed_object(i);
+ struct object *obj = get_indexed_object(the_repository, i);
if (obj)
check_object(obj);
@@ -626,7 +626,7 @@ static int fsck_loose(const struct object_id *oid, const char *path, void *data)
void *contents = NULL;
int eaten;
struct object_info oi = OBJECT_INFO_INIT;
- struct object_id real_oid = *null_oid();
+ struct object_id real_oid = *null_oid(the_hash_algo);
int err = 0;
strbuf_reset(&cb_data->obj_type);
diff --git a/builtin/gc.c b/builtin/gc.c
index b32cf937cd..856dd50559 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -28,11 +28,11 @@
#include "commit.h"
#include "commit-graph.h"
#include "packfile.h"
-#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "pack.h"
#include "pack-objects.h"
#include "path.h"
+#include "reflog.h"
#include "blob.h"
#include "tree.h"
#include "promisor-remote.h"
@@ -53,7 +53,6 @@ static const char * const builtin_gc_usage[] = {
static timestamp_t gc_log_expire_time;
-static struct strvec reflog = STRVEC_INIT;
static struct strvec repack = STRVEC_INIT;
static struct strvec prune = STRVEC_INIT;
static struct strvec prune_worktrees = STRVEC_INIT;
@@ -288,6 +287,58 @@ static int maintenance_task_pack_refs(struct maintenance_run_opts *opts,
return run_command(&cmd);
}
+struct count_reflog_entries_data {
+ struct expire_reflog_policy_cb policy;
+ size_t count;
+ size_t limit;
+};
+
+static int count_reflog_entries(struct object_id *old_oid, struct object_id *new_oid,
+ const char *committer, timestamp_t timestamp,
+ int tz, const char *msg, void *cb_data)
+{
+ struct count_reflog_entries_data *data = cb_data;
+ if (should_expire_reflog_ent(old_oid, new_oid, committer, timestamp, tz, msg, &data->policy))
+ data->count++;
+ return data->count >= data->limit;
+}
+
+static int reflog_expire_condition(struct gc_config *cfg UNUSED)
+{
+ timestamp_t now = time(NULL);
+ struct count_reflog_entries_data data = {
+ .policy = {
+ .opts = REFLOG_EXPIRE_OPTIONS_INIT(now),
+ },
+ };
+ int limit = 100;
+
+ git_config_get_int("maintenance.reflog-expire.auto", &limit);
+ if (!limit)
+ return 0;
+ if (limit < 0)
+ return 1;
+ data.limit = limit;
+
+ repo_config(the_repository, reflog_expire_config, &data.policy.opts);
+
+ reflog_expire_options_set_refname(&data.policy.opts, "HEAD");
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository), "HEAD",
+ count_reflog_entries, &data);
+
+ reflog_expiry_cleanup(&data.policy);
+ return data.count >= data.limit;
+}
+
+static int maintenance_task_reflog_expire(struct maintenance_run_opts *opts UNUSED,
+ struct gc_config *cfg UNUSED)
+{
+ struct child_process cmd = CHILD_PROCESS_INIT;
+ cmd.git_cmd = 1;
+ strvec_pushl(&cmd.args, "reflog", "expire", "--all", NULL);
+ return run_command(&cmd);
+}
+
static int too_many_loose_objects(struct gc_config *cfg)
{
/*
@@ -667,15 +718,8 @@ static void gc_before_repack(struct maintenance_run_opts *opts,
if (cfg->pack_refs && maintenance_task_pack_refs(opts, cfg))
die(FAILED_RUN, "pack-refs");
-
- if (cfg->prune_reflogs) {
- struct child_process cmd = CHILD_PROCESS_INIT;
-
- cmd.git_cmd = 1;
- strvec_pushv(&cmd.args, reflog.v);
- if (run_command(&cmd))
- die(FAILED_RUN, reflog.v[0]);
- }
+ if (cfg->prune_reflogs && maintenance_task_reflog_expire(opts, cfg))
+ die(FAILED_RUN, "reflog");
}
int cmd_gc(int argc,
@@ -729,7 +773,6 @@ struct repository *repo UNUSED)
show_usage_with_options_if_asked(argc, argv,
builtin_gc_usage, builtin_gc_options);
- strvec_pushl(&reflog, "reflog", "expire", "--all", NULL);
strvec_pushl(&repack, "repack", "-d", "-l", NULL);
strvec_pushl(&prune, "prune", "--expire", NULL);
strvec_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
@@ -1035,6 +1078,8 @@ static int run_write_commit_graph(struct maintenance_run_opts *opts)
if (opts->quiet)
strvec_push(&child.args, "--no-progress");
+ else
+ strvec_push(&child.args, "--progress");
return !!run_command(&child);
}
@@ -1167,6 +1212,7 @@ static int write_loose_object_to_stdin(const struct object_id *oid,
fprintf(d->in, "%s\n", oid_to_hex(oid));
+ /* If batch_size is INT_MAX, then this will return 0 always. */
return ++(d->count) > d->batch_size;
}
@@ -1191,6 +1237,8 @@ static int pack_loose(struct maintenance_run_opts *opts)
strvec_push(&pack_proc.args, "pack-objects");
if (opts->quiet)
strvec_push(&pack_proc.args, "--quiet");
+ else
+ strvec_push(&pack_proc.args, "--no-quiet");
strvec_pushf(&pack_proc.args, "%s/pack/loose", r->objects->odb->path);
pack_proc.in = -1;
@@ -1210,6 +1258,15 @@ static int pack_loose(struct maintenance_run_opts *opts)
data.count = 0;
data.batch_size = 50000;
+ repo_config_get_int(r, "maintenance.loose-objects.batchSize",
+ &data.batch_size);
+
+ /* If configured as 0, then remove limit. */
+ if (!data.batch_size)
+ data.batch_size = INT_MAX;
+ else if (data.batch_size > 0)
+ data.batch_size--; /* Decrease for equality on limit. */
+
for_each_loose_file_in_objdir(r->objects->odb->path,
write_loose_object_to_stdin,
NULL,
@@ -1269,6 +1326,8 @@ static int multi_pack_index_write(struct maintenance_run_opts *opts)
if (opts->quiet)
strvec_push(&child.args, "--no-progress");
+ else
+ strvec_push(&child.args, "--progress");
if (run_command(&child))
return error(_("failed to write multi-pack-index"));
@@ -1285,6 +1344,8 @@ static int multi_pack_index_expire(struct maintenance_run_opts *opts)
if (opts->quiet)
strvec_push(&child.args, "--no-progress");
+ else
+ strvec_push(&child.args, "--progress");
if (run_command(&child))
return error(_("'git multi-pack-index expire' failed"));
@@ -1341,6 +1402,8 @@ static int multi_pack_index_repack(struct maintenance_run_opts *opts)
if (opts->quiet)
strvec_push(&child.args, "--no-progress");
+ else
+ strvec_push(&child.args, "--progress");
strvec_pushf(&child.args, "--batch-size=%"PRIuMAX,
(uintmax_t)get_auto_pack_size());
@@ -1398,6 +1461,7 @@ enum maintenance_task_label {
TASK_GC,
TASK_COMMIT_GRAPH,
TASK_PACK_REFS,
+ TASK_REFLOG_EXPIRE,
/* Leave as final value */
TASK__COUNT
@@ -1434,6 +1498,11 @@ static struct maintenance_task tasks[] = {
maintenance_task_pack_refs,
pack_refs_condition,
},
+ [TASK_REFLOG_EXPIRE] = {
+ "reflog-expire",
+ maintenance_task_reflog_expire,
+ reflog_expire_condition,
+ },
};
static int compare_tasks_by_selection(const void *a_, const void *b_)
@@ -2105,7 +2174,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
}
strbuf_addstr(&plist, "</array>\n</dict>\n</plist>\n");
- if (safe_create_leading_directories(filename))
+ if (safe_create_leading_directories(the_repository, filename))
die(_("failed to create directories for '%s'"), filename);
if ((long)lock_file_timeout_ms < 0 &&
@@ -2571,7 +2640,7 @@ static int systemd_timer_write_timer_file(enum schedule_priority schedule,
filename = xdg_config_home_systemd(local_timer_name);
- if (safe_create_leading_directories(filename)) {
+ if (safe_create_leading_directories(the_repository, filename)) {
error(_("failed to create directories for '%s'"), filename);
goto error;
}
@@ -2644,7 +2713,7 @@ static int systemd_timer_write_service_template(const char *exec_path)
char *local_service_name = xstrfmt(SYSTEMD_UNIT_FORMAT, "", "service");
filename = xdg_config_home_systemd(local_service_name);
- if (safe_create_leading_directories(filename)) {
+ if (safe_create_leading_directories(the_repository, filename)) {
error(_("failed to create directories for '%s'"), filename);
goto error;
}
diff --git a/builtin/grep.c b/builtin/grep.c
index f23a6f1dc8..3ce574a605 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -26,7 +26,7 @@
#include "submodule-config.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "packfile.h"
#include "pager.h"
#include "path.h"
@@ -453,7 +453,7 @@ static int grep_submodule(struct grep_opt *opt,
return 0;
subrepo = xmalloc(sizeof(*subrepo));
- if (repo_submodule_init(subrepo, superproject, path, null_oid())) {
+ if (repo_submodule_init(subrepo, superproject, path, null_oid(opt->repo->hash_algo))) {
free(subrepo);
return 0;
}
@@ -1150,7 +1150,7 @@ int cmd_grep(int argc,
break;
}
- object = parse_object_or_die(&oid, arg);
+ object = parse_object_or_die(the_repository, &oid, arg);
if (!seen_dashdash)
verify_non_filename(prefix, arg);
add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index a25f0403f4..cd53fa3bde 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -11,7 +11,7 @@
#include "gettext.h"
#include "hex.h"
#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "blob.h"
#include "quote.h"
#include "parse-options.h"
@@ -19,6 +19,11 @@
#include "strbuf.h"
#include "write-or-die.h"
+enum {
+ HASH_OBJECT_CHECK = (1 << 0),
+ HASH_OBJECT_WRITE = (1 << 1),
+};
+
/*
* This is to create corrupt objects for debugging and as such it
* needs to bypass the data conversion performed by, and the type
@@ -33,7 +38,7 @@ static int hash_literally(struct object_id *oid, int fd, const char *type, unsig
ret = -1;
else
ret = write_object_file_literally(buf.buf, buf.len, type, oid,
- flags);
+ (flags & HASH_OBJECT_WRITE) ? WRITE_OBJECT_FILE_PERSIST : 0);
close(fd);
strbuf_release(&buf);
return ret;
@@ -42,15 +47,21 @@ static int hash_literally(struct object_id *oid, int fd, const char *type, unsig
static void hash_fd(int fd, const char *type, const char *path, unsigned flags,
int literally)
{
+ unsigned int index_flags = 0;
struct stat st;
struct object_id oid;
+ if (flags & HASH_OBJECT_WRITE)
+ index_flags |= INDEX_WRITE_OBJECT;
+ if (flags & HASH_OBJECT_CHECK)
+ index_flags |= INDEX_FORMAT_CHECK;
+
if (fstat(fd, &st) < 0 ||
(literally
? hash_literally(&oid, fd, type, flags)
: index_fd(the_repository->index, &oid, fd, &st,
- type_from_string(type), path, flags)))
- die((flags & HASH_WRITE_OBJECT)
+ type_from_string(type), path, index_flags)))
+ die((flags & HASH_OBJECT_WRITE)
? "Unable to add %s to database"
: "Unable to hash %s", path);
printf("%s\n", oid_to_hex(&oid));
@@ -102,13 +113,13 @@ int cmd_hash_object(int argc,
int no_filters = 0;
int literally = 0;
int nongit = 0;
- unsigned flags = HASH_FORMAT_CHECK;
+ unsigned flags = HASH_OBJECT_CHECK;
const char *vpath = NULL;
char *vpath_free = NULL;
const struct option hash_object_options[] = {
OPT_STRING('t', NULL, &type, N_("type"), N_("object type")),
OPT_BIT('w', NULL, &flags, N_("write the object into the object database"),
- HASH_WRITE_OBJECT),
+ HASH_OBJECT_WRITE),
OPT_COUNTUP( 0 , "stdin", &hashstdin, N_("read the object from stdin")),
OPT_BOOL( 0 , "stdin-paths", &stdin_paths, N_("read file names from stdin")),
OPT_BOOL( 0 , "no-filters", &no_filters, N_("store file as is without filters")),
@@ -122,7 +133,7 @@ int cmd_hash_object(int argc,
argc = parse_options(argc, argv, prefix, hash_object_options,
hash_object_usage, 0);
- if (flags & HASH_WRITE_OBJECT)
+ if (flags & HASH_OBJECT_WRITE)
prefix = setup_git_directory();
else
prefix = setup_git_directory_gently(&nongit);
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 52cc97d52c..60a8ee05db 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -21,7 +21,7 @@
#include "packfile.h"
#include "pack-revindex.h"
#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "oid-array.h"
#include "oidset.h"
#include "path.h"
@@ -279,14 +279,14 @@ static unsigned check_objects(void)
{
unsigned i, max, foreign_nr = 0;
- max = get_max_object_index();
+ max = get_max_object_index(the_repository);
if (verbose)
progress = start_delayed_progress(the_repository,
_("Checking objects"), max);
for (i = 0; i < max; i++) {
- foreign_nr += check_object(get_indexed_object(i));
+ foreign_nr += check_object(get_indexed_object(the_repository, i));
display_progress(progress, i + 1);
}
@@ -485,7 +485,8 @@ static void *unpack_entry_data(off_t offset, unsigned long size,
git_hash_update(&c, hdr, hdrlen);
} else
oid = NULL;
- if (type == OBJ_BLOB && size > big_file_threshold)
+ if (type == OBJ_BLOB &&
+ size > repo_settings_get_big_file_threshold(the_repository))
buf = fixed_buf;
else
buf = xmallocz(size);
@@ -799,7 +800,8 @@ static int check_collison(struct object_entry *entry)
enum object_type type;
unsigned long size;
- if (entry->size <= big_file_threshold || entry->type != OBJ_BLOB)
+ if (entry->size <= repo_settings_get_big_file_threshold(the_repository) ||
+ entry->type != OBJ_BLOB)
return -1;
memset(&data, 0, sizeof(data));
@@ -1286,6 +1288,7 @@ static void parse_pack_objects(unsigned char *hash)
/* Check pack integrity */
flush();
+ the_hash_algo->init_fn(&tmp_ctx);
git_hash_clone(&tmp_ctx, &input_ctx);
git_hash_final(hash, &tmp_ctx);
if (!hasheq(fill(the_hash_algo->rawsz), hash, the_repository->hash_algo))
@@ -1381,7 +1384,7 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha
REALLOC_ARRAY(objects, nr_objects + nr_unresolved + 1);
memset(objects + nr_objects + 1, 0,
nr_unresolved * sizeof(*objects));
- f = hashfd(output_fd, curr_pack);
+ f = hashfd(the_repository->hash_algo, output_fd, curr_pack);
fix_unresolved_deltas(f);
strbuf_addf(&msg, Q_("completed with %d local object",
"completed with %d local objects",
@@ -2088,10 +2091,10 @@ int cmd_index_pack(int argc,
ALLOC_ARRAY(idx_objects, nr_objects);
for (i = 0; i < nr_objects; i++)
idx_objects[i] = &objects[i].idx;
- curr_index = write_idx_file(the_hash_algo, index_name, idx_objects,
+ curr_index = write_idx_file(the_repository, index_name, idx_objects,
nr_objects, &opts, pack_hash);
if (rev_index)
- curr_rev_index = write_rev_file(the_hash_algo, rev_index_name,
+ curr_rev_index = write_rev_file(the_repository, rev_index_name,
idx_objects, nr_objects,
pack_hash, opts.flags);
free(idx_objects);
diff --git a/builtin/init-db.c b/builtin/init-db.c
index 4a950e44d8..bb853e69f5 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -8,7 +8,6 @@
#include "abspath.h"
#include "environment.h"
#include "gettext.h"
-#include "object-file.h"
#include "parse-options.h"
#include "path.h"
#include "refs.h"
@@ -139,7 +138,7 @@ int cmd_init_db(int argc,
*/
saved = repo_settings_get_shared_repository(the_repository);
repo_settings_set_shared_repository(the_repository, 0);
- switch (safe_create_leading_directories_const(argv[0])) {
+ switch (safe_create_leading_directories_const(the_repository, argv[0])) {
case SCLD_OK:
case SCLD_PERMS:
break;
diff --git a/builtin/log.c b/builtin/log.c
index 04a6ef97bc..b450cd3bde 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -14,9 +14,8 @@
#include "gettext.h"
#include "hex.h"
#include "refs.h"
-#include "object-file.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "pager.h"
#include "color.h"
#include "commit.h"
@@ -29,6 +28,7 @@
#include "tag.h"
#include "reflog-walk.h"
#include "patch-ids.h"
+#include "path.h"
#include "shortlog.h"
#include "remote.h"
#include "string-list.h"
@@ -2311,7 +2311,7 @@ int cmd_format_patch(int argc,
*/
saved = repo_settings_get_shared_repository(the_repository);
repo_settings_set_shared_repository(the_repository, 0);
- switch (safe_create_leading_directories_const(output_directory)) {
+ switch (safe_create_leading_directories_const(the_repository, output_directory)) {
case SCLD_OK:
case SCLD_EXISTS:
break;
@@ -2468,7 +2468,7 @@ int cmd_format_patch(int argc,
base = get_base_commit(&cfg, list, nr);
if (base) {
reset_revision_walk();
- clear_object_flags(UNINTERESTING);
+ clear_object_flags(the_repository, UNINTERESTING);
prepare_bases(&bases, base, list, nr);
}
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 70a377e9c0..be74f0a03b 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -233,7 +233,8 @@ static void show_submodule(struct repository *superproject,
{
struct repository subrepo;
- if (repo_submodule_init(&subrepo, superproject, path, null_oid()))
+ if (repo_submodule_init(&subrepo, superproject, path,
+ null_oid(superproject->hash_algo)))
return;
if (repo_read_index(&subrepo) < 0)
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index 8542b5d53e..8aafc30ca4 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -10,7 +10,7 @@
#include "gettext.h"
#include "hex.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "tree.h"
#include "path.h"
#include "quote.h"
diff --git a/builtin/merge-file.c b/builtin/merge-file.c
index 7e315f374b..2b16b10d2c 100644
--- a/builtin/merge-file.c
+++ b/builtin/merge-file.c
@@ -5,6 +5,7 @@
#include "abspath.h"
#include "diff.h"
#include "hex.h"
+#include "object-file.h"
#include "object-name.h"
#include "object-store.h"
#include "config.h"
diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c
index abfc060e28..03b5100cfa 100644
--- a/builtin/merge-recursive.c
+++ b/builtin/merge-recursive.c
@@ -3,7 +3,7 @@
#include "advice.h"
#include "gettext.h"
#include "hash.h"
-#include "merge-recursive.h"
+#include "merge-ort-wrappers.h"
#include "object-name.h"
static const char builtin_merge_recursive_usage[] =
@@ -89,7 +89,7 @@ int cmd_merge_recursive(int argc,
if (o.verbosity >= 3)
printf(_("Merging %s with %s\n"), o.branch1, o.branch2);
- failed = merge_recursive_generic(&o, &h1, &h2, bases_count, bases, &result);
+ failed = merge_ort_generic(&o, &h1, &h2, bases_count, bases, &result);
free(better1);
free(better2);
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 3ec7127b3a..4aafa73c61 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -10,7 +10,7 @@
#include "commit-reach.h"
#include "merge-ort.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "parse-options.h"
#include "blob.h"
#include "merge-blobs.h"
diff --git a/builtin/merge.c b/builtin/merge.c
index 9ab10c7db0..ce90e52fe4 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -39,7 +39,6 @@
#include "rerere.h"
#include "help.h"
#include "merge.h"
-#include "merge-recursive.h"
#include "merge-ort-wrappers.h"
#include "resolve-undo.h"
#include "remote.h"
@@ -171,7 +170,7 @@ static struct strategy *get_strategy(const char *name)
struct strategy *ret;
static struct cmdnames main_cmds = {0}, other_cmds = {0};
static int loaded;
- char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM");
+ char *default_strategy = NULL;
if (!name)
return NULL;
@@ -772,12 +771,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
repo_hold_locked_index(the_repository, &lock,
LOCK_DIE_ON_ERROR);
- if (!strcmp(strategy, "ort"))
- clean = merge_ort_recursive(&o, head, remoteheads->item,
- reversed, &result);
- else
- clean = merge_recursive(&o, head, remoteheads->item,
- reversed, &result);
+ clean = merge_ort_recursive(&o, head, remoteheads->item,
+ reversed, &result);
free_commit_list(reversed);
strbuf_release(&o.obuf);
@@ -1338,12 +1333,6 @@ int cmd_merge(int argc,
if (branch)
skip_prefix(branch, "refs/heads/", &branch);
- if (!pull_twohead) {
- char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM");
- if (default_strategy && !strcmp(default_strategy, "ort"))
- pull_twohead = xstrdup("ort");
- }
-
init_diff_ui_defaults();
git_config(git_merge_config, NULL);
@@ -1544,12 +1533,6 @@ int cmd_merge(int argc,
fast_forward = FF_NO;
}
- if (!use_strategies && !pull_twohead &&
- remoteheads && !remoteheads->next) {
- char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM");
- if (default_strategy)
- append_strategy(get_strategy(default_strategy));
- }
if (!use_strategies) {
if (!remoteheads)
; /* already up-to-date */
diff --git a/builtin/mktag.c b/builtin/mktag.c
index 6e188dce50..7ac11c46d5 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -6,7 +6,7 @@
#include "strbuf.h"
#include "replace-object.h"
#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "fsck.h"
#include "config.h"
diff --git a/builtin/mktree.c b/builtin/mktree.c
index 3c16faa40e..7ffe6eefd8 100644
--- a/builtin/mktree.c
+++ b/builtin/mktree.c
@@ -11,7 +11,8 @@
#include "strbuf.h"
#include "tree.h"
#include "parse-options.h"
-#include "object-store-ll.h"
+#include "object-file.h"
+#include "object-store.h"
static struct treeent {
unsigned mode;
diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c
index e4820fd721..69a9750732 100644
--- a/builtin/multi-pack-index.c
+++ b/builtin/multi-pack-index.c
@@ -7,7 +7,7 @@
#include "midx.h"
#include "strbuf.h"
#include "trace2.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "replace-object.h"
#include "repository.h"
diff --git a/builtin/mv.c b/builtin/mv.c
index 55a7d471dc..54b323fff7 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -15,6 +15,7 @@
#include "gettext.h"
#include "name-hash.h"
#include "object-file.h"
+#include "path.h"
#include "pathspec.h"
#include "lockfile.h"
#include "dir.h"
@@ -28,7 +29,8 @@
#include "entry.h"
static const char * const builtin_mv_usage[] = {
- N_("git mv [<options>] <source>... <destination>"),
+ N_("git mv [-v] [-f] [-n] [-k] <source> <destination>"),
+ N_("git mv [-v] [-f] [-n] [-k] <source>... <destination-directory>"),
NULL
};
@@ -555,7 +557,7 @@ remove_entry:
*/
char *dst_dup = xstrdup(dst);
string_list_append(&dirty_paths, dst);
- safe_create_leading_directories(dst_dup);
+ safe_create_leading_directories(the_repository, dst_dup);
FREE_AND_NULL(dst_dup);
rename(src, dst);
}
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index beac166b5c..ff199638de 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -567,7 +567,11 @@ int cmd_name_rev(int argc,
{
struct mem_pool string_pool;
struct object_array revs = OBJECT_ARRAY_INIT;
- int all = 0, annotate_stdin = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
+
+#ifndef WITH_BREAKING_CHANGES
+ int transform_stdin = 0;
+#endif
+ int all = 0, annotate_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
struct option opts[] = {
OPT_BOOL(0, "name-only", &data.name_only, N_("print only ref-based names (no object names)")),
@@ -578,11 +582,13 @@ int cmd_name_rev(int argc,
N_("ignore refs matching <pattern>")),
OPT_GROUP(""),
OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")),
+#ifndef WITH_BREAKING_CHANGES
OPT_BOOL_F(0,
"stdin",
&transform_stdin,
N_("deprecated: use --annotate-stdin instead"),
PARSE_OPT_HIDDEN),
+#endif /* WITH_BREAKING_CHANGES */
OPT_BOOL(0, "annotate-stdin", &annotate_stdin, N_("annotate text from stdin")),
OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")),
OPT_BOOL(0, "always", &always,
@@ -597,12 +603,14 @@ int cmd_name_rev(int argc,
git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0);
+#ifndef WITH_BREAKING_CHANGES
if (transform_stdin) {
warning("--stdin is deprecated. Please use --annotate-stdin instead, "
"which is functionally equivalent.\n"
"This option will be removed in a future release.");
annotate_stdin = 1;
}
+#endif
if (all + annotate_stdin + !!argc > 1) {
error("Specify either a list, or --all, not both!");
@@ -667,9 +675,9 @@ int cmd_name_rev(int argc,
} else if (all) {
int i, max;
- max = get_max_object_index();
+ max = get_max_object_index(the_repository);
for (i = 0; i < max; i++) {
- struct object *obj = get_indexed_object(i);
+ struct object *obj = get_indexed_object(the_repository, i);
if (!obj || obj->type != OBJ_COMMIT)
continue;
show_name(obj, NULL,
diff --git a/builtin/notes.c b/builtin/notes.c
index ff61ec5f2d..a3f433ca4c 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -14,8 +14,9 @@
#include "gettext.h"
#include "hex.h"
#include "notes.h"
+#include "object-file.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "path.h"
#include "pretty.h"
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 9328812e28..72ebe13fe0 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -32,7 +32,7 @@
#include "list.h"
#include "packfile.h"
#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "replace-object.h"
#include "dir.h"
#include "midx.h"
@@ -500,7 +500,8 @@ static unsigned long write_no_reuse_object(struct hashfile *f, struct object_ent
if (!usable_delta) {
if (oe_type(entry) == OBJ_BLOB &&
- oe_size_greater_than(&to_pack, entry, big_file_threshold) &&
+ oe_size_greater_than(&to_pack, entry,
+ repo_settings_get_big_file_threshold(the_repository)) &&
(st = open_istream(the_repository, &entry->idx.oid, &type,
&size, NULL)) != NULL)
buf = NULL;
@@ -1312,9 +1313,10 @@ static void write_pack_file(void)
char *pack_tmp_name = NULL;
if (pack_to_stdout)
- f = hashfd_throughput(1, "<stdout>", progress_state);
+ f = hashfd_throughput(the_repository->hash_algo, 1,
+ "<stdout>", progress_state);
else
- f = create_tmp_packfile(&pack_tmp_name);
+ f = create_tmp_packfile(the_repository, &pack_tmp_name);
offset = write_pack_header(f, nr_remaining);
@@ -1398,7 +1400,8 @@ static void write_pack_file(void)
if (write_bitmap_index) {
bitmap_writer_init(&bitmap_writer,
- the_repository, &to_pack);
+ the_repository, &to_pack,
+ NULL);
bitmap_writer_set_checksum(&bitmap_writer, hash);
bitmap_writer_build_type_index(&bitmap_writer,
written_list);
@@ -1407,7 +1410,7 @@ static void write_pack_file(void)
if (cruft)
pack_idx_opts.flags |= WRITE_MTIMES;
- stage_tmp_packfiles(the_hash_algo, &tmpname,
+ stage_tmp_packfiles(the_repository, &tmpname,
pack_tmp_name, written_list,
nr_written, &to_pack,
&pack_idx_opts, hash,
@@ -1817,7 +1820,8 @@ static int add_object_entry(const struct object_id *oid, enum object_type type,
static int add_object_entry_from_bitmap(const struct object_id *oid,
enum object_type type,
int flags UNUSED, uint32_t name_hash,
- struct packed_git *pack, off_t offset)
+ struct packed_git *pack, off_t offset,
+ void *payload UNUSED)
{
display_progress(progress_state, ++nr_seen);
@@ -2535,7 +2539,8 @@ static void get_object_details(void)
struct object_entry *entry = sorted_by_offset[i];
check_object(entry, i);
if (entry->type_valid &&
- oe_size_greater_than(&to_pack, entry, big_file_threshold))
+ oe_size_greater_than(&to_pack, entry,
+ repo_settings_get_big_file_threshold(the_repository)))
entry->no_try_delta = 1;
display_progress(progress_state, i + 1);
}
@@ -3928,7 +3933,7 @@ static void show_commit(struct commit *commit, void *data UNUSED)
index_commit_for_bitmap(commit);
if (use_delta_islands)
- propagate_island_marks(commit);
+ propagate_island_marks(the_repository, commit);
}
static void show_object(struct object *obj, const char *name,
@@ -4244,7 +4249,7 @@ static int mark_bitmap_preferred_tip(const char *refname,
if (!peel_iterated_oid(the_repository, oid, &peeled))
oid = &peeled;
- object = parse_object_or_die(oid, refname);
+ object = parse_object_or_die(the_repository, oid, refname);
if (object->type == OBJ_COMMIT)
object->flags |= NEEDS_BITMAP;
diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c
index 3febe732f8..5d1fc78176 100644
--- a/builtin/pack-redundant.c
+++ b/builtin/pack-redundant.c
@@ -13,7 +13,7 @@
#include "hex.h"
#include "packfile.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "strbuf.h"
#define BLKSIZE 512
diff --git a/builtin/prune.c b/builtin/prune.c
index 1c357fffd8..e930caa0c0 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -17,7 +17,7 @@
#include "replace-object.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "shallow.h"
static const char * const prune_usage[] = {
@@ -185,7 +185,7 @@ int cmd_prune(int argc,
const char *name = *argv++;
if (!repo_get_oid(the_repository, name, &oid)) {
- struct object *object = parse_object_or_die(&oid,
+ struct object *object = parse_object_or_die(the_repository, &oid,
name);
add_pending_object(&revs, object, "");
}
diff --git a/builtin/pull.c b/builtin/pull.c
index 9c4a00620a..a1ebc6ad33 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -738,7 +738,8 @@ static const char *get_tracking_branch(const char *remote, const char *refspec)
const char *spec_src;
const char *merge_branch;
- refspec_item_init_or_die(&spec, refspec, REFSPEC_FETCH);
+ if (!refspec_item_init_fetch(&spec, refspec))
+ die(_("invalid refspec '%s'"), refspec);
spec_src = spec.src;
if (!*spec_src || !strcmp(spec_src, "HEAD"))
spec_src = "HEAD";
diff --git a/builtin/rebase.c b/builtin/rebase.c
index d408335009..2e8c4ee678 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -267,7 +267,8 @@ static int init_basic_state(struct replay_opts *opts, const char *head_name,
{
FILE *interactive;
- if (!is_directory(merge_dir()) && mkdir_in_gitdir(merge_dir()))
+ if (!is_directory(merge_dir()) &&
+ safe_create_dir_in_gitdir(the_repository, merge_dir()))
return error_errno(_("could not create temporary %s"), merge_dir());
refs_delete_reflog(get_main_ref_store(the_repository), "REBASE_HEAD");
@@ -925,7 +926,7 @@ static void fill_branch_base(struct rebase_options *options,
options->orig_head, &merge_bases) < 0)
exit(128);
if (!merge_bases || merge_bases->next)
- oidcpy(branch_base, null_oid());
+ oidcpy(branch_base, null_oid(the_hash_algo));
else
oidcpy(branch_base, &merge_bases->item->object.oid);
@@ -1588,11 +1589,6 @@ int cmd_rebase(int argc,
options.default_backend);
}
- if (options.type == REBASE_MERGE &&
- !options.strategy &&
- getenv("GIT_TEST_MERGE_ALGORITHM"))
- options.strategy = xstrdup(getenv("GIT_TEST_MERGE_ALGORITHM"));
-
switch (options.type) {
case REBASE_MERGE:
options.state_dir = merge_dir();
@@ -1856,7 +1852,7 @@ int cmd_rebase(int argc,
strbuf_addf(&msg, "%s (start): checkout %s",
options.reflog_action, options.onto_name);
ropts.oid = &options.onto->object.oid;
- ropts.orig_head = &options.orig_head->object.oid,
+ ropts.orig_head = &options.orig_head->object.oid;
ropts.flags = RESET_HEAD_DETACH | RESET_ORIG_HEAD |
RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
ropts.head_msg = msg.buf;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 7b28fc9df6..be314879e8 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -31,8 +31,9 @@
#include "tmp-objdir.h"
#include "oidset.h"
#include "packfile.h"
+#include "object-file.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "path.h"
#include "protocol.h"
#include "commit-reach.h"
@@ -363,7 +364,7 @@ static void write_head_info(void)
strvec_clear(&excludes_vector);
if (!sent_capabilities)
- show_ref("capabilities^{}", null_oid());
+ show_ref("capabilities^{}", null_oid(the_hash_algo));
advertise_shallow_grafts(1);
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 95f264989b..3acaf3e32c 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -29,6 +29,9 @@
#define BUILTIN_REFLOG_EXISTS_USAGE \
N_("git reflog exists <ref>")
+#define BUILTIN_REFLOG_DROP_USAGE \
+ N_("git reflog drop [--all [--single-worktree] | <refs>...]")
+
static const char *const reflog_show_usage[] = {
BUILTIN_REFLOG_SHOW_USAGE,
NULL,
@@ -54,18 +57,21 @@ static const char *const reflog_exists_usage[] = {
NULL,
};
+static const char *const reflog_drop_usage[] = {
+ BUILTIN_REFLOG_DROP_USAGE,
+ NULL,
+};
+
static const char *const reflog_usage[] = {
BUILTIN_REFLOG_SHOW_USAGE,
BUILTIN_REFLOG_LIST_USAGE,
BUILTIN_REFLOG_EXPIRE_USAGE,
BUILTIN_REFLOG_DELETE_USAGE,
+ BUILTIN_REFLOG_DROP_USAGE,
BUILTIN_REFLOG_EXISTS_USAGE,
NULL
};
-static timestamp_t default_reflog_expire;
-static timestamp_t default_reflog_expire_unreachable;
-
struct worktree_reflogs {
struct worktree *worktree;
struct string_list reflogs;
@@ -91,131 +97,19 @@ static int collect_reflog(const char *ref, void *cb_data)
return 0;
}
-static struct reflog_expire_cfg {
- struct reflog_expire_cfg *next;
- timestamp_t expire_total;
- timestamp_t expire_unreachable;
- char pattern[FLEX_ARRAY];
-} *reflog_expire_cfg, **reflog_expire_cfg_tail;
-
-static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
-{
- struct reflog_expire_cfg *ent;
-
- if (!reflog_expire_cfg_tail)
- reflog_expire_cfg_tail = &reflog_expire_cfg;
-
- for (ent = reflog_expire_cfg; ent; ent = ent->next)
- if (!xstrncmpz(ent->pattern, pattern, len))
- return ent;
-
- FLEX_ALLOC_MEM(ent, pattern, pattern, len);
- *reflog_expire_cfg_tail = ent;
- reflog_expire_cfg_tail = &(ent->next);
- return ent;
-}
-
-/* expiry timer slot */
-#define EXPIRE_TOTAL 01
-#define EXPIRE_UNREACH 02
-
-static int reflog_expire_config(const char *var, const char *value,
- const struct config_context *ctx, void *cb)
-{
- const char *pattern, *key;
- size_t pattern_len;
- timestamp_t expire;
- int slot;
- struct reflog_expire_cfg *ent;
-
- if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
- return git_default_config(var, value, ctx, cb);
-
- if (!strcmp(key, "reflogexpire")) {
- slot = EXPIRE_TOTAL;
- if (git_config_expiry_date(&expire, var, value))
- return -1;
- } else if (!strcmp(key, "reflogexpireunreachable")) {
- slot = EXPIRE_UNREACH;
- if (git_config_expiry_date(&expire, var, value))
- return -1;
- } else
- return git_default_config(var, value, ctx, cb);
-
- if (!pattern) {
- switch (slot) {
- case EXPIRE_TOTAL:
- default_reflog_expire = expire;
- break;
- case EXPIRE_UNREACH:
- default_reflog_expire_unreachable = expire;
- break;
- }
- return 0;
- }
-
- ent = find_cfg_ent(pattern, pattern_len);
- if (!ent)
- return -1;
- switch (slot) {
- case EXPIRE_TOTAL:
- ent->expire_total = expire;
- break;
- case EXPIRE_UNREACH:
- ent->expire_unreachable = expire;
- break;
- }
- return 0;
-}
-
-static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, const char *ref)
-{
- struct reflog_expire_cfg *ent;
-
- if (cb->explicit_expiry == (EXPIRE_TOTAL|EXPIRE_UNREACH))
- return; /* both given explicitly -- nothing to tweak */
-
- for (ent = reflog_expire_cfg; ent; ent = ent->next) {
- if (!wildmatch(ent->pattern, ref, 0)) {
- if (!(cb->explicit_expiry & EXPIRE_TOTAL))
- cb->expire_total = ent->expire_total;
- if (!(cb->explicit_expiry & EXPIRE_UNREACH))
- cb->expire_unreachable = ent->expire_unreachable;
- return;
- }
- }
-
- /*
- * If unconfigured, make stash never expire
- */
- if (!strcmp(ref, "refs/stash")) {
- if (!(cb->explicit_expiry & EXPIRE_TOTAL))
- cb->expire_total = 0;
- if (!(cb->explicit_expiry & EXPIRE_UNREACH))
- cb->expire_unreachable = 0;
- return;
- }
-
- /* Nothing matched -- use the default value */
- if (!(cb->explicit_expiry & EXPIRE_TOTAL))
- cb->expire_total = default_reflog_expire;
- if (!(cb->explicit_expiry & EXPIRE_UNREACH))
- cb->expire_unreachable = default_reflog_expire_unreachable;
-}
-
static int expire_unreachable_callback(const struct option *opt,
const char *arg,
int unset)
{
- struct cmd_reflog_expire_cb *cmd = opt->value;
+ struct reflog_expire_options *opts = opt->value;
BUG_ON_OPT_NEG(unset);
- if (parse_expiry_date(arg, &cmd->expire_unreachable))
+ if (parse_expiry_date(arg, &opts->expire_unreachable))
die(_("invalid timestamp '%s' given to '--%s'"),
arg, opt->long_name);
- cmd->explicit_expiry |= EXPIRE_UNREACH;
+ opts->explicit_expiry |= REFLOG_EXPIRE_UNREACH;
return 0;
}
@@ -223,15 +117,15 @@ static int expire_total_callback(const struct option *opt,
const char *arg,
int unset)
{
- struct cmd_reflog_expire_cb *cmd = opt->value;
+ struct reflog_expire_options *opts = opt->value;
BUG_ON_OPT_NEG(unset);
- if (parse_expiry_date(arg, &cmd->expire_total))
+ if (parse_expiry_date(arg, &opts->expire_total))
die(_("invalid timestamp '%s' given to '--%s'"),
arg, opt->long_name);
- cmd->explicit_expiry |= EXPIRE_TOTAL;
+ opts->explicit_expiry |= REFLOG_EXPIRE_TOTAL;
return 0;
}
@@ -276,8 +170,8 @@ static int cmd_reflog_list(int argc, const char **argv, const char *prefix,
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
struct repository *repo UNUSED)
{
- struct cmd_reflog_expire_cb cmd = { 0 };
timestamp_t now = time(NULL);
+ struct reflog_expire_options opts = REFLOG_EXPIRE_OPTIONS_INIT(now);
int i, status, do_all, single_worktree = 0;
unsigned int flags = 0;
int verbose = 0;
@@ -292,15 +186,15 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
N_("update the reference to the value of the top reflog entry"),
EXPIRE_REFLOGS_UPDATE_REF),
OPT_BOOL(0, "verbose", &verbose, N_("print extra information on screen")),
- OPT_CALLBACK_F(0, "expire", &cmd, N_("timestamp"),
+ OPT_CALLBACK_F(0, "expire", &opts, N_("timestamp"),
N_("prune entries older than the specified time"),
PARSE_OPT_NONEG,
expire_total_callback),
- OPT_CALLBACK_F(0, "expire-unreachable", &cmd, N_("timestamp"),
+ OPT_CALLBACK_F(0, "expire-unreachable", &opts, N_("timestamp"),
N_("prune entries older than <time> that are not reachable from the current tip of the branch"),
PARSE_OPT_NONEG,
expire_unreachable_callback),
- OPT_BOOL(0, "stale-fix", &cmd.stalefix,
+ OPT_BOOL(0, "stale-fix", &opts.stalefix,
N_("prune any reflog entries that point to broken commits")),
OPT_BOOL(0, "all", &do_all, N_("process the reflogs of all references")),
OPT_BOOL(0, "single-worktree", &single_worktree,
@@ -308,17 +202,11 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
OPT_END()
};
- default_reflog_expire_unreachable = now - 30 * 24 * 3600;
- default_reflog_expire = now - 90 * 24 * 3600;
- git_config(reflog_expire_config, NULL);
+ git_config(reflog_expire_config, &opts);
save_commit_buffer = 0;
do_all = status = 0;
- cmd.explicit_expiry = 0;
- cmd.expire_total = default_reflog_expire;
- cmd.expire_unreachable = default_reflog_expire_unreachable;
-
argc = parse_options(argc, argv, prefix, options, reflog_expire_usage, 0);
if (verbose)
@@ -329,7 +217,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
* even in older repository. We cannot trust what's reachable
* from reflog if the repository was pruned with older git.
*/
- if (cmd.stalefix) {
+ if (opts.stalefix) {
struct rev_info revs;
repo_init_revisions(the_repository, &revs, prefix);
@@ -363,11 +251,11 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
for_each_string_list_item(item, &collected.reflogs) {
struct expire_reflog_policy_cb cb = {
- .cmd = cmd,
+ .opts = opts,
.dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
};
- set_reflog_expiry_param(&cb.cmd, item->string);
+ reflog_expire_options_set_refname(&cb.opts, item->string);
status |= refs_reflog_expire(get_main_ref_store(the_repository),
item->string, flags,
reflog_expiry_prepare,
@@ -380,13 +268,13 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
for (i = 0; i < argc; i++) {
char *ref;
- struct expire_reflog_policy_cb cb = { .cmd = cmd };
+ struct expire_reflog_policy_cb cb = { .opts = opts };
if (!repo_dwim_log(the_repository, argv[i], strlen(argv[i]), NULL, &ref)) {
- status |= error(_("%s points nowhere!"), argv[i]);
+ status |= error(_("reflog could not be found: '%s'"), argv[i]);
continue;
}
- set_reflog_expiry_param(&cb.cmd, ref);
+ reflog_expire_options_set_refname(&cb.opts, ref);
status |= refs_reflog_expire(get_main_ref_store(the_repository),
ref, flags,
reflog_expiry_prepare,
@@ -449,10 +337,64 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,
refname);
}
+static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,
+ struct repository *repo)
+{
+ int ret = 0, do_all = 0, single_worktree = 0;
+ const struct option options[] = {
+ OPT_BOOL(0, "all", &do_all, N_("drop the reflogs of all references")),
+ OPT_BOOL(0, "single-worktree", &single_worktree,
+ N_("drop reflogs from the current worktree only")),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);
+
+ if (argc && do_all)
+ usage(_("references specified along with --all"));
+
+ if (do_all) {
+ struct worktree_reflogs collected = {
+ .reflogs = STRING_LIST_INIT_DUP,
+ };
+ struct string_list_item *item;
+ struct worktree **worktrees, **p;
+
+ worktrees = get_worktrees();
+ for (p = worktrees; *p; p++) {
+ if (single_worktree && !(*p)->is_current)
+ continue;
+ collected.worktree = *p;
+ refs_for_each_reflog(get_worktree_ref_store(*p),
+ collect_reflog, &collected);
+ }
+ free_worktrees(worktrees);
+
+ for_each_string_list_item(item, &collected.reflogs)
+ ret |= refs_delete_reflog(get_main_ref_store(repo),
+ item->string);
+ string_list_clear(&collected.reflogs, 0);
+
+ return ret;
+ }
+
+ for (int i = 0; i < argc; i++) {
+ char *ref;
+ if (!repo_dwim_log(repo, argv[i], strlen(argv[i]), NULL, &ref)) {
+ ret |= error(_("reflog could not be found: '%s'"), argv[i]);
+ continue;
+ }
+
+ ret |= refs_delete_reflog(get_main_ref_store(repo), ref);
+ free(ref);
+ }
+
+ return ret;
+}
+
/*
* main "reflog"
*/
-
int cmd_reflog(int argc,
const char **argv,
const char *prefix,
@@ -465,6 +407,7 @@ int cmd_reflog(int argc,
OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),
+ OPT_SUBCOMMAND("drop", &fn, cmd_reflog_drop),
OPT_END()
};
diff --git a/builtin/remote.c b/builtin/remote.c
index 1b7aad8838..b4baa34e66 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -14,7 +14,7 @@
#include "rebase.h"
#include "refs.h"
#include "refspec.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "strvec.h"
#include "commit-reach.h"
#include "progress.h"
@@ -511,7 +511,7 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
- fetch_map, 1);
+ fetch_map, REMOTE_GUESS_HEAD_ALL);
for (ref = matches; ref; ref = ref->next)
string_list_append(&states->heads, abbrev_branch(ref->name));
diff --git a/builtin/repack.c b/builtin/repack.c
index 8bf9941b2c..59214dbdfd 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -17,7 +17,7 @@
#include "midx.h"
#include "packfile.h"
#include "prune-packed.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "promisor-remote.h"
#include "shallow.h"
#include "pack.h"
@@ -1022,29 +1022,13 @@ static int write_filtered_pack(const struct pack_objects_args *args,
return finish_pack_objects_cmd(&cmd, names, local);
}
-static int existing_cruft_pack_cmp(const void *va, const void *vb)
+static void combine_small_cruft_packs(FILE *in, size_t combine_cruft_below_size,
+ struct existing_packs *existing)
{
- struct packed_git *a = *(struct packed_git **)va;
- struct packed_git *b = *(struct packed_git **)vb;
-
- if (a->pack_size < b->pack_size)
- return -1;
- if (a->pack_size > b->pack_size)
- return 1;
- return 0;
-}
-
-static void collapse_small_cruft_packs(FILE *in, size_t max_size,
- struct existing_packs *existing)
-{
- struct packed_git **existing_cruft, *p;
+ struct packed_git *p;
struct strbuf buf = STRBUF_INIT;
- size_t total_size = 0;
- size_t existing_cruft_nr = 0;
size_t i;
- ALLOC_ARRAY(existing_cruft, existing->cruft_packs.nr);
-
for (p = get_all_packs(the_repository); p; p = p->next) {
if (!(p->is_cruft && p->pack_local))
continue;
@@ -1056,24 +1040,7 @@ static void collapse_small_cruft_packs(FILE *in, size_t max_size,
if (!string_list_has_string(&existing->cruft_packs, buf.buf))
continue;
- if (existing_cruft_nr >= existing->cruft_packs.nr)
- BUG("too many cruft packs (found %"PRIuMAX", but knew "
- "of %"PRIuMAX")",
- (uintmax_t)existing_cruft_nr + 1,
- (uintmax_t)existing->cruft_packs.nr);
- existing_cruft[existing_cruft_nr++] = p;
- }
-
- QSORT(existing_cruft, existing_cruft_nr, existing_cruft_pack_cmp);
-
- for (i = 0; i < existing_cruft_nr; i++) {
- size_t proposed;
-
- p = existing_cruft[i];
- proposed = st_add(total_size, p->pack_size);
-
- if (proposed <= max_size) {
- total_size = proposed;
+ if (p->pack_size < combine_cruft_below_size) {
fprintf(in, "-%s\n", pack_basename(p));
} else {
retain_cruft_pack(existing, p);
@@ -1086,13 +1053,13 @@ static void collapse_small_cruft_packs(FILE *in, size_t max_size,
existing->non_kept_packs.items[i].string);
strbuf_release(&buf);
- free(existing_cruft);
}
static int write_cruft_pack(const struct pack_objects_args *args,
const char *destination,
const char *pack_prefix,
const char *cruft_expiration,
+ unsigned long combine_cruft_below_size,
struct string_list *names,
struct existing_packs *existing)
{
@@ -1135,8 +1102,9 @@ static int write_cruft_pack(const struct pack_objects_args *args,
in = xfdopen(cmd.in, "w");
for_each_string_list_item(item, names)
fprintf(in, "%s-%s.pack\n", pack_prefix, item->string);
- if (args->max_pack_size && !cruft_expiration) {
- collapse_small_cruft_packs(in, args->max_pack_size, existing);
+ if (combine_cruft_below_size && !cruft_expiration) {
+ combine_small_cruft_packs(in, combine_cruft_below_size,
+ existing);
} else {
for_each_string_list_item(item, &existing->non_kept_packs)
fprintf(in, "-%s.pack\n", item->string);
@@ -1190,6 +1158,7 @@ int cmd_repack(int argc,
const char *opt_window_memory = NULL;
const char *opt_depth = NULL;
const char *opt_threads = NULL;
+ unsigned long combine_cruft_below_size = 0ul;
struct option builtin_repack_options[] = {
OPT_BIT('a', NULL, &pack_everything,
@@ -1202,6 +1171,9 @@ int cmd_repack(int argc,
PACK_CRUFT),
OPT_STRING(0, "cruft-expiration", &cruft_expiration, N_("approxidate"),
N_("with --cruft, expire objects older than this")),
+ OPT_UNSIGNED(0, "combine-cruft-below-size",
+ &combine_cruft_below_size,
+ N_("with --cruft, only repack cruft packs smaller than this")),
OPT_UNSIGNED(0, "max-cruft-size", &cruft_po_args.max_pack_size,
N_("with --cruft, limit the size of new cruft packs")),
OPT_BOOL('d', NULL, &delete_redundant,
@@ -1445,7 +1417,8 @@ int cmd_repack(int argc,
cruft_po_args.quiet = po_args.quiet;
ret = write_cruft_pack(&cruft_po_args, packtmp, pack_prefix,
- cruft_expiration, &names,
+ cruft_expiration,
+ combine_cruft_below_size, &names,
&existing);
if (ret)
goto cleanup;
@@ -1472,10 +1445,17 @@ int cmd_repack(int argc,
* generate an empty pack (since every object not in the
* cruft pack generated above will have an mtime older
* than the expiration).
+ *
+ * Pretend we don't have a `--combine-cruft-below-size`
+ * argument, since we're not selectively combining
+ * anything based on size to generate the limbo cruft
+ * pack, but rather removing all cruft packs from the
+ * main repository regardless of size.
*/
ret = write_cruft_pack(&cruft_po_args, expire_to,
pack_prefix,
NULL,
+ 0ul,
&names,
&existing);
if (ret)
diff --git a/builtin/replace.c b/builtin/replace.c
index 15ec0922ce..48c7c6a2d5 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -19,7 +19,7 @@
#include "run-command.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "replace-object.h"
#include "tag.h"
#include "wildmatch.h"
@@ -305,7 +305,7 @@ static int import_object(struct object_id *oid, enum object_type type,
strbuf_release(&result);
} else {
struct stat st;
- int flags = HASH_FORMAT_CHECK | HASH_WRITE_OBJECT;
+ int flags = INDEX_FORMAT_CHECK | INDEX_WRITE_OBJECT;
if (fstat(fd, &st) < 0) {
error_errno(_("unable to fstat %s"), filename);
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index bb26bee0d4..c4cd4ed5c8 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -14,8 +14,9 @@
#include "object.h"
#include "object-name.h"
#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "pack-bitmap.h"
+#include "parse-options.h"
#include "log-tree.h"
#include "graph.h"
#include "bisect.h"
@@ -64,6 +65,7 @@ static const char rev_list_usage[] =
" --abbrev-commit\n"
" --left-right\n"
" --count\n"
+" -z\n"
" special purpose:\n"
" --bisect\n"
" --bisect-vars\n"
@@ -96,6 +98,9 @@ static int arg_show_object_names = 1;
#define DEFAULT_OIDSET_SIZE (16*1024)
+static char line_term = '\n';
+static char info_term = ' ';
+
static int show_disk_usage;
static off_t total_disk_usage;
static int human_readable;
@@ -131,24 +136,37 @@ static void print_missing_object(struct missing_objects_map_entry *entry,
{
struct strbuf sb = STRBUF_INIT;
+ if (line_term)
+ printf("?%s", oid_to_hex(&entry->entry.oid));
+ else
+ printf("%s%cmissing=yes", oid_to_hex(&entry->entry.oid),
+ info_term);
+
if (!print_missing_info) {
- printf("?%s\n", oid_to_hex(&entry->entry.oid));
+ putchar(line_term);
return;
}
if (entry->path && *entry->path) {
- struct strbuf path = STRBUF_INIT;
+ strbuf_addf(&sb, "%cpath=", info_term);
- strbuf_addstr(&sb, " path=");
- quote_path(entry->path, NULL, &path, QUOTE_PATH_QUOTE_SP);
- strbuf_addbuf(&sb, &path);
+ if (line_term) {
+ struct strbuf path = STRBUF_INIT;
- strbuf_release(&path);
+ quote_path(entry->path, NULL, &path, QUOTE_PATH_QUOTE_SP);
+ strbuf_addbuf(&sb, &path);
+
+ strbuf_release(&path);
+ } else {
+ strbuf_addstr(&sb, entry->path);
+ }
}
if (entry->type)
- strbuf_addf(&sb, " type=%s", type_name(entry->type));
+ strbuf_addf(&sb, "%ctype=%s", info_term, type_name(entry->type));
+
+ fwrite(sb.buf, sizeof(char), sb.len, stdout);
+ putchar(line_term);
- printf("?%s%s\n", oid_to_hex(&entry->entry.oid), sb.buf);
strbuf_release(&sb);
}
@@ -235,13 +253,18 @@ static void show_commit(struct commit *commit, void *data)
fputs(info->header_prefix, stdout);
if (revs->include_header) {
- if (!revs->graph)
+ if (!revs->graph && line_term)
fputs(get_revision_mark(revs, commit), stdout);
if (revs->abbrev_commit && revs->abbrev)
fputs(repo_find_unique_abbrev(the_repository, &commit->object.oid, revs->abbrev),
stdout);
else
fputs(oid_to_hex(&commit->object.oid), stdout);
+
+ if (!line_term) {
+ if (commit->object.flags & BOUNDARY)
+ printf("%cboundary=yes", info_term);
+ }
}
if (revs->print_parents) {
struct commit_list *parents = commit->parents;
@@ -263,7 +286,7 @@ static void show_commit(struct commit *commit, void *data)
if (revs->commit_format == CMIT_FMT_ONELINE)
putchar(' ');
else if (revs->include_header)
- putchar('\n');
+ putchar(line_term);
if (revs->verbose_header) {
struct strbuf buf = STRBUF_INIT;
@@ -357,10 +380,19 @@ static void show_object(struct object *obj, const char *name, void *cb_data)
return;
}
- if (arg_show_object_names)
- show_object_with_name(stdout, obj, name);
- else
- printf("%s\n", oid_to_hex(&obj->oid));
+ printf("%s", oid_to_hex(&obj->oid));
+
+ if (arg_show_object_names) {
+ if (line_term) {
+ putchar(info_term);
+ for (const char *p = name; *p && *p != '\n'; p++)
+ putchar(*p);
+ } else if (*name) {
+ printf("%cpath=%s", info_term, name);
+ }
+ }
+
+ putchar(line_term);
}
static void show_edge(struct commit *commit)
@@ -429,7 +461,8 @@ static int show_object_fast(
int exclude UNUSED,
uint32_t name_hash UNUSED,
struct packed_git *found_pack UNUSED,
- off_t found_offset UNUSED)
+ off_t found_offset UNUSED,
+ void *payload UNUSED)
{
fprintf(stdout, "%s\n", oid_to_hex(oid));
return 1;
@@ -634,19 +667,18 @@ int cmd_rev_list(int argc,
if (!strcmp(arg, "--exclude-promisor-objects")) {
fetch_if_missing = 0;
revs.exclude_promisor_objects = 1;
- break;
- }
- }
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
- if (skip_prefix(arg, "--missing=", &arg)) {
- if (revs.exclude_promisor_objects)
- die(_("options '%s' and '%s' cannot be used together"), "--exclude-promisor-objects", "--missing");
- if (parse_missing_action_value(arg))
- break;
+ } else if (skip_prefix(arg, "--missing=", &arg)) {
+ parse_missing_action_value(arg);
+ } else if (!strcmp(arg, "-z")) {
+ line_term = '\0';
+ info_term = '\0';
}
}
+ die_for_incompatible_opt2(revs.exclude_promisor_objects,
+ "--exclude_promisor_objects",
+ arg_missing_action, "--missing");
+
if (arg_missing_action)
revs.do_not_die_on_missing_objects = 1;
@@ -755,6 +787,20 @@ int cmd_rev_list(int argc,
usage(rev_list_usage);
}
+
+ /*
+ * Reject options currently incompatible with -z. For some options, this
+ * is not an inherent limitation and support may be implemented in the
+ * future.
+ */
+ if (!line_term) {
+ if (revs.graph || revs.verbose_header || show_disk_usage ||
+ info.show_timestamp || info.header_prefix || bisect_list ||
+ use_bitmap_index || revs.edge_hint || revs.left_right ||
+ revs.cherry_mark)
+ die(_("-z option used with unsupported option"));
+ }
+
if (revs.commit_format != CMIT_FMT_USERFORMAT)
revs.include_header = 1;
if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {
diff --git a/builtin/revert.c b/builtin/revert.c
index 4f5ef97549..e07c2217fe 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -260,8 +260,6 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
free(opts->strategy);
opts->strategy = xstrdup_or_null(strategy);
}
- if (!opts->strategy && getenv("GIT_TEST_MERGE_ALGORITHM"))
- opts->strategy = xstrdup(getenv("GIT_TEST_MERGE_ALGORITHM"));
free(options);
if (cmd == 'q') {
diff --git a/builtin/rm.c b/builtin/rm.c
index 12ae086a55..a6565a69cf 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -5,7 +5,6 @@
*/
#define USE_THE_REPOSITORY_VARIABLE
-#define DISABLE_SIGN_COMPARE_WARNINGS
#include "builtin.h"
#include "advice.h"
@@ -40,14 +39,12 @@ static struct {
} *entry;
} list;
-static int get_ours_cache_pos(const char *path, int pos)
+static int get_ours_cache_pos(const char *path, unsigned int pos)
{
- int i = -pos - 1;
-
- while ((i < the_repository->index->cache_nr) && !strcmp(the_repository->index->cache[i]->name, path)) {
- if (ce_stage(the_repository->index->cache[i]) == 2)
- return i;
- i++;
+ while ((pos < the_repository->index->cache_nr) && !strcmp(the_repository->index->cache[pos]->name, path)) {
+ if (ce_stage(the_repository->index->cache[pos]) == 2)
+ return pos;
+ pos++;
}
return -1;
}
@@ -58,7 +55,7 @@ static void print_error_files(struct string_list *files_list,
int *errs)
{
if (files_list->nr) {
- int i;
+ unsigned int i;
struct strbuf err_msg = STRBUF_INIT;
strbuf_addstr(&err_msg, main_msg);
@@ -83,7 +80,7 @@ static void submodules_absorb_gitdir_if_needed(void)
pos = index_name_pos(the_repository->index, name, strlen(name));
if (pos < 0) {
- pos = get_ours_cache_pos(name, pos);
+ pos = get_ours_cache_pos(name, -pos - 1);
if (pos < 0)
continue;
}
@@ -131,7 +128,7 @@ static int check_local_mod(struct object_id *head, int index_only)
* Skip unmerged entries except for populated submodules
* that could lose history when removed.
*/
- pos = get_ours_cache_pos(name, pos);
+ pos = get_ours_cache_pos(name, -pos - 1);
if (pos < 0)
continue;
@@ -314,7 +311,7 @@ int cmd_rm(int argc,
if (pathspec_needs_expanded_index(the_repository->index, &pathspec))
ensure_full_index(the_repository->index);
- for (i = 0; i < the_repository->index->cache_nr; i++) {
+ for (unsigned int i = 0; i < the_repository->index->cache_nr; i++) {
const struct cache_entry *ce = the_repository->index->cache[i];
if (!include_sparse &&
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 285cd3e433..f81209f23c 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -5,7 +5,7 @@
#include "hex.h"
#include "refs/refs-internal.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "object.h"
#include "string-list.h"
#include "parse-options.h"
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 14dcace5f8..1bf01591b2 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -9,6 +9,7 @@
#include "object-file.h"
#include "object-name.h"
#include "parse-options.h"
+#include "path.h"
#include "pathspec.h"
#include "strbuf.h"
#include "string-list.h"
@@ -335,7 +336,7 @@ static int write_patterns_and_update(struct pattern_list *pl)
sparse_filename = get_sparse_checkout_filename();
- if (safe_create_leading_directories(sparse_filename))
+ if (safe_create_leading_directories(the_repository, sparse_filename))
die(_("failed to create directory for sparse-checkout file"));
hold_lock_file_for_update(&lk, sparse_filename, LOCK_DIE_ON_ERROR);
@@ -491,7 +492,7 @@ static int sparse_checkout_init(int argc, const char **argv, const char *prefix,
FILE *fp;
/* assume we are in a fresh repo, but update the sparse-checkout file */
- if (safe_create_leading_directories(sparse_filename))
+ if (safe_create_leading_directories(the_repository, sparse_filename))
die(_("unable to create leading directories of %s"),
sparse_filename);
fp = xfopen(sparse_filename, "w");
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index c1a8029714..53da2116dd 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -28,7 +28,7 @@
#include "diff.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "advice.h"
#include "branch.h"
#include "list-objects-filter-options.h"
@@ -78,7 +78,7 @@ static int get_default_remote_submodule(const char *module_path, char **default_
int ret;
if (repo_submodule_init(&subrepo, the_repository, module_path,
- null_oid()) < 0)
+ null_oid(the_hash_algo)) < 0)
return die_message(_("could not get a repository handle for submodule '%s'"),
module_path);
ret = repo_get_default_remote(&subrepo, default_remote);
@@ -308,7 +308,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
displaypath = get_submodule_displaypath(path, info->prefix,
info->super_prefix);
- sub = submodule_from_path(the_repository, null_oid(), path);
+ sub = submodule_from_path(the_repository, null_oid(the_hash_algo), path);
if (!sub)
die(_("No url found for submodule path '%s' in .gitmodules"),
@@ -468,7 +468,7 @@ static void init_submodule(const char *path, const char *prefix,
displaypath = get_submodule_displaypath(path, prefix, super_prefix);
- sub = submodule_from_path(the_repository, null_oid(), path);
+ sub = submodule_from_path(the_repository, null_oid(the_hash_algo), path);
if (!sub)
die(_("No url found for submodule path '%s' in .gitmodules"),
@@ -645,14 +645,14 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
if (validate_submodule_path(path) < 0)
exit(128);
- if (!submodule_from_path(the_repository, null_oid(), path))
+ if (!submodule_from_path(the_repository, null_oid(the_hash_algo), path))
die(_("no submodule mapping found in .gitmodules for path '%s'"),
path);
displaypath = get_submodule_displaypath(path, prefix, super_prefix);
if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) {
- print_status(flags, 'U', path, null_oid(), displaypath);
+ print_status(flags, 'U', path, null_oid(the_hash_algo), displaypath);
goto cleanup;
}
@@ -912,7 +912,7 @@ static void generate_submodule_summary(struct summary_cb *info,
struct strbuf errmsg = STRBUF_INIT;
int total_commits = -1;
- if (!info->cached && oideq(&p->oid_dst, null_oid())) {
+ if (!info->cached && oideq(&p->oid_dst, null_oid(the_hash_algo))) {
if (S_ISGITLINK(p->mod_dst)) {
struct ref_store *refs = repo_get_submodule_ref_store(the_repository,
p->sm_path);
@@ -1051,7 +1051,7 @@ static void prepare_submodule_summary(struct summary_cb *info,
if (info->for_status && p->status != 'A' &&
(sub = submodule_from_path(the_repository,
- null_oid(), p->sm_path))) {
+ null_oid(the_hash_algo), p->sm_path))) {
char *config_key = NULL;
const char *value;
int ignore_all = 0;
@@ -1259,7 +1259,7 @@ static void sync_submodule(const char *path, const char *prefix,
if (validate_submodule_path(path) < 0)
exit(128);
- sub = submodule_from_path(the_repository, null_oid(), path);
+ sub = submodule_from_path(the_repository, null_oid(the_hash_algo), path);
if (sub && sub->url) {
if (starts_with_dot_dot_slash(sub->url) ||
@@ -1404,7 +1404,7 @@ static void deinit_submodule(const char *path, const char *prefix,
if (validate_submodule_path(path) < 0)
exit(128);
- sub = submodule_from_path(the_repository, null_oid(), path);
+ sub = submodule_from_path(the_repository, null_oid(the_hash_algo), path);
if (!sub || !sub->name)
goto cleanup;
@@ -1739,7 +1739,7 @@ static int clone_submodule(const struct module_clone_data *clone_data,
!is_empty_dir(clone_data_path))
die(_("directory not empty: '%s'"), clone_data_path);
- if (safe_create_leading_directories_const(sm_gitdir) < 0)
+ if (safe_create_leading_directories_const(the_repository, sm_gitdir) < 0)
die(_("could not create directory '%s'"), sm_gitdir);
prepare_possible_alternates(clone_data->name, reference);
@@ -1800,7 +1800,7 @@ static int clone_submodule(const struct module_clone_data *clone_data,
if (clone_data->require_init && !stat(clone_data_path, &st) &&
!is_empty_dir(clone_data_path))
die(_("directory not empty: '%s'"), clone_data_path);
- if (safe_create_leading_directories_const(clone_data_path) < 0)
+ if (safe_create_leading_directories_const(the_repository, clone_data_path) < 0)
die(_("could not create directory '%s'"), clone_data_path);
path = xstrfmt("%s/index", sm_gitdir);
unlink_or_warn(path);
@@ -1929,7 +1929,7 @@ static int determine_submodule_update_strategy(struct repository *r,
enum submodule_update_type update,
struct submodule_update_strategy *out)
{
- const struct submodule *sub = submodule_from_path(r, null_oid(), path);
+ const struct submodule *sub = submodule_from_path(r, null_oid(the_hash_algo), path);
char *key;
const char *val;
int ret;
@@ -2089,7 +2089,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
goto cleanup;
}
- sub = submodule_from_path(the_repository, null_oid(), ce->name);
+ sub = submodule_from_path(the_repository, null_oid(the_hash_algo), ce->name);
if (!sub) {
next_submodule_warn_missing(suc, out, displaypath);
@@ -2485,7 +2485,7 @@ static int remote_submodule_branch(const char *path, const char **branch)
char *key;
*branch = NULL;
- sub = submodule_from_path(the_repository, null_oid(), path);
+ sub = submodule_from_path(the_repository, null_oid(the_hash_algo), path);
if (!sub)
return die_message(_("could not initialize submodule at path '%s'"),
path);
@@ -2531,7 +2531,7 @@ static int ensure_core_worktree(const char *path)
const char *cw;
struct repository subrepo;
- if (repo_submodule_init(&subrepo, the_repository, path, null_oid()))
+ if (repo_submodule_init(&subrepo, the_repository, path, null_oid(the_hash_algo)))
return die_message(_("could not get a repository handle for submodule '%s'"),
path);
@@ -2644,7 +2644,7 @@ static int update_submodule(struct update_data *update_data)
return ret;
if (update_data->just_cloned)
- oidcpy(&update_data->suboid, null_oid());
+ oidcpy(&update_data->suboid, null_oid(the_hash_algo));
else if (repo_resolve_gitlink_ref(the_repository, update_data->sm_path,
"HEAD", &update_data->suboid))
return die_message(_("Unable to find current revision in submodule path '%s'"),
@@ -2697,8 +2697,8 @@ static int update_submodule(struct update_data *update_data)
struct update_data next = *update_data;
next.prefix = NULL;
- oidcpy(&next.oid, null_oid());
- oidcpy(&next.suboid, null_oid());
+ oidcpy(&next.oid, null_oid(the_hash_algo));
+ oidcpy(&next.suboid, null_oid(the_hash_algo));
cp.dir = update_data->sm_path;
cp.git_cmd = 1;
@@ -3057,7 +3057,7 @@ static int module_set_url(int argc, const char **argv, const char *prefix,
if (argc != 2 || !(path = argv[0]) || !(newurl = argv[1]))
usage_with_options(usage, options);
- sub = submodule_from_path(the_repository, null_oid(), path);
+ sub = submodule_from_path(the_repository, null_oid(the_hash_algo), path);
if (!sub)
die(_("no submodule mapping found in .gitmodules for path '%s'"),
@@ -3113,7 +3113,7 @@ static int module_set_branch(int argc, const char **argv, const char *prefix,
if (argc != 1 || !(path = argv[0]))
usage_with_options(usage, options);
- sub = submodule_from_path(the_repository, null_oid(), path);
+ sub = submodule_from_path(the_repository, null_oid(the_hash_algo), path);
if (!sub)
die(_("no submodule mapping found in .gitmodules for path '%s'"),
diff --git a/builtin/tag.c b/builtin/tag.c
index 7597d93c71..4742b27d16 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -17,8 +17,9 @@
#include "gettext.h"
#include "hex.h"
#include "refs.h"
+#include "object-file.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "path.h"
#include "tag.h"
#include "parse-options.h"
@@ -172,7 +173,7 @@ static int do_sign(struct strbuf *buffer, struct object_id **compat_oid,
if (compat) {
const struct git_hash_algo *algo = the_repository->hash_algo;
- if (convert_object_file(&compat_buf, algo, compat,
+ if (convert_object_file(the_repository ,&compat_buf, algo, compat,
buffer->buf, buffer->len, OBJ_TAG, 1))
goto out;
if (sign_buffer(&compat_buf, &compat_sig, keyid))
diff --git a/builtin/unpack-file.c b/builtin/unpack-file.c
index fb5fcbc40a..e33acfc4ee 100644
--- a/builtin/unpack-file.c
+++ b/builtin/unpack-file.c
@@ -2,8 +2,9 @@
#include "builtin.h"
#include "config.h"
#include "hex.h"
+#include "object-file.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
static char *create_temp_file(struct object_id *oid)
{
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index 8383bcf404..661be789f1 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -8,7 +8,8 @@
#include "gettext.h"
#include "git-zlib.h"
#include "hex.h"
-#include "object-store-ll.h"
+#include "object-file.h"
+#include "object-store.h"
#include "object.h"
#include "delta.h"
#include "pack.h"
@@ -505,7 +506,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
* has not been resolved yet.
*/
oidclr(&obj_list[nr].oid, the_repository->hash_algo);
- add_delta_to_list(nr, null_oid(), base_offset,
+ add_delta_to_list(nr, null_oid(the_hash_algo), base_offset,
delta_data, delta_size);
return;
}
@@ -553,7 +554,8 @@ static void unpack_one(unsigned nr)
switch (type) {
case OBJ_BLOB:
- if (!dry_run && size > big_file_threshold) {
+ if (!dry_run &&
+ size > repo_settings_get_big_file_threshold(the_repository)) {
stream_blob(size, nr);
return;
}
@@ -668,6 +670,7 @@ int cmd_unpack_objects(int argc,
the_hash_algo->init_fn(&ctx);
unpack_all();
git_hash_update(&ctx, buffer, offset);
+ the_hash_algo->init_fn(&tmp_ctx);
git_hash_clone(&tmp_ctx, &ctx);
git_hash_final_oid(&oid, &tmp_ctx);
if (strict) {
diff --git a/builtin/update-index.c b/builtin/update-index.c
index ee64b02267..538b619ba4 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -304,7 +304,7 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
if (index_path(the_repository->index, &ce->oid, path, st,
- info_only ? 0 : HASH_WRITE_OBJECT)) {
+ info_only ? 0 : INDEX_WRITE_OBJECT)) {
discard_cache_entry(ce);
return -1;
}
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 1d541e13ad..2b1e336ba1 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -5,6 +5,7 @@
#include "config.h"
#include "gettext.h"
#include "hash.h"
+#include "hex.h"
#include "refs.h"
#include "object-name.h"
#include "parse-options.h"
@@ -13,7 +14,7 @@
static const char * const git_update_ref_usage[] = {
N_("git update-ref [<options>] -d <refname> [<old-oid>]"),
N_("git update-ref [<options>] <refname> <new-oid> [<old-oid>]"),
- N_("git update-ref [<options>] --stdin [-z]"),
+ N_("git update-ref [<options>] --stdin [-z] [--batch-updates]"),
NULL
};
@@ -503,7 +504,7 @@ static void parse_cmd_symref_verify(struct ref_transaction *transaction,
*/
old_target = parse_next_refname(&next);
if (!old_target)
- oidcpy(&old_oid, null_oid());
+ oidcpy(&old_oid, null_oid(the_hash_algo));
if (*next != line_termination)
die("symref-verify %s: extra input: %s", refname, next);
@@ -565,6 +566,49 @@ static void parse_cmd_abort(struct ref_transaction *transaction,
report_ok("abort");
}
+static void print_rejected_refs(const char *refname,
+ const struct object_id *old_oid,
+ const struct object_id *new_oid,
+ const char *old_target,
+ const char *new_target,
+ enum ref_transaction_error err,
+ void *cb_data UNUSED)
+{
+ struct strbuf sb = STRBUF_INIT;
+ const char *reason = "";
+
+ switch (err) {
+ case REF_TRANSACTION_ERROR_NAME_CONFLICT:
+ reason = "refname conflict";
+ break;
+ case REF_TRANSACTION_ERROR_CREATE_EXISTS:
+ reason = "reference already exists";
+ break;
+ case REF_TRANSACTION_ERROR_NONEXISTENT_REF:
+ reason = "reference does not exist";
+ break;
+ case REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE:
+ reason = "incorrect old value provided";
+ break;
+ case REF_TRANSACTION_ERROR_INVALID_NEW_VALUE:
+ reason = "invalid new value provided";
+ break;
+ case REF_TRANSACTION_ERROR_EXPECTED_SYMREF:
+ reason = "expected symref but found regular ref";
+ break;
+ default:
+ reason = "unkown failure";
+ }
+
+ strbuf_addf(&sb, "rejected %s %s %s %s\n", refname,
+ new_oid ? oid_to_hex(new_oid) : new_target,
+ old_oid ? oid_to_hex(old_oid) : old_target,
+ reason);
+
+ fwrite(sb.buf, sb.len, 1, stdout);
+ strbuf_release(&sb);
+}
+
static void parse_cmd_commit(struct ref_transaction *transaction,
const char *next, const char *end UNUSED)
{
@@ -573,6 +617,10 @@ static void parse_cmd_commit(struct ref_transaction *transaction,
die("commit: extra input: %s", next);
if (ref_transaction_commit(transaction, &error))
die("commit: %s", error.buf);
+
+ ref_transaction_for_each_rejected_update(transaction,
+ print_rejected_refs, NULL);
+
report_ok("commit");
ref_transaction_free(transaction);
}
@@ -609,7 +657,7 @@ static const struct parse_cmd {
{ "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED },
};
-static void update_refs_stdin(void)
+static void update_refs_stdin(unsigned int flags)
{
struct strbuf input = STRBUF_INIT, err = STRBUF_INIT;
enum update_refs_state state = UPDATE_REFS_OPEN;
@@ -617,7 +665,7 @@ static void update_refs_stdin(void)
int i, j;
transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
- 0, &err);
+ flags, &err);
if (!transaction)
die("%s", err.buf);
@@ -685,7 +733,7 @@ static void update_refs_stdin(void)
*/
state = cmd->state;
transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
- 0, &err);
+ flags, &err);
if (!transaction)
die("%s", err.buf);
@@ -701,6 +749,8 @@ static void update_refs_stdin(void)
/* Commit by default if no transaction was requested. */
if (ref_transaction_commit(transaction, &err))
die("%s", err.buf);
+ ref_transaction_for_each_rejected_update(transaction,
+ print_rejected_refs, NULL);
ref_transaction_free(transaction);
break;
case UPDATE_REFS_STARTED:
@@ -727,6 +777,8 @@ int cmd_update_ref(int argc,
struct object_id oid, oldoid;
int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0;
int create_reflog = 0;
+ unsigned int flags = 0;
+
struct option options[] = {
OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")),
OPT_BOOL('d', NULL, &delete, N_("delete the reference")),
@@ -735,6 +787,8 @@ int cmd_update_ref(int argc,
OPT_BOOL('z', NULL, &end_null, N_("stdin has NUL-terminated arguments")),
OPT_BOOL( 0 , "stdin", &read_stdin, N_("read updates from stdin")),
OPT_BOOL( 0 , "create-reflog", &create_reflog, N_("create a reflog")),
+ OPT_BIT('0', "batch-updates", &flags, N_("batch reference updates"),
+ REF_TRANSACTION_ALLOW_FAILURE),
OPT_END(),
};
@@ -756,8 +810,10 @@ int cmd_update_ref(int argc,
usage_with_options(git_update_ref_usage, options);
if (end_null)
line_termination = '\0';
- update_refs_stdin();
+ update_refs_stdin(flags);
return 0;
+ } else if (flags & REF_TRANSACTION_ALLOW_FAILURE) {
+ die("--batch-updates can only be used with --stdin");
}
if (end_null)
diff --git a/builtin/update-server-info.c b/builtin/update-server-info.c
index d7467290a8..ba702d30ef 100644
--- a/builtin/update-server-info.c
+++ b/builtin/update-server-info.c
@@ -20,8 +20,8 @@ int cmd_update_server_info(int argc,
OPT_END()
};
- if (repo)
- repo_config(repo, git_default_config, NULL);
+ repo_config(repo, git_default_config, NULL);
+
argc = parse_options(argc, argv, prefix, options,
update_server_info_usage, 0);
if (argc > 0)
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 48448a8355..88a36ea9f8 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -348,7 +348,7 @@ static void copy_sparse_checkout(const char *worktree_git_dir)
char *to_file = xstrfmt("%s/info/sparse-checkout", worktree_git_dir);
if (file_exists(from_file)) {
- if (safe_create_leading_directories(to_file) ||
+ if (safe_create_leading_directories(the_repository, to_file) ||
copy_file(to_file, from_file, 0666))
error(_("failed to copy '%s' to '%s'; sparse-checkout may not work correctly"),
from_file, to_file);
@@ -367,7 +367,7 @@ static void copy_filtered_worktree_config(const char *worktree_git_dir)
struct config_set cs = { { 0 } };
int bare;
- if (safe_create_leading_directories(to_file) ||
+ if (safe_create_leading_directories(the_repository, to_file) ||
copy_file(to_file, from_file, 0666)) {
error(_("failed to copy worktree config from '%s' to '%s'"),
from_file, to_file);
@@ -466,7 +466,7 @@ static int add_worktree(const char *path, const char *refname,
name = sb_name.buf;
repo_git_path_replace(the_repository, &sb_repo, "worktrees/%s", name);
len = sb_repo.len;
- if (safe_create_leading_directories_const(sb_repo.buf))
+ if (safe_create_leading_directories_const(the_repository, sb_repo.buf))
die_errno(_("could not create leading directories of '%s'"),
sb_repo.buf);
@@ -498,7 +498,7 @@ static int add_worktree(const char *path, const char *refname,
write_file(sb.buf, _("initializing"));
strbuf_addf(&sb_git, "%s/.git", path);
- if (safe_create_leading_directories_const(sb_git.buf))
+ if (safe_create_leading_directories_const(the_repository, sb_git.buf))
die_errno(_("could not create leading directories of '%s'"),
sb_git.buf);
junk_work_tree = xstrdup(path);
@@ -578,7 +578,7 @@ done:
strvec_pushl(&opt.env, "GIT_DIR", "GIT_WORK_TREE", NULL);
strvec_pushl(&opt.args,
- oid_to_hex(null_oid()),
+ oid_to_hex(null_oid(the_hash_algo)),
oid_to_hex(&commit->object.oid),
"1",
NULL);
diff --git a/bulk-checkin.c b/bulk-checkin.c
index 20f2da67b9..c31c31b18d 100644
--- a/bulk-checkin.c
+++ b/bulk-checkin.c
@@ -3,7 +3,6 @@
*/
#define USE_THE_REPOSITORY_VARIABLE
-#define DISABLE_SIGN_COMPARE_WARNINGS
#include "git-compat-util.h"
#include "bulk-checkin.h"
@@ -18,7 +17,7 @@
#include "tmp-objdir.h"
#include "packfile.h"
#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
static int odb_transaction_nesting;
@@ -44,7 +43,7 @@ static void finish_tmp_packfile(struct strbuf *basename,
{
char *idx_tmp_name = NULL;
- stage_tmp_packfiles(the_hash_algo, basename, pack_tmp_name,
+ stage_tmp_packfiles(the_repository, basename, pack_tmp_name,
written_list, nr_written, NULL, pack_idx_opts, hash,
&idx_tmp_name);
rename_tmp_packfile_idx(basename, &idx_tmp_name);
@@ -56,7 +55,6 @@ static void flush_bulk_checkin_packfile(struct bulk_checkin_packfile *state)
{
unsigned char hash[GIT_MAX_RAWSZ];
struct strbuf packname = STRBUF_INIT;
- int i;
if (!state->f)
return;
@@ -82,7 +80,7 @@ static void flush_bulk_checkin_packfile(struct bulk_checkin_packfile *state)
finish_tmp_packfile(&packname, state->pack_tmp_name,
state->written, state->nr_written,
&state->pack_idx_opts, hash);
- for (i = 0; i < state->nr_written; i++)
+ for (uint32_t i = 0; i < state->nr_written; i++)
free(state->written[i]);
clear_exit:
@@ -131,14 +129,12 @@ static void flush_batch_fsync(void)
static int already_written(struct bulk_checkin_packfile *state, struct object_id *oid)
{
- int i;
-
/* The object may already exist in the repository */
if (repo_has_object_file(the_repository, oid))
return 1;
/* Might want to keep the list sorted */
- for (i = 0; i < state->nr_written; i++)
+ for (uint32_t i = 0; i < state->nr_written; i++)
if (oideq(&state->written[i]->oid, oid))
return 1;
@@ -171,7 +167,7 @@ static int stream_blob_to_pack(struct bulk_checkin_packfile *state,
unsigned char obuf[16384];
unsigned hdrlen;
int status = Z_OK;
- int write_object = (flags & HASH_WRITE_OBJECT);
+ int write_object = (flags & INDEX_WRITE_OBJECT);
off_t offset = 0;
git_deflate_init(&s, pack_compression_level);
@@ -182,13 +178,13 @@ static int stream_blob_to_pack(struct bulk_checkin_packfile *state,
while (status != Z_STREAM_END) {
if (size && !s.avail_in) {
- ssize_t rsize = size < sizeof(ibuf) ? size : sizeof(ibuf);
+ size_t rsize = size < sizeof(ibuf) ? size : sizeof(ibuf);
ssize_t read_result = read_in_full(fd, ibuf, rsize);
if (read_result < 0)
die_errno("failed to read from '%s'", path);
- if (read_result != rsize)
- die("failed to read %d bytes from '%s'",
- (int)rsize, path);
+ if ((size_t)read_result != rsize)
+ die("failed to read %u bytes from '%s'",
+ (unsigned)rsize, path);
offset += rsize;
if (*already_hashed_to < offset) {
size_t hsize = offset - *already_hashed_to;
@@ -241,10 +237,10 @@ static int stream_blob_to_pack(struct bulk_checkin_packfile *state,
static void prepare_to_stream(struct bulk_checkin_packfile *state,
unsigned flags)
{
- if (!(flags & HASH_WRITE_OBJECT) || state->f)
+ if (!(flags & INDEX_WRITE_OBJECT) || state->f)
return;
- state->f = create_tmp_packfile(&state->pack_tmp_name);
+ state->f = create_tmp_packfile(the_repository, &state->pack_tmp_name);
reset_pack_idx_option(&state->pack_idx_opts);
/* Pretend we are going to write only one object */
@@ -275,7 +271,7 @@ static int deflate_blob_to_pack(struct bulk_checkin_packfile *state,
git_hash_update(&ctx, obuf, header_len);
/* Note: idx is non-NULL when we are writing */
- if ((flags & HASH_WRITE_OBJECT) != 0) {
+ if ((flags & INDEX_WRITE_OBJECT) != 0) {
CALLOC_ARRAY(idx, 1);
prepare_to_stream(state, flags);
diff --git a/bulk-checkin.h b/bulk-checkin.h
index aa7286a7b3..7246ea58dc 100644
--- a/bulk-checkin.h
+++ b/bulk-checkin.h
@@ -9,6 +9,21 @@
void prepare_loose_object_bulk_checkin(void);
void fsync_loose_object_bulk_checkin(int fd, const char *filename);
+/*
+ * This creates one packfile per large blob unless bulk-checkin
+ * machinery is "plugged".
+ *
+ * This also bypasses the usual "convert-to-git" dance, and that is on
+ * purpose. We could write a streaming version of the converting
+ * functions and insert that before feeding the data to fast-import
+ * (or equivalent in-core API described above). However, that is
+ * somewhat complicated, as we do not know the size of the filter
+ * result, which we need to know beforehand when writing a git object.
+ * Since the primary motivation for trying to stream from the working
+ * tree file and to avoid mmaping it in core is to deal with large
+ * binary blobs, they generally do not want to get any conversion, and
+ * callers should avoid this code path when filters are requested.
+ */
int index_blob_bulk_checkin(struct object_id *oid,
int fd, size_t size,
const char *path, unsigned flags);
diff --git a/bundle-uri.c b/bundle-uri.c
index 744257c49c..96d2ba726d 100644
--- a/bundle-uri.c
+++ b/bundle-uri.c
@@ -14,7 +14,7 @@
#include "fetch-pack.h"
#include "remote.h"
#include "trace2.h"
-#include "object-store-ll.h"
+#include "object-store.h"
static struct {
enum bundle_list_heuristic heuristic;
diff --git a/bundle.c b/bundle.c
index d7ad690843..b0a3fee2ef 100644
--- a/bundle.c
+++ b/bundle.c
@@ -7,7 +7,7 @@
#include "environment.h"
#include "gettext.h"
#include "hex.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "repository.h"
#include "object.h"
#include "commit.h"
@@ -384,6 +384,7 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
{
int i;
int ref_count = 0;
+ struct strset objects = STRSET_INIT;
for (i = 0; i < revs->pending.nr; i++) {
struct object_array_entry *e = revs->pending.objects + i;
@@ -401,6 +402,9 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
flag = 0;
display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
+ if (strset_contains(&objects, display_ref))
+ goto skip_write_ref;
+
if (e->item->type == OBJ_TAG &&
!is_tag_in_date_range(e->item, revs)) {
e->item->flags |= UNINTERESTING;
@@ -423,6 +427,7 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
}
ref_count++;
+ strset_add(&objects, display_ref);
write_or_die(bundle_fd, oid_to_hex(&e->item->oid), the_hash_algo->hexsz);
write_or_die(bundle_fd, " ", 1);
write_or_die(bundle_fd, display_ref, strlen(display_ref));
@@ -431,6 +436,8 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
free(ref);
}
+ strset_clear(&objects);
+
/* end header */
write_or_die(bundle_fd, "\n", 1);
return ref_count;
@@ -566,7 +573,6 @@ int create_bundle(struct repository *r, const char *path,
*/
revs.blob_objects = revs.tree_objects = 0;
traverse_commit_list(&revs, write_bundle_prerequisites, NULL, &bpi);
- object_array_remove_duplicates(&revs_copy.pending);
/* write bundle refs */
ref_count = write_bundle_refs(bundle_fd, &revs_copy);
diff --git a/cache-tree.c b/cache-tree.c
index bcbcad3d61..c0e1e9ee1d 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -10,7 +10,7 @@
#include "cache-tree.h"
#include "bulk-checkin.h"
#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "read-cache-ll.h"
#include "replace-object.h"
#include "repository.h"
@@ -452,7 +452,7 @@ static int update_one(struct cache_tree *it,
OBJ_TREE, &it->oid);
} else if (write_object_file_flags(buffer.buf, buffer.len, OBJ_TREE,
&it->oid, NULL, flags & WRITE_TREE_SILENT
- ? HASH_SILENT : 0)) {
+ ? WRITE_OBJECT_FILE_SILENT : 0)) {
strbuf_release(&buffer);
return -1;
}
diff --git a/ci/check-unsafe-assertions.sh b/ci/check-unsafe-assertions.sh
new file mode 100755
index 0000000000..233bd9dfbc
--- /dev/null
+++ b/ci/check-unsafe-assertions.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+make CHECK_ASSERTION_SIDE_EFFECTS=1 >compiler_output 2>compiler_error
+if test $? != 0
+then
+ echo >&2 "ERROR: The compiler could not verify the following assert()"
+ echo >&2 " calls are free of side-effects. Please replace with"
+ echo >&2 " ASSERT() calls."
+ grep undefined.reference.to..not_supposed_to_survive compiler_error |
+ sed -e s/:[^:]*$// | sort | uniq | tr ':' ' ' |
+ while read f l
+ do
+ printf "${f}:${l}\n "
+ awk -v start="$l" 'NR >= start { print; if (/\);/) exit }' $f
+ done
+ exit 1
+fi
+rm compiler_output compiler_error
diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh
index 0df74610d0..da19dada2c 100755
--- a/ci/install-dependencies.sh
+++ b/ci/install-dependencies.sh
@@ -31,7 +31,7 @@ alpine-*)
;;
fedora-*|almalinux-*)
dnf -yq update >/dev/null &&
- dnf -yq install shadow-utils sudo make gcc findutils diffutils perl python3 gettext zlib-devel expat-devel openssl-devel curl-devel pcre2-devel >/dev/null
+ dnf -yq install shadow-utils sudo make gcc findutils diffutils perl python3 gawk gettext zlib-devel expat-devel openssl-devel curl-devel pcre2-devel >/dev/null
;;
ubuntu-*|i386/ubuntu-*|debian-*)
# Required so that apt doesn't wait for user input on certain packages.
@@ -66,16 +66,29 @@ ubuntu-*|i386/ubuntu-*|debian-*)
mkdir --parents "$CUSTOM_PATH"
wget --quiet --directory-prefix="$CUSTOM_PATH" \
- "$P4WHENCE/bin.linux26x86_64/p4d" "$P4WHENCE/bin.linux26x86_64/p4"
- chmod a+x "$CUSTOM_PATH/p4d" "$CUSTOM_PATH/p4"
-
- wget --quiet "$LFSWHENCE/git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
+ "$P4WHENCE/bin.linux26x86_64/p4d" \
+ "$P4WHENCE/bin.linux26x86_64/p4" &&
+ chmod a+x "$CUSTOM_PATH/p4d" "$CUSTOM_PATH/p4" || {
+ rm -f "$CUSTOM_PATH/p4"
+ rm -f "$CUSTOM_PATH/p4d"
+ echo >&2 "P4 download (optional) failed"
+ }
+
+ wget --quiet \
+ "$LFSWHENCE/git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz" &&
tar -xzf "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz" \
- -C "$CUSTOM_PATH" --strip-components=1 "git-lfs-$LINUX_GIT_LFS_VERSION/git-lfs"
- rm "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
-
- wget --quiet "$JGITWHENCE" --output-document="$CUSTOM_PATH/jgit"
- chmod a+x "$CUSTOM_PATH/jgit"
+ -C "$CUSTOM_PATH" --strip-components=1 \
+ "git-lfs-$LINUX_GIT_LFS_VERSION/git-lfs" &&
+ rm "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz" || {
+ rm -f "$CUSTOM_PATH/git-lfs"
+ echo >&2 "LFS download (optional) failed"
+ }
+
+ wget --quiet "$JGITWHENCE" --output-document="$CUSTOM_PATH/jgit" &&
+ chmod a+x "$CUSTOM_PATH/jgit" || {
+ rm -f "$CUSTOM_PATH/jgit"
+ echo >&2 "JGit download (optional) failed"
+ }
;;
esac
;;
@@ -119,7 +132,7 @@ StaticAnalysis)
sparse)
sudo apt-get -q update -q
sudo apt-get -q -y install libssl-dev libcurl4-openssl-dev \
- libexpat-dev gettext zlib1g-dev
+ libexpat-dev gettext zlib1g-dev sparse
;;
Documentation)
sudo apt-get -q update
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index 1c69846723..f99b7db2ee 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -20,7 +20,6 @@ linux-breaking-changes)
linux-TEST-vars)
export OPENSSL_SHA1_UNSAFE=YesPlease
export GIT_TEST_SPLIT_INDEX=yes
- export GIT_TEST_MERGE_ALGORITHM=recursive
export GIT_TEST_FULL_IN_PACK_ARRAY=true
export GIT_TEST_OE_SIZE=10
export GIT_TEST_OE_DELTA_SIZE=5
diff --git a/ci/run-static-analysis.sh b/ci/run-static-analysis.sh
index 0d51e5ce0e..ae714e020a 100755
--- a/ci/run-static-analysis.sh
+++ b/ci/run-static-analysis.sh
@@ -31,4 +31,6 @@ exit 1
make check-pot
+${0%/*}/check-unsafe-assertions.sh
+
save_good_tree
diff --git a/ci/test-documentation.sh b/ci/test-documentation.sh
index 6c018b673e..49f87f50fd 100755
--- a/ci/test-documentation.sh
+++ b/ci/test-documentation.sh
@@ -15,6 +15,13 @@ filter_log () {
"$1"
}
+check_docs () {
+ test -s "$1"/Documentation/git.html &&
+ test -s "$1"/Documentation/git.xml &&
+ test -s "$1"/Documentation/git.1 &&
+ grep "<meta name=\"generator\" content=\"$2 " "$1"/Documentation/git.html
+}
+
make check-builtins
make check-docs
@@ -23,10 +30,7 @@ make doc > >(tee stdout.log) 2> >(tee stderr.raw >&2)
cat stderr.raw
filter_log stderr.raw >stderr.log
test ! -s stderr.log
-test -s Documentation/git.html
-test -s Documentation/git.xml
-test -s Documentation/git.1
-grep '<meta name="generator" content="AsciiDoc ' Documentation/git.html
+check_docs . AsciiDoc
rm -f stdout.log stderr.log stderr.raw
check_unignored_build_artifacts
@@ -37,10 +41,21 @@ make USE_ASCIIDOCTOR=1 doc > >(tee stdout.log) 2> >(tee stderr.raw >&2)
cat stderr.raw
filter_log stderr.raw >stderr.log
test ! -s stderr.log
-test -s Documentation/git.html
-grep '<meta name="generator" content="Asciidoctor ' Documentation/git.html
+check_docs . Asciidoctor
rm -f stdout.log stderr.log stderr.raw
check_unignored_build_artifacts
+# Build docs with Meson and AsciiDoc
+meson setup build-asciidoc -Ddocs=html,man -Ddocs_backend=asciidoc
+meson compile -C build-asciidoc
+check_docs build-asciidoc AsciiDoc
+rm -rf build-asciidoc
+
+# Build docs with Meson and AsciiDoctor
+meson setup build-asciidoctor -Ddocs=html,man -Ddocs_backend=asciidoctor
+meson compile -C build-asciidoctor
+check_docs build-asciidoctor Asciidoctor
+rm -rf build-asciidoctor
+
save_good_tree
diff --git a/combine-diff.c b/combine-diff.c
index 9527f3160d..dfae9f7995 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -2,7 +2,7 @@
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "git-compat-util.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "commit.h"
#include "convert.h"
#include "diff.h"
@@ -1066,7 +1066,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
&result_size, NULL, NULL);
} else if (textconv) {
struct diff_filespec *df = alloc_filespec(elem->path);
- fill_filespec(df, null_oid(), 0, st.st_mode);
+ fill_filespec(df, null_oid(the_hash_algo), 0, st.st_mode);
result_size = fill_textconv(opt->repo, textconv, df, &result);
free_filespec(df);
} else if (0 <= (fd = open(elem->path, O_RDONLY))) {
diff --git a/commit-graph.c b/commit-graph.c
index 1021ccb983..6394752b0b 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -13,8 +13,7 @@
#include "refs.h"
#include "hash-lookup.h"
#include "commit-graph.h"
-#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "oid-array.h"
#include "path.h"
#include "alloc.h"
@@ -2065,7 +2064,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
ctx->graph_name = get_commit_graph_filename(ctx->odb);
}
- if (safe_create_leading_directories(ctx->graph_name)) {
+ if (safe_create_leading_directories(the_repository, ctx->graph_name)) {
error(_("unable to create leading directories of %s"),
ctx->graph_name);
return -1;
@@ -2090,11 +2089,13 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
return -1;
}
- f = hashfd(get_tempfile_fd(graph_layer), get_tempfile_path(graph_layer));
+ f = hashfd(the_repository->hash_algo,
+ get_tempfile_fd(graph_layer), get_tempfile_path(graph_layer));
} else {
hold_lock_file_for_update_mode(&lk, ctx->graph_name,
LOCK_DIE_ON_ERROR, 0444);
- f = hashfd(get_lock_file_fd(&lk), get_lock_file_path(&lk));
+ f = hashfd(the_repository->hash_algo,
+ get_lock_file_fd(&lk), get_lock_file_path(&lk));
}
cf = init_chunkfile(f);
@@ -2716,7 +2717,8 @@ static void graph_report(const char *fmt, ...)
static int commit_graph_checksum_valid(struct commit_graph *g)
{
- return hashfile_checksum_valid(g->data, g->data_len);
+ return hashfile_checksum_valid(the_repository->hash_algo,
+ g->data, g->data_len);
}
static int verify_one_commit_graph(struct repository *r,
diff --git a/commit-graph.h b/commit-graph.h
index 6781940195..13f662827d 100644
--- a/commit-graph.h
+++ b/commit-graph.h
@@ -1,7 +1,7 @@
#ifndef COMMIT_GRAPH_H
#define COMMIT_GRAPH_H
-#include "object-store-ll.h"
+#include "object-store.h"
#include "oidset.h"
#define GIT_TEST_COMMIT_GRAPH "GIT_TEST_COMMIT_GRAPH"
diff --git a/commit.c b/commit.c
index 6efdb03997..e915b2b9a1 100644
--- a/commit.c
+++ b/commit.c
@@ -9,7 +9,7 @@
#include "hex.h"
#include "repository.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "utf8.h"
#include "diff.h"
#include "revision.h"
@@ -29,6 +29,7 @@
#include "tree.h"
#include "hook.h"
#include "parse.h"
+#include "object-file.h"
#include "object-file-convert.h"
static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
@@ -780,19 +781,17 @@ static void clear_commit_marks_1(struct commit_list **plist,
void clear_commit_marks_many(size_t nr, struct commit **commit, unsigned int mark)
{
- for (size_t i = 0; i < nr; i++) {
- struct commit_list *list = NULL;
-
- clear_commit_marks_1(&list, *commit, mark);
- while (list)
- clear_commit_marks_1(&list, pop_commit(&list), mark);
- commit++;
- }
+ for (size_t i = 0; i < nr; i++)
+ clear_commit_marks(commit[i], mark);
}
void clear_commit_marks(struct commit *commit, unsigned int mark)
{
- clear_commit_marks_many(1, &commit, mark);
+ struct commit_list *list = NULL;
+
+ clear_commit_marks_1(&list, commit, mark);
+ while (list)
+ clear_commit_marks_1(&list, pop_commit(&list), mark);
}
struct commit *pop_commit(struct commit_list **stack)
@@ -1380,7 +1379,7 @@ static int convert_commit_extra_headers(const struct commit_extra_header *orig,
struct commit_extra_header *new;
CALLOC_ARRAY(new, 1);
if (!strcmp(orig->key, "mergetag")) {
- if (convert_object_file(&out, algo, compat,
+ if (convert_object_file(the_repository, &out, algo, compat,
orig->value, orig->len,
OBJ_TAG, 1)) {
free(new);
diff --git a/compat/mingw-posix.h b/compat/mingw-posix.h
new file mode 100644
index 0000000000..88e0cf9292
--- /dev/null
+++ b/compat/mingw-posix.h
@@ -0,0 +1,435 @@
+#ifndef COMPAT_MINGW_POSIX_H
+#define COMPAT_MINGW_POSIX_H
+
+#ifdef __MINGW64_VERSION_MAJOR
+#include <stdint.h>
+#include <wchar.h>
+typedef _sigset_t sigset_t;
+#endif
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+/* MinGW-w64 reports to have flockfile, but it does not actually have it. */
+#ifdef __MINGW64_VERSION_MAJOR
+#undef _POSIX_THREAD_SAFE_FUNCTIONS
+#endif
+
+/*
+ * things that are not available in header files
+ */
+
+typedef int uid_t;
+typedef int socklen_t;
+#ifndef __MINGW64_VERSION_MAJOR
+typedef int pid_t;
+#define hstrerror strerror
+#endif
+
+#define S_IFLNK 0120000 /* Symbolic link */
+#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK)
+#define S_ISSOCK(x) 0
+
+#ifndef S_IRWXG
+#define S_IRGRP 0
+#define S_IWGRP 0
+#define S_IXGRP 0
+#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
+#endif
+#ifndef S_IRWXO
+#define S_IROTH 0
+#define S_IWOTH 0
+#define S_IXOTH 0
+#define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
+#endif
+
+#define S_ISUID 0004000
+#define S_ISGID 0002000
+#define S_ISVTX 0001000
+
+#define WIFEXITED(x) 1
+#define WIFSIGNALED(x) 0
+#define WEXITSTATUS(x) ((x) & 0xff)
+#define WTERMSIG(x) SIGTERM
+
+#ifndef EWOULDBLOCK
+#define EWOULDBLOCK EAGAIN
+#endif
+#ifndef ELOOP
+#define ELOOP EMLINK
+#endif
+#define SHUT_WR SD_SEND
+
+#define SIGHUP 1
+#define SIGQUIT 3
+#define SIGKILL 9
+#define SIGPIPE 13
+#define SIGALRM 14
+#define SIGCHLD 17
+
+#define F_GETFD 1
+#define F_SETFD 2
+#define FD_CLOEXEC 0x1
+
+#if !defined O_CLOEXEC && defined O_NOINHERIT
+#define O_CLOEXEC O_NOINHERIT
+#endif
+
+#ifndef EAFNOSUPPORT
+#define EAFNOSUPPORT WSAEAFNOSUPPORT
+#endif
+#ifndef ECONNABORTED
+#define ECONNABORTED WSAECONNABORTED
+#endif
+#ifndef ENOTSOCK
+#define ENOTSOCK WSAENOTSOCK
+#endif
+
+struct passwd {
+ char *pw_name;
+ char *pw_gecos;
+ char *pw_dir;
+};
+
+typedef void (__cdecl *sig_handler_t)(int);
+struct sigaction {
+ sig_handler_t sa_handler;
+ unsigned sa_flags;
+};
+#define SA_RESTART 0
+
+struct itimerval {
+ struct timeval it_value, it_interval;
+};
+#define ITIMER_REAL 0
+
+struct utsname {
+ char sysname[16];
+ char nodename[1];
+ char release[16];
+ char version[16];
+ char machine[1];
+};
+
+/*
+ * sanitize preprocessor namespace polluted by Windows headers defining
+ * macros which collide with git local versions
+ */
+#undef HELP_COMMAND /* from winuser.h */
+
+/*
+ * trivial stubs
+ */
+
+static inline int readlink(const char *path UNUSED, char *buf UNUSED, size_t bufsiz UNUSED)
+{ errno = ENOSYS; return -1; }
+static inline int symlink(const char *oldpath UNUSED, const char *newpath UNUSED)
+{ errno = ENOSYS; return -1; }
+static inline int fchmod(int fildes UNUSED, mode_t mode UNUSED)
+{ errno = ENOSYS; return -1; }
+#ifndef __MINGW64_VERSION_MAJOR
+static inline pid_t fork(void)
+{ errno = ENOSYS; return -1; }
+#endif
+static inline unsigned int alarm(unsigned int seconds UNUSED)
+{ return 0; }
+static inline int fsync(int fd)
+{ return _commit(fd); }
+static inline void sync(void)
+{}
+static inline uid_t getuid(void)
+{ return 1; }
+static inline struct passwd *getpwnam(const char *name UNUSED)
+{ return NULL; }
+static inline int fcntl(int fd UNUSED, int cmd, ...)
+{
+ if (cmd == F_GETFD || cmd == F_SETFD)
+ return 0;
+ errno = EINVAL;
+ return -1;
+}
+
+#define sigemptyset(x) (void)0
+static inline int sigaddset(sigset_t *set UNUSED, int signum UNUSED)
+{ return 0; }
+#define SIG_BLOCK 0
+#define SIG_UNBLOCK 0
+static inline int sigprocmask(int how UNUSED, const sigset_t *set UNUSED, sigset_t *oldset UNUSED)
+{ return 0; }
+static inline pid_t getppid(void)
+{ return 1; }
+static inline pid_t getpgid(pid_t pid)
+{ return pid == 0 ? getpid() : pid; }
+static inline pid_t tcgetpgrp(int fd UNUSED)
+{ return getpid(); }
+
+/*
+ * simple adaptors
+ */
+
+int mingw_mkdir(const char *path, int mode);
+#define mkdir mingw_mkdir
+
+#define WNOHANG 1
+pid_t waitpid(pid_t pid, int *status, int options);
+
+#define kill mingw_kill
+int mingw_kill(pid_t pid, int sig);
+
+#define locate_in_PATH mingw_locate_in_PATH
+char *mingw_locate_in_PATH(const char *cmd);
+
+/*
+ * implementations of missing functions
+ */
+
+int pipe(int filedes[2]);
+unsigned int sleep (unsigned int seconds);
+int mkstemp(char *template);
+int gettimeofday(struct timeval *tv, void *tz);
+#ifndef __MINGW64_VERSION_MAJOR
+struct tm *gmtime_r(const time_t *timep, struct tm *result);
+struct tm *localtime_r(const time_t *timep, struct tm *result);
+#endif
+int getpagesize(void); /* defined in MinGW's libgcc.a */
+struct passwd *getpwuid(uid_t uid);
+int setitimer(int type, struct itimerval *in, struct itimerval *out);
+int sigaction(int sig, struct sigaction *in, struct sigaction *out);
+int link(const char *oldpath, const char *newpath);
+int uname(struct utsname *buf);
+
+/*
+ * replacements of existing functions
+ */
+
+int mingw_unlink(const char *pathname, int handle_in_use_error);
+#ifdef MINGW_DONT_HANDLE_IN_USE_ERROR
+# define unlink(path) mingw_unlink(path, 0)
+#else
+# define unlink(path) mingw_unlink(path, 1)
+#endif
+
+int mingw_rmdir(const char *path);
+#define rmdir mingw_rmdir
+
+int mingw_open (const char *filename, int oflags, ...);
+#define open mingw_open
+#undef OPEN_RETURNS_EINTR
+
+int mingw_fgetc(FILE *stream);
+#define fgetc mingw_fgetc
+
+FILE *mingw_fopen (const char *filename, const char *otype);
+#define fopen mingw_fopen
+
+FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream);
+#define freopen mingw_freopen
+
+int mingw_fflush(FILE *stream);
+#define fflush mingw_fflush
+
+ssize_t mingw_write(int fd, const void *buf, size_t len);
+#define write mingw_write
+
+int mingw_access(const char *filename, int mode);
+#undef access
+#define access mingw_access
+
+int mingw_chdir(const char *dirname);
+#define chdir mingw_chdir
+
+int mingw_chmod(const char *filename, int mode);
+#define chmod mingw_chmod
+
+char *mingw_mktemp(char *template);
+#define mktemp mingw_mktemp
+
+char *mingw_getcwd(char *pointer, int len);
+#define getcwd mingw_getcwd
+
+#ifdef NO_UNSETENV
+#error "NO_UNSETENV is incompatible with the Windows-specific startup code!"
+#endif
+
+/*
+ * We bind *env() routines (even the mingw_ ones) to private mingw_ versions.
+ * These talk to the CRT using UNICODE/wchar_t, but maintain the original
+ * narrow-char API.
+ *
+ * Note that the MSCRT maintains both ANSI (getenv()) and UNICODE (_wgetenv())
+ * routines and stores both versions of each environment variable in parallel
+ * (and secretly updates both when you set one or the other), but it uses CP_ACP
+ * to do the conversion rather than CP_UTF8.
+ *
+ * Since everything in the git code base is UTF8, we define the mingw_ routines
+ * to access the CRT using the UNICODE routines and manually convert them to
+ * UTF8. This also avoids round-trip problems.
+ *
+ * This also helps with our linkage, since "_wenviron" is publicly exported
+ * from the CRT. But to access "_environ" we would have to statically link
+ * to the CRT (/MT).
+ *
+ * We require NO_SETENV (and let gitsetenv() call our mingw_putenv).
+ */
+#define getenv mingw_getenv
+#define putenv mingw_putenv
+#define unsetenv mingw_putenv
+char *mingw_getenv(const char *name);
+int mingw_putenv(const char *name);
+
+int mingw_gethostname(char *host, int namelen);
+#define gethostname mingw_gethostname
+
+struct hostent *mingw_gethostbyname(const char *host);
+#define gethostbyname mingw_gethostbyname
+
+int mingw_getaddrinfo(const char *node, const char *service,
+ const struct addrinfo *hints, struct addrinfo **res);
+#define getaddrinfo mingw_getaddrinfo
+
+int mingw_socket(int domain, int type, int protocol);
+#define socket mingw_socket
+
+int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz);
+#define connect mingw_connect
+
+int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz);
+#define bind mingw_bind
+
+int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen);
+#define setsockopt mingw_setsockopt
+
+int mingw_shutdown(int sockfd, int how);
+#define shutdown mingw_shutdown
+
+int mingw_listen(int sockfd, int backlog);
+#define listen mingw_listen
+
+int mingw_accept(int sockfd, struct sockaddr *sa, socklen_t *sz);
+#define accept mingw_accept
+
+int mingw_rename(const char*, const char*);
+#define rename mingw_rename
+
+#if defined(USE_WIN32_MMAP) || defined(_MSC_VER)
+int mingw_getpagesize(void);
+#define getpagesize mingw_getpagesize
+#endif
+
+int win32_fsync_no_flush(int fd);
+#define fsync_no_flush win32_fsync_no_flush
+
+#define FSYNC_COMPONENTS_PLATFORM_DEFAULT (FSYNC_COMPONENTS_DEFAULT | FSYNC_COMPONENT_LOOSE_OBJECT)
+#define FSYNC_METHOD_DEFAULT (FSYNC_METHOD_BATCH)
+
+struct rlimit {
+ unsigned int rlim_cur;
+};
+#define RLIMIT_NOFILE 0
+
+static inline int getrlimit(int resource, struct rlimit *rlp)
+{
+ if (resource != RLIMIT_NOFILE) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ rlp->rlim_cur = 2048;
+ return 0;
+}
+
+/*
+ * Use mingw specific stat()/lstat()/fstat() implementations on Windows,
+ * including our own struct stat with 64 bit st_size and nanosecond-precision
+ * file times.
+ */
+#ifndef __MINGW64_VERSION_MAJOR
+#define off_t off64_t
+#define lseek _lseeki64
+#ifndef _MSC_VER
+struct timespec {
+ time_t tv_sec;
+ long tv_nsec;
+};
+#endif
+#endif
+
+struct mingw_stat {
+ _dev_t st_dev;
+ _ino_t st_ino;
+ _mode_t st_mode;
+ short st_nlink;
+ short st_uid;
+ short st_gid;
+ _dev_t st_rdev;
+ off64_t st_size;
+ struct timespec st_atim;
+ struct timespec st_mtim;
+ struct timespec st_ctim;
+};
+
+#define st_atime st_atim.tv_sec
+#define st_mtime st_mtim.tv_sec
+#define st_ctime st_ctim.tv_sec
+
+#ifdef stat
+#undef stat
+#endif
+#define stat mingw_stat
+int mingw_lstat(const char *file_name, struct stat *buf);
+int mingw_stat(const char *file_name, struct stat *buf);
+int mingw_fstat(int fd, struct stat *buf);
+#ifdef fstat
+#undef fstat
+#endif
+#define fstat mingw_fstat
+#ifdef lstat
+#undef lstat
+#endif
+#define lstat mingw_lstat
+
+
+int mingw_utime(const char *file_name, const struct utimbuf *times);
+#define utime mingw_utime
+size_t mingw_strftime(char *s, size_t max,
+ const char *format, const struct tm *tm);
+#define strftime mingw_strftime
+
+pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
+ const char *dir,
+ int fhin, int fhout, int fherr);
+int mingw_execvp(const char *cmd, char *const *argv);
+#define execvp mingw_execvp
+int mingw_execv(const char *cmd, char *const *argv);
+#define execv mingw_execv
+
+static inline unsigned int git_ntohl(unsigned int x)
+{ return (unsigned int)ntohl(x); }
+#define ntohl git_ntohl
+
+sig_handler_t mingw_signal(int sig, sig_handler_t handler);
+#define signal mingw_signal
+
+int mingw_raise(int sig);
+#define raise mingw_raise
+
+/*
+ * ANSI emulation wrappers
+ */
+
+int winansi_isatty(int fd);
+#define isatty winansi_isatty
+
+int winansi_dup2(int oldfd, int newfd);
+#define dup2 winansi_dup2
+
+void winansi_init(void);
+HANDLE winansi_get_osfhandle(int fd);
+
+#if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800)
+#define PRIuMAX "I64u"
+#define PRId64 "I64d"
+#else
+#include <inttypes.h>
+#endif
+
+#endif /* COMPAT_MINGW_POSIX_H */
diff --git a/compat/mingw.c b/compat/mingw.c
index f524c54d06..8a9972a1ca 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -21,6 +21,9 @@
#include "gettext.h"
#define SECURITY_WIN32
#include <sspi.h>
+#include <winternl.h>
+
+#define STATUS_DELETE_PENDING ((NTSTATUS) 0xC0000056)
#define HCAST(type, handle) ((type)(intptr_t)handle)
@@ -302,7 +305,7 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
return wbuf;
}
-int mingw_unlink(const char *pathname)
+int mingw_unlink(const char *pathname, int handle_in_use_error)
{
int ret, tries = 0;
wchar_t wpathname[MAX_PATH];
@@ -317,6 +320,9 @@ int mingw_unlink(const char *pathname)
while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
if (!is_file_in_use_error(GetLastError()))
break;
+ if (!handle_in_use_error)
+ return ret;
+
/*
* We assume that some other process had the source or
* destination file open at the wrong moment and retry.
@@ -621,6 +627,8 @@ int mingw_open (const char *filename, int oflags, ...)
wchar_t wfilename[MAX_PATH];
open_fn_t open_fn;
+ DECLARE_PROC_ADDR(ntdll.dll, NTSTATUS, NTAPI, RtlGetLastNtStatus, void);
+
va_start(args, oflags);
mode = va_arg(args, int);
va_end(args);
@@ -644,6 +652,21 @@ int mingw_open (const char *filename, int oflags, ...)
fd = open_fn(wfilename, oflags, mode);
+ /*
+ * Internally, `_wopen()` uses the `CreateFile()` API with CREATE_NEW,
+ * which may error out with ERROR_ACCESS_DENIED and an NtStatus of
+ * STATUS_DELETE_PENDING when the file is scheduled for deletion via
+ * `DeleteFileW()`. The file essentially exists, so we map errno to
+ * EEXIST instead of EACCESS so that callers don't have to special-case
+ * this.
+ *
+ * This fixes issues for example with the lockfile interface when one
+ * process has a lock that it is about to commit or release while
+ * another process wants to acquire it.
+ */
+ if (fd < 0 && create && GetLastError() == ERROR_ACCESS_DENIED &&
+ INIT_PROC_ADDR(RtlGetLastNtStatus) && RtlGetLastNtStatus() == STATUS_DELETE_PENDING)
+ errno = EEXIST;
if (fd < 0 && (oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) {
DWORD attrs = GetFileAttributesW(wfilename);
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
@@ -2826,31 +2849,44 @@ static void setup_windows_environment(void)
}
}
-static PSID get_current_user_sid(void)
+static void get_current_user_sid(PSID *sid, HANDLE *linked_token)
{
HANDLE token;
DWORD len = 0;
- PSID result = NULL;
+ TOKEN_ELEVATION_TYPE elevationType;
+ DWORD size;
+
+ *sid = NULL;
+ *linked_token = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
- return NULL;
+ return;
if (!GetTokenInformation(token, TokenUser, NULL, 0, &len)) {
TOKEN_USER *info = xmalloc((size_t)len);
if (GetTokenInformation(token, TokenUser, info, len, &len)) {
len = GetLengthSid(info->User.Sid);
- result = xmalloc(len);
- if (!CopySid(len, result, info->User.Sid)) {
+ *sid = xmalloc(len);
+ if (!CopySid(len, *sid, info->User.Sid)) {
error(_("failed to copy SID (%ld)"),
GetLastError());
- FREE_AND_NULL(result);
+ FREE_AND_NULL(*sid);
}
}
FREE_AND_NULL(info);
}
- CloseHandle(token);
- return result;
+ if (GetTokenInformation(token, TokenElevationType, &elevationType, sizeof(elevationType), &size) &&
+ elevationType == TokenElevationTypeLimited) {
+ /*
+ * The current process is run by a member of the Administrators
+ * group, but is not running elevated.
+ */
+ if (!GetTokenInformation(token, TokenLinkedToken, linked_token, sizeof(*linked_token), &size))
+ linked_token = NULL; /* there is no linked token */
+ }
+
+ CloseHandle(token);
}
static BOOL user_sid_to_user_name(PSID sid, LPSTR *str)
@@ -2931,18 +2967,22 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
else if (sid && IsValidSid(sid)) {
/* Now, verify that the SID matches the current user's */
static PSID current_user_sid;
+ static HANDLE linked_token;
BOOL is_member;
if (!current_user_sid)
- current_user_sid = get_current_user_sid();
+ get_current_user_sid(&current_user_sid, &linked_token);
if (current_user_sid &&
IsValidSid(current_user_sid) &&
EqualSid(sid, current_user_sid))
result = 1;
else if (IsWellKnownSid(sid, WinBuiltinAdministratorsSid) &&
- CheckTokenMembership(NULL, sid, &is_member) &&
- is_member)
+ ((CheckTokenMembership(NULL, sid, &is_member) &&
+ is_member) ||
+ (linked_token &&
+ CheckTokenMembership(linked_token, sid, &is_member) &&
+ is_member)))
/*
* If owned by the Administrators group, and the
* current user is an administrator, we consider that
diff --git a/compat/mingw.h b/compat/mingw.h
index ebfb8ba423..444daedfa5 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -1,185 +1,10 @@
-#ifdef __MINGW64_VERSION_MAJOR
-#include <stdint.h>
-#include <wchar.h>
-typedef _sigset_t sigset_t;
-#endif
-#include <winsock2.h>
-#include <ws2tcpip.h>
-
-/* MinGW-w64 reports to have flockfile, but it does not actually have it. */
-#ifdef __MINGW64_VERSION_MAJOR
-#undef _POSIX_THREAD_SAFE_FUNCTIONS
-#endif
+#include "mingw-posix.h"
struct config_context;
int mingw_core_config(const char *var, const char *value,
const struct config_context *ctx, void *cb);
#define platform_core_config mingw_core_config
-/*
- * things that are not available in header files
- */
-
-typedef int uid_t;
-typedef int socklen_t;
-#ifndef __MINGW64_VERSION_MAJOR
-typedef int pid_t;
-#define hstrerror strerror
-#endif
-
-#define S_IFLNK 0120000 /* Symbolic link */
-#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK)
-#define S_ISSOCK(x) 0
-
-#ifndef S_IRWXG
-#define S_IRGRP 0
-#define S_IWGRP 0
-#define S_IXGRP 0
-#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
-#endif
-#ifndef S_IRWXO
-#define S_IROTH 0
-#define S_IWOTH 0
-#define S_IXOTH 0
-#define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
-#endif
-
-#define S_ISUID 0004000
-#define S_ISGID 0002000
-#define S_ISVTX 0001000
-
-#define WIFEXITED(x) 1
-#define WIFSIGNALED(x) 0
-#define WEXITSTATUS(x) ((x) & 0xff)
-#define WTERMSIG(x) SIGTERM
-
-#ifndef EWOULDBLOCK
-#define EWOULDBLOCK EAGAIN
-#endif
-#ifndef ELOOP
-#define ELOOP EMLINK
-#endif
-#define SHUT_WR SD_SEND
-
-#define SIGHUP 1
-#define SIGQUIT 3
-#define SIGKILL 9
-#define SIGPIPE 13
-#define SIGALRM 14
-#define SIGCHLD 17
-
-#define F_GETFD 1
-#define F_SETFD 2
-#define FD_CLOEXEC 0x1
-
-#if !defined O_CLOEXEC && defined O_NOINHERIT
-#define O_CLOEXEC O_NOINHERIT
-#endif
-
-#ifndef EAFNOSUPPORT
-#define EAFNOSUPPORT WSAEAFNOSUPPORT
-#endif
-#ifndef ECONNABORTED
-#define ECONNABORTED WSAECONNABORTED
-#endif
-#ifndef ENOTSOCK
-#define ENOTSOCK WSAENOTSOCK
-#endif
-
-struct passwd {
- char *pw_name;
- char *pw_gecos;
- char *pw_dir;
-};
-
-typedef void (__cdecl *sig_handler_t)(int);
-struct sigaction {
- sig_handler_t sa_handler;
- unsigned sa_flags;
-};
-#define SA_RESTART 0
-
-struct itimerval {
- struct timeval it_value, it_interval;
-};
-#define ITIMER_REAL 0
-
-struct utsname {
- char sysname[16];
- char nodename[1];
- char release[16];
- char version[16];
- char machine[1];
-};
-
-/*
- * sanitize preprocessor namespace polluted by Windows headers defining
- * macros which collide with git local versions
- */
-#undef HELP_COMMAND /* from winuser.h */
-
-/*
- * trivial stubs
- */
-
-static inline int readlink(const char *path UNUSED, char *buf UNUSED, size_t bufsiz UNUSED)
-{ errno = ENOSYS; return -1; }
-static inline int symlink(const char *oldpath UNUSED, const char *newpath UNUSED)
-{ errno = ENOSYS; return -1; }
-static inline int fchmod(int fildes UNUSED, mode_t mode UNUSED)
-{ errno = ENOSYS; return -1; }
-#ifndef __MINGW64_VERSION_MAJOR
-static inline pid_t fork(void)
-{ errno = ENOSYS; return -1; }
-#endif
-static inline unsigned int alarm(unsigned int seconds UNUSED)
-{ return 0; }
-static inline int fsync(int fd)
-{ return _commit(fd); }
-static inline void sync(void)
-{}
-static inline uid_t getuid(void)
-{ return 1; }
-static inline struct passwd *getpwnam(const char *name UNUSED)
-{ return NULL; }
-static inline int fcntl(int fd UNUSED, int cmd, ...)
-{
- if (cmd == F_GETFD || cmd == F_SETFD)
- return 0;
- errno = EINVAL;
- return -1;
-}
-
-#define sigemptyset(x) (void)0
-static inline int sigaddset(sigset_t *set UNUSED, int signum UNUSED)
-{ return 0; }
-#define SIG_BLOCK 0
-#define SIG_UNBLOCK 0
-static inline int sigprocmask(int how UNUSED, const sigset_t *set UNUSED, sigset_t *oldset UNUSED)
-{ return 0; }
-static inline pid_t getppid(void)
-{ return 1; }
-static inline pid_t getpgid(pid_t pid)
-{ return pid == 0 ? getpid() : pid; }
-static inline pid_t tcgetpgrp(int fd UNUSED)
-{ return getpid(); }
-
-/*
- * simple adaptors
- */
-
-int mingw_mkdir(const char *path, int mode);
-#define mkdir mingw_mkdir
-
-#define WNOHANG 1
-pid_t waitpid(pid_t pid, int *status, int options);
-
-#define kill mingw_kill
-int mingw_kill(pid_t pid, int sig);
-
-#define locate_in_PATH mingw_locate_in_PATH
-char *mingw_locate_in_PATH(const char *cmd);
-
#ifndef NO_OPENSSL
#include <openssl/ssl.h>
static inline int mingw_SSL_set_fd(SSL *ssl, int fd)
@@ -202,249 +27,6 @@ static inline int mingw_SSL_set_wfd(SSL *ssl, int fd)
#endif
/*
- * implementations of missing functions
- */
-
-int pipe(int filedes[2]);
-unsigned int sleep (unsigned int seconds);
-int mkstemp(char *template);
-int gettimeofday(struct timeval *tv, void *tz);
-#ifndef __MINGW64_VERSION_MAJOR
-struct tm *gmtime_r(const time_t *timep, struct tm *result);
-struct tm *localtime_r(const time_t *timep, struct tm *result);
-#endif
-int getpagesize(void); /* defined in MinGW's libgcc.a */
-struct passwd *getpwuid(uid_t uid);
-int setitimer(int type, struct itimerval *in, struct itimerval *out);
-int sigaction(int sig, struct sigaction *in, struct sigaction *out);
-int link(const char *oldpath, const char *newpath);
-int uname(struct utsname *buf);
-
-/*
- * replacements of existing functions
- */
-
-int mingw_unlink(const char *pathname);
-#define unlink mingw_unlink
-
-int mingw_rmdir(const char *path);
-#define rmdir mingw_rmdir
-
-int mingw_open (const char *filename, int oflags, ...);
-#define open mingw_open
-#undef OPEN_RETURNS_EINTR
-
-int mingw_fgetc(FILE *stream);
-#define fgetc mingw_fgetc
-
-FILE *mingw_fopen (const char *filename, const char *otype);
-#define fopen mingw_fopen
-
-FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream);
-#define freopen mingw_freopen
-
-int mingw_fflush(FILE *stream);
-#define fflush mingw_fflush
-
-ssize_t mingw_write(int fd, const void *buf, size_t len);
-#define write mingw_write
-
-int mingw_access(const char *filename, int mode);
-#undef access
-#define access mingw_access
-
-int mingw_chdir(const char *dirname);
-#define chdir mingw_chdir
-
-int mingw_chmod(const char *filename, int mode);
-#define chmod mingw_chmod
-
-char *mingw_mktemp(char *template);
-#define mktemp mingw_mktemp
-
-char *mingw_getcwd(char *pointer, int len);
-#define getcwd mingw_getcwd
-
-#ifdef NO_UNSETENV
-#error "NO_UNSETENV is incompatible with the Windows-specific startup code!"
-#endif
-
-/*
- * We bind *env() routines (even the mingw_ ones) to private mingw_ versions.
- * These talk to the CRT using UNICODE/wchar_t, but maintain the original
- * narrow-char API.
- *
- * Note that the MSCRT maintains both ANSI (getenv()) and UNICODE (_wgetenv())
- * routines and stores both versions of each environment variable in parallel
- * (and secretly updates both when you set one or the other), but it uses CP_ACP
- * to do the conversion rather than CP_UTF8.
- *
- * Since everything in the git code base is UTF8, we define the mingw_ routines
- * to access the CRT using the UNICODE routines and manually convert them to
- * UTF8. This also avoids round-trip problems.
- *
- * This also helps with our linkage, since "_wenviron" is publicly exported
- * from the CRT. But to access "_environ" we would have to statically link
- * to the CRT (/MT).
- *
- * We require NO_SETENV (and let gitsetenv() call our mingw_putenv).
- */
-#define getenv mingw_getenv
-#define putenv mingw_putenv
-#define unsetenv mingw_putenv
-char *mingw_getenv(const char *name);
-int mingw_putenv(const char *name);
-
-int mingw_gethostname(char *host, int namelen);
-#define gethostname mingw_gethostname
-
-struct hostent *mingw_gethostbyname(const char *host);
-#define gethostbyname mingw_gethostbyname
-
-int mingw_getaddrinfo(const char *node, const char *service,
- const struct addrinfo *hints, struct addrinfo **res);
-#define getaddrinfo mingw_getaddrinfo
-
-int mingw_socket(int domain, int type, int protocol);
-#define socket mingw_socket
-
-int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz);
-#define connect mingw_connect
-
-int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz);
-#define bind mingw_bind
-
-int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen);
-#define setsockopt mingw_setsockopt
-
-int mingw_shutdown(int sockfd, int how);
-#define shutdown mingw_shutdown
-
-int mingw_listen(int sockfd, int backlog);
-#define listen mingw_listen
-
-int mingw_accept(int sockfd, struct sockaddr *sa, socklen_t *sz);
-#define accept mingw_accept
-
-int mingw_rename(const char*, const char*);
-#define rename mingw_rename
-
-#if defined(USE_WIN32_MMAP) || defined(_MSC_VER)
-int mingw_getpagesize(void);
-#define getpagesize mingw_getpagesize
-#endif
-
-int win32_fsync_no_flush(int fd);
-#define fsync_no_flush win32_fsync_no_flush
-
-#define FSYNC_COMPONENTS_PLATFORM_DEFAULT (FSYNC_COMPONENTS_DEFAULT | FSYNC_COMPONENT_LOOSE_OBJECT)
-#define FSYNC_METHOD_DEFAULT (FSYNC_METHOD_BATCH)
-
-struct rlimit {
- unsigned int rlim_cur;
-};
-#define RLIMIT_NOFILE 0
-
-static inline int getrlimit(int resource, struct rlimit *rlp)
-{
- if (resource != RLIMIT_NOFILE) {
- errno = EINVAL;
- return -1;
- }
-
- rlp->rlim_cur = 2048;
- return 0;
-}
-
-/*
- * Use mingw specific stat()/lstat()/fstat() implementations on Windows,
- * including our own struct stat with 64 bit st_size and nanosecond-precision
- * file times.
- */
-#ifndef __MINGW64_VERSION_MAJOR
-#define off_t off64_t
-#define lseek _lseeki64
-#ifndef _MSC_VER
-struct timespec {
- time_t tv_sec;
- long tv_nsec;
-};
-#endif
-#endif
-
-struct mingw_stat {
- _dev_t st_dev;
- _ino_t st_ino;
- _mode_t st_mode;
- short st_nlink;
- short st_uid;
- short st_gid;
- _dev_t st_rdev;
- off64_t st_size;
- struct timespec st_atim;
- struct timespec st_mtim;
- struct timespec st_ctim;
-};
-
-#define st_atime st_atim.tv_sec
-#define st_mtime st_mtim.tv_sec
-#define st_ctime st_ctim.tv_sec
-
-#ifdef stat
-#undef stat
-#endif
-#define stat mingw_stat
-int mingw_lstat(const char *file_name, struct stat *buf);
-int mingw_stat(const char *file_name, struct stat *buf);
-int mingw_fstat(int fd, struct stat *buf);
-#ifdef fstat
-#undef fstat
-#endif
-#define fstat mingw_fstat
-#ifdef lstat
-#undef lstat
-#endif
-#define lstat mingw_lstat
-
-
-int mingw_utime(const char *file_name, const struct utimbuf *times);
-#define utime mingw_utime
-size_t mingw_strftime(char *s, size_t max,
- const char *format, const struct tm *tm);
-#define strftime mingw_strftime
-
-pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
- const char *dir,
- int fhin, int fhout, int fherr);
-int mingw_execvp(const char *cmd, char *const *argv);
-#define execvp mingw_execvp
-int mingw_execv(const char *cmd, char *const *argv);
-#define execv mingw_execv
-
-static inline unsigned int git_ntohl(unsigned int x)
-{ return (unsigned int)ntohl(x); }
-#define ntohl git_ntohl
-
-sig_handler_t mingw_signal(int sig, sig_handler_t handler);
-#define signal mingw_signal
-
-int mingw_raise(int sig);
-#define raise mingw_raise
-
-/*
- * ANSI emulation wrappers
- */
-
-int winansi_isatty(int fd);
-#define isatty winansi_isatty
-
-int winansi_dup2(int oldfd, int newfd);
-#define dup2 winansi_dup2
-
-void winansi_init(void);
-HANDLE winansi_get_osfhandle(int fd);
-
-/*
* git specific compatibility
*/
@@ -457,12 +39,6 @@ static inline void convert_slashes(char *path)
#define PATH_SEP ';'
char *mingw_query_user_email(void);
#define query_user_email mingw_query_user_email
-#if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800)
-#define PRIuMAX "I64u"
-#define PRId64 "I64d"
-#else
-#include <inttypes.h>
-#endif
/**
* Verifies that the specified path is owned by the user running the
diff --git a/compat/msvc-posix.h b/compat/msvc-posix.h
new file mode 100644
index 0000000000..c500b8b4aa
--- /dev/null
+++ b/compat/msvc-posix.h
@@ -0,0 +1,33 @@
+#ifndef COMPAT_MSVC_POSIX_H
+#define COMPAT_MSVC_POSIX_H
+
+#include <direct.h>
+#include <process.h>
+#include <malloc.h>
+#include <io.h>
+
+#pragma warning(disable: 4018) /* signed/unsigned comparison */
+#pragma warning(disable: 4244) /* type conversion, possible loss of data */
+#pragma warning(disable: 4090) /* 'function' : different 'const' qualifiers (ALLOC_GROW etc.)*/
+
+/* porting function */
+#define inline __inline
+#define __inline__ __inline
+#define __attribute__(x)
+#define strcasecmp _stricmp
+#define strncasecmp _strnicmp
+#define ftruncate _chsize
+#define strtoull _strtoui64
+#define strtoll _strtoi64
+
+#undef ERROR
+
+#define ftello _ftelli64
+
+typedef int sigset_t;
+/* open for reading, writing, or both (not in fcntl.h) */
+#define O_ACCMODE (_O_RDONLY | _O_WRONLY | _O_RDWR)
+
+#include "mingw-posix.h"
+
+#endif /* COMPAT_MSVC_POSIX_H */
diff --git a/compat/msvc.h b/compat/msvc.h
index 1d7a8c6145..2b87c0a7c7 100644
--- a/compat/msvc.h
+++ b/compat/msvc.h
@@ -1,33 +1,7 @@
#ifndef __MSVC__HEAD
#define __MSVC__HEAD
-#include <direct.h>
-#include <process.h>
-#include <malloc.h>
-#include <io.h>
-
-#pragma warning(disable: 4018) /* signed/unsigned comparison */
-#pragma warning(disable: 4244) /* type conversion, possible loss of data */
-#pragma warning(disable: 4090) /* 'function' : different 'const' qualifiers (ALLOC_GROW etc.)*/
-
-/* porting function */
-#define inline __inline
-#define __inline__ __inline
-#define __attribute__(x)
-#define strcasecmp _stricmp
-#define strncasecmp _strnicmp
-#define ftruncate _chsize
-#define strtoull _strtoui64
-#define strtoll _strtoi64
-
-#undef ERROR
-
-#define ftello _ftelli64
-
-typedef int sigset_t;
-/* open for reading, writing, or both (not in fcntl.h) */
-#define O_ACCMODE (_O_RDONLY | _O_WRONLY | _O_RDWR)
-
-#include "compat/mingw.h"
+#include "msvc-posix.h"
+#include "mingw.h"
#endif
diff --git a/compat/open.c b/compat/open.c
index eb3754a23b..37ae2b1aeb 100644
--- a/compat/open.c
+++ b/compat/open.c
@@ -1,5 +1,6 @@
#include "git-compat-util.h"
+#ifdef OPEN_RETURNS_EINTR
#undef open
int git_open_with_retry(const char *path, int flags, ...)
{
@@ -23,3 +24,31 @@ int git_open_with_retry(const char *path, int flags, ...)
return ret;
}
+#endif
+
+int git_open_cloexec(const char *name, int flags)
+{
+ int fd;
+ static int o_cloexec = O_CLOEXEC;
+
+ fd = open(name, flags | o_cloexec);
+ if ((o_cloexec & O_CLOEXEC) && fd < 0 && errno == EINVAL) {
+ /* Try again w/o O_CLOEXEC: the kernel might not support it */
+ o_cloexec &= ~O_CLOEXEC;
+ fd = open(name, flags | o_cloexec);
+ }
+
+#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
+ {
+ static int fd_cloexec = FD_CLOEXEC;
+
+ if (!o_cloexec && 0 <= fd && fd_cloexec) {
+ /* Opened w/o O_CLOEXEC? try with fcntl(2) to add it */
+ int flags = fcntl(fd, F_GETFD);
+ if (fcntl(fd, F_SETFD, flags | fd_cloexec))
+ fd_cloexec = 0;
+ }
+ }
+#endif
+ return fd;
+}
diff --git a/compat/posix.h b/compat/posix.h
new file mode 100644
index 0000000000..f4c71f9427
--- /dev/null
+++ b/compat/posix.h
@@ -0,0 +1,541 @@
+#ifndef COMPAT_POSIX_H
+#define COMPAT_POSIX_H
+
+#define _FILE_OFFSET_BITS 64
+
+/*
+ * Derived from Linux "Features Test Macro" header
+ * Convenience macros to test the versions of gcc (or
+ * a compatible compiler).
+ * Use them like this:
+ * #if GIT_GNUC_PREREQ (2,8)
+ * ... code requiring gcc 2.8 or later ...
+ * #endif
+ *
+ * This macro of course is not part of POSIX, but we need it for the UNUSED
+ * macro which is used by some of our POSIX compatibility wrappers.
+*/
+#if defined(__GNUC__) && defined(__GNUC_MINOR__)
+# define GIT_GNUC_PREREQ(maj, min) \
+ ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+#else
+ #define GIT_GNUC_PREREQ(maj, min) 0
+#endif
+
+/*
+ * UNUSED marks a function parameter that is always unused. It also
+ * can be used to annotate a function, a variable, or a type that is
+ * always unused.
+ *
+ * A callback interface may dictate that a function accepts a
+ * parameter at that position, but the implementation of the function
+ * may not need to use the parameter. In such a case, mark the parameter
+ * with UNUSED.
+ *
+ * When a parameter may be used or unused, depending on conditional
+ * compilation, consider using MAYBE_UNUSED instead.
+ */
+#if GIT_GNUC_PREREQ(4, 5)
+#define UNUSED __attribute__((unused)) \
+ __attribute__((deprecated ("parameter declared as UNUSED")))
+#elif defined(__GNUC__)
+#define UNUSED __attribute__((unused)) \
+ __attribute__((deprecated))
+#else
+#define UNUSED
+#endif
+
+#ifdef __MINGW64__
+#define _POSIX_C_SOURCE 1
+#elif defined(__sun__)
+ /*
+ * On Solaris, when _XOPEN_EXTENDED is set, its header file
+ * forces the programs to be XPG4v2, defeating any _XOPEN_SOURCE
+ * setting to say we are XPG5 or XPG6. Also on Solaris,
+ * XPG6 programs must be compiled with a c99 compiler, while
+ * non XPG6 programs must be compiled with a pre-c99 compiler.
+ */
+# if __STDC_VERSION__ - 0 >= 199901L
+# define _XOPEN_SOURCE 600
+# else
+# define _XOPEN_SOURCE 500
+# endif
+#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \
+ !defined(_M_UNIX) && !defined(__sgi) && !defined(__DragonFly__) && \
+ !defined(__TANDEM) && !defined(__QNX__) && !defined(__MirBSD__) && \
+ !defined(__CYGWIN__)
+#define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
+#define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
+#endif
+#define _ALL_SOURCE 1
+#define _GNU_SOURCE 1
+#define _BSD_SOURCE 1
+#define _DEFAULT_SOURCE 1
+#define _NETBSD_SOURCE 1
+#define _SGI_SOURCE 1
+
+#if defined(WIN32) && !defined(__CYGWIN__) /* Both MinGW and MSVC */
+# if !defined(_WIN32_WINNT)
+# define _WIN32_WINNT 0x0600
+# endif
+#define WIN32_LEAN_AND_MEAN /* stops windows.h including winsock.h */
+#include <winsock2.h>
+#ifndef NO_UNIX_SOCKETS
+#include <afunix.h>
+#endif
+#include <windows.h>
+#define GIT_WINDOWS_NATIVE
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+#include <strings.h> /* for strcasecmp() */
+#endif
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#ifdef NEEDS_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <time.h>
+#include <signal.h>
+#include <assert.h>
+#include <regex.h>
+#include <utime.h>
+#include <syslog.h>
+#if !defined(NO_POLL_H)
+#include <poll.h>
+#elif !defined(NO_SYS_POLL_H)
+#include <sys/poll.h>
+#else
+/* Pull the compat stuff */
+#include <poll.h>
+#endif
+#ifdef HAVE_BSD_SYSCTL
+#include <sys/sysctl.h>
+#endif
+
+#if defined(__MINGW32__)
+#include "mingw-posix.h"
+#elif defined(_MSC_VER)
+#include "msvc-posix.h"
+#else
+#include <sys/utsname.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/statvfs.h>
+#include <termios.h>
+#ifndef NO_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <sys/un.h>
+#ifndef NO_INTTYPES_H
+#include <inttypes.h>
+#else
+#include <stdint.h>
+#endif
+#ifdef HAVE_ARC4RANDOM_LIBBSD
+#include <bsd/stdlib.h>
+#endif
+#ifdef HAVE_GETRANDOM
+#include <sys/random.h>
+#endif
+#ifdef NO_INTPTR_T
+/*
+ * On I16LP32, ILP32 and LP64 "long" is the safe bet, however
+ * on LLP86, IL33LLP64 and P64 it needs to be "long long",
+ * while on IP16 and IP16L32 it is "int" (resp. "short")
+ * Size needs to match (or exceed) 'sizeof(void *)'.
+ * We can't take "long long" here as not everybody has it.
+ */
+typedef long intptr_t;
+typedef unsigned long uintptr_t;
+#endif
+#undef _ALL_SOURCE /* AIX 5.3L defines a struct list with _ALL_SOURCE. */
+#include <grp.h>
+#define _ALL_SOURCE 1
+#endif
+
+#ifdef MKDIR_WO_TRAILING_SLASH
+#define mkdir(a,b) compat_mkdir_wo_trailing_slash((a),(b))
+int compat_mkdir_wo_trailing_slash(const char*, mode_t);
+#endif
+
+#ifdef time
+#undef time
+#endif
+static inline time_t git_time(time_t *tloc)
+{
+ struct timeval tv;
+
+ /*
+ * Avoid time(NULL), which can disagree with gettimeofday(2)
+ * and filesystem timestamps.
+ */
+ gettimeofday(&tv, NULL);
+
+ if (tloc)
+ *tloc = tv.tv_sec;
+ return tv.tv_sec;
+}
+#define time git_time
+
+#ifdef NO_STRUCT_ITIMERVAL
+struct itimerval {
+ struct timeval it_interval;
+ struct timeval it_value;
+};
+#endif
+
+#ifdef NO_SETITIMER
+static inline int git_setitimer(int which UNUSED,
+ const struct itimerval *value UNUSED,
+ struct itimerval *newvalue UNUSED) {
+ return 0; /* pretend success */
+}
+#undef setitimer
+#define setitimer(which,value,ovalue) git_setitimer(which,value,ovalue)
+#endif
+
+#ifndef NO_LIBGEN_H
+#include <libgen.h>
+#else
+#define basename gitbasename
+char *gitbasename(char *);
+#define dirname gitdirname
+char *gitdirname(char *);
+#endif
+
+#ifndef NO_ICONV
+#include <iconv.h>
+#endif
+
+/* On most systems <netdb.h> would have given us this, but
+ * not on some systems (e.g. z/OS).
+ */
+#ifndef NI_MAXHOST
+#define NI_MAXHOST 1025
+#endif
+
+#ifndef NI_MAXSERV
+#define NI_MAXSERV 32
+#endif
+
+/* On most systems <limits.h> would have given us this, but
+ * not on some systems (e.g. GNU/Hurd).
+ */
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+#ifndef NAME_MAX
+#define NAME_MAX 255
+#endif
+
+typedef uintmax_t timestamp_t;
+#define PRItime PRIuMAX
+#define parse_timestamp strtoumax
+#define TIME_MAX UINTMAX_MAX
+#define TIME_MIN 0
+
+int lstat_cache_aware_rmdir(const char *path);
+#if !defined(__MINGW32__) && !defined(_MSC_VER)
+#define rmdir lstat_cache_aware_rmdir
+#endif
+
+#if defined(NO_MMAP) || defined(USE_WIN32_MMAP)
+
+#ifndef PROT_READ
+#define PROT_READ 1
+#define PROT_WRITE 2
+#define MAP_PRIVATE 1
+#endif
+
+#define mmap git_mmap
+#define munmap git_munmap
+void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
+int git_munmap(void *start, size_t length);
+
+#else /* NO_MMAP || USE_WIN32_MMAP */
+
+#include <sys/mman.h>
+
+#endif /* NO_MMAP || USE_WIN32_MMAP */
+
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)-1)
+#endif
+
+#ifdef NEEDS_MODE_TRANSLATION
+#undef S_IFMT
+#undef S_IFREG
+#undef S_IFDIR
+#undef S_IFLNK
+#undef S_IFBLK
+#undef S_IFCHR
+#undef S_IFIFO
+#undef S_IFSOCK
+#define S_IFMT 0170000
+#define S_IFREG 0100000
+#define S_IFDIR 0040000
+#define S_IFLNK 0120000
+#define S_IFBLK 0060000
+#define S_IFCHR 0020000
+#define S_IFIFO 0010000
+#define S_IFSOCK 0140000
+#ifdef stat
+#undef stat
+#endif
+#define stat(path, buf) git_stat(path, buf)
+int git_stat(const char *, struct stat *);
+#ifdef fstat
+#undef fstat
+#endif
+#define fstat(fd, buf) git_fstat(fd, buf)
+int git_fstat(int, struct stat *);
+#ifdef lstat
+#undef lstat
+#endif
+#define lstat(path, buf) git_lstat(path, buf)
+int git_lstat(const char *, struct stat *);
+#endif
+
+#ifdef NO_PREAD
+#define pread git_pread
+ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
+#endif
+
+#ifdef NO_SETENV
+#define setenv gitsetenv
+int gitsetenv(const char *, const char *, int);
+#endif
+
+#ifdef NO_MKDTEMP
+#define mkdtemp gitmkdtemp
+char *gitmkdtemp(char *);
+#endif
+
+#ifdef NO_UNSETENV
+#define unsetenv gitunsetenv
+int gitunsetenv(const char *);
+#endif
+
+#ifdef NO_STRCASESTR
+#define strcasestr gitstrcasestr
+char *gitstrcasestr(const char *haystack, const char *needle);
+#endif
+
+#ifdef NO_STRLCPY
+#define strlcpy gitstrlcpy
+size_t gitstrlcpy(char *, const char *, size_t);
+#endif
+
+#ifdef NO_STRTOUMAX
+#define strtoumax gitstrtoumax
+uintmax_t gitstrtoumax(const char *, char **, int);
+#define strtoimax gitstrtoimax
+intmax_t gitstrtoimax(const char *, char **, int);
+#endif
+
+#ifdef NO_HSTRERROR
+#define hstrerror githstrerror
+const char *githstrerror(int herror);
+#endif
+
+#ifdef NO_MEMMEM
+#define memmem gitmemmem
+void *gitmemmem(const void *haystack, size_t haystacklen,
+ const void *needle, size_t needlelen);
+#endif
+
+#ifdef OVERRIDE_STRDUP
+#ifdef strdup
+#undef strdup
+#endif
+#define strdup gitstrdup
+char *gitstrdup(const char *s);
+#endif
+
+#ifdef NO_GETPAGESIZE
+#define getpagesize() sysconf(_SC_PAGESIZE)
+#endif
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#ifdef FREAD_READS_DIRECTORIES
+# if !defined(SUPPRESS_FOPEN_REDEFINITION)
+# ifdef fopen
+# undef fopen
+# endif
+# define fopen(a,b) git_fopen(a,b)
+# endif
+FILE *git_fopen(const char*, const char*);
+#endif
+
+#ifdef SNPRINTF_RETURNS_BOGUS
+#ifdef snprintf
+#undef snprintf
+#endif
+#define snprintf git_snprintf
+int git_snprintf(char *str, size_t maxsize,
+ const char *format, ...);
+#ifdef vsnprintf
+#undef vsnprintf
+#endif
+#define vsnprintf git_vsnprintf
+int git_vsnprintf(char *str, size_t maxsize,
+ const char *format, va_list ap);
+#endif
+
+#ifdef OPEN_RETURNS_EINTR
+#undef open
+#define open git_open_with_retry
+int git_open_with_retry(const char *path, int flag, ...);
+#endif
+
+#ifdef __GLIBC_PREREQ
+#if __GLIBC_PREREQ(2, 1)
+#define HAVE_STRCHRNUL
+#endif
+#endif
+
+#ifndef HAVE_STRCHRNUL
+#define strchrnul gitstrchrnul
+static inline char *gitstrchrnul(const char *s, int c)
+{
+ while (*s && *s != c)
+ s++;
+ return (char *)s;
+}
+#endif
+
+#ifdef NO_INET_PTON
+int inet_pton(int af, const char *src, void *dst);
+#endif
+
+#ifdef NO_INET_NTOP
+const char *inet_ntop(int af, const void *src, char *dst, size_t size);
+#endif
+
+#ifdef NO_PTHREADS
+#define atexit git_atexit
+int git_atexit(void (*handler)(void));
+#endif
+
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 256
+#endif
+
+#include "../sane-ctype.h"
+
+void git_stable_qsort(void *base, size_t nmemb, size_t size,
+ int(*compar)(const void *, const void *));
+#ifdef INTERNAL_QSORT
+#define qsort git_stable_qsort
+#endif
+
+#define QSORT(base, n, compar) sane_qsort((base), (n), sizeof(*(base)), compar)
+static inline void sane_qsort(void *base, size_t nmemb, size_t size,
+ int(*compar)(const void *, const void *))
+{
+ if (nmemb > 1)
+ qsort(base, nmemb, size, compar);
+}
+
+#define STABLE_QSORT(base, n, compar) \
+ git_stable_qsort((base), (n), sizeof(*(base)), compar)
+
+#ifndef HAVE_ISO_QSORT_S
+int git_qsort_s(void *base, size_t nmemb, size_t size,
+ int (*compar)(const void *, const void *, void *), void *ctx);
+#define qsort_s git_qsort_s
+#endif
+
+#define QSORT_S(base, n, compar, ctx) do { \
+ if (qsort_s((base), (n), sizeof(*(base)), compar, ctx)) \
+ BUG("qsort_s() failed"); \
+} while (0)
+
+#ifdef NO_NSEC
+#undef USE_NSEC
+#define ST_CTIME_NSEC(st) 0
+#define ST_MTIME_NSEC(st) 0
+#else
+#ifdef USE_ST_TIMESPEC
+#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctimespec.tv_nsec))
+#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtimespec.tv_nsec))
+#else
+#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctim.tv_nsec))
+#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtim.tv_nsec))
+#endif
+#endif
+
+#ifndef va_copy
+/*
+ * Since an obvious implementation of va_list would be to make it a
+ * pointer into the stack frame, a simple assignment will work on
+ * many systems. But let's try to be more portable.
+ */
+#ifdef __va_copy
+#define va_copy(dst, src) __va_copy(dst, src)
+#else
+#define va_copy(dst, src) ((dst) = (src))
+#endif
+#endif
+
+#ifndef _POSIX_THREAD_SAFE_FUNCTIONS
+static inline void git_flockfile(FILE *fh UNUSED)
+{
+ ; /* nothing */
+}
+static inline void git_funlockfile(FILE *fh UNUSED)
+{
+ ; /* nothing */
+}
+#undef flockfile
+#undef funlockfile
+#undef getc_unlocked
+#define flockfile(fh) git_flockfile(fh)
+#define funlockfile(fh) git_funlockfile(fh)
+#define getc_unlocked(fh) getc(fh)
+#endif
+
+#ifdef FILENO_IS_A_MACRO
+int git_fileno(FILE *stream);
+# ifndef COMPAT_CODE_FILENO
+# undef fileno
+# define fileno(p) git_fileno(p)
+# endif
+#endif
+
+#ifdef NEED_ACCESS_ROOT_HANDLER
+int git_access(const char *path, int mode);
+# ifndef COMPAT_CODE_ACCESS
+# ifdef access
+# undef access
+# endif
+# define access(path, mode) git_access(path, mode)
+# endif
+#endif
+
+#endif /* COMPAT_POSIX_H */
diff --git a/compat/regex/regex_internal.c b/compat/regex/regex_internal.c
index ec5cc5d2dd..4a4f849629 100644
--- a/compat/regex/regex_internal.c
+++ b/compat/regex/regex_internal.c
@@ -1232,7 +1232,10 @@ re_node_set_merge (re_node_set *dest, const re_node_set *src)
is = src->nelem - 1, id = dest->nelem - 1; is >= 0 && id >= 0; )
{
if (dest->elems[id] == src->elems[is])
- is--, id--;
+ {
+ is--;
+ id--;
+ }
else if (dest->elems[id] < src->elems[is])
dest->elems[--sbase] = src->elems[is--];
else /* if (dest->elems[id] > src->elems[is]) */
diff --git a/compat/regex/regexec.c b/compat/regex/regexec.c
index 2eeec82f40..c08f1bbe1f 100644
--- a/compat/regex/regexec.c
+++ b/compat/regex/regexec.c
@@ -2210,7 +2210,7 @@ sift_states_bkref (const re_match_context_t *mctx, re_sift_context_t *sctx,
/* mctx->bkref_ents may have changed, reload the pointer. */
entry = mctx->bkref_ents + enabled_idx;
}
- while (enabled_idx++, entry++->more);
+ while ((void)enabled_idx++, entry++->more);
}
err = REG_NOERROR;
free_return:
diff --git a/config.c b/config.c
index e127afaa8f..b18b5617fc 100644
--- a/config.c
+++ b/config.c
@@ -31,7 +31,7 @@
#include "hashmap.h"
#include "string-list.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "pager.h"
#include "path.h"
#include "utf8.h"
@@ -1490,11 +1490,6 @@ static int git_default_core_config(const char *var, const char *value,
return 0;
}
- if (!strcmp(var, "core.bigfilethreshold")) {
- big_file_threshold = git_config_ulong(var, value, ctx->kvi);
- return 0;
- }
-
if (!strcmp(var, "core.autocrlf")) {
if (value && !strcasecmp(value, "input")) {
auto_crlf = AUTO_CRLF_INPUT;
diff --git a/config.mak.dev b/config.mak.dev
index 95b7bc46ae..e86b6e1b34 100644
--- a/config.mak.dev
+++ b/config.mak.dev
@@ -41,6 +41,10 @@ DEVELOPER_CFLAGS += -Wwrite-strings
DEVELOPER_CFLAGS += -fno-common
DEVELOPER_CFLAGS += -Wunreachable-code
+ifneq ($(filter clang9,$(COMPILER_FEATURES)),)
+DEVELOPER_CFLAGS += -Wcomma
+endif
+
ifneq ($(filter clang4,$(COMPILER_FEATURES)),)
DEVELOPER_CFLAGS += -Wtautological-constant-out-of-range-compare
endif
diff --git a/connected.c b/connected.c
index 3099da84f3..4415388beb 100644
--- a/connected.c
+++ b/connected.c
@@ -3,7 +3,7 @@
#include "git-compat-util.h"
#include "gettext.h"
#include "hex.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "run-command.h"
#include "sigchain.h"
#include "connected.h"
diff --git a/contrib/coccinelle/meson.build b/contrib/coccinelle/meson.build
index 5d76a7fee6..ea054c924f 100644
--- a/contrib/coccinelle/meson.build
+++ b/contrib/coccinelle/meson.build
@@ -1,4 +1,9 @@
-spatch = find_program('spatch', required: get_option('coccinelle'))
+coccinelle_opt = get_option('coccinelle').require(
+ fs.exists(meson.project_source_root() / '.git'),
+ error_message: 'coccinelle can only be run from a git checkout',
+)
+
+spatch = find_program('spatch', required: coccinelle_opt)
if not spatch.found()
subdir_done()
endif
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 413911be3b..e3d88b0672 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -234,6 +234,17 @@ __git_dequote ()
done
}
+# Prints the number of slash-separated components in a path.
+# 1: Path to count components of.
+__git_count_path_components ()
+{
+ local path="$1"
+ local relative="${path#/}"
+ relative="${relative%/}"
+ local slashes="/${relative//[^\/]}"
+ echo "${#slashes}"
+}
+
# The following function is based on code from:
#
# bash_completion - programmable completion functions for bash 3.2+
@@ -779,16 +790,39 @@ __git_tags ()
__git_dwim_remote_heads ()
{
local pfx="${1-}" cur_="${2-}" sfx="${3-}"
- local fer_pfx="${pfx//\%/%%}" # "escape" for-each-ref format specifiers
# employ the heuristic used by git checkout and git switch
# Try to find a remote branch that cur_es the completion word
# but only output if the branch name is unique
- __git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \
- --sort="refname:strip=3" \
- ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
- "refs/remotes/*/$cur_*" "refs/remotes/*/$cur_*/**" | \
- uniq -u
+ local awk_script='
+ function casemap(s) {
+ if (ENVIRON["IGNORE_CASE"])
+ return tolower(s)
+ else
+ return s
+ }
+ BEGIN {
+ split(ENVIRON["REMOTES"], remotes, /\n/)
+ for (i in remotes)
+ remotes[i] = "refs/remotes/" casemap(remotes[i])
+ cur_ = casemap(ENVIRON["CUR_"])
+ }
+ {
+ ref_case = casemap($0)
+ for (i in remotes) {
+ if (index(ref_case, remotes[i] "/" cur_) == 1) {
+ branch = substr($0, length(remotes[i] "/") + 1)
+ print ENVIRON["PFX"] branch ENVIRON["SFX"]
+ break
+ }
+ }
+ }
+ '
+ __git for-each-ref --format='%(refname)' refs/remotes/ |
+ PFX="$pfx" SFX="$sfx" CUR_="$cur_" \
+ IGNORE_CASE=${GIT_COMPLETION_IGNORE_CASE+1} \
+ REMOTES="$(__git_remotes | sort -r)" awk "$awk_script" |
+ sort | uniq -u
}
# Lists refs from the local (by default) or from a remote repository.
@@ -894,7 +928,8 @@ __git_refs ()
case "HEAD" in
$match*|$umatch*) echo "${pfx}HEAD$sfx" ;;
esac
- __git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \
+ local strip="$(__git_count_path_components "refs/remotes/$remote")"
+ __git for-each-ref --format="$fer_pfx%(refname:strip=$strip)$sfx" \
${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
"refs/remotes/$remote/$match*" \
"refs/remotes/$remote/$match*/**"
diff --git a/contrib/credential/netrc/meson.build b/contrib/credential/netrc/meson.build
index a990dbb86d..3d74547c8a 100644
--- a/contrib/credential/netrc/meson.build
+++ b/contrib/credential/netrc/meson.build
@@ -7,14 +7,16 @@ credential_netrc = custom_target(
install_dir: get_option('libexecdir') / 'git-core',
)
-credential_netrc_testenv = test_environment
-credential_netrc_testenv.set('CREDENTIAL_NETRC_PATH', credential_netrc.full_path())
+if get_option('tests')
+ credential_netrc_testenv = test_environment
+ credential_netrc_testenv.set('CREDENTIAL_NETRC_PATH', credential_netrc.full_path())
-test('t-git-credential-netrc',
- shell,
- args: [ meson.current_source_dir() / 't-git-credential-netrc.sh' ],
- workdir: meson.current_source_dir(),
- env: credential_netrc_testenv,
- depends: test_dependencies + bin_wrappers + [credential_netrc],
- timeout: 0,
-)
+ test('t-git-credential-netrc',
+ shell,
+ args: [ meson.current_source_dir() / 't-git-credential-netrc.sh' ],
+ workdir: meson.current_source_dir(),
+ env: credential_netrc_testenv,
+ depends: test_dependencies + bin_wrappers + [credential_netrc],
+ timeout: 0,
+ )
+endif
diff --git a/contrib/subtree/meson.build b/contrib/subtree/meson.build
index 9c72b23625..63714166a6 100644
--- a/contrib/subtree/meson.build
+++ b/contrib/subtree/meson.build
@@ -12,16 +12,18 @@ git_subtree = custom_target(
install_dir: get_option('libexecdir') / 'git-core',
)
-subtree_test_environment = test_environment
-subtree_test_environment.prepend('PATH', meson.current_build_dir())
+if get_option('tests')
+ subtree_test_environment = test_environment
+ subtree_test_environment.prepend('PATH', meson.current_build_dir())
-test('t7900-subtree', shell,
- args: [ 't7900-subtree.sh' ],
- env: subtree_test_environment,
- workdir: meson.current_source_dir() / 't',
- depends: test_dependencies + bin_wrappers + [ git_subtree ],
- timeout: 0,
-)
+ test('t7900-subtree', shell,
+ args: [ 't7900-subtree.sh' ],
+ env: subtree_test_environment,
+ workdir: meson.current_source_dir() / 't',
+ depends: test_dependencies + bin_wrappers + [ git_subtree ],
+ timeout: 0,
+ )
+endif
if get_option('docs').contains('man')
subtree_xml = custom_target(
diff --git a/convert.c b/convert.c
index 9cc0ca20ca..8783e17941 100644
--- a/convert.c
+++ b/convert.c
@@ -8,7 +8,7 @@
#include "copy.h"
#include "gettext.h"
#include "hex.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "attr.h"
#include "run-command.h"
#include "quote.h"
diff --git a/csum-file.c b/csum-file.c
index b58c183a4f..6e21e3cac8 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -8,8 +8,6 @@
* able to verify hasn't been messed with afterwards.
*/
-#define USE_THE_REPOSITORY_VARIABLE
-
#include "git-compat-util.h"
#include "csum-file.h"
#include "git-zlib.h"
@@ -148,21 +146,23 @@ void hashwrite(struct hashfile *f, const void *buf, unsigned int count)
}
}
-struct hashfile *hashfd_check(const char *name)
+struct hashfile *hashfd_check(const struct git_hash_algo *algop,
+ const char *name)
{
int sink, check;
struct hashfile *f;
sink = xopen("/dev/null", O_WRONLY);
check = xopen(name, O_RDONLY);
- f = hashfd(sink, name);
+ f = hashfd(algop, sink, name);
f->check_fd = check;
f->check_buffer = xmalloc(f->buffer_len);
return f;
}
-static struct hashfile *hashfd_internal(int fd, const char *name,
+static struct hashfile *hashfd_internal(const struct git_hash_algo *algop,
+ int fd, const char *name,
struct progress *tp,
size_t buffer_len)
{
@@ -176,7 +176,7 @@ static struct hashfile *hashfd_internal(int fd, const char *name,
f->do_crc = 0;
f->skip_hash = 0;
- f->algop = unsafe_hash_algo(the_hash_algo);
+ f->algop = unsafe_hash_algo(algop);
f->algop->init_fn(&f->ctx);
f->buffer_len = buffer_len;
@@ -186,17 +186,19 @@ static struct hashfile *hashfd_internal(int fd, const char *name,
return f;
}
-struct hashfile *hashfd(int fd, const char *name)
+struct hashfile *hashfd(const struct git_hash_algo *algop,
+ int fd, const char *name)
{
/*
* Since we are not going to use a progress meter to
* measure the rate of data passing through this hashfile,
* use a larger buffer size to reduce fsync() calls.
*/
- return hashfd_internal(fd, name, NULL, 128 * 1024);
+ return hashfd_internal(algop, fd, name, NULL, 128 * 1024);
}
-struct hashfile *hashfd_throughput(int fd, const char *name, struct progress *tp)
+struct hashfile *hashfd_throughput(const struct git_hash_algo *algop,
+ int fd, const char *name, struct progress *tp)
{
/*
* Since we are expecting to report progress of the
@@ -204,7 +206,7 @@ struct hashfile *hashfd_throughput(int fd, const char *name, struct progress *tp
* size so the progress indicators arrive at a more
* frequent rate.
*/
- return hashfd_internal(fd, name, tp, 8 * 1024);
+ return hashfd_internal(algop, fd, name, tp, 8 * 1024);
}
void hashfile_checkpoint_init(struct hashfile *f,
@@ -246,13 +248,15 @@ uint32_t crc32_end(struct hashfile *f)
return f->crc32;
}
-int hashfile_checksum_valid(const unsigned char *data, size_t total_len)
+int hashfile_checksum_valid(const struct git_hash_algo *algop,
+ const unsigned char *data, size_t total_len)
{
unsigned char got[GIT_MAX_RAWSZ];
struct git_hash_ctx ctx;
- const struct git_hash_algo *algop = unsafe_hash_algo(the_hash_algo);
size_t data_len = total_len - algop->rawsz;
+ algop = unsafe_hash_algo(algop);
+
if (total_len < algop->rawsz)
return 0; /* say "too short"? */
diff --git a/csum-file.h b/csum-file.h
index ffccbf0996..07ae11024a 100644
--- a/csum-file.h
+++ b/csum-file.h
@@ -45,9 +45,12 @@ int hashfile_truncate(struct hashfile *, struct hashfile_checkpoint *);
#define CSUM_FSYNC 2
#define CSUM_HASH_IN_STREAM 4
-struct hashfile *hashfd(int fd, const char *name);
-struct hashfile *hashfd_check(const char *name);
-struct hashfile *hashfd_throughput(int fd, const char *name, struct progress *tp);
+struct hashfile *hashfd(const struct git_hash_algo *algop,
+ int fd, const char *name);
+struct hashfile *hashfd_check(const struct git_hash_algo *algop,
+ const char *name);
+struct hashfile *hashfd_throughput(const struct git_hash_algo *algop,
+ int fd, const char *name, struct progress *tp);
/*
* Free the hashfile without flushing its contents to disk. This only
@@ -66,7 +69,8 @@ void crc32_begin(struct hashfile *);
uint32_t crc32_end(struct hashfile *);
/* Verify checksum validity while reading. Returns non-zero on success. */
-int hashfile_checksum_valid(const unsigned char *data, size_t len);
+int hashfile_checksum_valid(const struct git_hash_algo *algop,
+ const unsigned char *data, size_t len);
/*
* Returns the total number of bytes fed to the hashfile so far (including ones
diff --git a/delta-islands.c b/delta-islands.c
index 3aec43fada..36c94799d6 100644
--- a/delta-islands.c
+++ b/delta-islands.c
@@ -1,4 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "git-compat-util.h"
@@ -267,8 +266,7 @@ void resolve_tree_islands(struct repository *r,
QSORT(todo, nr, tree_depth_compare);
if (progress)
- progress_state = start_progress(the_repository,
- _("Propagating island marks"), nr);
+ progress_state = start_progress(r, _("Propagating island marks"), nr);
for (i = 0; i < nr; i++) {
struct object_entry *ent = todo[i].entry;
@@ -490,9 +488,9 @@ void load_delta_islands(struct repository *r, int progress)
island_marks = kh_init_oid_map();
- git_config(island_config_callback, &ild);
+ repo_config(r, island_config_callback, &ild);
ild.remote_islands = kh_init_str();
- refs_for_each_ref(get_main_ref_store(the_repository),
+ refs_for_each_ref(get_main_ref_store(r),
find_island_for_ref, &ild);
free_config_regexes(&ild);
deduplicate_islands(ild.remote_islands, r);
@@ -502,7 +500,7 @@ void load_delta_islands(struct repository *r, int progress)
fprintf(stderr, _("Marked %d islands, done.\n"), island_counter);
}
-void propagate_island_marks(struct commit *commit)
+void propagate_island_marks(struct repository *r, struct commit *commit)
{
khiter_t pos = kh_get_oid_map(island_marks, commit->object.oid);
@@ -510,8 +508,8 @@ void propagate_island_marks(struct commit *commit)
struct commit_list *p;
struct island_bitmap *root_marks = kh_value(island_marks, pos);
- repo_parse_commit(the_repository, commit);
- set_island_marks(&repo_get_commit_tree(the_repository, commit)->object,
+ repo_parse_commit(r, commit);
+ set_island_marks(&repo_get_commit_tree(r, commit)->object,
root_marks);
for (p = commit->parents; p; p = p->next)
set_island_marks(&p->item->object, root_marks);
diff --git a/delta-islands.h b/delta-islands.h
index 8d1591ae28..6107660306 100644
--- a/delta-islands.h
+++ b/delta-islands.h
@@ -12,7 +12,7 @@ void resolve_tree_islands(struct repository *r,
int progress,
struct packing_data *to_pack);
void load_delta_islands(struct repository *r, int progress);
-void propagate_island_marks(struct commit *commit);
+void propagate_island_marks(struct repository *r, struct commit *commit);
int compute_pack_layers(struct packing_data *to_pack);
void free_island_marks(void);
diff --git a/detect-compiler b/detect-compiler
index a87650b71b..124ebdd4c9 100755
--- a/detect-compiler
+++ b/detect-compiler
@@ -9,7 +9,7 @@ CC="$*"
#
# FreeBSD clang version 3.4.1 (tags/RELEASE...)
get_version_line() {
- LANG=C LC_ALL=C $CC -v 2>&1 | grep ' version '
+ LANG=C LC_ALL=C $CC -v 2>&1 | sed -n '/ version /{p;q;}'
}
get_family() {
diff --git a/diagnose.c b/diagnose.c
index bd485effea..b1be74be98 100644
--- a/diagnose.c
+++ b/diagnose.c
@@ -7,7 +7,7 @@
#include "gettext.h"
#include "hex.h"
#include "strvec.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "packfile.h"
#include "parse-options.h"
#include "repository.h"
diff --git a/diff-delta.c b/diff-delta.c
index a4faf73829..71d37368d6 100644
--- a/diff-delta.c
+++ b/diff-delta.c
@@ -438,19 +438,31 @@ create_delta(const struct delta_index *index,
op = out + outpos++;
i = 0x80;
- if (moff & 0x000000ff)
- out[outpos++] = moff >> 0, i |= 0x01;
- if (moff & 0x0000ff00)
- out[outpos++] = moff >> 8, i |= 0x02;
- if (moff & 0x00ff0000)
- out[outpos++] = moff >> 16, i |= 0x04;
- if (moff & 0xff000000)
- out[outpos++] = moff >> 24, i |= 0x08;
-
- if (msize & 0x00ff)
- out[outpos++] = msize >> 0, i |= 0x10;
- if (msize & 0xff00)
- out[outpos++] = msize >> 8, i |= 0x20;
+ if (moff & 0x000000ff) {
+ out[outpos++] = moff >> 0;
+ i |= 0x01;
+ }
+ if (moff & 0x0000ff00) {
+ out[outpos++] = moff >> 8;
+ i |= 0x02;
+ }
+ if (moff & 0x00ff0000) {
+ out[outpos++] = moff >> 16;
+ i |= 0x04;
+ }
+ if (moff & 0xff000000) {
+ out[outpos++] = moff >> 24;
+ i |= 0x08;
+ }
+
+ if (msize & 0x00ff) {
+ out[outpos++] = msize >> 0;
+ i |= 0x10;
+ }
+ if (msize & 0xff00) {
+ out[outpos++] = msize >> 8;
+ i |= 0x20;
+ }
*op = i;
diff --git a/diff-lib.c b/diff-lib.c
index 353b473ed5..244468dd1a 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -172,7 +172,7 @@ void run_diff_files(struct rev_info *revs, unsigned int option)
* these from (stage - 2).
*/
dpath = combine_diff_path_new(ce->name, ce_namelen(ce),
- wt_mode, null_oid(), 2);
+ wt_mode, null_oid(the_hash_algo), 2);
while (i < entries) {
struct cache_entry *nce = istate->cache[i];
@@ -257,7 +257,7 @@ void run_diff_files(struct rev_info *revs, unsigned int option)
ce_intent_to_add(ce)) {
newmode = ce_mode_from_stat(ce, st.st_mode);
diff_addremove(&revs->diffopt, '+', newmode,
- null_oid(), 0, ce->name, 0);
+ null_oid(the_hash_algo), 0, ce->name, 0);
continue;
}
@@ -274,7 +274,7 @@ void run_diff_files(struct rev_info *revs, unsigned int option)
}
oldmode = ce->ce_mode;
old_oid = &ce->oid;
- new_oid = changed ? null_oid() : &ce->oid;
+ new_oid = changed ? null_oid(the_hash_algo) : &ce->oid;
diff_change(&revs->diffopt, oldmode, newmode,
old_oid, new_oid,
!is_null_oid(old_oid),
@@ -330,7 +330,7 @@ static int get_stat_data(const struct cache_entry *ce,
0, dirty_submodule);
if (changed) {
mode = ce_mode_from_stat(ce, st.st_mode);
- oid = null_oid();
+ oid = null_oid(the_hash_algo);
}
}
@@ -402,7 +402,7 @@ static int show_modified(struct rev_info *revs,
p = combine_diff_path_new(new_entry->name,
ce_namelen(new_entry),
- mode, null_oid(), 2);
+ mode, null_oid(the_hash_algo), 2);
p->parent[0].status = DIFF_STATUS_MODIFIED;
p->parent[0].mode = new_entry->ce_mode;
oidcpy(&p->parent[0].oid, &new_entry->oid);
diff --git a/diff-no-index.c b/diff-no-index.c
index 6f277892d3..9739b2b268 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -113,7 +113,8 @@ static void populate_from_stdin(struct diff_filespec *s)
populate_common(s, &buf);
}
-static struct diff_filespec *noindex_filespec(const char *name, int mode,
+static struct diff_filespec *noindex_filespec(const struct git_hash_algo *algop,
+ const char *name, int mode,
enum special special)
{
struct diff_filespec *s;
@@ -121,7 +122,7 @@ static struct diff_filespec *noindex_filespec(const char *name, int mode,
if (!name)
name = "/dev/null";
s = alloc_filespec(name);
- fill_filespec(s, null_oid(), 0, mode);
+ fill_filespec(s, null_oid(algop), 0, mode);
if (special == SPECIAL_STDIN)
populate_from_stdin(s);
else if (special == SPECIAL_PIPE)
@@ -129,7 +130,7 @@ static struct diff_filespec *noindex_filespec(const char *name, int mode,
return s;
}
-static int queue_diff(struct diff_options *o,
+static int queue_diff(struct diff_options *o, const struct git_hash_algo *algop,
const char *name1, const char *name2, int recursing)
{
int mode1 = 0, mode2 = 0;
@@ -145,14 +146,14 @@ static int queue_diff(struct diff_options *o,
if (S_ISDIR(mode1)) {
/* 2 is file that is created */
- d1 = noindex_filespec(NULL, 0, SPECIAL_NONE);
- d2 = noindex_filespec(name2, mode2, special2);
+ d1 = noindex_filespec(algop, NULL, 0, SPECIAL_NONE);
+ d2 = noindex_filespec(algop, name2, mode2, special2);
name2 = NULL;
mode2 = 0;
} else {
/* 1 is file that is deleted */
- d1 = noindex_filespec(name1, mode1, special1);
- d2 = noindex_filespec(NULL, 0, SPECIAL_NONE);
+ d1 = noindex_filespec(algop, name1, mode1, special1);
+ d2 = noindex_filespec(algop, NULL, 0, SPECIAL_NONE);
name1 = NULL;
mode1 = 0;
}
@@ -217,7 +218,7 @@ static int queue_diff(struct diff_options *o,
n2 = buffer2.buf;
}
- ret = queue_diff(o, n1, n2, 1);
+ ret = queue_diff(o, algop, n1, n2, 1);
}
string_list_clear(&p1, 0);
string_list_clear(&p2, 0);
@@ -234,8 +235,8 @@ static int queue_diff(struct diff_options *o,
SWAP(special1, special2);
}
- d1 = noindex_filespec(name1, mode1, special1);
- d2 = noindex_filespec(name2, mode2, special2);
+ d1 = noindex_filespec(algop, name1, mode1, special1);
+ d2 = noindex_filespec(algop, name2, mode2, special2);
diff_queue(&diff_queued_diff, d1, d2);
return 0;
}
@@ -297,9 +298,8 @@ static const char * const diff_no_index_usage[] = {
NULL
};
-int diff_no_index(struct rev_info *revs,
- int implicit_no_index,
- int argc, const char **argv)
+int diff_no_index(struct rev_info *revs, const struct git_hash_algo *algop,
+ int implicit_no_index, int argc, const char **argv)
{
int i, no_index;
int ret = 1;
@@ -354,7 +354,7 @@ int diff_no_index(struct rev_info *revs,
setup_diff_pager(&revs->diffopt);
revs->diffopt.flags.exit_with_status = 1;
- if (queue_diff(&revs->diffopt, paths[0], paths[1], 0))
+ if (queue_diff(&revs->diffopt, algop, paths[0], paths[1], 0))
goto out;
diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/");
diffcore_std(&revs->diffopt);
diff --git a/diff.c b/diff.c
index f2fcc7f3c2..63e9ecb30c 100644
--- a/diff.c
+++ b/diff.c
@@ -23,7 +23,7 @@
#include "color.h"
#include "run-command.h"
#include "utf8.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "userdiff.h"
#include "submodule.h"
#include "hashmap.h"
@@ -4193,7 +4193,8 @@ int diff_populate_filespec(struct repository *r,
* is probably fine.
*/
if (check_binary &&
- s->size > big_file_threshold && s->is_binary == -1) {
+ s->size > repo_settings_get_big_file_threshold(the_repository) &&
+ s->is_binary == -1) {
s->is_binary = 1;
return 0;
}
@@ -4243,7 +4244,8 @@ object_read:
if (size_only || check_binary) {
if (size_only)
return 0;
- if (s->size > big_file_threshold && s->is_binary == -1) {
+ if (s->size > repo_settings_get_big_file_threshold(the_repository) &&
+ s->is_binary == -1) {
s->is_binary = 1;
return 0;
}
@@ -4344,7 +4346,7 @@ static struct diff_tempfile *prepare_temp_file(struct repository *r,
die_errno("readlink(%s)", one->path);
prep_temp_blob(r->index, one->path, temp, sb.buf, sb.len,
(one->oid_valid ?
- &one->oid : null_oid()),
+ &one->oid : null_oid(the_hash_algo)),
(one->oid_valid ?
one->mode : S_IFLNK));
strbuf_release(&sb);
@@ -4353,7 +4355,7 @@ static struct diff_tempfile *prepare_temp_file(struct repository *r,
/* we can borrow from the file in the work tree */
temp->name = one->path;
if (!one->oid_valid)
- oid_to_hex_r(temp->hex, null_oid());
+ oid_to_hex_r(temp->hex, null_oid(the_hash_algo));
else
oid_to_hex_r(temp->hex, &one->oid);
/* Even though we may sometimes borrow the
@@ -6652,8 +6654,8 @@ static void create_filepairs_for_header_only_notifications(struct diff_options *
one = alloc_filespec(e->key);
two = alloc_filespec(e->key);
- fill_filespec(one, null_oid(), 0, 0);
- fill_filespec(two, null_oid(), 0, 0);
+ fill_filespec(one, null_oid(the_hash_algo), 0, 0);
+ fill_filespec(two, null_oid(the_hash_algo), 0, 0);
p = diff_queue(q, one, two);
p->status = DIFF_STATUS_MODIFIED;
}
diff --git a/diff.h b/diff.h
index 42463edbdd..62e5768a9a 100644
--- a/diff.h
+++ b/diff.h
@@ -689,7 +689,7 @@ void flush_one_hunk(struct object_id *result, struct git_hash_ctx *ctx);
int diff_result_code(struct rev_info *);
-int diff_no_index(struct rev_info *,
+int diff_no_index(struct rev_info *, const struct git_hash_algo *algop,
int implicit_no_index, int, const char **);
int index_differs_from(struct repository *r, const char *def,
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 5002e896aa..179731462b 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -8,7 +8,7 @@
#include "git-compat-util.h"
#include "diff.h"
#include "diffcore.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "hashmap.h"
#include "mem-pool.h"
#include "oid-array.h"
@@ -1406,7 +1406,7 @@ void diffcore_rename_extended(struct diff_options *options,
trace2_region_enter("diff", "setup", options->repo);
info.setup = 0;
- assert(!dir_rename_count || strmap_empty(dir_rename_count));
+ ASSERT(!dir_rename_count || strmap_empty(dir_rename_count));
want_copies = (detect_rename == DIFF_DETECT_COPY);
if (dirs_removed && (break_idx || want_copies))
BUG("dirs_removed incompatible with break/copy detection");
diff --git a/dir.c b/dir.c
index cbd82be6c9..5c4675b4ac 100644
--- a/dir.c
+++ b/dir.c
@@ -17,8 +17,7 @@
#include "environment.h"
#include "gettext.h"
#include "name-hash.h"
-#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "path.h"
#include "refs.h"
#include "repository.h"
@@ -4035,7 +4034,7 @@ static void connect_wt_gitdir_in_nested(const char *sub_worktree,
*/
i++;
- sub = submodule_from_path(&subrepo, null_oid(), ce->name);
+ sub = submodule_from_path(&subrepo, null_oid(the_hash_algo), ce->name);
if (!sub || !is_submodule_active(&subrepo, ce->name))
/* .gitmodules broken or inactive sub */
continue;
@@ -4063,12 +4062,12 @@ void connect_work_tree_and_git_dir(const char *work_tree_,
/* Prepare .git file */
strbuf_addf(&gitfile_sb, "%s/.git", work_tree_);
- if (safe_create_leading_directories_const(gitfile_sb.buf))
+ if (safe_create_leading_directories_const(the_repository, gitfile_sb.buf))
die(_("could not create directories for %s"), gitfile_sb.buf);
/* Prepare config file */
strbuf_addf(&cfg_sb, "%s/config", git_dir_);
- if (safe_create_leading_directories_const(cfg_sb.buf))
+ if (safe_create_leading_directories_const(the_repository, cfg_sb.buf))
die(_("could not create directories for %s"), cfg_sb.buf);
git_dir = real_pathdup(git_dir_, 1);
diff --git a/entry.c b/entry.c
index 81b321e53d..f36ec5ad24 100644
--- a/entry.c
+++ b/entry.c
@@ -1,7 +1,7 @@
#define USE_THE_REPOSITORY_VARIABLE
#include "git-compat-util.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "dir.h"
#include "environment.h"
#include "gettext.h"
diff --git a/environment.c b/environment.c
index 9e4c7781be..3c32367c28 100644
--- a/environment.c
+++ b/environment.c
@@ -49,7 +49,6 @@ int fsync_object_files = -1;
int use_fsync = -1;
enum fsync_method fsync_method = FSYNC_METHOD_DEFAULT;
enum fsync_component fsync_components = FSYNC_COMPONENTS_DEFAULT;
-unsigned long big_file_threshold = 512 * 1024 * 1024;
char *editor_program;
char *askpass_program;
char *excludes_file;
diff --git a/environment.h b/environment.h
index 45e690f203..3d98461a06 100644
--- a/environment.h
+++ b/environment.h
@@ -152,9 +152,6 @@ extern char *apply_default_ignorewhitespace;
extern char *git_attributes_file;
extern int zlib_compression_level;
extern int pack_compression_level;
-extern size_t packed_git_window_size;
-extern size_t packed_git_limit;
-extern unsigned long big_file_threshold;
extern unsigned long pack_size_limit_cfg;
extern int max_allowed_tree_depth;
diff --git a/ewah/ewah_bitmap.c b/ewah/ewah_bitmap.c
index 67f8f588e0..056c410efb 100644
--- a/ewah/ewah_bitmap.c
+++ b/ewah/ewah_bitmap.c
@@ -371,6 +371,39 @@ void ewah_iterator_init(struct ewah_iterator *it, struct ewah_bitmap *parent)
read_new_rlw(it);
}
+void ewah_or_iterator_init(struct ewah_or_iterator *it,
+ struct ewah_bitmap **parents, size_t nr)
+{
+ size_t i;
+
+ memset(it, 0, sizeof(*it));
+
+ ALLOC_ARRAY(it->its, nr);
+ for (i = 0; i < nr; i++)
+ ewah_iterator_init(&it->its[it->nr++], parents[i]);
+}
+
+int ewah_or_iterator_next(eword_t *next, struct ewah_or_iterator *it)
+{
+ eword_t buf, out = 0;
+ size_t i;
+ int ret = 0;
+
+ for (i = 0; i < it->nr; i++)
+ if (ewah_iterator_next(&buf, &it->its[i])) {
+ out |= buf;
+ ret = 1;
+ }
+
+ *next = out;
+ return ret;
+}
+
+void ewah_or_iterator_release(struct ewah_or_iterator *it)
+{
+ free(it->its);
+}
+
void ewah_xor(
struct ewah_bitmap *ewah_i,
struct ewah_bitmap *ewah_j,
diff --git a/ewah/ewok.h b/ewah/ewok.h
index 5e357e2493..c29d354236 100644
--- a/ewah/ewok.h
+++ b/ewah/ewok.h
@@ -148,6 +148,18 @@ void ewah_iterator_init(struct ewah_iterator *it, struct ewah_bitmap *parent);
*/
int ewah_iterator_next(eword_t *next, struct ewah_iterator *it);
+struct ewah_or_iterator {
+ struct ewah_iterator *its;
+ size_t nr;
+};
+
+void ewah_or_iterator_init(struct ewah_or_iterator *it,
+ struct ewah_bitmap **parents, size_t nr);
+
+int ewah_or_iterator_next(eword_t *next, struct ewah_or_iterator *it);
+
+void ewah_or_iterator_release(struct ewah_or_iterator *it);
+
void ewah_xor(
struct ewah_bitmap *ewah_i,
struct ewah_bitmap *ewah_j,
diff --git a/fetch-pack.c b/fetch-pack.c
index 1ed5e11dd5..210dc30d50 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -24,7 +24,7 @@
#include "oid-array.h"
#include "oidset.h"
#include "packfile.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "path.h"
#include "connected.h"
#include "fetch-negotiator.h"
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index 5b63c3b088..501b5acdd4 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -6,7 +6,7 @@
#include "environment.h"
#include "refs.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "diff.h"
#include "diff-merges.h"
#include "hex.h"
diff --git a/fsck.c b/fsck.c
index 9fc4c25ffd..8dc8472ceb 100644
--- a/fsck.c
+++ b/fsck.c
@@ -4,7 +4,7 @@
#include "date.h"
#include "dir.h"
#include "hex.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "path.h"
#include "repository.h"
#include "object.h"
diff --git a/generate-configlist.sh b/generate-configlist.sh
index dffdaada8b..b06da53c89 100755
--- a/generate-configlist.sh
+++ b/generate-configlist.sh
@@ -13,10 +13,18 @@ print_config_list () {
cat <<EOF
static const char *config_name_list[] = {
EOF
- grep -h '^[a-zA-Z].*\..*::$' "$SOURCE_DIR"/Documentation/*config.adoc "$SOURCE_DIR"/Documentation/config/*.adoc |
- sed '/deprecated/d; s/::$//; s/, */\n/g' |
- sort |
- sed 's/^.*$/ "&",/'
+ sed -E '
+/^`?[a-zA-Z].*\..*`?::$/ {
+ /deprecated/d;
+ s/::$//;
+ s/`//g;
+ s/^.*$/ "&",/;
+ s/, */",\n "/g;
+ p;};
+d' \
+ "$SOURCE_DIR"/Documentation/*config.adoc \
+ "$SOURCE_DIR"/Documentation/config/*.adoc|
+ sort
cat <<EOF
NULL,
};
diff --git a/git-compat-util.h b/git-compat-util.h
index 1218fcf81a..36b9577c8d 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -23,26 +23,9 @@
#include <crtdbg.h>
#endif
-struct strbuf;
-
-
-#define _FILE_OFFSET_BITS 64
+#include "compat/posix.h"
-
-/* Derived from Linux "Features Test Macro" header
- * Convenience macros to test the versions of gcc (or
- * a compatible compiler).
- * Use them like this:
- * #if GIT_GNUC_PREREQ (2,8)
- * ... code requiring gcc 2.8 or later ...
- * #endif
-*/
-#if defined(__GNUC__) && defined(__GNUC_MINOR__)
-# define GIT_GNUC_PREREQ(maj, min) \
- ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
-#else
- #define GIT_GNUC_PREREQ(maj, min) 0
-#endif
+struct strbuf;
#if defined(__GNUC__) || defined(__clang__)
# define PRAGMA(pragma) _Pragma(#pragma)
@@ -183,71 +166,6 @@ DISABLE_WARNING(-Wsign-compare)
/* Approximation of the length of the decimal representation of this type. */
#define decimal_length(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1)
-#ifdef __MINGW64__
-#define _POSIX_C_SOURCE 1
-#elif defined(__sun__)
- /*
- * On Solaris, when _XOPEN_EXTENDED is set, its header file
- * forces the programs to be XPG4v2, defeating any _XOPEN_SOURCE
- * setting to say we are XPG5 or XPG6. Also on Solaris,
- * XPG6 programs must be compiled with a c99 compiler, while
- * non XPG6 programs must be compiled with a pre-c99 compiler.
- */
-# if __STDC_VERSION__ - 0 >= 199901L
-# define _XOPEN_SOURCE 600
-# else
-# define _XOPEN_SOURCE 500
-# endif
-#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \
- !defined(_M_UNIX) && !defined(__sgi) && !defined(__DragonFly__) && \
- !defined(__TANDEM) && !defined(__QNX__) && !defined(__MirBSD__) && \
- !defined(__CYGWIN__)
-#define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
-#define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
-#endif
-#define _ALL_SOURCE 1
-#define _GNU_SOURCE 1
-#define _BSD_SOURCE 1
-#define _DEFAULT_SOURCE 1
-#define _NETBSD_SOURCE 1
-#define _SGI_SOURCE 1
-
-/*
- * UNUSED marks a function parameter that is always unused. It also
- * can be used to annotate a function, a variable, or a type that is
- * always unused.
- *
- * A callback interface may dictate that a function accepts a
- * parameter at that position, but the implementation of the function
- * may not need to use the parameter. In such a case, mark the parameter
- * with UNUSED.
- *
- * When a parameter may be used or unused, depending on conditional
- * compilation, consider using MAYBE_UNUSED instead.
- */
-#if GIT_GNUC_PREREQ(4, 5)
-#define UNUSED __attribute__((unused)) \
- __attribute__((deprecated ("parameter declared as UNUSED")))
-#elif defined(__GNUC__)
-#define UNUSED __attribute__((unused)) \
- __attribute__((deprecated))
-#else
-#define UNUSED
-#endif
-
-#if defined(WIN32) && !defined(__CYGWIN__) /* Both MinGW and MSVC */
-# if !defined(_WIN32_WINNT)
-# define _WIN32_WINNT 0x0600
-# endif
-#define WIN32_LEAN_AND_MEAN /* stops windows.h including winsock.h */
-#include <winsock2.h>
-#ifndef NO_UNIX_SOCKETS
-#include <afunix.h>
-#endif
-#include <windows.h>
-#define GIT_WINDOWS_NATIVE
-#endif
-
#if defined(NO_UNIX_SOCKETS) || !defined(GIT_WINDOWS_NATIVE)
static inline int _have_unix_sockets(void)
{
@@ -260,45 +178,6 @@ static inline int _have_unix_sockets(void)
#define have_unix_sockets _have_unix_sockets
#endif
-#include <unistd.h>
-#include <stdio.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <string.h>
-#ifdef HAVE_STRINGS_H
-#include <strings.h> /* for strcasecmp() */
-#endif
-#include <errno.h>
-#include <limits.h>
-#include <locale.h>
-#ifdef NEEDS_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-#include <sys/types.h>
-#include <dirent.h>
-#include <sys/time.h>
-#include <time.h>
-#include <signal.h>
-#include <assert.h>
-#include <regex.h>
-#include <utime.h>
-#include <syslog.h>
-#if !defined(NO_POLL_H)
-#include <poll.h>
-#elif !defined(NO_SYS_POLL_H)
-#include <sys/poll.h>
-#else
-/* Pull the compat stuff */
-#include <poll.h>
-#endif
-#ifdef HAVE_BSD_SYSCTL
-#include <sys/sysctl.h>
-#endif
-
/* Used by compat/win32/path-utils.h, and more */
static inline int is_xplatform_dir_sep(int c)
{
@@ -315,48 +194,6 @@ static inline int is_xplatform_dir_sep(int c)
#elif defined(_MSC_VER)
#include "compat/win32/path-utils.h"
#include "compat/msvc.h"
-#else
-#include <sys/utsname.h>
-#include <sys/wait.h>
-#include <sys/resource.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <sys/statvfs.h>
-#include <termios.h>
-#ifndef NO_SYS_SELECT_H
-#include <sys/select.h>
-#endif
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <pwd.h>
-#include <sys/un.h>
-#ifndef NO_INTTYPES_H
-#include <inttypes.h>
-#else
-#include <stdint.h>
-#endif
-#ifdef HAVE_ARC4RANDOM_LIBBSD
-#include <bsd/stdlib.h>
-#endif
-#ifdef HAVE_GETRANDOM
-#include <sys/random.h>
-#endif
-#ifdef NO_INTPTR_T
-/*
- * On I16LP32, ILP32 and LP64 "long" is the safe bet, however
- * on LLP86, IL33LLP64 and P64 it needs to be "long long",
- * while on IP16 and IP16L32 it is "int" (resp. "short")
- * Size needs to match (or exceed) 'sizeof(void *)'.
- * We can't take "long long" here as not everybody has it.
- */
-typedef long intptr_t;
-typedef unsigned long uintptr_t;
-#endif
-#undef _ALL_SOURCE /* AIX 5.3L defines a struct list with _ALL_SOURCE. */
-#include <grp.h>
-#define _ALL_SOURCE 1
#endif
/* used on Mac OS X */
@@ -377,60 +214,6 @@ static inline const char *precompose_string_if_needed(const char *in)
#define probe_utf8_pathname_composition()
#endif
-#ifdef MKDIR_WO_TRAILING_SLASH
-#define mkdir(a,b) compat_mkdir_wo_trailing_slash((a),(b))
-int compat_mkdir_wo_trailing_slash(const char*, mode_t);
-#endif
-
-#ifdef time
-#undef time
-#endif
-static inline time_t git_time(time_t *tloc)
-{
- struct timeval tv;
-
- /*
- * Avoid time(NULL), which can disagree with gettimeofday(2)
- * and filesystem timestamps.
- */
- gettimeofday(&tv, NULL);
-
- if (tloc)
- *tloc = tv.tv_sec;
- return tv.tv_sec;
-}
-#define time git_time
-
-#ifdef NO_STRUCT_ITIMERVAL
-struct itimerval {
- struct timeval it_interval;
- struct timeval it_value;
-};
-#endif
-
-#ifdef NO_SETITIMER
-static inline int git_setitimer(int which UNUSED,
- const struct itimerval *value UNUSED,
- struct itimerval *newvalue UNUSED) {
- return 0; /* pretend success */
-}
-#undef setitimer
-#define setitimer(which,value,ovalue) git_setitimer(which,value,ovalue)
-#endif
-
-#ifndef NO_LIBGEN_H
-#include <libgen.h>
-#else
-#define basename gitbasename
-char *gitbasename(char *);
-#define dirname gitdirname
-char *gitdirname(char *);
-#endif
-
-#ifndef NO_ICONV
-#include <iconv.h>
-#endif
-
#ifndef NO_OPENSSL
#ifdef __APPLE__
#undef __AVAILABILITY_MACROS_USES_AVAILABILITY
@@ -448,34 +231,6 @@ char *gitdirname(char *);
# include <sys/sysinfo.h>
#endif
-/* On most systems <netdb.h> would have given us this, but
- * not on some systems (e.g. z/OS).
- */
-#ifndef NI_MAXHOST
-#define NI_MAXHOST 1025
-#endif
-
-#ifndef NI_MAXSERV
-#define NI_MAXSERV 32
-#endif
-
-/* On most systems <limits.h> would have given us this, but
- * not on some systems (e.g. GNU/Hurd).
- */
-#ifndef PATH_MAX
-#define PATH_MAX 4096
-#endif
-
-#ifndef NAME_MAX
-#define NAME_MAX 255
-#endif
-
-typedef uintmax_t timestamp_t;
-#define PRItime PRIuMAX
-#define parse_timestamp strtoumax
-#define TIME_MAX UINTMAX_MAX
-#define TIME_MIN 0
-
#ifndef PATH_SEP
#define PATH_SEP ':'
#endif
@@ -499,11 +254,6 @@ static inline int noop_core_config(const char *var UNUSED,
#define platform_core_config noop_core_config
#endif
-int lstat_cache_aware_rmdir(const char *path);
-#if !defined(__MINGW32__) && !defined(_MSC_VER)
-#define rmdir lstat_cache_aware_rmdir
-#endif
-
#ifndef has_dos_drive_prefix
static inline int git_has_dos_drive_prefix(const char *path UNUSED)
{
@@ -831,25 +581,6 @@ static inline bool strip_suffix(const char *str, const char *suffix,
memcpy(_swap_b_ptr, _swap_buffer, sizeof(a)); \
} while (0)
-#if defined(NO_MMAP) || defined(USE_WIN32_MMAP)
-
-#ifndef PROT_READ
-#define PROT_READ 1
-#define PROT_WRITE 2
-#define MAP_PRIVATE 1
-#endif
-
-#define mmap git_mmap
-#define munmap git_munmap
-void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
-int git_munmap(void *start, size_t length);
-
-#else /* NO_MMAP || USE_WIN32_MMAP */
-
-#include <sys/mman.h>
-
-#endif /* NO_MMAP || USE_WIN32_MMAP */
-
#ifdef NO_MMAP
/* This value must be multiple of (pagesize * 2) */
@@ -865,176 +596,17 @@ int git_munmap(void *start, size_t length);
#endif /* NO_MMAP */
-#ifndef MAP_FAILED
-#define MAP_FAILED ((void *)-1)
-#endif
-
#ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
#define on_disk_bytes(st) ((st).st_size)
#else
#define on_disk_bytes(st) ((st).st_blocks * 512)
#endif
-#ifdef NEEDS_MODE_TRANSLATION
-#undef S_IFMT
-#undef S_IFREG
-#undef S_IFDIR
-#undef S_IFLNK
-#undef S_IFBLK
-#undef S_IFCHR
-#undef S_IFIFO
-#undef S_IFSOCK
-#define S_IFMT 0170000
-#define S_IFREG 0100000
-#define S_IFDIR 0040000
-#define S_IFLNK 0120000
-#define S_IFBLK 0060000
-#define S_IFCHR 0020000
-#define S_IFIFO 0010000
-#define S_IFSOCK 0140000
-#ifdef stat
-#undef stat
-#endif
-#define stat(path, buf) git_stat(path, buf)
-int git_stat(const char *, struct stat *);
-#ifdef fstat
-#undef fstat
-#endif
-#define fstat(fd, buf) git_fstat(fd, buf)
-int git_fstat(int, struct stat *);
-#ifdef lstat
-#undef lstat
-#endif
-#define lstat(path, buf) git_lstat(path, buf)
-int git_lstat(const char *, struct stat *);
-#endif
-
#define DEFAULT_PACKED_GIT_LIMIT \
((1024L * 1024L) * (size_t)(sizeof(void*) >= 8 ? (32 * 1024L * 1024L) : 256))
-#ifdef NO_PREAD
-#define pread git_pread
-ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
-#endif
-
-#ifdef NO_SETENV
-#define setenv gitsetenv
-int gitsetenv(const char *, const char *, int);
-#endif
-
-#ifdef NO_MKDTEMP
-#define mkdtemp gitmkdtemp
-char *gitmkdtemp(char *);
-#endif
-
-#ifdef NO_UNSETENV
-#define unsetenv gitunsetenv
-int gitunsetenv(const char *);
-#endif
-
-#ifdef NO_STRCASESTR
-#define strcasestr gitstrcasestr
-char *gitstrcasestr(const char *haystack, const char *needle);
-#endif
-
-#ifdef NO_STRLCPY
-#define strlcpy gitstrlcpy
-size_t gitstrlcpy(char *, const char *, size_t);
-#endif
-
-#ifdef NO_STRTOUMAX
-#define strtoumax gitstrtoumax
-uintmax_t gitstrtoumax(const char *, char **, int);
-#define strtoimax gitstrtoimax
-intmax_t gitstrtoimax(const char *, char **, int);
-#endif
-
-#ifdef NO_HSTRERROR
-#define hstrerror githstrerror
-const char *githstrerror(int herror);
-#endif
-
-#ifdef NO_MEMMEM
-#define memmem gitmemmem
-void *gitmemmem(const void *haystack, size_t haystacklen,
- const void *needle, size_t needlelen);
-#endif
-
-#ifdef OVERRIDE_STRDUP
-#ifdef strdup
-#undef strdup
-#endif
-#define strdup gitstrdup
-char *gitstrdup(const char *s);
-#endif
-
-#ifdef NO_GETPAGESIZE
-#define getpagesize() sysconf(_SC_PAGESIZE)
-#endif
-
-#ifndef O_CLOEXEC
-#define O_CLOEXEC 0
-#endif
-
-#ifdef FREAD_READS_DIRECTORIES
-# if !defined(SUPPRESS_FOPEN_REDEFINITION)
-# ifdef fopen
-# undef fopen
-# endif
-# define fopen(a,b) git_fopen(a,b)
-# endif
-FILE *git_fopen(const char*, const char*);
-#endif
-
-#ifdef SNPRINTF_RETURNS_BOGUS
-#ifdef snprintf
-#undef snprintf
-#endif
-#define snprintf git_snprintf
-int git_snprintf(char *str, size_t maxsize,
- const char *format, ...);
-#ifdef vsnprintf
-#undef vsnprintf
-#endif
-#define vsnprintf git_vsnprintf
-int git_vsnprintf(char *str, size_t maxsize,
- const char *format, va_list ap);
-#endif
-
-#ifdef OPEN_RETURNS_EINTR
-#undef open
-#define open git_open_with_retry
-int git_open_with_retry(const char *path, int flag, ...);
-#endif
-
-#ifdef __GLIBC_PREREQ
-#if __GLIBC_PREREQ(2, 1)
-#define HAVE_STRCHRNUL
-#endif
-#endif
-
-#ifndef HAVE_STRCHRNUL
-#define strchrnul gitstrchrnul
-static inline char *gitstrchrnul(const char *s, int c)
-{
- while (*s && *s != c)
- s++;
- return (char *)s;
-}
-#endif
-
-#ifdef NO_INET_PTON
-int inet_pton(int af, const char *src, void *dst);
-#endif
-
-#ifdef NO_INET_NTOP
-const char *inet_ntop(int af, const void *src, char *dst, size_t size);
-#endif
-
-#ifdef NO_PTHREADS
-#define atexit git_atexit
-int git_atexit(void (*handler)(void));
-#endif
+int git_open_cloexec(const char *name, int flags);
+#define git_open(name) git_open_cloexec(name, O_RDONLY)
static inline size_t st_add(size_t a, size_t b)
{
@@ -1302,12 +874,6 @@ static inline size_t xsize_t(off_t len)
return (size_t) len;
}
-#ifndef HOST_NAME_MAX
-#define HOST_NAME_MAX 256
-#endif
-
-#include "sane-ctype.h"
-
/*
* Like skip_prefix, but compare case-insensitively. Note that the comparison
* is done via tolower(), so it is strictly ASCII (no multi-byte characters or
@@ -1373,34 +939,6 @@ static inline int strtol_i(char const *s, int base, int *result)
return 0;
}
-void git_stable_qsort(void *base, size_t nmemb, size_t size,
- int(*compar)(const void *, const void *));
-#ifdef INTERNAL_QSORT
-#define qsort git_stable_qsort
-#endif
-
-#define QSORT(base, n, compar) sane_qsort((base), (n), sizeof(*(base)), compar)
-static inline void sane_qsort(void *base, size_t nmemb, size_t size,
- int(*compar)(const void *, const void *))
-{
- if (nmemb > 1)
- qsort(base, nmemb, size, compar);
-}
-
-#define STABLE_QSORT(base, n, compar) \
- git_stable_qsort((base), (n), sizeof(*(base)), compar)
-
-#ifndef HAVE_ISO_QSORT_S
-int git_qsort_s(void *base, size_t nmemb, size_t size,
- int (*compar)(const void *, const void *, void *), void *ctx);
-#define qsort_s git_qsort_s
-#endif
-
-#define QSORT_S(base, n, compar, ctx) do { \
- if (qsort_s((base), (n), sizeof(*(base)), compar, ctx)) \
- BUG("qsort_s() failed"); \
-} while (0)
-
#ifndef REG_STARTEND
#error "Git requires REG_STARTEND support. Compile with NO_REGEX=NeedsStartEnd"
#endif
@@ -1425,39 +963,12 @@ int git_regcomp(regex_t *preg, const char *pattern, int cflags);
# define FORCE_DIR_SET_GID 0
#endif
-#ifdef NO_NSEC
-#undef USE_NSEC
-#define ST_CTIME_NSEC(st) 0
-#define ST_MTIME_NSEC(st) 0
-#else
-#ifdef USE_ST_TIMESPEC
-#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctimespec.tv_nsec))
-#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtimespec.tv_nsec))
-#else
-#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctim.tv_nsec))
-#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtim.tv_nsec))
-#endif
-#endif
-
#ifdef UNRELIABLE_FSTAT
#define fstat_is_reliable() 0
#else
#define fstat_is_reliable() 1
#endif
-#ifndef va_copy
-/*
- * Since an obvious implementation of va_list would be to make it a
- * pointer into the stack frame, a simple assignment will work on
- * many systems. But let's try to be more portable.
- */
-#ifdef __va_copy
-#define va_copy(dst, src) __va_copy(dst, src)
-#else
-#define va_copy(dst, src) ((dst) = (src))
-#endif
-#endif
-
/* usage.c: only to be used for testing BUG() implementation (see test-tool) */
extern int BUG_exit_code;
@@ -1467,6 +978,8 @@ extern int bug_called_must_BUG;
__attribute__((format (printf, 3, 4))) NORETURN
void BUG_fl(const char *file, int line, const char *fmt, ...);
#define BUG(...) BUG_fl(__FILE__, __LINE__, __VA_ARGS__)
+/* ASSERT: like assert(), but won't be compiled out with NDEBUG */
+#define ASSERT(a) if (!(a)) BUG("Assertion `" #a "' failed.")
__attribute__((format (printf, 3, 4)))
void bug_fl(const char *file, int line, const char *fmt, ...);
#define bug(...) bug_fl(__FILE__, __LINE__, __VA_ARGS__)
@@ -1487,41 +1000,6 @@ void bug_fl(const char *file, int line, const char *fmt, ...);
# define SHELL_PATH "/bin/sh"
#endif
-#ifndef _POSIX_THREAD_SAFE_FUNCTIONS
-static inline void git_flockfile(FILE *fh UNUSED)
-{
- ; /* nothing */
-}
-static inline void git_funlockfile(FILE *fh UNUSED)
-{
- ; /* nothing */
-}
-#undef flockfile
-#undef funlockfile
-#undef getc_unlocked
-#define flockfile(fh) git_flockfile(fh)
-#define funlockfile(fh) git_funlockfile(fh)
-#define getc_unlocked(fh) getc(fh)
-#endif
-
-#ifdef FILENO_IS_A_MACRO
-int git_fileno(FILE *stream);
-# ifndef COMPAT_CODE_FILENO
-# undef fileno
-# define fileno(p) git_fileno(p)
-# endif
-#endif
-
-#ifdef NEED_ACCESS_ROOT_HANDLER
-int git_access(const char *path, int mode);
-# ifndef COMPAT_CODE_ACCESS
-# ifdef access
-# undef access
-# endif
-# define access(path, mode) git_access(path, mode)
-# endif
-#endif
-
/*
* Our code often opens a path to an optional file, to work on its
* contents when we can successfully open it. We can ignore a failure
@@ -1599,4 +1077,11 @@ static inline void *container_of_or_null_offset(void *ptr, size_t offset)
*/
#define NOT_CONSTANT(expr) ((expr) || false_but_the_compiler_does_not_know_it_)
extern int false_but_the_compiler_does_not_know_it_;
+
+#ifdef CHECK_ASSERTION_SIDE_EFFECTS
+#undef assert
+extern int not_supposed_to_survive;
+#define assert(expr) ((void)(not_supposed_to_survive || (expr)))
+#endif /* CHECK_ASSERTION_SIDE_EFFECTS */
+
#endif
diff --git a/git-curl-compat.h b/git-curl-compat.h
index 703756ba85..aa8eed7ed2 100644
--- a/git-curl-compat.h
+++ b/git-curl-compat.h
@@ -45,4 +45,11 @@
#define GIT_CURL_HAVE_CURLOPT_PROTOCOLS_STR 1
#endif
+/**
+ * CURLOPT_TCP_KEEPCNT was added in 8.9.0, released in July, 2024.
+ */
+#if LIBCURL_VERSION_NUM >= 0x080900
+#define GIT_CURL_HAVE_CURLOPT_TCP_KEEPCNT
+#endif
+
#endif
diff --git a/git-send-email.perl b/git-send-email.perl
index 798d59b84f..1f613fa979 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -1419,7 +1419,7 @@ sub smtp_auth_maybe {
die "invalid smtp auth: '${smtp_auth}'";
}
- # TODO: Authentication may fail not because credentials were
+ # Authentication may fail not because credentials were
# invalid but due to other reasons, in which we should not
# reject credentials.
$auth = Git::credential({
@@ -1431,24 +1431,61 @@ sub smtp_auth_maybe {
'password' => $smtp_authpass
}, sub {
my $cred = shift;
+ my $result;
+ my $error;
+
+ # catch all SMTP auth error in a unified eval block
+ eval {
+ if ($smtp_auth) {
+ my $sasl = Authen::SASL->new(
+ mechanism => $smtp_auth,
+ callback => {
+ user => $cred->{'username'},
+ pass => $cred->{'password'},
+ authname => $cred->{'username'},
+ }
+ );
+ $result = $smtp->auth($sasl);
+ } else {
+ $result = $smtp->auth($cred->{'username'}, $cred->{'password'});
+ }
+ 1; # ensure true value is returned if no exception is thrown
+ } or do {
+ $error = $@ || 'Unknown error';
+ };
+
+ return ($error
+ ? handle_smtp_error($error)
+ : ($result ? 1 : 0));
+ });
- if ($smtp_auth) {
- my $sasl = Authen::SASL->new(
- mechanism => $smtp_auth,
- callback => {
- user => $cred->{'username'},
- pass => $cred->{'password'},
- authname => $cred->{'username'},
- }
- );
+ return $auth;
+}
- return !!$smtp->auth($sasl);
+sub handle_smtp_error {
+ my ($error) = @_;
+
+ # Parse SMTP status code from error message in:
+ # https://www.rfc-editor.org/rfc/rfc5321.html
+ if ($error =~ /\b(\d{3})\b/) {
+ my $status_code = $1;
+ if ($status_code =~ /^4/) {
+ # 4yz: Transient Negative Completion reply
+ warn "SMTP transient error (status code $status_code): $error";
+ return 1;
+ } elsif ($status_code =~ /^5/) {
+ # 5yz: Permanent Negative Completion reply
+ warn "SMTP permanent error (status code $status_code): $error";
+ return 0;
}
+ # If no recognized status code is found, treat as transient error
+ warn "SMTP unknown error: $error. Treating as transient failure.";
+ return 1;
+ }
- return !!$smtp->auth($cred->{'username'}, $cred->{'password'});
- });
-
- return $auth;
+ # If no status code is found, treat as transient error
+ warn "SMTP generic error: $error";
+ return 1;
}
sub ssl_verify_params {
diff --git a/git-zlib.c b/git-zlib.c
index 651dd9e07c..df9604910e 100644
--- a/git-zlib.c
+++ b/git-zlib.c
@@ -45,7 +45,7 @@ static void zlib_pre_call(git_zstream *s)
s->z.avail_out = zlib_buf_cap(s->avail_out);
}
-static void zlib_post_call(git_zstream *s)
+static void zlib_post_call(git_zstream *s, int status)
{
unsigned long bytes_consumed;
unsigned long bytes_produced;
@@ -54,7 +54,12 @@ static void zlib_post_call(git_zstream *s)
bytes_produced = s->z.next_out - s->next_out;
if (s->z.total_out != s->total_out + bytes_produced)
BUG("total_out mismatch");
- if (s->z.total_in != s->total_in + bytes_consumed)
+ /*
+ * zlib does not update total_in when it returns Z_NEED_DICT,
+ * causing a mismatch here. Skip the sanity check in that case.
+ */
+ if (status != Z_NEED_DICT &&
+ s->z.total_in != s->total_in + bytes_consumed)
BUG("total_in mismatch");
s->total_out = s->z.total_out;
@@ -72,7 +77,7 @@ void git_inflate_init(git_zstream *strm)
zlib_pre_call(strm);
status = inflateInit(&strm->z);
- zlib_post_call(strm);
+ zlib_post_call(strm, status);
if (status == Z_OK)
return;
die("inflateInit: %s (%s)", zerr_to_string(status),
@@ -90,7 +95,7 @@ void git_inflate_init_gzip_only(git_zstream *strm)
zlib_pre_call(strm);
status = inflateInit2(&strm->z, windowBits);
- zlib_post_call(strm);
+ zlib_post_call(strm, status);
if (status == Z_OK)
return;
die("inflateInit2: %s (%s)", zerr_to_string(status),
@@ -103,7 +108,7 @@ void git_inflate_end(git_zstream *strm)
zlib_pre_call(strm);
status = inflateEnd(&strm->z);
- zlib_post_call(strm);
+ zlib_post_call(strm, status);
if (status == Z_OK)
return;
error("inflateEnd: %s (%s)", zerr_to_string(status),
@@ -122,7 +127,7 @@ int git_inflate(git_zstream *strm, int flush)
? 0 : flush);
if (status == Z_MEM_ERROR)
die("inflate: out of memory");
- zlib_post_call(strm);
+ zlib_post_call(strm, status);
/*
* Let zlib work another round, while we can still
@@ -160,7 +165,7 @@ void git_deflate_init(git_zstream *strm, int level)
memset(strm, 0, sizeof(*strm));
zlib_pre_call(strm);
status = deflateInit(&strm->z, level);
- zlib_post_call(strm);
+ zlib_post_call(strm, status);
if (status == Z_OK)
return;
die("deflateInit: %s (%s)", zerr_to_string(status),
@@ -176,7 +181,7 @@ static void do_git_deflate_init(git_zstream *strm, int level, int windowBits)
status = deflateInit2(&strm->z, level,
Z_DEFLATED, windowBits,
8, Z_DEFAULT_STRATEGY);
- zlib_post_call(strm);
+ zlib_post_call(strm, status);
if (status == Z_OK)
return;
die("deflateInit2: %s (%s)", zerr_to_string(status),
@@ -207,7 +212,7 @@ int git_deflate_abort(git_zstream *strm)
zlib_pre_call(strm);
status = deflateEnd(&strm->z);
- zlib_post_call(strm);
+ zlib_post_call(strm, status);
return status;
}
@@ -227,7 +232,7 @@ int git_deflate_end_gently(git_zstream *strm)
zlib_pre_call(strm);
status = deflateEnd(&strm->z);
- zlib_post_call(strm);
+ zlib_post_call(strm, status);
return status;
}
@@ -244,7 +249,7 @@ int git_deflate(git_zstream *strm, int flush)
? 0 : flush);
if (status == Z_MEM_ERROR)
die("deflate: out of memory");
- zlib_post_call(strm);
+ zlib_post_call(strm, status);
/*
* Let zlib work another round, while we can still
diff --git a/gitweb/Makefile b/gitweb/Makefile
index d5748e9359..26a683d442 100644
--- a/gitweb/Makefile
+++ b/gitweb/Makefile
@@ -118,7 +118,7 @@ $(MAK_DIR_GITWEB)gitweb.cgi: $(MAK_DIR_GITWEB)gitweb.perl
$(MAK_DIR_GITWEB)static/gitweb.js: $(MAK_DIR_GITWEB)generate-gitweb-js.sh
$(MAK_DIR_GITWEB)static/gitweb.js: $(addprefix $(MAK_DIR_GITWEB),$(GITWEB_JSLIB_FILES))
$(QUIET_GEN)$(RM) $@ $@+ && \
- $(MAK_DIR_GITWEB)generate-gitweb-js.sh $@+ $^ && \
+ $(MAK_DIR_GITWEB)generate-gitweb-js.sh $@+ $(filter %.js,$^) && \
mv $@+ $@
### Installation rules
diff --git a/gitweb/meson.build b/gitweb/meson.build
index 89b403dc9d..88a54b4dc9 100644
--- a/gitweb/meson.build
+++ b/gitweb/meson.build
@@ -1,5 +1,5 @@
gitweb_config = configuration_data()
-gitweb_config.set_quoted('PERL_PATH', perl.full_path())
+gitweb_config.set_quoted('PERL_PATH', target_perl.full_path())
gitweb_config.set_quoted('CSSMIN', '')
gitweb_config.set_quoted('JSMIN', '')
gitweb_config.set_quoted('GIT_BINDIR', get_option('prefix') / get_option('bindir'))
diff --git a/grep.c b/grep.c
index 4e155ee9e6..f8d535182c 100644
--- a/grep.c
+++ b/grep.c
@@ -5,7 +5,7 @@
#include "gettext.h"
#include "grep.h"
#include "hex.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "pretty.h"
#include "userdiff.h"
#include "xdiff-interface.h"
@@ -1517,7 +1517,7 @@ static int fill_textconv_grep(struct repository *r,
fill_filespec(df, gs->identifier, 1, 0100644);
break;
case GREP_SOURCE_FILE:
- fill_filespec(df, null_oid(), 0, 0100644);
+ fill_filespec(df, null_oid(r->hash_algo), 0, 0100644);
break;
default:
BUG("attempt to textconv something without a path?");
diff --git a/hash.c b/hash.c
new file mode 100644
index 0000000000..4a04ecb50e
--- /dev/null
+++ b/hash.c
@@ -0,0 +1,277 @@
+#include "git-compat-util.h"
+#include "hash.h"
+#include "hex.h"
+
+static const struct object_id empty_tree_oid = {
+ .hash = {
+ 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60,
+ 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04
+ },
+ .algo = GIT_HASH_SHA1,
+};
+static const struct object_id empty_blob_oid = {
+ .hash = {
+ 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1, 0xd6, 0x43, 0x4b, 0x8b,
+ 0x29, 0xae, 0x77, 0x5a, 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91
+ },
+ .algo = GIT_HASH_SHA1,
+};
+static const struct object_id null_oid_sha1 = {
+ .hash = {0},
+ .algo = GIT_HASH_SHA1,
+};
+static const struct object_id empty_tree_oid_sha256 = {
+ .hash = {
+ 0x6e, 0xf1, 0x9b, 0x41, 0x22, 0x5c, 0x53, 0x69, 0xf1, 0xc1,
+ 0x04, 0xd4, 0x5d, 0x8d, 0x85, 0xef, 0xa9, 0xb0, 0x57, 0xb5,
+ 0x3b, 0x14, 0xb4, 0xb9, 0xb9, 0x39, 0xdd, 0x74, 0xde, 0xcc,
+ 0x53, 0x21
+ },
+ .algo = GIT_HASH_SHA256,
+};
+static const struct object_id empty_blob_oid_sha256 = {
+ .hash = {
+ 0x47, 0x3a, 0x0f, 0x4c, 0x3b, 0xe8, 0xa9, 0x36, 0x81, 0xa2,
+ 0x67, 0xe3, 0xb1, 0xe9, 0xa7, 0xdc, 0xda, 0x11, 0x85, 0x43,
+ 0x6f, 0xe1, 0x41, 0xf7, 0x74, 0x91, 0x20, 0xa3, 0x03, 0x72,
+ 0x18, 0x13
+ },
+ .algo = GIT_HASH_SHA256,
+};
+static const struct object_id null_oid_sha256 = {
+ .hash = {0},
+ .algo = GIT_HASH_SHA256,
+};
+
+static void git_hash_sha1_init(struct git_hash_ctx *ctx)
+{
+ ctx->algop = &hash_algos[GIT_HASH_SHA1];
+ git_SHA1_Init(&ctx->state.sha1);
+}
+
+static void git_hash_sha1_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src)
+{
+ dst->algop = src->algop;
+ git_SHA1_Clone(&dst->state.sha1, &src->state.sha1);
+}
+
+static void git_hash_sha1_update(struct git_hash_ctx *ctx, const void *data, size_t len)
+{
+ git_SHA1_Update(&ctx->state.sha1, data, len);
+}
+
+static void git_hash_sha1_final(unsigned char *hash, struct git_hash_ctx *ctx)
+{
+ git_SHA1_Final(hash, &ctx->state.sha1);
+}
+
+static void git_hash_sha1_final_oid(struct object_id *oid, struct git_hash_ctx *ctx)
+{
+ git_SHA1_Final(oid->hash, &ctx->state.sha1);
+ memset(oid->hash + GIT_SHA1_RAWSZ, 0, GIT_MAX_RAWSZ - GIT_SHA1_RAWSZ);
+ oid->algo = GIT_HASH_SHA1;
+}
+
+static void git_hash_sha1_init_unsafe(struct git_hash_ctx *ctx)
+{
+ ctx->algop = unsafe_hash_algo(&hash_algos[GIT_HASH_SHA1]);
+ git_SHA1_Init_unsafe(&ctx->state.sha1_unsafe);
+}
+
+static void git_hash_sha1_clone_unsafe(struct git_hash_ctx *dst, const struct git_hash_ctx *src)
+{
+ dst->algop = src->algop;
+ git_SHA1_Clone_unsafe(&dst->state.sha1_unsafe, &src->state.sha1_unsafe);
+}
+
+static void git_hash_sha1_update_unsafe(struct git_hash_ctx *ctx, const void *data,
+ size_t len)
+{
+ git_SHA1_Update_unsafe(&ctx->state.sha1_unsafe, data, len);
+}
+
+static void git_hash_sha1_final_unsafe(unsigned char *hash, struct git_hash_ctx *ctx)
+{
+ git_SHA1_Final_unsafe(hash, &ctx->state.sha1_unsafe);
+}
+
+static void git_hash_sha1_final_oid_unsafe(struct object_id *oid, struct git_hash_ctx *ctx)
+{
+ git_SHA1_Final_unsafe(oid->hash, &ctx->state.sha1_unsafe);
+ memset(oid->hash + GIT_SHA1_RAWSZ, 0, GIT_MAX_RAWSZ - GIT_SHA1_RAWSZ);
+ oid->algo = GIT_HASH_SHA1;
+}
+
+static void git_hash_sha256_init(struct git_hash_ctx *ctx)
+{
+ ctx->algop = unsafe_hash_algo(&hash_algos[GIT_HASH_SHA256]);
+ git_SHA256_Init(&ctx->state.sha256);
+}
+
+static void git_hash_sha256_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src)
+{
+ dst->algop = src->algop;
+ git_SHA256_Clone(&dst->state.sha256, &src->state.sha256);
+}
+
+static void git_hash_sha256_update(struct git_hash_ctx *ctx, const void *data, size_t len)
+{
+ git_SHA256_Update(&ctx->state.sha256, data, len);
+}
+
+static void git_hash_sha256_final(unsigned char *hash, struct git_hash_ctx *ctx)
+{
+ git_SHA256_Final(hash, &ctx->state.sha256);
+}
+
+static void git_hash_sha256_final_oid(struct object_id *oid, struct git_hash_ctx *ctx)
+{
+ git_SHA256_Final(oid->hash, &ctx->state.sha256);
+ /*
+ * This currently does nothing, so the compiler should optimize it out,
+ * but keep it in case we extend the hash size again.
+ */
+ memset(oid->hash + GIT_SHA256_RAWSZ, 0, GIT_MAX_RAWSZ - GIT_SHA256_RAWSZ);
+ oid->algo = GIT_HASH_SHA256;
+}
+
+static void git_hash_unknown_init(struct git_hash_ctx *ctx UNUSED)
+{
+ BUG("trying to init unknown hash");
+}
+
+static void git_hash_unknown_clone(struct git_hash_ctx *dst UNUSED,
+ const struct git_hash_ctx *src UNUSED)
+{
+ BUG("trying to clone unknown hash");
+}
+
+static void git_hash_unknown_update(struct git_hash_ctx *ctx UNUSED,
+ const void *data UNUSED,
+ size_t len UNUSED)
+{
+ BUG("trying to update unknown hash");
+}
+
+static void git_hash_unknown_final(unsigned char *hash UNUSED,
+ struct git_hash_ctx *ctx UNUSED)
+{
+ BUG("trying to finalize unknown hash");
+}
+
+static void git_hash_unknown_final_oid(struct object_id *oid UNUSED,
+ struct git_hash_ctx *ctx UNUSED)
+{
+ BUG("trying to finalize unknown hash");
+}
+
+static const struct git_hash_algo sha1_unsafe_algo = {
+ .name = "sha1",
+ .format_id = GIT_SHA1_FORMAT_ID,
+ .rawsz = GIT_SHA1_RAWSZ,
+ .hexsz = GIT_SHA1_HEXSZ,
+ .blksz = GIT_SHA1_BLKSZ,
+ .init_fn = git_hash_sha1_init_unsafe,
+ .clone_fn = git_hash_sha1_clone_unsafe,
+ .update_fn = git_hash_sha1_update_unsafe,
+ .final_fn = git_hash_sha1_final_unsafe,
+ .final_oid_fn = git_hash_sha1_final_oid_unsafe,
+ .empty_tree = &empty_tree_oid,
+ .empty_blob = &empty_blob_oid,
+ .null_oid = &null_oid_sha1,
+};
+
+const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
+ {
+ .name = NULL,
+ .format_id = 0x00000000,
+ .rawsz = 0,
+ .hexsz = 0,
+ .blksz = 0,
+ .init_fn = git_hash_unknown_init,
+ .clone_fn = git_hash_unknown_clone,
+ .update_fn = git_hash_unknown_update,
+ .final_fn = git_hash_unknown_final,
+ .final_oid_fn = git_hash_unknown_final_oid,
+ .empty_tree = NULL,
+ .empty_blob = NULL,
+ .null_oid = NULL,
+ },
+ {
+ .name = "sha1",
+ .format_id = GIT_SHA1_FORMAT_ID,
+ .rawsz = GIT_SHA1_RAWSZ,
+ .hexsz = GIT_SHA1_HEXSZ,
+ .blksz = GIT_SHA1_BLKSZ,
+ .init_fn = git_hash_sha1_init,
+ .clone_fn = git_hash_sha1_clone,
+ .update_fn = git_hash_sha1_update,
+ .final_fn = git_hash_sha1_final,
+ .final_oid_fn = git_hash_sha1_final_oid,
+ .unsafe = &sha1_unsafe_algo,
+ .empty_tree = &empty_tree_oid,
+ .empty_blob = &empty_blob_oid,
+ .null_oid = &null_oid_sha1,
+ },
+ {
+ .name = "sha256",
+ .format_id = GIT_SHA256_FORMAT_ID,
+ .rawsz = GIT_SHA256_RAWSZ,
+ .hexsz = GIT_SHA256_HEXSZ,
+ .blksz = GIT_SHA256_BLKSZ,
+ .init_fn = git_hash_sha256_init,
+ .clone_fn = git_hash_sha256_clone,
+ .update_fn = git_hash_sha256_update,
+ .final_fn = git_hash_sha256_final,
+ .final_oid_fn = git_hash_sha256_final_oid,
+ .empty_tree = &empty_tree_oid_sha256,
+ .empty_blob = &empty_blob_oid_sha256,
+ .null_oid = &null_oid_sha256,
+ }
+};
+
+const struct object_id *null_oid(const struct git_hash_algo *algop)
+{
+ return algop->null_oid;
+}
+
+const char *empty_tree_oid_hex(const struct git_hash_algo *algop)
+{
+ static char buf[GIT_MAX_HEXSZ + 1];
+ return oid_to_hex_r(buf, algop->empty_tree);
+}
+
+int hash_algo_by_name(const char *name)
+{
+ if (!name)
+ return GIT_HASH_UNKNOWN;
+ for (size_t i = 1; i < GIT_HASH_NALGOS; i++)
+ if (!strcmp(name, hash_algos[i].name))
+ return i;
+ return GIT_HASH_UNKNOWN;
+}
+
+int hash_algo_by_id(uint32_t format_id)
+{
+ for (size_t i = 1; i < GIT_HASH_NALGOS; i++)
+ if (format_id == hash_algos[i].format_id)
+ return i;
+ return GIT_HASH_UNKNOWN;
+}
+
+int hash_algo_by_length(size_t len)
+{
+ for (size_t i = 1; i < GIT_HASH_NALGOS; i++)
+ if (len == hash_algos[i].rawsz)
+ return i;
+ return GIT_HASH_UNKNOWN;
+}
+
+const struct git_hash_algo *unsafe_hash_algo(const struct git_hash_algo *algop)
+{
+ /* If we have a faster "unsafe" implementation, use that. */
+ if (algop->unsafe)
+ return algop->unsafe;
+ /* Otherwise use the default one. */
+ return algop;
+}
diff --git a/hash.h b/hash.h
index 5e3c462dc5..d6422ddf45 100644
--- a/hash.h
+++ b/hash.h
@@ -2,26 +2,32 @@
#define HASH_H
#if defined(SHA1_APPLE)
+#define SHA1_BACKEND "SHA1_APPLE (No collision detection)"
#include <CommonCrypto/CommonDigest.h>
#elif defined(SHA1_OPENSSL)
+# define SHA1_BACKEND "SHA1_OPENSSL (No collision detection)"
# include <openssl/sha.h>
# if defined(OPENSSL_API_LEVEL) && OPENSSL_API_LEVEL >= 3
# define SHA1_NEEDS_CLONE_HELPER
# include "sha1/openssl.h"
# endif
#elif defined(SHA1_DC)
+#define SHA1_BACKEND "SHA1_DC"
#include "sha1dc_git.h"
#else /* SHA1_BLK */
+#define SHA1_BACKEND "SHA1_BLK (No collision detection)"
#include "block-sha1/sha1.h"
#endif
#if defined(SHA1_APPLE_UNSAFE)
+# define SHA1_UNSAFE_BACKEND "SHA1_APPLE_UNSAFE"
# include <CommonCrypto/CommonDigest.h>
# define platform_SHA_CTX_unsafe CC_SHA1_CTX
# define platform_SHA1_Init_unsafe CC_SHA1_Init
# define platform_SHA1_Update_unsafe CC_SHA1_Update
# define platform_SHA1_Final_unsafe CC_SHA1_Final
#elif defined(SHA1_OPENSSL_UNSAFE)
+# define SHA1_UNSAFE_BACKEND "SHA1_OPENSSL_UNSAFE"
# include <openssl/sha.h>
# if defined(OPENSSL_API_LEVEL) && OPENSSL_API_LEVEL >= 3
# define SHA1_NEEDS_CLONE_HELPER_UNSAFE
@@ -38,6 +44,7 @@
# define platform_SHA1_Final_unsafe SHA1_Final
# endif
#elif defined(SHA1_BLK_UNSAFE)
+# define SHA1_UNSAFE_BACKEND "SHA1_BLK_UNSAFE"
# include "block-sha1/sha1.h"
# define platform_SHA_CTX_unsafe blk_SHA_CTX
# define platform_SHA1_Init_unsafe blk_SHA1_Init
@@ -46,17 +53,21 @@
#endif
#if defined(SHA256_NETTLE)
+#define SHA256_BACKEND "SHA256_NETTLE"
#include "sha256/nettle.h"
#elif defined(SHA256_GCRYPT)
+#define SHA256_BACKEND "SHA256_GCRYPT"
#define SHA256_NEEDS_CLONE_HELPER
#include "sha256/gcrypt.h"
#elif defined(SHA256_OPENSSL)
+# define SHA256_BACKEND "SHA256_OPENSSL"
# include <openssl/sha.h>
# if defined(OPENSSL_API_LEVEL) && OPENSSL_API_LEVEL >= 3
# define SHA256_NEEDS_CLONE_HELPER
# include "sha256/openssl.h"
# endif
#else
+#define SHA256_BACKEND "SHA256_BLK"
#include "sha256/block/sha256.h"
#endif
@@ -326,7 +337,7 @@ int hash_algo_by_name(const char *name);
/* Identical, except based on the format ID. */
int hash_algo_by_id(uint32_t format_id);
/* Identical, except based on the length. */
-int hash_algo_by_length(int len);
+int hash_algo_by_length(size_t len);
/* Identical, except for a pointer to struct git_hash_algo. */
static inline int hash_algo_by_ptr(const struct git_hash_algo *p)
{
@@ -341,7 +352,7 @@ static inline int hash_algo_by_ptr(const struct git_hash_algo *p)
const struct git_hash_algo *unsafe_hash_algo(const struct git_hash_algo *algop);
-const struct object_id *null_oid(void);
+const struct object_id *null_oid(const struct git_hash_algo *algop);
static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2, const struct git_hash_algo *algop)
{
diff --git a/help.c b/help.c
index c54bd9918a..6ef90838f1 100644
--- a/help.c
+++ b/help.c
@@ -9,6 +9,7 @@
#include "run-command.h"
#include "levenshtein.h"
#include "gettext.h"
+#include "hash.h"
#include "help.h"
#include "command-list.h"
#include "string-list.h"
@@ -803,6 +804,12 @@ void get_version_info(struct strbuf *buf, int show_build_options)
#elif defined ZLIB_VERSION
strbuf_addf(buf, "zlib: %s\n", ZLIB_VERSION);
#endif
+ strbuf_addf(buf, "SHA-1: %s\n", SHA1_BACKEND);
+#if defined SHA1_UNSAFE_BACKEND
+ strbuf_addf(buf, "non-collision-detecting-SHA-1: %s\n",
+ SHA1_UNSAFE_BACKEND);
+#endif
+ strbuf_addf(buf, "SHA-256: %s\n", SHA256_BACKEND);
}
}
diff --git a/http-backend.c b/http-backend.c
index 50b2858fad..0c575aa88a 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -18,7 +18,7 @@
#include "url.h"
#include "strvec.h"
#include "packfile.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "protocol.h"
#include "date.h"
#include "write-or-die.h"
diff --git a/http-push.c b/http-push.c
index 1b030d96f4..32e37565f4 100644
--- a/http-push.c
+++ b/http-push.c
@@ -19,7 +19,8 @@
#include "tree-walk.h"
#include "url.h"
#include "packfile.h"
-#include "object-store-ll.h"
+#include "object-file.h"
+#include "object-store.h"
#include "commit-reach.h"
#ifdef EXPAT_NEEDS_XMLPARSE_H
diff --git a/http-walker.c b/http-walker.c
index 7918ddc096..882cae19c2 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -9,7 +9,7 @@
#include "list.h"
#include "transport.h"
#include "packfile.h"
-#include "object-store-ll.h"
+#include "object-store.h"
struct alt_base {
char *base;
diff --git a/http.c b/http.c
index 0c9a872809..0c41138042 100644
--- a/http.c
+++ b/http.c
@@ -19,7 +19,7 @@
#include "packfile.h"
#include "string-list.h"
#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "tempfile.h"
static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);
@@ -104,6 +104,10 @@ static struct {
};
#endif
+static long curl_tcp_keepidle = -1;
+static long curl_tcp_keepintvl = -1;
+static long curl_tcp_keepcnt = -1;
+
enum proactive_auth {
PROACTIVE_AUTH_NONE = 0,
PROACTIVE_AUTH_IF_CREDENTIALS,
@@ -438,11 +442,11 @@ static int http_options(const char *var, const char *value,
return 0;
}
if (!strcmp("http.lowspeedlimit", var)) {
- curl_low_speed_limit = (long)git_config_int(var, value, ctx->kvi);
+ curl_low_speed_limit = git_config_int(var, value, ctx->kvi);
return 0;
}
if (!strcmp("http.lowspeedtime", var)) {
- curl_low_speed_time = (long)git_config_int(var, value, ctx->kvi);
+ curl_low_speed_time = git_config_int(var, value, ctx->kvi);
return 0;
}
@@ -557,6 +561,19 @@ static int http_options(const char *var, const char *value,
return 0;
}
+ if (!strcmp("http.keepaliveidle", var)) {
+ curl_tcp_keepidle = git_config_int(var, value, ctx->kvi);
+ return 0;
+ }
+ if (!strcmp("http.keepaliveinterval", var)) {
+ curl_tcp_keepintvl = git_config_int(var, value, ctx->kvi);
+ return 0;
+ }
+ if (!strcmp("http.keepalivecount", var)) {
+ curl_tcp_keepcnt = git_config_int(var, value, ctx->kvi);
+ return 0;
+ }
+
/* Fall back on the default ones */
return git_default_config(var, value, ctx, data);
}
@@ -704,11 +721,6 @@ static int has_proxy_cert_password(void)
return 1;
}
-static void set_curl_keepalive(CURL *c)
-{
- curl_easy_setopt(c, CURLOPT_TCP_KEEPALIVE, 1);
-}
-
/* Return 1 if redactions have been made, 0 otherwise. */
static int redact_sensitive_header(struct strbuf *header, size_t offset)
{
@@ -1242,7 +1254,18 @@ static CURL *get_curl_handle(void)
}
init_curl_proxy_auth(result);
- set_curl_keepalive(result);
+ curl_easy_setopt(result, CURLOPT_TCP_KEEPALIVE, 1);
+
+ if (curl_tcp_keepidle > -1)
+ curl_easy_setopt(result, CURLOPT_TCP_KEEPIDLE,
+ curl_tcp_keepidle);
+ if (curl_tcp_keepintvl > -1)
+ curl_easy_setopt(result, CURLOPT_TCP_KEEPINTVL,
+ curl_tcp_keepintvl);
+#ifdef GIT_CURL_HAVE_CURLOPT_TCP_KEEPCNT
+ if (curl_tcp_keepcnt > -1)
+ curl_easy_setopt(result, CURLOPT_TCP_KEEPCNT, curl_tcp_keepcnt);
+#endif
return result;
}
@@ -1256,10 +1279,30 @@ static void set_from_env(char **var, const char *envname)
}
}
+static void set_long_from_env(long *var, const char *envname)
+{
+ const char *val = getenv(envname);
+ if (val) {
+ long tmp;
+ char *endp;
+ int saved_errno = errno;
+
+ errno = 0;
+ tmp = strtol(val, &endp, 10);
+
+ if (errno)
+ warning_errno(_("failed to parse %s"), envname);
+ else if (*endp || endp == val)
+ warning(_("failed to parse %s"), envname);
+ else
+ *var = tmp;
+
+ errno = saved_errno;
+ }
+}
+
void http_init(struct remote *remote, const char *url, int proactive_auth)
{
- char *low_speed_limit;
- char *low_speed_time;
char *normalized_url;
struct urlmatch_config config = URLMATCH_CONFIG_INIT;
@@ -1338,12 +1381,8 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
set_from_env(&user_agent, "GIT_HTTP_USER_AGENT");
- low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT");
- if (low_speed_limit)
- curl_low_speed_limit = strtol(low_speed_limit, NULL, 10);
- low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME");
- if (low_speed_time)
- curl_low_speed_time = strtol(low_speed_time, NULL, 10);
+ set_long_from_env(&curl_low_speed_limit, "GIT_HTTP_LOW_SPEED_LIMIT");
+ set_long_from_env(&curl_low_speed_time, "GIT_HTTP_LOW_SPEED_TIME");
if (curl_ssl_verify == -1)
curl_ssl_verify = 1;
@@ -1370,6 +1409,10 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
ssl_cert_password_required = 1;
}
+ set_long_from_env(&curl_tcp_keepidle, "GIT_TCP_KEEPIDLE");
+ set_long_from_env(&curl_tcp_keepintvl, "GIT_TCP_KEEPINTVL");
+ set_long_from_env(&curl_tcp_keepcnt, "GIT_TCP_KEEPCNT");
+
curl_default = get_curl_handle();
}
diff --git a/imap-send.c b/imap-send.c
index 6c8f84e836..27dc033c7f 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -324,6 +324,8 @@ static int ssl_socket_connect(struct imap_socket *sock,
cert = SSL_get_peer_certificate(sock->ssl);
if (!cert)
return error("unable to get peer certificate.");
+ if (SSL_get_verify_result(sock->ssl) != X509_V_OK)
+ return error("unable to verify peer certificate");
if (verify_hostname(cert, cfg->host) < 0)
return -1;
}
diff --git a/kwset.c b/kwset.c
index 1714eada60..064329434e 100644
--- a/kwset.c
+++ b/kwset.c
@@ -197,10 +197,13 @@ kwsincr (kwset_t kws, char const *text, size_t len)
while (link && label != link->label)
{
links[depth] = link;
- if (label < link->label)
- dirs[depth++] = L, link = link->llink;
- else
- dirs[depth++] = R, link = link->rlink;
+ if (label < link->label) {
+ dirs[depth++] = L;
+ link = link->llink;
+ } else {
+ dirs[depth++] = R;
+ link = link->rlink;
+ }
}
/* The current character doesn't have an outgoing link at
@@ -257,14 +260,14 @@ kwsincr (kwset_t kws, char const *text, size_t len)
switch (dirs[depth + 1])
{
case L:
- r = links[depth], t = r->llink, rl = t->rlink;
- t->rlink = r, r->llink = rl;
+ r = links[depth]; t = r->llink; rl = t->rlink;
+ t->rlink = r; r->llink = rl;
t->balance = r->balance = 0;
break;
case R:
- r = links[depth], l = r->llink, t = l->rlink;
- rl = t->rlink, lr = t->llink;
- t->llink = l, l->rlink = lr, t->rlink = r, r->llink = rl;
+ r = links[depth]; l = r->llink; t = l->rlink;
+ rl = t->rlink; lr = t->llink;
+ t->llink = l; l->rlink = lr; t->rlink = r; r->llink = rl;
l->balance = t->balance != 1 ? 0 : -1;
r->balance = t->balance != (char) -1 ? 0 : 1;
t->balance = 0;
@@ -277,14 +280,14 @@ kwsincr (kwset_t kws, char const *text, size_t len)
switch (dirs[depth + 1])
{
case R:
- l = links[depth], t = l->rlink, lr = t->llink;
- t->llink = l, l->rlink = lr;
+ l = links[depth]; t = l->rlink; lr = t->llink;
+ t->llink = l; l->rlink = lr;
t->balance = l->balance = 0;
break;
case L:
- l = links[depth], r = l->rlink, t = r->llink;
- lr = t->llink, rl = t->rlink;
- t->llink = l, l->rlink = lr, t->rlink = r, r->llink = rl;
+ l = links[depth]; r = l->rlink; t = r->llink;
+ lr = t->llink; rl = t->rlink;
+ t->llink = l; l->rlink = lr; t->rlink = r; r->llink = rl;
l->balance = t->balance != 1 ? 0 : -1;
r->balance = t->balance != (char) -1 ? 0 : 1;
t->balance = 0;
@@ -567,22 +570,22 @@ bmexec (kwset_t kws, char const *text, size_t size)
{
while (tp <= ep)
{
- d = d1[U(tp[-1])], tp += d;
- d = d1[U(tp[-1])], tp += d;
+ d = d1[U(tp[-1])]; tp += d;
+ d = d1[U(tp[-1])]; tp += d;
if (d == 0)
goto found;
- d = d1[U(tp[-1])], tp += d;
- d = d1[U(tp[-1])], tp += d;
- d = d1[U(tp[-1])], tp += d;
+ d = d1[U(tp[-1])]; tp += d;
+ d = d1[U(tp[-1])]; tp += d;
+ d = d1[U(tp[-1])]; tp += d;
if (d == 0)
goto found;
- d = d1[U(tp[-1])], tp += d;
- d = d1[U(tp[-1])], tp += d;
- d = d1[U(tp[-1])], tp += d;
+ d = d1[U(tp[-1])]; tp += d;
+ d = d1[U(tp[-1])]; tp += d;
+ d = d1[U(tp[-1])]; tp += d;
if (d == 0)
goto found;
- d = d1[U(tp[-1])], tp += d;
- d = d1[U(tp[-1])], tp += d;
+ d = d1[U(tp[-1])]; tp += d;
+ d = d1[U(tp[-1])]; tp += d;
}
break;
found:
@@ -649,7 +652,8 @@ cwexec (kwset_t kws, char const *text, size_t len, struct kwsmatch *kwsmatch)
mch = NULL;
else
{
- mch = text, accept = kwset->trie;
+ mch = text;
+ accept = kwset->trie;
goto match;
}
diff --git a/list-objects-filter.c b/list-objects-filter.c
index dc598a081b..7765761b3c 100644
--- a/list-objects-filter.c
+++ b/list-objects-filter.c
@@ -12,7 +12,7 @@
#include "oidmap.h"
#include "oidset.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
/* Remember to update object flag allocation in object.h */
/*
diff --git a/list-objects.c b/list-objects.c
index 943e62e868..1e5512e131 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -14,7 +14,7 @@
#include "list-objects-filter.h"
#include "list-objects-filter-options.h"
#include "packfile.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "trace.h"
#include "environment.h"
diff --git a/log-tree.c b/log-tree.c
index 8b184d6776..a4d4ab59ca 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -9,7 +9,7 @@
#include "environment.h"
#include "hex.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "repository.h"
#include "tmp-objdir.h"
#include "commit.h"
@@ -499,7 +499,7 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
{
struct strbuf headers = STRBUF_INIT;
const char *name = oid_to_hex(opt->zero_commit ?
- null_oid() : &commit->object.oid);
+ null_oid(the_hash_algo) : &commit->object.oid);
*need_8bit_cte_p = 0; /* unknown */
diff --git a/mailmap.c b/mailmap.c
index f35d20ed7f..9e2642a043 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -6,7 +6,7 @@
#include "string-list.h"
#include "mailmap.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "setup.h"
char *git_mailmap_file;
diff --git a/match-trees.c b/match-trees.c
index ef14ceb594..72922d5d64 100644
--- a/match-trees.c
+++ b/match-trees.c
@@ -6,7 +6,8 @@
#include "strbuf.h"
#include "tree.h"
#include "tree-walk.h"
-#include "object-store-ll.h"
+#include "object-file.h"
+#include "object-store.h"
#include "repository.h"
static int score_missing(unsigned mode)
diff --git a/merge-blobs.c b/merge-blobs.c
index 0ad0390fea..53f36dbc17 100644
--- a/merge-blobs.c
+++ b/merge-blobs.c
@@ -4,7 +4,7 @@
#include "merge-ll.h"
#include "blob.h"
#include "merge-blobs.h"
-#include "object-store-ll.h"
+#include "object-store.h"
static int fill_mmfile_blob(mmfile_t *f, struct blob *obj)
{
diff --git a/merge-ort-wrappers.h b/merge-ort-wrappers.h
index aeffa1c87b..b7e1ced9d7 100644
--- a/merge-ort-wrappers.h
+++ b/merge-ort-wrappers.h
@@ -1,7 +1,7 @@
#ifndef MERGE_ORT_WRAPPERS_H
#define MERGE_ORT_WRAPPERS_H
-#include "merge-recursive.h"
+#include "merge-ort.h"
/*
* rename-detecting three-way merge, no recursion.
diff --git a/merge-ort.c b/merge-ort.c
index 2b7d86aa4e..77310a4a52 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -26,6 +26,7 @@
#include "cache-tree.h"
#include "commit.h"
#include "commit-reach.h"
+#include "config.h"
#include "diff.h"
#include "diffcore.h"
#include "dir.h"
@@ -36,8 +37,9 @@
#include "merge-ll.h"
#include "match-trees.h"
#include "mem-pool.h"
+#include "object-file.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "oid-array.h"
#include "path.h"
#include "promisor-remote.h"
@@ -791,7 +793,7 @@ static void path_msg(struct merge_options *opt,
struct strbuf tmp = STRBUF_INIT;
/* Sanity checks */
- assert(omittable_hint ==
+ ASSERT(omittable_hint ==
(!starts_with(type_short_descriptions[type], "CONFLICT") &&
!starts_with(type_short_descriptions[type], "ERROR")) ||
type == CONFLICT_DIR_RENAME_SUGGESTED);
@@ -1642,7 +1644,7 @@ static int handle_deferred_entries(struct merge_options *opt,
ci = strmap_get(&opt->priv->paths, path);
VERIFY_CI(ci);
- assert(renames->deferred[side].trivial_merges_okay &&
+ ASSERT(renames->deferred[side].trivial_merges_okay &&
!strset_contains(&renames->deferred[side].target_dirs,
path));
resolve_trivial_directory_merge(ci, side);
@@ -1817,7 +1819,7 @@ static int merge_submodule(struct merge_options *opt,
BUG("submodule deleted on one side; this should be handled outside of merge_submodule()");
if ((sub_not_initialized = repo_submodule_init(&subrepo,
- opt->repo, path, null_oid()))) {
+ opt->repo, path, null_oid(the_hash_algo)))) {
path_msg(opt, CONFLICT_SUBMODULE_NOT_INITIALIZED, 0,
path, NULL, NULL, NULL,
_("Failed to merge submodule %s (not checked out)"),
@@ -2199,7 +2201,7 @@ static int handle_content_merge(struct merge_options *opt,
two_way = ((S_IFMT & o->mode) != (S_IFMT & a->mode));
merge_status = merge_3way(opt, path,
- two_way ? null_oid() : &o->oid,
+ two_way ? null_oid(the_hash_algo) : &o->oid,
&a->oid, &b->oid,
pathnames, extra_marker_size,
&result_buf);
@@ -2231,7 +2233,7 @@ static int handle_content_merge(struct merge_options *opt,
} else if (S_ISGITLINK(a->mode)) {
int two_way = ((S_IFMT & o->mode) != (S_IFMT & a->mode));
clean = merge_submodule(opt, pathnames[0],
- two_way ? null_oid() : &o->oid,
+ two_way ? null_oid(the_hash_algo) : &o->oid,
&a->oid, &b->oid, &result->oid);
if (clean < 0)
return -1;
@@ -2739,7 +2741,7 @@ static void apply_directory_rename_modifications(struct merge_options *opt,
assert(!new_ci->match_mask);
new_ci->dirmask = 0;
new_ci->stages[1].mode = 0;
- oidcpy(&new_ci->stages[1].oid, null_oid());
+ oidcpy(&new_ci->stages[1].oid, null_oid(the_hash_algo));
/*
* Now that we have the file information in new_ci, make sure
@@ -2752,7 +2754,7 @@ static void apply_directory_rename_modifications(struct merge_options *opt,
continue;
/* zero out any entries related to files */
ci->stages[i].mode = 0;
- oidcpy(&ci->stages[i].oid, null_oid());
+ oidcpy(&ci->stages[i].oid, null_oid(the_hash_algo));
}
/* Now we want to focus on new_ci, so reassign ci to it. */
@@ -3123,7 +3125,7 @@ static int process_renames(struct merge_options *opt,
if (type_changed) {
/* rename vs. typechange */
/* Mark the original as resolved by removal */
- memcpy(&oldinfo->stages[0].oid, null_oid(),
+ memcpy(&oldinfo->stages[0].oid, null_oid(the_hash_algo),
sizeof(oldinfo->stages[0].oid));
oldinfo->stages[0].mode = 0;
oldinfo->filemask &= 0x06;
@@ -4006,7 +4008,7 @@ static int process_entry(struct merge_options *opt,
if (ci->filemask & (1 << i))
continue;
ci->stages[i].mode = 0;
- oidcpy(&ci->stages[i].oid, null_oid());
+ oidcpy(&ci->stages[i].oid, null_oid(the_hash_algo));
}
} else if (ci->df_conflict && ci->merged.result.mode != 0) {
/*
@@ -4053,7 +4055,7 @@ static int process_entry(struct merge_options *opt,
continue;
/* zero out any entries related to directories */
new_ci->stages[i].mode = 0;
- oidcpy(&new_ci->stages[i].oid, null_oid());
+ oidcpy(&new_ci->stages[i].oid, null_oid(the_hash_algo));
}
/*
@@ -4175,11 +4177,11 @@ static int process_entry(struct merge_options *opt,
new_ci->merged.result.mode = ci->stages[2].mode;
oidcpy(&new_ci->merged.result.oid, &ci->stages[2].oid);
new_ci->stages[1].mode = 0;
- oidcpy(&new_ci->stages[1].oid, null_oid());
+ oidcpy(&new_ci->stages[1].oid, null_oid(the_hash_algo));
new_ci->filemask = 5;
if ((S_IFMT & b_mode) != (S_IFMT & o_mode)) {
new_ci->stages[0].mode = 0;
- oidcpy(&new_ci->stages[0].oid, null_oid());
+ oidcpy(&new_ci->stages[0].oid, null_oid(the_hash_algo));
new_ci->filemask = 4;
}
@@ -4187,11 +4189,11 @@ static int process_entry(struct merge_options *opt,
ci->merged.result.mode = ci->stages[1].mode;
oidcpy(&ci->merged.result.oid, &ci->stages[1].oid);
ci->stages[2].mode = 0;
- oidcpy(&ci->stages[2].oid, null_oid());
+ oidcpy(&ci->stages[2].oid, null_oid(the_hash_algo));
ci->filemask = 3;
if ((S_IFMT & a_mode) != (S_IFMT & o_mode)) {
ci->stages[0].mode = 0;
- oidcpy(&ci->stages[0].oid, null_oid());
+ oidcpy(&ci->stages[0].oid, null_oid(the_hash_algo));
ci->filemask = 2;
}
@@ -4316,7 +4318,7 @@ static int process_entry(struct merge_options *opt,
/* Deleted on both sides */
ci->merged.is_null = 1;
ci->merged.result.mode = 0;
- oidcpy(&ci->merged.result.oid, null_oid());
+ oidcpy(&ci->merged.result.oid, null_oid(the_hash_algo));
assert(!ci->df_conflict);
ci->merged.clean = !ci->path_conflict;
}
@@ -4957,9 +4959,6 @@ static void merge_start(struct merge_options *opt, struct merge_result *result)
}
trace2_region_leave("merge", "sanity checks", opt->repo);
- /* Default to histogram diff. Actually, just hardcode it...for now. */
- opt->xdl_opts = DIFF_WITH_ALG(opt, HISTOGRAM_DIFF);
-
/* Handle attr direction stuff for renormalization */
if (opt->renormalize)
git_attr_set_direction(GIT_ATTR_CHECKOUT);
@@ -5325,3 +5324,161 @@ void merge_incore_recursive(struct merge_options *opt,
merge_ort_internal(opt, merge_bases, side1, side2, result);
trace2_region_leave("merge", "incore_recursive", opt->repo);
}
+
+static void merge_recursive_config(struct merge_options *opt, int ui)
+{
+ char *value = NULL;
+ int renormalize = 0;
+ git_config_get_int("merge.verbosity", &opt->verbosity);
+ git_config_get_int("diff.renamelimit", &opt->rename_limit);
+ git_config_get_int("merge.renamelimit", &opt->rename_limit);
+ git_config_get_bool("merge.renormalize", &renormalize);
+ opt->renormalize = renormalize;
+ if (!git_config_get_string("diff.renames", &value)) {
+ opt->detect_renames = git_config_rename("diff.renames", value);
+ free(value);
+ }
+ if (!git_config_get_string("merge.renames", &value)) {
+ opt->detect_renames = git_config_rename("merge.renames", value);
+ free(value);
+ }
+ if (!git_config_get_string("merge.directoryrenames", &value)) {
+ int boolval = git_parse_maybe_bool(value);
+ if (0 <= boolval) {
+ opt->detect_directory_renames = boolval ?
+ MERGE_DIRECTORY_RENAMES_TRUE :
+ MERGE_DIRECTORY_RENAMES_NONE;
+ } else if (!strcasecmp(value, "conflict")) {
+ opt->detect_directory_renames =
+ MERGE_DIRECTORY_RENAMES_CONFLICT;
+ } /* avoid erroring on values from future versions of git */
+ free(value);
+ }
+ if (ui) {
+ if (!git_config_get_string("diff.algorithm", &value)) {
+ long diff_algorithm = parse_algorithm_value(value);
+ if (diff_algorithm < 0)
+ die(_("unknown value for config '%s': %s"), "diff.algorithm", value);
+ opt->xdl_opts = (opt->xdl_opts & ~XDF_DIFF_ALGORITHM_MASK) | diff_algorithm;
+ free(value);
+ }
+ }
+ git_config(git_xmerge_config, NULL);
+}
+
+static void init_merge_options(struct merge_options *opt,
+ struct repository *repo, int ui)
+{
+ const char *merge_verbosity;
+ memset(opt, 0, sizeof(struct merge_options));
+
+ opt->repo = repo;
+
+ opt->detect_renames = -1;
+ opt->detect_directory_renames = MERGE_DIRECTORY_RENAMES_CONFLICT;
+ opt->rename_limit = -1;
+
+ opt->verbosity = 2;
+ opt->buffer_output = 1;
+ strbuf_init(&opt->obuf, 0);
+
+ opt->renormalize = 0;
+
+ opt->conflict_style = -1;
+ opt->xdl_opts = DIFF_WITH_ALG(opt, HISTOGRAM_DIFF);
+
+ merge_recursive_config(opt, ui);
+ merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
+ if (merge_verbosity)
+ opt->verbosity = strtol(merge_verbosity, NULL, 10);
+ if (opt->verbosity >= 5)
+ opt->buffer_output = 0;
+}
+
+void init_ui_merge_options(struct merge_options *opt,
+ struct repository *repo)
+{
+ init_merge_options(opt, repo, 1);
+}
+
+void init_basic_merge_options(struct merge_options *opt,
+ struct repository *repo)
+{
+ init_merge_options(opt, repo, 0);
+}
+
+/*
+ * For now, members of merge_options do not need deep copying, but
+ * it may change in the future, in which case we would need to update
+ * this, and also make a matching change to clear_merge_options() to
+ * release the resources held by a copied instance.
+ */
+void copy_merge_options(struct merge_options *dst, struct merge_options *src)
+{
+ *dst = *src;
+}
+
+void clear_merge_options(struct merge_options *opt UNUSED)
+{
+ ; /* no-op as our copy is shallow right now */
+}
+
+int parse_merge_opt(struct merge_options *opt, const char *s)
+{
+ const char *arg;
+
+ if (!s || !*s)
+ return -1;
+ if (!strcmp(s, "ours"))
+ opt->recursive_variant = MERGE_VARIANT_OURS;
+ else if (!strcmp(s, "theirs"))
+ opt->recursive_variant = MERGE_VARIANT_THEIRS;
+ else if (!strcmp(s, "subtree"))
+ opt->subtree_shift = "";
+ else if (skip_prefix(s, "subtree=", &arg))
+ opt->subtree_shift = arg;
+ else if (!strcmp(s, "patience"))
+ opt->xdl_opts = DIFF_WITH_ALG(opt, PATIENCE_DIFF);
+ else if (!strcmp(s, "histogram"))
+ opt->xdl_opts = DIFF_WITH_ALG(opt, HISTOGRAM_DIFF);
+ else if (skip_prefix(s, "diff-algorithm=", &arg)) {
+ long value = parse_algorithm_value(arg);
+ if (value < 0)
+ return -1;
+ /* clear out previous settings */
+ DIFF_XDL_CLR(opt, NEED_MINIMAL);
+ opt->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
+ opt->xdl_opts |= value;
+ }
+ else if (!strcmp(s, "ignore-space-change"))
+ DIFF_XDL_SET(opt, IGNORE_WHITESPACE_CHANGE);
+ else if (!strcmp(s, "ignore-all-space"))
+ DIFF_XDL_SET(opt, IGNORE_WHITESPACE);
+ else if (!strcmp(s, "ignore-space-at-eol"))
+ DIFF_XDL_SET(opt, IGNORE_WHITESPACE_AT_EOL);
+ else if (!strcmp(s, "ignore-cr-at-eol"))
+ DIFF_XDL_SET(opt, IGNORE_CR_AT_EOL);
+ else if (!strcmp(s, "renormalize"))
+ opt->renormalize = 1;
+ else if (!strcmp(s, "no-renormalize"))
+ opt->renormalize = 0;
+ else if (!strcmp(s, "no-renames"))
+ opt->detect_renames = 0;
+ else if (!strcmp(s, "find-renames")) {
+ opt->detect_renames = 1;
+ opt->rename_score = 0;
+ }
+ else if (skip_prefix(s, "find-renames=", &arg) ||
+ skip_prefix(s, "rename-threshold=", &arg)) {
+ if ((opt->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0)
+ return -1;
+ opt->detect_renames = 1;
+ }
+ /*
+ * Please update $__git_merge_strategy_options in
+ * git-completion.bash when you add new options
+ */
+ else
+ return -1;
+ return 0;
+}
diff --git a/merge-ort.h b/merge-ort.h
index b63bc5424e..30750c0396 100644
--- a/merge-ort.h
+++ b/merge-ort.h
@@ -1,10 +1,11 @@
#ifndef MERGE_ORT_H
#define MERGE_ORT_H
-#include "merge-recursive.h"
#include "hash.h"
+#include "strbuf.h"
struct commit;
+struct commit_list;
struct tree;
struct strmap;
@@ -44,6 +45,51 @@ struct merge_result {
unsigned _properly_initialized;
};
+struct merge_options_internal;
+struct merge_options {
+ struct repository *repo;
+
+ /* ref names used in console messages and conflict markers */
+ const char *ancestor;
+ const char *branch1;
+ const char *branch2;
+
+ /* rename related options */
+ int detect_renames;
+ enum {
+ MERGE_DIRECTORY_RENAMES_NONE = 0,
+ MERGE_DIRECTORY_RENAMES_CONFLICT = 1,
+ MERGE_DIRECTORY_RENAMES_TRUE = 2
+ } detect_directory_renames;
+ int rename_limit;
+ int rename_score;
+ int show_rename_progress;
+
+ /* xdiff-related options (patience, ignore whitespace, ours/theirs) */
+ long xdl_opts;
+ int conflict_style;
+ enum {
+ MERGE_VARIANT_NORMAL = 0,
+ MERGE_VARIANT_OURS,
+ MERGE_VARIANT_THEIRS
+ } recursive_variant;
+
+ /* console output related options */
+ int verbosity;
+ unsigned buffer_output; /* 1: output at end, 2: keep buffered */
+ struct strbuf obuf; /* output buffer; if buffer_output == 2, caller
+ * must handle and call strbuf_release */
+
+ /* miscellaneous control options */
+ const char *subtree_shift;
+ unsigned renormalize : 1;
+ unsigned record_conflict_msgs_as_headers : 1;
+ const char *msg_header_prefix;
+
+ /* internal fields used by the implementation */
+ struct merge_options_internal *priv;
+};
+
/* Mostly internal function also used by merge-ort-wrappers.c */
struct commit *make_virtual_commit(struct repository *repo,
struct tree *tree,
@@ -119,4 +165,16 @@ void merge_get_conflicted_files(struct merge_result *result,
void merge_finalize(struct merge_options *opt,
struct merge_result *result);
+
+/* for use by porcelain commands */
+void init_ui_merge_options(struct merge_options *opt, struct repository *repo);
+/* for use by plumbing commands */
+void init_basic_merge_options(struct merge_options *opt, struct repository *repo);
+
+void copy_merge_options(struct merge_options *dst, struct merge_options *src);
+void clear_merge_options(struct merge_options *opt);
+
+/* parse the option in s and update the relevant field of opt */
+int parse_merge_opt(struct merge_options *opt, const char *s);
+
#endif
diff --git a/merge-recursive.c b/merge-recursive.c
deleted file mode 100644
index 884ccf99a5..0000000000
--- a/merge-recursive.c
+++ /dev/null
@@ -1,4079 +0,0 @@
-/*
- * Recursive Merge algorithm stolen from git-merge-recursive.py by
- * Fredrik Kuivinen.
- * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
- */
-
-#define USE_THE_REPOSITORY_VARIABLE
-#define DISABLE_SIGN_COMPARE_WARNINGS
-
-#include "git-compat-util.h"
-#include "merge-recursive.h"
-
-#include "alloc.h"
-#include "cache-tree.h"
-#include "commit.h"
-#include "commit-reach.h"
-#include "config.h"
-#include "diff.h"
-#include "diffcore.h"
-#include "dir.h"
-#include "environment.h"
-#include "gettext.h"
-#include "hex.h"
-#include "merge-ll.h"
-#include "lockfile.h"
-#include "match-trees.h"
-#include "name-hash.h"
-#include "object-file.h"
-#include "object-name.h"
-#include "object-store-ll.h"
-#include "path.h"
-#include "repository.h"
-#include "revision.h"
-#include "sparse-index.h"
-#include "string-list.h"
-#include "symlinks.h"
-#include "tag.h"
-#include "tree-walk.h"
-#include "unpack-trees.h"
-#include "xdiff-interface.h"
-
-struct merge_options_internal {
- int call_depth;
- int needed_rename_limit;
- struct hashmap current_file_dir_set;
- struct string_list df_conflict_file_set;
- struct unpack_trees_options unpack_opts;
- struct index_state orig_index;
-};
-
-struct path_hashmap_entry {
- struct hashmap_entry e;
- char path[FLEX_ARRAY];
-};
-
-static int path_hashmap_cmp(const void *cmp_data UNUSED,
- const struct hashmap_entry *eptr,
- const struct hashmap_entry *entry_or_key,
- const void *keydata)
-{
- const struct path_hashmap_entry *a, *b;
- const char *key = keydata;
-
- a = container_of(eptr, const struct path_hashmap_entry, e);
- b = container_of(entry_or_key, const struct path_hashmap_entry, e);
-
- return fspathcmp(a->path, key ? key : b->path);
-}
-
-/*
- * For dir_rename_entry, directory names are stored as a full path from the
- * toplevel of the repository and do not include a trailing '/'. Also:
- *
- * dir: original name of directory being renamed
- * non_unique_new_dir: if true, could not determine new_dir
- * new_dir: final name of directory being renamed
- * possible_new_dirs: temporary used to help determine new_dir; see comments
- * in get_directory_renames() for details
- */
-struct dir_rename_entry {
- struct hashmap_entry ent;
- char *dir;
- unsigned non_unique_new_dir:1;
- struct strbuf new_dir;
- struct string_list possible_new_dirs;
-};
-
-static struct dir_rename_entry *dir_rename_find_entry(struct hashmap *hashmap,
- char *dir)
-{
- struct dir_rename_entry key;
-
- if (!dir)
- return NULL;
- hashmap_entry_init(&key.ent, strhash(dir));
- key.dir = dir;
- return hashmap_get_entry(hashmap, &key, ent, NULL);
-}
-
-static int dir_rename_cmp(const void *cmp_data UNUSED,
- const struct hashmap_entry *eptr,
- const struct hashmap_entry *entry_or_key,
- const void *keydata UNUSED)
-{
- const struct dir_rename_entry *e1, *e2;
-
- e1 = container_of(eptr, const struct dir_rename_entry, ent);
- e2 = container_of(entry_or_key, const struct dir_rename_entry, ent);
-
- return strcmp(e1->dir, e2->dir);
-}
-
-static void dir_rename_init(struct hashmap *map)
-{
- hashmap_init(map, dir_rename_cmp, NULL, 0);
-}
-
-static void dir_rename_entry_init(struct dir_rename_entry *entry,
- char *directory)
-{
- hashmap_entry_init(&entry->ent, strhash(directory));
- entry->dir = directory;
- entry->non_unique_new_dir = 0;
- strbuf_init(&entry->new_dir, 0);
- string_list_init_nodup(&entry->possible_new_dirs);
-}
-
-struct collision_entry {
- struct hashmap_entry ent;
- char *target_file;
- struct string_list source_files;
- unsigned reported_already:1;
-};
-
-static struct collision_entry *collision_find_entry(struct hashmap *hashmap,
- char *target_file)
-{
- struct collision_entry key;
-
- hashmap_entry_init(&key.ent, strhash(target_file));
- key.target_file = target_file;
- return hashmap_get_entry(hashmap, &key, ent, NULL);
-}
-
-static int collision_cmp(const void *cmp_data UNUSED,
- const struct hashmap_entry *eptr,
- const struct hashmap_entry *entry_or_key,
- const void *keydata UNUSED)
-{
- const struct collision_entry *e1, *e2;
-
- e1 = container_of(eptr, const struct collision_entry, ent);
- e2 = container_of(entry_or_key, const struct collision_entry, ent);
-
- return strcmp(e1->target_file, e2->target_file);
-}
-
-static void collision_init(struct hashmap *map)
-{
- hashmap_init(map, collision_cmp, NULL, 0);
-}
-
-static void flush_output(struct merge_options *opt)
-{
- if (opt->buffer_output < 2 && opt->obuf.len) {
- fputs(opt->obuf.buf, stdout);
- strbuf_reset(&opt->obuf);
- }
-}
-
-__attribute__((format (printf, 2, 3)))
-static int err(struct merge_options *opt, const char *err, ...)
-{
- va_list params;
-
- if (opt->buffer_output < 2)
- flush_output(opt);
- else {
- strbuf_complete(&opt->obuf, '\n');
- strbuf_addstr(&opt->obuf, "error: ");
- }
- va_start(params, err);
- strbuf_vaddf(&opt->obuf, err, params);
- va_end(params);
- if (opt->buffer_output > 1)
- strbuf_addch(&opt->obuf, '\n');
- else {
- error("%s", opt->obuf.buf);
- strbuf_reset(&opt->obuf);
- }
-
- return -1;
-}
-
-static struct tree *shift_tree_object(struct repository *repo,
- struct tree *one, struct tree *two,
- const char *subtree_shift)
-{
- struct object_id shifted;
-
- if (!*subtree_shift) {
- shift_tree(repo, &one->object.oid, &two->object.oid, &shifted, 0);
- } else {
- shift_tree_by(repo, &one->object.oid, &two->object.oid, &shifted,
- subtree_shift);
- }
- if (oideq(&two->object.oid, &shifted))
- return two;
- return lookup_tree(repo, &shifted);
-}
-
-static inline void set_commit_tree(struct commit *c, struct tree *t)
-{
- c->maybe_tree = t;
-}
-
-static struct commit *make_virtual_commit(struct repository *repo,
- struct tree *tree,
- const char *comment)
-{
- struct commit *commit = alloc_commit_node(repo);
-
- set_merge_remote_desc(commit, comment, (struct object *)commit);
- set_commit_tree(commit, tree);
- commit->object.parsed = 1;
- return commit;
-}
-
-enum rename_type {
- RENAME_NORMAL = 0,
- RENAME_VIA_DIR,
- RENAME_ADD,
- RENAME_DELETE,
- RENAME_ONE_FILE_TO_ONE,
- RENAME_ONE_FILE_TO_TWO,
- RENAME_TWO_FILES_TO_ONE
-};
-
-/*
- * Since we want to write the index eventually, we cannot reuse the index
- * for these (temporary) data.
- */
-struct stage_data {
- struct diff_filespec stages[4]; /* mostly for oid & mode; maybe path */
- struct rename_conflict_info *rename_conflict_info;
- unsigned processed:1,
- rename_conflict_info_owned:1;
-};
-
-struct rename {
- unsigned processed:1;
- struct diff_filepair *pair;
- const char *branch; /* branch that the rename occurred on */
- /*
- * If directory rename detection affected this rename, what was its
- * original type ('A' or 'R') and it's original destination before
- * the directory rename (otherwise, '\0' and NULL for these two vars).
- */
- char dir_rename_original_type;
- char *dir_rename_original_dest;
- /*
- * Purpose of src_entry and dst_entry:
- *
- * If 'before' is renamed to 'after' then src_entry will contain
- * the versions of 'before' from the merge_base, HEAD, and MERGE in
- * stages 1, 2, and 3; dst_entry will contain the respective
- * versions of 'after' in corresponding locations. Thus, we have a
- * total of six modes and oids, though some will be null. (Stage 0
- * is ignored; we're interested in handling conflicts.)
- *
- * Since we don't turn on break-rewrites by default, neither
- * src_entry nor dst_entry can have all three of their stages have
- * non-null oids, meaning at most four of the six will be non-null.
- * Also, since this is a rename, both src_entry and dst_entry will
- * have at least one non-null oid, meaning at least two will be
- * non-null. Of the six oids, a typical rename will have three be
- * non-null. Only two implies a rename/delete, and four implies a
- * rename/add.
- */
- struct stage_data *src_entry;
- struct stage_data *dst_entry;
-};
-
-struct rename_conflict_info {
- enum rename_type rename_type;
- struct rename *ren1;
- struct rename *ren2;
-};
-
-static inline void setup_rename_conflict_info(enum rename_type rename_type,
- struct merge_options *opt,
- struct rename *ren1,
- struct rename *ren2)
-{
- struct rename_conflict_info *ci;
-
- /*
- * When we have two renames involved, it's easiest to get the
- * correct things into stage 2 and 3, and to make sure that the
- * content merge puts HEAD before the other branch if we just
- * ensure that branch1 == opt->branch1. So, simply flip arguments
- * around if we don't have that.
- */
- if (ren2 && ren1->branch != opt->branch1) {
- setup_rename_conflict_info(rename_type, opt, ren2, ren1);
- return;
- }
-
- CALLOC_ARRAY(ci, 1);
- ci->rename_type = rename_type;
- ci->ren1 = ren1;
- ci->ren2 = ren2;
-
- ci->ren1->dst_entry->processed = 0;
- ci->ren1->dst_entry->rename_conflict_info = ci;
- ci->ren1->dst_entry->rename_conflict_info_owned = 1;
- if (ren2) {
- ci->ren2->dst_entry->rename_conflict_info = ci;
- }
-}
-
-static int show(struct merge_options *opt, int v)
-{
- return (!opt->priv->call_depth && opt->verbosity >= v) ||
- opt->verbosity >= 5;
-}
-
-__attribute__((format (printf, 3, 4)))
-static void output(struct merge_options *opt, int v, const char *fmt, ...)
-{
- va_list ap;
-
- if (!show(opt, v))
- return;
-
- strbuf_addchars(&opt->obuf, ' ', opt->priv->call_depth * 2);
-
- va_start(ap, fmt);
- strbuf_vaddf(&opt->obuf, fmt, ap);
- va_end(ap);
-
- strbuf_addch(&opt->obuf, '\n');
- if (!opt->buffer_output)
- flush_output(opt);
-}
-
-static void repo_output_commit_title(struct merge_options *opt,
- struct repository *repo,
- struct commit *commit)
-{
- struct merge_remote_desc *desc;
-
- strbuf_addchars(&opt->obuf, ' ', opt->priv->call_depth * 2);
- desc = merge_remote_util(commit);
- if (desc)
- strbuf_addf(&opt->obuf, "virtual %s\n", desc->name);
- else {
- strbuf_repo_add_unique_abbrev(&opt->obuf, repo,
- &commit->object.oid,
- DEFAULT_ABBREV);
- strbuf_addch(&opt->obuf, ' ');
- if (repo_parse_commit(repo, commit) != 0)
- strbuf_addstr(&opt->obuf, _("(bad commit)\n"));
- else {
- const char *title;
- const char *msg = repo_get_commit_buffer(repo, commit, NULL);
- int len = find_commit_subject(msg, &title);
- if (len)
- strbuf_addf(&opt->obuf, "%.*s\n", len, title);
- repo_unuse_commit_buffer(repo, commit, msg);
- }
- }
- flush_output(opt);
-}
-
-static void output_commit_title(struct merge_options *opt, struct commit *commit)
-{
- repo_output_commit_title(opt, the_repository, commit);
-}
-
-static int add_cacheinfo(struct merge_options *opt,
- const struct diff_filespec *blob,
- const char *path, int stage, int refresh, int options)
-{
- struct index_state *istate = opt->repo->index;
- struct cache_entry *ce;
- int ret;
-
- ce = make_cache_entry(istate, blob->mode, &blob->oid, path, stage, 0);
- if (!ce)
- return err(opt, _("add_cacheinfo failed for path '%s'; merge aborting."), path);
-
- ret = add_index_entry(istate, ce, options);
- if (refresh) {
- struct cache_entry *nce;
-
- nce = refresh_cache_entry(istate, ce,
- CE_MATCH_REFRESH | CE_MATCH_IGNORE_MISSING);
- if (!nce)
- return err(opt, _("add_cacheinfo failed to refresh for path '%s'; merge aborting."), path);
- if (nce != ce)
- ret = add_index_entry(istate, nce, options);
- }
- return ret;
-}
-
-static inline int merge_detect_rename(struct merge_options *opt)
-{
- return (opt->detect_renames >= 0) ? opt->detect_renames : 1;
-}
-
-static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree)
-{
- if (parse_tree(tree) < 0)
- exit(128);
- init_tree_desc(desc, &tree->object.oid, tree->buffer, tree->size);
-}
-
-static int unpack_trees_start(struct merge_options *opt,
- struct tree *common,
- struct tree *head,
- struct tree *merge)
-{
- int rc;
- struct tree_desc t[3];
- struct index_state tmp_index = INDEX_STATE_INIT(opt->repo);
-
- memset(&opt->priv->unpack_opts, 0, sizeof(opt->priv->unpack_opts));
- if (opt->priv->call_depth)
- opt->priv->unpack_opts.index_only = 1;
- else {
- opt->priv->unpack_opts.update = 1;
- /* FIXME: should only do this if !overwrite_ignore */
- opt->priv->unpack_opts.preserve_ignored = 0;
- }
- opt->priv->unpack_opts.merge = 1;
- opt->priv->unpack_opts.head_idx = 2;
- opt->priv->unpack_opts.fn = threeway_merge;
- opt->priv->unpack_opts.src_index = opt->repo->index;
- opt->priv->unpack_opts.dst_index = &tmp_index;
- opt->priv->unpack_opts.aggressive = !merge_detect_rename(opt);
- setup_unpack_trees_porcelain(&opt->priv->unpack_opts, "merge");
-
- init_tree_desc_from_tree(t+0, common);
- init_tree_desc_from_tree(t+1, head);
- init_tree_desc_from_tree(t+2, merge);
-
- rc = unpack_trees(3, t, &opt->priv->unpack_opts);
- cache_tree_free(&opt->repo->index->cache_tree);
-
- /*
- * Update opt->repo->index to match the new results, AFTER saving a
- * copy in opt->priv->orig_index. Update src_index to point to the
- * saved copy. (verify_uptodate() checks src_index, and the original
- * index is the one that had the necessary modification timestamps.)
- */
- opt->priv->orig_index = *opt->repo->index;
- *opt->repo->index = tmp_index;
- opt->priv->unpack_opts.src_index = &opt->priv->orig_index;
-
- return rc;
-}
-
-static void unpack_trees_finish(struct merge_options *opt)
-{
- discard_index(&opt->priv->orig_index);
- clear_unpack_trees_porcelain(&opt->priv->unpack_opts);
-}
-
-static int save_files_dirs(const struct object_id *oid UNUSED,
- struct strbuf *base, const char *path,
- unsigned int mode, void *context)
-{
- struct path_hashmap_entry *entry;
- int baselen = base->len;
- struct merge_options *opt = context;
-
- strbuf_addstr(base, path);
-
- FLEX_ALLOC_MEM(entry, path, base->buf, base->len);
- hashmap_entry_init(&entry->e, fspathhash(entry->path));
- hashmap_add(&opt->priv->current_file_dir_set, &entry->e);
-
- strbuf_setlen(base, baselen);
- return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
-}
-
-static void get_files_dirs(struct merge_options *opt, struct tree *tree)
-{
- struct pathspec match_all;
- memset(&match_all, 0, sizeof(match_all));
- read_tree(opt->repo, tree,
- &match_all, save_files_dirs, opt);
-}
-
-static int get_tree_entry_if_blob(struct repository *r,
- const struct object_id *tree,
- const char *path,
- struct diff_filespec *dfs)
-{
- int ret;
-
- ret = get_tree_entry(r, tree, path, &dfs->oid, &dfs->mode);
- if (S_ISDIR(dfs->mode)) {
- oidcpy(&dfs->oid, null_oid());
- dfs->mode = 0;
- }
- return ret;
-}
-
-/*
- * Returns an index_entry instance which doesn't have to correspond to
- * a real cache entry in Git's index.
- */
-static struct stage_data *insert_stage_data(struct repository *r,
- const char *path,
- struct tree *o, struct tree *a, struct tree *b,
- struct string_list *entries)
-{
- struct string_list_item *item;
- struct stage_data *e = xcalloc(1, sizeof(struct stage_data));
- get_tree_entry_if_blob(r, &o->object.oid, path, &e->stages[1]);
- get_tree_entry_if_blob(r, &a->object.oid, path, &e->stages[2]);
- get_tree_entry_if_blob(r, &b->object.oid, path, &e->stages[3]);
- item = string_list_insert(entries, path);
- item->util = e;
- return e;
-}
-
-/*
- * Create a dictionary mapping file names to stage_data objects. The
- * dictionary contains one entry for every path with a non-zero stage entry.
- */
-static struct string_list *get_unmerged(struct index_state *istate)
-{
- struct string_list *unmerged = xmalloc(sizeof(struct string_list));
- int i;
-
- string_list_init_dup(unmerged);
-
- /* TODO: audit for interaction with sparse-index. */
- ensure_full_index(istate);
- for (i = 0; i < istate->cache_nr; i++) {
- struct string_list_item *item;
- struct stage_data *e;
- const struct cache_entry *ce = istate->cache[i];
- if (!ce_stage(ce))
- continue;
-
- item = string_list_lookup(unmerged, ce->name);
- if (!item) {
- item = string_list_insert(unmerged, ce->name);
- item->util = xcalloc(1, sizeof(struct stage_data));
- }
- e = item->util;
- e->stages[ce_stage(ce)].mode = ce->ce_mode;
- oidcpy(&e->stages[ce_stage(ce)].oid, &ce->oid);
- }
-
- return unmerged;
-}
-
-static int string_list_df_name_compare(const char *one, const char *two)
-{
- int onelen = strlen(one);
- int twolen = strlen(two);
- /*
- * Here we only care that entries for D/F conflicts are
- * adjacent, in particular with the file of the D/F conflict
- * appearing before files below the corresponding directory.
- * The order of the rest of the list is irrelevant for us.
- *
- * To achieve this, we sort with df_name_compare and provide
- * the mode S_IFDIR so that D/F conflicts will sort correctly.
- * We use the mode S_IFDIR for everything else for simplicity,
- * since in other cases any changes in their order due to
- * sorting cause no problems for us.
- */
- int cmp = df_name_compare(one, onelen, S_IFDIR,
- two, twolen, S_IFDIR);
- /*
- * Now that 'foo' and 'foo/bar' compare equal, we have to make sure
- * that 'foo' comes before 'foo/bar'.
- */
- if (cmp)
- return cmp;
- return onelen - twolen;
-}
-
-static void record_df_conflict_files(struct merge_options *opt,
- struct string_list *entries)
-{
- /* If there is a D/F conflict and the file for such a conflict
- * currently exists in the working tree, we want to allow it to be
- * removed to make room for the corresponding directory if needed.
- * The files underneath the directories of such D/F conflicts will
- * be processed before the corresponding file involved in the D/F
- * conflict. If the D/F directory ends up being removed by the
- * merge, then we won't have to touch the D/F file. If the D/F
- * directory needs to be written to the working copy, then the D/F
- * file will simply be removed (in make_room_for_path()) to make
- * room for the necessary paths. Note that if both the directory
- * and the file need to be present, then the D/F file will be
- * reinstated with a new unique name at the time it is processed.
- */
- struct string_list df_sorted_entries = STRING_LIST_INIT_NODUP;
- const char *last_file = NULL;
- int last_len = 0;
- int i;
-
- /*
- * If we're merging merge-bases, we don't want to bother with
- * any working directory changes.
- */
- if (opt->priv->call_depth)
- return;
-
- /* Ensure D/F conflicts are adjacent in the entries list. */
- for (i = 0; i < entries->nr; i++) {
- struct string_list_item *next = &entries->items[i];
- string_list_append(&df_sorted_entries, next->string)->util =
- next->util;
- }
- df_sorted_entries.cmp = string_list_df_name_compare;
- string_list_sort(&df_sorted_entries);
-
- string_list_clear(&opt->priv->df_conflict_file_set, 1);
- for (i = 0; i < df_sorted_entries.nr; i++) {
- const char *path = df_sorted_entries.items[i].string;
- int len = strlen(path);
- struct stage_data *e = df_sorted_entries.items[i].util;
-
- /*
- * Check if last_file & path correspond to a D/F conflict;
- * i.e. whether path is last_file+'/'+<something>.
- * If so, record that it's okay to remove last_file to make
- * room for path and friends if needed.
- */
- if (last_file &&
- len > last_len &&
- memcmp(path, last_file, last_len) == 0 &&
- path[last_len] == '/') {
- string_list_insert(&opt->priv->df_conflict_file_set, last_file);
- }
-
- /*
- * Determine whether path could exist as a file in the
- * working directory as a possible D/F conflict. This
- * will only occur when it exists in stage 2 as a
- * file.
- */
- if (S_ISREG(e->stages[2].mode) || S_ISLNK(e->stages[2].mode)) {
- last_file = path;
- last_len = len;
- } else {
- last_file = NULL;
- }
- }
- string_list_clear(&df_sorted_entries, 0);
-}
-
-static int update_stages(struct merge_options *opt, const char *path,
- const struct diff_filespec *o,
- const struct diff_filespec *a,
- const struct diff_filespec *b)
-{
-
- /*
- * NOTE: It is usually a bad idea to call update_stages on a path
- * before calling update_file on that same path, since it can
- * sometimes lead to spurious "refusing to lose untracked file..."
- * messages from update_file (via make_room_for path via
- * would_lose_untracked). Instead, reverse the order of the calls
- * (executing update_file first and then update_stages).
- */
- int clear = 1;
- int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_SKIP_DFCHECK;
- if (clear)
- if (remove_file_from_index(opt->repo->index, path))
- return -1;
- if (o)
- if (add_cacheinfo(opt, o, path, 1, 0, options))
- return -1;
- if (a)
- if (add_cacheinfo(opt, a, path, 2, 0, options))
- return -1;
- if (b)
- if (add_cacheinfo(opt, b, path, 3, 0, options))
- return -1;
- return 0;
-}
-
-static void update_entry(struct stage_data *entry,
- struct diff_filespec *o,
- struct diff_filespec *a,
- struct diff_filespec *b)
-{
- entry->processed = 0;
- entry->stages[1].mode = o->mode;
- entry->stages[2].mode = a->mode;
- entry->stages[3].mode = b->mode;
- oidcpy(&entry->stages[1].oid, &o->oid);
- oidcpy(&entry->stages[2].oid, &a->oid);
- oidcpy(&entry->stages[3].oid, &b->oid);
-}
-
-static int remove_file(struct merge_options *opt, int clean,
- const char *path, int no_wd)
-{
- int update_cache = opt->priv->call_depth || clean;
- int update_working_directory = !opt->priv->call_depth && !no_wd;
-
- if (update_cache) {
- if (remove_file_from_index(opt->repo->index, path))
- return -1;
- }
- if (update_working_directory) {
- if (ignore_case) {
- struct cache_entry *ce;
- ce = index_file_exists(opt->repo->index, path, strlen(path),
- ignore_case);
- if (ce && ce_stage(ce) == 0 && strcmp(path, ce->name))
- return 0;
- }
- if (remove_path(path))
- return -1;
- }
- return 0;
-}
-
-/* add a string to a strbuf, but converting "/" to "_" */
-static void add_flattened_path(struct strbuf *out, const char *s)
-{
- size_t i = out->len;
- strbuf_addstr(out, s);
- for (; i < out->len; i++)
- if (out->buf[i] == '/')
- out->buf[i] = '_';
-}
-
-static char *unique_path(struct merge_options *opt,
- const char *path,
- const char *branch)
-{
- struct path_hashmap_entry *entry;
- struct strbuf newpath = STRBUF_INIT;
- int suffix = 0;
- size_t base_len;
-
- strbuf_addf(&newpath, "%s~", path);
- add_flattened_path(&newpath, branch);
-
- base_len = newpath.len;
- while (hashmap_get_from_hash(&opt->priv->current_file_dir_set,
- fspathhash(newpath.buf), newpath.buf) ||
- (!opt->priv->call_depth && file_exists(newpath.buf))) {
- strbuf_setlen(&newpath, base_len);
- strbuf_addf(&newpath, "_%d", suffix++);
- }
-
- FLEX_ALLOC_MEM(entry, path, newpath.buf, newpath.len);
- hashmap_entry_init(&entry->e, fspathhash(entry->path));
- hashmap_add(&opt->priv->current_file_dir_set, &entry->e);
- return strbuf_detach(&newpath, NULL);
-}
-
-/**
- * Check whether a directory in the index is in the way of an incoming
- * file. Return 1 if so. If check_working_copy is non-zero, also
- * check the working directory. If empty_ok is non-zero, also return
- * 0 in the case where the working-tree dir exists but is empty.
- */
-static int dir_in_way(struct index_state *istate, const char *path,
- int check_working_copy, int empty_ok)
-{
- int pos;
- struct strbuf dirpath = STRBUF_INIT;
- struct stat st;
-
- strbuf_addstr(&dirpath, path);
- strbuf_addch(&dirpath, '/');
-
- pos = index_name_pos(istate, dirpath.buf, dirpath.len);
-
- if (pos < 0)
- pos = -1 - pos;
- if (pos < istate->cache_nr &&
- !strncmp(dirpath.buf, istate->cache[pos]->name, dirpath.len)) {
- strbuf_release(&dirpath);
- return 1;
- }
-
- strbuf_release(&dirpath);
- return check_working_copy && !lstat(path, &st) && S_ISDIR(st.st_mode) &&
- !(empty_ok && is_empty_dir(path)) &&
- !has_symlink_leading_path(path, strlen(path));
-}
-
-/*
- * Returns whether path was tracked in the index before the merge started,
- * and its oid and mode match the specified values
- */
-static int was_tracked_and_matches(struct merge_options *opt, const char *path,
- const struct diff_filespec *blob)
-{
- int pos = index_name_pos(&opt->priv->orig_index, path, strlen(path));
- struct cache_entry *ce;
-
- if (0 > pos)
- /* we were not tracking this path before the merge */
- return 0;
-
- /* See if the file we were tracking before matches */
- ce = opt->priv->orig_index.cache[pos];
- return (oideq(&ce->oid, &blob->oid) && ce->ce_mode == blob->mode);
-}
-
-/*
- * Returns whether path was tracked in the index before the merge started
- */
-static int was_tracked(struct merge_options *opt, const char *path)
-{
- int pos = index_name_pos(&opt->priv->orig_index, path, strlen(path));
-
- if (0 <= pos)
- /* we were tracking this path before the merge */
- return 1;
-
- return 0;
-}
-
-static int would_lose_untracked(struct merge_options *opt, const char *path)
-{
- struct index_state *istate = opt->repo->index;
-
- /*
- * This may look like it can be simplified to:
- * return !was_tracked(opt, path) && file_exists(path)
- * but it can't. This function needs to know whether path was in
- * the working tree due to EITHER having been tracked in the index
- * before the merge OR having been put into the working copy and
- * index by unpack_trees(). Due to that either-or requirement, we
- * check the current index instead of the original one.
- *
- * Note that we do not need to worry about merge-recursive itself
- * updating the index after unpack_trees() and before calling this
- * function, because we strictly require all code paths in
- * merge-recursive to update the working tree first and the index
- * second. Doing otherwise would break
- * update_file()/would_lose_untracked(); see every comment in this
- * file which mentions "update_stages".
- */
- int pos = index_name_pos(istate, path, strlen(path));
-
- if (pos < 0)
- pos = -1 - pos;
- while (pos < istate->cache_nr &&
- !strcmp(path, istate->cache[pos]->name)) {
- /*
- * If stage #0, it is definitely tracked.
- * If it has stage #2 then it was tracked
- * before this merge started. All other
- * cases the path was not tracked.
- */
- switch (ce_stage(istate->cache[pos])) {
- case 0:
- case 2:
- return 0;
- }
- pos++;
- }
- return file_exists(path);
-}
-
-static int was_dirty(struct merge_options *opt, const char *path)
-{
- struct cache_entry *ce;
- int dirty = 1;
-
- if (opt->priv->call_depth || !was_tracked(opt, path))
- return !dirty;
-
- ce = index_file_exists(opt->priv->unpack_opts.src_index,
- path, strlen(path), ignore_case);
- dirty = verify_uptodate(ce, &opt->priv->unpack_opts) != 0;
- return dirty;
-}
-
-static int make_room_for_path(struct merge_options *opt, const char *path)
-{
- int status, i;
- const char *msg = _("failed to create path '%s'%s");
-
- /* Unlink any D/F conflict files that are in the way */
- for (i = 0; i < opt->priv->df_conflict_file_set.nr; i++) {
- const char *df_path = opt->priv->df_conflict_file_set.items[i].string;
- size_t pathlen = strlen(path);
- size_t df_pathlen = strlen(df_path);
- if (df_pathlen < pathlen &&
- path[df_pathlen] == '/' &&
- strncmp(path, df_path, df_pathlen) == 0) {
- output(opt, 3,
- _("Removing %s to make room for subdirectory\n"),
- df_path);
- unlink(df_path);
- unsorted_string_list_delete_item(&opt->priv->df_conflict_file_set,
- i, 0);
- break;
- }
- }
-
- /* Make sure leading directories are created */
- status = safe_create_leading_directories_const(path);
- if (status) {
- if (status == SCLD_EXISTS)
- /* something else exists */
- return err(opt, msg, path, _(": perhaps a D/F conflict?"));
- return err(opt, msg, path, "");
- }
-
- /*
- * Do not unlink a file in the work tree if we are not
- * tracking it.
- */
- if (would_lose_untracked(opt, path))
- return err(opt, _("refusing to lose untracked file at '%s'"),
- path);
-
- /* Successful unlink is good.. */
- if (!unlink(path))
- return 0;
- /* .. and so is no existing file */
- if (errno == ENOENT)
- return 0;
- /* .. but not some other error (who really cares what?) */
- return err(opt, msg, path, _(": perhaps a D/F conflict?"));
-}
-
-static int update_file_flags(struct merge_options *opt,
- const struct diff_filespec *contents,
- const char *path,
- int update_cache,
- int update_wd)
-{
- int ret = 0;
-
- if (opt->priv->call_depth)
- update_wd = 0;
-
- if (update_wd) {
- enum object_type type;
- void *buf;
- unsigned long size;
-
- if (S_ISGITLINK(contents->mode)) {
- /*
- * We may later decide to recursively descend into
- * the submodule directory and update its index
- * and/or work tree, but we do not do that now.
- */
- update_wd = 0;
- goto update_index;
- }
-
- buf = repo_read_object_file(the_repository, &contents->oid,
- &type, &size);
- if (!buf) {
- ret = err(opt, _("cannot read object %s '%s'"),
- oid_to_hex(&contents->oid), path);
- goto free_buf;
- }
- if (type != OBJ_BLOB) {
- ret = err(opt, _("blob expected for %s '%s'"),
- oid_to_hex(&contents->oid), path);
- goto free_buf;
- }
- if (S_ISREG(contents->mode)) {
- struct strbuf strbuf = STRBUF_INIT;
- if (convert_to_working_tree(opt->repo->index,
- path, buf, size, &strbuf, NULL)) {
- free(buf);
- size = strbuf.len;
- buf = strbuf_detach(&strbuf, NULL);
- }
- }
-
- if (make_room_for_path(opt, path) < 0) {
- update_wd = 0;
- goto free_buf;
- }
- if (S_ISREG(contents->mode) ||
- (!has_symlinks && S_ISLNK(contents->mode))) {
- int fd;
- int mode = (contents->mode & 0100 ? 0777 : 0666);
-
- fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
- if (fd < 0) {
- ret = err(opt, _("failed to open '%s': %s"),
- path, strerror(errno));
- goto free_buf;
- }
- write_in_full(fd, buf, size);
- close(fd);
- } else if (S_ISLNK(contents->mode)) {
- char *lnk = xmemdupz(buf, size);
- safe_create_leading_directories_const(path);
- unlink(path);
- if (symlink(lnk, path))
- ret = err(opt, _("failed to symlink '%s': %s"),
- path, strerror(errno));
- free(lnk);
- } else
- ret = err(opt,
- _("do not know what to do with %06o %s '%s'"),
- contents->mode, oid_to_hex(&contents->oid), path);
- free_buf:
- free(buf);
- }
-update_index:
- if (!ret && update_cache) {
- int refresh = (!opt->priv->call_depth &&
- contents->mode != S_IFGITLINK);
- if (add_cacheinfo(opt, contents, path, 0, refresh,
- ADD_CACHE_OK_TO_ADD))
- return -1;
- }
- return ret;
-}
-
-static int update_file(struct merge_options *opt,
- int clean,
- const struct diff_filespec *contents,
- const char *path)
-{
- return update_file_flags(opt, contents, path,
- opt->priv->call_depth || clean, !opt->priv->call_depth);
-}
-
-/* Low level file merging, update and removal */
-
-struct merge_file_info {
- struct diff_filespec blob; /* mostly use oid & mode; sometimes path */
- unsigned clean:1,
- merge:1;
-};
-
-static int merge_3way(struct merge_options *opt,
- mmbuffer_t *result_buf,
- const struct diff_filespec *o,
- const struct diff_filespec *a,
- const struct diff_filespec *b,
- const char *branch1,
- const char *branch2,
- const int extra_marker_size)
-{
- mmfile_t orig, src1, src2;
- struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT;
- char *base, *name1, *name2;
- enum ll_merge_result merge_status;
-
- ll_opts.renormalize = opt->renormalize;
- ll_opts.extra_marker_size = extra_marker_size;
- ll_opts.xdl_opts = opt->xdl_opts;
- ll_opts.conflict_style = opt->conflict_style;
-
- if (opt->priv->call_depth) {
- ll_opts.virtual_ancestor = 1;
- ll_opts.variant = 0;
- } else {
- switch (opt->recursive_variant) {
- case MERGE_VARIANT_OURS:
- ll_opts.variant = XDL_MERGE_FAVOR_OURS;
- break;
- case MERGE_VARIANT_THEIRS:
- ll_opts.variant = XDL_MERGE_FAVOR_THEIRS;
- break;
- default:
- ll_opts.variant = 0;
- break;
- }
- }
-
- assert(a->path && b->path && o->path && opt->ancestor);
- if (strcmp(a->path, b->path) || strcmp(a->path, o->path) != 0) {
- base = mkpathdup("%s:%s", opt->ancestor, o->path);
- name1 = mkpathdup("%s:%s", branch1, a->path);
- name2 = mkpathdup("%s:%s", branch2, b->path);
- } else {
- base = mkpathdup("%s", opt->ancestor);
- name1 = mkpathdup("%s", branch1);
- name2 = mkpathdup("%s", branch2);
- }
-
- read_mmblob(&orig, &o->oid);
- read_mmblob(&src1, &a->oid);
- read_mmblob(&src2, &b->oid);
-
- /*
- * FIXME: Using a->path for normalization rules in ll_merge could be
- * wrong if we renamed from a->path to b->path. We should use the
- * target path for where the file will be written.
- */
- merge_status = ll_merge(result_buf, a->path, &orig, base,
- &src1, name1, &src2, name2,
- opt->repo->index, &ll_opts);
- if (merge_status == LL_MERGE_BINARY_CONFLICT)
- warning("Cannot merge binary files: %s (%s vs. %s)",
- a->path, name1, name2);
-
- free(base);
- free(name1);
- free(name2);
- free(orig.ptr);
- free(src1.ptr);
- free(src2.ptr);
- return merge_status;
-}
-
-static int find_first_merges(struct repository *repo,
- struct object_array *result, const char *path,
- struct commit *a, struct commit *b)
-{
- int i, j;
- struct object_array merges = OBJECT_ARRAY_INIT;
- struct commit *commit;
- int contains_another;
-
- char merged_revision[GIT_MAX_HEXSZ + 2];
- const char *rev_args[] = { "rev-list", "--merges", "--ancestry-path",
- "--all", merged_revision, NULL };
- struct rev_info revs;
- struct setup_revision_opt rev_opts;
-
- memset(result, 0, sizeof(struct object_array));
- memset(&rev_opts, 0, sizeof(rev_opts));
-
- /* get all revisions that merge commit a */
- xsnprintf(merged_revision, sizeof(merged_revision), "^%s",
- oid_to_hex(&a->object.oid));
- repo_init_revisions(repo, &revs, NULL);
- /* FIXME: can't handle linked worktrees in submodules yet */
- revs.single_worktree = path != NULL;
- setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
-
- /* save all revisions from the above list that contain b */
- if (prepare_revision_walk(&revs))
- die("revision walk setup failed");
- while ((commit = get_revision(&revs)) != NULL) {
- struct object *o = &(commit->object);
- int ret = repo_in_merge_bases(repo, b, commit);
- if (ret < 0) {
- object_array_clear(&merges);
- release_revisions(&revs);
- return ret;
- }
- if (ret)
- add_object_array(o, NULL, &merges);
- }
- reset_revision_walk();
-
- /* Now we've got all merges that contain a and b. Prune all
- * merges that contain another found merge and save them in
- * result.
- */
- for (i = 0; i < merges.nr; i++) {
- struct commit *m1 = (struct commit *) merges.objects[i].item;
-
- contains_another = 0;
- for (j = 0; j < merges.nr; j++) {
- struct commit *m2 = (struct commit *) merges.objects[j].item;
- if (i != j) {
- int ret = repo_in_merge_bases(repo, m2, m1);
- if (ret < 0) {
- object_array_clear(&merges);
- release_revisions(&revs);
- return ret;
- }
- if (ret > 0) {
- contains_another = 1;
- break;
- }
- }
- }
-
- if (!contains_another)
- add_object_array(merges.objects[i].item, NULL, result);
- }
-
- object_array_clear(&merges);
- release_revisions(&revs);
- return result->nr;
-}
-
-static void print_commit(struct repository *repo, struct commit *commit)
-{
- struct strbuf sb = STRBUF_INIT;
- struct pretty_print_context ctx = {0};
- ctx.date_mode.type = DATE_NORMAL;
- /* FIXME: Merge this with output_commit_title() */
- assert(!merge_remote_util(commit));
- repo_format_commit_message(repo, commit, " %h: %m %s", &sb, &ctx);
- fprintf(stderr, "%s\n", sb.buf);
- strbuf_release(&sb);
-}
-
-static int is_valid(const struct diff_filespec *dfs)
-{
- return dfs->mode != 0 && !is_null_oid(&dfs->oid);
-}
-
-static int merge_submodule(struct merge_options *opt,
- struct object_id *result, const char *path,
- const struct object_id *base, const struct object_id *a,
- const struct object_id *b)
-{
- struct repository subrepo;
- int ret = 0, ret2;
- struct commit *commit_base, *commit_a, *commit_b;
- int parent_count;
- struct object_array merges;
-
- int i;
- int search = !opt->priv->call_depth;
-
- /* store a in result in case we fail */
- /* FIXME: This is the WRONG resolution for the recursive case when
- * we need to be careful to avoid accidentally matching either side.
- * Should probably use o instead there, much like we do for merging
- * binaries.
- */
- oidcpy(result, a);
-
- /* we can not handle deletion conflicts */
- if (is_null_oid(base))
- return 0;
- if (is_null_oid(a))
- return 0;
- if (is_null_oid(b))
- return 0;
-
- if (repo_submodule_init(&subrepo, opt->repo, path, null_oid())) {
- output(opt, 1, _("Failed to merge submodule %s (not checked out)"), path);
- return 0;
- }
-
- if (!(commit_base = lookup_commit_reference(&subrepo, base)) ||
- !(commit_a = lookup_commit_reference(&subrepo, a)) ||
- !(commit_b = lookup_commit_reference(&subrepo, b))) {
- output(opt, 1, _("Failed to merge submodule %s (commits not present)"), path);
- goto cleanup;
- }
-
- /* check whether both changes are forward */
- ret2 = repo_in_merge_bases(&subrepo, commit_base, commit_a);
- if (ret2 < 0) {
- output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
- ret = -1;
- goto cleanup;
- }
- if (ret2 > 0)
- ret2 = repo_in_merge_bases(&subrepo, commit_base, commit_b);
- if (ret2 < 0) {
- output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
- ret = -1;
- goto cleanup;
- }
- if (!ret2) {
- output(opt, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path);
- goto cleanup;
- }
-
- /* Case #1: a is contained in b or vice versa */
- ret2 = repo_in_merge_bases(&subrepo, commit_a, commit_b);
- if (ret2 < 0) {
- output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
- ret = -1;
- goto cleanup;
- }
- if (ret2) {
- oidcpy(result, b);
- if (show(opt, 3)) {
- output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
- repo_output_commit_title(opt, &subrepo, commit_b);
- } else if (show(opt, 2))
- output(opt, 2, _("Fast-forwarding submodule %s"), path);
- else
- ; /* no output */
-
- ret = 1;
- goto cleanup;
- }
- ret2 = repo_in_merge_bases(&subrepo, commit_b, commit_a);
- if (ret2 < 0) {
- output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
- ret = -1;
- goto cleanup;
- }
- if (ret2) {
- oidcpy(result, a);
- if (show(opt, 3)) {
- output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
- repo_output_commit_title(opt, &subrepo, commit_a);
- } else if (show(opt, 2))
- output(opt, 2, _("Fast-forwarding submodule %s"), path);
- else
- ; /* no output */
-
- ret = 1;
- goto cleanup;
- }
-
- /*
- * Case #2: There are one or more merges that contain a and b in
- * the submodule. If there is only one, then present it as a
- * suggestion to the user, but leave it marked unmerged so the
- * user needs to confirm the resolution.
- */
-
- /* Skip the search if makes no sense to the calling context. */
- if (!search)
- goto cleanup;
-
- /* find commit which merges them */
- parent_count = find_first_merges(&subrepo, &merges, path,
- commit_a, commit_b);
- switch (parent_count) {
- case -1:
- output(opt, 1,_("Failed to merge submodule %s (repository corrupt)"), path);
- ret = -1;
- break;
- case 0:
- output(opt, 1, _("Failed to merge submodule %s (merge following commits not found)"), path);
- break;
-
- case 1:
- output(opt, 1, _("Failed to merge submodule %s (not fast-forward)"), path);
- output(opt, 2, _("Found a possible merge resolution for the submodule:\n"));
- print_commit(&subrepo, (struct commit *) merges.objects[0].item);
- output(opt, 2, _(
- "If this is correct simply add it to the index "
- "for example\n"
- "by using:\n\n"
- " git update-index --cacheinfo 160000 %s \"%s\"\n\n"
- "which will accept this suggestion.\n"),
- oid_to_hex(&merges.objects[0].item->oid), path);
- break;
-
- default:
- output(opt, 1, _("Failed to merge submodule %s (multiple merges found)"), path);
- for (i = 0; i < merges.nr; i++)
- print_commit(&subrepo, (struct commit *) merges.objects[i].item);
- }
-
- object_array_clear(&merges);
-cleanup:
- repo_clear(&subrepo);
- return ret;
-}
-
-static int merge_mode_and_contents(struct merge_options *opt,
- const struct diff_filespec *o,
- const struct diff_filespec *a,
- const struct diff_filespec *b,
- const char *filename,
- const char *branch1,
- const char *branch2,
- const int extra_marker_size,
- struct merge_file_info *result)
-{
- if (opt->branch1 != branch1) {
- /*
- * It's weird getting a reverse merge with HEAD on the bottom
- * side of the conflict markers and the other branch on the
- * top. Fix that.
- */
- return merge_mode_and_contents(opt, o, b, a,
- filename,
- branch2, branch1,
- extra_marker_size, result);
- }
-
- result->merge = 0;
- result->clean = 1;
-
- if ((S_IFMT & a->mode) != (S_IFMT & b->mode)) {
- result->clean = 0;
- /*
- * FIXME: This is a bad resolution for recursive case; for
- * the recursive case we want something that is unlikely to
- * accidentally match either side. Also, while it makes
- * sense to prefer regular files over symlinks, it doesn't
- * make sense to prefer regular files over submodules.
- */
- if (S_ISREG(a->mode)) {
- result->blob.mode = a->mode;
- oidcpy(&result->blob.oid, &a->oid);
- } else {
- result->blob.mode = b->mode;
- oidcpy(&result->blob.oid, &b->oid);
- }
- } else {
- if (!oideq(&a->oid, &o->oid) && !oideq(&b->oid, &o->oid))
- result->merge = 1;
-
- /*
- * Merge modes
- */
- if (a->mode == b->mode || a->mode == o->mode)
- result->blob.mode = b->mode;
- else {
- result->blob.mode = a->mode;
- if (b->mode != o->mode) {
- result->clean = 0;
- result->merge = 1;
- }
- }
-
- if (oideq(&a->oid, &b->oid) || oideq(&a->oid, &o->oid))
- oidcpy(&result->blob.oid, &b->oid);
- else if (oideq(&b->oid, &o->oid))
- oidcpy(&result->blob.oid, &a->oid);
- else if (S_ISREG(a->mode)) {
- mmbuffer_t result_buf;
- int ret = 0, merge_status;
-
- merge_status = merge_3way(opt, &result_buf, o, a, b,
- branch1, branch2,
- extra_marker_size);
-
- if ((merge_status < 0) || !result_buf.ptr)
- ret = err(opt, _("failed to execute internal merge"));
-
- if (!ret &&
- write_object_file(result_buf.ptr, result_buf.size,
- OBJ_BLOB, &result->blob.oid))
- ret = err(opt, _("unable to add %s to database"),
- a->path);
-
- free(result_buf.ptr);
- if (ret)
- return ret;
- /* FIXME: bug, what if modes didn't match? */
- result->clean = (merge_status == 0);
- } else if (S_ISGITLINK(a->mode)) {
- int clean = merge_submodule(opt, &result->blob.oid,
- o->path,
- &o->oid,
- &a->oid,
- &b->oid);
- if (clean < 0)
- return -1;
- result->clean = clean;
- } else if (S_ISLNK(a->mode)) {
- switch (opt->recursive_variant) {
- case MERGE_VARIANT_NORMAL:
- oidcpy(&result->blob.oid, &a->oid);
- if (!oideq(&a->oid, &b->oid))
- result->clean = 0;
- break;
- case MERGE_VARIANT_OURS:
- oidcpy(&result->blob.oid, &a->oid);
- break;
- case MERGE_VARIANT_THEIRS:
- oidcpy(&result->blob.oid, &b->oid);
- break;
- }
- } else
- BUG("unsupported object type in the tree");
- }
-
- if (result->merge)
- output(opt, 2, _("Auto-merging %s"), filename);
-
- return 0;
-}
-
-static int handle_rename_via_dir(struct merge_options *opt,
- struct rename_conflict_info *ci)
-{
- /*
- * Handle file adds that need to be renamed due to directory rename
- * detection. This differs from handle_rename_normal, because
- * there is no content merge to do; just move the file into the
- * desired final location.
- */
- const struct rename *ren = ci->ren1;
- const struct diff_filespec *dest = ren->pair->two;
- char *file_path = dest->path;
- int mark_conflicted = (opt->detect_directory_renames ==
- MERGE_DIRECTORY_RENAMES_CONFLICT);
- assert(ren->dir_rename_original_dest);
-
- if (!opt->priv->call_depth && would_lose_untracked(opt, dest->path)) {
- mark_conflicted = 1;
- file_path = unique_path(opt, dest->path, ren->branch);
- output(opt, 1, _("Error: Refusing to lose untracked file at %s; "
- "writing to %s instead."),
- dest->path, file_path);
- }
-
- if (mark_conflicted) {
- /*
- * Write the file in worktree at file_path. In the index,
- * only record the file at dest->path in the appropriate
- * higher stage.
- */
- if (update_file(opt, 0, dest, file_path))
- return -1;
- if (file_path != dest->path)
- free(file_path);
- if (update_stages(opt, dest->path, NULL,
- ren->branch == opt->branch1 ? dest : NULL,
- ren->branch == opt->branch1 ? NULL : dest))
- return -1;
- return 0; /* not clean, but conflicted */
- } else {
- /* Update dest->path both in index and in worktree */
- if (update_file(opt, 1, dest, dest->path))
- return -1;
- return 1; /* clean */
- }
-}
-
-static int handle_change_delete(struct merge_options *opt,
- const char *path, const char *old_path,
- const struct diff_filespec *o,
- const struct diff_filespec *changed,
- const char *change_branch,
- const char *delete_branch,
- const char *change, const char *change_past)
-{
- char *alt_path = NULL;
- const char *update_path = path;
- int ret = 0;
-
- if (dir_in_way(opt->repo->index, path, !opt->priv->call_depth, 0) ||
- (!opt->priv->call_depth && would_lose_untracked(opt, path))) {
- update_path = alt_path = unique_path(opt, path, change_branch);
- }
-
- if (opt->priv->call_depth) {
- /*
- * We cannot arbitrarily accept either a_sha or b_sha as
- * correct; since there is no true "middle point" between
- * them, simply reuse the base version for virtual merge base.
- */
- ret = remove_file_from_index(opt->repo->index, path);
- if (!ret)
- ret = update_file(opt, 0, o, update_path);
- } else {
- /*
- * Despite the four nearly duplicate messages and argument
- * lists below and the ugliness of the nested if-statements,
- * having complete messages makes the job easier for
- * translators.
- *
- * The slight variance among the cases is due to the fact
- * that:
- * 1) directory/file conflicts (in effect if
- * !alt_path) could cause us to need to write the
- * file to a different path.
- * 2) renames (in effect if !old_path) could mean that
- * there are two names for the path that the user
- * may know the file by.
- */
- if (!alt_path) {
- if (!old_path) {
- output(opt, 1, _("CONFLICT (%s/delete): %s deleted in %s "
- "and %s in %s. Version %s of %s left in tree."),
- change, path, delete_branch, change_past,
- change_branch, change_branch, path);
- } else {
- output(opt, 1, _("CONFLICT (%s/delete): %s deleted in %s "
- "and %s to %s in %s. Version %s of %s left in tree."),
- change, old_path, delete_branch, change_past, path,
- change_branch, change_branch, path);
- }
- } else {
- if (!old_path) {
- output(opt, 1, _("CONFLICT (%s/delete): %s deleted in %s "
- "and %s in %s. Version %s of %s left in tree at %s."),
- change, path, delete_branch, change_past,
- change_branch, change_branch, path, alt_path);
- } else {
- output(opt, 1, _("CONFLICT (%s/delete): %s deleted in %s "
- "and %s to %s in %s. Version %s of %s left in tree at %s."),
- change, old_path, delete_branch, change_past, path,
- change_branch, change_branch, path, alt_path);
- }
- }
- /*
- * No need to call update_file() on path when change_branch ==
- * opt->branch1 && !alt_path, since that would needlessly touch
- * path. We could call update_file_flags() with update_cache=0
- * and update_wd=0, but that's a no-op.
- */
- if (change_branch != opt->branch1 || alt_path)
- ret = update_file(opt, 0, changed, update_path);
- }
- free(alt_path);
-
- return ret;
-}
-
-static int handle_rename_delete(struct merge_options *opt,
- struct rename_conflict_info *ci)
-{
- const struct rename *ren = ci->ren1;
- const struct diff_filespec *orig = ren->pair->one;
- const struct diff_filespec *dest = ren->pair->two;
- const char *rename_branch = ren->branch;
- const char *delete_branch = (opt->branch1 == ren->branch ?
- opt->branch2 : opt->branch1);
-
- if (handle_change_delete(opt,
- opt->priv->call_depth ? orig->path : dest->path,
- opt->priv->call_depth ? NULL : orig->path,
- orig, dest,
- rename_branch, delete_branch,
- _("rename"), _("renamed")))
- return -1;
-
- if (opt->priv->call_depth)
- return remove_file_from_index(opt->repo->index, dest->path);
- else
- return update_stages(opt, dest->path, NULL,
- rename_branch == opt->branch1 ? dest : NULL,
- rename_branch == opt->branch1 ? NULL : dest);
-}
-
-static int handle_file_collision(struct merge_options *opt,
- const char *collide_path,
- const char *prev_path1,
- const char *prev_path2,
- const char *branch1, const char *branch2,
- struct diff_filespec *a,
- struct diff_filespec *b)
-{
- struct merge_file_info mfi;
- struct diff_filespec null;
- char *alt_path = NULL;
- const char *update_path = collide_path;
-
- /*
- * It's easiest to get the correct things into stage 2 and 3, and
- * to make sure that the content merge puts HEAD before the other
- * branch if we just ensure that branch1 == opt->branch1. So, simply
- * flip arguments around if we don't have that.
- */
- if (branch1 != opt->branch1) {
- return handle_file_collision(opt, collide_path,
- prev_path2, prev_path1,
- branch2, branch1,
- b, a);
- }
-
- /* Remove rename sources if rename/add or rename/rename(2to1) */
- if (prev_path1)
- remove_file(opt, 1, prev_path1,
- opt->priv->call_depth || would_lose_untracked(opt, prev_path1));
- if (prev_path2)
- remove_file(opt, 1, prev_path2,
- opt->priv->call_depth || would_lose_untracked(opt, prev_path2));
-
- /*
- * Remove the collision path, if it wouldn't cause dirty contents
- * or an untracked file to get lost. We'll either overwrite with
- * merged contents, or just write out to differently named files.
- */
- if (was_dirty(opt, collide_path)) {
- output(opt, 1, _("Refusing to lose dirty file at %s"),
- collide_path);
- update_path = alt_path = unique_path(opt, collide_path, "merged");
- } else if (would_lose_untracked(opt, collide_path)) {
- /*
- * Only way we get here is if both renames were from
- * a directory rename AND user had an untracked file
- * at the location where both files end up after the
- * two directory renames. See testcase 10d of t6043.
- */
- output(opt, 1, _("Refusing to lose untracked file at "
- "%s, even though it's in the way."),
- collide_path);
- update_path = alt_path = unique_path(opt, collide_path, "merged");
- } else {
- /*
- * FIXME: It's possible that the two files are identical
- * and that the current working copy happens to match, in
- * which case we are unnecessarily touching the working
- * tree file. It's not a likely enough scenario that I
- * want to code up the checks for it and a better fix is
- * available if we restructure how unpack_trees() and
- * merge-recursive interoperate anyway, so punting for
- * now...
- */
- remove_file(opt, 0, collide_path, 0);
- }
-
- /* Store things in diff_filespecs for functions that need it */
- null.path = (char *)collide_path;
- oidcpy(&null.oid, null_oid());
- null.mode = 0;
-
- if (merge_mode_and_contents(opt, &null, a, b, collide_path,
- branch1, branch2, opt->priv->call_depth * 2, &mfi))
- return -1;
- mfi.clean &= !alt_path;
- if (update_file(opt, mfi.clean, &mfi.blob, update_path))
- return -1;
- if (!mfi.clean && !opt->priv->call_depth &&
- update_stages(opt, collide_path, NULL, a, b))
- return -1;
- free(alt_path);
- /*
- * FIXME: If both a & b both started with conflicts (only possible
- * if they came from a rename/rename(2to1)), but had IDENTICAL
- * contents including those conflicts, then in the next line we claim
- * it was clean. If someone cares about this case, we should have the
- * caller notify us if we started with conflicts.
- */
- return mfi.clean;
-}
-
-static int handle_rename_add(struct merge_options *opt,
- struct rename_conflict_info *ci)
-{
- /* a was renamed to c, and a separate c was added. */
- struct diff_filespec *a = ci->ren1->pair->one;
- struct diff_filespec *c = ci->ren1->pair->two;
- char *path = c->path;
- char *prev_path_desc;
- struct merge_file_info mfi;
-
- const char *rename_branch = ci->ren1->branch;
- const char *add_branch = (opt->branch1 == rename_branch ?
- opt->branch2 : opt->branch1);
- int other_stage = (ci->ren1->branch == opt->branch1 ? 3 : 2);
-
- output(opt, 1, _("CONFLICT (rename/add): "
- "Rename %s->%s in %s. Added %s in %s"),
- a->path, c->path, rename_branch,
- c->path, add_branch);
-
- prev_path_desc = xstrfmt("version of %s from %s", path, a->path);
- ci->ren1->src_entry->stages[other_stage].path = a->path;
- if (merge_mode_and_contents(opt, a, c,
- &ci->ren1->src_entry->stages[other_stage],
- prev_path_desc,
- opt->branch1, opt->branch2,
- 1 + opt->priv->call_depth * 2, &mfi))
- return -1;
- free(prev_path_desc);
-
- ci->ren1->dst_entry->stages[other_stage].path = mfi.blob.path = c->path;
- return handle_file_collision(opt,
- c->path, a->path, NULL,
- rename_branch, add_branch,
- &mfi.blob,
- &ci->ren1->dst_entry->stages[other_stage]);
-}
-
-static char *find_path_for_conflict(struct merge_options *opt,
- const char *path,
- const char *branch1,
- const char *branch2)
-{
- char *new_path = NULL;
- if (dir_in_way(opt->repo->index, path, !opt->priv->call_depth, 0)) {
- new_path = unique_path(opt, path, branch1);
- output(opt, 1, _("%s is a directory in %s adding "
- "as %s instead"),
- path, branch2, new_path);
- } else if (would_lose_untracked(opt, path)) {
- new_path = unique_path(opt, path, branch1);
- output(opt, 1, _("Refusing to lose untracked file"
- " at %s; adding as %s instead"),
- path, new_path);
- }
-
- return new_path;
-}
-
-/*
- * Toggle the stage number between "ours" and "theirs" (2 and 3).
- */
-static inline int flip_stage(int stage)
-{
- return (2 + 3) - stage;
-}
-
-static int handle_rename_rename_1to2(struct merge_options *opt,
- struct rename_conflict_info *ci)
-{
- /* One file was renamed in both branches, but to different names. */
- struct merge_file_info mfi;
- struct diff_filespec *add;
- struct diff_filespec *o = ci->ren1->pair->one;
- struct diff_filespec *a = ci->ren1->pair->two;
- struct diff_filespec *b = ci->ren2->pair->two;
- char *path_desc;
-
- output(opt, 1, _("CONFLICT (rename/rename): "
- "Rename \"%s\"->\"%s\" in branch \"%s\" "
- "rename \"%s\"->\"%s\" in \"%s\"%s"),
- o->path, a->path, ci->ren1->branch,
- o->path, b->path, ci->ren2->branch,
- opt->priv->call_depth ? _(" (left unresolved)") : "");
-
- path_desc = xstrfmt("%s and %s, both renamed from %s",
- a->path, b->path, o->path);
- if (merge_mode_and_contents(opt, o, a, b, path_desc,
- ci->ren1->branch, ci->ren2->branch,
- opt->priv->call_depth * 2, &mfi))
- return -1;
- free(path_desc);
-
- if (opt->priv->call_depth)
- remove_file_from_index(opt->repo->index, o->path);
-
- /*
- * For each destination path, we need to see if there is a
- * rename/add collision. If not, we can write the file out
- * to the specified location.
- */
- add = &ci->ren1->dst_entry->stages[flip_stage(2)];
- if (is_valid(add)) {
- add->path = mfi.blob.path = a->path;
- if (handle_file_collision(opt, a->path,
- NULL, NULL,
- ci->ren1->branch,
- ci->ren2->branch,
- &mfi.blob, add) < 0)
- return -1;
- } else {
- char *new_path = find_path_for_conflict(opt, a->path,
- ci->ren1->branch,
- ci->ren2->branch);
- if (update_file(opt, 0, &mfi.blob,
- new_path ? new_path : a->path))
- return -1;
- free(new_path);
- if (!opt->priv->call_depth &&
- update_stages(opt, a->path, NULL, a, NULL))
- return -1;
- }
-
- if (!mfi.clean && mfi.blob.mode == a->mode &&
- oideq(&mfi.blob.oid, &a->oid)) {
- /*
- * Getting here means we were attempting to merge a binary
- * blob. Since we can't merge binaries, the merge algorithm
- * just takes one side. But we don't want to copy the
- * contents of one side to both paths; we'd rather use the
- * original content at the given path for each path.
- */
- oidcpy(&mfi.blob.oid, &b->oid);
- mfi.blob.mode = b->mode;
- }
- add = &ci->ren2->dst_entry->stages[flip_stage(3)];
- if (is_valid(add)) {
- add->path = mfi.blob.path = b->path;
- if (handle_file_collision(opt, b->path,
- NULL, NULL,
- ci->ren1->branch,
- ci->ren2->branch,
- add, &mfi.blob) < 0)
- return -1;
- } else {
- char *new_path = find_path_for_conflict(opt, b->path,
- ci->ren2->branch,
- ci->ren1->branch);
- if (update_file(opt, 0, &mfi.blob,
- new_path ? new_path : b->path))
- return -1;
- free(new_path);
- if (!opt->priv->call_depth &&
- update_stages(opt, b->path, NULL, NULL, b))
- return -1;
- }
-
- return 0;
-}
-
-static int handle_rename_rename_2to1(struct merge_options *opt,
- struct rename_conflict_info *ci)
-{
- /* Two files, a & b, were renamed to the same thing, c. */
- struct diff_filespec *a = ci->ren1->pair->one;
- struct diff_filespec *b = ci->ren2->pair->one;
- struct diff_filespec *c1 = ci->ren1->pair->two;
- struct diff_filespec *c2 = ci->ren2->pair->two;
- char *path = c1->path; /* == c2->path */
- char *path_side_1_desc;
- char *path_side_2_desc;
- struct merge_file_info mfi_c1;
- struct merge_file_info mfi_c2;
- int ostage1, ostage2;
-
- output(opt, 1, _("CONFLICT (rename/rename): "
- "Rename %s->%s in %s. "
- "Rename %s->%s in %s"),
- a->path, c1->path, ci->ren1->branch,
- b->path, c2->path, ci->ren2->branch);
-
- path_side_1_desc = xstrfmt("version of %s from %s", path, a->path);
- path_side_2_desc = xstrfmt("version of %s from %s", path, b->path);
- ostage1 = ci->ren1->branch == opt->branch1 ? 3 : 2;
- ostage2 = flip_stage(ostage1);
- ci->ren1->src_entry->stages[ostage1].path = a->path;
- ci->ren2->src_entry->stages[ostage2].path = b->path;
- if (merge_mode_and_contents(opt, a, c1,
- &ci->ren1->src_entry->stages[ostage1],
- path_side_1_desc,
- opt->branch1, opt->branch2,
- 1 + opt->priv->call_depth * 2, &mfi_c1) ||
- merge_mode_and_contents(opt, b,
- &ci->ren2->src_entry->stages[ostage2],
- c2, path_side_2_desc,
- opt->branch1, opt->branch2,
- 1 + opt->priv->call_depth * 2, &mfi_c2))
- return -1;
- free(path_side_1_desc);
- free(path_side_2_desc);
- mfi_c1.blob.path = path;
- mfi_c2.blob.path = path;
-
- return handle_file_collision(opt, path, a->path, b->path,
- ci->ren1->branch, ci->ren2->branch,
- &mfi_c1.blob, &mfi_c2.blob);
-}
-
-/*
- * Get the diff_filepairs changed between o_tree and tree.
- */
-static struct diff_queue_struct *get_diffpairs(struct merge_options *opt,
- struct tree *o_tree,
- struct tree *tree)
-{
- struct diff_queue_struct *ret;
- struct diff_options opts;
-
- repo_diff_setup(opt->repo, &opts);
- opts.flags.recursive = 1;
- opts.flags.rename_empty = 0;
- opts.detect_rename = merge_detect_rename(opt);
- /*
- * We do not have logic to handle the detection of copies. In
- * fact, it may not even make sense to add such logic: would we
- * really want a change to a base file to be propagated through
- * multiple other files by a merge?
- */
- if (opts.detect_rename > DIFF_DETECT_RENAME)
- opts.detect_rename = DIFF_DETECT_RENAME;
- opts.rename_limit = (opt->rename_limit >= 0) ? opt->rename_limit : 7000;
- opts.rename_score = opt->rename_score;
- opts.show_rename_progress = opt->show_rename_progress;
- opts.output_format = DIFF_FORMAT_NO_OUTPUT;
- diff_setup_done(&opts);
- diff_tree_oid(&o_tree->object.oid, &tree->object.oid, "", &opts);
- diffcore_std(&opts);
- if (opts.needed_rename_limit > opt->priv->needed_rename_limit)
- opt->priv->needed_rename_limit = opts.needed_rename_limit;
-
- ret = xmalloc(sizeof(*ret));
- *ret = diff_queued_diff;
-
- opts.output_format = DIFF_FORMAT_NO_OUTPUT;
- diff_queued_diff.nr = 0;
- diff_queued_diff.queue = NULL;
- diff_flush(&opts);
- return ret;
-}
-
-static int tree_has_path(struct repository *r, struct tree *tree,
- const char *path)
-{
- struct object_id hashy;
- unsigned short mode_o;
-
- return !get_tree_entry(r,
- &tree->object.oid, path,
- &hashy, &mode_o);
-}
-
-/*
- * Return a new string that replaces the beginning portion (which matches
- * entry->dir), with entry->new_dir. In perl-speak:
- * new_path_name = (old_path =~ s/entry->dir/entry->new_dir/);
- * NOTE:
- * Caller must ensure that old_path starts with entry->dir + '/'.
- */
-static char *apply_dir_rename(struct dir_rename_entry *entry,
- const char *old_path)
-{
- struct strbuf new_path = STRBUF_INIT;
- int oldlen, newlen;
-
- if (entry->non_unique_new_dir)
- return NULL;
-
- oldlen = strlen(entry->dir);
- if (entry->new_dir.len == 0)
- /*
- * If someone renamed/merged a subdirectory into the root
- * directory (e.g. 'some/subdir' -> ''), then we want to
- * avoid returning
- * '' + '/filename'
- * as the rename; we need to make old_path + oldlen advance
- * past the '/' character.
- */
- oldlen++;
- newlen = entry->new_dir.len + (strlen(old_path) - oldlen) + 1;
- strbuf_grow(&new_path, newlen);
- strbuf_addbuf(&new_path, &entry->new_dir);
- strbuf_addstr(&new_path, &old_path[oldlen]);
-
- return strbuf_detach(&new_path, NULL);
-}
-
-static void get_renamed_dir_portion(const char *old_path, const char *new_path,
- char **old_dir, char **new_dir)
-{
- char *end_of_old, *end_of_new;
-
- /* Default return values: NULL, meaning no rename */
- *old_dir = NULL;
- *new_dir = NULL;
-
- /*
- * For
- * "a/b/c/d/e/foo.c" -> "a/b/some/thing/else/e/foo.c"
- * the "e/foo.c" part is the same, we just want to know that
- * "a/b/c/d" was renamed to "a/b/some/thing/else"
- * so, for this example, this function returns "a/b/c/d" in
- * *old_dir and "a/b/some/thing/else" in *new_dir.
- */
-
- /*
- * If the basename of the file changed, we don't care. We want
- * to know which portion of the directory, if any, changed.
- */
- end_of_old = strrchr(old_path, '/');
- end_of_new = strrchr(new_path, '/');
-
- /*
- * If end_of_old is NULL, old_path wasn't in a directory, so there
- * could not be a directory rename (our rule elsewhere that a
- * directory which still exists is not considered to have been
- * renamed means the root directory can never be renamed -- because
- * the root directory always exists).
- */
- if (!end_of_old)
- return; /* Note: *old_dir and *new_dir are still NULL */
-
- /*
- * If new_path contains no directory (end_of_new is NULL), then we
- * have a rename of old_path's directory to the root directory.
- */
- if (!end_of_new) {
- *old_dir = xstrndup(old_path, end_of_old - old_path);
- *new_dir = xstrdup("");
- return;
- }
-
- /* Find the first non-matching character traversing backwards */
- while (*--end_of_new == *--end_of_old &&
- end_of_old != old_path &&
- end_of_new != new_path)
- ; /* Do nothing; all in the while loop */
-
- /*
- * If both got back to the beginning of their strings, then the
- * directory didn't change at all, only the basename did.
- */
- if (end_of_old == old_path && end_of_new == new_path &&
- *end_of_old == *end_of_new)
- return; /* Note: *old_dir and *new_dir are still NULL */
-
- /*
- * If end_of_new got back to the beginning of its string, and
- * end_of_old got back to the beginning of some subdirectory, then
- * we have a rename/merge of a subdirectory into the root, which
- * needs slightly special handling.
- *
- * Note: There is no need to consider the opposite case, with a
- * rename/merge of the root directory into some subdirectory
- * because as noted above the root directory always exists so it
- * cannot be considered to be renamed.
- */
- if (end_of_new == new_path &&
- end_of_old != old_path && end_of_old[-1] == '/') {
- *old_dir = xstrndup(old_path, --end_of_old - old_path);
- *new_dir = xstrdup("");
- return;
- }
-
- /*
- * We've found the first non-matching character in the directory
- * paths. That means the current characters we were looking at
- * were part of the first non-matching subdir name going back from
- * the end of the strings. Get the whole name by advancing both
- * end_of_old and end_of_new to the NEXT '/' character. That will
- * represent the entire directory rename.
- *
- * The reason for the increment is cases like
- * a/b/star/foo/whatever.c -> a/b/tar/foo/random.c
- * After dropping the basename and going back to the first
- * non-matching character, we're now comparing:
- * a/b/s and a/b/
- * and we want to be comparing:
- * a/b/star/ and a/b/tar/
- * but without the pre-increment, the one on the right would stay
- * a/b/.
- */
- end_of_old = strchr(++end_of_old, '/');
- end_of_new = strchr(++end_of_new, '/');
-
- /* Copy the old and new directories into *old_dir and *new_dir. */
- *old_dir = xstrndup(old_path, end_of_old - old_path);
- *new_dir = xstrndup(new_path, end_of_new - new_path);
-}
-
-static void remove_hashmap_entries(struct hashmap *dir_renames,
- struct string_list *items_to_remove)
-{
- int i;
- struct dir_rename_entry *entry;
-
- for (i = 0; i < items_to_remove->nr; i++) {
- entry = items_to_remove->items[i].util;
- hashmap_remove(dir_renames, &entry->ent, NULL);
- }
- string_list_clear(items_to_remove, 0);
-}
-
-/*
- * See if there is a directory rename for path, and if there are any file
- * level conflicts for the renamed location. If there is a rename and
- * there are no conflicts, return the new name. Otherwise, return NULL.
- */
-static char *handle_path_level_conflicts(struct merge_options *opt,
- const char *path,
- struct dir_rename_entry *entry,
- struct hashmap *collisions,
- struct tree *tree)
-{
- char *new_path = NULL;
- struct collision_entry *collision_ent;
- int clean = 1;
- struct strbuf collision_paths = STRBUF_INIT;
-
- /*
- * entry has the mapping of old directory name to new directory name
- * that we want to apply to path.
- */
- new_path = apply_dir_rename(entry, path);
-
- if (!new_path) {
- /* This should only happen when entry->non_unique_new_dir set */
- if (!entry->non_unique_new_dir)
- BUG("entry->non_unique_new_dir not set and !new_path");
- output(opt, 1, _("CONFLICT (directory rename split): "
- "Unclear where to place %s because directory "
- "%s was renamed to multiple other directories, "
- "with no destination getting a majority of the "
- "files."),
- path, entry->dir);
- clean = 0;
- return NULL;
- }
-
- /*
- * The caller needs to have ensured that it has pre-populated
- * collisions with all paths that map to new_path. Do a quick check
- * to ensure that's the case.
- */
- collision_ent = collision_find_entry(collisions, new_path);
- if (!collision_ent)
- BUG("collision_ent is NULL");
-
- /*
- * Check for one-sided add/add/.../add conflicts, i.e.
- * where implicit renames from the other side doing
- * directory rename(s) can affect this side of history
- * to put multiple paths into the same location. Warn
- * and bail on directory renames for such paths.
- */
- if (collision_ent->reported_already) {
- clean = 0;
- } else if (tree_has_path(opt->repo, tree, new_path)) {
- collision_ent->reported_already = 1;
- strbuf_add_separated_string_list(&collision_paths, ", ",
- &collision_ent->source_files);
- output(opt, 1, _("CONFLICT (implicit dir rename): Existing "
- "file/dir at %s in the way of implicit "
- "directory rename(s) putting the following "
- "path(s) there: %s."),
- new_path, collision_paths.buf);
- clean = 0;
- } else if (collision_ent->source_files.nr > 1) {
- collision_ent->reported_already = 1;
- strbuf_add_separated_string_list(&collision_paths, ", ",
- &collision_ent->source_files);
- output(opt, 1, _("CONFLICT (implicit dir rename): Cannot map "
- "more than one path to %s; implicit directory "
- "renames tried to put these paths there: %s"),
- new_path, collision_paths.buf);
- clean = 0;
- }
-
- /* Free memory we no longer need */
- strbuf_release(&collision_paths);
- if (!clean && new_path) {
- free(new_path);
- return NULL;
- }
-
- return new_path;
-}
-
-/*
- * There are a couple things we want to do at the directory level:
- * 1. Check for both sides renaming to the same thing, in order to avoid
- * implicit renaming of files that should be left in place. (See
- * testcase 6b in t6043 for details.)
- * 2. Prune directory renames if there are still files left in the
- * original directory. These represent a partial directory rename,
- * i.e. a rename where only some of the files within the directory
- * were renamed elsewhere. (Technically, this could be done earlier
- * in get_directory_renames(), except that would prevent us from
- * doing the previous check and thus failing testcase 6b.)
- * 3. Check for rename/rename(1to2) conflicts (at the directory level).
- * In the future, we could potentially record this info as well and
- * omit reporting rename/rename(1to2) conflicts for each path within
- * the affected directories, thus cleaning up the merge output.
- * NOTE: We do NOT check for rename/rename(2to1) conflicts at the
- * directory level, because merging directories is fine. If it
- * causes conflicts for files within those merged directories, then
- * that should be detected at the individual path level.
- */
-static void handle_directory_level_conflicts(struct merge_options *opt,
- struct hashmap *dir_re_head,
- struct tree *head,
- struct hashmap *dir_re_merge,
- struct tree *merge)
-{
- struct hashmap_iter iter;
- struct dir_rename_entry *head_ent;
- struct dir_rename_entry *merge_ent;
-
- struct string_list remove_from_head = STRING_LIST_INIT_NODUP;
- struct string_list remove_from_merge = STRING_LIST_INIT_NODUP;
-
- hashmap_for_each_entry(dir_re_head, &iter, head_ent,
- ent /* member name */) {
- merge_ent = dir_rename_find_entry(dir_re_merge, head_ent->dir);
- if (merge_ent &&
- !head_ent->non_unique_new_dir &&
- !merge_ent->non_unique_new_dir &&
- !strbuf_cmp(&head_ent->new_dir, &merge_ent->new_dir)) {
- /* 1. Renamed identically; remove it from both sides */
- string_list_append(&remove_from_head,
- head_ent->dir)->util = head_ent;
- strbuf_release(&head_ent->new_dir);
- string_list_append(&remove_from_merge,
- merge_ent->dir)->util = merge_ent;
- strbuf_release(&merge_ent->new_dir);
- } else if (tree_has_path(opt->repo, head, head_ent->dir)) {
- /* 2. This wasn't a directory rename after all */
- string_list_append(&remove_from_head,
- head_ent->dir)->util = head_ent;
- strbuf_release(&head_ent->new_dir);
- }
- }
-
- remove_hashmap_entries(dir_re_head, &remove_from_head);
- remove_hashmap_entries(dir_re_merge, &remove_from_merge);
-
- hashmap_for_each_entry(dir_re_merge, &iter, merge_ent,
- ent /* member name */) {
- head_ent = dir_rename_find_entry(dir_re_head, merge_ent->dir);
- if (tree_has_path(opt->repo, merge, merge_ent->dir)) {
- /* 2. This wasn't a directory rename after all */
- string_list_append(&remove_from_merge,
- merge_ent->dir)->util = merge_ent;
- } else if (head_ent &&
- !head_ent->non_unique_new_dir &&
- !merge_ent->non_unique_new_dir) {
- /* 3. rename/rename(1to2) */
- /*
- * We can assume it's not rename/rename(1to1) because
- * that was case (1), already checked above. So we
- * know that head_ent->new_dir and merge_ent->new_dir
- * are different strings.
- */
- output(opt, 1, _("CONFLICT (rename/rename): "
- "Rename directory %s->%s in %s. "
- "Rename directory %s->%s in %s"),
- head_ent->dir, head_ent->new_dir.buf, opt->branch1,
- head_ent->dir, merge_ent->new_dir.buf, opt->branch2);
- string_list_append(&remove_from_head,
- head_ent->dir)->util = head_ent;
- strbuf_release(&head_ent->new_dir);
- string_list_append(&remove_from_merge,
- merge_ent->dir)->util = merge_ent;
- strbuf_release(&merge_ent->new_dir);
- }
- }
-
- remove_hashmap_entries(dir_re_head, &remove_from_head);
- remove_hashmap_entries(dir_re_merge, &remove_from_merge);
-}
-
-static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs)
-{
- struct hashmap *dir_renames;
- struct hashmap_iter iter;
- struct dir_rename_entry *entry;
- int i;
-
- /*
- * Typically, we think of a directory rename as all files from a
- * certain directory being moved to a target directory. However,
- * what if someone first moved two files from the original
- * directory in one commit, and then renamed the directory
- * somewhere else in a later commit? At merge time, we just know
- * that files from the original directory went to two different
- * places, and that the bulk of them ended up in the same place.
- * We want each directory rename to represent where the bulk of the
- * files from that directory end up; this function exists to find
- * where the bulk of the files went.
- *
- * The first loop below simply iterates through the list of file
- * renames, finding out how often each directory rename pair
- * possibility occurs.
- */
- dir_renames = xmalloc(sizeof(*dir_renames));
- dir_rename_init(dir_renames);
- for (i = 0; i < pairs->nr; ++i) {
- struct string_list_item *item;
- int *count;
- struct diff_filepair *pair = pairs->queue[i];
- char *old_dir, *new_dir;
-
- /* File not part of directory rename if it wasn't renamed */
- if (pair->status != 'R')
- continue;
-
- get_renamed_dir_portion(pair->one->path, pair->two->path,
- &old_dir, &new_dir);
- if (!old_dir)
- /* Directory didn't change at all; ignore this one. */
- continue;
-
- entry = dir_rename_find_entry(dir_renames, old_dir);
- if (!entry) {
- entry = xmalloc(sizeof(*entry));
- dir_rename_entry_init(entry, old_dir);
- hashmap_put(dir_renames, &entry->ent);
- } else {
- free(old_dir);
- }
- item = string_list_lookup(&entry->possible_new_dirs, new_dir);
- if (!item) {
- item = string_list_insert(&entry->possible_new_dirs,
- new_dir);
- item->util = xcalloc(1, sizeof(int));
- } else {
- free(new_dir);
- }
- count = item->util;
- *count += 1;
- }
-
- /*
- * For each directory with files moved out of it, we find out which
- * target directory received the most files so we can declare it to
- * be the "winning" target location for the directory rename. This
- * winner gets recorded in new_dir. If there is no winner
- * (multiple target directories received the same number of files),
- * we set non_unique_new_dir. Once we've determined the winner (or
- * that there is no winner), we no longer need possible_new_dirs.
- */
- hashmap_for_each_entry(dir_renames, &iter, entry,
- ent /* member name */) {
- int max = 0;
- int bad_max = 0;
- char *best = NULL;
-
- for (i = 0; i < entry->possible_new_dirs.nr; i++) {
- int *count = entry->possible_new_dirs.items[i].util;
-
- if (*count == max)
- bad_max = max;
- else if (*count > max) {
- max = *count;
- best = entry->possible_new_dirs.items[i].string;
- }
- }
- if (bad_max == max)
- entry->non_unique_new_dir = 1;
- else {
- assert(entry->new_dir.len == 0);
- strbuf_addstr(&entry->new_dir, best);
- }
- /*
- * The relevant directory sub-portion of the original full
- * filepaths were xstrndup'ed before inserting into
- * possible_new_dirs, and instead of manually iterating the
- * list and free'ing each, just lie and tell
- * possible_new_dirs that it did the strdup'ing so that it
- * will free them for us.
- */
- entry->possible_new_dirs.strdup_strings = 1;
- string_list_clear(&entry->possible_new_dirs, 1);
- }
-
- return dir_renames;
-}
-
-static struct dir_rename_entry *check_dir_renamed(const char *path,
- struct hashmap *dir_renames)
-{
- char *temp = xstrdup(path);
- char *end;
- struct dir_rename_entry *entry = NULL;
-
- while ((end = strrchr(temp, '/'))) {
- *end = '\0';
- entry = dir_rename_find_entry(dir_renames, temp);
- if (entry)
- break;
- }
- free(temp);
- return entry;
-}
-
-static void compute_collisions(struct hashmap *collisions,
- struct hashmap *dir_renames,
- struct diff_queue_struct *pairs)
-{
- int i;
-
- /*
- * Multiple files can be mapped to the same path due to directory
- * renames done by the other side of history. Since that other
- * side of history could have merged multiple directories into one,
- * if our side of history added the same file basename to each of
- * those directories, then all N of them would get implicitly
- * renamed by the directory rename detection into the same path,
- * and we'd get an add/add/.../add conflict, and all those adds
- * from *this* side of history. This is not representable in the
- * index, and users aren't going to easily be able to make sense of
- * it. So we need to provide a good warning about what's
- * happening, and fall back to no-directory-rename detection
- * behavior for those paths.
- *
- * See testcases 9e and all of section 5 from t6043 for examples.
- */
- collision_init(collisions);
-
- for (i = 0; i < pairs->nr; ++i) {
- struct dir_rename_entry *dir_rename_ent;
- struct collision_entry *collision_ent;
- char *new_path;
- struct diff_filepair *pair = pairs->queue[i];
-
- if (pair->status != 'A' && pair->status != 'R')
- continue;
- dir_rename_ent = check_dir_renamed(pair->two->path,
- dir_renames);
- if (!dir_rename_ent)
- continue;
-
- new_path = apply_dir_rename(dir_rename_ent, pair->two->path);
- if (!new_path)
- /*
- * dir_rename_ent->non_unique_new_path is true, which
- * means there is no directory rename for us to use,
- * which means it won't cause us any additional
- * collisions.
- */
- continue;
- collision_ent = collision_find_entry(collisions, new_path);
- if (!collision_ent) {
- CALLOC_ARRAY(collision_ent, 1);
- hashmap_entry_init(&collision_ent->ent,
- strhash(new_path));
- hashmap_put(collisions, &collision_ent->ent);
- collision_ent->target_file = new_path;
- } else {
- free(new_path);
- }
- string_list_insert(&collision_ent->source_files,
- pair->two->path);
- }
-}
-
-static char *check_for_directory_rename(struct merge_options *opt,
- const char *path,
- struct tree *tree,
- struct hashmap *dir_renames,
- struct hashmap *dir_rename_exclusions,
- struct hashmap *collisions,
- int *clean_merge)
-{
- char *new_path = NULL;
- struct dir_rename_entry *entry = check_dir_renamed(path, dir_renames);
- struct dir_rename_entry *oentry = NULL;
-
- if (!entry)
- return new_path;
-
- /*
- * This next part is a little weird. We do not want to do an
- * implicit rename into a directory we renamed on our side, because
- * that will result in a spurious rename/rename(1to2) conflict. An
- * example:
- * Base commit: dumbdir/afile, otherdir/bfile
- * Side 1: smrtdir/afile, otherdir/bfile
- * Side 2: dumbdir/afile, dumbdir/bfile
- * Here, while working on Side 1, we could notice that otherdir was
- * renamed/merged to dumbdir, and change the diff_filepair for
- * otherdir/bfile into a rename into dumbdir/bfile. However, Side
- * 2 will notice the rename from dumbdir to smrtdir, and do the
- * transitive rename to move it from dumbdir/bfile to
- * smrtdir/bfile. That gives us bfile in dumbdir vs being in
- * smrtdir, a rename/rename(1to2) conflict. We really just want
- * the file to end up in smrtdir. And the way to achieve that is
- * to not let Side1 do the rename to dumbdir, since we know that is
- * the source of one of our directory renames.
- *
- * That's why oentry and dir_rename_exclusions is here.
- *
- * As it turns out, this also prevents N-way transient rename
- * confusion; See testcases 9c and 9d of t6043.
- */
- oentry = dir_rename_find_entry(dir_rename_exclusions, entry->new_dir.buf);
- if (oentry) {
- output(opt, 1, _("WARNING: Avoiding applying %s -> %s rename "
- "to %s, because %s itself was renamed."),
- entry->dir, entry->new_dir.buf, path, entry->new_dir.buf);
- } else {
- new_path = handle_path_level_conflicts(opt, path, entry,
- collisions, tree);
- *clean_merge &= (new_path != NULL);
- }
-
- return new_path;
-}
-
-static void apply_directory_rename_modifications(struct merge_options *opt,
- struct diff_filepair *pair,
- char *new_path,
- struct rename *re,
- struct tree *tree,
- struct tree *o_tree,
- struct tree *a_tree,
- struct tree *b_tree,
- struct string_list *entries)
-{
- struct string_list_item *item;
- int stage = (tree == a_tree ? 2 : 3);
- int update_wd;
-
- /*
- * In all cases where we can do directory rename detection,
- * unpack_trees() will have read pair->two->path into the
- * index and the working copy. We need to remove it so that
- * we can instead place it at new_path. It is guaranteed to
- * not be untracked (unpack_trees() would have errored out
- * saying the file would have been overwritten), but it might
- * be dirty, though.
- */
- update_wd = !was_dirty(opt, pair->two->path);
- if (!update_wd)
- output(opt, 1, _("Refusing to lose dirty file at %s"),
- pair->two->path);
- remove_file(opt, 1, pair->two->path, !update_wd);
-
- /* Find or create a new re->dst_entry */
- item = string_list_lookup(entries, new_path);
- if (item) {
- /*
- * Since we're renaming on this side of history, and it's
- * due to a directory rename on the other side of history
- * (which we only allow when the directory in question no
- * longer exists on the other side of history), the
- * original entry for re->dst_entry is no longer
- * necessary...
- */
- re->dst_entry->processed = 1;
-
- /*
- * ...because we'll be using this new one.
- */
- re->dst_entry = item->util;
- } else {
- /*
- * re->dst_entry is for the before-dir-rename path, and we
- * need it to hold information for the after-dir-rename
- * path. Before creating a new entry, we need to mark the
- * old one as unnecessary (...unless it is shared by
- * src_entry, i.e. this didn't use to be a rename, in which
- * case we can just allow the normal processing to happen
- * for it).
- */
- if (pair->status == 'R')
- re->dst_entry->processed = 1;
-
- re->dst_entry = insert_stage_data(opt->repo, new_path,
- o_tree, a_tree, b_tree,
- entries);
- item = string_list_insert(entries, new_path);
- item->util = re->dst_entry;
- }
-
- /*
- * Update the stage_data with the information about the path we are
- * moving into place. That slot will be empty and available for us
- * to write to because of the collision checks in
- * handle_path_level_conflicts(). In other words,
- * re->dst_entry->stages[stage].oid will be the null_oid, so it's
- * open for us to write to.
- *
- * It may be tempting to actually update the index at this point as
- * well, using update_stages_for_stage_data(), but as per the big
- * "NOTE" in update_stages(), doing so will modify the current
- * in-memory index which will break calls to would_lose_untracked()
- * that we need to make. Instead, we need to just make sure that
- * the various handle_rename_*() functions update the index
- * explicitly rather than relying on unpack_trees() to have done it.
- */
- get_tree_entry(opt->repo,
- &tree->object.oid,
- pair->two->path,
- &re->dst_entry->stages[stage].oid,
- &re->dst_entry->stages[stage].mode);
-
- /*
- * Record the original change status (or 'type' of change). If it
- * was originally an add ('A'), this lets us differentiate later
- * between a RENAME_DELETE conflict and RENAME_VIA_DIR (they
- * otherwise look the same). If it was originally a rename ('R'),
- * this lets us remember and report accurately about the transitive
- * renaming that occurred via the directory rename detection. Also,
- * record the original destination name.
- */
- re->dir_rename_original_type = pair->status;
- re->dir_rename_original_dest = pair->two->path;
-
- /*
- * We don't actually look at pair->status again, but it seems
- * pedagogically correct to adjust it.
- */
- pair->status = 'R';
-
- /*
- * Finally, record the new location.
- */
- pair->two->path = new_path;
-}
-
-/*
- * Get information of all renames which occurred in 'pairs', making use of
- * any implicit directory renames inferred from the other side of history.
- * We need the three trees in the merge ('o_tree', 'a_tree' and 'b_tree')
- * to be able to associate the correct cache entries with the rename
- * information; tree is always equal to either a_tree or b_tree.
- */
-static struct string_list *get_renames(struct merge_options *opt,
- const char *branch,
- struct diff_queue_struct *pairs,
- struct hashmap *dir_renames,
- struct hashmap *dir_rename_exclusions,
- struct tree *tree,
- struct tree *o_tree,
- struct tree *a_tree,
- struct tree *b_tree,
- struct string_list *entries,
- int *clean_merge)
-{
- int i;
- struct hashmap collisions;
- struct hashmap_iter iter;
- struct collision_entry *e;
- struct string_list *renames;
-
- compute_collisions(&collisions, dir_renames, pairs);
- CALLOC_ARRAY(renames, 1);
-
- for (i = 0; i < pairs->nr; ++i) {
- struct string_list_item *item;
- struct rename *re;
- struct diff_filepair *pair = pairs->queue[i];
- char *new_path; /* non-NULL only with directory renames */
-
- if (pair->status != 'A' && pair->status != 'R') {
- diff_free_filepair(pair);
- continue;
- }
- new_path = check_for_directory_rename(opt, pair->two->path, tree,
- dir_renames,
- dir_rename_exclusions,
- &collisions,
- clean_merge);
- if (pair->status != 'R' && !new_path) {
- diff_free_filepair(pair);
- continue;
- }
-
- re = xmalloc(sizeof(*re));
- re->processed = 0;
- re->pair = pair;
- re->branch = branch;
- re->dir_rename_original_type = '\0';
- re->dir_rename_original_dest = NULL;
- item = string_list_lookup(entries, re->pair->one->path);
- if (!item)
- re->src_entry = insert_stage_data(opt->repo,
- re->pair->one->path,
- o_tree, a_tree, b_tree, entries);
- else
- re->src_entry = item->util;
-
- item = string_list_lookup(entries, re->pair->two->path);
- if (!item)
- re->dst_entry = insert_stage_data(opt->repo,
- re->pair->two->path,
- o_tree, a_tree, b_tree, entries);
- else
- re->dst_entry = item->util;
- item = string_list_insert(renames, pair->one->path);
- item->util = re;
- if (new_path)
- apply_directory_rename_modifications(opt, pair, new_path,
- re, tree, o_tree,
- a_tree, b_tree,
- entries);
- }
-
- hashmap_for_each_entry(&collisions, &iter, e,
- ent /* member name */) {
- free(e->target_file);
- string_list_clear(&e->source_files, 0);
- }
- hashmap_clear_and_free(&collisions, struct collision_entry, ent);
- return renames;
-}
-
-static int process_renames(struct merge_options *opt,
- struct string_list *a_renames,
- struct string_list *b_renames)
-{
- int clean_merge = 1, i, j;
- struct string_list a_by_dst = STRING_LIST_INIT_NODUP;
- struct string_list b_by_dst = STRING_LIST_INIT_NODUP;
- const struct rename *sre;
-
- /*
- * Note that as we build the list, we do not need to check if the
- * existing destination path is already in the list, because the
- * structure of diffcore_rename guarantees we won't have duplicates.
- */
- for (i = 0; i < a_renames->nr; i++) {
- sre = a_renames->items[i].util;
- string_list_append(&a_by_dst, sre->pair->two->path)->util
- = (void *)sre;
- }
- for (i = 0; i < b_renames->nr; i++) {
- sre = b_renames->items[i].util;
- string_list_append(&b_by_dst, sre->pair->two->path)->util
- = (void *)sre;
- }
- string_list_sort(&a_by_dst);
- string_list_sort(&b_by_dst);
-
- for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) {
- struct string_list *renames1, *renames2Dst;
- struct rename *ren1 = NULL, *ren2 = NULL;
- const char *ren1_src, *ren1_dst;
- struct string_list_item *lookup;
-
- if (i >= a_renames->nr) {
- ren2 = b_renames->items[j++].util;
- } else if (j >= b_renames->nr) {
- ren1 = a_renames->items[i++].util;
- } else {
- int compare = strcmp(a_renames->items[i].string,
- b_renames->items[j].string);
- if (compare <= 0)
- ren1 = a_renames->items[i++].util;
- if (compare >= 0)
- ren2 = b_renames->items[j++].util;
- }
-
- /* TODO: refactor, so that 1/2 are not needed */
- if (ren1) {
- renames1 = a_renames;
- renames2Dst = &b_by_dst;
- } else {
- renames1 = b_renames;
- renames2Dst = &a_by_dst;
- SWAP(ren2, ren1);
- }
-
- if (ren1->processed)
- continue;
- ren1->processed = 1;
- ren1->dst_entry->processed = 1;
- /* BUG: We should only mark src_entry as processed if we
- * are not dealing with a rename + add-source case.
- */
- ren1->src_entry->processed = 1;
-
- ren1_src = ren1->pair->one->path;
- ren1_dst = ren1->pair->two->path;
-
- if (ren2) {
- /* One file renamed on both sides */
- const char *ren2_src = ren2->pair->one->path;
- const char *ren2_dst = ren2->pair->two->path;
- enum rename_type rename_type;
- if (strcmp(ren1_src, ren2_src) != 0)
- BUG("ren1_src != ren2_src");
- ren2->dst_entry->processed = 1;
- ren2->processed = 1;
- if (strcmp(ren1_dst, ren2_dst) != 0) {
- rename_type = RENAME_ONE_FILE_TO_TWO;
- clean_merge = 0;
- } else {
- rename_type = RENAME_ONE_FILE_TO_ONE;
- /* BUG: We should only remove ren1_src in
- * the base stage (think of rename +
- * add-source cases).
- */
- remove_file(opt, 1, ren1_src, 1);
- update_entry(ren1->dst_entry,
- ren1->pair->one,
- ren1->pair->two,
- ren2->pair->two);
- }
- setup_rename_conflict_info(rename_type, opt, ren1, ren2);
- } else if ((lookup = string_list_lookup(renames2Dst, ren1_dst))) {
- /* Two different files renamed to the same thing */
- char *ren2_dst;
- ren2 = lookup->util;
- ren2_dst = ren2->pair->two->path;
- if (strcmp(ren1_dst, ren2_dst) != 0)
- BUG("ren1_dst != ren2_dst");
-
- clean_merge = 0;
- ren2->processed = 1;
- /*
- * BUG: We should only mark src_entry as processed
- * if we are not dealing with a rename + add-source
- * case.
- */
- ren2->src_entry->processed = 1;
-
- setup_rename_conflict_info(RENAME_TWO_FILES_TO_ONE,
- opt, ren1, ren2);
- } else {
- /* Renamed in 1, maybe changed in 2 */
- /* we only use sha1 and mode of these */
- struct diff_filespec src_other, dst_other;
- int try_merge;
-
- /*
- * unpack_trees loads entries from common-commit
- * into stage 1, from head-commit into stage 2, and
- * from merge-commit into stage 3. We keep track
- * of which side corresponds to the rename.
- */
- int renamed_stage = a_renames == renames1 ? 2 : 3;
- int other_stage = a_renames == renames1 ? 3 : 2;
-
- /*
- * Directory renames have a funny corner case...
- */
- int renamed_to_self = !strcmp(ren1_src, ren1_dst);
-
- /* BUG: We should only remove ren1_src in the base
- * stage and in other_stage (think of rename +
- * add-source case).
- */
- if (!renamed_to_self)
- remove_file(opt, 1, ren1_src,
- renamed_stage == 2 ||
- !was_tracked(opt, ren1_src));
-
- oidcpy(&src_other.oid,
- &ren1->src_entry->stages[other_stage].oid);
- src_other.mode = ren1->src_entry->stages[other_stage].mode;
- oidcpy(&dst_other.oid,
- &ren1->dst_entry->stages[other_stage].oid);
- dst_other.mode = ren1->dst_entry->stages[other_stage].mode;
- try_merge = 0;
-
- if (oideq(&src_other.oid, null_oid()) &&
- ren1->dir_rename_original_type == 'A') {
- setup_rename_conflict_info(RENAME_VIA_DIR,
- opt, ren1, NULL);
- } else if (renamed_to_self) {
- setup_rename_conflict_info(RENAME_NORMAL,
- opt, ren1, NULL);
- } else if (oideq(&src_other.oid, null_oid())) {
- setup_rename_conflict_info(RENAME_DELETE,
- opt, ren1, NULL);
- } else if ((dst_other.mode == ren1->pair->two->mode) &&
- oideq(&dst_other.oid, &ren1->pair->two->oid)) {
- /*
- * Added file on the other side identical to
- * the file being renamed: clean merge.
- * Also, there is no need to overwrite the
- * file already in the working copy, so call
- * update_file_flags() instead of
- * update_file().
- */
- if (update_file_flags(opt,
- ren1->pair->two,
- ren1_dst,
- 1, /* update_cache */
- 0 /* update_wd */))
- clean_merge = -1;
- } else if (!oideq(&dst_other.oid, null_oid())) {
- /*
- * Probably not a clean merge, but it's
- * premature to set clean_merge to 0 here,
- * because if the rename merges cleanly and
- * the merge exactly matches the newly added
- * file, then the merge will be clean.
- */
- setup_rename_conflict_info(RENAME_ADD,
- opt, ren1, NULL);
- } else
- try_merge = 1;
-
- if (clean_merge < 0)
- goto cleanup_and_return;
- if (try_merge) {
- struct diff_filespec *o, *a, *b;
- src_other.path = (char *)ren1_src;
-
- o = ren1->pair->one;
- if (a_renames == renames1) {
- a = ren1->pair->two;
- b = &src_other;
- } else {
- b = ren1->pair->two;
- a = &src_other;
- }
- update_entry(ren1->dst_entry, o, a, b);
- setup_rename_conflict_info(RENAME_NORMAL,
- opt, ren1, NULL);
- }
- }
- }
-cleanup_and_return:
- string_list_clear(&a_by_dst, 0);
- string_list_clear(&b_by_dst, 0);
-
- return clean_merge;
-}
-
-struct rename_info {
- struct string_list *head_renames;
- struct string_list *merge_renames;
-};
-
-static void initial_cleanup_rename(struct diff_queue_struct *pairs,
- struct hashmap *dir_renames)
-{
- struct hashmap_iter iter;
- struct dir_rename_entry *e;
-
- hashmap_for_each_entry(dir_renames, &iter, e,
- ent /* member name */) {
- free(e->dir);
- strbuf_release(&e->new_dir);
- /* possible_new_dirs already cleared in get_directory_renames */
- }
- hashmap_clear_and_free(dir_renames, struct dir_rename_entry, ent);
- free(dir_renames);
-
- free(pairs->queue);
- free(pairs);
-}
-
-static int detect_and_process_renames(struct merge_options *opt,
- struct tree *common,
- struct tree *head,
- struct tree *merge,
- struct string_list *entries,
- struct rename_info *ri)
-{
- struct diff_queue_struct *head_pairs, *merge_pairs;
- struct hashmap *dir_re_head, *dir_re_merge;
- int clean = 1;
-
- ri->head_renames = NULL;
- ri->merge_renames = NULL;
-
- if (!merge_detect_rename(opt))
- return 1;
-
- head_pairs = get_diffpairs(opt, common, head);
- merge_pairs = get_diffpairs(opt, common, merge);
-
- if ((opt->detect_directory_renames == MERGE_DIRECTORY_RENAMES_TRUE) ||
- (opt->detect_directory_renames == MERGE_DIRECTORY_RENAMES_CONFLICT &&
- !opt->priv->call_depth)) {
- dir_re_head = get_directory_renames(head_pairs);
- dir_re_merge = get_directory_renames(merge_pairs);
-
- handle_directory_level_conflicts(opt,
- dir_re_head, head,
- dir_re_merge, merge);
- } else {
- dir_re_head = xmalloc(sizeof(*dir_re_head));
- dir_re_merge = xmalloc(sizeof(*dir_re_merge));
- dir_rename_init(dir_re_head);
- dir_rename_init(dir_re_merge);
- }
-
- ri->head_renames = get_renames(opt, opt->branch1, head_pairs,
- dir_re_merge, dir_re_head, head,
- common, head, merge, entries,
- &clean);
- if (clean < 0)
- goto cleanup;
- ri->merge_renames = get_renames(opt, opt->branch2, merge_pairs,
- dir_re_head, dir_re_merge, merge,
- common, head, merge, entries,
- &clean);
- if (clean < 0)
- goto cleanup;
- clean &= process_renames(opt, ri->head_renames, ri->merge_renames);
-
-cleanup:
- /*
- * Some cleanup is deferred until cleanup_renames() because the
- * data structures are still needed and referenced in
- * process_entry(). But there are a few things we can free now.
- */
- initial_cleanup_rename(head_pairs, dir_re_head);
- initial_cleanup_rename(merge_pairs, dir_re_merge);
-
- return clean;
-}
-
-static void final_cleanup_rename(struct string_list *rename)
-{
- const struct rename *re;
- int i;
-
- if (!rename)
- return;
-
- for (i = 0; i < rename->nr; i++) {
- re = rename->items[i].util;
- diff_free_filepair(re->pair);
- if (re->src_entry->rename_conflict_info_owned)
- FREE_AND_NULL(re->src_entry->rename_conflict_info);
- if (re->dst_entry->rename_conflict_info_owned)
- FREE_AND_NULL(re->dst_entry->rename_conflict_info);
- }
- string_list_clear(rename, 1);
- free(rename);
-}
-
-static void final_cleanup_renames(struct rename_info *re_info)
-{
- final_cleanup_rename(re_info->head_renames);
- final_cleanup_rename(re_info->merge_renames);
-}
-
-static int read_oid_strbuf(struct merge_options *opt,
- const struct object_id *oid,
- struct strbuf *dst)
-{
- void *buf;
- enum object_type type;
- unsigned long size;
- buf = repo_read_object_file(the_repository, oid, &type, &size);
- if (!buf)
- return err(opt, _("cannot read object %s"), oid_to_hex(oid));
- if (type != OBJ_BLOB) {
- free(buf);
- return err(opt, _("object %s is not a blob"), oid_to_hex(oid));
- }
- strbuf_attach(dst, buf, size, size + 1);
- return 0;
-}
-
-static int blob_unchanged(struct merge_options *opt,
- const struct diff_filespec *o,
- const struct diff_filespec *a,
- int renormalize, const char *path)
-{
- struct strbuf obuf = STRBUF_INIT;
- struct strbuf abuf = STRBUF_INIT;
- int ret = 0; /* assume changed for safety */
- struct index_state *idx = opt->repo->index;
-
- if (a->mode != o->mode)
- return 0;
- if (oideq(&o->oid, &a->oid))
- return 1;
- if (!renormalize)
- return 0;
-
- if (read_oid_strbuf(opt, &o->oid, &obuf) ||
- read_oid_strbuf(opt, &a->oid, &abuf))
- goto error_return;
- /*
- * Note: binary | is used so that both renormalizations are
- * performed. Comparison can be skipped if both files are
- * unchanged since their sha1s have already been compared.
- */
- if (renormalize_buffer(idx, path, obuf.buf, obuf.len, &obuf) |
- renormalize_buffer(idx, path, abuf.buf, abuf.len, &abuf))
- ret = (obuf.len == abuf.len && !memcmp(obuf.buf, abuf.buf, obuf.len));
-
-error_return:
- strbuf_release(&obuf);
- strbuf_release(&abuf);
- return ret;
-}
-
-static int handle_modify_delete(struct merge_options *opt,
- const char *path,
- const struct diff_filespec *o,
- const struct diff_filespec *a,
- const struct diff_filespec *b)
-{
- const char *modify_branch, *delete_branch;
- const struct diff_filespec *changed;
-
- if (is_valid(a)) {
- modify_branch = opt->branch1;
- delete_branch = opt->branch2;
- changed = a;
- } else {
- modify_branch = opt->branch2;
- delete_branch = opt->branch1;
- changed = b;
- }
-
- return handle_change_delete(opt,
- path, NULL,
- o, changed,
- modify_branch, delete_branch,
- _("modify"), _("modified"));
-}
-
-static int handle_content_merge(struct merge_file_info *mfi,
- struct merge_options *opt,
- const char *path,
- int is_dirty,
- const struct diff_filespec *o,
- const struct diff_filespec *a,
- const struct diff_filespec *b,
- struct rename_conflict_info *ci)
-{
- const char *reason = _("content");
- unsigned df_conflict_remains = 0;
-
- if (!is_valid(o))
- reason = _("add/add");
-
- assert(o->path && a->path && b->path);
- if (ci && dir_in_way(opt->repo->index, path, !opt->priv->call_depth,
- S_ISGITLINK(ci->ren1->pair->two->mode)))
- df_conflict_remains = 1;
-
- if (merge_mode_and_contents(opt, o, a, b, path,
- opt->branch1, opt->branch2,
- opt->priv->call_depth * 2, mfi))
- return -1;
-
- /*
- * We can skip updating the working tree file iff:
- * a) The merge is clean
- * b) The merge matches what was in HEAD (content, mode, pathname)
- * c) The target path is usable (i.e. not involved in D/F conflict)
- */
- if (mfi->clean && was_tracked_and_matches(opt, path, &mfi->blob) &&
- !df_conflict_remains) {
- int pos;
- struct cache_entry *ce;
-
- output(opt, 3, _("Skipped %s (merged same as existing)"), path);
- if (add_cacheinfo(opt, &mfi->blob, path,
- 0, (!opt->priv->call_depth && !is_dirty), 0))
- return -1;
- /*
- * However, add_cacheinfo() will delete the old cache entry
- * and add a new one. We need to copy over any skip_worktree
- * flag to avoid making the file appear as if it were
- * deleted by the user.
- */
- pos = index_name_pos(&opt->priv->orig_index, path, strlen(path));
- ce = opt->priv->orig_index.cache[pos];
- if (ce_skip_worktree(ce)) {
- pos = index_name_pos(opt->repo->index, path, strlen(path));
- ce = opt->repo->index->cache[pos];
- ce->ce_flags |= CE_SKIP_WORKTREE;
- }
- return mfi->clean;
- }
-
- if (!mfi->clean) {
- if (S_ISGITLINK(mfi->blob.mode))
- reason = _("submodule");
- output(opt, 1, _("CONFLICT (%s): Merge conflict in %s"),
- reason, path);
- if (ci && !df_conflict_remains)
- if (update_stages(opt, path, o, a, b))
- return -1;
- }
-
- if (df_conflict_remains || is_dirty) {
- char *new_path;
- if (opt->priv->call_depth) {
- remove_file_from_index(opt->repo->index, path);
- } else {
- if (!mfi->clean) {
- if (update_stages(opt, path, o, a, b))
- return -1;
- } else {
- int file_from_stage2 = was_tracked(opt, path);
-
- if (update_stages(opt, path, NULL,
- file_from_stage2 ? &mfi->blob : NULL,
- file_from_stage2 ? NULL : &mfi->blob))
- return -1;
- }
-
- }
- new_path = unique_path(opt, path, ci->ren1->branch);
- if (is_dirty) {
- output(opt, 1, _("Refusing to lose dirty file at %s"),
- path);
- }
- output(opt, 1, _("Adding as %s instead"), new_path);
- if (update_file(opt, 0, &mfi->blob, new_path)) {
- free(new_path);
- return -1;
- }
- free(new_path);
- mfi->clean = 0;
- } else if (update_file(opt, mfi->clean, &mfi->blob, path))
- return -1;
- return !is_dirty && mfi->clean;
-}
-
-static int handle_rename_normal(struct merge_options *opt,
- const char *path,
- const struct diff_filespec *o,
- const struct diff_filespec *a,
- const struct diff_filespec *b,
- struct rename_conflict_info *ci)
-{
- struct rename *ren = ci->ren1;
- struct merge_file_info mfi;
- int clean;
-
- /* Merge the content and write it out */
- clean = handle_content_merge(&mfi, opt, path, was_dirty(opt, path),
- o, a, b, ci);
-
- if (clean &&
- opt->detect_directory_renames == MERGE_DIRECTORY_RENAMES_CONFLICT &&
- ren->dir_rename_original_dest) {
- if (update_stages(opt, path,
- &mfi.blob, &mfi.blob, &mfi.blob))
- return -1;
- clean = 0; /* not clean, but conflicted */
- }
- return clean;
-}
-
-static void dir_rename_warning(const char *msg,
- int is_add,
- int clean,
- struct merge_options *opt,
- struct rename *ren)
-{
- const char *other_branch;
- other_branch = (ren->branch == opt->branch1 ?
- opt->branch2 : opt->branch1);
- if (is_add) {
- output(opt, clean ? 2 : 1, msg,
- ren->pair->one->path, ren->branch,
- other_branch, ren->pair->two->path);
- return;
- }
- output(opt, clean ? 2 : 1, msg,
- ren->pair->one->path, ren->dir_rename_original_dest, ren->branch,
- other_branch, ren->pair->two->path);
-}
-static int warn_about_dir_renamed_entries(struct merge_options *opt,
- struct rename *ren)
-{
- const char *msg;
- int clean = 1, is_add;
-
- if (!ren)
- return clean;
-
- /* Return early if ren was not affected/created by a directory rename */
- if (!ren->dir_rename_original_dest)
- return clean;
-
- /* Sanity checks */
- assert(opt->detect_directory_renames > MERGE_DIRECTORY_RENAMES_NONE);
- assert(ren->dir_rename_original_type == 'A' ||
- ren->dir_rename_original_type == 'R');
-
- /* Check whether to treat directory renames as a conflict */
- clean = (opt->detect_directory_renames == MERGE_DIRECTORY_RENAMES_TRUE);
-
- is_add = (ren->dir_rename_original_type == 'A');
- if (ren->dir_rename_original_type == 'A' && clean) {
- msg = _("Path updated: %s added in %s inside a "
- "directory that was renamed in %s; moving it to %s.");
- } else if (ren->dir_rename_original_type == 'A' && !clean) {
- msg = _("CONFLICT (file location): %s added in %s "
- "inside a directory that was renamed in %s, "
- "suggesting it should perhaps be moved to %s.");
- } else if (ren->dir_rename_original_type == 'R' && clean) {
- msg = _("Path updated: %s renamed to %s in %s, inside a "
- "directory that was renamed in %s; moving it to %s.");
- } else if (ren->dir_rename_original_type == 'R' && !clean) {
- msg = _("CONFLICT (file location): %s renamed to %s in %s, "
- "inside a directory that was renamed in %s, "
- "suggesting it should perhaps be moved to %s.");
- } else {
- BUG("Impossible dir_rename_original_type/clean combination");
- }
- dir_rename_warning(msg, is_add, clean, opt, ren);
-
- return clean;
-}
-
-/* Per entry merge function */
-static int process_entry(struct merge_options *opt,
- const char *path, struct stage_data *entry)
-{
- int clean_merge = 1;
- int normalize = opt->renormalize;
-
- struct diff_filespec *o = &entry->stages[1];
- struct diff_filespec *a = &entry->stages[2];
- struct diff_filespec *b = &entry->stages[3];
- int o_valid = is_valid(o);
- int a_valid = is_valid(a);
- int b_valid = is_valid(b);
- o->path = a->path = b->path = (char*)path;
-
- entry->processed = 1;
- if (entry->rename_conflict_info) {
- struct rename_conflict_info *ci = entry->rename_conflict_info;
- struct diff_filespec *temp;
- int path_clean;
-
- path_clean = warn_about_dir_renamed_entries(opt, ci->ren1);
- path_clean &= warn_about_dir_renamed_entries(opt, ci->ren2);
-
- /*
- * For cases with a single rename, {o,a,b}->path have all been
- * set to the rename target path; we need to set two of these
- * back to the rename source.
- * For rename/rename conflicts, we'll manually fix paths below.
- */
- temp = (opt->branch1 == ci->ren1->branch) ? b : a;
- o->path = temp->path = ci->ren1->pair->one->path;
- if (ci->ren2) {
- assert(opt->branch1 == ci->ren1->branch);
- }
-
- switch (ci->rename_type) {
- case RENAME_NORMAL:
- case RENAME_ONE_FILE_TO_ONE:
- clean_merge = handle_rename_normal(opt, path, o, a, b,
- ci);
- break;
- case RENAME_VIA_DIR:
- clean_merge = handle_rename_via_dir(opt, ci);
- break;
- case RENAME_ADD:
- /*
- * Probably unclean merge, but if the renamed file
- * merges cleanly and the result can then be
- * two-way merged cleanly with the added file, I
- * guess it's a clean merge?
- */
- clean_merge = handle_rename_add(opt, ci);
- break;
- case RENAME_DELETE:
- clean_merge = 0;
- if (handle_rename_delete(opt, ci))
- clean_merge = -1;
- break;
- case RENAME_ONE_FILE_TO_TWO:
- /*
- * Manually fix up paths; note:
- * ren[12]->pair->one->path are equal.
- */
- o->path = ci->ren1->pair->one->path;
- a->path = ci->ren1->pair->two->path;
- b->path = ci->ren2->pair->two->path;
-
- clean_merge = 0;
- if (handle_rename_rename_1to2(opt, ci))
- clean_merge = -1;
- break;
- case RENAME_TWO_FILES_TO_ONE:
- /*
- * Manually fix up paths; note,
- * ren[12]->pair->two->path are actually equal.
- */
- o->path = NULL;
- a->path = ci->ren1->pair->two->path;
- b->path = ci->ren2->pair->two->path;
-
- /*
- * Probably unclean merge, but if the two renamed
- * files merge cleanly and the two resulting files
- * can then be two-way merged cleanly, I guess it's
- * a clean merge?
- */
- clean_merge = handle_rename_rename_2to1(opt, ci);
- break;
- default:
- entry->processed = 0;
- break;
- }
- if (path_clean < clean_merge)
- clean_merge = path_clean;
- } else if (o_valid && (!a_valid || !b_valid)) {
- /* Case A: Deleted in one */
- if ((!a_valid && !b_valid) ||
- (!b_valid && blob_unchanged(opt, o, a, normalize, path)) ||
- (!a_valid && blob_unchanged(opt, o, b, normalize, path))) {
- /* Deleted in both or deleted in one and
- * unchanged in the other */
- if (a_valid)
- output(opt, 2, _("Removing %s"), path);
- /* do not touch working file if it did not exist */
- remove_file(opt, 1, path, !a_valid);
- } else {
- /* Modify/delete; deleted side may have put a directory in the way */
- clean_merge = 0;
- if (handle_modify_delete(opt, path, o, a, b))
- clean_merge = -1;
- }
- } else if ((!o_valid && a_valid && !b_valid) ||
- (!o_valid && !a_valid && b_valid)) {
- /* Case B: Added in one. */
- /* [nothing|directory] -> ([nothing|directory], file) */
-
- const char *add_branch;
- const char *other_branch;
- const char *conf;
- const struct diff_filespec *contents;
-
- if (a_valid) {
- add_branch = opt->branch1;
- other_branch = opt->branch2;
- contents = a;
- conf = _("file/directory");
- } else {
- add_branch = opt->branch2;
- other_branch = opt->branch1;
- contents = b;
- conf = _("directory/file");
- }
- if (dir_in_way(opt->repo->index, path,
- !opt->priv->call_depth && !S_ISGITLINK(a->mode),
- 0)) {
- char *new_path = unique_path(opt, path, add_branch);
- clean_merge = 0;
- output(opt, 1, _("CONFLICT (%s): There is a directory with name %s in %s. "
- "Adding %s as %s"),
- conf, path, other_branch, path, new_path);
- if (update_file(opt, 0, contents, new_path))
- clean_merge = -1;
- else if (opt->priv->call_depth)
- remove_file_from_index(opt->repo->index, path);
- free(new_path);
- } else {
- output(opt, 2, _("Adding %s"), path);
- /* do not overwrite file if already present */
- if (update_file_flags(opt, contents, path, 1, !a_valid))
- clean_merge = -1;
- }
- } else if (a_valid && b_valid) {
- if (!o_valid) {
- /* Case C: Added in both (check for same permissions) */
- output(opt, 1,
- _("CONFLICT (add/add): Merge conflict in %s"),
- path);
- clean_merge = handle_file_collision(opt,
- path, NULL, NULL,
- opt->branch1,
- opt->branch2,
- a, b);
- } else {
- /* case D: Modified in both, but differently. */
- struct merge_file_info mfi;
- int is_dirty = 0; /* unpack_trees would have bailed if dirty */
- clean_merge = handle_content_merge(&mfi, opt, path,
- is_dirty,
- o, a, b, NULL);
- }
- } else if (!o_valid && !a_valid && !b_valid) {
- /*
- * this entry was deleted altogether. a_mode == 0 means
- * we had that path and want to actively remove it.
- */
- remove_file(opt, 1, path, !a->mode);
- } else
- BUG("fatal merge failure, shouldn't happen.");
-
- return clean_merge;
-}
-
-static int merge_trees_internal(struct merge_options *opt,
- struct tree *head,
- struct tree *merge,
- struct tree *merge_base,
- struct tree **result)
-{
- struct index_state *istate = opt->repo->index;
- int code, clean;
-
- if (opt->subtree_shift) {
- merge = shift_tree_object(opt->repo, head, merge,
- opt->subtree_shift);
- merge_base = shift_tree_object(opt->repo, head, merge_base,
- opt->subtree_shift);
- }
-
- if (oideq(&merge_base->object.oid, &merge->object.oid)) {
- output(opt, 0, _("Already up to date."));
- *result = head;
- return 1;
- }
-
- code = unpack_trees_start(opt, merge_base, head, merge);
-
- if (code != 0) {
- if (show(opt, 4) || opt->priv->call_depth)
- err(opt, _("merging of trees %s and %s failed"),
- oid_to_hex(&head->object.oid),
- oid_to_hex(&merge->object.oid));
- unpack_trees_finish(opt);
- return -1;
- }
-
- if (unmerged_index(istate)) {
- struct string_list *entries;
- struct rename_info re_info;
- int i;
- /*
- * Only need the hashmap while processing entries, so
- * initialize it here and free it when we are done running
- * through the entries. Keeping it in the merge_options as
- * opposed to decaring a local hashmap is for convenience
- * so that we don't have to pass it to around.
- */
- hashmap_init(&opt->priv->current_file_dir_set, path_hashmap_cmp,
- NULL, 512);
- get_files_dirs(opt, head);
- get_files_dirs(opt, merge);
-
- entries = get_unmerged(opt->repo->index);
- clean = detect_and_process_renames(opt, merge_base, head, merge,
- entries, &re_info);
- record_df_conflict_files(opt, entries);
- if (clean < 0)
- goto cleanup;
- for (i = entries->nr-1; 0 <= i; i--) {
- const char *path = entries->items[i].string;
- struct stage_data *e = entries->items[i].util;
- if (!e->processed) {
- int ret = process_entry(opt, path, e);
- if (!ret)
- clean = 0;
- else if (ret < 0) {
- clean = ret;
- goto cleanup;
- }
- }
- }
- for (i = 0; i < entries->nr; i++) {
- struct stage_data *e = entries->items[i].util;
- if (!e->processed)
- BUG("unprocessed path??? %s",
- entries->items[i].string);
- }
-
- cleanup:
- final_cleanup_renames(&re_info);
-
- string_list_clear(entries, 1);
- free(entries);
-
- hashmap_clear_and_free(&opt->priv->current_file_dir_set,
- struct path_hashmap_entry, e);
-
- if (clean < 0) {
- unpack_trees_finish(opt);
- return clean;
- }
- }
- else
- clean = 1;
-
- unpack_trees_finish(opt);
-
- if (opt->priv->call_depth &&
- !(*result = write_in_core_index_as_tree(opt->repo)))
- return -1;
-
- return clean;
-}
-
-/*
- * Merge the commits h1 and h2, returning a flag (int) indicating the
- * cleanness of the merge. Also, if opt->priv->call_depth, create a
- * virtual commit and write its location to *result.
- */
-static int merge_recursive_internal(struct merge_options *opt,
- struct commit *h1,
- struct commit *h2,
- const struct commit_list *_merge_bases,
- struct commit **result)
-{
- struct commit_list *merge_bases = copy_commit_list(_merge_bases);
- struct commit_list *iter;
- struct commit *merged_merge_bases;
- struct tree *result_tree;
- const char *ancestor_name;
- struct strbuf merge_base_abbrev = STRBUF_INIT;
- int ret;
-
- if (show(opt, 4)) {
- output(opt, 4, _("Merging:"));
- output_commit_title(opt, h1);
- output_commit_title(opt, h2);
- }
-
- if (!merge_bases) {
- if (repo_get_merge_bases(the_repository, h1, h2,
- &merge_bases) < 0) {
- ret = -1;
- goto out;
- }
- merge_bases = reverse_commit_list(merge_bases);
- }
-
- if (show(opt, 5)) {
- unsigned cnt = commit_list_count(merge_bases);
-
- output(opt, 5, Q_("found %u common ancestor:",
- "found %u common ancestors:", cnt), cnt);
- for (iter = merge_bases; iter; iter = iter->next)
- output_commit_title(opt, iter->item);
- }
-
- merged_merge_bases = pop_commit(&merge_bases);
- if (!merged_merge_bases) {
- /* if there is no common ancestor, use an empty tree */
- struct tree *tree;
-
- tree = lookup_tree(opt->repo, opt->repo->hash_algo->empty_tree);
- merged_merge_bases = make_virtual_commit(opt->repo, tree,
- "ancestor");
- ancestor_name = "empty tree";
- } else if (opt->ancestor && !opt->priv->call_depth) {
- ancestor_name = opt->ancestor;
- } else if (merge_bases) {
- ancestor_name = "merged common ancestors";
- } else {
- strbuf_add_unique_abbrev(&merge_base_abbrev,
- &merged_merge_bases->object.oid,
- DEFAULT_ABBREV);
- ancestor_name = merge_base_abbrev.buf;
- }
-
- for (iter = merge_bases; iter; iter = iter->next) {
- const char *saved_b1, *saved_b2;
- opt->priv->call_depth++;
- /*
- * When the merge fails, the result contains files
- * with conflict markers. The cleanness flag is
- * ignored (unless indicating an error), it was never
- * actually used, as result of merge_trees has always
- * overwritten it: the committed "conflicts" were
- * already resolved.
- */
- discard_index(opt->repo->index);
- saved_b1 = opt->branch1;
- saved_b2 = opt->branch2;
- opt->branch1 = "Temporary merge branch 1";
- opt->branch2 = "Temporary merge branch 2";
- if (merge_recursive_internal(opt, merged_merge_bases, iter->item,
- NULL, &merged_merge_bases) < 0) {
- ret = -1;
- goto out;
- }
- opt->branch1 = saved_b1;
- opt->branch2 = saved_b2;
- opt->priv->call_depth--;
-
- if (!merged_merge_bases) {
- ret = err(opt, _("merge returned no commit"));
- goto out;
- }
- }
-
- /*
- * FIXME: Since merge_recursive_internal() is only ever called by
- * places that ensure the index is loaded first
- * (e.g. builtin/merge.c, rebase/sequencer, etc.), in the common
- * case where the merge base was unique that means when we get here
- * we immediately discard the index and re-read it, which is a
- * complete waste of time. We should only be discarding and
- * re-reading if we were forced to recurse.
- */
- discard_index(opt->repo->index);
- if (!opt->priv->call_depth)
- repo_read_index(opt->repo);
-
- opt->ancestor = ancestor_name;
- ret = merge_trees_internal(opt,
- repo_get_commit_tree(opt->repo, h1),
- repo_get_commit_tree(opt->repo, h2),
- repo_get_commit_tree(opt->repo,
- merged_merge_bases),
- &result_tree);
- opt->ancestor = NULL; /* avoid accidental re-use of opt->ancestor */
- if (ret < 0) {
- flush_output(opt);
- goto out;
- }
-
- if (opt->priv->call_depth) {
- *result = make_virtual_commit(opt->repo, result_tree,
- "merged tree");
- commit_list_insert(h1, &(*result)->parents);
- commit_list_insert(h2, &(*result)->parents->next);
- }
-
-out:
- strbuf_release(&merge_base_abbrev);
- free_commit_list(merge_bases);
- return ret;
-}
-
-static int merge_start(struct merge_options *opt, struct tree *head)
-{
- struct strbuf sb = STRBUF_INIT;
-
- /* Sanity checks on opt */
- assert(opt->repo);
-
- assert(opt->branch1 && opt->branch2);
-
- assert(opt->detect_renames >= -1 &&
- opt->detect_renames <= DIFF_DETECT_COPY);
- assert(opt->detect_directory_renames >= MERGE_DIRECTORY_RENAMES_NONE &&
- opt->detect_directory_renames <= MERGE_DIRECTORY_RENAMES_TRUE);
- assert(opt->rename_limit >= -1);
- assert(opt->rename_score >= 0 && opt->rename_score <= MAX_SCORE);
- assert(opt->show_rename_progress >= 0 && opt->show_rename_progress <= 1);
-
- assert(opt->xdl_opts >= 0);
- assert(opt->recursive_variant >= MERGE_VARIANT_NORMAL &&
- opt->recursive_variant <= MERGE_VARIANT_THEIRS);
-
- assert(opt->verbosity >= 0 && opt->verbosity <= 5);
- assert(opt->buffer_output <= 2);
- assert(opt->obuf.len == 0);
-
- assert(opt->priv == NULL);
-
- /* Not supported; option specific to merge-ort */
- assert(!opt->record_conflict_msgs_as_headers);
- assert(!opt->msg_header_prefix);
-
- /* Sanity check on repo state; index must match head */
- if (repo_index_has_changes(opt->repo, head, &sb)) {
- err(opt, _("Your local changes to the following files would be overwritten by merge:\n %s"),
- sb.buf);
- strbuf_release(&sb);
- return -1;
- }
-
- CALLOC_ARRAY(opt->priv, 1);
- string_list_init_dup(&opt->priv->df_conflict_file_set);
- return 0;
-}
-
-static void merge_finalize(struct merge_options *opt)
-{
- flush_output(opt);
- if (!opt->priv->call_depth && opt->buffer_output < 2)
- strbuf_release(&opt->obuf);
- if (show(opt, 2))
- diff_warn_rename_limit("merge.renamelimit",
- opt->priv->needed_rename_limit, 0);
- hashmap_clear_and_free(&opt->priv->current_file_dir_set,
- struct path_hashmap_entry, e);
- string_list_clear(&opt->priv->df_conflict_file_set, 0);
- FREE_AND_NULL(opt->priv);
-}
-
-int merge_trees(struct merge_options *opt,
- struct tree *head,
- struct tree *merge,
- struct tree *merge_base)
-{
- int clean;
- struct tree *ignored;
-
- assert(opt->ancestor != NULL);
-
- if (merge_start(opt, head))
- return -1;
- clean = merge_trees_internal(opt, head, merge, merge_base, &ignored);
- merge_finalize(opt);
-
- return clean;
-}
-
-int merge_recursive(struct merge_options *opt,
- struct commit *h1,
- struct commit *h2,
- const struct commit_list *merge_bases,
- struct commit **result)
-{
- int clean;
-
- assert(opt->ancestor == NULL ||
- !strcmp(opt->ancestor, "constructed merge base"));
-
- prepare_repo_settings(opt->repo);
- opt->repo->settings.command_requires_full_index = 1;
-
- if (merge_start(opt, repo_get_commit_tree(opt->repo, h1)))
- return -1;
- clean = merge_recursive_internal(opt, h1, h2, merge_bases, result);
- merge_finalize(opt);
-
- return clean;
-}
-
-static struct commit *get_ref(struct repository *repo,
- const struct object_id *oid,
- const char *name)
-{
- struct object *object;
-
- object = deref_tag(repo, parse_object(repo, oid),
- name, strlen(name));
- if (!object)
- return NULL;
- if (object->type == OBJ_TREE)
- return make_virtual_commit(repo, (struct tree*)object, name);
- if (object->type != OBJ_COMMIT)
- return NULL;
- if (repo_parse_commit(repo, (struct commit *)object))
- return NULL;
- return (struct commit *)object;
-}
-
-int merge_recursive_generic(struct merge_options *opt,
- const struct object_id *head,
- const struct object_id *merge,
- int num_merge_bases,
- const struct object_id *merge_bases,
- struct commit **result)
-{
- int clean;
- struct lock_file lock = LOCK_INIT;
- struct commit *head_commit = get_ref(opt->repo, head, opt->branch1);
- struct commit *next_commit = get_ref(opt->repo, merge, opt->branch2);
- struct commit_list *ca = NULL;
-
- if (merge_bases) {
- int i;
- for (i = 0; i < num_merge_bases; ++i) {
- struct commit *base;
- if (!(base = get_ref(opt->repo, &merge_bases[i],
- oid_to_hex(&merge_bases[i]))))
- return err(opt, _("Could not parse object '%s'"),
- oid_to_hex(&merge_bases[i]));
- commit_list_insert(base, &ca);
- }
- if (num_merge_bases == 1)
- opt->ancestor = "constructed merge base";
- }
-
- repo_hold_locked_index(opt->repo, &lock, LOCK_DIE_ON_ERROR);
- clean = merge_recursive(opt, head_commit, next_commit, ca,
- result);
- free_commit_list(ca);
- if (clean < 0) {
- rollback_lock_file(&lock);
- return clean;
- }
-
- if (write_locked_index(opt->repo->index, &lock,
- COMMIT_LOCK | SKIP_IF_UNCHANGED))
- return err(opt, _("Unable to write index."));
-
- return clean ? 0 : 1;
-}
-
-static void merge_recursive_config(struct merge_options *opt, int ui)
-{
- char *value = NULL;
- int renormalize = 0;
- git_config_get_int("merge.verbosity", &opt->verbosity);
- git_config_get_int("diff.renamelimit", &opt->rename_limit);
- git_config_get_int("merge.renamelimit", &opt->rename_limit);
- git_config_get_bool("merge.renormalize", &renormalize);
- opt->renormalize = renormalize;
- if (!git_config_get_string("diff.renames", &value)) {
- opt->detect_renames = git_config_rename("diff.renames", value);
- free(value);
- }
- if (!git_config_get_string("merge.renames", &value)) {
- opt->detect_renames = git_config_rename("merge.renames", value);
- free(value);
- }
- if (!git_config_get_string("merge.directoryrenames", &value)) {
- int boolval = git_parse_maybe_bool(value);
- if (0 <= boolval) {
- opt->detect_directory_renames = boolval ?
- MERGE_DIRECTORY_RENAMES_TRUE :
- MERGE_DIRECTORY_RENAMES_NONE;
- } else if (!strcasecmp(value, "conflict")) {
- opt->detect_directory_renames =
- MERGE_DIRECTORY_RENAMES_CONFLICT;
- } /* avoid erroring on values from future versions of git */
- free(value);
- }
- if (ui) {
- if (!git_config_get_string("diff.algorithm", &value)) {
- long diff_algorithm = parse_algorithm_value(value);
- if (diff_algorithm < 0)
- die(_("unknown value for config '%s': %s"), "diff.algorithm", value);
- opt->xdl_opts = (opt->xdl_opts & ~XDF_DIFF_ALGORITHM_MASK) | diff_algorithm;
- free(value);
- }
- }
- git_config(git_xmerge_config, NULL);
-}
-
-static void init_merge_options(struct merge_options *opt,
- struct repository *repo, int ui)
-{
- const char *merge_verbosity;
- memset(opt, 0, sizeof(struct merge_options));
-
- opt->repo = repo;
-
- opt->detect_renames = -1;
- opt->detect_directory_renames = MERGE_DIRECTORY_RENAMES_CONFLICT;
- opt->rename_limit = -1;
-
- opt->verbosity = 2;
- opt->buffer_output = 1;
- strbuf_init(&opt->obuf, 0);
-
- opt->renormalize = 0;
-
- opt->conflict_style = -1;
-
- merge_recursive_config(opt, ui);
- merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
- if (merge_verbosity)
- opt->verbosity = strtol(merge_verbosity, NULL, 10);
- if (opt->verbosity >= 5)
- opt->buffer_output = 0;
-}
-
-void init_ui_merge_options(struct merge_options *opt,
- struct repository *repo)
-{
- init_merge_options(opt, repo, 1);
-}
-
-void init_basic_merge_options(struct merge_options *opt,
- struct repository *repo)
-{
- init_merge_options(opt, repo, 0);
-}
-
-/*
- * For now, members of merge_options do not need deep copying, but
- * it may change in the future, in which case we would need to update
- * this, and also make a matching change to clear_merge_options() to
- * release the resources held by a copied instance.
- */
-void copy_merge_options(struct merge_options *dst, struct merge_options *src)
-{
- *dst = *src;
-}
-
-void clear_merge_options(struct merge_options *opt UNUSED)
-{
- ; /* no-op as our copy is shallow right now */
-}
-
-int parse_merge_opt(struct merge_options *opt, const char *s)
-{
- const char *arg;
-
- if (!s || !*s)
- return -1;
- if (!strcmp(s, "ours"))
- opt->recursive_variant = MERGE_VARIANT_OURS;
- else if (!strcmp(s, "theirs"))
- opt->recursive_variant = MERGE_VARIANT_THEIRS;
- else if (!strcmp(s, "subtree"))
- opt->subtree_shift = "";
- else if (skip_prefix(s, "subtree=", &arg))
- opt->subtree_shift = arg;
- else if (!strcmp(s, "patience"))
- opt->xdl_opts = DIFF_WITH_ALG(opt, PATIENCE_DIFF);
- else if (!strcmp(s, "histogram"))
- opt->xdl_opts = DIFF_WITH_ALG(opt, HISTOGRAM_DIFF);
- else if (skip_prefix(s, "diff-algorithm=", &arg)) {
- long value = parse_algorithm_value(arg);
- if (value < 0)
- return -1;
- /* clear out previous settings */
- DIFF_XDL_CLR(opt, NEED_MINIMAL);
- opt->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
- opt->xdl_opts |= value;
- }
- else if (!strcmp(s, "ignore-space-change"))
- DIFF_XDL_SET(opt, IGNORE_WHITESPACE_CHANGE);
- else if (!strcmp(s, "ignore-all-space"))
- DIFF_XDL_SET(opt, IGNORE_WHITESPACE);
- else if (!strcmp(s, "ignore-space-at-eol"))
- DIFF_XDL_SET(opt, IGNORE_WHITESPACE_AT_EOL);
- else if (!strcmp(s, "ignore-cr-at-eol"))
- DIFF_XDL_SET(opt, IGNORE_CR_AT_EOL);
- else if (!strcmp(s, "renormalize"))
- opt->renormalize = 1;
- else if (!strcmp(s, "no-renormalize"))
- opt->renormalize = 0;
- else if (!strcmp(s, "no-renames"))
- opt->detect_renames = 0;
- else if (!strcmp(s, "find-renames")) {
- opt->detect_renames = 1;
- opt->rename_score = 0;
- }
- else if (skip_prefix(s, "find-renames=", &arg) ||
- skip_prefix(s, "rename-threshold=", &arg)) {
- if ((opt->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0)
- return -1;
- opt->detect_renames = 1;
- }
- /*
- * Please update $__git_merge_strategy_options in
- * git-completion.bash when you add new options
- */
- else
- return -1;
- return 0;
-}
diff --git a/merge-recursive.h b/merge-recursive.h
deleted file mode 100644
index 0b91f28f90..0000000000
--- a/merge-recursive.h
+++ /dev/null
@@ -1,132 +0,0 @@
-#ifndef MERGE_RECURSIVE_H
-#define MERGE_RECURSIVE_H
-
-#include "strbuf.h"
-
-struct commit;
-struct commit_list;
-struct object_id;
-struct repository;
-struct tree;
-
-struct merge_options_internal;
-struct merge_options {
- struct repository *repo;
-
- /* ref names used in console messages and conflict markers */
- const char *ancestor;
- const char *branch1;
- const char *branch2;
-
- /* rename related options */
- int detect_renames;
- enum {
- MERGE_DIRECTORY_RENAMES_NONE = 0,
- MERGE_DIRECTORY_RENAMES_CONFLICT = 1,
- MERGE_DIRECTORY_RENAMES_TRUE = 2
- } detect_directory_renames;
- int rename_limit;
- int rename_score;
- int show_rename_progress;
-
- /* xdiff-related options (patience, ignore whitespace, ours/theirs) */
- long xdl_opts;
- int conflict_style;
- enum {
- MERGE_VARIANT_NORMAL = 0,
- MERGE_VARIANT_OURS,
- MERGE_VARIANT_THEIRS
- } recursive_variant;
-
- /* console output related options */
- int verbosity;
- unsigned buffer_output; /* 1: output at end, 2: keep buffered */
- struct strbuf obuf; /* output buffer; if buffer_output == 2, caller
- * must handle and call strbuf_release */
-
- /* miscellaneous control options */
- const char *subtree_shift;
- unsigned renormalize : 1;
- unsigned record_conflict_msgs_as_headers : 1;
- const char *msg_header_prefix;
-
- /* internal fields used by the implementation */
- struct merge_options_internal *priv;
-};
-
-/* for use by porcelain commands */
-void init_ui_merge_options(struct merge_options *opt, struct repository *repo);
-/* for use by plumbing commands */
-void init_basic_merge_options(struct merge_options *opt, struct repository *repo);
-
-void copy_merge_options(struct merge_options *dst, struct merge_options *src);
-void clear_merge_options(struct merge_options *opt);
-
-/* parse the option in s and update the relevant field of opt */
-int parse_merge_opt(struct merge_options *opt, const char *s);
-
-/*
- * RETURN VALUES: All the merge_* functions below return a value as follows:
- * > 0 Merge was clean
- * = 0 Merge had conflicts
- * < 0 Merge hit an unexpected and unrecoverable problem (e.g. disk
- * full) and aborted merge part-way through.
- */
-
-/*
- * rename-detecting three-way merge, no recursion.
- *
- * Outputs:
- * - See RETURN VALUES above
- * - opt->repo->index has the new index
- * - new index NOT written to disk
- * - The working tree is updated with results of the merge
- */
-int merge_trees(struct merge_options *opt,
- struct tree *head,
- struct tree *merge,
- struct tree *merge_base);
-
-/*
- * merge_recursive is like merge_trees() but with recursive ancestor
- * consolidation.
- *
- * NOTE: empirically, about a decade ago it was determined that with more
- * than two merge bases, optimal behavior was found when the
- * merge_bases were passed in the order of oldest commit to newest
- * commit. Also, merge_bases will be consumed (emptied) so make a
- * copy if you need it.
- *
- * Outputs:
- * - See RETURN VALUES above
- * - *result is treated as scratch space for temporary recursive merges
- * - opt->repo->index has the new index
- * - new index NOT written to disk
- * - The working tree is updated with results of the merge
- */
-int merge_recursive(struct merge_options *opt,
- struct commit *h1,
- struct commit *h2,
- const struct commit_list *merge_bases,
- struct commit **result);
-
-/*
- * merge_recursive_generic can operate on trees instead of commits, by
- * wrapping the trees into virtual commits, and calling merge_recursive().
- * It also writes out the in-memory index to disk if the merge is successful.
- *
- * Outputs:
- * - See RETURN VALUES above
- * - *result is treated as scratch space for temporary recursive merges
- * - opt->repo->index has the new index
- * - new index also written to $GIT_INDEX_FILE on disk
- * - The working tree is updated with results of the merge
- */
-int merge_recursive_generic(struct merge_options *opt,
- const struct object_id *head,
- const struct object_id *merge,
- int num_merge_bases,
- const struct object_id *merge_bases,
- struct commit **result);
-
-#endif
diff --git a/mergetools/vimdiff b/mergetools/vimdiff
index ffc9be86c8..78710858e8 100644
--- a/mergetools/vimdiff
+++ b/mergetools/vimdiff
@@ -305,6 +305,9 @@ gen_cmd () {
elif echo "$LAYOUT" | grep @BASE >/dev/null
then
FINAL_TARGET="BASE"
+ elif echo "$LAYOUT" | grep @REMOTE >/dev/null
+ then
+ FINAL_TARGET="REMOTE"
else
FINAL_TARGET="MERGED"
fi
@@ -529,7 +532,7 @@ run_unit_tests () {
# Function to make sure that we don't break anything when modifying this
# script.
- NUMBER_OF_TEST_CASES=16
+ NUMBER_OF_TEST_CASES=19
TEST_CASE_01="(LOCAL,BASE,REMOTE)/MERGED" # default behaviour
TEST_CASE_02="@LOCAL,REMOTE" # when using vimdiff1
@@ -547,6 +550,9 @@ run_unit_tests () {
TEST_CASE_14="BASE,REMOTE+BASE,LOCAL"
TEST_CASE_15=" (( (LOCAL , BASE , REMOTE) / MERGED)) +(BASE) , LOCAL+ BASE , REMOTE+ (((LOCAL / BASE / REMOTE)) , MERGED ) "
TEST_CASE_16="LOCAL,BASE,REMOTE / MERGED + BASE,LOCAL + BASE,REMOTE + (LOCAL / BASE / REMOTE),MERGED"
+ TEST_CASE_17="(LOCAL,@BASE,REMOTE)/MERGED"
+ TEST_CASE_18="LOCAL,@REMOTE"
+ TEST_CASE_19="@REMOTE"
EXPECTED_CMD_01="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | execute 'tabdo windo diffthis' | tabfirst\""
EXPECTED_CMD_02="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | 1b | wincmd l | 3b | execute 'tabdo windo diffthis' | tabfirst\""
@@ -564,6 +570,9 @@ run_unit_tests () {
EXPECTED_CMD_14="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | 2b | wincmd l | 3b | tabnew | leftabove vertical split | 2b | wincmd l | 1b | execute 'tabdo windo diffthis' | tabfirst\""
EXPECTED_CMD_15="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabnew | leftabove vertical split | 2b | wincmd l | 1b | tabnew | leftabove vertical split | 2b | wincmd l | 3b | tabnew | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\""
EXPECTED_CMD_16="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabnew | leftabove vertical split | 2b | wincmd l | 1b | tabnew | leftabove vertical split | 2b | wincmd l | 3b | tabnew | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\""
+ EXPECTED_CMD_17="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | execute 'tabdo windo diffthis' | tabfirst\""
+ EXPECTED_CMD_18="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | 1b | wincmd l | 3b | execute 'tabdo windo diffthis' | tabfirst\""
+ EXPECTED_CMD_19="-c \"set hidden diffopt-=hiddenoff | echo | silent execute 'bufdo diffthis' | 3b | execute 'tabdo windo diffthis' | tabfirst\""
EXPECTED_TARGET_01="MERGED"
EXPECTED_TARGET_02="LOCAL"
@@ -581,6 +590,9 @@ run_unit_tests () {
EXPECTED_TARGET_14="MERGED"
EXPECTED_TARGET_15="MERGED"
EXPECTED_TARGET_16="MERGED"
+ EXPECTED_TARGET_17="BASE"
+ EXPECTED_TARGET_18="REMOTE"
+ EXPECTED_TARGET_19="REMOTE"
at_least_one_ko="false"
diff --git a/meson.build b/meson.build
index e98cfa4909..b906e78d89 100644
--- a/meson.build
+++ b/meson.build
@@ -155,6 +155,37 @@
# These machine files can be passed to `meson setup` via the `--native-file`
# option.
#
+# Cross compilation
+# =================
+#
+# Machine files can also be used in the context of cross-compilation to
+# describe the target machine as well as the cross-compiler toolchain that
+# shall be used. An example machine file could look like the following:
+#
+# [binaries]
+# c = 'x86_64-w64-mingw32-gcc'
+# cpp = 'x86_64-w64-mingw32-g++'
+# ar = 'x86_64-w64-mingw32-ar'
+# windres = 'x86_64-w64-mingw32-windres'
+# strip = 'x86_64-w64-mingw32-strip'
+# exe_wrapper = 'wine64'
+# sh = 'C:/Program Files/Git for Windows/usr/bin/sh.exe'
+#
+# [host_machine]
+# system = 'windows'
+# cpu_family = 'x86_64'
+# cpu = 'x86_64'
+# endian = 'little'
+#
+# These machine files can be passed to `meson setup` via the `--cross-file`
+# option.
+#
+# Note that next to the cross-compiler toolchain, the `[binaries]` section is
+# also used to locate a couple of binaries that will be built into Git. This
+# includes `sh`, `python` and `perl`, so when cross-compiling Git you likely
+# want to set these binary paths in addition to the cross-compiler toolchain
+# binaries.
+#
# Subproject wrappers
# ===================
#
@@ -173,7 +204,7 @@ project('git', 'c',
# The version is only of cosmetic nature, so if we cannot find a shell yet we
# simply don't set up a version at all. This may be the case for example on
# Windows systems, where we first have to bootstrap the host environment.
- version: find_program('sh', required: false).found() ? run_command(
+ version: find_program('sh', native: true, required: false).found() ? run_command(
'GIT-VERSION-GEN', meson.current_source_dir(), '--format=@GIT_VERSION@',
capture: true,
check: true,
@@ -198,16 +229,18 @@ elif host_machine.system() == 'windows'
program_path = [ 'C:/Program Files/Git/bin', 'C:/Program Files/Git/usr/bin' ]
endif
-cygpath = find_program('cygpath', dirs: program_path, required: false)
-diff = find_program('diff', dirs: program_path)
-git = find_program('git', dirs: program_path, required: false)
-sed = find_program('sed', dirs: program_path)
-shell = find_program('sh', dirs: program_path)
-tar = find_program('tar', dirs: program_path)
+cygpath = find_program('cygpath', dirs: program_path, native: true, required: false)
+diff = find_program('diff', dirs: program_path, native: true)
+git = find_program('git', dirs: program_path, native: true, required: false)
+sed = find_program('sed', dirs: program_path, native: true)
+shell = find_program('sh', dirs: program_path, native: true)
+tar = find_program('tar', dirs: program_path, native: true)
+
+target_shell = find_program('sh', dirs: program_path, native: false)
# Sanity-check that programs required for the build exist.
foreach tool : ['cat', 'cut', 'grep', 'sort', 'tr', 'uname']
- find_program(tool, dirs: program_path)
+ find_program(tool, dirs: program_path, native: true)
endforeach
script_environment = environment()
@@ -263,6 +296,7 @@ libgit_sources = [
'common-init.c',
'compat/nonblock.c',
'compat/obstack.c',
+ 'compat/open.c',
'compat/terminal.c',
'compiler-tricks/not-constant.c',
'config.c',
@@ -311,6 +345,7 @@ libgit_sources = [
'graph.c',
'grep.c',
'hash-lookup.c',
+ 'hash.c',
'hashmap.c',
'help.c',
'hex.c',
@@ -338,7 +373,6 @@ libgit_sources = [
'merge-ll.c',
'merge-ort.c',
'merge-ort-wrappers.c',
- 'merge-recursive.c',
'merge.c',
'midx.c',
'midx-write.c',
@@ -353,6 +387,7 @@ libgit_sources = [
'object-file-convert.c',
'object-file.c',
'object-name.c',
+ 'object-store.c',
'object.c',
'oid-array.c',
'oidmap.c',
@@ -705,7 +740,7 @@ libgit_c_args = [
'-DGIT_LOCALE_PATH="' + get_option('localedir') + '"',
'-DGIT_MAN_PATH="' + get_option('mandir') + '"',
'-DPAGER_ENV="' + get_option('pager_environment') + '"',
- '-DSHELL_PATH="' + fs.as_posix(shell.full_path()) + '"',
+ '-DSHELL_PATH="' + fs.as_posix(target_shell.full_path()) + '"',
]
libgit_include_directories = [ '.' ]
libgit_dependencies = [ ]
@@ -714,6 +749,7 @@ libgit_dependencies = [ ]
# Makefile.
if get_option('warning_level') in ['2','3', 'everything'] and compiler.get_argument_syntax() == 'gcc'
foreach cflag : [
+ '-Wcomma',
'-Wdeclaration-after-statement',
'-Wformat-security',
'-Wold-style-definition',
@@ -768,6 +804,7 @@ endif
build_options_config.set_quoted('X', executable_suffix)
python = import('python').find_installation('python3', required: get_option('python'))
+target_python = find_program('python3', native: false, required: python.found())
if python.found()
build_options_config.set('NO_PYTHON', '')
else
@@ -779,7 +816,7 @@ endif
# features. It is optional if you want to neither execute tests nor use any of
# these optional features.
perl_required = get_option('perl')
-if get_option('tests') or get_option('gitweb').enabled() or 'netrc' in get_option('credential_helpers') or get_option('docs') != []
+if get_option('gitweb').enabled() or 'netrc' in get_option('credential_helpers') or get_option('docs') != []
perl_required = true
endif
@@ -797,9 +834,11 @@ endif
# which we can do starting with Meson 1.5.0 and newer, or we have to
# match against the minor version.
if meson.version().version_compare('>=1.5.0')
- perl = find_program('perl', dirs: program_path, required: perl_required, version: '>=5.26.0', version_argument: '-V:version')
+ perl = find_program('perl', dirs: program_path, native: true, required: perl_required, version: '>=5.26.0', version_argument: '-V:version')
+ target_perl = find_program('perl', dirs: program_path, native: false, required: perl.found(), version: '>=5.26.0', version_argument: '-V:version')
else
- perl = find_program('perl', dirs: program_path, required: perl_required, version: '>=26')
+ perl = find_program('perl', dirs: program_path, native: true, required: perl_required, version: '>=26')
+ target_perl = find_program('perl', dirs: program_path, native: false, required: perl.found(), version: '>=26')
endif
perl_features_enabled = perl.found() and get_option('perl').allowed()
if perl_features_enabled
@@ -850,7 +889,7 @@ else
build_options_config.set('NO_PTHREADS', '1')
endif
-msgfmt = find_program('msgfmt', dirs: program_path, required: false)
+msgfmt = find_program('msgfmt', dirs: program_path, native: true, required: false)
gettext_option = get_option('gettext').disable_auto_if(not msgfmt.found())
if not msgfmt.found() and gettext_option.enabled()
error('Internationalization via libintl requires msgfmt')
@@ -1114,7 +1153,6 @@ if host_machine.system() == 'cygwin'
]
elif host_machine.system() == 'windows'
libgit_sources += [
- 'compat/mingw.c',
'compat/winansi.c',
'compat/win32/dirent.c',
'compat/win32/flush.c',
@@ -1141,6 +1179,9 @@ elif host_machine.system() == 'windows'
libgit_include_directories += 'compat/win32'
if compiler.get_id() == 'msvc'
libgit_include_directories += 'compat/vcbuild/include'
+ libgit_sources += 'compat/msvc.c'
+ else
+ libgit_sources += 'compat/mingw.c'
endif
endif
@@ -1693,7 +1734,7 @@ bin_wrappers += executable('scalar',
install_dir: get_option('libexecdir') / 'git-core',
)
-if get_option('curl').enabled()
+if curl.found()
libgit_curl = declare_dependency(
sources: [
'http.c',
@@ -1981,9 +2022,9 @@ foreach key, value : {
'GIT_TEST_TEMPLATE_DIR': meson.project_build_root() / 'templates',
'GIT_TEST_TEXTDOMAINDIR': meson.project_build_root() / 'po',
'PAGER_ENV': get_option('pager_environment'),
- 'PERL_PATH': perl.found() ? perl.full_path() : '',
- 'PYTHON_PATH': python.found () ? python.full_path() : '',
- 'SHELL_PATH': shell.full_path(),
+ 'PERL_PATH': target_perl.found() ? target_perl.full_path() : '',
+ 'PYTHON_PATH': target_python.found () ? target_python.full_path() : '',
+ 'SHELL_PATH': target_shell.full_path(),
'TAR': tar.full_path(),
'TEST_OUTPUT_DIRECTORY': test_output_directory,
'TEST_SHELL_PATH': shell.full_path(),
diff --git a/midx-write.c b/midx-write.c
index 48d6558253..dd3b3070e5 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -647,18 +647,24 @@ static uint32_t *midx_pack_order(struct write_midx_context *ctx)
return pack_order;
}
-static void write_midx_reverse_index(char *midx_name, unsigned char *midx_hash,
- struct write_midx_context *ctx)
+static void write_midx_reverse_index(struct write_midx_context *ctx,
+ const char *object_dir,
+ unsigned char *midx_hash)
{
struct strbuf buf = STRBUF_INIT;
char *tmp_file;
trace2_region_enter("midx", "write_midx_reverse_index", ctx->repo);
- strbuf_addf(&buf, "%s-%s.rev", midx_name, hash_to_hex_algop(midx_hash,
- ctx->repo->hash_algo));
+ if (ctx->incremental)
+ get_split_midx_filename_ext(ctx->repo->hash_algo, &buf,
+ object_dir, midx_hash,
+ MIDX_EXT_REV);
+ else
+ get_midx_filename_ext(ctx->repo->hash_algo, &buf, object_dir,
+ midx_hash, MIDX_EXT_REV);
- tmp_file = write_rev_file_order(ctx->repo->hash_algo, NULL, ctx->pack_order,
+ tmp_file = write_rev_file_order(ctx->repo, NULL, ctx->pack_order,
ctx->entries_nr, midx_hash, WRITE_REV);
if (finalize_object_file(tmp_file, buf.buf))
@@ -708,7 +714,7 @@ static int add_ref_to_pending(const char *refname, const char *referent UNUSED,
if (!peel_iterated_oid(revs->repo, oid, &peeled))
oid = &peeled;
- object = parse_object_or_die(oid, refname);
+ object = parse_object_or_die(revs->repo, oid, refname);
if (object->type != OBJ_COMMIT)
return 0;
@@ -768,7 +774,7 @@ static int read_refs_snapshot(const char *refs_snapshot,
if (*end)
die(_("malformed line: %s"), buf.buf);
- object = parse_object_or_die(&oid, NULL);
+ object = parse_object_or_die(revs->repo, &oid, NULL);
if (preferred)
object->flags |= NEEDS_BITMAP;
@@ -829,22 +835,29 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr
return cb.commits;
}
-static int write_midx_bitmap(struct repository *r, const char *midx_name,
+static int write_midx_bitmap(struct write_midx_context *ctx,
+ const char *object_dir,
const unsigned char *midx_hash,
struct packing_data *pdata,
struct commit **commits,
uint32_t commits_nr,
- uint32_t *pack_order,
unsigned flags)
{
int ret, i;
uint16_t options = 0;
struct bitmap_writer writer;
struct pack_idx_entry **index;
- char *bitmap_name = xstrfmt("%s-%s.bitmap", midx_name,
- hash_to_hex_algop(midx_hash, r->hash_algo));
+ struct strbuf bitmap_name = STRBUF_INIT;
+
+ trace2_region_enter("midx", "write_midx_bitmap", ctx->repo);
- trace2_region_enter("midx", "write_midx_bitmap", r);
+ if (ctx->incremental)
+ get_split_midx_filename_ext(ctx->repo->hash_algo, &bitmap_name,
+ object_dir, midx_hash,
+ MIDX_EXT_BITMAP);
+ else
+ get_midx_filename_ext(ctx->repo->hash_algo, &bitmap_name,
+ object_dir, midx_hash, MIDX_EXT_BITMAP);
if (flags & MIDX_WRITE_BITMAP_HASH_CACHE)
options |= BITMAP_OPT_HASH_CACHE;
@@ -861,7 +874,8 @@ static int write_midx_bitmap(struct repository *r, const char *midx_name,
for (i = 0; i < pdata->nr_objects; i++)
index[i] = &pdata->objects[i].idx;
- bitmap_writer_init(&writer, r, pdata);
+ bitmap_writer_init(&writer, ctx->repo, pdata,
+ ctx->incremental ? ctx->base_midx : NULL);
bitmap_writer_show_progress(&writer, flags & MIDX_PROGRESS);
bitmap_writer_build_type_index(&writer, index);
@@ -879,7 +893,7 @@ static int write_midx_bitmap(struct repository *r, const char *midx_name,
* bitmap_writer_finish().
*/
for (i = 0; i < pdata->nr_objects; i++)
- index[pack_order[i]] = &pdata->objects[i].idx;
+ index[ctx->pack_order[i]] = &pdata->objects[i].idx;
bitmap_writer_select_commits(&writer, commits, commits_nr);
ret = bitmap_writer_build(&writer);
@@ -887,14 +901,14 @@ static int write_midx_bitmap(struct repository *r, const char *midx_name,
goto cleanup;
bitmap_writer_set_checksum(&writer, midx_hash);
- bitmap_writer_finish(&writer, index, bitmap_name, options);
+ bitmap_writer_finish(&writer, index, bitmap_name.buf, options);
cleanup:
free(index);
- free(bitmap_name);
+ strbuf_release(&bitmap_name);
bitmap_writer_free(&writer);
- trace2_region_leave("midx", "write_midx_bitmap", r);
+ trace2_region_leave("midx", "write_midx_bitmap", ctx->repo);
return ret;
}
@@ -1077,8 +1091,6 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
ctx.repo = r;
ctx.incremental = !!(flags & MIDX_WRITE_INCREMENTAL);
- if (ctx.incremental && (flags & MIDX_WRITE_BITMAP))
- die(_("cannot write incremental MIDX with bitmap"));
if (ctx.incremental)
strbuf_addf(&midx_name,
@@ -1086,7 +1098,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
object_dir);
else
get_midx_filename(r->hash_algo, &midx_name, object_dir);
- if (safe_create_leading_directories(midx_name.buf))
+ if (safe_create_leading_directories(r, midx_name.buf))
die_errno(_("unable to create leading directories of %s"),
midx_name.buf);
@@ -1119,6 +1131,13 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
if (ctx.incremental) {
struct multi_pack_index *m = ctx.base_midx;
while (m) {
+ if (flags & MIDX_WRITE_BITMAP && load_midx_revindex(m)) {
+ error(_("could not load reverse index for MIDX %s"),
+ hash_to_hex_algop(get_midx_checksum(m),
+ m->repo->hash_algo));
+ result = 1;
+ goto cleanup;
+ }
ctx.num_multi_pack_indexes_before++;
m = m->base_midx;
}
@@ -1342,10 +1361,12 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
return -1;
}
- f = hashfd(get_tempfile_fd(incr), get_tempfile_path(incr));
+ f = hashfd(r->hash_algo, get_tempfile_fd(incr),
+ get_tempfile_path(incr));
} else {
hold_lock_file_for_update(&lk, midx_name.buf, LOCK_DIE_ON_ERROR);
- f = hashfd(get_lock_file_fd(&lk), get_lock_file_path(&lk));
+ f = hashfd(r->hash_algo, get_lock_file_fd(&lk),
+ get_lock_file_path(&lk));
}
cf = init_chunkfile(f);
@@ -1387,7 +1408,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
if (flags & MIDX_WRITE_REV_INDEX &&
git_env_bool("GIT_TEST_MIDX_WRITE_REV", 0))
- write_midx_reverse_index(midx_name.buf, midx_hash, &ctx);
+ write_midx_reverse_index(&ctx, object_dir, midx_hash);
if (flags & MIDX_WRITE_BITMAP) {
struct packing_data pdata;
@@ -1410,8 +1431,8 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
FREE_AND_NULL(ctx.entries);
ctx.entries_nr = 0;
- if (write_midx_bitmap(r, midx_name.buf, midx_hash, &pdata,
- commits, commits_nr, ctx.pack_order,
+ if (write_midx_bitmap(&ctx, object_dir,
+ midx_hash, &pdata, commits, commits_nr,
flags) < 0) {
error(_("could not write multi-pack bitmap"));
result = 1;
diff --git a/midx.c b/midx.c
index d91088efb8..3d0015f782 100644
--- a/midx.c
+++ b/midx.c
@@ -5,7 +5,6 @@
#include "dir.h"
#include "hex.h"
#include "packfile.h"
-#include "object-file.h"
#include "hash-lookup.h"
#include "midx.h"
#include "progress.h"
@@ -747,7 +746,8 @@ int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, i
int midx_checksum_valid(struct multi_pack_index *m)
{
- return hashfile_checksum_valid(m->data, m->data_len);
+ return hashfile_checksum_valid(m->repo->hash_algo,
+ m->data, m->data_len);
}
struct clear_midx_data {
diff --git a/notes-cache.c b/notes-cache.c
index ecfdf6e43b..150241b15e 100644
--- a/notes-cache.c
+++ b/notes-cache.c
@@ -2,7 +2,8 @@
#include "git-compat-util.h"
#include "notes-cache.h"
-#include "object-store-ll.h"
+#include "object-file.h"
+#include "object-store.h"
#include "pretty.h"
#include "repository.h"
#include "commit.h"
diff --git a/notes-merge.c b/notes-merge.c
index 67a472020d..dae8e6a281 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -8,7 +8,7 @@
#include "refs.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "path.h"
#include "repository.h"
#include "diff.h"
@@ -296,7 +296,7 @@ static void check_notes_merge_worktree(struct notes_merge_options *o)
"(%s exists)."), repo_git_path_replace(the_repository, &buf, "NOTES_MERGE_*"));
}
- if (safe_create_leading_directories_const(repo_git_path_replace(the_repository, &buf,
+ if (safe_create_leading_directories_const(the_repository, repo_git_path_replace(the_repository, &buf,
NOTES_MERGE_WORKTREE "/.test")))
die_errno("unable to create directory %s",
repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE));
@@ -314,7 +314,7 @@ static void write_buf_to_worktree(const struct object_id *obj,
{
int fd;
char *path = repo_git_path(the_repository, NOTES_MERGE_WORKTREE "/%s", oid_to_hex(obj));
- if (safe_create_leading_directories_const(path))
+ if (safe_create_leading_directories_const(the_repository, path))
die_errno("unable to create directory for '%s'", path);
fd = xopen(path, O_WRONLY | O_EXCL | O_CREAT, 0666);
@@ -617,7 +617,7 @@ int notes_merge(struct notes_merge_options *o,
if (repo_get_merge_bases(the_repository, local, remote, &bases) < 0)
exit(128);
if (!bases) {
- base_oid = null_oid();
+ base_oid = null_oid(the_hash_algo);
base_tree_oid = the_hash_algo->empty_tree;
if (o->verbosity >= 4)
printf("No merge base found; doing history-less merge\n");
@@ -729,7 +729,7 @@ int notes_merge_commit(struct notes_merge_options *o,
/* write file as blob, and add to partial_tree */
if (stat(path.buf, &st))
die_errno("Failed to stat '%s'", path.buf);
- if (index_path(o->repo->index, &blob_oid, path.buf, &st, HASH_WRITE_OBJECT))
+ if (index_path(o->repo->index, &blob_oid, path.buf, &st, INDEX_WRITE_OBJECT))
die("Failed to write blob object from '%s'", path.buf);
if (add_note(partial_tree, &obj_oid, &blob_oid, NULL))
die("Failed to add resolved note '%s' to notes tree",
diff --git a/notes.c b/notes.c
index f534423050..d9645c4b5d 100644
--- a/notes.c
+++ b/notes.c
@@ -6,8 +6,9 @@
#include "environment.h"
#include "hex.h"
#include "notes.h"
+#include "object-file.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "utf8.h"
#include "strbuf.h"
#include "tree-walk.h"
@@ -1353,7 +1354,7 @@ int copy_note(struct notes_tree *t,
if (note)
return add_note(t, to_obj, note, combine_notes);
else if (existing_note)
- return add_note(t, to_obj, null_oid(), combine_notes);
+ return add_note(t, to_obj, null_oid(the_hash_algo), combine_notes);
return 0;
}
diff --git a/object-file-convert.c b/object-file-convert.c
index eba71955cf..7ab875afe6 100644
--- a/object-file-convert.c
+++ b/object-file-convert.c
@@ -1,4 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "git-compat-util.h"
@@ -63,7 +62,8 @@ static int decode_tree_entry_raw(struct object_id *oid, const char **path,
return 0;
}
-static int convert_tree_object(struct strbuf *out,
+static int convert_tree_object(struct repository *repo,
+ struct strbuf *out,
const struct git_hash_algo *from,
const struct git_hash_algo *to,
const char *buffer, size_t size)
@@ -78,7 +78,7 @@ static int convert_tree_object(struct strbuf *out,
if (decode_tree_entry_raw(&entry_oid, &path, &pathlen, from, p,
end - p))
return error(_("failed to decode tree entry"));
- if (repo_oid_to_algop(the_repository, &entry_oid, to, &mapped_oid))
+ if (repo_oid_to_algop(repo, &entry_oid, to, &mapped_oid))
return error(_("failed to map tree entry for %s"), oid_to_hex(&entry_oid));
strbuf_add(out, p, path - p);
strbuf_add(out, path, pathlen);
@@ -88,7 +88,8 @@ static int convert_tree_object(struct strbuf *out,
return 0;
}
-static int convert_tag_object(struct strbuf *out,
+static int convert_tag_object(struct repository *repo,
+ struct strbuf *out,
const struct git_hash_algo *from,
const struct git_hash_algo *to,
const char *buffer, size_t size)
@@ -105,7 +106,7 @@ static int convert_tag_object(struct strbuf *out,
return error("bogus tag object");
if (parse_oid_hex_algop(buffer + 7, &oid, &p, from) < 0)
return error("bad tag object ID");
- if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
+ if (repo_oid_to_algop(repo, &oid, to, &mapped_oid))
return error("unable to map tree %s in tag object",
oid_to_hex(&oid));
size -= ((p + 1) - buffer);
@@ -139,7 +140,8 @@ static int convert_tag_object(struct strbuf *out,
return 0;
}
-static int convert_commit_object(struct strbuf *out,
+static int convert_commit_object(struct repository *repo,
+ struct strbuf *out,
const struct git_hash_algo *from,
const struct git_hash_algo *to,
const char *buffer, size_t size)
@@ -165,7 +167,7 @@ static int convert_commit_object(struct strbuf *out,
(p != eol))
return error(_("bad %s in commit"), "tree");
- if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
+ if (repo_oid_to_algop(repo, &oid, to, &mapped_oid))
return error(_("unable to map %s %s in commit object"),
"tree", oid_to_hex(&oid));
strbuf_addf(out, "tree %s\n", oid_to_hex(&mapped_oid));
@@ -177,7 +179,7 @@ static int convert_commit_object(struct strbuf *out,
(p != eol))
return error(_("bad %s in commit"), "parent");
- if (repo_oid_to_algop(the_repository, &oid, to, &mapped_oid))
+ if (repo_oid_to_algop(repo, &oid, to, &mapped_oid))
return error(_("unable to map %s %s in commit object"),
"parent", oid_to_hex(&oid));
@@ -202,7 +204,7 @@ static int convert_commit_object(struct strbuf *out,
}
/* Compute the new tag object */
- if (convert_tag_object(&new_tag, from, to, tag.buf, tag.len)) {
+ if (convert_tag_object(repo, &new_tag, from, to, tag.buf, tag.len)) {
strbuf_release(&tag);
strbuf_release(&new_tag);
return -1;
@@ -241,7 +243,8 @@ static int convert_commit_object(struct strbuf *out,
return 0;
}
-int convert_object_file(struct strbuf *outbuf,
+int convert_object_file(struct repository *repo,
+ struct strbuf *outbuf,
const struct git_hash_algo *from,
const struct git_hash_algo *to,
const void *buf, size_t len,
@@ -256,13 +259,13 @@ int convert_object_file(struct strbuf *outbuf,
switch (type) {
case OBJ_COMMIT:
- ret = convert_commit_object(outbuf, from, to, buf, len);
+ ret = convert_commit_object(repo, outbuf, from, to, buf, len);
break;
case OBJ_TREE:
- ret = convert_tree_object(outbuf, from, to, buf, len);
+ ret = convert_tree_object(repo, outbuf, from, to, buf, len);
break;
case OBJ_TAG:
- ret = convert_tag_object(outbuf, from, to, buf, len);
+ ret = convert_tag_object(repo, outbuf, from, to, buf, len);
break;
default:
/* Not implemented yet, so fail. */
diff --git a/object-file-convert.h b/object-file-convert.h
index a4f802aa8e..9b3cc5e533 100644
--- a/object-file-convert.h
+++ b/object-file-convert.h
@@ -14,7 +14,8 @@ int repo_oid_to_algop(struct repository *repo, const struct object_id *src,
* Convert an object file from one hash algorithm to another algorithm.
* Return -1 on failure, 0 on success.
*/
-int convert_object_file(struct strbuf *outbuf,
+int convert_object_file(struct repository *repo,
+ struct strbuf *outbuf,
const struct git_hash_algo *from,
const struct git_hash_algo *to,
const void *buf, size_t len,
diff --git a/object-file.c b/object-file.c
index 726e41a047..9cc3a24a40 100644
--- a/object-file.c
+++ b/object-file.c
@@ -11,502 +11,37 @@
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "git-compat-util.h"
-#include "abspath.h"
-#include "config.h"
+#include "bulk-checkin.h"
#include "convert.h"
+#include "dir.h"
#include "environment.h"
+#include "fsck.h"
#include "gettext.h"
#include "hex.h"
-#include "string-list.h"
-#include "lockfile.h"
-#include "pack.h"
-#include "commit.h"
-#include "run-command.h"
-#include "refs.h"
-#include "bulk-checkin.h"
-#include "repository.h"
-#include "replace-object.h"
-#include "streaming.h"
-#include "dir.h"
-#include "list.h"
-#include "quote.h"
-#include "packfile.h"
+#include "loose.h"
+#include "object-file-convert.h"
#include "object-file.h"
#include "object-store.h"
#include "oidtree.h"
+#include "pack.h"
+#include "packfile.h"
#include "path.h"
-#include "promisor-remote.h"
#include "setup.h"
-#include "submodule.h"
-#include "fsck.h"
-#include "loose.h"
-#include "object-file-convert.h"
+#include "streaming.h"
/* The maximum size for an object header. */
#define MAX_HEADER_LEN 32
-static const struct object_id empty_tree_oid = {
- .hash = {
- 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60,
- 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04
- },
- .algo = GIT_HASH_SHA1,
-};
-static const struct object_id empty_blob_oid = {
- .hash = {
- 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1, 0xd6, 0x43, 0x4b, 0x8b,
- 0x29, 0xae, 0x77, 0x5a, 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91
- },
- .algo = GIT_HASH_SHA1,
-};
-static const struct object_id null_oid_sha1 = {
- .hash = {0},
- .algo = GIT_HASH_SHA1,
-};
-static const struct object_id empty_tree_oid_sha256 = {
- .hash = {
- 0x6e, 0xf1, 0x9b, 0x41, 0x22, 0x5c, 0x53, 0x69, 0xf1, 0xc1,
- 0x04, 0xd4, 0x5d, 0x8d, 0x85, 0xef, 0xa9, 0xb0, 0x57, 0xb5,
- 0x3b, 0x14, 0xb4, 0xb9, 0xb9, 0x39, 0xdd, 0x74, 0xde, 0xcc,
- 0x53, 0x21
- },
- .algo = GIT_HASH_SHA256,
-};
-static const struct object_id empty_blob_oid_sha256 = {
- .hash = {
- 0x47, 0x3a, 0x0f, 0x4c, 0x3b, 0xe8, 0xa9, 0x36, 0x81, 0xa2,
- 0x67, 0xe3, 0xb1, 0xe9, 0xa7, 0xdc, 0xda, 0x11, 0x85, 0x43,
- 0x6f, 0xe1, 0x41, 0xf7, 0x74, 0x91, 0x20, 0xa3, 0x03, 0x72,
- 0x18, 0x13
- },
- .algo = GIT_HASH_SHA256,
-};
-static const struct object_id null_oid_sha256 = {
- .hash = {0},
- .algo = GIT_HASH_SHA256,
-};
-
-static void git_hash_sha1_init(struct git_hash_ctx *ctx)
-{
- ctx->algop = &hash_algos[GIT_HASH_SHA1];
- git_SHA1_Init(&ctx->state.sha1);
-}
-
-static void git_hash_sha1_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src)
-{
- dst->algop = src->algop;
- git_SHA1_Clone(&dst->state.sha1, &src->state.sha1);
-}
-
-static void git_hash_sha1_update(struct git_hash_ctx *ctx, const void *data, size_t len)
-{
- git_SHA1_Update(&ctx->state.sha1, data, len);
-}
-
-static void git_hash_sha1_final(unsigned char *hash, struct git_hash_ctx *ctx)
-{
- git_SHA1_Final(hash, &ctx->state.sha1);
-}
-
-static void git_hash_sha1_final_oid(struct object_id *oid, struct git_hash_ctx *ctx)
-{
- git_SHA1_Final(oid->hash, &ctx->state.sha1);
- memset(oid->hash + GIT_SHA1_RAWSZ, 0, GIT_MAX_RAWSZ - GIT_SHA1_RAWSZ);
- oid->algo = GIT_HASH_SHA1;
-}
-
-static void git_hash_sha1_init_unsafe(struct git_hash_ctx *ctx)
-{
- ctx->algop = unsafe_hash_algo(&hash_algos[GIT_HASH_SHA1]);
- git_SHA1_Init_unsafe(&ctx->state.sha1_unsafe);
-}
-
-static void git_hash_sha1_clone_unsafe(struct git_hash_ctx *dst, const struct git_hash_ctx *src)
-{
- dst->algop = src->algop;
- git_SHA1_Clone_unsafe(&dst->state.sha1_unsafe, &src->state.sha1_unsafe);
-}
-
-static void git_hash_sha1_update_unsafe(struct git_hash_ctx *ctx, const void *data,
- size_t len)
-{
- git_SHA1_Update_unsafe(&ctx->state.sha1_unsafe, data, len);
-}
-
-static void git_hash_sha1_final_unsafe(unsigned char *hash, struct git_hash_ctx *ctx)
-{
- git_SHA1_Final_unsafe(hash, &ctx->state.sha1_unsafe);
-}
-
-static void git_hash_sha1_final_oid_unsafe(struct object_id *oid, struct git_hash_ctx *ctx)
-{
- git_SHA1_Final_unsafe(oid->hash, &ctx->state.sha1_unsafe);
- memset(oid->hash + GIT_SHA1_RAWSZ, 0, GIT_MAX_RAWSZ - GIT_SHA1_RAWSZ);
- oid->algo = GIT_HASH_SHA1;
-}
-
-static void git_hash_sha256_init(struct git_hash_ctx *ctx)
-{
- ctx->algop = unsafe_hash_algo(&hash_algos[GIT_HASH_SHA256]);
- git_SHA256_Init(&ctx->state.sha256);
-}
-
-static void git_hash_sha256_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src)
-{
- dst->algop = src->algop;
- git_SHA256_Clone(&dst->state.sha256, &src->state.sha256);
-}
-
-static void git_hash_sha256_update(struct git_hash_ctx *ctx, const void *data, size_t len)
-{
- git_SHA256_Update(&ctx->state.sha256, data, len);
-}
-
-static void git_hash_sha256_final(unsigned char *hash, struct git_hash_ctx *ctx)
-{
- git_SHA256_Final(hash, &ctx->state.sha256);
-}
-
-static void git_hash_sha256_final_oid(struct object_id *oid, struct git_hash_ctx *ctx)
-{
- git_SHA256_Final(oid->hash, &ctx->state.sha256);
- /*
- * This currently does nothing, so the compiler should optimize it out,
- * but keep it in case we extend the hash size again.
- */
- memset(oid->hash + GIT_SHA256_RAWSZ, 0, GIT_MAX_RAWSZ - GIT_SHA256_RAWSZ);
- oid->algo = GIT_HASH_SHA256;
-}
-
-static void git_hash_unknown_init(struct git_hash_ctx *ctx UNUSED)
-{
- BUG("trying to init unknown hash");
-}
-
-static void git_hash_unknown_clone(struct git_hash_ctx *dst UNUSED,
- const struct git_hash_ctx *src UNUSED)
-{
- BUG("trying to clone unknown hash");
-}
-
-static void git_hash_unknown_update(struct git_hash_ctx *ctx UNUSED,
- const void *data UNUSED,
- size_t len UNUSED)
-{
- BUG("trying to update unknown hash");
-}
-
-static void git_hash_unknown_final(unsigned char *hash UNUSED,
- struct git_hash_ctx *ctx UNUSED)
-{
- BUG("trying to finalize unknown hash");
-}
-
-static void git_hash_unknown_final_oid(struct object_id *oid UNUSED,
- struct git_hash_ctx *ctx UNUSED)
-{
- BUG("trying to finalize unknown hash");
-}
-
-static const struct git_hash_algo sha1_unsafe_algo = {
- .name = "sha1",
- .format_id = GIT_SHA1_FORMAT_ID,
- .rawsz = GIT_SHA1_RAWSZ,
- .hexsz = GIT_SHA1_HEXSZ,
- .blksz = GIT_SHA1_BLKSZ,
- .init_fn = git_hash_sha1_init_unsafe,
- .clone_fn = git_hash_sha1_clone_unsafe,
- .update_fn = git_hash_sha1_update_unsafe,
- .final_fn = git_hash_sha1_final_unsafe,
- .final_oid_fn = git_hash_sha1_final_oid_unsafe,
- .empty_tree = &empty_tree_oid,
- .empty_blob = &empty_blob_oid,
- .null_oid = &null_oid_sha1,
-};
-
-const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
- {
- .name = NULL,
- .format_id = 0x00000000,
- .rawsz = 0,
- .hexsz = 0,
- .blksz = 0,
- .init_fn = git_hash_unknown_init,
- .clone_fn = git_hash_unknown_clone,
- .update_fn = git_hash_unknown_update,
- .final_fn = git_hash_unknown_final,
- .final_oid_fn = git_hash_unknown_final_oid,
- .empty_tree = NULL,
- .empty_blob = NULL,
- .null_oid = NULL,
- },
- {
- .name = "sha1",
- .format_id = GIT_SHA1_FORMAT_ID,
- .rawsz = GIT_SHA1_RAWSZ,
- .hexsz = GIT_SHA1_HEXSZ,
- .blksz = GIT_SHA1_BLKSZ,
- .init_fn = git_hash_sha1_init,
- .clone_fn = git_hash_sha1_clone,
- .update_fn = git_hash_sha1_update,
- .final_fn = git_hash_sha1_final,
- .final_oid_fn = git_hash_sha1_final_oid,
- .unsafe = &sha1_unsafe_algo,
- .empty_tree = &empty_tree_oid,
- .empty_blob = &empty_blob_oid,
- .null_oid = &null_oid_sha1,
- },
- {
- .name = "sha256",
- .format_id = GIT_SHA256_FORMAT_ID,
- .rawsz = GIT_SHA256_RAWSZ,
- .hexsz = GIT_SHA256_HEXSZ,
- .blksz = GIT_SHA256_BLKSZ,
- .init_fn = git_hash_sha256_init,
- .clone_fn = git_hash_sha256_clone,
- .update_fn = git_hash_sha256_update,
- .final_fn = git_hash_sha256_final,
- .final_oid_fn = git_hash_sha256_final_oid,
- .empty_tree = &empty_tree_oid_sha256,
- .empty_blob = &empty_blob_oid_sha256,
- .null_oid = &null_oid_sha256,
- }
-};
-
-const struct object_id *null_oid(void)
-{
- return the_hash_algo->null_oid;
-}
-
-const char *empty_tree_oid_hex(const struct git_hash_algo *algop)
-{
- static char buf[GIT_MAX_HEXSZ + 1];
- return oid_to_hex_r(buf, algop->empty_tree);
-}
-
-int hash_algo_by_name(const char *name)
-{
- int i;
- if (!name)
- return GIT_HASH_UNKNOWN;
- for (i = 1; i < GIT_HASH_NALGOS; i++)
- if (!strcmp(name, hash_algos[i].name))
- return i;
- return GIT_HASH_UNKNOWN;
-}
-
-int hash_algo_by_id(uint32_t format_id)
-{
- int i;
- for (i = 1; i < GIT_HASH_NALGOS; i++)
- if (format_id == hash_algos[i].format_id)
- return i;
- return GIT_HASH_UNKNOWN;
-}
-
-int hash_algo_by_length(int len)
-{
- int i;
- for (i = 1; i < GIT_HASH_NALGOS; i++)
- if (len == hash_algos[i].rawsz)
- return i;
- return GIT_HASH_UNKNOWN;
-}
-
-const struct git_hash_algo *unsafe_hash_algo(const struct git_hash_algo *algop)
-{
- /* If we have a faster "unsafe" implementation, use that. */
- if (algop->unsafe)
- return algop->unsafe;
- /* Otherwise use the default one. */
- return algop;
-}
-
-/*
- * This is meant to hold a *small* number of objects that you would
- * want repo_read_object_file() to be able to return, but yet you do not want
- * to write them into the object store (e.g. a browse-only
- * application).
- */
-static struct cached_object_entry {
- struct object_id oid;
- struct cached_object {
- enum object_type type;
- const void *buf;
- unsigned long size;
- } value;
-} *cached_objects;
-static int cached_object_nr, cached_object_alloc;
-
-static const struct cached_object *find_cached_object(const struct object_id *oid)
-{
- static const struct cached_object empty_tree = {
- .type = OBJ_TREE,
- .buf = "",
- };
- int i;
- const struct cached_object_entry *co = cached_objects;
-
- for (i = 0; i < cached_object_nr; i++, co++) {
- if (oideq(&co->oid, oid))
- return &co->value;
- }
- if (oideq(oid, the_hash_algo->empty_tree))
- return &empty_tree;
- return NULL;
-}
-
-
static int get_conv_flags(unsigned flags)
{
- if (flags & HASH_RENORMALIZE)
+ if (flags & INDEX_RENORMALIZE)
return CONV_EOL_RENORMALIZE;
- else if (flags & HASH_WRITE_OBJECT)
+ else if (flags & INDEX_WRITE_OBJECT)
return global_conv_flags_eol | CONV_WRITE_OBJECT;
else
return 0;
}
-
-int mkdir_in_gitdir(const char *path)
-{
- if (mkdir(path, 0777)) {
- int saved_errno = errno;
- struct stat st;
- struct strbuf sb = STRBUF_INIT;
-
- if (errno != EEXIST)
- return -1;
- /*
- * Are we looking at a path in a symlinked worktree
- * whose original repository does not yet have it?
- * e.g. .git/rr-cache pointing at its original
- * repository in which the user hasn't performed any
- * conflict resolution yet?
- */
- if (lstat(path, &st) || !S_ISLNK(st.st_mode) ||
- strbuf_readlink(&sb, path, st.st_size) ||
- !is_absolute_path(sb.buf) ||
- mkdir(sb.buf, 0777)) {
- strbuf_release(&sb);
- errno = saved_errno;
- return -1;
- }
- strbuf_release(&sb);
- }
- return adjust_shared_perm(the_repository, path);
-}
-
-static enum scld_error safe_create_leading_directories_1(char *path, int share)
-{
- char *next_component = path + offset_1st_component(path);
- enum scld_error ret = SCLD_OK;
-
- while (ret == SCLD_OK && next_component) {
- struct stat st;
- char *slash = next_component, slash_character;
-
- while (*slash && !is_dir_sep(*slash))
- slash++;
-
- if (!*slash)
- break;
-
- next_component = slash + 1;
- while (is_dir_sep(*next_component))
- next_component++;
- if (!*next_component)
- break;
-
- slash_character = *slash;
- *slash = '\0';
- if (!stat(path, &st)) {
- /* path exists */
- if (!S_ISDIR(st.st_mode)) {
- errno = ENOTDIR;
- ret = SCLD_EXISTS;
- }
- } else if (mkdir(path, 0777)) {
- if (errno == EEXIST &&
- !stat(path, &st) && S_ISDIR(st.st_mode))
- ; /* somebody created it since we checked */
- else if (errno == ENOENT)
- /*
- * Either mkdir() failed because
- * somebody just pruned the containing
- * directory, or stat() failed because
- * the file that was in our way was
- * just removed. Either way, inform
- * the caller that it might be worth
- * trying again:
- */
- ret = SCLD_VANISHED;
- else
- ret = SCLD_FAILED;
- } else if (share && adjust_shared_perm(the_repository, path)) {
- ret = SCLD_PERMS;
- }
- *slash = slash_character;
- }
- return ret;
-}
-
-enum scld_error safe_create_leading_directories(char *path)
-{
- return safe_create_leading_directories_1(path, 1);
-}
-
-enum scld_error safe_create_leading_directories_no_share(char *path)
-{
- return safe_create_leading_directories_1(path, 0);
-}
-
-enum scld_error safe_create_leading_directories_const(const char *path)
-{
- int save_errno;
- /* path points to cache entries, so xstrdup before messing with it */
- char *buf = xstrdup(path);
- enum scld_error result = safe_create_leading_directories(buf);
-
- save_errno = errno;
- free(buf);
- errno = save_errno;
- return result;
-}
-
-int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)
-{
- int fd;
- /*
- * we let the umask do its job, don't try to be more
- * restrictive except to remove write permission.
- */
- int mode = 0444;
- repo_git_path_replace(the_repository, temp_filename, "objects/%s", pattern);
- fd = git_mkstemp_mode(temp_filename->buf, mode);
- if (0 <= fd)
- return fd;
-
- /* slow path */
- /* some mkstemp implementations erase temp_filename on failure */
- repo_git_path_replace(the_repository, temp_filename, "objects/%s", pattern);
- safe_create_leading_directories(temp_filename->buf);
- return xmkstemp_mode(temp_filename->buf, mode);
-}
-
-int odb_pack_keep(const char *name)
-{
- int fd;
-
- fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
- if (0 <= fd)
- return fd;
-
- /* slow path */
- safe_create_leading_directories_const(name);
- return open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
-}
-
static void fill_loose_path(struct strbuf *buf, const struct object_id *oid)
{
int i;
@@ -520,9 +55,9 @@ static void fill_loose_path(struct strbuf *buf, const struct object_id *oid)
}
}
-static const char *odb_loose_path(struct object_directory *odb,
- struct strbuf *buf,
- const struct object_id *oid)
+const char *odb_loose_path(struct object_directory *odb,
+ struct strbuf *buf,
+ const struct object_id *oid)
{
strbuf_reset(buf);
strbuf_addstr(buf, odb->path);
@@ -531,513 +66,6 @@ static const char *odb_loose_path(struct object_directory *odb,
return buf->buf;
}
-const char *loose_object_path(struct repository *r, struct strbuf *buf,
- const struct object_id *oid)
-{
- return odb_loose_path(r->objects->odb, buf, oid);
-}
-
-/*
- * Return non-zero iff the path is usable as an alternate object database.
- */
-static int alt_odb_usable(struct raw_object_store *o,
- struct strbuf *path,
- const char *normalized_objdir, khiter_t *pos)
-{
- int r;
-
- /* Detect cases where alternate disappeared */
- if (!is_directory(path->buf)) {
- error(_("object directory %s does not exist; "
- "check .git/objects/info/alternates"),
- path->buf);
- return 0;
- }
-
- /*
- * Prevent the common mistake of listing the same
- * thing twice, or object directory itself.
- */
- if (!o->odb_by_path) {
- khiter_t p;
-
- o->odb_by_path = kh_init_odb_path_map();
- assert(!o->odb->next);
- p = kh_put_odb_path_map(o->odb_by_path, o->odb->path, &r);
- assert(r == 1); /* never used */
- kh_value(o->odb_by_path, p) = o->odb;
- }
- if (fspatheq(path->buf, normalized_objdir))
- return 0;
- *pos = kh_put_odb_path_map(o->odb_by_path, path->buf, &r);
- /* r: 0 = exists, 1 = never used, 2 = deleted */
- return r == 0 ? 0 : 1;
-}
-
-/*
- * Prepare alternate object database registry.
- *
- * The variable alt_odb_list points at the list of struct
- * object_directory. The elements on this list come from
- * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT
- * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates,
- * whose contents is similar to that environment variable but can be
- * LF separated. Its base points at a statically allocated buffer that
- * contains "/the/directory/corresponding/to/.git/objects/...", while
- * its name points just after the slash at the end of ".git/objects/"
- * in the example above, and has enough space to hold all hex characters
- * of the object ID, an extra slash for the first level indirection, and
- * the terminating NUL.
- */
-static void read_info_alternates(struct repository *r,
- const char *relative_base,
- int depth);
-static int link_alt_odb_entry(struct repository *r, const struct strbuf *entry,
- const char *relative_base, int depth, const char *normalized_objdir)
-{
- struct object_directory *ent;
- struct strbuf pathbuf = STRBUF_INIT;
- struct strbuf tmp = STRBUF_INIT;
- khiter_t pos;
- int ret = -1;
-
- if (!is_absolute_path(entry->buf) && relative_base) {
- strbuf_realpath(&pathbuf, relative_base, 1);
- strbuf_addch(&pathbuf, '/');
- }
- strbuf_addbuf(&pathbuf, entry);
-
- if (!strbuf_realpath(&tmp, pathbuf.buf, 0)) {
- error(_("unable to normalize alternate object path: %s"),
- pathbuf.buf);
- goto error;
- }
- strbuf_swap(&pathbuf, &tmp);
-
- /*
- * The trailing slash after the directory name is given by
- * this function at the end. Remove duplicates.
- */
- while (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/')
- strbuf_setlen(&pathbuf, pathbuf.len - 1);
-
- if (!alt_odb_usable(r->objects, &pathbuf, normalized_objdir, &pos))
- goto error;
-
- CALLOC_ARRAY(ent, 1);
- /* pathbuf.buf is already in r->objects->odb_by_path */
- ent->path = strbuf_detach(&pathbuf, NULL);
-
- /* add the alternate entry */
- *r->objects->odb_tail = ent;
- r->objects->odb_tail = &(ent->next);
- ent->next = NULL;
- assert(r->objects->odb_by_path);
- kh_value(r->objects->odb_by_path, pos) = ent;
-
- /* recursively add alternates */
- read_info_alternates(r, ent->path, depth + 1);
- ret = 0;
- error:
- strbuf_release(&tmp);
- strbuf_release(&pathbuf);
- return ret;
-}
-
-static const char *parse_alt_odb_entry(const char *string,
- int sep,
- struct strbuf *out)
-{
- const char *end;
-
- strbuf_reset(out);
-
- if (*string == '#') {
- /* comment; consume up to next separator */
- end = strchrnul(string, sep);
- } else if (*string == '"' && !unquote_c_style(out, string, &end)) {
- /*
- * quoted path; unquote_c_style has copied the
- * data for us and set "end". Broken quoting (e.g.,
- * an entry that doesn't end with a quote) falls
- * back to the unquoted case below.
- */
- } else {
- /* normal, unquoted path */
- end = strchrnul(string, sep);
- strbuf_add(out, string, end - string);
- }
-
- if (*end)
- end++;
- return end;
-}
-
-static void link_alt_odb_entries(struct repository *r, const char *alt,
- int sep, const char *relative_base, int depth)
-{
- struct strbuf objdirbuf = STRBUF_INIT;
- struct strbuf entry = STRBUF_INIT;
-
- if (!alt || !*alt)
- return;
-
- if (depth > 5) {
- error(_("%s: ignoring alternate object stores, nesting too deep"),
- relative_base);
- return;
- }
-
- strbuf_realpath(&objdirbuf, r->objects->odb->path, 1);
-
- while (*alt) {
- alt = parse_alt_odb_entry(alt, sep, &entry);
- if (!entry.len)
- continue;
- link_alt_odb_entry(r, &entry,
- relative_base, depth, objdirbuf.buf);
- }
- strbuf_release(&entry);
- strbuf_release(&objdirbuf);
-}
-
-static void read_info_alternates(struct repository *r,
- const char *relative_base,
- int depth)
-{
- char *path;
- struct strbuf buf = STRBUF_INIT;
-
- path = xstrfmt("%s/info/alternates", relative_base);
- if (strbuf_read_file(&buf, path, 1024) < 0) {
- warn_on_fopen_errors(path);
- free(path);
- return;
- }
-
- link_alt_odb_entries(r, buf.buf, '\n', relative_base, depth);
- strbuf_release(&buf);
- free(path);
-}
-
-void add_to_alternates_file(const char *reference)
-{
- struct lock_file lock = LOCK_INIT;
- char *alts = repo_git_path(the_repository, "objects/info/alternates");
- FILE *in, *out;
- int found = 0;
-
- hold_lock_file_for_update(&lock, alts, LOCK_DIE_ON_ERROR);
- out = fdopen_lock_file(&lock, "w");
- if (!out)
- die_errno(_("unable to fdopen alternates lockfile"));
-
- in = fopen(alts, "r");
- if (in) {
- struct strbuf line = STRBUF_INIT;
-
- while (strbuf_getline(&line, in) != EOF) {
- if (!strcmp(reference, line.buf)) {
- found = 1;
- break;
- }
- fprintf_or_die(out, "%s\n", line.buf);
- }
-
- strbuf_release(&line);
- fclose(in);
- }
- else if (errno != ENOENT)
- die_errno(_("unable to read alternates file"));
-
- if (found) {
- rollback_lock_file(&lock);
- } else {
- fprintf_or_die(out, "%s\n", reference);
- if (commit_lock_file(&lock))
- die_errno(_("unable to move new alternates file into place"));
- if (the_repository->objects->loaded_alternates)
- link_alt_odb_entries(the_repository, reference,
- '\n', NULL, 0);
- }
- free(alts);
-}
-
-void add_to_alternates_memory(const char *reference)
-{
- /*
- * Make sure alternates are initialized, or else our entry may be
- * overwritten when they are.
- */
- prepare_alt_odb(the_repository);
-
- link_alt_odb_entries(the_repository, reference,
- '\n', NULL, 0);
-}
-
-struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy)
-{
- struct object_directory *new_odb;
-
- /*
- * Make sure alternates are initialized, or else our entry may be
- * overwritten when they are.
- */
- prepare_alt_odb(the_repository);
-
- /*
- * Make a new primary odb and link the old primary ODB in as an
- * alternate
- */
- new_odb = xcalloc(1, sizeof(*new_odb));
- new_odb->path = xstrdup(dir);
-
- /*
- * Disable ref updates while a temporary odb is active, since
- * the objects in the database may roll back.
- */
- new_odb->disable_ref_updates = 1;
- new_odb->will_destroy = will_destroy;
- new_odb->next = the_repository->objects->odb;
- the_repository->objects->odb = new_odb;
- return new_odb->next;
-}
-
-void restore_primary_odb(struct object_directory *restore_odb, const char *old_path)
-{
- struct object_directory *cur_odb = the_repository->objects->odb;
-
- if (strcmp(old_path, cur_odb->path))
- BUG("expected %s as primary object store; found %s",
- old_path, cur_odb->path);
-
- if (cur_odb->next != restore_odb)
- BUG("we expect the old primary object store to be the first alternate");
-
- the_repository->objects->odb = restore_odb;
- free_object_directory(cur_odb);
-}
-
-/*
- * Compute the exact path an alternate is at and returns it. In case of
- * error NULL is returned and the human readable error is added to `err`
- * `path` may be relative and should point to $GIT_DIR.
- * `err` must not be null.
- */
-char *compute_alternate_path(const char *path, struct strbuf *err)
-{
- char *ref_git = NULL;
- const char *repo;
- int seen_error = 0;
-
- ref_git = real_pathdup(path, 0);
- if (!ref_git) {
- seen_error = 1;
- strbuf_addf(err, _("path '%s' does not exist"), path);
- goto out;
- }
-
- repo = read_gitfile(ref_git);
- if (!repo)
- repo = read_gitfile(mkpath("%s/.git", ref_git));
- if (repo) {
- free(ref_git);
- ref_git = xstrdup(repo);
- }
-
- if (!repo && is_directory(mkpath("%s/.git/objects", ref_git))) {
- char *ref_git_git = mkpathdup("%s/.git", ref_git);
- free(ref_git);
- ref_git = ref_git_git;
- } else if (!is_directory(mkpath("%s/objects", ref_git))) {
- struct strbuf sb = STRBUF_INIT;
- seen_error = 1;
- if (get_common_dir(&sb, ref_git)) {
- strbuf_addf(err,
- _("reference repository '%s' as a linked "
- "checkout is not supported yet."),
- path);
- goto out;
- }
-
- strbuf_addf(err, _("reference repository '%s' is not a "
- "local repository."), path);
- goto out;
- }
-
- if (!access(mkpath("%s/shallow", ref_git), F_OK)) {
- strbuf_addf(err, _("reference repository '%s' is shallow"),
- path);
- seen_error = 1;
- goto out;
- }
-
- if (!access(mkpath("%s/info/grafts", ref_git), F_OK)) {
- strbuf_addf(err,
- _("reference repository '%s' is grafted"),
- path);
- seen_error = 1;
- goto out;
- }
-
-out:
- if (seen_error) {
- FREE_AND_NULL(ref_git);
- }
-
- return ref_git;
-}
-
-struct object_directory *find_odb(struct repository *r, const char *obj_dir)
-{
- struct object_directory *odb;
- char *obj_dir_real = real_pathdup(obj_dir, 1);
- struct strbuf odb_path_real = STRBUF_INIT;
-
- prepare_alt_odb(r);
- for (odb = r->objects->odb; odb; odb = odb->next) {
- strbuf_realpath(&odb_path_real, odb->path, 1);
- if (!strcmp(obj_dir_real, odb_path_real.buf))
- break;
- }
-
- free(obj_dir_real);
- strbuf_release(&odb_path_real);
-
- if (!odb)
- die(_("could not find object directory matching %s"), obj_dir);
- return odb;
-}
-
-static void fill_alternate_refs_command(struct child_process *cmd,
- const char *repo_path)
-{
- const char *value;
-
- if (!git_config_get_value("core.alternateRefsCommand", &value)) {
- cmd->use_shell = 1;
-
- strvec_push(&cmd->args, value);
- strvec_push(&cmd->args, repo_path);
- } else {
- cmd->git_cmd = 1;
-
- strvec_pushf(&cmd->args, "--git-dir=%s", repo_path);
- strvec_push(&cmd->args, "for-each-ref");
- strvec_push(&cmd->args, "--format=%(objectname)");
-
- if (!git_config_get_value("core.alternateRefsPrefixes", &value)) {
- strvec_push(&cmd->args, "--");
- strvec_split(&cmd->args, value);
- }
- }
-
- strvec_pushv(&cmd->env, (const char **)local_repo_env);
- cmd->out = -1;
-}
-
-static void read_alternate_refs(const char *path,
- alternate_ref_fn *cb,
- void *data)
-{
- struct child_process cmd = CHILD_PROCESS_INIT;
- struct strbuf line = STRBUF_INIT;
- FILE *fh;
-
- fill_alternate_refs_command(&cmd, path);
-
- if (start_command(&cmd))
- return;
-
- fh = xfdopen(cmd.out, "r");
- while (strbuf_getline_lf(&line, fh) != EOF) {
- struct object_id oid;
- const char *p;
-
- if (parse_oid_hex(line.buf, &oid, &p) || *p) {
- warning(_("invalid line while parsing alternate refs: %s"),
- line.buf);
- break;
- }
-
- cb(&oid, data);
- }
-
- fclose(fh);
- finish_command(&cmd);
- strbuf_release(&line);
-}
-
-struct alternate_refs_data {
- alternate_ref_fn *fn;
- void *data;
-};
-
-static int refs_from_alternate_cb(struct object_directory *e,
- void *data)
-{
- struct strbuf path = STRBUF_INIT;
- size_t base_len;
- struct alternate_refs_data *cb = data;
-
- if (!strbuf_realpath(&path, e->path, 0))
- goto out;
- if (!strbuf_strip_suffix(&path, "/objects"))
- goto out;
- base_len = path.len;
-
- /* Is this a git repository with refs? */
- strbuf_addstr(&path, "/refs");
- if (!is_directory(path.buf))
- goto out;
- strbuf_setlen(&path, base_len);
-
- read_alternate_refs(path.buf, cb->fn, cb->data);
-
-out:
- strbuf_release(&path);
- return 0;
-}
-
-void for_each_alternate_ref(alternate_ref_fn fn, void *data)
-{
- struct alternate_refs_data cb;
- cb.fn = fn;
- cb.data = data;
- foreach_alt_odb(refs_from_alternate_cb, &cb);
-}
-
-int foreach_alt_odb(alt_odb_fn fn, void *cb)
-{
- struct object_directory *ent;
- int r = 0;
-
- prepare_alt_odb(the_repository);
- for (ent = the_repository->objects->odb->next; ent; ent = ent->next) {
- r = fn(ent, cb);
- if (r)
- break;
- }
- return r;
-}
-
-void prepare_alt_odb(struct repository *r)
-{
- if (r->objects->loaded_alternates)
- return;
-
- link_alt_odb_entries(r, r->objects->alternate_db, PATH_SEP, NULL, 0);
-
- read_info_alternates(r, r->objects->odb->path, 0);
- r->objects->loaded_alternates = 1;
-}
-
-int has_alt_odb(struct repository *r)
-{
- prepare_alt_odb(r);
- return !!r->objects->odb->next;
-}
-
/* Returns 1 if we have successfully freshened the file, 0 otherwise. */
static int freshen_file(const char *fn)
{
@@ -1102,54 +130,6 @@ int has_loose_object(const struct object_id *oid)
return check_and_freshen(oid, 0);
}
-static void mmap_limit_check(size_t length)
-{
- static size_t limit = 0;
- if (!limit) {
- limit = git_env_ulong("GIT_MMAP_LIMIT", 0);
- if (!limit)
- limit = SIZE_MAX;
- }
- if (length > limit)
- die(_("attempting to mmap %"PRIuMAX" over limit %"PRIuMAX),
- (uintmax_t)length, (uintmax_t)limit);
-}
-
-void *xmmap_gently(void *start, size_t length,
- int prot, int flags, int fd, off_t offset)
-{
- void *ret;
-
- mmap_limit_check(length);
- ret = mmap(start, length, prot, flags, fd, offset);
- if (ret == MAP_FAILED && !length)
- ret = NULL;
- return ret;
-}
-
-const char *mmap_os_err(void)
-{
- static const char blank[] = "";
-#if defined(__linux__)
- if (errno == ENOMEM) {
- /* this continues an existing error message: */
- static const char enomem[] =
-", check sys.vm.max_map_count and/or RLIMIT_DATA";
- return enomem;
- }
-#endif /* OS-specific bits */
- return blank;
-}
-
-void *xmmap(void *start, size_t length,
- int prot, int flags, int fd, off_t offset)
-{
- void *ret = xmmap_gently(start, length, prot, flags, fd, offset);
- if (ret == MAP_FAILED)
- die_errno(_("mmap failed%s"), mmap_os_err());
- return ret;
-}
-
static int format_object_header_literally(char *str, size_t size,
const char *type, size_t objsize)
{
@@ -1217,33 +197,6 @@ int stream_object_signature(struct repository *r, const struct object_id *oid)
return !oideq(oid, &real_oid) ? -1 : 0;
}
-int git_open_cloexec(const char *name, int flags)
-{
- int fd;
- static int o_cloexec = O_CLOEXEC;
-
- fd = open(name, flags | o_cloexec);
- if ((o_cloexec & O_CLOEXEC) && fd < 0 && errno == EINVAL) {
- /* Try again w/o O_CLOEXEC: the kernel might not support it */
- o_cloexec &= ~O_CLOEXEC;
- fd = open(name, flags | o_cloexec);
- }
-
-#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
- {
- static int fd_cloexec = FD_CLOEXEC;
-
- if (!o_cloexec && 0 <= fd && fd_cloexec) {
- /* Opened w/o O_CLOEXEC? try with fcntl(2) to add it */
- int flags = fcntl(fd, F_GETFD);
- if (fcntl(fd, F_SETFD, flags | fd_cloexec))
- fd_cloexec = 0;
- }
- }
-#endif
- return fd;
-}
-
/*
* Find "oid" as a loose object in the local repository or in an alternate.
* Returns 0 on success, negative on failure.
@@ -1362,7 +315,7 @@ enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
obj_read_unlock();
status = git_inflate(stream, 0);
obj_read_lock();
- if (status < Z_OK)
+ if (status != Z_OK && status != Z_STREAM_END)
return ULHR_BAD;
/*
@@ -1385,20 +338,19 @@ enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
* reading the stream.
*/
strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer);
- stream->next_out = buffer;
- stream->avail_out = bufsiz;
do {
+ stream->next_out = buffer;
+ stream->avail_out = bufsiz;
+
obj_read_unlock();
status = git_inflate(stream, 0);
obj_read_lock();
strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer);
if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
return 0;
- stream->next_out = buffer;
- stream->avail_out = bufsiz;
- } while (status != Z_STREAM_END);
- return ULHR_TOO_LONG;
+ } while (status == Z_OK);
+ return ULHR_BAD;
}
static void *unpack_loose_rest(git_zstream *stream,
@@ -1437,18 +389,17 @@ static void *unpack_loose_rest(git_zstream *stream,
obj_read_lock();
}
}
- if (status == Z_STREAM_END && !stream->avail_in) {
- git_inflate_end(stream);
- return buf;
- }
- if (status < 0)
+ if (status != Z_STREAM_END) {
error(_("corrupt loose object '%s'"), oid_to_hex(oid));
- else if (stream->avail_in)
+ FREE_AND_NULL(buf);
+ } else if (stream->avail_in) {
error(_("garbage at end of loose object '%s'"),
oid_to_hex(oid));
- free(buf);
- return NULL;
+ FREE_AND_NULL(buf);
+ }
+
+ return buf;
}
/*
@@ -1514,9 +465,9 @@ int parse_loose_header(const char *hdr, struct object_info *oi)
return 0;
}
-static int loose_object_info(struct repository *r,
- const struct object_id *oid,
- struct object_info *oi, int flags)
+int loose_object_info(struct repository *r,
+ const struct object_id *oid,
+ struct object_info *oi, int flags)
{
int status = 0;
int fd;
@@ -1580,6 +531,8 @@ static int loose_object_info(struct repository *r,
if (!oi->contentp)
break;
+ if (hdrbuf.len)
+ BUG("unpacking content with unknown types not yet supported");
*oi->contentp = unpack_loose_rest(&stream, hdr, *oi->sizep, oid);
if (*oi->contentp)
goto cleanup;
@@ -1600,8 +553,8 @@ static int loose_object_info(struct repository *r,
die(_("loose object %s (stored in %s) is corrupt"),
oid_to_hex(oid), path);
- git_inflate_end(&stream);
cleanup:
+ git_inflate_end(&stream);
munmap(map, mapsize);
if (oi->sizep == &size_scratch)
oi->sizep = NULL;
@@ -1612,345 +565,6 @@ cleanup:
return status;
}
-int obj_read_use_lock = 0;
-pthread_mutex_t obj_read_mutex;
-
-void enable_obj_read_lock(void)
-{
- if (obj_read_use_lock)
- return;
-
- obj_read_use_lock = 1;
- init_recursive_mutex(&obj_read_mutex);
-}
-
-void disable_obj_read_lock(void)
-{
- if (!obj_read_use_lock)
- return;
-
- obj_read_use_lock = 0;
- pthread_mutex_destroy(&obj_read_mutex);
-}
-
-int fetch_if_missing = 1;
-
-static int do_oid_object_info_extended(struct repository *r,
- const struct object_id *oid,
- struct object_info *oi, unsigned flags)
-{
- static struct object_info blank_oi = OBJECT_INFO_INIT;
- const struct cached_object *co;
- struct pack_entry e;
- int rtype;
- const struct object_id *real = oid;
- int already_retried = 0;
-
-
- if (flags & OBJECT_INFO_LOOKUP_REPLACE)
- real = lookup_replace_object(r, oid);
-
- if (is_null_oid(real))
- return -1;
-
- if (!oi)
- oi = &blank_oi;
-
- co = find_cached_object(real);
- if (co) {
- if (oi->typep)
- *(oi->typep) = co->type;
- if (oi->sizep)
- *(oi->sizep) = co->size;
- if (oi->disk_sizep)
- *(oi->disk_sizep) = 0;
- if (oi->delta_base_oid)
- oidclr(oi->delta_base_oid, the_repository->hash_algo);
- if (oi->type_name)
- strbuf_addstr(oi->type_name, type_name(co->type));
- if (oi->contentp)
- *oi->contentp = xmemdupz(co->buf, co->size);
- oi->whence = OI_CACHED;
- return 0;
- }
-
- while (1) {
- if (find_pack_entry(r, real, &e))
- break;
-
- /* Most likely it's a loose object. */
- if (!loose_object_info(r, real, oi, flags))
- return 0;
-
- /* Not a loose object; someone else may have just packed it. */
- if (!(flags & OBJECT_INFO_QUICK)) {
- reprepare_packed_git(r);
- if (find_pack_entry(r, real, &e))
- break;
- }
-
- /*
- * If r is the_repository, this might be an attempt at
- * accessing a submodule object as if it were in the_repository
- * (having called add_submodule_odb() on that submodule's ODB).
- * If any such ODBs exist, register them and try again.
- */
- if (r == the_repository &&
- register_all_submodule_odb_as_alternates())
- /* We added some alternates; retry */
- continue;
-
- /* Check if it is a missing object */
- if (fetch_if_missing && repo_has_promisor_remote(r) &&
- !already_retried &&
- !(flags & OBJECT_INFO_SKIP_FETCH_OBJECT)) {
- promisor_remote_get_direct(r, real, 1);
- already_retried = 1;
- continue;
- }
-
- if (flags & OBJECT_INFO_DIE_IF_CORRUPT) {
- const struct packed_git *p;
- if ((flags & OBJECT_INFO_LOOKUP_REPLACE) && !oideq(real, oid))
- die(_("replacement %s not found for %s"),
- oid_to_hex(real), oid_to_hex(oid));
- if ((p = has_packed_and_bad(r, real)))
- die(_("packed object %s (stored in %s) is corrupt"),
- oid_to_hex(real), p->pack_name);
- }
- return -1;
- }
-
- if (oi == &blank_oi)
- /*
- * We know that the caller doesn't actually need the
- * information below, so return early.
- */
- return 0;
- rtype = packed_object_info(r, e.p, e.offset, oi);
- if (rtype < 0) {
- mark_bad_packed_object(e.p, real);
- return do_oid_object_info_extended(r, real, oi, 0);
- } else if (oi->whence == OI_PACKED) {
- oi->u.packed.offset = e.offset;
- oi->u.packed.pack = e.p;
- oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||
- rtype == OBJ_OFS_DELTA);
- }
-
- return 0;
-}
-
-static int oid_object_info_convert(struct repository *r,
- const struct object_id *input_oid,
- struct object_info *input_oi, unsigned flags)
-{
- const struct git_hash_algo *input_algo = &hash_algos[input_oid->algo];
- int do_die = flags & OBJECT_INFO_DIE_IF_CORRUPT;
- struct strbuf type_name = STRBUF_INIT;
- struct object_id oid, delta_base_oid;
- struct object_info new_oi, *oi;
- unsigned long size;
- void *content;
- int ret;
-
- if (repo_oid_to_algop(r, input_oid, the_hash_algo, &oid)) {
- if (do_die)
- die(_("missing mapping of %s to %s"),
- oid_to_hex(input_oid), the_hash_algo->name);
- return -1;
- }
-
- /* Is new_oi needed? */
- oi = input_oi;
- if (input_oi && (input_oi->delta_base_oid || input_oi->sizep ||
- input_oi->contentp)) {
- new_oi = *input_oi;
- /* Does delta_base_oid need to be converted? */
- if (input_oi->delta_base_oid)
- new_oi.delta_base_oid = &delta_base_oid;
- /* Will the attributes differ when converted? */
- if (input_oi->sizep || input_oi->contentp) {
- new_oi.contentp = &content;
- new_oi.sizep = &size;
- new_oi.type_name = &type_name;
- }
- oi = &new_oi;
- }
-
- ret = oid_object_info_extended(r, &oid, oi, flags);
- if (ret)
- return -1;
- if (oi == input_oi)
- return ret;
-
- if (new_oi.contentp) {
- struct strbuf outbuf = STRBUF_INIT;
- enum object_type type;
-
- type = type_from_string_gently(type_name.buf, type_name.len,
- !do_die);
- if (type == -1)
- return -1;
- if (type != OBJ_BLOB) {
- ret = convert_object_file(&outbuf,
- the_hash_algo, input_algo,
- content, size, type, !do_die);
- free(content);
- if (ret == -1)
- return -1;
- size = outbuf.len;
- content = strbuf_detach(&outbuf, NULL);
- }
- if (input_oi->sizep)
- *input_oi->sizep = size;
- if (input_oi->contentp)
- *input_oi->contentp = content;
- else
- free(content);
- if (input_oi->type_name)
- *input_oi->type_name = type_name;
- else
- strbuf_release(&type_name);
- }
- if (new_oi.delta_base_oid == &delta_base_oid) {
- if (repo_oid_to_algop(r, &delta_base_oid, input_algo,
- input_oi->delta_base_oid)) {
- if (do_die)
- die(_("missing mapping of %s to %s"),
- oid_to_hex(&delta_base_oid),
- input_algo->name);
- return -1;
- }
- }
- input_oi->whence = new_oi.whence;
- input_oi->u = new_oi.u;
- return ret;
-}
-
-int oid_object_info_extended(struct repository *r, const struct object_id *oid,
- struct object_info *oi, unsigned flags)
-{
- int ret;
-
- if (oid->algo && (hash_algo_by_ptr(r->hash_algo) != oid->algo))
- return oid_object_info_convert(r, oid, oi, flags);
-
- obj_read_lock();
- ret = do_oid_object_info_extended(r, oid, oi, flags);
- obj_read_unlock();
- return ret;
-}
-
-
-/* returns enum object_type or negative */
-int oid_object_info(struct repository *r,
- const struct object_id *oid,
- unsigned long *sizep)
-{
- enum object_type type;
- struct object_info oi = OBJECT_INFO_INIT;
-
- oi.typep = &type;
- oi.sizep = sizep;
- if (oid_object_info_extended(r, oid, &oi,
- OBJECT_INFO_LOOKUP_REPLACE) < 0)
- return -1;
- return type;
-}
-
-int pretend_object_file(void *buf, unsigned long len, enum object_type type,
- struct object_id *oid)
-{
- struct cached_object_entry *co;
- char *co_buf;
-
- hash_object_file(the_hash_algo, buf, len, type, oid);
- if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
- find_cached_object(oid))
- return 0;
- ALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);
- co = &cached_objects[cached_object_nr++];
- co->value.size = len;
- co->value.type = type;
- co_buf = xmalloc(len);
- memcpy(co_buf, buf, len);
- co->value.buf = co_buf;
- oidcpy(&co->oid, oid);
- return 0;
-}
-
-/*
- * This function dies on corrupt objects; the callers who want to
- * deal with them should arrange to call oid_object_info_extended() and give
- * error messages themselves.
- */
-void *repo_read_object_file(struct repository *r,
- const struct object_id *oid,
- enum object_type *type,
- unsigned long *size)
-{
- struct object_info oi = OBJECT_INFO_INIT;
- unsigned flags = OBJECT_INFO_DIE_IF_CORRUPT | OBJECT_INFO_LOOKUP_REPLACE;
- void *data;
-
- oi.typep = type;
- oi.sizep = size;
- oi.contentp = &data;
- if (oid_object_info_extended(r, oid, &oi, flags))
- return NULL;
-
- return data;
-}
-
-void *read_object_with_reference(struct repository *r,
- const struct object_id *oid,
- enum object_type required_type,
- unsigned long *size,
- struct object_id *actual_oid_return)
-{
- enum object_type type;
- void *buffer;
- unsigned long isize;
- struct object_id actual_oid;
-
- oidcpy(&actual_oid, oid);
- while (1) {
- int ref_length = -1;
- const char *ref_type = NULL;
-
- buffer = repo_read_object_file(r, &actual_oid, &type, &isize);
- if (!buffer)
- return NULL;
- if (type == required_type) {
- *size = isize;
- if (actual_oid_return)
- oidcpy(actual_oid_return, &actual_oid);
- return buffer;
- }
- /* Handle references */
- else if (type == OBJ_COMMIT)
- ref_type = "tree ";
- else if (type == OBJ_TAG)
- ref_type = "object ";
- else {
- free(buffer);
- return NULL;
- }
- ref_length = strlen(ref_type);
-
- if (ref_length + the_hash_algo->hexsz > isize ||
- memcmp(buffer, ref_type, ref_length) ||
- get_oid_hex((char *) buffer + ref_length, &actual_oid)) {
- free(buffer);
- return NULL;
- }
- free(buffer);
- /* Now we have the ID of the referred-to object in
- * actual_oid. Check again. */
- }
-}
-
static void hash_object_body(const struct git_hash_algo *algo, struct git_hash_ctx *c,
const void *buf, unsigned long len,
struct object_id *oid,
@@ -2222,7 +836,7 @@ static int start_loose_object_common(struct strbuf *tmp_file,
fd = create_tmpfile(tmp_file, filename);
if (fd < 0) {
- if (flags & HASH_SILENT)
+ if (flags & WRITE_OBJECT_FILE_SILENT)
return -1;
else if (errno == EACCES)
return error(_("insufficient permission for adding "
@@ -2354,7 +968,7 @@ static int write_loose_object(const struct object_id *oid, char *hdr,
utb.actime = mtime;
utb.modtime = mtime;
if (utime(tmp_file.buf, &utb) < 0 &&
- !(flags & HASH_SILENT))
+ !(flags & WRITE_OBJECT_FILE_SILENT))
warning_errno(_("failed utime() on %s"), tmp_file.buf);
}
@@ -2473,7 +1087,8 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,
struct strbuf dir = STRBUF_INIT;
strbuf_add(&dir, filename.buf, dirlen);
- if (mkdir_in_gitdir(dir.buf) && errno != EEXIST) {
+ if (safe_create_dir_in_gitdir(the_repository, dir.buf) &&
+ errno != EEXIST) {
err = error_errno(_("unable to create directory %s"), dir.buf);
strbuf_release(&dir);
goto cleanup;
@@ -2510,7 +1125,7 @@ int write_object_file_flags(const void *buf, unsigned long len,
hash_object_file(compat, buf, len, type, &compat_oid);
else {
struct strbuf converted = STRBUF_INIT;
- convert_object_file(&converted, algo, compat,
+ convert_object_file(the_repository, &converted, algo, compat,
buf, len, type, 0);
hash_object_file(compat, converted.buf, converted.len,
type, &compat_oid);
@@ -2550,7 +1165,8 @@ int write_object_file_literally(const void *buf, unsigned long len,
&compat_oid);
else if (compat_type != -1) {
struct strbuf converted = STRBUF_INIT;
- convert_object_file(&converted, algo, compat,
+ convert_object_file(the_repository,
+ &converted, algo, compat,
buf, len, compat_type, 0);
hash_object_file(compat, converted.buf, converted.len,
compat_type, &compat_oid);
@@ -2564,7 +1180,7 @@ int write_object_file_literally(const void *buf, unsigned long len,
write_object_file_prepare_literally(the_hash_algo, buf, len, type,
oid, header, &hdrlen);
- if (!(flags & HASH_WRITE_OBJECT))
+ if (!(flags & WRITE_OBJECT_FILE_PERSIST))
goto cleanup;
if (freshen_packed_object(oid) || freshen_loose_object(oid))
goto cleanup;
@@ -2611,32 +1227,6 @@ int force_object_loose(const struct object_id *oid, time_t mtime)
return ret;
}
-int has_object(struct repository *r, const struct object_id *oid,
- unsigned flags)
-{
- int quick = !(flags & HAS_OBJECT_RECHECK_PACKED);
- unsigned object_info_flags = OBJECT_INFO_SKIP_FETCH_OBJECT |
- (quick ? OBJECT_INFO_QUICK : 0);
-
- if (!startup_info->have_repository)
- return 0;
- return oid_object_info_extended(r, oid, NULL, object_info_flags) >= 0;
-}
-
-int repo_has_object_file_with_flags(struct repository *r,
- const struct object_id *oid, int flags)
-{
- if (!startup_info->have_repository)
- return 0;
- return oid_object_info_extended(r, oid, NULL, flags) >= 0;
-}
-
-int repo_has_object_file(struct repository *r,
- const struct object_id *oid)
-{
- return repo_has_object_file_with_flags(r, oid, 0);
-}
-
/*
* We can't use the normal fsck_error_function() for index_mem(),
* because we don't yet have a valid oid for it to report. Instead,
@@ -2661,7 +1251,7 @@ static int index_mem(struct index_state *istate,
{
struct strbuf nbuf = STRBUF_INIT;
int ret = 0;
- int write_object = flags & HASH_WRITE_OBJECT;
+ int write_object = flags & INDEX_WRITE_OBJECT;
if (!type)
type = OBJ_BLOB;
@@ -2676,12 +1266,12 @@ static int index_mem(struct index_state *istate,
size = nbuf.len;
}
}
- if (flags & HASH_FORMAT_CHECK) {
+ if (flags & INDEX_FORMAT_CHECK) {
struct fsck_options opts = FSCK_OPTIONS_DEFAULT;
opts.strict = 1;
opts.error_func = hash_format_check_report;
- if (fsck_buffer(null_oid(), type, buf, size, &opts))
+ if (fsck_buffer(null_oid(the_hash_algo), type, buf, size, &opts))
die(_("refusing to create malformed object"));
fsck_finish(&opts);
}
@@ -2702,11 +1292,11 @@ static int index_stream_convert_blob(struct index_state *istate,
unsigned flags)
{
int ret = 0;
- const int write_object = flags & HASH_WRITE_OBJECT;
+ const int write_object = flags & INDEX_WRITE_OBJECT;
struct strbuf sbuf = STRBUF_INIT;
assert(path);
- assert(would_convert_to_git_filter_fd(istate, path));
+ ASSERT(would_convert_to_git_filter_fd(istate, path));
convert_to_git_filter_fd(istate, path, fd, &sbuf,
get_conv_flags(flags));
@@ -2767,28 +1357,6 @@ static int index_core(struct index_state *istate,
return ret;
}
-/*
- * This creates one packfile per large blob unless bulk-checkin
- * machinery is "plugged".
- *
- * This also bypasses the usual "convert-to-git" dance, and that is on
- * purpose. We could write a streaming version of the converting
- * functions and insert that before feeding the data to fast-import
- * (or equivalent in-core API described above). However, that is
- * somewhat complicated, as we do not know the size of the filter
- * result, which we need to know beforehand when writing a git object.
- * Since the primary motivation for trying to stream from the working
- * tree file and to avoid mmaping it in core is to deal with large
- * binary blobs, they generally do not want to get any conversion, and
- * callers should avoid this code path when filters are requested.
- */
-static int index_blob_stream(struct object_id *oid, int fd, size_t size,
- const char *path,
- unsigned flags)
-{
- return index_blob_bulk_checkin(oid, fd, size, path, flags);
-}
-
int index_fd(struct index_state *istate, struct object_id *oid,
int fd, struct stat *st,
enum object_type type, const char *path, unsigned flags)
@@ -2803,13 +1371,14 @@ int index_fd(struct index_state *istate, struct object_id *oid,
ret = index_stream_convert_blob(istate, oid, fd, path, flags);
else if (!S_ISREG(st->st_mode))
ret = index_pipe(istate, oid, fd, type, path, flags);
- else if (st->st_size <= big_file_threshold || type != OBJ_BLOB ||
+ else if (st->st_size <= repo_settings_get_big_file_threshold(the_repository) ||
+ type != OBJ_BLOB ||
(path && would_convert_to_git(istate, path)))
ret = index_core(istate, oid, fd, xsize_t(st->st_size),
type, path, flags);
else
- ret = index_blob_stream(oid, fd, xsize_t(st->st_size), path,
- flags);
+ ret = index_blob_bulk_checkin(oid, fd, xsize_t(st->st_size), path,
+ flags);
close(fd);
return ret;
}
@@ -2833,7 +1402,7 @@ int index_path(struct index_state *istate, struct object_id *oid,
case S_IFLNK:
if (strbuf_readlink(&sb, path, st->st_size))
return error_errno("readlink(\"%s\")", path);
- if (!(flags & HASH_WRITE_OBJECT))
+ if (!(flags & INDEX_WRITE_OBJECT))
hash_object_file(the_hash_algo, sb.buf, sb.len,
OBJ_BLOB, oid);
else if (write_object_file(sb.buf, sb.len, OBJ_BLOB, oid))
@@ -2863,16 +1432,6 @@ int read_pack_header(int fd, struct pack_header *header)
return 0;
}
-void assert_oid_type(const struct object_id *oid, enum object_type expect)
-{
- enum object_type type = oid_object_info(the_repository, oid, NULL);
- if (type < 0)
- die(_("%s is not a valid object"), oid_to_hex(oid));
- if (type != expect)
- die(_("%s is not a valid '%s' object"), oid_to_hex(oid),
- type_name(expect));
-}
-
int for_each_file_in_obj_subdir(unsigned int subdir_nr,
struct strbuf *path,
each_loose_object_fn obj_cb,
@@ -3080,7 +1639,6 @@ static int check_stream_oid(git_zstream *stream,
git_hash_update(&c, buf, stream->next_out - buf);
total_read += stream->next_out - buf;
}
- git_inflate_end(stream);
if (status != Z_STREAM_END) {
error(_("corrupt loose object '%s'"), oid_to_hex(expected_oid));
@@ -3127,35 +1685,35 @@ int read_loose_object(const char *path,
if (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr),
NULL) != ULHR_OK) {
error(_("unable to unpack header of %s"), path);
- git_inflate_end(&stream);
- goto out;
+ goto out_inflate;
}
if (parse_loose_header(hdr, oi) < 0) {
error(_("unable to parse header of %s"), path);
- git_inflate_end(&stream);
- goto out;
+ goto out_inflate;
}
- if (*oi->typep == OBJ_BLOB && *size > big_file_threshold) {
+ if (*oi->typep == OBJ_BLOB &&
+ *size > repo_settings_get_big_file_threshold(the_repository)) {
if (check_stream_oid(&stream, hdr, *size, path, expected_oid) < 0)
- goto out;
+ goto out_inflate;
} else {
*contents = unpack_loose_rest(&stream, hdr, *size, expected_oid);
if (!*contents) {
error(_("unable to unpack contents of %s"), path);
- git_inflate_end(&stream);
- goto out;
+ goto out_inflate;
}
hash_object_file_literally(the_repository->hash_algo,
*contents, *size,
oi->type_name->buf, real_oid);
if (!oideq(expected_oid, real_oid))
- goto out;
+ goto out_inflate;
}
ret = 0; /* everything checks out */
+out_inflate:
+ git_inflate_end(&stream);
out:
if (map)
munmap(map, mapsize);
diff --git a/object-file.h b/object-file.h
index 81b30d269c..c002fbe234 100644
--- a/object-file.h
+++ b/object-file.h
@@ -14,50 +14,37 @@ struct index_state;
*/
extern int fetch_if_missing;
-#define HASH_WRITE_OBJECT 1
-#define HASH_FORMAT_CHECK 2
-#define HASH_RENORMALIZE 4
-#define HASH_SILENT 8
+enum {
+ INDEX_WRITE_OBJECT = (1 << 0),
+ INDEX_FORMAT_CHECK = (1 << 1),
+ INDEX_RENORMALIZE = (1 << 2),
+};
+
int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);
+struct object_directory;
+
+const char *odb_loose_path(struct object_directory *odb,
+ struct strbuf *buf,
+ const struct object_id *oid);
+
/*
- * Create the directory containing the named path, using care to be
- * somewhat safe against races. Return one of the scld_error values to
- * indicate success/failure. On error, set errno to describe the
- * problem.
- *
- * SCLD_VANISHED indicates that one of the ancestor directories of the
- * path existed at one point during the function call and then
- * suddenly vanished, probably because another process pruned the
- * directory while we were working. To be robust against this kind of
- * race, callers might want to try invoking the function again when it
- * returns SCLD_VANISHED.
- *
- * safe_create_leading_directories() temporarily changes path while it
- * is working but restores it before returning.
- * safe_create_leading_directories_const() doesn't modify path, even
- * temporarily. Both these variants adjust the permissions of the
- * created directories to honor core.sharedRepository, so they are best
- * suited for files inside the git dir. For working tree files, use
- * safe_create_leading_directories_no_share() instead, as it ignores
- * the core.sharedRepository setting.
+ * Return true iff an alternate object database has a loose object
+ * with the specified name. This function does not respect replace
+ * references.
*/
-enum scld_error {
- SCLD_OK = 0,
- SCLD_FAILED = -1,
- SCLD_PERMS = -2,
- SCLD_EXISTS = -3,
- SCLD_VANISHED = -4
-};
-enum scld_error safe_create_leading_directories(char *path);
-enum scld_error safe_create_leading_directories_const(const char *path);
-enum scld_error safe_create_leading_directories_no_share(char *path);
+int has_loose_object_nonlocal(const struct object_id *);
-int mkdir_in_gitdir(const char *path);
+int has_loose_object(const struct object_id *);
-int git_open_cloexec(const char *name, int flags);
-#define git_open(name) git_open_cloexec(name, O_RDONLY)
+/**
+ * format_object_header() is a thin wrapper around s xsnprintf() that
+ * writes the initial "<type> <obj-len>" part of the loose object
+ * header. It returns the size that snprintf() returns + 1.
+ */
+int format_object_header(char *str, size_t size, enum object_type type,
+ size_t objsize);
/**
* unpack_loose_header() initializes the data stream needed to unpack
@@ -99,6 +86,44 @@ enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
struct object_info;
int parse_loose_header(const char *hdr, struct object_info *oi);
+enum {
+ /*
+ * By default, `write_object_file_literally()` does not actually write
+ * anything into the object store, but only computes the object ID.
+ * This flag changes that so that the object will be written as a loose
+ * object and persisted.
+ */
+ WRITE_OBJECT_FILE_PERSIST = (1 << 0),
+
+ /*
+ * Do not print an error in case something gose wrong.
+ */
+ WRITE_OBJECT_FILE_SILENT = (1 << 1),
+};
+
+int write_object_file_flags(const void *buf, unsigned long len,
+ enum object_type type, struct object_id *oid,
+ struct object_id *comapt_oid_in, unsigned flags);
+static inline int write_object_file(const void *buf, unsigned long len,
+ enum object_type type, struct object_id *oid)
+{
+ return write_object_file_flags(buf, len, type, oid, NULL, 0);
+}
+
+struct input_stream {
+ const void *(*read)(struct input_stream *, unsigned long *len);
+ void *data;
+ int is_finished;
+};
+
+int write_object_file_literally(const void *buf, unsigned long len,
+ const char *type, struct object_id *oid,
+ unsigned flags);
+int stream_loose_object(struct input_stream *in_stream, size_t len,
+ struct object_id *oid);
+
+int force_object_loose(const struct object_id *oid, time_t mtime);
+
/**
* With in-core object data in "buf", rehash it to make sure the
* object name actually matches "oid" to detect object corruption.
@@ -117,6 +142,10 @@ int check_object_signature(struct repository *r, const struct object_id *oid,
*/
int stream_object_signature(struct repository *r, const struct object_id *oid);
+int loose_object_info(struct repository *r,
+ const struct object_id *oid,
+ struct object_info *oi, int flags);
+
enum finalize_object_file_flags {
FOF_SKIP_COLLISION_CHECK = 1,
};
@@ -128,10 +157,18 @@ int finalize_object_file_flags(const char *tmpfile, const char *filename,
/* Helper to check and "touch" a file */
int check_and_freshen_file(const char *fn, int freshen);
-void *read_object_with_reference(struct repository *r,
- const struct object_id *oid,
- enum object_type required_type,
- unsigned long *size,
- struct object_id *oid_ret);
+/*
+ * Open the loose object at path, check its hash, and return the contents,
+ * use the "oi" argument to assert things about the object, or e.g. populate its
+ * type, and size. If the object is a blob, then "contents" may return NULL,
+ * to allow streaming of large blobs.
+ *
+ * Returns 0 on success, negative on error (details may be written to stderr).
+ */
+int read_loose_object(const char *path,
+ const struct object_id *expected_oid,
+ struct object_id *real_oid,
+ void **contents,
+ struct object_info *oi);
#endif /* OBJECT_FILE_H */
diff --git a/object-name.c b/object-name.c
index 91f731373a..2c751a5352 100644
--- a/object-name.c
+++ b/object-name.c
@@ -19,7 +19,7 @@
#include "oidtree.h"
#include "packfile.h"
#include "pretty.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "read-cache-ll.h"
#include "repo-settings.h"
#include "repository.h"
diff --git a/object-store-ll.h b/object-store-ll.h
deleted file mode 100644
index cd3bd5bd99..0000000000
--- a/object-store-ll.h
+++ /dev/null
@@ -1,556 +0,0 @@
-#ifndef OBJECT_STORE_LL_H
-#define OBJECT_STORE_LL_H
-
-#include "hashmap.h"
-#include "object.h"
-#include "list.h"
-#include "thread-utils.h"
-#include "oidset.h"
-
-struct oidmap;
-struct oidtree;
-struct strbuf;
-struct repository;
-
-struct object_directory {
- struct object_directory *next;
-
- /*
- * Used to store the results of readdir(3) calls when we are OK
- * sacrificing accuracy due to races for speed. That includes
- * object existence with OBJECT_INFO_QUICK, as well as
- * our search for unique abbreviated hashes. Don't use it for tasks
- * requiring greater accuracy!
- *
- * Be sure to call odb_load_loose_cache() before using.
- */
- uint32_t loose_objects_subdir_seen[8]; /* 256 bits */
- struct oidtree *loose_objects_cache;
-
- /* Map between object IDs for loose objects. */
- struct loose_object_map *loose_map;
-
- /*
- * This is a temporary object store created by the tmp_objdir
- * facility. Disable ref updates since the objects in the store
- * might be discarded on rollback.
- */
- int disable_ref_updates;
-
- /*
- * This object store is ephemeral, so there is no need to fsync.
- */
- int will_destroy;
-
- /*
- * Path to the alternative object store. If this is a relative path,
- * it is relative to the current working directory.
- */
- char *path;
-};
-
-struct input_stream {
- const void *(*read)(struct input_stream *, unsigned long *len);
- void *data;
- int is_finished;
-};
-
-void prepare_alt_odb(struct repository *r);
-int has_alt_odb(struct repository *r);
-char *compute_alternate_path(const char *path, struct strbuf *err);
-struct object_directory *find_odb(struct repository *r, const char *obj_dir);
-typedef int alt_odb_fn(struct object_directory *, void *);
-int foreach_alt_odb(alt_odb_fn, void*);
-typedef void alternate_ref_fn(const struct object_id *oid, void *);
-void for_each_alternate_ref(alternate_ref_fn, void *);
-
-/*
- * Add the directory to the on-disk alternates file; the new entry will also
- * take effect in the current process.
- */
-void add_to_alternates_file(const char *dir);
-
-/*
- * Add the directory to the in-memory list of alternates (along with any
- * recursive alternates it points to), but do not modify the on-disk alternates
- * file.
- */
-void add_to_alternates_memory(const char *dir);
-
-/*
- * Replace the current writable object directory with the specified temporary
- * object directory; returns the former primary object directory.
- */
-struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy);
-
-/*
- * Restore a previous ODB replaced by set_temporary_main_odb.
- */
-void restore_primary_odb(struct object_directory *restore_odb, const char *old_path);
-
-/*
- * Populate and return the loose object cache array corresponding to the
- * given object ID.
- */
-struct oidtree *odb_loose_cache(struct object_directory *odb,
- const struct object_id *oid);
-
-/* Empty the loose object cache for the specified object directory. */
-void odb_clear_loose_cache(struct object_directory *odb);
-
-/* Clear and free the specified object directory */
-void free_object_directory(struct object_directory *odb);
-
-struct packed_git {
- struct hashmap_entry packmap_ent;
- struct packed_git *next;
- struct list_head mru;
- struct pack_window *windows;
- off_t pack_size;
- const void *index_data;
- size_t index_size;
- uint32_t num_objects;
- size_t crc_offset;
- struct oidset bad_objects;
- int index_version;
- time_t mtime;
- int pack_fd;
- int index; /* for builtin/pack-objects.c */
- unsigned pack_local:1,
- pack_keep:1,
- pack_keep_in_core:1,
- freshened:1,
- do_not_close:1,
- pack_promisor:1,
- multi_pack_index:1,
- is_cruft:1;
- unsigned char hash[GIT_MAX_RAWSZ];
- struct revindex_entry *revindex;
- const uint32_t *revindex_data;
- const uint32_t *revindex_map;
- size_t revindex_size;
- /*
- * mtimes_map points at the beginning of the memory mapped region of
- * this pack's corresponding .mtimes file, and mtimes_size is the size
- * of that .mtimes file
- */
- const uint32_t *mtimes_map;
- size_t mtimes_size;
-
- /* repo denotes the repository this packfile belongs to */
- struct repository *repo;
-
- /* something like ".git/objects/pack/xxxxx.pack" */
- char pack_name[FLEX_ARRAY]; /* more */
-};
-
-struct multi_pack_index;
-
-static inline int pack_map_entry_cmp(const void *cmp_data UNUSED,
- const struct hashmap_entry *entry,
- const struct hashmap_entry *entry2,
- const void *keydata)
-{
- const char *key = keydata;
- const struct packed_git *pg1, *pg2;
-
- pg1 = container_of(entry, const struct packed_git, packmap_ent);
- pg2 = container_of(entry2, const struct packed_git, packmap_ent);
-
- return strcmp(pg1->pack_name, key ? key : pg2->pack_name);
-}
-
-struct raw_object_store {
- /*
- * Set of all object directories; the main directory is first (and
- * cannot be NULL after initialization). Subsequent directories are
- * alternates.
- */
- struct object_directory *odb;
- struct object_directory **odb_tail;
- struct kh_odb_path_map *odb_by_path;
-
- int loaded_alternates;
-
- /*
- * A list of alternate object directories loaded from the environment;
- * this should not generally need to be accessed directly, but will
- * populate the "odb" list when prepare_alt_odb() is run.
- */
- char *alternate_db;
-
- /*
- * Objects that should be substituted by other objects
- * (see git-replace(1)).
- */
- struct oidmap *replace_map;
- unsigned replace_map_initialized : 1;
- pthread_mutex_t replace_mutex; /* protect object replace functions */
-
- struct commit_graph *commit_graph;
- unsigned commit_graph_attempted : 1; /* if loading has been attempted */
-
- /*
- * private data
- *
- * should only be accessed directly by packfile.c and midx.c
- */
- struct multi_pack_index *multi_pack_index;
-
- /*
- * private data
- *
- * should only be accessed directly by packfile.c
- */
-
- struct packed_git *packed_git;
- /* A most-recently-used ordered version of the packed_git list. */
- struct list_head packed_git_mru;
-
- struct {
- struct packed_git **packs;
- unsigned flags;
- } kept_pack_cache;
-
- /*
- * A map of packfiles to packed_git structs for tracking which
- * packs have been loaded already.
- */
- struct hashmap pack_map;
-
- /*
- * A fast, rough count of the number of objects in the repository.
- * These two fields are not meant for direct access. Use
- * repo_approximate_object_count() instead.
- */
- unsigned long approximate_object_count;
- unsigned approximate_object_count_valid : 1;
-
- /*
- * Whether packed_git has already been populated with this repository's
- * packs.
- */
- unsigned packed_git_initialized : 1;
-};
-
-struct raw_object_store *raw_object_store_new(void);
-void raw_object_store_clear(struct raw_object_store *o);
-
-/*
- * Create a temporary file rooted in the object database directory, or
- * die on failure. The filename is taken from "pattern", which should have the
- * usual "XXXXXX" trailer, and the resulting filename is written into the
- * "template" buffer. Returns the open descriptor.
- */
-int odb_mkstemp(struct strbuf *temp_filename, const char *pattern);
-
-/*
- * Create a pack .keep file named "name" (which should generally be the output
- * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on
- * error.
- */
-int odb_pack_keep(const char *name);
-
-/*
- * Put in `buf` the name of the file in the local object database that
- * would be used to store a loose object with the specified oid.
- */
-const char *loose_object_path(struct repository *r, struct strbuf *buf,
- const struct object_id *oid);
-
-void *map_loose_object(struct repository *r, const struct object_id *oid,
- unsigned long *size);
-
-void *repo_read_object_file(struct repository *r,
- const struct object_id *oid,
- enum object_type *type,
- unsigned long *size);
-
-/* Read and unpack an object file into memory, write memory to an object file */
-int oid_object_info(struct repository *r, const struct object_id *, unsigned long *);
-
-void hash_object_file(const struct git_hash_algo *algo, const void *buf,
- unsigned long len, enum object_type type,
- struct object_id *oid);
-
-int write_object_file_flags(const void *buf, unsigned long len,
- enum object_type type, struct object_id *oid,
- struct object_id *comapt_oid_in, unsigned flags);
-static inline int write_object_file(const void *buf, unsigned long len,
- enum object_type type, struct object_id *oid)
-{
- return write_object_file_flags(buf, len, type, oid, NULL, 0);
-}
-
-int write_object_file_literally(const void *buf, unsigned long len,
- const char *type, struct object_id *oid,
- unsigned flags);
-int stream_loose_object(struct input_stream *in_stream, size_t len,
- struct object_id *oid);
-
-/*
- * Add an object file to the in-memory object store, without writing it
- * to disk.
- *
- * Callers are responsible for calling write_object_file to record the
- * object in persistent storage before writing any other new objects
- * that reference it.
- */
-int pretend_object_file(void *, unsigned long, enum object_type,
- struct object_id *oid);
-
-int force_object_loose(const struct object_id *oid, time_t mtime);
-
-struct object_info {
- /* Request */
- enum object_type *typep;
- unsigned long *sizep;
- off_t *disk_sizep;
- struct object_id *delta_base_oid;
- struct strbuf *type_name;
- void **contentp;
-
- /* Response */
- enum {
- OI_CACHED,
- OI_LOOSE,
- OI_PACKED,
- OI_DBCACHED
- } whence;
- union {
- /*
- * struct {
- * ... Nothing to expose in this case
- * } cached;
- * struct {
- * ... Nothing to expose in this case
- * } loose;
- */
- struct {
- struct packed_git *pack;
- off_t offset;
- unsigned int is_delta;
- } packed;
- } u;
-};
-
-/*
- * Initializer for a "struct object_info" that wants no items. You may
- * also memset() the memory to all-zeroes.
- */
-#define OBJECT_INFO_INIT { 0 }
-
-/* Invoke lookup_replace_object() on the given hash */
-#define OBJECT_INFO_LOOKUP_REPLACE 1
-/* Allow reading from a loose object file of unknown/bogus type */
-#define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2
-/* Do not retry packed storage after checking packed and loose storage */
-#define OBJECT_INFO_QUICK 8
-/*
- * Do not attempt to fetch the object if missing (even if fetch_is_missing is
- * nonzero).
- */
-#define OBJECT_INFO_SKIP_FETCH_OBJECT 16
-/*
- * This is meant for bulk prefetching of missing blobs in a partial
- * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK
- */
-#define OBJECT_INFO_FOR_PREFETCH (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK)
-
-/* Die if object corruption (not just an object being missing) was detected. */
-#define OBJECT_INFO_DIE_IF_CORRUPT 32
-
-int oid_object_info_extended(struct repository *r,
- const struct object_id *,
- struct object_info *, unsigned flags);
-
-/*
- * Open the loose object at path, check its hash, and return the contents,
- * use the "oi" argument to assert things about the object, or e.g. populate its
- * type, and size. If the object is a blob, then "contents" may return NULL,
- * to allow streaming of large blobs.
- *
- * Returns 0 on success, negative on error (details may be written to stderr).
- */
-int read_loose_object(const char *path,
- const struct object_id *expected_oid,
- struct object_id *real_oid,
- void **contents,
- struct object_info *oi);
-
-/* Retry packed storage after checking packed and loose storage */
-#define HAS_OBJECT_RECHECK_PACKED 1
-
-/*
- * Returns 1 if the object exists. This function will not lazily fetch objects
- * in a partial clone.
- */
-int has_object(struct repository *r, const struct object_id *oid,
- unsigned flags);
-
-/*
- * These macros and functions are deprecated. If checking existence for an
- * object that is likely to be missing and/or whose absence is relatively
- * inconsequential (or is consequential but the caller is prepared to handle
- * it), use has_object(), which has better defaults (no lazy fetch in a partial
- * clone and no rechecking of packed storage). In the unlikely event that a
- * caller needs to assert existence of an object that it fully expects to
- * exist, and wants to trigger a lazy fetch in a partial clone, use
- * oid_object_info_extended() with a NULL struct object_info.
- *
- * These functions can be removed once all callers have migrated to
- * has_object() and/or oid_object_info_extended().
- */
-int repo_has_object_file(struct repository *r, const struct object_id *oid);
-int repo_has_object_file_with_flags(struct repository *r,
- const struct object_id *oid, int flags);
-
-/*
- * Return true iff an alternate object database has a loose object
- * with the specified name. This function does not respect replace
- * references.
- */
-int has_loose_object_nonlocal(const struct object_id *);
-
-int has_loose_object(const struct object_id *);
-
-/**
- * format_object_header() is a thin wrapper around s xsnprintf() that
- * writes the initial "<type> <obj-len>" part of the loose object
- * header. It returns the size that snprintf() returns + 1.
- */
-int format_object_header(char *str, size_t size, enum object_type type,
- size_t objsize);
-
-void assert_oid_type(const struct object_id *oid, enum object_type expect);
-
-/*
- * Enabling the object read lock allows multiple threads to safely call the
- * following functions in parallel: repo_read_object_file(),
- * read_object_with_reference(), oid_object_info() and oid_object_info_extended().
- *
- * obj_read_lock() and obj_read_unlock() may also be used to protect other
- * section which cannot execute in parallel with object reading. Since the used
- * lock is a recursive mutex, these sections can even contain calls to object
- * reading functions. However, beware that in these cases zlib inflation won't
- * be performed in parallel, losing performance.
- *
- * TODO: oid_object_info_extended()'s call stack has a recursive behavior. If
- * any of its callees end up calling it, this recursive call won't benefit from
- * parallel inflation.
- */
-void enable_obj_read_lock(void);
-void disable_obj_read_lock(void);
-
-extern int obj_read_use_lock;
-extern pthread_mutex_t obj_read_mutex;
-
-static inline void obj_read_lock(void)
-{
- if(obj_read_use_lock)
- pthread_mutex_lock(&obj_read_mutex);
-}
-
-static inline void obj_read_unlock(void)
-{
- if(obj_read_use_lock)
- pthread_mutex_unlock(&obj_read_mutex);
-}
-
-/*
- * Iterate over the files in the loose-object parts of the object
- * directory "path", triggering the following callbacks:
- *
- * - loose_object is called for each loose object we find.
- *
- * - loose_cruft is called for any files that do not appear to be
- * loose objects. Note that we only look in the loose object
- * directories "objects/[0-9a-f]{2}/", so we will not report
- * "objects/foobar" as cruft.
- *
- * - loose_subdir is called for each top-level hashed subdirectory
- * of the object directory (e.g., "$OBJDIR/f0"). It is called
- * after the objects in the directory are processed.
- *
- * Any callback that is NULL will be ignored. Callbacks returning non-zero
- * will end the iteration.
- *
- * In the "buf" variant, "path" is a strbuf which will also be used as a
- * scratch buffer, but restored to its original contents before
- * the function returns.
- */
-typedef int each_loose_object_fn(const struct object_id *oid,
- const char *path,
- void *data);
-typedef int each_loose_cruft_fn(const char *basename,
- const char *path,
- void *data);
-typedef int each_loose_subdir_fn(unsigned int nr,
- const char *path,
- void *data);
-int for_each_file_in_obj_subdir(unsigned int subdir_nr,
- struct strbuf *path,
- each_loose_object_fn obj_cb,
- each_loose_cruft_fn cruft_cb,
- each_loose_subdir_fn subdir_cb,
- void *data);
-int for_each_loose_file_in_objdir(const char *path,
- each_loose_object_fn obj_cb,
- each_loose_cruft_fn cruft_cb,
- each_loose_subdir_fn subdir_cb,
- void *data);
-int for_each_loose_file_in_objdir_buf(struct strbuf *path,
- each_loose_object_fn obj_cb,
- each_loose_cruft_fn cruft_cb,
- each_loose_subdir_fn subdir_cb,
- void *data);
-
-/* Flags for for_each_*_object() below. */
-enum for_each_object_flags {
- /* Iterate only over local objects, not alternates. */
- FOR_EACH_OBJECT_LOCAL_ONLY = (1<<0),
-
- /* Only iterate over packs obtained from the promisor remote. */
- FOR_EACH_OBJECT_PROMISOR_ONLY = (1<<1),
-
- /*
- * Visit objects within a pack in packfile order rather than .idx order
- */
- FOR_EACH_OBJECT_PACK_ORDER = (1<<2),
-
- /* Only iterate over packs that are not marked as kept in-core. */
- FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS = (1<<3),
-
- /* Only iterate over packs that do not have .keep files. */
- FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS = (1<<4),
-};
-
-/*
- * Iterate over all accessible loose objects without respect to
- * reachability. By default, this includes both local and alternate objects.
- * The order in which objects are visited is unspecified.
- *
- * Any flags specific to packs are ignored.
- */
-int for_each_loose_object(each_loose_object_fn, void *,
- enum for_each_object_flags flags);
-
-/*
- * Iterate over all accessible packed objects without respect to reachability.
- * By default, this includes both local and alternate packs.
- *
- * Note that some objects may appear twice if they are found in multiple packs.
- * Each pack is visited in an unspecified order. By default, objects within a
- * pack are visited in pack-idx order (i.e., sorted by oid).
- */
-typedef int each_packed_object_fn(const struct object_id *oid,
- struct packed_git *pack,
- uint32_t pos,
- void *data);
-int for_each_object_in_pack(struct packed_git *p,
- each_packed_object_fn, void *data,
- enum for_each_object_flags flags);
-int for_each_packed_object(struct repository *repo, each_packed_object_fn cb,
- void *data, enum for_each_object_flags flags);
-
-#endif /* OBJECT_STORE_LL_H */
diff --git a/object-store.c b/object-store.c
new file mode 100644
index 0000000000..6ab50d25d3
--- /dev/null
+++ b/object-store.c
@@ -0,0 +1,1050 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
+#include "git-compat-util.h"
+#include "abspath.h"
+#include "commit-graph.h"
+#include "config.h"
+#include "dir.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "khash.h"
+#include "lockfile.h"
+#include "loose.h"
+#include "object-file-convert.h"
+#include "object-file.h"
+#include "object-store.h"
+#include "packfile.h"
+#include "path.h"
+#include "promisor-remote.h"
+#include "quote.h"
+#include "replace-object.h"
+#include "run-command.h"
+#include "setup.h"
+#include "strbuf.h"
+#include "strvec.h"
+#include "submodule.h"
+#include "write-or-die.h"
+
+KHASH_INIT(odb_path_map, const char * /* key: odb_path */,
+ struct object_directory *, 1, fspathhash, fspatheq)
+
+/*
+ * This is meant to hold a *small* number of objects that you would
+ * want repo_read_object_file() to be able to return, but yet you do not want
+ * to write them into the object store (e.g. a browse-only
+ * application).
+ */
+struct cached_object_entry {
+ struct object_id oid;
+ struct cached_object {
+ enum object_type type;
+ const void *buf;
+ unsigned long size;
+ } value;
+};
+
+static const struct cached_object *find_cached_object(struct raw_object_store *object_store,
+ const struct object_id *oid)
+{
+ static const struct cached_object empty_tree = {
+ .type = OBJ_TREE,
+ .buf = "",
+ };
+ const struct cached_object_entry *co = object_store->cached_objects;
+
+ for (size_t i = 0; i < object_store->cached_object_nr; i++, co++)
+ if (oideq(&co->oid, oid))
+ return &co->value;
+
+ if (oid->algo && oideq(oid, hash_algos[oid->algo].empty_tree))
+ return &empty_tree;
+
+ return NULL;
+}
+
+int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)
+{
+ int fd;
+ /*
+ * we let the umask do its job, don't try to be more
+ * restrictive except to remove write permission.
+ */
+ int mode = 0444;
+ repo_git_path_replace(the_repository, temp_filename, "objects/%s", pattern);
+ fd = git_mkstemp_mode(temp_filename->buf, mode);
+ if (0 <= fd)
+ return fd;
+
+ /* slow path */
+ /* some mkstemp implementations erase temp_filename on failure */
+ repo_git_path_replace(the_repository, temp_filename, "objects/%s", pattern);
+ safe_create_leading_directories(the_repository, temp_filename->buf);
+ return xmkstemp_mode(temp_filename->buf, mode);
+}
+
+int odb_pack_keep(const char *name)
+{
+ int fd;
+
+ fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
+ if (0 <= fd)
+ return fd;
+
+ /* slow path */
+ safe_create_leading_directories_const(the_repository, name);
+ return open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
+}
+
+const char *loose_object_path(struct repository *r, struct strbuf *buf,
+ const struct object_id *oid)
+{
+ return odb_loose_path(r->objects->odb, buf, oid);
+}
+
+/*
+ * Return non-zero iff the path is usable as an alternate object database.
+ */
+static int alt_odb_usable(struct raw_object_store *o,
+ struct strbuf *path,
+ const char *normalized_objdir, khiter_t *pos)
+{
+ int r;
+
+ /* Detect cases where alternate disappeared */
+ if (!is_directory(path->buf)) {
+ error(_("object directory %s does not exist; "
+ "check .git/objects/info/alternates"),
+ path->buf);
+ return 0;
+ }
+
+ /*
+ * Prevent the common mistake of listing the same
+ * thing twice, or object directory itself.
+ */
+ if (!o->odb_by_path) {
+ khiter_t p;
+
+ o->odb_by_path = kh_init_odb_path_map();
+ assert(!o->odb->next);
+ p = kh_put_odb_path_map(o->odb_by_path, o->odb->path, &r);
+ assert(r == 1); /* never used */
+ kh_value(o->odb_by_path, p) = o->odb;
+ }
+ if (fspatheq(path->buf, normalized_objdir))
+ return 0;
+ *pos = kh_put_odb_path_map(o->odb_by_path, path->buf, &r);
+ /* r: 0 = exists, 1 = never used, 2 = deleted */
+ return r == 0 ? 0 : 1;
+}
+
+/*
+ * Prepare alternate object database registry.
+ *
+ * The variable alt_odb_list points at the list of struct
+ * object_directory. The elements on this list come from
+ * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT
+ * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates,
+ * whose contents is similar to that environment variable but can be
+ * LF separated. Its base points at a statically allocated buffer that
+ * contains "/the/directory/corresponding/to/.git/objects/...", while
+ * its name points just after the slash at the end of ".git/objects/"
+ * in the example above, and has enough space to hold all hex characters
+ * of the object ID, an extra slash for the first level indirection, and
+ * the terminating NUL.
+ */
+static void read_info_alternates(struct repository *r,
+ const char *relative_base,
+ int depth);
+static int link_alt_odb_entry(struct repository *r, const struct strbuf *entry,
+ const char *relative_base, int depth, const char *normalized_objdir)
+{
+ struct object_directory *ent;
+ struct strbuf pathbuf = STRBUF_INIT;
+ struct strbuf tmp = STRBUF_INIT;
+ khiter_t pos;
+ int ret = -1;
+
+ if (!is_absolute_path(entry->buf) && relative_base) {
+ strbuf_realpath(&pathbuf, relative_base, 1);
+ strbuf_addch(&pathbuf, '/');
+ }
+ strbuf_addbuf(&pathbuf, entry);
+
+ if (!strbuf_realpath(&tmp, pathbuf.buf, 0)) {
+ error(_("unable to normalize alternate object path: %s"),
+ pathbuf.buf);
+ goto error;
+ }
+ strbuf_swap(&pathbuf, &tmp);
+
+ /*
+ * The trailing slash after the directory name is given by
+ * this function at the end. Remove duplicates.
+ */
+ while (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/')
+ strbuf_setlen(&pathbuf, pathbuf.len - 1);
+
+ if (!alt_odb_usable(r->objects, &pathbuf, normalized_objdir, &pos))
+ goto error;
+
+ CALLOC_ARRAY(ent, 1);
+ /* pathbuf.buf is already in r->objects->odb_by_path */
+ ent->path = strbuf_detach(&pathbuf, NULL);
+
+ /* add the alternate entry */
+ *r->objects->odb_tail = ent;
+ r->objects->odb_tail = &(ent->next);
+ ent->next = NULL;
+ assert(r->objects->odb_by_path);
+ kh_value(r->objects->odb_by_path, pos) = ent;
+
+ /* recursively add alternates */
+ read_info_alternates(r, ent->path, depth + 1);
+ ret = 0;
+ error:
+ strbuf_release(&tmp);
+ strbuf_release(&pathbuf);
+ return ret;
+}
+
+static const char *parse_alt_odb_entry(const char *string,
+ int sep,
+ struct strbuf *out)
+{
+ const char *end;
+
+ strbuf_reset(out);
+
+ if (*string == '#') {
+ /* comment; consume up to next separator */
+ end = strchrnul(string, sep);
+ } else if (*string == '"' && !unquote_c_style(out, string, &end)) {
+ /*
+ * quoted path; unquote_c_style has copied the
+ * data for us and set "end". Broken quoting (e.g.,
+ * an entry that doesn't end with a quote) falls
+ * back to the unquoted case below.
+ */
+ } else {
+ /* normal, unquoted path */
+ end = strchrnul(string, sep);
+ strbuf_add(out, string, end - string);
+ }
+
+ if (*end)
+ end++;
+ return end;
+}
+
+static void link_alt_odb_entries(struct repository *r, const char *alt,
+ int sep, const char *relative_base, int depth)
+{
+ struct strbuf objdirbuf = STRBUF_INIT;
+ struct strbuf entry = STRBUF_INIT;
+
+ if (!alt || !*alt)
+ return;
+
+ if (depth > 5) {
+ error(_("%s: ignoring alternate object stores, nesting too deep"),
+ relative_base);
+ return;
+ }
+
+ strbuf_realpath(&objdirbuf, r->objects->odb->path, 1);
+
+ while (*alt) {
+ alt = parse_alt_odb_entry(alt, sep, &entry);
+ if (!entry.len)
+ continue;
+ link_alt_odb_entry(r, &entry,
+ relative_base, depth, objdirbuf.buf);
+ }
+ strbuf_release(&entry);
+ strbuf_release(&objdirbuf);
+}
+
+static void read_info_alternates(struct repository *r,
+ const char *relative_base,
+ int depth)
+{
+ char *path;
+ struct strbuf buf = STRBUF_INIT;
+
+ path = xstrfmt("%s/info/alternates", relative_base);
+ if (strbuf_read_file(&buf, path, 1024) < 0) {
+ warn_on_fopen_errors(path);
+ free(path);
+ return;
+ }
+
+ link_alt_odb_entries(r, buf.buf, '\n', relative_base, depth);
+ strbuf_release(&buf);
+ free(path);
+}
+
+void add_to_alternates_file(const char *reference)
+{
+ struct lock_file lock = LOCK_INIT;
+ char *alts = repo_git_path(the_repository, "objects/info/alternates");
+ FILE *in, *out;
+ int found = 0;
+
+ hold_lock_file_for_update(&lock, alts, LOCK_DIE_ON_ERROR);
+ out = fdopen_lock_file(&lock, "w");
+ if (!out)
+ die_errno(_("unable to fdopen alternates lockfile"));
+
+ in = fopen(alts, "r");
+ if (in) {
+ struct strbuf line = STRBUF_INIT;
+
+ while (strbuf_getline(&line, in) != EOF) {
+ if (!strcmp(reference, line.buf)) {
+ found = 1;
+ break;
+ }
+ fprintf_or_die(out, "%s\n", line.buf);
+ }
+
+ strbuf_release(&line);
+ fclose(in);
+ }
+ else if (errno != ENOENT)
+ die_errno(_("unable to read alternates file"));
+
+ if (found) {
+ rollback_lock_file(&lock);
+ } else {
+ fprintf_or_die(out, "%s\n", reference);
+ if (commit_lock_file(&lock))
+ die_errno(_("unable to move new alternates file into place"));
+ if (the_repository->objects->loaded_alternates)
+ link_alt_odb_entries(the_repository, reference,
+ '\n', NULL, 0);
+ }
+ free(alts);
+}
+
+void add_to_alternates_memory(const char *reference)
+{
+ /*
+ * Make sure alternates are initialized, or else our entry may be
+ * overwritten when they are.
+ */
+ prepare_alt_odb(the_repository);
+
+ link_alt_odb_entries(the_repository, reference,
+ '\n', NULL, 0);
+}
+
+struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy)
+{
+ struct object_directory *new_odb;
+
+ /*
+ * Make sure alternates are initialized, or else our entry may be
+ * overwritten when they are.
+ */
+ prepare_alt_odb(the_repository);
+
+ /*
+ * Make a new primary odb and link the old primary ODB in as an
+ * alternate
+ */
+ new_odb = xcalloc(1, sizeof(*new_odb));
+ new_odb->path = xstrdup(dir);
+
+ /*
+ * Disable ref updates while a temporary odb is active, since
+ * the objects in the database may roll back.
+ */
+ new_odb->disable_ref_updates = 1;
+ new_odb->will_destroy = will_destroy;
+ new_odb->next = the_repository->objects->odb;
+ the_repository->objects->odb = new_odb;
+ return new_odb->next;
+}
+
+static void free_object_directory(struct object_directory *odb)
+{
+ free(odb->path);
+ odb_clear_loose_cache(odb);
+ loose_object_map_clear(&odb->loose_map);
+ free(odb);
+}
+
+void restore_primary_odb(struct object_directory *restore_odb, const char *old_path)
+{
+ struct object_directory *cur_odb = the_repository->objects->odb;
+
+ if (strcmp(old_path, cur_odb->path))
+ BUG("expected %s as primary object store; found %s",
+ old_path, cur_odb->path);
+
+ if (cur_odb->next != restore_odb)
+ BUG("we expect the old primary object store to be the first alternate");
+
+ the_repository->objects->odb = restore_odb;
+ free_object_directory(cur_odb);
+}
+
+/*
+ * Compute the exact path an alternate is at and returns it. In case of
+ * error NULL is returned and the human readable error is added to `err`
+ * `path` may be relative and should point to $GIT_DIR.
+ * `err` must not be null.
+ */
+char *compute_alternate_path(const char *path, struct strbuf *err)
+{
+ char *ref_git = NULL;
+ const char *repo;
+ int seen_error = 0;
+
+ ref_git = real_pathdup(path, 0);
+ if (!ref_git) {
+ seen_error = 1;
+ strbuf_addf(err, _("path '%s' does not exist"), path);
+ goto out;
+ }
+
+ repo = read_gitfile(ref_git);
+ if (!repo)
+ repo = read_gitfile(mkpath("%s/.git", ref_git));
+ if (repo) {
+ free(ref_git);
+ ref_git = xstrdup(repo);
+ }
+
+ if (!repo && is_directory(mkpath("%s/.git/objects", ref_git))) {
+ char *ref_git_git = mkpathdup("%s/.git", ref_git);
+ free(ref_git);
+ ref_git = ref_git_git;
+ } else if (!is_directory(mkpath("%s/objects", ref_git))) {
+ struct strbuf sb = STRBUF_INIT;
+ seen_error = 1;
+ if (get_common_dir(&sb, ref_git)) {
+ strbuf_addf(err,
+ _("reference repository '%s' as a linked "
+ "checkout is not supported yet."),
+ path);
+ goto out;
+ }
+
+ strbuf_addf(err, _("reference repository '%s' is not a "
+ "local repository."), path);
+ goto out;
+ }
+
+ if (!access(mkpath("%s/shallow", ref_git), F_OK)) {
+ strbuf_addf(err, _("reference repository '%s' is shallow"),
+ path);
+ seen_error = 1;
+ goto out;
+ }
+
+ if (!access(mkpath("%s/info/grafts", ref_git), F_OK)) {
+ strbuf_addf(err,
+ _("reference repository '%s' is grafted"),
+ path);
+ seen_error = 1;
+ goto out;
+ }
+
+out:
+ if (seen_error) {
+ FREE_AND_NULL(ref_git);
+ }
+
+ return ref_git;
+}
+
+struct object_directory *find_odb(struct repository *r, const char *obj_dir)
+{
+ struct object_directory *odb;
+ char *obj_dir_real = real_pathdup(obj_dir, 1);
+ struct strbuf odb_path_real = STRBUF_INIT;
+
+ prepare_alt_odb(r);
+ for (odb = r->objects->odb; odb; odb = odb->next) {
+ strbuf_realpath(&odb_path_real, odb->path, 1);
+ if (!strcmp(obj_dir_real, odb_path_real.buf))
+ break;
+ }
+
+ free(obj_dir_real);
+ strbuf_release(&odb_path_real);
+
+ if (!odb)
+ die(_("could not find object directory matching %s"), obj_dir);
+ return odb;
+}
+
+static void fill_alternate_refs_command(struct child_process *cmd,
+ const char *repo_path)
+{
+ const char *value;
+
+ if (!git_config_get_value("core.alternateRefsCommand", &value)) {
+ cmd->use_shell = 1;
+
+ strvec_push(&cmd->args, value);
+ strvec_push(&cmd->args, repo_path);
+ } else {
+ cmd->git_cmd = 1;
+
+ strvec_pushf(&cmd->args, "--git-dir=%s", repo_path);
+ strvec_push(&cmd->args, "for-each-ref");
+ strvec_push(&cmd->args, "--format=%(objectname)");
+
+ if (!git_config_get_value("core.alternateRefsPrefixes", &value)) {
+ strvec_push(&cmd->args, "--");
+ strvec_split(&cmd->args, value);
+ }
+ }
+
+ strvec_pushv(&cmd->env, (const char **)local_repo_env);
+ cmd->out = -1;
+}
+
+static void read_alternate_refs(const char *path,
+ alternate_ref_fn *cb,
+ void *data)
+{
+ struct child_process cmd = CHILD_PROCESS_INIT;
+ struct strbuf line = STRBUF_INIT;
+ FILE *fh;
+
+ fill_alternate_refs_command(&cmd, path);
+
+ if (start_command(&cmd))
+ return;
+
+ fh = xfdopen(cmd.out, "r");
+ while (strbuf_getline_lf(&line, fh) != EOF) {
+ struct object_id oid;
+ const char *p;
+
+ if (parse_oid_hex(line.buf, &oid, &p) || *p) {
+ warning(_("invalid line while parsing alternate refs: %s"),
+ line.buf);
+ break;
+ }
+
+ cb(&oid, data);
+ }
+
+ fclose(fh);
+ finish_command(&cmd);
+ strbuf_release(&line);
+}
+
+struct alternate_refs_data {
+ alternate_ref_fn *fn;
+ void *data;
+};
+
+static int refs_from_alternate_cb(struct object_directory *e,
+ void *data)
+{
+ struct strbuf path = STRBUF_INIT;
+ size_t base_len;
+ struct alternate_refs_data *cb = data;
+
+ if (!strbuf_realpath(&path, e->path, 0))
+ goto out;
+ if (!strbuf_strip_suffix(&path, "/objects"))
+ goto out;
+ base_len = path.len;
+
+ /* Is this a git repository with refs? */
+ strbuf_addstr(&path, "/refs");
+ if (!is_directory(path.buf))
+ goto out;
+ strbuf_setlen(&path, base_len);
+
+ read_alternate_refs(path.buf, cb->fn, cb->data);
+
+out:
+ strbuf_release(&path);
+ return 0;
+}
+
+void for_each_alternate_ref(alternate_ref_fn fn, void *data)
+{
+ struct alternate_refs_data cb;
+ cb.fn = fn;
+ cb.data = data;
+ foreach_alt_odb(refs_from_alternate_cb, &cb);
+}
+
+int foreach_alt_odb(alt_odb_fn fn, void *cb)
+{
+ struct object_directory *ent;
+ int r = 0;
+
+ prepare_alt_odb(the_repository);
+ for (ent = the_repository->objects->odb->next; ent; ent = ent->next) {
+ r = fn(ent, cb);
+ if (r)
+ break;
+ }
+ return r;
+}
+
+void prepare_alt_odb(struct repository *r)
+{
+ if (r->objects->loaded_alternates)
+ return;
+
+ link_alt_odb_entries(r, r->objects->alternate_db, PATH_SEP, NULL, 0);
+
+ read_info_alternates(r, r->objects->odb->path, 0);
+ r->objects->loaded_alternates = 1;
+}
+
+int has_alt_odb(struct repository *r)
+{
+ prepare_alt_odb(r);
+ return !!r->objects->odb->next;
+}
+
+int obj_read_use_lock = 0;
+pthread_mutex_t obj_read_mutex;
+
+void enable_obj_read_lock(void)
+{
+ if (obj_read_use_lock)
+ return;
+
+ obj_read_use_lock = 1;
+ init_recursive_mutex(&obj_read_mutex);
+}
+
+void disable_obj_read_lock(void)
+{
+ if (!obj_read_use_lock)
+ return;
+
+ obj_read_use_lock = 0;
+ pthread_mutex_destroy(&obj_read_mutex);
+}
+
+int fetch_if_missing = 1;
+
+static int do_oid_object_info_extended(struct repository *r,
+ const struct object_id *oid,
+ struct object_info *oi, unsigned flags)
+{
+ static struct object_info blank_oi = OBJECT_INFO_INIT;
+ const struct cached_object *co;
+ struct pack_entry e;
+ int rtype;
+ const struct object_id *real = oid;
+ int already_retried = 0;
+
+
+ if (flags & OBJECT_INFO_LOOKUP_REPLACE)
+ real = lookup_replace_object(r, oid);
+
+ if (is_null_oid(real))
+ return -1;
+
+ if (!oi)
+ oi = &blank_oi;
+
+ co = find_cached_object(r->objects, real);
+ if (co) {
+ if (oi->typep)
+ *(oi->typep) = co->type;
+ if (oi->sizep)
+ *(oi->sizep) = co->size;
+ if (oi->disk_sizep)
+ *(oi->disk_sizep) = 0;
+ if (oi->delta_base_oid)
+ oidclr(oi->delta_base_oid, the_repository->hash_algo);
+ if (oi->type_name)
+ strbuf_addstr(oi->type_name, type_name(co->type));
+ if (oi->contentp)
+ *oi->contentp = xmemdupz(co->buf, co->size);
+ oi->whence = OI_CACHED;
+ return 0;
+ }
+
+ while (1) {
+ if (find_pack_entry(r, real, &e))
+ break;
+
+ /* Most likely it's a loose object. */
+ if (!loose_object_info(r, real, oi, flags))
+ return 0;
+
+ /* Not a loose object; someone else may have just packed it. */
+ if (!(flags & OBJECT_INFO_QUICK)) {
+ reprepare_packed_git(r);
+ if (find_pack_entry(r, real, &e))
+ break;
+ }
+
+ /*
+ * If r is the_repository, this might be an attempt at
+ * accessing a submodule object as if it were in the_repository
+ * (having called add_submodule_odb() on that submodule's ODB).
+ * If any such ODBs exist, register them and try again.
+ */
+ if (r == the_repository &&
+ register_all_submodule_odb_as_alternates())
+ /* We added some alternates; retry */
+ continue;
+
+ /* Check if it is a missing object */
+ if (fetch_if_missing && repo_has_promisor_remote(r) &&
+ !already_retried &&
+ !(flags & OBJECT_INFO_SKIP_FETCH_OBJECT)) {
+ promisor_remote_get_direct(r, real, 1);
+ already_retried = 1;
+ continue;
+ }
+
+ if (flags & OBJECT_INFO_DIE_IF_CORRUPT) {
+ const struct packed_git *p;
+ if ((flags & OBJECT_INFO_LOOKUP_REPLACE) && !oideq(real, oid))
+ die(_("replacement %s not found for %s"),
+ oid_to_hex(real), oid_to_hex(oid));
+ if ((p = has_packed_and_bad(r, real)))
+ die(_("packed object %s (stored in %s) is corrupt"),
+ oid_to_hex(real), p->pack_name);
+ }
+ return -1;
+ }
+
+ if (oi == &blank_oi)
+ /*
+ * We know that the caller doesn't actually need the
+ * information below, so return early.
+ */
+ return 0;
+ rtype = packed_object_info(r, e.p, e.offset, oi);
+ if (rtype < 0) {
+ mark_bad_packed_object(e.p, real);
+ return do_oid_object_info_extended(r, real, oi, 0);
+ } else if (oi->whence == OI_PACKED) {
+ oi->u.packed.offset = e.offset;
+ oi->u.packed.pack = e.p;
+ oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||
+ rtype == OBJ_OFS_DELTA);
+ }
+
+ return 0;
+}
+
+static int oid_object_info_convert(struct repository *r,
+ const struct object_id *input_oid,
+ struct object_info *input_oi, unsigned flags)
+{
+ const struct git_hash_algo *input_algo = &hash_algos[input_oid->algo];
+ int do_die = flags & OBJECT_INFO_DIE_IF_CORRUPT;
+ struct strbuf type_name = STRBUF_INIT;
+ struct object_id oid, delta_base_oid;
+ struct object_info new_oi, *oi;
+ unsigned long size;
+ void *content;
+ int ret;
+
+ if (repo_oid_to_algop(r, input_oid, the_hash_algo, &oid)) {
+ if (do_die)
+ die(_("missing mapping of %s to %s"),
+ oid_to_hex(input_oid), the_hash_algo->name);
+ return -1;
+ }
+
+ /* Is new_oi needed? */
+ oi = input_oi;
+ if (input_oi && (input_oi->delta_base_oid || input_oi->sizep ||
+ input_oi->contentp)) {
+ new_oi = *input_oi;
+ /* Does delta_base_oid need to be converted? */
+ if (input_oi->delta_base_oid)
+ new_oi.delta_base_oid = &delta_base_oid;
+ /* Will the attributes differ when converted? */
+ if (input_oi->sizep || input_oi->contentp) {
+ new_oi.contentp = &content;
+ new_oi.sizep = &size;
+ new_oi.type_name = &type_name;
+ }
+ oi = &new_oi;
+ }
+
+ ret = oid_object_info_extended(r, &oid, oi, flags);
+ if (ret)
+ return -1;
+ if (oi == input_oi)
+ return ret;
+
+ if (new_oi.contentp) {
+ struct strbuf outbuf = STRBUF_INIT;
+ enum object_type type;
+
+ type = type_from_string_gently(type_name.buf, type_name.len,
+ !do_die);
+ if (type == -1)
+ return -1;
+ if (type != OBJ_BLOB) {
+ ret = convert_object_file(the_repository, &outbuf,
+ the_hash_algo, input_algo,
+ content, size, type, !do_die);
+ free(content);
+ if (ret == -1)
+ return -1;
+ size = outbuf.len;
+ content = strbuf_detach(&outbuf, NULL);
+ }
+ if (input_oi->sizep)
+ *input_oi->sizep = size;
+ if (input_oi->contentp)
+ *input_oi->contentp = content;
+ else
+ free(content);
+ if (input_oi->type_name)
+ *input_oi->type_name = type_name;
+ else
+ strbuf_release(&type_name);
+ }
+ if (new_oi.delta_base_oid == &delta_base_oid) {
+ if (repo_oid_to_algop(r, &delta_base_oid, input_algo,
+ input_oi->delta_base_oid)) {
+ if (do_die)
+ die(_("missing mapping of %s to %s"),
+ oid_to_hex(&delta_base_oid),
+ input_algo->name);
+ return -1;
+ }
+ }
+ input_oi->whence = new_oi.whence;
+ input_oi->u = new_oi.u;
+ return ret;
+}
+
+int oid_object_info_extended(struct repository *r, const struct object_id *oid,
+ struct object_info *oi, unsigned flags)
+{
+ int ret;
+
+ if (oid->algo && (hash_algo_by_ptr(r->hash_algo) != oid->algo))
+ return oid_object_info_convert(r, oid, oi, flags);
+
+ obj_read_lock();
+ ret = do_oid_object_info_extended(r, oid, oi, flags);
+ obj_read_unlock();
+ return ret;
+}
+
+
+/* returns enum object_type or negative */
+int oid_object_info(struct repository *r,
+ const struct object_id *oid,
+ unsigned long *sizep)
+{
+ enum object_type type;
+ struct object_info oi = OBJECT_INFO_INIT;
+
+ oi.typep = &type;
+ oi.sizep = sizep;
+ if (oid_object_info_extended(r, oid, &oi,
+ OBJECT_INFO_LOOKUP_REPLACE) < 0)
+ return -1;
+ return type;
+}
+
+int pretend_object_file(struct repository *repo,
+ void *buf, unsigned long len, enum object_type type,
+ struct object_id *oid)
+{
+ struct cached_object_entry *co;
+ char *co_buf;
+
+ hash_object_file(repo->hash_algo, buf, len, type, oid);
+ if (repo_has_object_file_with_flags(repo, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
+ find_cached_object(repo->objects, oid))
+ return 0;
+
+ ALLOC_GROW(repo->objects->cached_objects,
+ repo->objects->cached_object_nr + 1, repo->objects->cached_object_alloc);
+ co = &repo->objects->cached_objects[repo->objects->cached_object_nr++];
+ co->value.size = len;
+ co->value.type = type;
+ co_buf = xmalloc(len);
+ memcpy(co_buf, buf, len);
+ co->value.buf = co_buf;
+ oidcpy(&co->oid, oid);
+ return 0;
+}
+
+/*
+ * This function dies on corrupt objects; the callers who want to
+ * deal with them should arrange to call oid_object_info_extended() and give
+ * error messages themselves.
+ */
+void *repo_read_object_file(struct repository *r,
+ const struct object_id *oid,
+ enum object_type *type,
+ unsigned long *size)
+{
+ struct object_info oi = OBJECT_INFO_INIT;
+ unsigned flags = OBJECT_INFO_DIE_IF_CORRUPT | OBJECT_INFO_LOOKUP_REPLACE;
+ void *data;
+
+ oi.typep = type;
+ oi.sizep = size;
+ oi.contentp = &data;
+ if (oid_object_info_extended(r, oid, &oi, flags))
+ return NULL;
+
+ return data;
+}
+
+void *read_object_with_reference(struct repository *r,
+ const struct object_id *oid,
+ enum object_type required_type,
+ unsigned long *size,
+ struct object_id *actual_oid_return)
+{
+ enum object_type type;
+ void *buffer;
+ unsigned long isize;
+ struct object_id actual_oid;
+
+ oidcpy(&actual_oid, oid);
+ while (1) {
+ int ref_length = -1;
+ const char *ref_type = NULL;
+
+ buffer = repo_read_object_file(r, &actual_oid, &type, &isize);
+ if (!buffer)
+ return NULL;
+ if (type == required_type) {
+ *size = isize;
+ if (actual_oid_return)
+ oidcpy(actual_oid_return, &actual_oid);
+ return buffer;
+ }
+ /* Handle references */
+ else if (type == OBJ_COMMIT)
+ ref_type = "tree ";
+ else if (type == OBJ_TAG)
+ ref_type = "object ";
+ else {
+ free(buffer);
+ return NULL;
+ }
+ ref_length = strlen(ref_type);
+
+ if (ref_length + the_hash_algo->hexsz > isize ||
+ memcmp(buffer, ref_type, ref_length) ||
+ get_oid_hex((char *) buffer + ref_length, &actual_oid)) {
+ free(buffer);
+ return NULL;
+ }
+ free(buffer);
+ /* Now we have the ID of the referred-to object in
+ * actual_oid. Check again. */
+ }
+}
+
+int has_object(struct repository *r, const struct object_id *oid,
+ unsigned flags)
+{
+ int quick = !(flags & HAS_OBJECT_RECHECK_PACKED);
+ unsigned object_info_flags = OBJECT_INFO_SKIP_FETCH_OBJECT |
+ (quick ? OBJECT_INFO_QUICK : 0);
+
+ if (!startup_info->have_repository)
+ return 0;
+ return oid_object_info_extended(r, oid, NULL, object_info_flags) >= 0;
+}
+
+int repo_has_object_file_with_flags(struct repository *r,
+ const struct object_id *oid, int flags)
+{
+ if (!startup_info->have_repository)
+ return 0;
+ return oid_object_info_extended(r, oid, NULL, flags) >= 0;
+}
+
+int repo_has_object_file(struct repository *r,
+ const struct object_id *oid)
+{
+ return repo_has_object_file_with_flags(r, oid, 0);
+}
+
+void assert_oid_type(const struct object_id *oid, enum object_type expect)
+{
+ enum object_type type = oid_object_info(the_repository, oid, NULL);
+ if (type < 0)
+ die(_("%s is not a valid object"), oid_to_hex(oid));
+ if (type != expect)
+ die(_("%s is not a valid '%s' object"), oid_to_hex(oid),
+ type_name(expect));
+}
+
+struct raw_object_store *raw_object_store_new(void)
+{
+ struct raw_object_store *o = xmalloc(sizeof(*o));
+
+ memset(o, 0, sizeof(*o));
+ INIT_LIST_HEAD(&o->packed_git_mru);
+ hashmap_init(&o->pack_map, pack_map_entry_cmp, NULL, 0);
+ pthread_mutex_init(&o->replace_mutex, NULL);
+ return o;
+}
+
+static void free_object_directories(struct raw_object_store *o)
+{
+ while (o->odb) {
+ struct object_directory *next;
+
+ next = o->odb->next;
+ free_object_directory(o->odb);
+ o->odb = next;
+ }
+ kh_destroy_odb_path_map(o->odb_by_path);
+ o->odb_by_path = NULL;
+}
+
+void raw_object_store_clear(struct raw_object_store *o)
+{
+ FREE_AND_NULL(o->alternate_db);
+
+ oidmap_free(o->replace_map, 1);
+ FREE_AND_NULL(o->replace_map);
+ pthread_mutex_destroy(&o->replace_mutex);
+
+ free_commit_graph(o->commit_graph);
+ o->commit_graph = NULL;
+ o->commit_graph_attempted = 0;
+
+ free_object_directories(o);
+ o->odb_tail = NULL;
+ o->loaded_alternates = 0;
+
+ for (size_t i = 0; i < o->cached_object_nr; i++)
+ free((char *) o->cached_objects[i].value.buf);
+ FREE_AND_NULL(o->cached_objects);
+
+ INIT_LIST_HEAD(&o->packed_git_mru);
+ close_object_store(o);
+
+ /*
+ * `close_object_store()` only closes the packfiles, but doesn't free
+ * them. We thus have to do this manually.
+ */
+ for (struct packed_git *p = o->packed_git, *next; p; p = next) {
+ next = p->next;
+ free(p);
+ }
+ o->packed_git = NULL;
+
+ hashmap_clear(&o->pack_map);
+}
diff --git a/object-store.h b/object-store.h
index 1b3e3d7d01..46961dc954 100644
--- a/object-store.h
+++ b/object-store.h
@@ -1,11 +1,517 @@
#ifndef OBJECT_STORE_H
#define OBJECT_STORE_H
-#include "khash.h"
-#include "dir.h"
-#include "object-store-ll.h"
+#include "hashmap.h"
+#include "object.h"
+#include "list.h"
+#include "oidset.h"
+#include "thread-utils.h"
-KHASH_INIT(odb_path_map, const char * /* key: odb_path */,
- struct object_directory *, 1, fspathhash, fspatheq)
+struct oidmap;
+struct oidtree;
+struct strbuf;
+struct repository;
+
+struct object_directory {
+ struct object_directory *next;
+
+ /*
+ * Used to store the results of readdir(3) calls when we are OK
+ * sacrificing accuracy due to races for speed. That includes
+ * object existence with OBJECT_INFO_QUICK, as well as
+ * our search for unique abbreviated hashes. Don't use it for tasks
+ * requiring greater accuracy!
+ *
+ * Be sure to call odb_load_loose_cache() before using.
+ */
+ uint32_t loose_objects_subdir_seen[8]; /* 256 bits */
+ struct oidtree *loose_objects_cache;
+
+ /* Map between object IDs for loose objects. */
+ struct loose_object_map *loose_map;
+
+ /*
+ * This is a temporary object store created by the tmp_objdir
+ * facility. Disable ref updates since the objects in the store
+ * might be discarded on rollback.
+ */
+ int disable_ref_updates;
+
+ /*
+ * This object store is ephemeral, so there is no need to fsync.
+ */
+ int will_destroy;
+
+ /*
+ * Path to the alternative object store. If this is a relative path,
+ * it is relative to the current working directory.
+ */
+ char *path;
+};
+
+void prepare_alt_odb(struct repository *r);
+int has_alt_odb(struct repository *r);
+char *compute_alternate_path(const char *path, struct strbuf *err);
+struct object_directory *find_odb(struct repository *r, const char *obj_dir);
+typedef int alt_odb_fn(struct object_directory *, void *);
+int foreach_alt_odb(alt_odb_fn, void*);
+typedef void alternate_ref_fn(const struct object_id *oid, void *);
+void for_each_alternate_ref(alternate_ref_fn, void *);
+
+/*
+ * Add the directory to the on-disk alternates file; the new entry will also
+ * take effect in the current process.
+ */
+void add_to_alternates_file(const char *dir);
+
+/*
+ * Add the directory to the in-memory list of alternates (along with any
+ * recursive alternates it points to), but do not modify the on-disk alternates
+ * file.
+ */
+void add_to_alternates_memory(const char *dir);
+
+/*
+ * Replace the current writable object directory with the specified temporary
+ * object directory; returns the former primary object directory.
+ */
+struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy);
+
+/*
+ * Restore a previous ODB replaced by set_temporary_main_odb.
+ */
+void restore_primary_odb(struct object_directory *restore_odb, const char *old_path);
+
+/*
+ * Populate and return the loose object cache array corresponding to the
+ * given object ID.
+ */
+struct oidtree *odb_loose_cache(struct object_directory *odb,
+ const struct object_id *oid);
+
+/* Empty the loose object cache for the specified object directory. */
+void odb_clear_loose_cache(struct object_directory *odb);
+
+struct packed_git {
+ struct hashmap_entry packmap_ent;
+ struct packed_git *next;
+ struct list_head mru;
+ struct pack_window *windows;
+ off_t pack_size;
+ const void *index_data;
+ size_t index_size;
+ uint32_t num_objects;
+ size_t crc_offset;
+ struct oidset bad_objects;
+ int index_version;
+ time_t mtime;
+ int pack_fd;
+ int index; /* for builtin/pack-objects.c */
+ unsigned pack_local:1,
+ pack_keep:1,
+ pack_keep_in_core:1,
+ freshened:1,
+ do_not_close:1,
+ pack_promisor:1,
+ multi_pack_index:1,
+ is_cruft:1;
+ unsigned char hash[GIT_MAX_RAWSZ];
+ struct revindex_entry *revindex;
+ const uint32_t *revindex_data;
+ const uint32_t *revindex_map;
+ size_t revindex_size;
+ /*
+ * mtimes_map points at the beginning of the memory mapped region of
+ * this pack's corresponding .mtimes file, and mtimes_size is the size
+ * of that .mtimes file
+ */
+ const uint32_t *mtimes_map;
+ size_t mtimes_size;
+
+ /* repo denotes the repository this packfile belongs to */
+ struct repository *repo;
+
+ /* something like ".git/objects/pack/xxxxx.pack" */
+ char pack_name[FLEX_ARRAY]; /* more */
+};
+
+struct multi_pack_index;
+
+static inline int pack_map_entry_cmp(const void *cmp_data UNUSED,
+ const struct hashmap_entry *entry,
+ const struct hashmap_entry *entry2,
+ const void *keydata)
+{
+ const char *key = keydata;
+ const struct packed_git *pg1, *pg2;
+
+ pg1 = container_of(entry, const struct packed_git, packmap_ent);
+ pg2 = container_of(entry2, const struct packed_git, packmap_ent);
+
+ return strcmp(pg1->pack_name, key ? key : pg2->pack_name);
+}
+
+struct cached_object_entry;
+
+struct raw_object_store {
+ /*
+ * Set of all object directories; the main directory is first (and
+ * cannot be NULL after initialization). Subsequent directories are
+ * alternates.
+ */
+ struct object_directory *odb;
+ struct object_directory **odb_tail;
+ struct kh_odb_path_map *odb_by_path;
+
+ int loaded_alternates;
+
+ /*
+ * A list of alternate object directories loaded from the environment;
+ * this should not generally need to be accessed directly, but will
+ * populate the "odb" list when prepare_alt_odb() is run.
+ */
+ char *alternate_db;
+
+ /*
+ * Objects that should be substituted by other objects
+ * (see git-replace(1)).
+ */
+ struct oidmap *replace_map;
+ unsigned replace_map_initialized : 1;
+ pthread_mutex_t replace_mutex; /* protect object replace functions */
+
+ struct commit_graph *commit_graph;
+ unsigned commit_graph_attempted : 1; /* if loading has been attempted */
+
+ /*
+ * private data
+ *
+ * should only be accessed directly by packfile.c and midx.c
+ */
+ struct multi_pack_index *multi_pack_index;
+
+ /*
+ * private data
+ *
+ * should only be accessed directly by packfile.c
+ */
+
+ struct packed_git *packed_git;
+ /* A most-recently-used ordered version of the packed_git list. */
+ struct list_head packed_git_mru;
+
+ struct {
+ struct packed_git **packs;
+ unsigned flags;
+ } kept_pack_cache;
+
+ /*
+ * This is meant to hold a *small* number of objects that you would
+ * want repo_read_object_file() to be able to return, but yet you do not want
+ * to write them into the object store (e.g. a browse-only
+ * application).
+ */
+ struct cached_object_entry *cached_objects;
+ size_t cached_object_nr, cached_object_alloc;
+
+ /*
+ * A map of packfiles to packed_git structs for tracking which
+ * packs have been loaded already.
+ */
+ struct hashmap pack_map;
+
+ /*
+ * A fast, rough count of the number of objects in the repository.
+ * These two fields are not meant for direct access. Use
+ * repo_approximate_object_count() instead.
+ */
+ unsigned long approximate_object_count;
+ unsigned approximate_object_count_valid : 1;
+
+ /*
+ * Whether packed_git has already been populated with this repository's
+ * packs.
+ */
+ unsigned packed_git_initialized : 1;
+};
+
+struct raw_object_store *raw_object_store_new(void);
+void raw_object_store_clear(struct raw_object_store *o);
+
+/*
+ * Create a temporary file rooted in the object database directory, or
+ * die on failure. The filename is taken from "pattern", which should have the
+ * usual "XXXXXX" trailer, and the resulting filename is written into the
+ * "template" buffer. Returns the open descriptor.
+ */
+int odb_mkstemp(struct strbuf *temp_filename, const char *pattern);
+
+/*
+ * Create a pack .keep file named "name" (which should generally be the output
+ * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on
+ * error.
+ */
+int odb_pack_keep(const char *name);
+
+/*
+ * Put in `buf` the name of the file in the local object database that
+ * would be used to store a loose object with the specified oid.
+ */
+const char *loose_object_path(struct repository *r, struct strbuf *buf,
+ const struct object_id *oid);
+
+void *map_loose_object(struct repository *r, const struct object_id *oid,
+ unsigned long *size);
+
+void *repo_read_object_file(struct repository *r,
+ const struct object_id *oid,
+ enum object_type *type,
+ unsigned long *size);
+
+/* Read and unpack an object file into memory, write memory to an object file */
+int oid_object_info(struct repository *r, const struct object_id *, unsigned long *);
+
+void hash_object_file(const struct git_hash_algo *algo, const void *buf,
+ unsigned long len, enum object_type type,
+ struct object_id *oid);
+
+/*
+ * Add an object file to the in-memory object store, without writing it
+ * to disk.
+ *
+ * Callers are responsible for calling write_object_file to record the
+ * object in persistent storage before writing any other new objects
+ * that reference it.
+ */
+int pretend_object_file(struct repository *repo,
+ void *buf, unsigned long len, enum object_type type,
+ struct object_id *oid);
+
+struct object_info {
+ /* Request */
+ enum object_type *typep;
+ unsigned long *sizep;
+ off_t *disk_sizep;
+ struct object_id *delta_base_oid;
+ struct strbuf *type_name;
+ void **contentp;
+
+ /* Response */
+ enum {
+ OI_CACHED,
+ OI_LOOSE,
+ OI_PACKED,
+ OI_DBCACHED
+ } whence;
+ union {
+ /*
+ * struct {
+ * ... Nothing to expose in this case
+ * } cached;
+ * struct {
+ * ... Nothing to expose in this case
+ * } loose;
+ */
+ struct {
+ struct packed_git *pack;
+ off_t offset;
+ unsigned int is_delta;
+ } packed;
+ } u;
+};
+
+/*
+ * Initializer for a "struct object_info" that wants no items. You may
+ * also memset() the memory to all-zeroes.
+ */
+#define OBJECT_INFO_INIT { 0 }
+
+/* Invoke lookup_replace_object() on the given hash */
+#define OBJECT_INFO_LOOKUP_REPLACE 1
+/* Allow reading from a loose object file of unknown/bogus type */
+#define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2
+/* Do not retry packed storage after checking packed and loose storage */
+#define OBJECT_INFO_QUICK 8
+/*
+ * Do not attempt to fetch the object if missing (even if fetch_is_missing is
+ * nonzero).
+ */
+#define OBJECT_INFO_SKIP_FETCH_OBJECT 16
+/*
+ * This is meant for bulk prefetching of missing blobs in a partial
+ * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK
+ */
+#define OBJECT_INFO_FOR_PREFETCH (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK)
+
+/* Die if object corruption (not just an object being missing) was detected. */
+#define OBJECT_INFO_DIE_IF_CORRUPT 32
+
+int oid_object_info_extended(struct repository *r,
+ const struct object_id *,
+ struct object_info *, unsigned flags);
+
+/* Retry packed storage after checking packed and loose storage */
+#define HAS_OBJECT_RECHECK_PACKED 1
+
+/*
+ * Returns 1 if the object exists. This function will not lazily fetch objects
+ * in a partial clone.
+ */
+int has_object(struct repository *r, const struct object_id *oid,
+ unsigned flags);
+
+/*
+ * These macros and functions are deprecated. If checking existence for an
+ * object that is likely to be missing and/or whose absence is relatively
+ * inconsequential (or is consequential but the caller is prepared to handle
+ * it), use has_object(), which has better defaults (no lazy fetch in a partial
+ * clone and no rechecking of packed storage). In the unlikely event that a
+ * caller needs to assert existence of an object that it fully expects to
+ * exist, and wants to trigger a lazy fetch in a partial clone, use
+ * oid_object_info_extended() with a NULL struct object_info.
+ *
+ * These functions can be removed once all callers have migrated to
+ * has_object() and/or oid_object_info_extended().
+ */
+int repo_has_object_file(struct repository *r, const struct object_id *oid);
+int repo_has_object_file_with_flags(struct repository *r,
+ const struct object_id *oid, int flags);
+
+void assert_oid_type(const struct object_id *oid, enum object_type expect);
+
+/*
+ * Enabling the object read lock allows multiple threads to safely call the
+ * following functions in parallel: repo_read_object_file(),
+ * read_object_with_reference(), oid_object_info() and oid_object_info_extended().
+ *
+ * obj_read_lock() and obj_read_unlock() may also be used to protect other
+ * section which cannot execute in parallel with object reading. Since the used
+ * lock is a recursive mutex, these sections can even contain calls to object
+ * reading functions. However, beware that in these cases zlib inflation won't
+ * be performed in parallel, losing performance.
+ *
+ * TODO: oid_object_info_extended()'s call stack has a recursive behavior. If
+ * any of its callees end up calling it, this recursive call won't benefit from
+ * parallel inflation.
+ */
+void enable_obj_read_lock(void);
+void disable_obj_read_lock(void);
+
+extern int obj_read_use_lock;
+extern pthread_mutex_t obj_read_mutex;
+
+static inline void obj_read_lock(void)
+{
+ if(obj_read_use_lock)
+ pthread_mutex_lock(&obj_read_mutex);
+}
+
+static inline void obj_read_unlock(void)
+{
+ if(obj_read_use_lock)
+ pthread_mutex_unlock(&obj_read_mutex);
+}
+
+/*
+ * Iterate over the files in the loose-object parts of the object
+ * directory "path", triggering the following callbacks:
+ *
+ * - loose_object is called for each loose object we find.
+ *
+ * - loose_cruft is called for any files that do not appear to be
+ * loose objects. Note that we only look in the loose object
+ * directories "objects/[0-9a-f]{2}/", so we will not report
+ * "objects/foobar" as cruft.
+ *
+ * - loose_subdir is called for each top-level hashed subdirectory
+ * of the object directory (e.g., "$OBJDIR/f0"). It is called
+ * after the objects in the directory are processed.
+ *
+ * Any callback that is NULL will be ignored. Callbacks returning non-zero
+ * will end the iteration.
+ *
+ * In the "buf" variant, "path" is a strbuf which will also be used as a
+ * scratch buffer, but restored to its original contents before
+ * the function returns.
+ */
+typedef int each_loose_object_fn(const struct object_id *oid,
+ const char *path,
+ void *data);
+typedef int each_loose_cruft_fn(const char *basename,
+ const char *path,
+ void *data);
+typedef int each_loose_subdir_fn(unsigned int nr,
+ const char *path,
+ void *data);
+int for_each_file_in_obj_subdir(unsigned int subdir_nr,
+ struct strbuf *path,
+ each_loose_object_fn obj_cb,
+ each_loose_cruft_fn cruft_cb,
+ each_loose_subdir_fn subdir_cb,
+ void *data);
+int for_each_loose_file_in_objdir(const char *path,
+ each_loose_object_fn obj_cb,
+ each_loose_cruft_fn cruft_cb,
+ each_loose_subdir_fn subdir_cb,
+ void *data);
+int for_each_loose_file_in_objdir_buf(struct strbuf *path,
+ each_loose_object_fn obj_cb,
+ each_loose_cruft_fn cruft_cb,
+ each_loose_subdir_fn subdir_cb,
+ void *data);
+
+/* Flags for for_each_*_object() below. */
+enum for_each_object_flags {
+ /* Iterate only over local objects, not alternates. */
+ FOR_EACH_OBJECT_LOCAL_ONLY = (1<<0),
+
+ /* Only iterate over packs obtained from the promisor remote. */
+ FOR_EACH_OBJECT_PROMISOR_ONLY = (1<<1),
+
+ /*
+ * Visit objects within a pack in packfile order rather than .idx order
+ */
+ FOR_EACH_OBJECT_PACK_ORDER = (1<<2),
+
+ /* Only iterate over packs that are not marked as kept in-core. */
+ FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS = (1<<3),
+
+ /* Only iterate over packs that do not have .keep files. */
+ FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS = (1<<4),
+};
+
+/*
+ * Iterate over all accessible loose objects without respect to
+ * reachability. By default, this includes both local and alternate objects.
+ * The order in which objects are visited is unspecified.
+ *
+ * Any flags specific to packs are ignored.
+ */
+int for_each_loose_object(each_loose_object_fn, void *,
+ enum for_each_object_flags flags);
+
+/*
+ * Iterate over all accessible packed objects without respect to reachability.
+ * By default, this includes both local and alternate packs.
+ *
+ * Note that some objects may appear twice if they are found in multiple packs.
+ * Each pack is visited in an unspecified order. By default, objects within a
+ * pack are visited in pack-idx order (i.e., sorted by oid).
+ */
+typedef int each_packed_object_fn(const struct object_id *oid,
+ struct packed_git *pack,
+ uint32_t pos,
+ void *data);
+int for_each_object_in_pack(struct packed_git *p,
+ each_packed_object_fn, void *data,
+ enum for_each_object_flags flags);
+int for_each_packed_object(struct repository *repo, each_packed_object_fn cb,
+ void *data, enum for_each_object_flags flags);
+
+void *read_object_with_reference(struct repository *r,
+ const struct object_id *oid,
+ enum object_type required_type,
+ unsigned long *size,
+ struct object_id *oid_ret);
#endif /* OBJECT_STORE_H */
diff --git a/object.c b/object.c
index 100bf9b8d1..3b15469139 100644
--- a/object.c
+++ b/object.c
@@ -1,4 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "git-compat-util.h"
@@ -7,25 +6,23 @@
#include "object.h"
#include "replace-object.h"
#include "object-file.h"
-#include "object-store.h"
#include "blob.h"
#include "statinfo.h"
#include "tree.h"
#include "commit.h"
#include "tag.h"
#include "alloc.h"
-#include "packfile.h"
#include "commit-graph.h"
-#include "loose.h"
-unsigned int get_max_object_index(void)
+unsigned int get_max_object_index(const struct repository *repo)
{
- return the_repository->parsed_objects->obj_hash_size;
+ return repo->parsed_objects->obj_hash_size;
}
-struct object *get_indexed_object(unsigned int idx)
+struct object *get_indexed_object(const struct repository *repo,
+ unsigned int idx)
{
- return the_repository->parsed_objects->obj_hash[idx];
+ return repo->parsed_objects->obj_hash[idx];
}
static const char *object_type_strings[] = {
@@ -283,10 +280,11 @@ struct object *parse_object_buffer(struct repository *r, const struct object_id
return obj;
}
-struct object *parse_object_or_die(const struct object_id *oid,
+struct object *parse_object_or_die(struct repository *repo,
+ const struct object_id *oid,
const char *name)
{
- struct object *o = parse_object(the_repository, oid);
+ struct object *o = parse_object(repo, oid);
if (o)
return o;
@@ -491,45 +489,12 @@ void object_array_clear(struct object_array *array)
array->nr = array->alloc = 0;
}
-/*
- * Return true if array already contains an entry.
- */
-static int contains_object(struct object_array *array,
- const struct object *item, const char *name)
-{
- unsigned nr = array->nr, i;
- struct object_array_entry *object = array->objects;
-
- for (i = 0; i < nr; i++, object++)
- if (item == object->item && !strcmp(object->name, name))
- return 1;
- return 0;
-}
-
-void object_array_remove_duplicates(struct object_array *array)
-{
- unsigned nr = array->nr, src;
- struct object_array_entry *objects = array->objects;
-
- array->nr = 0;
- for (src = 0; src < nr; src++) {
- if (!contains_object(array, objects[src].item,
- objects[src].name)) {
- if (src != array->nr)
- objects[array->nr] = objects[src];
- array->nr++;
- } else {
- object_array_release_entry(&objects[src]);
- }
- }
-}
-
-void clear_object_flags(unsigned flags)
+void clear_object_flags(struct repository *repo, unsigned flags)
{
int i;
- for (i=0; i < the_repository->parsed_objects->obj_hash_size; i++) {
- struct object *obj = the_repository->parsed_objects->obj_hash[i];
+ for (i = 0; i < repo->parsed_objects->obj_hash_size; i++) {
+ struct object *obj = repo->parsed_objects->obj_hash[i];
if (obj)
obj->flags &= ~flags;
}
@@ -566,70 +531,6 @@ struct parsed_object_pool *parsed_object_pool_new(struct repository *repo)
return o;
}
-struct raw_object_store *raw_object_store_new(void)
-{
- struct raw_object_store *o = xmalloc(sizeof(*o));
-
- memset(o, 0, sizeof(*o));
- INIT_LIST_HEAD(&o->packed_git_mru);
- hashmap_init(&o->pack_map, pack_map_entry_cmp, NULL, 0);
- pthread_mutex_init(&o->replace_mutex, NULL);
- return o;
-}
-
-void free_object_directory(struct object_directory *odb)
-{
- free(odb->path);
- odb_clear_loose_cache(odb);
- loose_object_map_clear(&odb->loose_map);
- free(odb);
-}
-
-static void free_object_directories(struct raw_object_store *o)
-{
- while (o->odb) {
- struct object_directory *next;
-
- next = o->odb->next;
- free_object_directory(o->odb);
- o->odb = next;
- }
- kh_destroy_odb_path_map(o->odb_by_path);
- o->odb_by_path = NULL;
-}
-
-void raw_object_store_clear(struct raw_object_store *o)
-{
- FREE_AND_NULL(o->alternate_db);
-
- oidmap_free(o->replace_map, 1);
- FREE_AND_NULL(o->replace_map);
- pthread_mutex_destroy(&o->replace_mutex);
-
- free_commit_graph(o->commit_graph);
- o->commit_graph = NULL;
- o->commit_graph_attempted = 0;
-
- free_object_directories(o);
- o->odb_tail = NULL;
- o->loaded_alternates = 0;
-
- INIT_LIST_HEAD(&o->packed_git_mru);
- close_object_store(o);
-
- /*
- * `close_object_store()` only closes the packfiles, but doesn't free
- * them. We thus have to do this manually.
- */
- for (struct packed_git *p = o->packed_git, *next; p; p = next) {
- next = p->next;
- free(p);
- }
- o->packed_git = NULL;
-
- hashmap_clear(&o->pack_map);
-}
-
void parsed_object_pool_reset_commit_grafts(struct parsed_object_pool *o)
{
for (int i = 0; i < o->grafts_nr; i++) {
diff --git a/object.h b/object.h
index 17f32f1103..8c3c1c46e1 100644
--- a/object.h
+++ b/object.h
@@ -169,12 +169,13 @@ int type_from_string_gently(const char *str, ssize_t, int gentle);
/*
* Return the current number of buckets in the object hashmap.
*/
-unsigned int get_max_object_index(void);
+unsigned int get_max_object_index(const struct repository *repo);
/*
* Return the object from the specified bucket in the object hashmap.
*/
-struct object *get_indexed_object(unsigned int);
+struct object *get_indexed_object(const struct repository *repo,
+ unsigned int);
/*
* This can be used to see if we have heard of the object before, but
@@ -231,7 +232,8 @@ struct object *parse_object_with_flags(struct repository *r,
* "name" parameter is not NULL, it is included in the error message
* (otherwise, the hex object ID is given).
*/
-struct object *parse_object_or_die(const struct object_id *oid, const char *name);
+struct object *parse_object_or_die(struct repository *repo, const struct object_id *oid,
+ const char *name);
/* Given the result of read_sha1_file(), returns the object after
* parsing it. eaten_p indicates if the object has a borrowed copy
@@ -325,18 +327,12 @@ void object_array_filter(struct object_array *array,
object_array_each_func_t want, void *cb_data);
/*
- * Remove from array all but the first entry with a given name.
- * Warning: this function uses an O(N^2) algorithm.
- */
-void object_array_remove_duplicates(struct object_array *array);
-
-/*
* Remove any objects from the array, freeing all used memory; afterwards
* the array is ready to store more objects with add_object_array().
*/
void object_array_clear(struct object_array *array);
-void clear_object_flags(unsigned flags);
+void clear_object_flags(struct repository *repo, unsigned flags);
/*
* Clear the specified object flags from all in-core commit objects from
diff --git a/oss-fuzz/fuzz-pack-idx.c b/oss-fuzz/fuzz-pack-idx.c
index 3e190214d1..609a343ee3 100644
--- a/oss-fuzz/fuzz-pack-idx.c
+++ b/oss-fuzz/fuzz-pack-idx.c
@@ -1,5 +1,5 @@
#include "git-compat-util.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "packfile.h"
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index 34e86d4994..7f400ee012 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -1,11 +1,10 @@
-#define USE_THE_REPOSITORY_VARIABLE
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "git-compat-util.h"
#include "environment.h"
#include "gettext.h"
#include "hex.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "commit.h"
#include "diff.h"
#include "revision.h"
@@ -26,6 +25,8 @@
#include "alloc.h"
#include "refs.h"
#include "strmap.h"
+#include "midx.h"
+#include "pack-revindex.h"
struct bitmapped_commit {
struct commit *commit;
@@ -43,14 +44,17 @@ static inline int bitmap_writer_nr_selected_commits(struct bitmap_writer *writer
}
void bitmap_writer_init(struct bitmap_writer *writer, struct repository *r,
- struct packing_data *pdata)
+ struct packing_data *pdata,
+ struct multi_pack_index *midx)
{
memset(writer, 0, sizeof(struct bitmap_writer));
if (writer->bitmaps)
BUG("bitmap writer already initialized");
+ writer->repo = r;
writer->bitmaps = kh_init_oid_map();
writer->pseudo_merge_commits = kh_init_oid_map();
writer->to_pack = pdata;
+ writer->midx = midx;
string_list_init_dup(&writer->pseudo_merge_groups);
@@ -113,6 +117,11 @@ void bitmap_writer_build_type_index(struct bitmap_writer *writer,
struct pack_idx_entry **index)
{
uint32_t i;
+ uint32_t base_objects = 0;
+
+ if (writer->midx)
+ base_objects = writer->midx->num_objects +
+ writer->midx->num_objects_in_base;
writer->commits = ewah_new();
writer->trees = ewah_new();
@@ -142,19 +151,19 @@ void bitmap_writer_build_type_index(struct bitmap_writer *writer,
switch (real_type) {
case OBJ_COMMIT:
- ewah_set(writer->commits, i);
+ ewah_set(writer->commits, i + base_objects);
break;
case OBJ_TREE:
- ewah_set(writer->trees, i);
+ ewah_set(writer->trees, i + base_objects);
break;
case OBJ_BLOB:
- ewah_set(writer->blobs, i);
+ ewah_set(writer->blobs, i + base_objects);
break;
case OBJ_TAG:
- ewah_set(writer->tags, i);
+ ewah_set(writer->tags, i + base_objects);
break;
default:
@@ -207,19 +216,37 @@ void bitmap_writer_push_commit(struct bitmap_writer *writer,
static uint32_t find_object_pos(struct bitmap_writer *writer,
const struct object_id *oid, int *found)
{
- struct object_entry *entry = packlist_find(writer->to_pack, oid);
+ struct object_entry *entry;
+
+ entry = packlist_find(writer->to_pack, oid);
+ if (entry) {
+ uint32_t base_objects = 0;
+ if (writer->midx)
+ base_objects = writer->midx->num_objects +
+ writer->midx->num_objects_in_base;
- if (!entry) {
if (found)
- *found = 0;
- warning("Failed to write bitmap index. Packfile doesn't have full closure "
- "(object %s is missing)", oid_to_hex(oid));
- return 0;
+ *found = 1;
+ return oe_in_pack_pos(writer->to_pack, entry) + base_objects;
+ } else if (writer->midx) {
+ uint32_t at, pos;
+
+ if (!bsearch_midx(oid, writer->midx, &at))
+ goto missing;
+ if (midx_to_pack_pos(writer->midx, at, &pos) < 0)
+ goto missing;
+
+ if (found)
+ *found = 1;
+ return pos;
}
+missing:
if (found)
- *found = 1;
- return oe_in_pack_pos(writer->to_pack, entry);
+ *found = 0;
+ warning("Failed to write bitmap index. Packfile doesn't have full closure "
+ "(object %s is missing)", oid_to_hex(oid));
+ return 0;
}
static void compute_xor_offsets(struct bitmap_writer *writer)
@@ -415,9 +442,9 @@ next:
bb->commits[bb->commits_nr++] = r->item;
}
- trace2_data_intmax("pack-bitmap-write", the_repository,
+ trace2_data_intmax("pack-bitmap-write", writer->repo,
"num_selected_commits", writer->selected_nr);
- trace2_data_intmax("pack-bitmap-write", the_repository,
+ trace2_data_intmax("pack-bitmap-write", writer->repo,
"num_maximal_commits", num_maximal);
release_revisions(&revs);
@@ -460,7 +487,7 @@ static int fill_bitmap_tree(struct bitmap_writer *writer,
switch (object_type(entry.mode)) {
case OBJ_TREE:
if (fill_bitmap_tree(writer, bitmap,
- lookup_tree(the_repository, &entry.oid)) < 0)
+ lookup_tree(writer->repo, &entry.oid)) < 0)
return -1;
break;
case OBJ_BLOB:
@@ -536,7 +563,7 @@ static int fill_bitmap_commit(struct bitmap_writer *writer,
return -1;
bitmap_set(ent->bitmap, pos);
prio_queue_put(tree_queue,
- repo_get_commit_tree(the_repository, c));
+ repo_get_commit_tree(writer->repo, c));
}
for (p = c->parents; p; p = p->next) {
@@ -586,15 +613,15 @@ int bitmap_writer_build(struct bitmap_writer *writer)
struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
struct prio_queue tree_queue = { NULL };
struct bitmap_index *old_bitmap;
- uint32_t *mapping;
+ uint32_t *mapping = NULL;
int closed = 1; /* until proven otherwise */
if (writer->show_progress)
- writer->progress = start_progress(the_repository,
+ writer->progress = start_progress(writer->repo,
"Building bitmaps",
writer->selected_nr);
trace2_region_enter("pack-bitmap-write", "building_bitmaps_total",
- the_repository);
+ writer->repo);
old_bitmap = prepare_bitmap_git(writer->to_pack->repo);
if (old_bitmap)
@@ -645,10 +672,10 @@ int bitmap_writer_build(struct bitmap_writer *writer)
free(mapping);
trace2_region_leave("pack-bitmap-write", "building_bitmaps_total",
- the_repository);
- trace2_data_intmax("pack-bitmap-write", the_repository,
+ writer->repo);
+ trace2_data_intmax("pack-bitmap-write", writer->repo,
"building_bitmaps_reused", reused_bitmaps_nr);
- trace2_data_intmax("pack-bitmap-write", the_repository,
+ trace2_data_intmax("pack-bitmap-write", writer->repo,
"building_bitmaps_pseudo_merge_reused",
reused_pseudo_merge_bitmaps_nr);
@@ -711,7 +738,7 @@ void bitmap_writer_select_commits(struct bitmap_writer *writer,
}
if (writer->show_progress)
- writer->progress = start_progress(the_repository,
+ writer->progress = start_progress(writer->repo,
"Selecting bitmap commits", 0);
for (;;) {
@@ -960,7 +987,7 @@ static void write_lookup_table(struct bitmap_writer *writer, struct hashfile *f,
for (i = 0; i < bitmap_writer_nr_selected_commits(writer); i++)
table_inv[table[i]] = i;
- trace2_region_enter("pack-bitmap-write", "writing_lookup_table", the_repository);
+ trace2_region_enter("pack-bitmap-write", "writing_lookup_table", writer->repo);
for (i = 0; i < bitmap_writer_nr_selected_commits(writer); i++) {
struct bitmapped_commit *selected = &writer->selected[table[i]];
uint32_t xor_offset = selected->xor_offset;
@@ -987,7 +1014,7 @@ static void write_lookup_table(struct bitmap_writer *writer, struct hashfile *f,
hashwrite_be64(f, (uint64_t)offsets[table[i]]);
hashwrite_be32(f, xor_row);
}
- trace2_region_leave("pack-bitmap-write", "writing_lookup_table", the_repository);
+ trace2_region_leave("pack-bitmap-write", "writing_lookup_table", writer->repo);
free(table);
free(table_inv);
@@ -1008,7 +1035,7 @@ static void write_hash_cache(struct hashfile *f,
void bitmap_writer_set_checksum(struct bitmap_writer *writer,
const unsigned char *sha1)
{
- hashcpy(writer->pack_checksum, sha1, the_repository->hash_algo);
+ hashcpy(writer->pack_checksum, sha1, writer->repo->hash_algo);
}
void bitmap_writer_finish(struct bitmap_writer *writer,
@@ -1021,7 +1048,7 @@ void bitmap_writer_finish(struct bitmap_writer *writer,
struct strbuf tmp_file = STRBUF_INIT;
struct hashfile *f;
off_t *offsets = NULL;
- uint32_t i;
+ uint32_t i, base_objects;
struct bitmap_disk_header header;
@@ -1030,15 +1057,15 @@ void bitmap_writer_finish(struct bitmap_writer *writer,
if (writer->pseudo_merges_nr)
options |= BITMAP_OPT_PSEUDO_MERGES;
- f = hashfd(fd, tmp_file.buf);
+ f = hashfd(writer->repo->hash_algo, fd, tmp_file.buf);
memcpy(header.magic, BITMAP_IDX_SIGNATURE, sizeof(BITMAP_IDX_SIGNATURE));
header.version = htons(default_version);
header.options = htons(flags | options);
header.entry_count = htonl(bitmap_writer_nr_selected_commits(writer));
- hashcpy(header.checksum, writer->pack_checksum, the_repository->hash_algo);
+ hashcpy(header.checksum, writer->pack_checksum, writer->repo->hash_algo);
- hashwrite(f, &header, sizeof(header) - GIT_MAX_RAWSZ + the_hash_algo->rawsz);
+ hashwrite(f, &header, sizeof(header) - GIT_MAX_RAWSZ + writer->repo->hash_algo->rawsz);
dump_bitmap(f, writer->commits);
dump_bitmap(f, writer->trees);
dump_bitmap(f, writer->blobs);
@@ -1047,6 +1074,12 @@ void bitmap_writer_finish(struct bitmap_writer *writer,
if (options & BITMAP_OPT_LOOKUP_TABLE)
CALLOC_ARRAY(offsets, writer->to_pack->nr_objects);
+ if (writer->midx)
+ base_objects = writer->midx->num_objects +
+ writer->midx->num_objects_in_base;
+ else
+ base_objects = 0;
+
for (i = 0; i < bitmap_writer_nr_selected_commits(writer); i++) {
struct bitmapped_commit *stored = &writer->selected[i];
int commit_pos = oid_pos(&stored->commit->object.oid, index,
@@ -1055,7 +1088,7 @@ void bitmap_writer_finish(struct bitmap_writer *writer,
if (commit_pos < 0)
BUG(_("trying to write commit not in index"));
- stored->commit_pos = commit_pos;
+ stored->commit_pos = commit_pos + base_objects;
}
write_selected_commits_v1(writer, f, offsets);
@@ -1072,7 +1105,7 @@ void bitmap_writer_finish(struct bitmap_writer *writer,
finalize_hashfile(f, NULL, FSYNC_COMPONENT_PACK_METADATA,
CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);
- if (adjust_shared_perm(the_repository, tmp_file.buf))
+ if (adjust_shared_perm(writer->repo, tmp_file.buf))
die_errno("unable to make temporary bitmap file readable");
if (rename(tmp_file.buf, filename))
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 6406953d32..b9f1d86604 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -17,8 +17,7 @@
#include "packfile.h"
#include "repository.h"
#include "trace2.h"
-#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "list-objects-filter-options.h"
#include "midx.h"
#include "config.h"
@@ -54,6 +53,16 @@ struct bitmap_index {
struct packed_git *pack;
struct multi_pack_index *midx;
+ /*
+ * If using a multi-pack index chain, 'base' points to the
+ * bitmap index corresponding to this bitmap's midx->base_midx.
+ *
+ * base_nr indicates how many layers precede this one, and is
+ * zero when base is NULL.
+ */
+ struct bitmap_index *base;
+ uint32_t base_nr;
+
/* mmapped buffer of the whole bitmap index */
unsigned char *map;
size_t map_size; /* size of the mmaped buffer */
@@ -71,6 +80,23 @@ struct bitmap_index {
struct ewah_bitmap *blobs;
struct ewah_bitmap *tags;
+ /*
+ * Type index arrays when this bitmap is associated with an
+ * incremental multi-pack index chain.
+ *
+ * If n is the number of unique layers in the MIDX chain, then
+ * commits_all[n-1] is this structs 'commits' field,
+ * commits_all[n-2] is the commits field of this bitmap's
+ * 'base', and so on.
+ *
+ * When associated either with a non-incremental MIDX or a
+ * single packfile, these arrays each contain a single element.
+ */
+ struct ewah_bitmap **commits_all;
+ struct ewah_bitmap **trees_all;
+ struct ewah_bitmap **blobs_all;
+ struct ewah_bitmap **tags_all;
+
/* Map from object ID -> `stored_bitmap` for all the bitmapped commits */
kh_oid_map_t *bitmaps;
@@ -170,6 +196,15 @@ static struct ewah_bitmap *read_bitmap_1(struct bitmap_index *index)
return read_bitmap(index->map, index->map_size, &index->map_pos);
}
+static uint32_t bitmap_num_objects_total(struct bitmap_index *index)
+{
+ if (index->midx) {
+ struct multi_pack_index *m = index->midx;
+ return m->num_objects + m->num_objects_in_base;
+ }
+ return index->pack->num_objects;
+}
+
static uint32_t bitmap_num_objects(struct bitmap_index *index)
{
if (index->midx)
@@ -377,8 +412,15 @@ static int load_bitmap_entries_v1(struct bitmap_index *index)
char *midx_bitmap_filename(struct multi_pack_index *midx)
{
struct strbuf buf = STRBUF_INIT;
- get_midx_filename_ext(midx->repo->hash_algo, &buf, midx->object_dir,
- get_midx_checksum(midx), MIDX_EXT_BITMAP);
+ if (midx->has_chain)
+ get_split_midx_filename_ext(midx->repo->hash_algo, &buf,
+ midx->object_dir,
+ get_midx_checksum(midx),
+ MIDX_EXT_BITMAP);
+ else
+ get_midx_filename_ext(midx->repo->hash_algo, &buf,
+ midx->object_dir, get_midx_checksum(midx),
+ MIDX_EXT_BITMAP);
return strbuf_detach(&buf, NULL);
}
@@ -445,16 +487,21 @@ static int open_midx_bitmap_1(struct bitmap_index *bitmap_git,
goto cleanup;
}
- for (i = 0; i < bitmap_git->midx->num_packs; i++) {
- if (prepare_midx_pack(bitmap_repo(bitmap_git),
- bitmap_git->midx,
- i)) {
+ for (i = 0; i < bitmap_git->midx->num_packs + bitmap_git->midx->num_packs_in_base; i++) {
+ if (prepare_midx_pack(bitmap_repo(bitmap_git), bitmap_git->midx, i)) {
warning(_("could not open pack %s"),
bitmap_git->midx->pack_names[i]);
goto cleanup;
}
}
+ if (midx->base_midx) {
+ bitmap_git->base = prepare_midx_bitmap_git(midx->base_midx);
+ bitmap_git->base_nr = bitmap_git->base->base_nr + 1;
+ } else {
+ bitmap_git->base_nr = 0;
+ }
+
return 0;
cleanup:
@@ -506,6 +553,7 @@ static int open_pack_bitmap_1(struct bitmap_index *bitmap_git, struct packed_git
bitmap_git->map_size = xsize_t(st.st_size);
bitmap_git->map = xmmap(NULL, bitmap_git->map_size, PROT_READ, MAP_PRIVATE, fd, 0);
bitmap_git->map_pos = 0;
+ bitmap_git->base_nr = 0;
close(fd);
if (load_bitmap_header(bitmap_git) < 0) {
@@ -525,8 +573,7 @@ static int open_pack_bitmap_1(struct bitmap_index *bitmap_git, struct packed_git
static int load_reverse_index(struct repository *r, struct bitmap_index *bitmap_git)
{
if (bitmap_is_midx(bitmap_git)) {
- uint32_t i;
- int ret;
+ struct multi_pack_index *m;
/*
* The multi-pack-index's .rev file is already loaded via
@@ -535,17 +582,47 @@ static int load_reverse_index(struct repository *r, struct bitmap_index *bitmap_
* But we still need to open the individual pack .rev files,
* since we will need to make use of them in pack-objects.
*/
- for (i = 0; i < bitmap_git->midx->num_packs; i++) {
- ret = load_pack_revindex(r, bitmap_git->midx->packs[i]);
- if (ret)
- return ret;
+ for (m = bitmap_git->midx; m; m = m->base_midx) {
+ uint32_t i;
+ int ret;
+
+ for (i = 0; i < m->num_packs; i++) {
+ ret = load_pack_revindex(r, m->packs[i]);
+ if (ret)
+ return ret;
+ }
}
return 0;
}
return load_pack_revindex(r, bitmap_git->pack);
}
-static int load_bitmap(struct repository *r, struct bitmap_index *bitmap_git)
+static void load_all_type_bitmaps(struct bitmap_index *bitmap_git)
+{
+ struct bitmap_index *curr = bitmap_git;
+ size_t i = bitmap_git->base_nr;
+
+ ALLOC_ARRAY(bitmap_git->commits_all, bitmap_git->base_nr + 1);
+ ALLOC_ARRAY(bitmap_git->trees_all, bitmap_git->base_nr + 1);
+ ALLOC_ARRAY(bitmap_git->blobs_all, bitmap_git->base_nr + 1);
+ ALLOC_ARRAY(bitmap_git->tags_all, bitmap_git->base_nr + 1);
+
+ while (curr) {
+ bitmap_git->commits_all[i] = curr->commits;
+ bitmap_git->trees_all[i] = curr->trees;
+ bitmap_git->blobs_all[i] = curr->blobs;
+ bitmap_git->tags_all[i] = curr->tags;
+
+ curr = curr->base;
+ if (curr && !i)
+ BUG("unexpected number of bitmap layers, expected %"PRIu32,
+ bitmap_git->base_nr + 1);
+ i -= 1;
+ }
+}
+
+static int load_bitmap(struct repository *r, struct bitmap_index *bitmap_git,
+ int recursing)
{
assert(bitmap_git->map);
@@ -564,6 +641,16 @@ static int load_bitmap(struct repository *r, struct bitmap_index *bitmap_git)
if (!bitmap_git->table_lookup && load_bitmap_entries_v1(bitmap_git) < 0)
goto failed;
+ if (bitmap_git->base) {
+ if (!bitmap_is_midx(bitmap_git))
+ BUG("non-MIDX bitmap has non-NULL base bitmap index");
+ if (load_bitmap(r, bitmap_git->base, 1) < 0)
+ goto failed;
+ }
+
+ if (!recursing)
+ load_all_type_bitmaps(bitmap_git);
+
return 0;
failed:
@@ -639,7 +726,7 @@ struct bitmap_index *prepare_bitmap_git(struct repository *r)
{
struct bitmap_index *bitmap_git = xcalloc(1, sizeof(*bitmap_git));
- if (!open_bitmap(r, bitmap_git) && !load_bitmap(r, bitmap_git))
+ if (!open_bitmap(r, bitmap_git) && !load_bitmap(r, bitmap_git, 0))
return bitmap_git;
free_bitmap_index(bitmap_git);
@@ -648,16 +735,30 @@ struct bitmap_index *prepare_bitmap_git(struct repository *r)
struct bitmap_index *prepare_midx_bitmap_git(struct multi_pack_index *midx)
{
- struct repository *r = midx->repo;
struct bitmap_index *bitmap_git = xcalloc(1, sizeof(*bitmap_git));
- if (!open_midx_bitmap_1(bitmap_git, midx) && !load_bitmap(r, bitmap_git))
+ if (!open_midx_bitmap_1(bitmap_git, midx))
return bitmap_git;
free_bitmap_index(bitmap_git);
return NULL;
}
+int bitmap_index_contains_pack(struct bitmap_index *bitmap, struct packed_git *pack)
+{
+ for (; bitmap; bitmap = bitmap->base) {
+ if (bitmap_is_midx(bitmap)) {
+ for (size_t i = 0; i < bitmap->midx->num_packs; i++)
+ if (bitmap->midx->packs[i] == pack)
+ return 1;
+ } else if (bitmap->pack == pack) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
struct include_data {
struct bitmap_index *bitmap_git;
struct bitmap *base;
@@ -896,26 +997,42 @@ corrupt:
return NULL;
}
-struct ewah_bitmap *bitmap_for_commit(struct bitmap_index *bitmap_git,
- struct commit *commit)
+static struct ewah_bitmap *find_bitmap_for_commit(struct bitmap_index *bitmap_git,
+ struct commit *commit,
+ struct bitmap_index **found)
{
- khiter_t hash_pos = kh_get_oid_map(bitmap_git->bitmaps,
- commit->object.oid);
+ khiter_t hash_pos;
+ if (!bitmap_git)
+ return NULL;
+
+ hash_pos = kh_get_oid_map(bitmap_git->bitmaps, commit->object.oid);
if (hash_pos >= kh_end(bitmap_git->bitmaps)) {
struct stored_bitmap *bitmap = NULL;
if (!bitmap_git->table_lookup)
- return NULL;
+ return find_bitmap_for_commit(bitmap_git->base, commit,
+ found);
/* this is a fairly hot codepath - no trace2_region please */
/* NEEDSWORK: cache misses aren't recorded */
bitmap = lazy_bitmap_for_commit(bitmap_git, commit);
if (!bitmap)
- return NULL;
+ return find_bitmap_for_commit(bitmap_git->base, commit,
+ found);
+ if (found)
+ *found = bitmap_git;
return lookup_stored_bitmap(bitmap);
}
+ if (found)
+ *found = bitmap_git;
return lookup_stored_bitmap(kh_value(bitmap_git->bitmaps, hash_pos));
}
+struct ewah_bitmap *bitmap_for_commit(struct bitmap_index *bitmap_git,
+ struct commit *commit)
+{
+ return find_bitmap_for_commit(bitmap_git, commit, NULL);
+}
+
static inline int bitmap_position_extended(struct bitmap_index *bitmap_git,
const struct object_id *oid)
{
@@ -924,7 +1041,7 @@ static inline int bitmap_position_extended(struct bitmap_index *bitmap_git,
if (pos < kh_end(positions)) {
int bitmap_pos = kh_value(positions, pos);
- return bitmap_pos + bitmap_num_objects(bitmap_git);
+ return bitmap_pos + bitmap_num_objects_total(bitmap_git);
}
return -1;
@@ -992,7 +1109,7 @@ static int ext_index_add_object(struct bitmap_index *bitmap_git,
bitmap_pos = kh_value(eindex->positions, hash_pos);
}
- return bitmap_pos + bitmap_num_objects(bitmap_git);
+ return bitmap_pos + bitmap_num_objects_total(bitmap_git);
}
struct bitmap_show_data {
@@ -1024,10 +1141,15 @@ static unsigned apply_pseudo_merges_for_commit_1(struct bitmap_index *bitmap_git
struct commit *commit,
uint32_t commit_pos)
{
- int ret;
+ struct bitmap_index *curr = bitmap_git;
+ int ret = 0;
- ret = apply_pseudo_merges_for_commit(&bitmap_git->pseudo_merges,
- result, commit, commit_pos);
+ while (curr) {
+ ret += apply_pseudo_merges_for_commit(&curr->pseudo_merges,
+ result, commit,
+ commit_pos);
+ curr = curr->base;
+ }
if (ret)
pseudo_merges_satisfied_nr += ret;
@@ -1301,7 +1423,7 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
revs->tag_objects = tmp_tags;
reset_revision_walk();
- clear_object_flags(UNINTERESTING);
+ clear_object_flags(repo, UNINTERESTING);
/*
* Then add the boundary commit(s) as fill-in traversal tips.
@@ -1342,11 +1464,17 @@ struct ewah_bitmap *pseudo_merge_bitmap_for_commit(struct bitmap_index *bitmap_g
if (pos < 0 || pos >= bitmap_num_objects(bitmap_git))
goto done;
+ /*
+ * Use bitmap-relative positions instead of offsetting
+ * by bitmap_git->num_objects_in_base because we use
+ * this to find a match in pseudo_merge_for_parents(),
+ * and pseudo-merge groups cannot span multiple bitmap
+ * layers.
+ */
bitmap_set(parents, pos);
}
- match = pseudo_merge_for_parents(&bitmap_git->pseudo_merges,
- parents);
+ match = pseudo_merge_for_parents(&bitmap_git->pseudo_merges, parents);
done:
bitmap_free(parents);
@@ -1500,7 +1628,9 @@ static void show_extended_objects(struct bitmap_index *bitmap_git,
for (i = 0; i < eindex->count; ++i) {
struct object *obj;
- if (!bitmap_get(objects, st_add(bitmap_num_objects(bitmap_git), i)))
+ if (!bitmap_get(objects,
+ st_add(bitmap_num_objects_total(bitmap_git),
+ i)))
continue;
obj = eindex->objects[i];
@@ -1509,29 +1639,33 @@ static void show_extended_objects(struct bitmap_index *bitmap_git,
(obj->type == OBJ_TAG && !revs->tag_objects))
continue;
- show_reach(&obj->oid, obj->type, 0, eindex->hashes[i], NULL, 0);
+ show_reach(&obj->oid, obj->type, 0, eindex->hashes[i], NULL, 0, NULL);
}
}
-static void init_type_iterator(struct ewah_iterator *it,
+static void init_type_iterator(struct ewah_or_iterator *it,
struct bitmap_index *bitmap_git,
enum object_type type)
{
switch (type) {
case OBJ_COMMIT:
- ewah_iterator_init(it, bitmap_git->commits);
+ ewah_or_iterator_init(it, bitmap_git->commits_all,
+ bitmap_git->base_nr + 1);
break;
case OBJ_TREE:
- ewah_iterator_init(it, bitmap_git->trees);
+ ewah_or_iterator_init(it, bitmap_git->trees_all,
+ bitmap_git->base_nr + 1);
break;
case OBJ_BLOB:
- ewah_iterator_init(it, bitmap_git->blobs);
+ ewah_or_iterator_init(it, bitmap_git->blobs_all,
+ bitmap_git->base_nr + 1);
break;
case OBJ_TAG:
- ewah_iterator_init(it, bitmap_git->tags);
+ ewah_or_iterator_init(it, bitmap_git->tags_all,
+ bitmap_git->base_nr + 1);
break;
default:
@@ -1542,21 +1676,21 @@ static void init_type_iterator(struct ewah_iterator *it,
static void show_objects_for_type(
struct bitmap_index *bitmap_git,
+ struct bitmap *objects,
enum object_type object_type,
- show_reachable_fn show_reach)
+ show_reachable_fn show_reach,
+ void *payload)
{
size_t i = 0;
uint32_t offset;
- struct ewah_iterator it;
+ struct ewah_or_iterator it;
eword_t filter;
- struct bitmap *objects = bitmap_git->result;
-
init_type_iterator(&it, bitmap_git, object_type);
for (i = 0; i < objects->word_alloc &&
- ewah_iterator_next(&filter, &it); i++) {
+ ewah_or_iterator_next(&filter, &it); i++) {
eword_t word = objects->words[i] & filter;
size_t pos = (i * BITS_IN_EWORD);
@@ -1583,7 +1717,7 @@ static void show_objects_for_type(
nth_midxed_object_oid(&oid, m, index_pos);
pack_id = nth_midxed_pack_int_id(m, index_pos);
- pack = bitmap_git->midx->packs[pack_id];
+ pack = nth_midxed_pack(bitmap_git->midx, pack_id);
} else {
index_pos = pack_pos_to_index(bitmap_git->pack, pos + offset);
ofs = pack_pos_to_offset(bitmap_git->pack, pos + offset);
@@ -1595,9 +1729,11 @@ static void show_objects_for_type(
if (bitmap_git->hashes)
hash = get_be32(bitmap_git->hashes + index_pos);
- show_reach(&oid, object_type, 0, hash, pack, ofs);
+ show_reach(&oid, object_type, 0, hash, pack, ofs, payload);
}
}
+
+ ewah_or_iterator_release(&it);
}
static int in_bitmapped_pack(struct bitmap_index *bitmap_git,
@@ -1649,7 +1785,7 @@ static void filter_bitmap_exclude_type(struct bitmap_index *bitmap_git,
{
struct eindex *eindex = &bitmap_git->ext_index;
struct bitmap *tips;
- struct ewah_iterator it;
+ struct ewah_or_iterator it;
eword_t mask;
uint32_t i;
@@ -1666,7 +1802,7 @@ static void filter_bitmap_exclude_type(struct bitmap_index *bitmap_git,
* packfile.
*/
for (i = 0, init_type_iterator(&it, bitmap_git, type);
- i < to_filter->word_alloc && ewah_iterator_next(&mask, &it);
+ i < to_filter->word_alloc && ewah_or_iterator_next(&mask, &it);
i++) {
if (i < tips->word_alloc)
mask &= ~tips->words[i];
@@ -1679,13 +1815,14 @@ static void filter_bitmap_exclude_type(struct bitmap_index *bitmap_git,
* them individually.
*/
for (i = 0; i < eindex->count; i++) {
- size_t pos = st_add(i, bitmap_num_objects(bitmap_git));
+ size_t pos = st_add(i, bitmap_num_objects_total(bitmap_git));
if (eindex->objects[i]->type == type &&
bitmap_get(to_filter, pos) &&
!bitmap_get(tips, pos))
bitmap_unset(to_filter, pos);
}
+ ewah_or_iterator_release(&it);
bitmap_free(tips);
}
@@ -1705,7 +1842,7 @@ static unsigned long get_size_by_pos(struct bitmap_index *bitmap_git,
oi.sizep = &size;
- if (pos < bitmap_num_objects(bitmap_git)) {
+ if (pos < bitmap_num_objects_total(bitmap_git)) {
struct packed_git *pack;
off_t ofs;
@@ -1713,7 +1850,7 @@ static unsigned long get_size_by_pos(struct bitmap_index *bitmap_git,
uint32_t midx_pos = pack_pos_to_midx(bitmap_git->midx, pos);
uint32_t pack_id = nth_midxed_pack_int_id(bitmap_git->midx, midx_pos);
- pack = bitmap_git->midx->packs[pack_id];
+ pack = nth_midxed_pack(bitmap_git->midx, pack_id);
ofs = nth_midxed_offset(bitmap_git->midx, midx_pos);
} else {
pack = bitmap_git->pack;
@@ -1728,8 +1865,9 @@ static unsigned long get_size_by_pos(struct bitmap_index *bitmap_git,
die(_("unable to get size of %s"), oid_to_hex(&oid));
}
} else {
+ size_t eindex_pos = pos - bitmap_num_objects_total(bitmap_git);
struct eindex *eindex = &bitmap_git->ext_index;
- struct object *obj = eindex->objects[pos - bitmap_num_objects(bitmap_git)];
+ struct object *obj = eindex->objects[eindex_pos];
if (oid_object_info_extended(bitmap_repo(bitmap_git), &obj->oid,
&oi, 0) < 0)
die(_("unable to get size of %s"), oid_to_hex(&obj->oid));
@@ -1745,14 +1883,14 @@ static void filter_bitmap_blob_limit(struct bitmap_index *bitmap_git,
{
struct eindex *eindex = &bitmap_git->ext_index;
struct bitmap *tips;
- struct ewah_iterator it;
+ struct ewah_or_iterator it;
eword_t mask;
uint32_t i;
tips = find_tip_objects(bitmap_git, tip_objects, OBJ_BLOB);
for (i = 0, init_type_iterator(&it, bitmap_git, OBJ_BLOB);
- i < to_filter->word_alloc && ewah_iterator_next(&mask, &it);
+ i < to_filter->word_alloc && ewah_or_iterator_next(&mask, &it);
i++) {
eword_t word = to_filter->words[i] & mask;
unsigned offset;
@@ -1780,6 +1918,7 @@ static void filter_bitmap_blob_limit(struct bitmap_index *bitmap_git,
bitmap_unset(to_filter, pos);
}
+ ewah_or_iterator_release(&it);
bitmap_free(tips);
}
@@ -1882,7 +2021,7 @@ static void filter_packed_objects_from_bitmap(struct bitmap_index *bitmap_git,
uint32_t objects_nr;
size_t i, pos;
- objects_nr = bitmap_num_objects(bitmap_git);
+ objects_nr = bitmap_num_objects_total(bitmap_git);
pos = objects_nr / BITS_IN_EWORD;
if (pos > result->word_alloc)
@@ -1899,6 +2038,50 @@ static void filter_packed_objects_from_bitmap(struct bitmap_index *bitmap_git,
}
}
+int for_each_bitmapped_object(struct bitmap_index *bitmap_git,
+ struct list_objects_filter_options *filter,
+ show_reachable_fn show_reach,
+ void *payload)
+{
+ struct bitmap *filtered_bitmap = NULL;
+ uint32_t objects_nr;
+ size_t full_word_count;
+ int ret;
+
+ if (!can_filter_bitmap(filter)) {
+ ret = -1;
+ goto out;
+ }
+
+ objects_nr = bitmap_num_objects(bitmap_git);
+ full_word_count = objects_nr / BITS_IN_EWORD;
+
+ /* We start from the all-1 bitmap and then filter down from there. */
+ filtered_bitmap = bitmap_word_alloc(full_word_count + !!(objects_nr % BITS_IN_EWORD));
+ memset(filtered_bitmap->words, 0xff, full_word_count * sizeof(*filtered_bitmap->words));
+ for (size_t i = full_word_count * BITS_IN_EWORD; i < objects_nr; i++)
+ bitmap_set(filtered_bitmap, i);
+
+ if (filter_bitmap(bitmap_git, NULL, filtered_bitmap, filter) < 0) {
+ ret = -1;
+ goto out;
+ }
+
+ show_objects_for_type(bitmap_git, filtered_bitmap,
+ OBJ_COMMIT, show_reach, payload);
+ show_objects_for_type(bitmap_git, filtered_bitmap,
+ OBJ_TREE, show_reach, payload);
+ show_objects_for_type(bitmap_git, filtered_bitmap,
+ OBJ_BLOB, show_reach, payload);
+ show_objects_for_type(bitmap_git, filtered_bitmap,
+ OBJ_TAG, show_reach, payload);
+
+ ret = 0;
+out:
+ bitmap_free(filtered_bitmap);
+ return ret;
+}
+
struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
int filter_provided_objects)
{
@@ -1935,7 +2118,7 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
struct object *object = revs->pending.objects[i].item;
if (object->type == OBJ_NONE)
- parse_object_or_die(&object->oid, NULL);
+ parse_object_or_die(revs->repo, &object->oid, NULL);
while (object->type == OBJ_TAG) {
struct tag *tag = (struct tag *) object;
@@ -1945,7 +2128,7 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
else
object_list_insert(object, &wants);
- object = parse_object_or_die(get_tagged_oid(tag), NULL);
+ object = parse_object_or_die(revs->repo, get_tagged_oid(tag), NULL);
object->flags |= (tag->object.flags & UNINTERESTING);
}
@@ -1980,7 +2163,7 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
* from disk. this is the point of no return; after this the rev_list
* becomes invalidated and we must perform the revwalk through bitmaps
*/
- if (load_bitmap(revs->repo, bitmap_git) < 0)
+ if (load_bitmap(revs->repo, bitmap_git, 0) < 0)
goto cleanup;
if (!use_boundary_traversal)
@@ -2281,7 +2464,8 @@ void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
multi_pack_reuse = 0;
if (multi_pack_reuse) {
- for (i = 0; i < bitmap_git->midx->num_packs; i++) {
+ struct multi_pack_index *m = bitmap_git->midx;
+ for (i = 0; i < m->num_packs + m->num_packs_in_base; 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"),
@@ -2307,14 +2491,18 @@ void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
uint32_t pack_int_id;
if (bitmap_is_midx(bitmap_git)) {
+ struct multi_pack_index *m = bitmap_git->midx;
uint32_t preferred_pack_pos;
- if (midx_preferred_pack(bitmap_git->midx, &preferred_pack_pos) < 0) {
+ while (m->base_midx)
+ m = m->base_midx;
+
+ if (midx_preferred_pack(m, &preferred_pack_pos) < 0) {
warning(_("unable to compute preferred pack, disabling pack-reuse"));
return;
}
- pack = bitmap_git->midx->packs[preferred_pack_pos];
+ pack = nth_midxed_pack(m, preferred_pack_pos);
pack_int_id = preferred_pack_pos;
} else {
pack = bitmap_git->pack;
@@ -2388,13 +2576,17 @@ void traverse_bitmap_commit_list(struct bitmap_index *bitmap_git,
{
assert(bitmap_git->result);
- show_objects_for_type(bitmap_git, OBJ_COMMIT, show_reachable);
+ show_objects_for_type(bitmap_git, bitmap_git->result,
+ OBJ_COMMIT, show_reachable, NULL);
if (revs->tree_objects)
- show_objects_for_type(bitmap_git, OBJ_TREE, show_reachable);
+ show_objects_for_type(bitmap_git, bitmap_git->result,
+ OBJ_TREE, show_reachable, NULL);
if (revs->blob_objects)
- show_objects_for_type(bitmap_git, OBJ_BLOB, show_reachable);
+ show_objects_for_type(bitmap_git, bitmap_git->result,
+ OBJ_BLOB, show_reachable, NULL);
if (revs->tag_objects)
- show_objects_for_type(bitmap_git, OBJ_TAG, show_reachable);
+ show_objects_for_type(bitmap_git, bitmap_git->result,
+ OBJ_TAG, show_reachable, NULL);
show_extended_objects(bitmap_git, revs, show_reachable);
}
@@ -2406,12 +2598,12 @@ static uint32_t count_object_type(struct bitmap_index *bitmap_git,
struct eindex *eindex = &bitmap_git->ext_index;
uint32_t i = 0, count = 0;
- struct ewah_iterator it;
+ struct ewah_or_iterator it;
eword_t filter;
init_type_iterator(&it, bitmap_git, type);
- while (i < objects->word_alloc && ewah_iterator_next(&filter, &it)) {
+ while (i < objects->word_alloc && ewah_or_iterator_next(&filter, &it)) {
eword_t word = objects->words[i++] & filter;
count += ewah_bit_popcount64(word);
}
@@ -2419,10 +2611,12 @@ static uint32_t count_object_type(struct bitmap_index *bitmap_git,
for (i = 0; i < eindex->count; ++i) {
if (eindex->objects[i]->type == type &&
bitmap_get(objects,
- st_add(bitmap_num_objects(bitmap_git), i)))
+ st_add(bitmap_num_objects_total(bitmap_git), i)))
count++;
}
+ ewah_or_iterator_release(&it);
+
return count;
}
@@ -2454,6 +2648,8 @@ struct bitmap_test_data {
struct bitmap *tags;
struct progress *prg;
size_t seen;
+
+ struct bitmap_test_data *base_tdata;
};
static void test_bitmap_type(struct bitmap_test_data *tdata,
@@ -2462,6 +2658,11 @@ static void test_bitmap_type(struct bitmap_test_data *tdata,
enum object_type bitmap_type = OBJ_NONE;
int bitmaps_nr = 0;
+ if (bitmap_is_midx(tdata->bitmap_git)) {
+ while (pos < tdata->bitmap_git->midx->num_objects_in_base)
+ tdata = tdata->base_tdata;
+ }
+
if (bitmap_get(tdata->commits, pos)) {
bitmap_type = OBJ_COMMIT;
bitmaps_nr++;
@@ -2525,13 +2726,57 @@ static void test_show_commit(struct commit *commit, void *data)
display_progress(tdata->prg, ++tdata->seen);
}
+static uint32_t bitmap_total_entry_count(struct bitmap_index *bitmap_git)
+{
+ uint32_t total = 0;
+ do {
+ total = st_add(total, bitmap_git->entry_count);
+ bitmap_git = bitmap_git->base;
+ } while (bitmap_git);
+
+ return total;
+}
+
+static void bitmap_test_data_prepare(struct bitmap_test_data *tdata,
+ struct bitmap_index *bitmap_git)
+{
+ memset(tdata, 0, sizeof(struct bitmap_test_data));
+
+ tdata->bitmap_git = bitmap_git;
+ tdata->base = bitmap_new();
+ tdata->commits = ewah_to_bitmap(bitmap_git->commits);
+ tdata->trees = ewah_to_bitmap(bitmap_git->trees);
+ tdata->blobs = ewah_to_bitmap(bitmap_git->blobs);
+ tdata->tags = ewah_to_bitmap(bitmap_git->tags);
+
+ if (bitmap_git->base) {
+ tdata->base_tdata = xmalloc(sizeof(struct bitmap_test_data));
+ bitmap_test_data_prepare(tdata->base_tdata, bitmap_git->base);
+ }
+}
+
+static void bitmap_test_data_release(struct bitmap_test_data *tdata)
+{
+ if (!tdata)
+ return;
+
+ bitmap_test_data_release(tdata->base_tdata);
+ free(tdata->base_tdata);
+
+ bitmap_free(tdata->base);
+ bitmap_free(tdata->commits);
+ bitmap_free(tdata->trees);
+ bitmap_free(tdata->blobs);
+ bitmap_free(tdata->tags);
+}
+
void test_bitmap_walk(struct rev_info *revs)
{
struct object *root;
struct bitmap *result = NULL;
size_t result_popcnt;
struct bitmap_test_data tdata;
- struct bitmap_index *bitmap_git;
+ struct bitmap_index *bitmap_git, *found;
struct ewah_bitmap *bm;
if (!(bitmap_git = prepare_bitmap_git(revs->repo)))
@@ -2540,17 +2785,28 @@ void test_bitmap_walk(struct rev_info *revs)
if (revs->pending.nr != 1)
die(_("you must specify exactly one commit to test"));
- fprintf_ln(stderr, "Bitmap v%d test (%d entries%s)",
+ fprintf_ln(stderr, "Bitmap v%d test (%d entries%s, %d total)",
bitmap_git->version,
bitmap_git->entry_count,
- bitmap_git->table_lookup ? "" : " loaded");
+ bitmap_git->table_lookup ? "" : " loaded",
+ bitmap_total_entry_count(bitmap_git));
root = revs->pending.objects[0].item;
- bm = bitmap_for_commit(bitmap_git, (struct commit *)root);
+ bm = find_bitmap_for_commit(bitmap_git, (struct commit *)root, &found);
if (bm) {
fprintf_ln(stderr, "Found bitmap for '%s'. %d bits / %08x checksum",
- oid_to_hex(&root->oid), (int)bm->bit_size, ewah_checksum(bm));
+ oid_to_hex(&root->oid),
+ (int)bm->bit_size, ewah_checksum(bm));
+
+ if (bitmap_is_midx(found))
+ fprintf_ln(stderr, "Located via MIDX '%s'.",
+ hash_to_hex_algop(get_midx_checksum(found->midx),
+ revs->repo->hash_algo));
+ else
+ fprintf_ln(stderr, "Located via pack '%s'.",
+ hash_to_hex_algop(found->pack->hash,
+ revs->repo->hash_algo));
result = ewah_to_bitmap(bm);
}
@@ -2567,16 +2823,10 @@ void test_bitmap_walk(struct rev_info *revs)
if (prepare_revision_walk(revs))
die(_("revision walk setup failed"));
- tdata.bitmap_git = bitmap_git;
- tdata.base = bitmap_new();
- tdata.commits = ewah_to_bitmap(bitmap_git->commits);
- tdata.trees = ewah_to_bitmap(bitmap_git->trees);
- tdata.blobs = ewah_to_bitmap(bitmap_git->blobs);
- tdata.tags = ewah_to_bitmap(bitmap_git->tags);
+ bitmap_test_data_prepare(&tdata, bitmap_git);
tdata.prg = start_progress(revs->repo,
"Verifying bitmap entries",
result_popcnt);
- tdata.seen = 0;
traverse_commit_list(revs, &test_show_commit, &test_show_object, &tdata);
@@ -2588,11 +2838,7 @@ void test_bitmap_walk(struct rev_info *revs)
die(_("mismatch in bitmap results"));
bitmap_free(result);
- bitmap_free(tdata.base);
- bitmap_free(tdata.commits);
- bitmap_free(tdata.trees);
- bitmap_free(tdata.blobs);
- bitmap_free(tdata.tags);
+ bitmap_test_data_release(&tdata);
free_bitmap_index(bitmap_git);
}
@@ -2820,7 +3066,7 @@ uint32_t *create_bitmap_mapping(struct bitmap_index *bitmap_git,
BUG("rebuild_existing_bitmaps: missing required rev-cache "
"extension");
- num_objects = bitmap_num_objects(bitmap_git);
+ num_objects = bitmap_num_objects_total(bitmap_git);
CALLOC_ARRAY(reposition, num_objects);
for (i = 0; i < num_objects; ++i) {
@@ -2856,6 +3102,10 @@ void free_bitmap_index(struct bitmap_index *b)
ewah_pool_free(b->trees);
ewah_pool_free(b->blobs);
ewah_pool_free(b->tags);
+ free(b->commits_all);
+ free(b->trees_all);
+ free(b->blobs_all);
+ free(b->tags_all);
if (b->bitmaps) {
struct stored_bitmap *sb;
kh_foreach_value(b->bitmaps, sb, {
@@ -2883,6 +3133,7 @@ void free_bitmap_index(struct bitmap_index *b)
close_midx_revindex(b->midx);
}
free_pseudo_merge_map(&b->pseudo_merges);
+ free_bitmap_index(b->base);
free(b);
}
@@ -2898,13 +3149,13 @@ static off_t get_disk_usage_for_type(struct bitmap_index *bitmap_git,
{
struct bitmap *result = bitmap_git->result;
off_t total = 0;
- struct ewah_iterator it;
+ struct ewah_or_iterator it;
eword_t filter;
size_t i;
init_type_iterator(&it, bitmap_git, object_type);
for (i = 0; i < result->word_alloc &&
- ewah_iterator_next(&filter, &it); i++) {
+ ewah_or_iterator_next(&filter, &it); i++) {
eword_t word = result->words[i] & filter;
size_t base = (i * BITS_IN_EWORD);
unsigned offset;
@@ -2924,7 +3175,7 @@ static off_t get_disk_usage_for_type(struct bitmap_index *bitmap_git,
off_t offset = nth_midxed_offset(bitmap_git->midx, midx_pos);
uint32_t pack_id = nth_midxed_pack_int_id(bitmap_git->midx, midx_pos);
- struct packed_git *pack = bitmap_git->midx->packs[pack_id];
+ struct packed_git *pack = nth_midxed_pack(bitmap_git->midx, pack_id);
if (offset_to_pack_pos(pack, offset, &pack_pos) < 0) {
struct object_id oid;
@@ -2945,6 +3196,8 @@ static off_t get_disk_usage_for_type(struct bitmap_index *bitmap_git,
}
}
+ ewah_or_iterator_release(&it);
+
return total;
}
@@ -2963,7 +3216,8 @@ static off_t get_disk_usage_for_extended(struct bitmap_index *bitmap_git)
struct object *obj = eindex->objects[i];
if (!bitmap_get(result,
- st_add(bitmap_num_objects(bitmap_git), i)))
+ st_add(bitmap_num_objects_total(bitmap_git),
+ i)))
continue;
if (oid_object_info_extended(bitmap_repo(bitmap_git), &obj->oid,
@@ -3024,7 +3278,8 @@ int bitmap_is_preferred_refname(struct repository *r, const char *refname)
return 0;
}
-static int verify_bitmap_file(const char *name)
+static int verify_bitmap_file(const struct git_hash_algo *algop,
+ const char *name)
{
struct stat st;
unsigned char *data;
@@ -3040,7 +3295,7 @@ static int verify_bitmap_file(const char *name)
data = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
- if (!hashfile_checksum_valid(data, st.st_size))
+ if (!hashfile_checksum_valid(algop, data, st.st_size))
res = error(_("bitmap file '%s' has invalid checksum"),
name);
@@ -3055,14 +3310,14 @@ int verify_bitmap_files(struct repository *r)
for (struct multi_pack_index *m = get_multi_pack_index(r);
m; m = m->next) {
char *midx_bitmap_name = midx_bitmap_filename(m);
- res |= verify_bitmap_file(midx_bitmap_name);
+ res |= verify_bitmap_file(r->hash_algo, midx_bitmap_name);
free(midx_bitmap_name);
}
for (struct packed_git *p = get_all_packs(r);
p; p = p->next) {
char *pack_bitmap_name = pack_bitmap_filename(p);
- res |= verify_bitmap_file(pack_bitmap_name);
+ res |= verify_bitmap_file(r->hash_algo, pack_bitmap_name);
free(pack_bitmap_name);
}
diff --git a/pack-bitmap.h b/pack-bitmap.h
index d7f4b8b8e9..382d39499a 100644
--- a/pack-bitmap.h
+++ b/pack-bitmap.h
@@ -50,7 +50,8 @@ typedef int (*show_reachable_fn)(
int flags,
uint32_t hash,
struct packed_git *found_pack,
- off_t found_offset);
+ off_t found_offset,
+ void *payload);
struct bitmap_index;
@@ -66,6 +67,13 @@ struct bitmapped_pack {
struct bitmap_index *prepare_bitmap_git(struct repository *r);
struct bitmap_index *prepare_midx_bitmap_git(struct multi_pack_index *midx);
+
+/*
+ * Given a bitmap index, determine whether it contains the pack either directly
+ * or via the multi-pack-index.
+ */
+int bitmap_index_contains_pack(struct bitmap_index *bitmap, struct packed_git *pack);
+
void count_bitmap_commit_list(struct bitmap_index *, uint32_t *commits,
uint32_t *trees, uint32_t *blobs, uint32_t *tags);
void traverse_bitmap_commit_list(struct bitmap_index *,
@@ -78,6 +86,18 @@ int test_bitmap_pseudo_merges(struct repository *r);
int test_bitmap_pseudo_merge_commits(struct repository *r, uint32_t n);
int test_bitmap_pseudo_merge_objects(struct repository *r, uint32_t n);
+struct list_objects_filter_options;
+
+/*
+ * Filter bitmapped objects and iterate through all resulting objects,
+ * executing `show_reach` for each of them. Returns `-1` in case the filter is
+ * not supported, `0` otherwise.
+ */
+int for_each_bitmapped_object(struct bitmap_index *bitmap_git,
+ struct list_objects_filter_options *filter,
+ show_reachable_fn show_reach,
+ void *payload);
+
#define GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL \
"GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL"
@@ -104,6 +124,7 @@ int bitmap_has_oid_in_uninteresting(struct bitmap_index *, const struct object_i
off_t get_disk_usage_from_bitmap(struct bitmap_index *, struct rev_info *);
struct bitmap_writer {
+ struct repository *repo;
struct ewah_bitmap *commits;
struct ewah_bitmap *trees;
struct ewah_bitmap *blobs;
@@ -111,6 +132,7 @@ struct bitmap_writer {
kh_oid_map_t *bitmaps;
struct packing_data *to_pack;
+ struct multi_pack_index *midx; /* if appending to a MIDX chain */
struct bitmapped_commit *selected;
unsigned int selected_nr, selected_alloc;
@@ -125,7 +147,8 @@ struct bitmap_writer {
};
void bitmap_writer_init(struct bitmap_writer *writer, struct repository *r,
- struct packing_data *pdata);
+ struct packing_data *pdata,
+ struct multi_pack_index *midx);
void bitmap_writer_show_progress(struct bitmap_writer *writer, int show);
void bitmap_writer_set_checksum(struct bitmap_writer *writer,
const unsigned char *sha1);
diff --git a/pack-check.c b/pack-check.c
index d0aeb5ec41..874897d6cb 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -1,4 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "git-compat-util.h"
@@ -9,7 +8,7 @@
#include "progress.h"
#include "packfile.h"
#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
struct idx_entry {
off_t offset;
@@ -44,7 +43,7 @@ int check_pack_crc(struct packed_git *p, struct pack_window **w_curs,
} while (len);
index_crc = p->index_data;
- index_crc += 2 + 256 + (size_t)p->num_objects * (the_hash_algo->rawsz/4) + nr;
+ index_crc += 2 + 256 + (size_t)p->num_objects * (p->repo->hash_algo->rawsz/4) + nr;
return data_crc != ntohl(*index_crc);
}
@@ -81,11 +80,11 @@ static int verify_packfile(struct repository *r,
} while (offset < pack_sig_ofs);
git_hash_final(hash, &ctx);
pack_sig = use_pack(p, w_curs, pack_sig_ofs, NULL);
- if (!hasheq(hash, pack_sig, the_repository->hash_algo))
+ if (!hasheq(hash, pack_sig, r->hash_algo))
err = error("%s pack checksum mismatch",
p->pack_name);
if (!hasheq(index_base + index_size - r->hash_algo->hexsz, pack_sig,
- the_repository->hash_algo))
+ r->hash_algo))
err = error("%s pack checksum does not match its index",
p->pack_name);
unuse_pack(w_curs);
@@ -131,7 +130,8 @@ static int verify_packfile(struct repository *r,
type = unpack_object_header(p, w_curs, &curpos, &size);
unuse_pack(w_curs);
- if (type == OBJ_BLOB && big_file_threshold <= size) {
+ if (type == OBJ_BLOB &&
+ repo_settings_get_big_file_threshold(r) <= size) {
/*
* Let stream_object_signature() check it with
* the streaming interface; no point slurping
@@ -180,7 +180,7 @@ int verify_pack_index(struct packed_git *p)
return error("packfile %s index not opened", p->pack_name);
/* Verify SHA1 sum of the index file */
- if (!hashfile_checksum_valid(p->index_data, p->index_size))
+ if (!hashfile_checksum_valid(p->repo->hash_algo, p->index_data, p->index_size))
err = error("Packfile index for %s hash mismatch",
p->pack_name);
return err;
diff --git a/pack-mtimes.c b/pack-mtimes.c
index cdf30b8d2b..20900ca88d 100644
--- a/pack-mtimes.c
+++ b/pack-mtimes.c
@@ -1,8 +1,7 @@
#include "git-compat-util.h"
#include "gettext.h"
#include "pack-mtimes.h"
-#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "packfile.h"
#include "strbuf.h"
diff --git a/pack-objects.h b/pack-objects.h
index d73e3843c9..d1c4ae7f9b 100644
--- a/pack-objects.h
+++ b/pack-objects.h
@@ -1,7 +1,7 @@
#ifndef PACK_OBJECTS_H
#define PACK_OBJECTS_H
-#include "object-store-ll.h"
+#include "object-store.h"
#include "thread-utils.h"
#include "pack.h"
diff --git a/pack-revindex.c b/pack-revindex.c
index d3832478d9..ffcde48870 100644
--- a/pack-revindex.c
+++ b/pack-revindex.c
@@ -1,14 +1,12 @@
-#define USE_THE_REPOSITORY_VARIABLE
-
#include "git-compat-util.h"
#include "gettext.h"
#include "pack-revindex.h"
-#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "packfile.h"
#include "strbuf.h"
#include "trace2.h"
#include "parse.h"
+#include "repository.h"
#include "midx.h"
#include "csum-file.h"
@@ -137,7 +135,7 @@ static void create_pack_revindex(struct packed_git *p)
const unsigned num_ent = p->num_objects;
unsigned i;
const char *index = p->index_data;
- const unsigned hashsz = the_hash_algo->rawsz;
+ const unsigned hashsz = p->repo->hash_algo->rawsz;
ALLOC_ARRAY(p->revindex, num_ent + 1);
index += 4 * 256;
@@ -193,7 +191,11 @@ static char *pack_revindex_filename(struct packed_git *p)
}
#define RIDX_HEADER_SIZE (12)
-#define RIDX_MIN_SIZE (RIDX_HEADER_SIZE + (2 * the_hash_algo->rawsz))
+
+static size_t ridx_min_size(const struct git_hash_algo *algo)
+{
+ return RIDX_HEADER_SIZE + (2 * algo->rawsz);
+}
struct revindex_header {
uint32_t signature;
@@ -201,7 +203,8 @@ struct revindex_header {
uint32_t hash_id;
};
-static int load_revindex_from_disk(char *revindex_name,
+static int load_revindex_from_disk(const struct git_hash_algo *algo,
+ char *revindex_name,
uint32_t num_objects,
const uint32_t **data_p, size_t *len_p)
{
@@ -228,12 +231,12 @@ static int load_revindex_from_disk(char *revindex_name,
revindex_size = xsize_t(st.st_size);
- if (revindex_size < RIDX_MIN_SIZE) {
+ if (revindex_size < ridx_min_size(algo)) {
ret = error(_("reverse-index file %s is too small"), revindex_name);
goto cleanup;
}
- if (revindex_size - RIDX_MIN_SIZE != st_mult(sizeof(uint32_t), num_objects)) {
+ if (revindex_size - ridx_min_size(algo) != st_mult(sizeof(uint32_t), num_objects)) {
ret = error(_("reverse-index file %s is corrupt"), revindex_name);
goto cleanup;
}
@@ -279,7 +282,8 @@ int load_pack_revindex_from_disk(struct packed_git *p)
revindex_name = pack_revindex_filename(p);
- ret = load_revindex_from_disk(revindex_name,
+ ret = load_revindex_from_disk(p->repo->hash_algo,
+ revindex_name,
p->num_objects,
&p->revindex_map,
&p->revindex_size);
@@ -322,7 +326,8 @@ int verify_pack_revindex(struct packed_git *p)
if (!p->revindex_map || !p->revindex_data)
return res;
- if (!hashfile_checksum_valid((const unsigned char *)p->revindex_map, p->revindex_size)) {
+ if (!hashfile_checksum_valid(p->repo->hash_algo,
+ (const unsigned char *)p->revindex_map, p->revindex_size)) {
error(_("invalid checksum"));
res = -1;
}
@@ -374,19 +379,26 @@ int load_midx_revindex(struct multi_pack_index *m)
* not want to accidentally call munmap() in the middle of the
* MIDX.
*/
- trace2_data_string("load_midx_revindex", the_repository,
+ trace2_data_string("load_midx_revindex", m->repo,
"source", "midx");
m->revindex_data = (const uint32_t *)m->chunk_revindex;
return 0;
}
- trace2_data_string("load_midx_revindex", the_repository,
+ trace2_data_string("load_midx_revindex", m->repo,
"source", "rev");
- get_midx_filename_ext(m->repo->hash_algo, &revindex_name, m->object_dir,
- get_midx_checksum(m), MIDX_EXT_REV);
+ if (m->has_chain)
+ get_split_midx_filename_ext(m->repo->hash_algo, &revindex_name,
+ m->object_dir, get_midx_checksum(m),
+ MIDX_EXT_REV);
+ else
+ get_midx_filename_ext(m->repo->hash_algo, &revindex_name,
+ m->object_dir, get_midx_checksum(m),
+ MIDX_EXT_REV);
- ret = load_revindex_from_disk(revindex_name.buf,
+ ret = load_revindex_from_disk(m->repo->hash_algo,
+ revindex_name.buf,
m->num_objects,
&m->revindex_map,
&m->revindex_len);
@@ -418,7 +430,7 @@ int offset_to_pack_pos(struct packed_git *p, off_t ofs, uint32_t *pos)
{
unsigned lo, hi;
- if (load_pack_revindex(the_repository, p) < 0)
+ if (load_pack_revindex(p->repo, p) < 0)
return -1;
lo = 0;
@@ -464,18 +476,22 @@ off_t pack_pos_to_offset(struct packed_git *p, uint32_t pos)
if (p->revindex)
return p->revindex[pos].offset;
else if (pos == p->num_objects)
- return p->pack_size - the_hash_algo->rawsz;
+ return p->pack_size - p->repo->hash_algo->rawsz;
else
return nth_packed_object_offset(p, pack_pos_to_index(p, pos));
}
uint32_t pack_pos_to_midx(struct multi_pack_index *m, uint32_t pos)
{
+ while (m && pos < m->num_objects_in_base)
+ m = m->base_midx;
+ if (!m)
+ BUG("NULL multi-pack-index for object position: %"PRIu32, pos);
if (!m->revindex_data)
BUG("pack_pos_to_midx: reverse index not yet loaded");
- if (m->num_objects <= pos)
+ if (m->num_objects + m->num_objects_in_base <= pos)
BUG("pack_pos_to_midx: out-of-bounds object at %"PRIu32, pos);
- return get_be32(m->revindex_data + pos);
+ return get_be32(m->revindex_data + pos - m->num_objects_in_base);
}
struct midx_pack_key {
@@ -491,7 +507,8 @@ static int midx_pack_order_cmp(const void *va, const void *vb)
const struct midx_pack_key *key = va;
struct multi_pack_index *midx = key->midx;
- uint32_t versus = pack_pos_to_midx(midx, (uint32_t*)vb - (const uint32_t *)midx->revindex_data);
+ size_t pos = (uint32_t *)vb - (const uint32_t *)midx->revindex_data;
+ uint32_t versus = pack_pos_to_midx(midx, pos + midx->num_objects_in_base);
uint32_t versus_pack = nth_midxed_pack_int_id(midx, versus);
off_t versus_offset;
@@ -529,9 +546,9 @@ static int midx_key_to_pack_pos(struct multi_pack_index *m,
{
uint32_t *found;
- if (key->pack >= m->num_packs)
+ if (key->pack >= m->num_packs + m->num_packs_in_base)
BUG("MIDX pack lookup out of bounds (%"PRIu32" >= %"PRIu32")",
- key->pack, m->num_packs);
+ key->pack, m->num_packs + m->num_packs_in_base);
/*
* The preferred pack sorts first, so determine its identifier by
* looking at the first object in pseudo-pack order.
@@ -551,7 +568,8 @@ static int midx_key_to_pack_pos(struct multi_pack_index *m,
if (!found)
return -1;
- *pos = found - m->revindex_data;
+ *pos = (found - m->revindex_data) + m->num_objects_in_base;
+
return 0;
}
@@ -559,9 +577,13 @@ int midx_to_pack_pos(struct multi_pack_index *m, uint32_t at, uint32_t *pos)
{
struct midx_pack_key key;
+ while (m && at < m->num_objects_in_base)
+ m = m->base_midx;
+ if (!m)
+ BUG("NULL multi-pack-index for object position: %"PRIu32, at);
if (!m->revindex_data)
BUG("midx_to_pack_pos: reverse index not yet loaded");
- if (m->num_objects <= at)
+ if (m->num_objects + m->num_objects_in_base <= at)
BUG("midx_to_pack_pos: out-of-bounds object at %"PRIu32, at);
key.pack = nth_midxed_pack_int_id(m, at);
diff --git a/pack-write.c b/pack-write.c
index 823e40b42f..6b06315f80 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -1,5 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
-
#include "git-compat-util.h"
#include "environment.h"
#include "gettext.h"
@@ -56,7 +54,7 @@ static int need_large_offset(off_t offset, const struct pack_idx_option *opts)
* The *sha1 contains the pack content SHA1 hash.
* The objects array passed in will be sorted by SHA1 on exit.
*/
-const char *write_idx_file(const struct git_hash_algo *hash_algo,
+const char *write_idx_file(struct repository *repo,
const char *index_name, struct pack_idx_entry **objects,
int nr_objects, const struct pack_idx_option *opts,
const unsigned char *sha1)
@@ -82,7 +80,7 @@ const char *write_idx_file(const struct git_hash_algo *hash_algo,
if (opts->flags & WRITE_IDX_VERIFY) {
assert(index_name);
- f = hashfd_check(index_name);
+ f = hashfd_check(repo->hash_algo, index_name);
} else {
if (!index_name) {
struct strbuf tmp_file = STRBUF_INIT;
@@ -92,7 +90,7 @@ const char *write_idx_file(const struct git_hash_algo *hash_algo,
unlink(index_name);
fd = xopen(index_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
}
- f = hashfd(fd, index_name);
+ f = hashfd(repo->hash_algo, fd, index_name);
}
/* if last object's offset is >= 2^31 we should use index V2 */
@@ -131,7 +129,7 @@ const char *write_idx_file(const struct git_hash_algo *hash_algo,
struct pack_idx_entry *obj = *list++;
if (index_version < 2)
hashwrite_be32(f, obj->offset);
- hashwrite(f, obj->oid.hash, hash_algo->rawsz);
+ hashwrite(f, obj->oid.hash, repo->hash_algo->rawsz);
if ((opts->flags & WRITE_IDX_STRICT) &&
(i && oideq(&list[-2]->oid, &obj->oid)))
die("The same object %s appears twice in the pack",
@@ -173,7 +171,7 @@ const char *write_idx_file(const struct git_hash_algo *hash_algo,
}
}
- hashwrite(f, sha1, hash_algo->rawsz);
+ hashwrite(f, sha1, repo->hash_algo->rawsz);
finalize_hashfile(f, NULL, FSYNC_COMPONENT_PACK_METADATA,
CSUM_HASH_IN_STREAM | CSUM_CLOSE |
((opts->flags & WRITE_IDX_VERIFY) ? 0 : CSUM_FSYNC));
@@ -217,7 +215,7 @@ static void write_rev_trailer(const struct git_hash_algo *hash_algo,
hashwrite(f, hash, hash_algo->rawsz);
}
-char *write_rev_file(const struct git_hash_algo *hash_algo,
+char *write_rev_file(struct repository *repo,
const char *rev_name,
struct pack_idx_entry **objects,
uint32_t nr_objects,
@@ -236,7 +234,7 @@ char *write_rev_file(const struct git_hash_algo *hash_algo,
pack_order[i] = i;
QSORT_S(pack_order, nr_objects, pack_order_cmp, objects);
- ret = write_rev_file_order(hash_algo, rev_name, pack_order, nr_objects,
+ ret = write_rev_file_order(repo, rev_name, pack_order, nr_objects,
hash, flags);
free(pack_order);
@@ -244,7 +242,7 @@ char *write_rev_file(const struct git_hash_algo *hash_algo,
return ret;
}
-char *write_rev_file_order(const struct git_hash_algo *hash_algo,
+char *write_rev_file_order(struct repository *repo,
const char *rev_name,
uint32_t *pack_order,
uint32_t nr_objects,
@@ -268,7 +266,7 @@ char *write_rev_file_order(const struct git_hash_algo *hash_algo,
fd = xopen(rev_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
path = xstrdup(rev_name);
}
- f = hashfd(fd, path);
+ f = hashfd(repo->hash_algo, fd, path);
} else if (flags & WRITE_REV_VERIFY) {
struct stat statbuf;
if (stat(rev_name, &statbuf)) {
@@ -278,18 +276,18 @@ char *write_rev_file_order(const struct git_hash_algo *hash_algo,
} else
die_errno(_("could not stat: %s"), rev_name);
}
- f = hashfd_check(rev_name);
+ f = hashfd_check(repo->hash_algo, rev_name);
path = xstrdup(rev_name);
} else {
return NULL;
}
- write_rev_header(hash_algo, f);
+ write_rev_header(repo->hash_algo, f);
write_rev_index_positions(f, pack_order, nr_objects);
- write_rev_trailer(hash_algo, f, hash);
+ write_rev_trailer(repo->hash_algo, f, hash);
- if (adjust_shared_perm(the_repository, path) < 0)
+ if (adjust_shared_perm(repo, path) < 0)
die(_("failed to make %s readable"), path);
finalize_hashfile(f, NULL, FSYNC_COMPONENT_PACK_METADATA,
@@ -330,7 +328,7 @@ static void write_mtimes_trailer(const struct git_hash_algo *hash_algo,
hashwrite(f, hash, hash_algo->rawsz);
}
-static char *write_mtimes_file(const struct git_hash_algo *hash_algo,
+static char *write_mtimes_file(struct repository *repo,
struct packing_data *to_pack,
struct pack_idx_entry **objects,
uint32_t nr_objects,
@@ -346,13 +344,13 @@ static char *write_mtimes_file(const struct git_hash_algo *hash_algo,
fd = odb_mkstemp(&tmp_file, "pack/tmp_mtimes_XXXXXX");
mtimes_name = strbuf_detach(&tmp_file, NULL);
- f = hashfd(fd, mtimes_name);
+ f = hashfd(repo->hash_algo, fd, mtimes_name);
- write_mtimes_header(hash_algo, f);
+ write_mtimes_header(repo->hash_algo, f);
write_mtimes_objects(f, to_pack, objects, nr_objects);
- write_mtimes_trailer(hash_algo, f, hash);
+ write_mtimes_trailer(repo->hash_algo, f, hash);
- if (adjust_shared_perm(the_repository, mtimes_name) < 0)
+ if (adjust_shared_perm(repo, mtimes_name) < 0)
die(_("failed to make %s readable"), mtimes_name);
finalize_hashfile(f, NULL, FSYNC_COMPONENT_PACK_METADATA,
@@ -527,14 +525,15 @@ int encode_in_pack_object_header(unsigned char *hdr, int hdr_len,
return n;
}
-struct hashfile *create_tmp_packfile(char **pack_tmp_name)
+struct hashfile *create_tmp_packfile(struct repository *repo,
+ char **pack_tmp_name)
{
struct strbuf tmpname = STRBUF_INIT;
int fd;
fd = odb_mkstemp(&tmpname, "pack/tmp_pack_XXXXXX");
*pack_tmp_name = strbuf_detach(&tmpname, NULL);
- return hashfd(fd, *pack_tmp_name);
+ return hashfd(repo->hash_algo, fd, *pack_tmp_name);
}
static void rename_tmp_packfile(struct strbuf *name_prefix, const char *source,
@@ -555,7 +554,7 @@ void rename_tmp_packfile_idx(struct strbuf *name_buffer,
rename_tmp_packfile(name_buffer, *idx_tmp_name, "idx");
}
-void stage_tmp_packfiles(const struct git_hash_algo *hash_algo,
+void stage_tmp_packfiles(struct repository *repo,
struct strbuf *name_buffer,
const char *pack_tmp_name,
struct pack_idx_entry **written_list,
@@ -568,19 +567,19 @@ void stage_tmp_packfiles(const struct git_hash_algo *hash_algo,
char *rev_tmp_name = NULL;
char *mtimes_tmp_name = NULL;
- if (adjust_shared_perm(the_repository, pack_tmp_name))
+ if (adjust_shared_perm(repo, pack_tmp_name))
die_errno("unable to make temporary pack file readable");
- *idx_tmp_name = (char *)write_idx_file(hash_algo, NULL, written_list,
+ *idx_tmp_name = (char *)write_idx_file(repo, NULL, written_list,
nr_written, pack_idx_opts, hash);
- if (adjust_shared_perm(the_repository, *idx_tmp_name))
+ if (adjust_shared_perm(repo, *idx_tmp_name))
die_errno("unable to make temporary index file readable");
- rev_tmp_name = write_rev_file(hash_algo, NULL, written_list, nr_written,
+ rev_tmp_name = write_rev_file(repo, NULL, written_list, nr_written,
hash, pack_idx_opts->flags);
if (pack_idx_opts->flags & WRITE_MTIMES) {
- mtimes_tmp_name = write_mtimes_file(hash_algo, to_pack,
+ mtimes_tmp_name = write_mtimes_file(repo, to_pack,
written_list, nr_written,
hash);
}
diff --git a/pack.h b/pack.h
index 9f1194ac13..5d4393eaff 100644
--- a/pack.h
+++ b/pack.h
@@ -87,7 +87,7 @@ struct progress;
/* Note, the data argument could be NULL if object type is blob */
typedef int (*verify_fn)(const struct object_id *, enum object_type, unsigned long, void*, int*);
-const char *write_idx_file(const struct git_hash_algo *hash_algo,
+const char *write_idx_file(struct repository *repo,
const char *index_name,
struct pack_idx_entry **objects,
int nr_objects,
@@ -106,13 +106,13 @@ struct ref;
void write_promisor_file(const char *promisor_name, struct ref **sought, int nr_sought);
-char *write_rev_file(const struct git_hash_algo *hash_algo,
+char *write_rev_file(struct repository *repo,
const char *rev_name,
struct pack_idx_entry **objects,
uint32_t nr_objects,
const unsigned char *hash,
unsigned flags);
-char *write_rev_file_order(const struct git_hash_algo *hash_algo,
+char *write_rev_file_order(struct repository *repo,
const char *rev_name,
uint32_t *pack_order,
uint32_t nr_objects,
@@ -134,8 +134,9 @@ int read_pack_header(int fd, struct pack_header *);
struct packing_data;
-struct hashfile *create_tmp_packfile(char **pack_tmp_name);
-void stage_tmp_packfiles(const struct git_hash_algo *hash_algo,
+struct hashfile *create_tmp_packfile(struct repository *repo,
+ char **pack_tmp_name);
+void stage_tmp_packfiles(struct repository *repo,
struct strbuf *name_buffer,
const char *pack_tmp_name,
struct pack_idx_entry **written_list,
diff --git a/packfile.c b/packfile.c
index 9d09f8bc72..d91016f1c7 100644
--- a/packfile.c
+++ b/packfile.c
@@ -19,7 +19,7 @@
#include "tree-walk.h"
#include "tree.h"
#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "midx.h"
#include "commit-graph.h"
#include "pack-revindex.h"
diff --git a/parallel-checkout.c b/parallel-checkout.c
index 7cc6b30528..57c2dcaa8f 100644
--- a/parallel-checkout.c
+++ b/parallel-checkout.c
@@ -277,7 +277,7 @@ static int write_pc_item_to_fd(struct parallel_checkout_item *pc_item, int fd,
ssize_t wrote;
/* Sanity check */
- assert(is_eligible_for_parallel_checkout(pc_item->ce, &pc_item->ca));
+ ASSERT(is_eligible_for_parallel_checkout(pc_item->ce, &pc_item->ca));
filter = get_stream_filter_ca(&pc_item->ca, &pc_item->ce->oid);
if (filter) {
diff --git a/parse-options-cb.c b/parse-options-cb.c
index 166d35e0eb..50c8afe412 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -145,7 +145,7 @@ int parse_opt_object_id(const struct option *opt, const char *arg, int unset)
struct object_id *target = opt->value;
if (unset) {
- oidcpy(target, null_oid());
+ oidcpy(target, null_oid(the_hash_algo));
return 0;
}
if (!arg)
diff --git a/path.c b/path.c
index 910756c8b3..4505bb78e8 100644
--- a/path.c
+++ b/path.c
@@ -15,7 +15,7 @@
#include "submodule-config.h"
#include "path.h"
#include "packfile.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "lockfile.h"
#include "exec-cmd.h"
@@ -902,6 +902,115 @@ void safe_create_dir(struct repository *repo, const char *dir, int share)
die(_("Could not make %s writable by group"), dir);
}
+int safe_create_dir_in_gitdir(struct repository *repo, const char *path)
+{
+ if (mkdir(path, 0777)) {
+ int saved_errno = errno;
+ struct stat st;
+ struct strbuf sb = STRBUF_INIT;
+
+ if (errno != EEXIST)
+ return -1;
+ /*
+ * Are we looking at a path in a symlinked worktree
+ * whose original repository does not yet have it?
+ * e.g. .git/rr-cache pointing at its original
+ * repository in which the user hasn't performed any
+ * conflict resolution yet?
+ */
+ if (lstat(path, &st) || !S_ISLNK(st.st_mode) ||
+ strbuf_readlink(&sb, path, st.st_size) ||
+ !is_absolute_path(sb.buf) ||
+ mkdir(sb.buf, 0777)) {
+ strbuf_release(&sb);
+ errno = saved_errno;
+ return -1;
+ }
+ strbuf_release(&sb);
+ }
+ return adjust_shared_perm(repo, path);
+}
+
+static enum scld_error safe_create_leading_directories_1(struct repository *repo,
+ char *path)
+{
+ char *next_component = path + offset_1st_component(path);
+ enum scld_error ret = SCLD_OK;
+
+ while (ret == SCLD_OK && next_component) {
+ struct stat st;
+ char *slash = next_component, slash_character;
+
+ while (*slash && !is_dir_sep(*slash))
+ slash++;
+
+ if (!*slash)
+ break;
+
+ next_component = slash + 1;
+ while (is_dir_sep(*next_component))
+ next_component++;
+ if (!*next_component)
+ break;
+
+ slash_character = *slash;
+ *slash = '\0';
+ if (!stat(path, &st)) {
+ /* path exists */
+ if (!S_ISDIR(st.st_mode)) {
+ errno = ENOTDIR;
+ ret = SCLD_EXISTS;
+ }
+ } else if (mkdir(path, 0777)) {
+ if (errno == EEXIST &&
+ !stat(path, &st) && S_ISDIR(st.st_mode))
+ ; /* somebody created it since we checked */
+ else if (errno == ENOENT)
+ /*
+ * Either mkdir() failed because
+ * somebody just pruned the containing
+ * directory, or stat() failed because
+ * the file that was in our way was
+ * just removed. Either way, inform
+ * the caller that it might be worth
+ * trying again:
+ */
+ ret = SCLD_VANISHED;
+ else
+ ret = SCLD_FAILED;
+ } else if (repo && adjust_shared_perm(repo, path)) {
+ ret = SCLD_PERMS;
+ }
+ *slash = slash_character;
+ }
+ return ret;
+}
+
+enum scld_error safe_create_leading_directories(struct repository *repo,
+ char *path)
+{
+ return safe_create_leading_directories_1(repo, path);
+}
+
+enum scld_error safe_create_leading_directories_no_share(char *path)
+{
+ return safe_create_leading_directories_1(NULL, path);
+}
+
+enum scld_error safe_create_leading_directories_const(struct repository *repo,
+ const char *path)
+{
+ int save_errno;
+ /* path points to cache entries, so xstrdup before messing with it */
+ char *buf = xstrdup(path);
+ enum scld_error result = safe_create_leading_directories(repo, buf);
+
+ save_errno = errno;
+ free(buf);
+ errno = save_errno;
+ return result;
+}
+
static int have_same_root(const char *path1, const char *path2)
{
int is_abs1, is_abs2;
diff --git a/path.h b/path.h
index 65fe968a13..fd1a194b06 100644
--- a/path.h
+++ b/path.h
@@ -221,6 +221,51 @@ char *xdg_cache_home(const char *filename);
*/
void safe_create_dir(struct repository *repo, const char *dir, int share);
+/*
+ * Similar to `safe_create_dir()`, but with two differences:
+ *
+ * - It knows to resolve gitlink files for symlinked worktrees.
+ *
+ * - It always adjusts shared permissions.
+ *
+ * Returns a negative erorr code on error, 0 on success.
+ */
+int safe_create_dir_in_gitdir(struct repository *repo, const char *path);
+
+/*
+ * Create the directory containing the named path, using care to be
+ * somewhat safe against races. Return one of the scld_error values to
+ * indicate success/failure. On error, set errno to describe the
+ * problem.
+ *
+ * SCLD_VANISHED indicates that one of the ancestor directories of the
+ * path existed at one point during the function call and then
+ * suddenly vanished, probably because another process pruned the
+ * directory while we were working. To be robust against this kind of
+ * race, callers might want to try invoking the function again when it
+ * returns SCLD_VANISHED.
+ *
+ * safe_create_leading_directories() temporarily changes path while it
+ * is working but restores it before returning.
+ * safe_create_leading_directories_const() doesn't modify path, even
+ * temporarily. Both these variants adjust the permissions of the
+ * created directories to honor core.sharedRepository, so they are best
+ * suited for files inside the git dir. For working tree files, use
+ * safe_create_leading_directories_no_share() instead, as it ignores
+ * the core.sharedRepository setting.
+ */
+enum scld_error {
+ SCLD_OK = 0,
+ SCLD_FAILED = -1,
+ SCLD_PERMS = -2,
+ SCLD_EXISTS = -3,
+ SCLD_VANISHED = -4
+};
+enum scld_error safe_create_leading_directories(struct repository *repo, char *path);
+enum scld_error safe_create_leading_directories_const(struct repository *repo,
+ const char *path);
+enum scld_error safe_create_leading_directories_no_share(char *path);
+
# ifdef USE_THE_REPOSITORY_VARIABLE
# include "strbuf.h"
# include "repository.h"
diff --git a/pathspec.c b/pathspec.c
index 89663645e1..2b4e434bc0 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -1,5 +1,4 @@
#define USE_THE_REPOSITORY_VARIABLE
-#define DISABLE_SIGN_COMPARE_WARNINGS
#include "git-compat-util.h"
#include "abspath.h"
@@ -35,7 +34,7 @@ void add_pathspec_matches_against_index(const struct pathspec *pathspec,
char *seen,
enum ps_skip_worktree_action sw_action)
{
- int num_unmatched = 0, i;
+ int num_unmatched = 0;
/*
* Since we are walking the index as if we were walking the directory,
@@ -43,12 +42,12 @@ void add_pathspec_matches_against_index(const struct pathspec *pathspec,
* mistakenly think that the user gave a pathspec that did not match
* anything.
*/
- for (i = 0; i < pathspec->nr; i++)
+ for (int i = 0; i < pathspec->nr; i++)
if (!seen[i])
num_unmatched++;
if (!num_unmatched)
return;
- for (i = 0; i < istate->cache_nr; i++) {
+ for (unsigned int i = 0; i < istate->cache_nr; i++) {
const struct cache_entry *ce = istate->cache[i];
if (sw_action == PS_IGNORE_SKIP_WORKTREE &&
(ce_skip_worktree(ce) || !path_in_sparse_checkout(ce->name, istate)))
@@ -78,7 +77,7 @@ char *find_pathspecs_matching_skip_worktree(const struct pathspec *pathspec)
{
struct index_state *istate = the_repository->index;
char *seen = xcalloc(pathspec->nr, 1);
- int i;
+ unsigned int i;
for (i = 0; i < istate->cache_nr; i++) {
struct cache_entry *ce = istate->cache[i];
@@ -130,7 +129,7 @@ static void prefix_magic(struct strbuf *sb, int prefixlen,
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++) {
+ for (unsigned 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] != '(')
@@ -341,7 +340,7 @@ static const char *parse_long_magic(unsigned *magic, int *prefix_len,
for (pos = elem + 2; *pos && *pos != ')'; pos = nextat) {
size_t len = strcspn_escaped(pos, ",)");
- int i;
+ unsigned int i;
if (pos[len] == ',')
nextat = pos + len + 1; /* handle ',' */
@@ -354,7 +353,7 @@ static const char *parse_long_magic(unsigned *magic, int *prefix_len,
if (starts_with(pos, "prefix:")) {
char *endptr;
*prefix_len = strtol(pos + 7, &endptr, 10);
- if (endptr - pos != len)
+ if ((size_t)(endptr - pos) != len)
die(_("invalid parameter for pathspec magic 'prefix'"));
continue;
}
@@ -400,7 +399,7 @@ static const char *parse_short_magic(unsigned *magic, const char *elem)
for (pos = elem + 1; *pos && *pos != ':'; pos++) {
char ch = *pos;
- int i;
+ unsigned int i;
/* Special case alias for '!' */
if (ch == '^') {
@@ -564,7 +563,7 @@ static int pathspec_item_cmp(const void *a_, const void *b_)
void pathspec_magic_names(unsigned magic, struct strbuf *out)
{
- int i;
+ unsigned int i;
for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
const struct pathspec_magic *m = pathspec_magic + i;
if (!(magic & m->bit))
@@ -803,8 +802,8 @@ int match_pathspec_attrs(struct index_state *istate,
int pathspec_needs_expanded_index(struct index_state *istate,
const struct pathspec *pathspec)
{
- unsigned int i, pos;
- int res = 0;
+ unsigned int pos;
+ int i, res = 0;
char *skip_worktree_seen = NULL;
/*
@@ -845,7 +844,8 @@ int pathspec_needs_expanded_index(struct index_state *istate,
* - not-in-cone/bar*: may need expanded index
* - **.c: may need expanded index
*/
- if (strspn(item.original + item.nowildcard_len, "*") == item.len - item.nowildcard_len &&
+ if (strspn(item.original + item.nowildcard_len, "*") ==
+ (unsigned int)(item.len - item.nowildcard_len) &&
path_in_cone_mode_sparse_checkout(item.original, istate))
continue;
@@ -860,8 +860,10 @@ int pathspec_needs_expanded_index(struct index_state *istate,
* directory name and the sparse directory is the first
* component of the pathspec, need to expand the index.
*/
- if (item.nowildcard_len > ce_namelen(ce) &&
- !strncmp(item.original, ce->name, ce_namelen(ce))) {
+ if ((unsigned int)item.nowildcard_len >
+ ce_namelen(ce) &&
+ !strncmp(item.original, ce->name,
+ ce_namelen(ce))) {
res = 1;
break;
}
diff --git a/promisor-remote.c b/promisor-remote.c
index 6a0a61382f..9d058586df 100644
--- a/promisor-remote.c
+++ b/promisor-remote.c
@@ -3,7 +3,7 @@
#include "git-compat-util.h"
#include "gettext.h"
#include "hex.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "promisor-remote.h"
#include "config.h"
#include "trace2.h"
@@ -323,13 +323,15 @@ static void promisor_info_vecs(struct repository *repo,
promisor_remote_init(repo);
for (r = repo->promisor_remote_config->promisors; r; r = r->next) {
- char *url;
+ const char *url;
char *url_key = xstrfmt("remote.%s.url", r->name);
- strvec_push(names, r->name);
- strvec_push(urls, git_config_get_string(url_key, &url) ? NULL : url);
+ /* Only add remotes with a non empty URL */
+ if (!git_config_get_string_tmp(url_key, &url) && *url) {
+ strvec_push(names, r->name);
+ strvec_push(urls, url);
+ }
- free(url);
free(url_key);
}
}
@@ -356,10 +358,8 @@ char *promisor_remote_info(struct repository *repo)
strbuf_addch(&sb, ';');
strbuf_addstr(&sb, "name=");
strbuf_addstr_urlencode(&sb, names.v[i], allow_unsanitized);
- if (urls.v[i]) {
- strbuf_addstr(&sb, ",url=");
- strbuf_addstr_urlencode(&sb, urls.v[i], allow_unsanitized);
- }
+ strbuf_addstr(&sb, ",url=");
+ strbuf_addstr_urlencode(&sb, urls.v[i], allow_unsanitized);
}
strvec_clear(&names);
@@ -370,13 +370,13 @@ char *promisor_remote_info(struct repository *repo)
/*
* Find first index of 'nicks' where there is 'nick'. 'nick' is
- * compared case insensitively to the strings in 'nicks'. If not found
+ * compared case sensitively to the strings in 'nicks'. If not found
* 'nicks->nr' is returned.
*/
static size_t remote_nick_find(struct strvec *nicks, const char *nick)
{
for (size_t i = 0; i < nicks->nr; i++)
- if (!strcasecmp(nicks->v[i], nick))
+ if (!strcmp(nicks->v[i], nick))
return i;
return nicks->nr;
}
@@ -409,10 +409,15 @@ static int should_accept_remote(enum accept_promisor accept,
if (accept != ACCEPT_KNOWN_URL)
BUG("Unhandled 'enum accept_promisor' value '%d'", accept);
+ if (!remote_url || !*remote_url) {
+ warning(_("no or empty URL advertised for remote '%s'"), remote_name);
+ return 0;
+ }
+
if (!strcmp(urls->v[i], remote_url))
return 1;
- warning(_("known remote named '%s' but with url '%s' instead of '%s'"),
+ warning(_("known remote named '%s' but with URL '%s' instead of '%s'"),
remote_name, urls->v[i], remote_url);
return 0;
diff --git a/protocol-caps.c b/protocol-caps.c
index 855f279c2f..9b8db37a21 100644
--- a/protocol-caps.c
+++ b/protocol-caps.c
@@ -6,7 +6,7 @@
#include "hash.h"
#include "hex.h"
#include "object.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "repository.h"
#include "string-list.h"
#include "strbuf.h"
diff --git a/prune-packed.c b/prune-packed.c
index 7dad2fc0c1..c1d95a519d 100644
--- a/prune-packed.c
+++ b/prune-packed.c
@@ -2,7 +2,7 @@
#include "git-compat-util.h"
#include "gettext.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "packfile.h"
#include "progress.h"
#include "prune-packed.h"
diff --git a/pseudo-merge.h b/pseudo-merge.h
index c9fbe9d312..cf0e62ecd1 100644
--- a/pseudo-merge.h
+++ b/pseudo-merge.h
@@ -210,7 +210,7 @@ int cascade_pseudo_merges(const struct pseudo_merge_map *pm,
/*
* Returns a pseudo-merge which contains the exact set of commits
- * listed in the "parents" bitamp, or NULL if none could be found.
+ * listed in the "parents" bitmap, or NULL if none could be found.
*/
struct pseudo_merge *pseudo_merge_for_parents(const struct pseudo_merge_map *pm,
struct bitmap *parents);
diff --git a/range-diff.c b/range-diff.c
index 9501c358a8..8a2dcbee32 100644
--- a/range-diff.c
+++ b/range-diff.c
@@ -467,7 +467,7 @@ static struct diff_filespec *get_filespec(const char *name, const char *p)
{
struct diff_filespec *spec = alloc_filespec(name);
- fill_filespec(spec, null_oid(), 0, 0100644);
+ fill_filespec(spec, null_oid(the_hash_algo), 0, 0100644);
spec->data = (char *)p;
spec->size = strlen(p);
spec->should_munmap = 0;
diff --git a/reachable.c b/reachable.c
index 9ee04c89ec..e5f56f4018 100644
--- a/reachable.c
+++ b/reachable.c
@@ -14,7 +14,7 @@
#include "list-objects.h"
#include "packfile.h"
#include "worktree.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "pack-bitmap.h"
#include "pack-mtimes.h"
#include "config.h"
@@ -45,7 +45,7 @@ static void add_one_file(const char *path, struct rev_info *revs)
}
strbuf_trim(&buf);
if (!get_oid_hex(buf.buf, &oid)) {
- object = parse_object_or_die(&oid, buf.buf);
+ object = parse_object_or_die(the_repository, &oid, buf.buf);
add_pending_object(revs, object, "");
}
strbuf_release(&buf);
@@ -94,7 +94,7 @@ static int add_one_ref(const char *path, const char *referent UNUSED, const stru
return 0;
}
- object = parse_object_or_die(oid, path);
+ object = parse_object_or_die(the_repository, oid, path);
add_pending_object(revs, object, "");
return 0;
@@ -218,7 +218,7 @@ static void add_recent_object(const struct object_id *oid,
switch (type) {
case OBJ_TAG:
case OBJ_COMMIT:
- obj = parse_object_or_die(oid, NULL);
+ obj = parse_object_or_die(the_repository, oid, NULL);
break;
case OBJ_TREE:
obj = (struct object *)lookup_tree(the_repository, oid);
@@ -341,7 +341,8 @@ static int mark_object_seen(const struct object_id *oid,
int exclude UNUSED,
uint32_t name_hash UNUSED,
struct packed_git *found_pack UNUSED,
- off_t found_offset UNUSED)
+ off_t found_offset UNUSED,
+ void *payload UNUSED)
{
struct object *obj = lookup_object_by_type(the_repository, oid, type);
if (!obj)
diff --git a/read-cache.c b/read-cache.c
index e678c13e8f..73f83a7e7a 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -20,7 +20,7 @@
#include "refs.h"
#include "dir.h"
#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "oid-array.h"
#include "tree.h"
#include "commit.h"
@@ -706,11 +706,11 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
int intent_only = flags & ADD_CACHE_INTENT;
int add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|
(intent_only ? ADD_CACHE_NEW_ONLY : 0));
- unsigned hash_flags = pretend ? 0 : HASH_WRITE_OBJECT;
+ unsigned hash_flags = pretend ? 0 : INDEX_WRITE_OBJECT;
struct object_id oid;
if (flags & ADD_CACHE_RENORMALIZE)
- hash_flags |= HASH_RENORMALIZE;
+ hash_flags |= INDEX_RENORMALIZE;
if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode))
return error(_("%s: can only add regular files, symbolic links or git-directories"), path);
@@ -1735,7 +1735,7 @@ static int verify_hdr(const struct cache_header *hdr, unsigned long size)
end = (unsigned char *)hdr + size;
start = end - the_hash_algo->rawsz;
oidread(&oid, start, the_repository->hash_algo);
- if (oideq(&oid, null_oid()))
+ if (oideq(&oid, null_oid(the_hash_algo)))
return 0;
the_hash_algo->init_fn(&c);
@@ -2686,8 +2686,8 @@ static int ce_write_entry(struct hashfile *f, struct cache_entry *ce,
int common, to_remove, prefix_size;
unsigned char to_remove_vi[16];
for (common = 0;
- (ce->name[common] &&
- common < previous_name->len &&
+ (common < previous_name->len &&
+ ce->name[common] &&
ce->name[common] == previous_name->buf[common]);
common++)
; /* still matching */
@@ -2848,7 +2848,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
struct strbuf sb = STRBUF_INIT;
int nr, nr_threads, ret;
- f = hashfd(tempfile->fd, tempfile->filename.buf);
+ f = hashfd(the_repository->hash_algo, tempfile->fd, tempfile->filename.buf);
prepare_repo_settings(r);
f->skip_hash = r->settings.index_skip_hash;
diff --git a/ref-filter.c b/ref-filter.c
index 6da8d4c03b..7a274633cf 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -12,7 +12,7 @@
#include "refs.h"
#include "wildmatch.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "oid-array.h"
#include "repo-settings.h"
#include "repository.h"
diff --git a/reflog.c b/reflog.c
index 1b5f031f6d..12f7a02e34 100644
--- a/reflog.c
+++ b/reflog.c
@@ -2,13 +2,120 @@
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "git-compat-util.h"
+#include "config.h"
#include "gettext.h"
-#include "object-store-ll.h"
+#include "parse-options.h"
+#include "object-store.h"
#include "reflog.h"
#include "refs.h"
#include "revision.h"
#include "tree.h"
#include "tree-walk.h"
+#include "wildmatch.h"
+
+static struct reflog_expire_entry_option *find_cfg_ent(struct reflog_expire_options *opts,
+ const char *pattern, size_t len)
+{
+ struct reflog_expire_entry_option *ent;
+
+ if (!opts->entries_tail)
+ opts->entries_tail = &opts->entries;
+
+ for (ent = opts->entries; ent; ent = ent->next)
+ if (!xstrncmpz(ent->pattern, pattern, len))
+ return ent;
+
+ FLEX_ALLOC_MEM(ent, pattern, pattern, len);
+ *opts->entries_tail = ent;
+ opts->entries_tail = &(ent->next);
+ return ent;
+}
+
+int reflog_expire_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb)
+{
+ struct reflog_expire_options *opts = cb;
+ const char *pattern, *key;
+ size_t pattern_len;
+ timestamp_t expire;
+ int slot;
+ struct reflog_expire_entry_option *ent;
+
+ if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
+ return git_default_config(var, value, ctx, cb);
+
+ if (!strcmp(key, "reflogexpire")) {
+ slot = REFLOG_EXPIRE_TOTAL;
+ if (git_config_expiry_date(&expire, var, value))
+ return -1;
+ } else if (!strcmp(key, "reflogexpireunreachable")) {
+ slot = REFLOG_EXPIRE_UNREACH;
+ if (git_config_expiry_date(&expire, var, value))
+ return -1;
+ } else
+ return git_default_config(var, value, ctx, cb);
+
+ if (!pattern) {
+ switch (slot) {
+ case REFLOG_EXPIRE_TOTAL:
+ opts->default_expire_total = expire;
+ break;
+ case REFLOG_EXPIRE_UNREACH:
+ opts->default_expire_unreachable = expire;
+ break;
+ }
+ return 0;
+ }
+
+ ent = find_cfg_ent(opts, pattern, pattern_len);
+ if (!ent)
+ return -1;
+ switch (slot) {
+ case REFLOG_EXPIRE_TOTAL:
+ ent->expire_total = expire;
+ break;
+ case REFLOG_EXPIRE_UNREACH:
+ ent->expire_unreachable = expire;
+ break;
+ }
+ return 0;
+}
+
+void reflog_expire_options_set_refname(struct reflog_expire_options *cb,
+ const char *ref)
+{
+ struct reflog_expire_entry_option *ent;
+
+ if (cb->explicit_expiry == (REFLOG_EXPIRE_TOTAL|REFLOG_EXPIRE_UNREACH))
+ return; /* both given explicitly -- nothing to tweak */
+
+ for (ent = cb->entries; ent; ent = ent->next) {
+ if (!wildmatch(ent->pattern, ref, 0)) {
+ if (!(cb->explicit_expiry & REFLOG_EXPIRE_TOTAL))
+ cb->expire_total = ent->expire_total;
+ if (!(cb->explicit_expiry & REFLOG_EXPIRE_UNREACH))
+ cb->expire_unreachable = ent->expire_unreachable;
+ return;
+ }
+ }
+
+ /*
+ * If unconfigured, make stash never expire
+ */
+ if (!strcmp(ref, "refs/stash")) {
+ if (!(cb->explicit_expiry & REFLOG_EXPIRE_TOTAL))
+ cb->expire_total = 0;
+ if (!(cb->explicit_expiry & REFLOG_EXPIRE_UNREACH))
+ cb->expire_unreachable = 0;
+ return;
+ }
+
+ /* Nothing matched -- use the default value */
+ if (!(cb->explicit_expiry & REFLOG_EXPIRE_TOTAL))
+ cb->expire_total = cb->default_expire_total;
+ if (!(cb->explicit_expiry & REFLOG_EXPIRE_UNREACH))
+ cb->expire_unreachable = cb->default_expire_unreachable;
+}
/* Remember to update object flag allocation in object.h */
#define INCOMPLETE (1u<<10)
@@ -252,15 +359,15 @@ int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
struct expire_reflog_policy_cb *cb = cb_data;
struct commit *old_commit, *new_commit;
- if (timestamp < cb->cmd.expire_total)
+ if (timestamp < cb->opts.expire_total)
return 1;
old_commit = new_commit = NULL;
- if (cb->cmd.stalefix &&
+ if (cb->opts.stalefix &&
(!keep_entry(&old_commit, ooid) || !keep_entry(&new_commit, noid)))
return 1;
- if (timestamp < cb->cmd.expire_unreachable) {
+ if (timestamp < cb->opts.expire_unreachable) {
switch (cb->unreachable_expire_kind) {
case UE_ALWAYS:
return 1;
@@ -272,7 +379,7 @@ int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
}
}
- if (cb->cmd.recno && --(cb->cmd.recno) == 0)
+ if (cb->opts.recno && --(cb->opts.recno) == 0)
return 1;
return 0;
@@ -331,7 +438,7 @@ void reflog_expiry_prepare(const char *refname,
struct commit_list *elem;
struct commit *commit = NULL;
- if (!cb->cmd.expire_unreachable || is_head(refname)) {
+ if (!cb->opts.expire_unreachable || is_head(refname)) {
cb->unreachable_expire_kind = UE_HEAD;
} else {
commit = lookup_commit_reference_gently(the_repository,
@@ -341,7 +448,7 @@ void reflog_expiry_prepare(const char *refname,
cb->unreachable_expire_kind = commit ? UE_NORMAL : UE_ALWAYS;
}
- if (cb->cmd.expire_unreachable <= cb->cmd.expire_total)
+ if (cb->opts.expire_unreachable <= cb->opts.expire_total)
cb->unreachable_expire_kind = UE_ALWAYS;
switch (cb->unreachable_expire_kind) {
@@ -358,7 +465,7 @@ void reflog_expiry_prepare(const char *refname,
/* For reflog_expiry_cleanup() below */
cb->tip_commit = commit;
}
- cb->mark_limit = cb->cmd.expire_total;
+ cb->mark_limit = cb->opts.expire_total;
mark_reachable(cb);
}
@@ -390,7 +497,7 @@ int count_reflog_ent(struct object_id *ooid UNUSED,
timestamp_t timestamp, int tz UNUSED,
const char *message UNUSED, void *cb_data)
{
- struct cmd_reflog_expire_cb *cb = cb_data;
+ struct reflog_expire_options *cb = cb_data;
if (!cb->expire_total || timestamp < cb->expire_total)
cb->recno++;
return 0;
@@ -398,7 +505,7 @@ int count_reflog_ent(struct object_id *ooid UNUSED,
int reflog_delete(const char *rev, enum expire_reflog_flags flags, int verbose)
{
- struct cmd_reflog_expire_cb cmd = { 0 };
+ struct reflog_expire_options opts = { 0 };
int status = 0;
reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
const char *spec = strstr(rev, "@{");
@@ -421,17 +528,17 @@ int reflog_delete(const char *rev, enum expire_reflog_flags flags, int verbose)
recno = strtoul(spec + 2, &ep, 10);
if (*ep == '}') {
- cmd.recno = -recno;
+ opts.recno = -recno;
refs_for_each_reflog_ent(get_main_ref_store(the_repository),
- ref, count_reflog_ent, &cmd);
+ ref, count_reflog_ent, &opts);
} else {
- cmd.expire_total = approxidate(spec + 2);
+ opts.expire_total = approxidate(spec + 2);
refs_for_each_reflog_ent(get_main_ref_store(the_repository),
- ref, count_reflog_ent, &cmd);
- cmd.expire_total = 0;
+ ref, count_reflog_ent, &opts);
+ opts.expire_total = 0;
}
- cb.cmd = cmd;
+ cb.opts = opts;
status |= refs_reflog_expire(get_main_ref_store(the_repository), ref,
flags,
reflog_expiry_prepare,
diff --git a/reflog.h b/reflog.h
index d2906fb9f8..63bb56280f 100644
--- a/reflog.h
+++ b/reflog.h
@@ -2,13 +2,44 @@
#define REFLOG_H
#include "refs.h"
-struct cmd_reflog_expire_cb {
+#define REFLOG_EXPIRE_TOTAL (1 << 0)
+#define REFLOG_EXPIRE_UNREACH (1 << 1)
+
+struct reflog_expire_entry_option {
+ struct reflog_expire_entry_option *next;
+ timestamp_t expire_total;
+ timestamp_t expire_unreachable;
+ char pattern[FLEX_ARRAY];
+};
+
+struct reflog_expire_options {
+ struct reflog_expire_entry_option *entries, **entries_tail;
int stalefix;
int explicit_expiry;
+ timestamp_t default_expire_total;
timestamp_t expire_total;
+ timestamp_t default_expire_unreachable;
timestamp_t expire_unreachable;
int recno;
};
+#define REFLOG_EXPIRE_OPTIONS_INIT(now) { \
+ .default_expire_total = now - 30 * 24 * 3600, \
+ .default_expire_unreachable = now - 90 * 24 * 3600, \
+}
+
+/*
+ * Parse the reflog expire configuration. This should be used with
+ * `repo_config()`.
+ */
+int reflog_expire_config(const char *var, const char *value,
+ const struct config_context *ctx, void *cb);
+
+/*
+ * Adapt the options so that they apply to the given refname. This applies any
+ * per-reference reflog expiry configuration that may exist to the options.
+ */
+void reflog_expire_options_set_refname(struct reflog_expire_options *cb,
+ const char *refname);
struct expire_reflog_policy_cb {
enum {
@@ -18,7 +49,7 @@ struct expire_reflog_policy_cb {
} unreachable_expire_kind;
struct commit_list *mark_list;
unsigned long mark_limit;
- struct cmd_reflog_expire_cb cmd;
+ struct reflog_expire_options opts;
struct commit *tip_commit;
struct commit_list *tips;
unsigned int dry_run:1;
diff --git a/refs.c b/refs.c
index f0fe77bd7c..6559db3789 100644
--- a/refs.c
+++ b/refs.c
@@ -19,7 +19,7 @@
#include "run-command.h"
#include "hook.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "object.h"
#include "path.h"
#include "submodule.h"
@@ -664,7 +664,8 @@ char *repo_default_branch_name(struct repository *r, int quiet)
if (!ret) {
ret = xstrdup("master");
if (!quiet)
- advise(_(default_branch_name_advice), ret);
+ advise_if_enabled(ADVICE_DEFAULT_BRANCH_NAME,
+ _(default_branch_name_advice), ret);
}
full_ref = xstrfmt("refs/heads/%s", ret);
@@ -1175,6 +1176,11 @@ struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
CALLOC_ARRAY(tr, 1);
tr->ref_store = refs;
tr->flags = flags;
+ string_list_init_dup(&tr->refnames);
+
+ if (flags & REF_TRANSACTION_ALLOW_FAILURE)
+ CALLOC_ARRAY(tr->rejections, 1);
+
return tr;
}
@@ -1205,10 +1211,45 @@ void ref_transaction_free(struct ref_transaction *transaction)
free((char *)transaction->updates[i]->old_target);
free(transaction->updates[i]);
}
+
+ if (transaction->rejections)
+ free(transaction->rejections->update_indices);
+ free(transaction->rejections);
+
+ string_list_clear(&transaction->refnames, 0);
free(transaction->updates);
free(transaction);
}
+int ref_transaction_maybe_set_rejected(struct ref_transaction *transaction,
+ size_t update_idx,
+ enum ref_transaction_error err)
+{
+ if (update_idx >= transaction->nr)
+ BUG("trying to set rejection on invalid update index");
+
+ if (!(transaction->flags & REF_TRANSACTION_ALLOW_FAILURE))
+ return 0;
+
+ if (!transaction->rejections)
+ BUG("transaction not inititalized with failure support");
+
+ /*
+ * Don't accept generic errors, since these errors are not user
+ * input related.
+ */
+ if (err == REF_TRANSACTION_ERROR_GENERIC)
+ return 0;
+
+ transaction->updates[update_idx]->rejection_err = err;
+ ALLOC_GROW(transaction->rejections->update_indices,
+ transaction->rejections->nr + 1,
+ transaction->rejections->alloc);
+ transaction->rejections->update_indices[transaction->rejections->nr++] = update_idx;
+
+ return 1;
+}
+
struct ref_update *ref_transaction_add_update(
struct ref_transaction *transaction,
const char *refname, unsigned int flags,
@@ -1218,6 +1259,7 @@ struct ref_update *ref_transaction_add_update(
const char *committer_info,
const char *msg)
{
+ struct string_list_item *item;
struct ref_update *update;
if (transaction->state != REF_TRANSACTION_OPEN)
@@ -1233,6 +1275,7 @@ struct ref_update *ref_transaction_add_update(
transaction->updates[transaction->nr++] = update;
update->flags = flags;
+ update->rejection_err = 0;
update->new_target = xstrdup_or_null(new_target);
update->old_target = xstrdup_or_null(old_target);
@@ -1245,6 +1288,16 @@ struct ref_update *ref_transaction_add_update(
update->msg = normalize_reflog_message(msg);
}
+ /*
+ * This list is generally used by the backends to avoid duplicates.
+ * But we do support multiple log updates for a given refname within
+ * a single transaction.
+ */
+ if (!(update->flags & REF_LOG_ONLY)) {
+ item = string_list_append(&transaction->refnames, refname);
+ item->util = update;
+ }
+
return update;
}
@@ -1377,7 +1430,7 @@ int ref_transaction_create(struct ref_transaction *transaction,
return 1;
}
return ref_transaction_update(transaction, refname, new_oid,
- null_oid(), new_target, NULL, flags,
+ null_oid(the_hash_algo), new_target, NULL, flags,
msg, err);
}
@@ -1396,7 +1449,7 @@ int ref_transaction_delete(struct ref_transaction *transaction,
if (old_target && !(flags & REF_NO_DEREF))
BUG("delete cannot operate on symrefs with deref mode");
return ref_transaction_update(transaction, refname,
- null_oid(), old_oid,
+ null_oid(the_hash_algo), old_oid,
NULL, old_target, flags,
msg, err);
}
@@ -2180,7 +2233,7 @@ struct ref_store *repo_get_submodule_ref_store(struct repository *repo,
subrepo = xmalloc(sizeof(*subrepo));
if (repo_submodule_init(subrepo, repo, submodule,
- null_oid())) {
+ null_oid(the_hash_algo))) {
free(subrepo);
goto done;
}
@@ -2278,7 +2331,7 @@ int refs_update_symref_extended(struct ref_store *refs, const char *ref,
REF_NO_DEREF, logmsg, &err))
goto error_return;
prepret = ref_transaction_prepare(transaction, &err);
- if (prepret && prepret != TRANSACTION_CREATE_EXISTS)
+ if (prepret && prepret != REF_TRANSACTION_ERROR_CREATE_EXISTS)
goto error_return;
} else {
if (ref_transaction_update(transaction, ref, NULL, NULL,
@@ -2296,7 +2349,7 @@ int refs_update_symref_extended(struct ref_store *refs, const char *ref,
}
}
- if (prepret == TRANSACTION_CREATE_EXISTS)
+ if (prepret == REF_TRANSACTION_ERROR_CREATE_EXISTS)
goto cleanup;
if (ref_transaction_commit(transaction, &err))
@@ -2310,8 +2363,13 @@ cleanup:
return ret;
}
-int ref_update_reject_duplicates(struct string_list *refnames,
- struct strbuf *err)
+/*
+ * Write an error to `err` and return a nonzero value iff the same
+ * refname appears multiple times in `refnames`. `refnames` must be
+ * sorted on entry to this function.
+ */
+static int ref_update_reject_duplicates(struct string_list *refnames,
+ struct strbuf *err)
{
size_t i, n = refnames->nr;
@@ -2365,14 +2423,14 @@ static int run_transaction_hook(struct ref_transaction *transaction,
strbuf_reset(&buf);
if (!(update->flags & REF_HAVE_OLD))
- strbuf_addf(&buf, "%s ", oid_to_hex(null_oid()));
+ strbuf_addf(&buf, "%s ", oid_to_hex(null_oid(the_hash_algo)));
else if (update->old_target)
strbuf_addf(&buf, "ref:%s ", update->old_target);
else
strbuf_addf(&buf, "%s ", oid_to_hex(&update->old_oid));
if (!(update->flags & REF_HAVE_NEW))
- strbuf_addf(&buf, "%s ", oid_to_hex(null_oid()));
+ strbuf_addf(&buf, "%s ", oid_to_hex(null_oid(the_hash_algo)));
else if (update->new_target)
strbuf_addf(&buf, "ref:%s ", update->new_target);
else
@@ -2425,6 +2483,10 @@ int ref_transaction_prepare(struct ref_transaction *transaction,
return -1;
}
+ string_list_sort(&transaction->refnames);
+ if (ref_update_reject_duplicates(&transaction->refnames, err))
+ return REF_TRANSACTION_ERROR_GENERIC;
+
ret = refs->be->transaction_prepare(refs, transaction, err);
if (ret)
return ret;
@@ -2495,19 +2557,21 @@ int ref_transaction_commit(struct ref_transaction *transaction,
return ret;
}
-int refs_verify_refnames_available(struct ref_store *refs,
- const struct string_list *refnames,
- const struct string_list *extras,
- const struct string_list *skip,
- unsigned int initial_transaction,
- struct strbuf *err)
+enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs,
+ const struct string_list *refnames,
+ const struct string_list *extras,
+ const struct string_list *skip,
+ struct ref_transaction *transaction,
+ unsigned int initial_transaction,
+ struct strbuf *err)
{
struct strbuf dirname = STRBUF_INIT;
struct strbuf referent = STRBUF_INIT;
struct string_list_item *item;
struct ref_iterator *iter = NULL;
+ struct strset conflicting_dirnames;
struct strset dirnames;
- int ret = -1;
+ int ret = REF_TRANSACTION_ERROR_NAME_CONFLICT;
/*
* For the sake of comments in this function, suppose that
@@ -2516,9 +2580,11 @@ int refs_verify_refnames_available(struct ref_store *refs,
assert(err);
+ strset_init(&conflicting_dirnames);
strset_init(&dirnames);
for_each_string_list_item(item, refnames) {
+ const size_t *update_idx = (size_t *)item->util;
const char *refname = item->string;
const char *extra_refname;
struct object_id oid;
@@ -2549,21 +2615,37 @@ int refs_verify_refnames_available(struct ref_store *refs,
/*
* If we've already seen the directory we don't need to
- * process it again. Skip it to avoid checking checking
- * common prefixes like "refs/heads/" repeatedly.
+ * process it again. Skip it to avoid checking common
+ * prefixes like "refs/heads/" repeatedly.
*/
if (!strset_add(&dirnames, dirname.buf))
continue;
if (!initial_transaction &&
- !refs_read_raw_ref(refs, dirname.buf, &oid, &referent,
- &type, &ignore_errno)) {
+ (strset_contains(&conflicting_dirnames, dirname.buf) ||
+ !refs_read_raw_ref(refs, dirname.buf, &oid, &referent,
+ &type, &ignore_errno))) {
+ if (transaction && ref_transaction_maybe_set_rejected(
+ transaction, *update_idx,
+ REF_TRANSACTION_ERROR_NAME_CONFLICT)) {
+ strset_remove(&dirnames, dirname.buf);
+ strset_add(&conflicting_dirnames, dirname.buf);
+ continue;
+ }
+
strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
dirname.buf, refname);
goto cleanup;
}
if (extras && string_list_has_string(extras, dirname.buf)) {
+ if (transaction && ref_transaction_maybe_set_rejected(
+ transaction, *update_idx,
+ REF_TRANSACTION_ERROR_NAME_CONFLICT)) {
+ strset_remove(&dirnames, dirname.buf);
+ continue;
+ }
+
strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
refname, dirname.buf);
goto cleanup;
@@ -2596,6 +2678,11 @@ int refs_verify_refnames_available(struct ref_store *refs,
string_list_has_string(skip, iter->refname))
continue;
+ if (transaction && ref_transaction_maybe_set_rejected(
+ transaction, *update_idx,
+ REF_TRANSACTION_ERROR_NAME_CONFLICT))
+ continue;
+
strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
iter->refname, refname);
goto cleanup;
@@ -2607,6 +2694,11 @@ int refs_verify_refnames_available(struct ref_store *refs,
extra_refname = find_descendant_ref(dirname.buf, extras, skip);
if (extra_refname) {
+ if (transaction && ref_transaction_maybe_set_rejected(
+ transaction, *update_idx,
+ REF_TRANSACTION_ERROR_NAME_CONFLICT))
+ continue;
+
strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
refname, extra_refname);
goto cleanup;
@@ -2618,17 +2710,19 @@ int refs_verify_refnames_available(struct ref_store *refs,
cleanup:
strbuf_release(&referent);
strbuf_release(&dirname);
+ strset_clear(&conflicting_dirnames);
strset_clear(&dirnames);
ref_iterator_free(iter);
return ret;
}
-int refs_verify_refname_available(struct ref_store *refs,
- const char *refname,
- const struct string_list *extras,
- const struct string_list *skip,
- unsigned int initial_transaction,
- struct strbuf *err)
+enum ref_transaction_error refs_verify_refname_available(
+ struct ref_store *refs,
+ const char *refname,
+ const struct string_list *extras,
+ const struct string_list *skip,
+ unsigned int initial_transaction,
+ struct strbuf *err)
{
struct string_list_item item = { .string = (char *) refname };
struct string_list refnames = {
@@ -2637,7 +2731,7 @@ int refs_verify_refname_available(struct ref_store *refs,
};
return refs_verify_refnames_available(refs, &refnames, extras, skip,
- initial_transaction, err);
+ NULL, initial_transaction, err);
}
struct do_for_each_reflog_help {
@@ -2725,6 +2819,28 @@ void ref_transaction_for_each_queued_update(struct ref_transaction *transaction,
}
}
+void ref_transaction_for_each_rejected_update(struct ref_transaction *transaction,
+ ref_transaction_for_each_rejected_update_fn cb,
+ void *cb_data)
+{
+ if (!transaction->rejections)
+ return;
+
+ for (size_t i = 0; i < transaction->rejections->nr; i++) {
+ size_t update_index = transaction->rejections->update_indices[i];
+ struct ref_update *update = transaction->updates[update_index];
+
+ if (!update->rejection_err)
+ continue;
+
+ cb(update->refname,
+ (update->flags & REF_HAVE_OLD) ? &update->old_oid : NULL,
+ (update->flags & REF_HAVE_NEW) ? &update->new_oid : NULL,
+ update->old_target, update->new_target,
+ update->rejection_err, cb_data);
+ }
+}
+
int refs_delete_refs(struct ref_store *refs, const char *logmsg,
struct string_list *refnames, unsigned int flags)
{
@@ -2816,8 +2932,9 @@ int ref_update_has_null_new_value(struct ref_update *update)
return !update->new_target && is_null_oid(&update->new_oid);
}
-int ref_update_check_old_target(const char *referent, struct ref_update *update,
- struct strbuf *err)
+enum ref_transaction_error ref_update_check_old_target(const char *referent,
+ struct ref_update *update,
+ struct strbuf *err)
{
if (!update->old_target)
BUG("called without old_target set");
@@ -2825,17 +2942,18 @@ int ref_update_check_old_target(const char *referent, struct ref_update *update,
if (!strcmp(referent, update->old_target))
return 0;
- if (!strcmp(referent, ""))
+ if (!strcmp(referent, "")) {
strbuf_addf(err, "verifying symref target: '%s': "
"reference is missing but expected %s",
ref_update_original_update_refname(update),
update->old_target);
- else
- strbuf_addf(err, "verifying symref target: '%s': "
- "is at %s but expected %s",
+ return REF_TRANSACTION_ERROR_NONEXISTENT_REF;
+ }
+
+ strbuf_addf(err, "verifying symref target: '%s': is at %s but expected %s",
ref_update_original_update_refname(update),
referent, update->old_target);
- return -1;
+ return REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE;
}
struct migration_data {
@@ -2857,7 +2975,7 @@ static int migrate_one_ref(const char *refname, const char *referent UNUSED, con
if (ret < 0)
goto done;
- ret = ref_transaction_update(data->transaction, refname, NULL, null_oid(),
+ ret = ref_transaction_update(data->transaction, refname, NULL, null_oid(the_hash_algo),
symref_target.buf, NULL,
REF_SKIP_CREATE_REFLOG | REF_NO_DEREF, NULL, data->errbuf);
if (ret < 0)
diff --git a/refs.h b/refs.h
index 240e2d8537..46a6008e07 100644
--- a/refs.h
+++ b/refs.h
@@ -16,6 +16,23 @@ struct worktree;
enum ref_storage_format ref_storage_format_by_name(const char *name);
const char *ref_storage_format_to_name(enum ref_storage_format ref_storage_format);
+enum ref_transaction_error {
+ /* Default error code */
+ REF_TRANSACTION_ERROR_GENERIC = -1,
+ /* Ref name conflict like A vs A/B */
+ REF_TRANSACTION_ERROR_NAME_CONFLICT = -2,
+ /* Ref to be created already exists */
+ REF_TRANSACTION_ERROR_CREATE_EXISTS = -3,
+ /* ref expected but doesn't exist */
+ REF_TRANSACTION_ERROR_NONEXISTENT_REF = -4,
+ /* Provided old_oid or old_target of reference doesn't match actual */
+ REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE = -5,
+ /* Provided new_oid or new_target is invalid */
+ REF_TRANSACTION_ERROR_INVALID_NEW_VALUE = -6,
+ /* Expected ref to be symref, but is a regular ref */
+ REF_TRANSACTION_ERROR_EXPECTED_SYMREF = -7,
+};
+
/*
* Resolve a reference, recursively following symbolic references.
*
@@ -117,24 +134,12 @@ int refs_read_symbolic_ref(struct ref_store *ref_store, const char *refname,
*
* extras and skip must be sorted.
*/
-int refs_verify_refname_available(struct ref_store *refs,
- const char *refname,
- const struct string_list *extras,
- const struct string_list *skip,
- unsigned int initial_transaction,
- struct strbuf *err);
-
-/*
- * Same as `refs_verify_refname_available()`, but checking for a list of
- * refnames instead of only a single item. This is more efficient in the case
- * where one needs to check multiple refnames.
- */
-int refs_verify_refnames_available(struct ref_store *refs,
- const struct string_list *refnames,
- const struct string_list *extras,
- const struct string_list *skip,
- unsigned int initial_transaction,
- struct strbuf *err);
+enum ref_transaction_error refs_verify_refname_available(struct ref_store *refs,
+ const char *refname,
+ const struct string_list *extras,
+ const struct string_list *skip,
+ unsigned int initial_transaction,
+ struct strbuf *err);
int refs_ref_exists(struct ref_store *refs, const char *refname);
@@ -650,6 +655,13 @@ enum ref_transaction_flag {
* either be absent or null_oid.
*/
REF_TRANSACTION_FLAG_INITIAL = (1 << 0),
+
+ /*
+ * The transaction mechanism by default fails all updates if any conflict
+ * is detected. This flag allows transactions to partially apply updates
+ * while rejecting updates which do not match the expected state.
+ */
+ REF_TRANSACTION_ALLOW_FAILURE = (1 << 1),
};
/*
@@ -830,13 +842,6 @@ int ref_transaction_verify(struct ref_transaction *transaction,
unsigned int flags,
struct strbuf *err);
-/* Naming conflict (for example, the ref names A and A/B conflict). */
-#define TRANSACTION_NAME_CONFLICT -1
-/* When only creation was requested, but the ref already exists. */
-#define TRANSACTION_CREATE_EXISTS -2
-/* All other errors. */
-#define TRANSACTION_GENERIC_ERROR -3
-
/*
* Perform the preparatory stages of committing `transaction`. Acquire
* any needed locks, check preconditions, etc.; basically, do as much
@@ -888,6 +893,21 @@ void ref_transaction_for_each_queued_update(struct ref_transaction *transaction,
void *cb_data);
/*
+ * Execute the given callback function for each of the reference updates which
+ * have been rejected in the given transaction.
+ */
+typedef void ref_transaction_for_each_rejected_update_fn(const char *refname,
+ const struct object_id *old_oid,
+ const struct object_id *new_oid,
+ const char *old_target,
+ const char *new_target,
+ enum ref_transaction_error err,
+ void *cb_data);
+void ref_transaction_for_each_rejected_update(struct ref_transaction *transaction,
+ ref_transaction_for_each_rejected_update_fn cb,
+ void *cb_data);
+
+/*
* Free `*transaction` and all associated data.
*/
void ref_transaction_free(struct ref_transaction *transaction);
diff --git a/refs/debug.c b/refs/debug.c
index 5390fa9c18..485e3079d7 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -227,7 +227,7 @@ static int debug_read_raw_ref(struct ref_store *ref_store, const char *refname,
struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
int res = 0;
- oidcpy(oid, null_oid());
+ oidcpy(oid, null_oid(ref_store->repo->hash_algo));
res = drefs->refs->be->read_raw_ref(drefs->refs, refname, oid, referent,
type, failure_errno);
diff --git a/refs/files-backend.c b/refs/files-backend.c
index ff54a4bb7e..4d1f65a57a 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -663,7 +663,7 @@ static void unlock_ref(struct ref_lock *lock)
* broken, lock the reference anyway but clear old_oid.
*
* Return 0 on success. On failure, write an error message to err and
- * return TRANSACTION_NAME_CONFLICT or TRANSACTION_GENERIC_ERROR.
+ * return REF_TRANSACTION_ERROR_NAME_CONFLICT or REF_TRANSACTION_ERROR_GENERIC.
*
* Implementation note: This function is basically
*
@@ -676,19 +676,22 @@ static void unlock_ref(struct ref_lock *lock)
* avoided, namely if we were successfully able to read the ref
* - Generate informative error messages in the case of failure
*/
-static int lock_raw_ref(struct files_ref_store *refs,
- const char *refname, int mustexist,
- struct string_list *refnames_to_check,
- const struct string_list *extras,
- struct ref_lock **lock_p,
- struct strbuf *referent,
- unsigned int *type,
- struct strbuf *err)
-{
+static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs,
+ struct ref_update *update,
+ size_t update_idx,
+ int mustexist,
+ struct string_list *refnames_to_check,
+ const struct string_list *extras,
+ struct ref_lock **lock_p,
+ struct strbuf *referent,
+ struct strbuf *err)
+{
+ enum ref_transaction_error ret = REF_TRANSACTION_ERROR_GENERIC;
+ const char *refname = update->refname;
+ unsigned int *type = &update->type;
struct ref_lock *lock;
struct strbuf ref_file = STRBUF_INIT;
int attempts_remaining = 3;
- int ret = TRANSACTION_GENERIC_ERROR;
int failure_errno;
assert(err);
@@ -705,7 +708,7 @@ static int lock_raw_ref(struct files_ref_store *refs,
files_ref_path(refs, &ref_file, refname);
retry:
- switch (safe_create_leading_directories(ref_file.buf)) {
+ switch (safe_create_leading_directories(the_repository, ref_file.buf)) {
case SCLD_OK:
break; /* success */
case SCLD_EXISTS:
@@ -728,13 +731,14 @@ retry:
strbuf_reset(err);
strbuf_addf(err, "unable to resolve reference '%s'",
refname);
+ ret = REF_TRANSACTION_ERROR_NONEXISTENT_REF;
} else {
/*
* The error message set by
* refs_verify_refname_available() is
* OK.
*/
- ret = TRANSACTION_NAME_CONFLICT;
+ ret = REF_TRANSACTION_ERROR_NAME_CONFLICT;
}
} else {
/*
@@ -783,11 +787,14 @@ retry:
if (files_read_raw_ref(&refs->base, refname, &lock->old_oid, referent,
type, &failure_errno)) {
+ struct string_list_item *item;
+
if (failure_errno == ENOENT) {
if (mustexist) {
/* Garden variety missing reference. */
strbuf_addf(err, "unable to resolve reference '%s'",
refname);
+ ret = REF_TRANSACTION_ERROR_NONEXISTENT_REF;
goto error_return;
} else {
/*
@@ -820,6 +827,7 @@ retry:
/* Garden variety missing reference. */
strbuf_addf(err, "unable to resolve reference '%s'",
refname);
+ ret = REF_TRANSACTION_ERROR_NONEXISTENT_REF;
goto error_return;
} else if (remove_dir_recursively(&ref_file,
REMOVE_DIR_EMPTY_ONLY)) {
@@ -830,7 +838,7 @@ retry:
* The error message set by
* verify_refname_available() is OK.
*/
- ret = TRANSACTION_NAME_CONFLICT;
+ ret = REF_TRANSACTION_ERROR_NAME_CONFLICT;
goto error_return;
} else {
/*
@@ -860,7 +868,9 @@ retry:
* make sure there is no existing packed ref that conflicts
* with refname. This check is deferred so that we can batch it.
*/
- string_list_append(refnames_to_check, refname);
+ item = string_list_append(refnames_to_check, refname);
+ item->util = xmalloc(sizeof(update_idx));
+ memcpy(item->util, &update_idx, sizeof(update_idx));
}
ret = 0;
@@ -1109,7 +1119,7 @@ retry_fn:
strbuf_addstr(&path_copy, path);
do {
- scld_result = safe_create_leading_directories(path_copy.buf);
+ scld_result = safe_create_leading_directories(the_repository, path_copy.buf);
if (scld_result == SCLD_OK)
goto retry_fn;
} while (scld_result == SCLD_VANISHED && create_directories_remaining-- > 0);
@@ -1265,7 +1275,7 @@ static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)
ref_transaction_add_update(
transaction, r->name,
REF_NO_DEREF | REF_HAVE_NEW | REF_HAVE_OLD | REF_IS_PRUNING,
- null_oid(), &r->oid, NULL, NULL, NULL, NULL);
+ null_oid(the_hash_algo), &r->oid, NULL, NULL, NULL, NULL);
if (ref_transaction_commit(transaction, &err))
goto cleanup;
@@ -1517,10 +1527,11 @@ static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)
return ret;
}
-static int write_ref_to_lockfile(struct files_ref_store *refs,
- struct ref_lock *lock,
- const struct object_id *oid,
- int skip_oid_verification, struct strbuf *err);
+static enum ref_transaction_error write_ref_to_lockfile(struct files_ref_store *refs,
+ struct ref_lock *lock,
+ const struct object_id *oid,
+ int skip_oid_verification,
+ struct strbuf *err);
static int commit_ref_update(struct files_ref_store *refs,
struct ref_lock *lock,
const struct object_id *oid, const char *logmsg,
@@ -1926,10 +1937,11 @@ static int files_log_ref_write(struct files_ref_store *refs,
* Write oid into the open lockfile, then close the lockfile. On
* errors, rollback the lockfile, fill in *err and return -1.
*/
-static int write_ref_to_lockfile(struct files_ref_store *refs,
- struct ref_lock *lock,
- const struct object_id *oid,
- int skip_oid_verification, struct strbuf *err)
+static enum ref_transaction_error write_ref_to_lockfile(struct files_ref_store *refs,
+ struct ref_lock *lock,
+ const struct object_id *oid,
+ int skip_oid_verification,
+ struct strbuf *err)
{
static char term = '\n';
struct object *o;
@@ -1943,7 +1955,7 @@ static int write_ref_to_lockfile(struct files_ref_store *refs,
"trying to write ref '%s' with nonexistent object %s",
lock->ref_name, oid_to_hex(oid));
unlock_ref(lock);
- return -1;
+ return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
}
if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
strbuf_addf(
@@ -1951,7 +1963,7 @@ static int write_ref_to_lockfile(struct files_ref_store *refs,
"trying to write non-commit object %s to branch '%s'",
oid_to_hex(oid), lock->ref_name);
unlock_ref(lock);
- return -1;
+ return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
}
}
fd = get_lock_file_fd(&lock->lk);
@@ -1962,7 +1974,7 @@ static int write_ref_to_lockfile(struct files_ref_store *refs,
strbuf_addf(err,
"couldn't write '%s'", get_lock_file_path(&lock->lk));
unlock_ref(lock);
- return -1;
+ return REF_TRANSACTION_ERROR_GENERIC;
}
return 0;
}
@@ -2376,13 +2388,11 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st
* If update is a direct update of head_ref (the reference pointed to
* by HEAD), then add an extra REF_LOG_ONLY update for HEAD.
*/
-static int split_head_update(struct ref_update *update,
- struct ref_transaction *transaction,
- const char *head_ref,
- struct string_list *affected_refnames,
- struct strbuf *err)
+static enum ref_transaction_error split_head_update(struct ref_update *update,
+ struct ref_transaction *transaction,
+ const char *head_ref,
+ struct strbuf *err)
{
- struct string_list_item *item;
struct ref_update *new_update;
if ((update->flags & REF_LOG_ONLY) ||
@@ -2399,13 +2409,13 @@ static int split_head_update(struct ref_update *update,
* transaction. This check is O(lg N) in the transaction
* size, but it happens at most once per transaction.
*/
- if (string_list_has_string(affected_refnames, "HEAD")) {
+ if (string_list_has_string(&transaction->refnames, "HEAD")) {
/* An entry already existed */
strbuf_addf(err,
"multiple updates for 'HEAD' (including one "
"via its referent '%s') are not allowed",
update->refname);
- return TRANSACTION_NAME_CONFLICT;
+ return REF_TRANSACTION_ERROR_NAME_CONFLICT;
}
new_update = ref_transaction_add_update(
@@ -2421,8 +2431,6 @@ static int split_head_update(struct ref_update *update,
*/
if (strcmp(new_update->refname, "HEAD"))
BUG("%s unexpectedly not 'HEAD'", new_update->refname);
- item = string_list_insert(affected_refnames, new_update->refname);
- item->util = new_update;
return 0;
}
@@ -2435,13 +2443,11 @@ static int split_head_update(struct ref_update *update,
* Note that the new update will itself be subject to splitting when
* the iteration gets to it.
*/
-static int split_symref_update(struct ref_update *update,
- const char *referent,
- struct ref_transaction *transaction,
- struct string_list *affected_refnames,
- struct strbuf *err)
+static enum ref_transaction_error split_symref_update(struct ref_update *update,
+ const char *referent,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
{
- struct string_list_item *item;
struct ref_update *new_update;
unsigned int new_flags;
@@ -2451,13 +2457,13 @@ static int split_symref_update(struct ref_update *update,
* size, but it happens at most once per symref in a
* transaction.
*/
- if (string_list_has_string(affected_refnames, referent)) {
+ if (string_list_has_string(&transaction->refnames, referent)) {
/* An entry already exists */
strbuf_addf(err,
"multiple updates for '%s' (including one "
"via symref '%s') are not allowed",
referent, update->refname);
- return TRANSACTION_NAME_CONFLICT;
+ return REF_TRANSACTION_ERROR_NAME_CONFLICT;
}
new_flags = update->flags;
@@ -2489,19 +2495,6 @@ static int split_symref_update(struct ref_update *update,
update->flags |= REF_LOG_ONLY | REF_NO_DEREF;
update->flags &= ~REF_HAVE_OLD;
- /*
- * Add the referent. This insertion is O(N) in the transaction
- * size, but it happens at most once per symref in a
- * transaction. Make sure to add new_update->refname, which will
- * be valid as long as affected_refnames is in use, and NOT
- * referent, which might soon be freed by our caller.
- */
- item = string_list_insert(affected_refnames, new_update->refname);
- if (item->util)
- BUG("%s unexpectedly found in affected_refnames",
- new_update->refname);
- item->util = new_update;
-
return 0;
}
@@ -2511,11 +2504,10 @@ static int split_symref_update(struct ref_update *update,
* everything is OK, return 0; otherwise, write an error message to
* err and return -1.
*/
-static int check_old_oid(struct ref_update *update, struct object_id *oid,
- struct strbuf *err)
+static enum ref_transaction_error check_old_oid(struct ref_update *update,
+ struct object_id *oid,
+ struct strbuf *err)
{
- int ret = TRANSACTION_GENERIC_ERROR;
-
if (!(update->flags & REF_HAVE_OLD) ||
oideq(oid, &update->old_oid))
return 0;
@@ -2524,21 +2516,20 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid,
strbuf_addf(err, "cannot lock ref '%s': "
"reference already exists",
ref_update_original_update_refname(update));
- ret = TRANSACTION_CREATE_EXISTS;
- }
- else if (is_null_oid(oid))
+ return REF_TRANSACTION_ERROR_CREATE_EXISTS;
+ } else if (is_null_oid(oid)) {
strbuf_addf(err, "cannot lock ref '%s': "
"reference is missing but expected %s",
ref_update_original_update_refname(update),
oid_to_hex(&update->old_oid));
- else
- strbuf_addf(err, "cannot lock ref '%s': "
- "is at %s but expected %s",
- ref_update_original_update_refname(update),
- oid_to_hex(oid),
- oid_to_hex(&update->old_oid));
+ return REF_TRANSACTION_ERROR_NONEXISTENT_REF;
+ }
- return ret;
+ strbuf_addf(err, "cannot lock ref '%s': is at %s but expected %s",
+ ref_update_original_update_refname(update), oid_to_hex(oid),
+ oid_to_hex(&update->old_oid));
+
+ return REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE;
}
struct files_transaction_backend_data {
@@ -2560,18 +2551,18 @@ struct files_transaction_backend_data {
* - If it is an update of head_ref, add a corresponding REF_LOG_ONLY
* update of HEAD.
*/
-static int lock_ref_for_update(struct files_ref_store *refs,
- struct ref_update *update,
- struct ref_transaction *transaction,
- const char *head_ref,
- struct string_list *refnames_to_check,
- struct string_list *affected_refnames,
- struct strbuf *err)
+static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *refs,
+ struct ref_update *update,
+ size_t update_idx,
+ struct ref_transaction *transaction,
+ const char *head_ref,
+ struct string_list *refnames_to_check,
+ struct strbuf *err)
{
struct strbuf referent = STRBUF_INIT;
int mustexist = ref_update_expects_existing_old_ref(update);
struct files_transaction_backend_data *backend_data;
- int ret = 0;
+ enum ref_transaction_error ret = 0;
struct ref_lock *lock;
files_assert_main_repository(refs, "lock_ref_for_update");
@@ -2582,8 +2573,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
update->flags |= REF_DELETING;
if (head_ref) {
- ret = split_head_update(update, transaction, head_ref,
- affected_refnames, err);
+ ret = split_head_update(update, transaction, head_ref, err);
if (ret)
goto out;
}
@@ -2592,10 +2582,9 @@ static int lock_ref_for_update(struct files_ref_store *refs,
if (lock) {
lock->count++;
} else {
- ret = lock_raw_ref(refs, update->refname, mustexist,
- refnames_to_check, affected_refnames,
- &lock, &referent,
- &update->type, err);
+ ret = lock_raw_ref(refs, update, update_idx, mustexist,
+ refnames_to_check, &transaction->refnames,
+ &lock, &referent, err);
if (ret) {
char *reason;
@@ -2625,22 +2614,17 @@ static int lock_ref_for_update(struct files_ref_store *refs,
strbuf_addf(err, "cannot lock ref '%s': "
"error reading reference",
ref_update_original_update_refname(update));
- ret = TRANSACTION_GENERIC_ERROR;
+ ret = REF_TRANSACTION_ERROR_GENERIC;
goto out;
}
}
- if (update->old_target) {
- if (ref_update_check_old_target(referent.buf, update, err)) {
- ret = TRANSACTION_GENERIC_ERROR;
- goto out;
- }
- } else {
+ if (update->old_target)
+ ret = ref_update_check_old_target(referent.buf, update, err);
+ else
ret = check_old_oid(update, &lock->old_oid, err);
- if (ret) {
- goto out;
- }
- }
+ if (ret)
+ goto out;
} else {
/*
* Create a new update for the reference this
@@ -2649,9 +2633,8 @@ static int lock_ref_for_update(struct files_ref_store *refs,
* of processing the split-off update, so we
* don't have to do it here.
*/
- ret = split_symref_update(update,
- referent.buf, transaction,
- affected_refnames, err);
+ ret = split_symref_update(update, referent.buf,
+ transaction, err);
if (ret)
goto out;
}
@@ -2668,7 +2651,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
"but is a regular ref"),
ref_update_original_update_refname(update),
update->old_target);
- ret = TRANSACTION_GENERIC_ERROR;
+ ret = REF_TRANSACTION_ERROR_EXPECTED_SYMREF;
goto out;
} else {
ret = check_old_oid(update, &lock->old_oid, err);
@@ -2692,14 +2675,14 @@ static int lock_ref_for_update(struct files_ref_store *refs,
if (update->new_target && !(update->flags & REF_LOG_ONLY)) {
if (create_symref_lock(lock, update->new_target, err)) {
- ret = TRANSACTION_GENERIC_ERROR;
+ ret = REF_TRANSACTION_ERROR_GENERIC;
goto out;
}
if (close_ref_gently(lock)) {
strbuf_addf(err, "couldn't close '%s.lock'",
update->refname);
- ret = TRANSACTION_GENERIC_ERROR;
+ ret = REF_TRANSACTION_ERROR_GENERIC;
goto out;
}
@@ -2717,25 +2700,27 @@ static int lock_ref_for_update(struct files_ref_store *refs,
* The reference already has the desired
* value, so we don't need to write it.
*/
- } else if (write_ref_to_lockfile(
- refs, lock, &update->new_oid,
- update->flags & REF_SKIP_OID_VERIFICATION,
- err)) {
- char *write_err = strbuf_detach(err, NULL);
-
- /*
- * The lock was freed upon failure of
- * write_ref_to_lockfile():
- */
- update->backend_data = NULL;
- strbuf_addf(err,
- "cannot update ref '%s': %s",
- update->refname, write_err);
- free(write_err);
- ret = TRANSACTION_GENERIC_ERROR;
- goto out;
} else {
- update->flags |= REF_NEEDS_COMMIT;
+ ret = write_ref_to_lockfile(
+ refs, lock, &update->new_oid,
+ update->flags & REF_SKIP_OID_VERIFICATION,
+ err);
+ if (ret) {
+ char *write_err = strbuf_detach(err, NULL);
+
+ /*
+ * The lock was freed upon failure of
+ * write_ref_to_lockfile():
+ */
+ update->backend_data = NULL;
+ strbuf_addf(err,
+ "cannot update ref '%s': %s",
+ update->refname, write_err);
+ free(write_err);
+ goto out;
+ } else {
+ update->flags |= REF_NEEDS_COMMIT;
+ }
}
}
if (!(update->flags & REF_NEEDS_COMMIT)) {
@@ -2747,7 +2732,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
if (close_ref_gently(lock)) {
strbuf_addf(err, "couldn't close '%s.lock'",
update->refname);
- ret = TRANSACTION_GENERIC_ERROR;
+ ret = REF_TRANSACTION_ERROR_GENERIC;
goto out;
}
}
@@ -2806,7 +2791,6 @@ static int files_transaction_prepare(struct ref_store *ref_store,
"ref_transaction_prepare");
size_t i;
int ret = 0;
- struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
struct string_list refnames_to_check = STRING_LIST_INIT_NODUP;
char *head_ref = NULL;
int head_type;
@@ -2825,36 +2809,14 @@ static int files_transaction_prepare(struct ref_store *ref_store,
transaction->backend_data = backend_data;
/*
- * Fail if a refname appears more than once in the
- * transaction. (If we end up splitting up any updates using
- * split_symref_update() or split_head_update(), those
- * functions will check that the new updates don't have the
- * same refname as any existing ones.) Also fail if any of the
- * updates use REF_IS_PRUNING without REF_NO_DEREF.
+ * Fail if any of the updates use REF_IS_PRUNING without REF_NO_DEREF.
*/
for (i = 0; i < transaction->nr; i++) {
struct ref_update *update = transaction->updates[i];
- struct string_list_item *item;
if ((update->flags & REF_IS_PRUNING) &&
!(update->flags & REF_NO_DEREF))
BUG("REF_IS_PRUNING set without REF_NO_DEREF");
-
- if (update->flags & REF_LOG_ONLY)
- continue;
-
- item = string_list_append(&affected_refnames, update->refname);
- /*
- * We store a pointer to update in item->util, but at
- * the moment we never use the value of this field
- * except to check whether it is non-NULL.
- */
- item->util = update;
- }
- string_list_sort(&affected_refnames);
- if (ref_update_reject_duplicates(&affected_refnames, err)) {
- ret = TRANSACTION_GENERIC_ERROR;
- goto cleanup;
}
/*
@@ -2894,11 +2856,18 @@ static int files_transaction_prepare(struct ref_store *ref_store,
for (i = 0; i < transaction->nr; i++) {
struct ref_update *update = transaction->updates[i];
- ret = lock_ref_for_update(refs, update, transaction,
+ ret = lock_ref_for_update(refs, update, i, transaction,
head_ref, &refnames_to_check,
- &affected_refnames, err);
- if (ret)
+ err);
+ if (ret) {
+ if (ref_transaction_maybe_set_rejected(transaction, i, ret)) {
+ strbuf_reset(err);
+ ret = 0;
+
+ continue;
+ }
goto cleanup;
+ }
if (update->flags & REF_DELETING &&
!(update->flags & REF_LOG_ONLY) &&
@@ -2912,7 +2881,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
refs->packed_ref_store,
transaction->flags, err);
if (!packed_transaction) {
- ret = TRANSACTION_GENERIC_ERROR;
+ ret = REF_TRANSACTION_ERROR_GENERIC;
goto cleanup;
}
@@ -2943,14 +2912,15 @@ static int files_transaction_prepare(struct ref_store *ref_store,
* So instead, we accept the race for now.
*/
if (refs_verify_refnames_available(refs->packed_ref_store, &refnames_to_check,
- &affected_refnames, NULL, 0, err)) {
- ret = TRANSACTION_NAME_CONFLICT;
+ &transaction->refnames, NULL, transaction,
+ 0, err)) {
+ ret = REF_TRANSACTION_ERROR_NAME_CONFLICT;
goto cleanup;
}
if (packed_transaction) {
if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
- ret = TRANSACTION_GENERIC_ERROR;
+ ret = REF_TRANSACTION_ERROR_GENERIC;
goto cleanup;
}
backend_data->packed_refs_locked = 1;
@@ -2981,7 +2951,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
*/
backend_data->packed_transaction = NULL;
if (ref_transaction_abort(packed_transaction, err)) {
- ret = TRANSACTION_GENERIC_ERROR;
+ ret = REF_TRANSACTION_ERROR_GENERIC;
goto cleanup;
}
}
@@ -2989,8 +2959,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
cleanup:
free(head_ref);
- string_list_clear(&affected_refnames, 0);
- string_list_clear(&refnames_to_check, 0);
+ string_list_clear(&refnames_to_check, 1);
if (ret)
files_transaction_cleanup(refs, transaction);
@@ -3064,17 +3033,6 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
if (transaction->state != REF_TRANSACTION_PREPARED)
BUG("commit called for transaction that is not prepared");
- /* Fail if a refname appears more than once in the transaction: */
- for (i = 0; i < transaction->nr; i++)
- if (!(transaction->updates[i]->flags & REF_LOG_ONLY))
- string_list_append(&affected_refnames,
- transaction->updates[i]->refname);
- string_list_sort(&affected_refnames);
- if (ref_update_reject_duplicates(&affected_refnames, err)) {
- ret = TRANSACTION_GENERIC_ERROR;
- goto cleanup;
- }
-
/*
* It's really undefined to call this function in an active
* repository or when there are existing references: we are
@@ -3088,13 +3046,13 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
* that we are creating already exists.
*/
if (refs_for_each_rawref(&refs->base, ref_present,
- &affected_refnames))
+ &transaction->refnames))
BUG("initial ref transaction called with existing refs");
packed_transaction = ref_store_transaction_begin(refs->packed_ref_store,
transaction->flags, err);
if (!packed_transaction) {
- ret = TRANSACTION_GENERIC_ERROR;
+ ret = REF_TRANSACTION_ERROR_GENERIC;
goto cleanup;
}
@@ -3117,7 +3075,7 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
if (!loose_transaction) {
loose_transaction = ref_store_transaction_begin(&refs->base, 0, err);
if (!loose_transaction) {
- ret = TRANSACTION_GENERIC_ERROR;
+ ret = REF_TRANSACTION_ERROR_GENERIC;
goto cleanup;
}
}
@@ -3142,19 +3100,20 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
}
if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
- ret = TRANSACTION_GENERIC_ERROR;
+ ret = REF_TRANSACTION_ERROR_GENERIC;
goto cleanup;
}
if (refs_verify_refnames_available(&refs->base, &refnames_to_check,
- &affected_refnames, NULL, 1, err)) {
+ &affected_refnames, NULL, transaction,
+ 1, err)) {
packed_refs_unlock(refs->packed_ref_store);
- ret = TRANSACTION_NAME_CONFLICT;
+ ret = REF_TRANSACTION_ERROR_NAME_CONFLICT;
goto cleanup;
}
if (ref_transaction_commit(packed_transaction, err)) {
- ret = TRANSACTION_GENERIC_ERROR;
+ ret = REF_TRANSACTION_ERROR_GENERIC;
goto cleanup;
}
packed_refs_unlock(refs->packed_ref_store);
@@ -3162,7 +3121,7 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
if (loose_transaction) {
if (ref_transaction_prepare(loose_transaction, err) ||
ref_transaction_commit(loose_transaction, err)) {
- ret = TRANSACTION_GENERIC_ERROR;
+ ret = REF_TRANSACTION_ERROR_GENERIC;
goto cleanup;
}
}
@@ -3208,10 +3167,13 @@ static int files_transaction_finish(struct ref_store *ref_store,
struct ref_update *update = transaction->updates[i];
struct ref_lock *lock = update->backend_data;
+ if (update->rejection_err)
+ continue;
+
if (update->flags & REF_NEEDS_COMMIT ||
update->flags & REF_LOG_ONLY) {
if (parse_and_write_reflog(refs, update, lock, err)) {
- ret = TRANSACTION_GENERIC_ERROR;
+ ret = REF_TRANSACTION_ERROR_GENERIC;
goto cleanup;
}
}
@@ -3230,7 +3192,7 @@ static int files_transaction_finish(struct ref_store *ref_store,
strbuf_addf(err, "couldn't set '%s'", lock->ref_name);
unlock_ref(lock);
update->backend_data = NULL;
- ret = TRANSACTION_GENERIC_ERROR;
+ ret = REF_TRANSACTION_ERROR_GENERIC;
goto cleanup;
}
}
@@ -3286,7 +3248,7 @@ static int files_transaction_finish(struct ref_store *ref_store,
strbuf_reset(&sb);
files_ref_path(refs, &sb, lock->ref_name);
if (unlink_or_msg(sb.buf, err)) {
- ret = TRANSACTION_GENERIC_ERROR;
+ ret = REF_TRANSACTION_ERROR_GENERIC;
goto cleanup;
}
}
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index b4289a7d9c..3ad1ed0787 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -980,9 +980,9 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
continue;
while (prefix && *prefix) {
- if (*refname < *prefix)
+ if ((unsigned char)*refname < (unsigned char)*prefix)
BUG("packed-refs backend yielded reference preceding its prefix");
- else if (*refname > *prefix)
+ else if ((unsigned char)*refname > (unsigned char)*prefix)
return ITER_DONE;
prefix++;
refname++;
@@ -1351,10 +1351,12 @@ static int packed_ref_store_remove_on_disk(struct ref_store *ref_store,
* The packfile must be locked before calling this function and will
* remain locked when it is done.
*/
-static int write_with_updates(struct packed_ref_store *refs,
- struct string_list *updates,
- struct strbuf *err)
+static enum ref_transaction_error write_with_updates(struct packed_ref_store *refs,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
{
+ enum ref_transaction_error ret = REF_TRANSACTION_ERROR_GENERIC;
+ struct string_list *updates = &transaction->refnames;
struct ref_iterator *iter = NULL;
size_t i;
int ok;
@@ -1378,7 +1380,7 @@ static int write_with_updates(struct packed_ref_store *refs,
strbuf_addf(err, "unable to create file %s: %s",
sb.buf, strerror(errno));
strbuf_release(&sb);
- return -1;
+ return REF_TRANSACTION_ERROR_GENERIC;
}
strbuf_release(&sb);
@@ -1434,6 +1436,14 @@ static int write_with_updates(struct packed_ref_store *refs,
strbuf_addf(err, "cannot update ref '%s': "
"reference already exists",
update->refname);
+ ret = REF_TRANSACTION_ERROR_CREATE_EXISTS;
+
+ if (ref_transaction_maybe_set_rejected(transaction, i, ret)) {
+ strbuf_reset(err);
+ ret = 0;
+ continue;
+ }
+
goto error;
} else if (!oideq(&update->old_oid, iter->oid)) {
strbuf_addf(err, "cannot update ref '%s': "
@@ -1441,6 +1451,14 @@ static int write_with_updates(struct packed_ref_store *refs,
update->refname,
oid_to_hex(iter->oid),
oid_to_hex(&update->old_oid));
+ ret = REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE;
+
+ if (ref_transaction_maybe_set_rejected(transaction, i, ret)) {
+ strbuf_reset(err);
+ ret = 0;
+ continue;
+ }
+
goto error;
}
}
@@ -1477,6 +1495,14 @@ static int write_with_updates(struct packed_ref_store *refs,
"reference is missing but expected %s",
update->refname,
oid_to_hex(&update->old_oid));
+ ret = REF_TRANSACTION_ERROR_NONEXISTENT_REF;
+
+ if (ref_transaction_maybe_set_rejected(transaction, i, ret)) {
+ strbuf_reset(err);
+ ret = 0;
+ continue;
+ }
+
goto error;
}
}
@@ -1534,7 +1560,7 @@ static int write_with_updates(struct packed_ref_store *refs,
strerror(errno));
strbuf_release(&sb);
delete_tempfile(&refs->tempfile);
- return -1;
+ return REF_TRANSACTION_ERROR_GENERIC;
}
return 0;
@@ -1542,11 +1568,12 @@ static int write_with_updates(struct packed_ref_store *refs,
write_error:
strbuf_addf(err, "error writing to %s: %s",
get_tempfile_path(refs->tempfile), strerror(errno));
+ ret = REF_TRANSACTION_ERROR_GENERIC;
error:
ref_iterator_free(iter);
delete_tempfile(&refs->tempfile);
- return -1;
+ return ret;
}
int is_packed_transaction_needed(struct ref_store *ref_store,
@@ -1647,8 +1674,6 @@ int is_packed_transaction_needed(struct ref_store *ref_store,
struct packed_transaction_backend_data {
/* True iff the transaction owns the packed-refs lock. */
int own_lock;
-
- struct string_list updates;
};
static void packed_transaction_cleanup(struct packed_ref_store *refs,
@@ -1657,8 +1682,6 @@ static void packed_transaction_cleanup(struct packed_ref_store *refs,
struct packed_transaction_backend_data *data = transaction->backend_data;
if (data) {
- string_list_clear(&data->updates, 0);
-
if (is_tempfile_active(refs->tempfile))
delete_tempfile(&refs->tempfile);
@@ -1683,8 +1706,7 @@ static int packed_transaction_prepare(struct ref_store *ref_store,
REF_STORE_READ | REF_STORE_WRITE | REF_STORE_ODB,
"ref_transaction_prepare");
struct packed_transaction_backend_data *data;
- size_t i;
- int ret = TRANSACTION_GENERIC_ERROR;
+ enum ref_transaction_error ret = REF_TRANSACTION_ERROR_GENERIC;
/*
* Note that we *don't* skip transactions with zero updates,
@@ -1696,34 +1718,17 @@ static int packed_transaction_prepare(struct ref_store *ref_store,
*/
CALLOC_ARRAY(data, 1);
- string_list_init_nodup(&data->updates);
transaction->backend_data = data;
- /*
- * Stick the updates in a string list by refname so that we
- * can sort them:
- */
- for (i = 0; i < transaction->nr; i++) {
- struct ref_update *update = transaction->updates[i];
- struct string_list_item *item =
- string_list_append(&data->updates, update->refname);
-
- /* Store a pointer to update in item->util: */
- item->util = update;
- }
- string_list_sort(&data->updates);
-
- if (ref_update_reject_duplicates(&data->updates, err))
- goto failure;
-
if (!is_lock_file_locked(&refs->lock)) {
if (packed_refs_lock(ref_store, 0, err))
goto failure;
data->own_lock = 1;
}
- if (write_with_updates(refs, &data->updates, err))
+ ret = write_with_updates(refs, transaction, err);
+ if (ret)
goto failure;
transaction->state = REF_TRANSACTION_PREPARED;
@@ -1755,7 +1760,7 @@ static int packed_transaction_finish(struct ref_store *ref_store,
ref_store,
REF_STORE_READ | REF_STORE_WRITE | REF_STORE_ODB,
"ref_transaction_finish");
- int ret = TRANSACTION_GENERIC_ERROR;
+ int ret = REF_TRANSACTION_ERROR_GENERIC;
char *packed_refs_path;
clear_snapshot(refs);
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index e5862757a7..f868870851 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -3,6 +3,7 @@
#include "refs.h"
#include "iterator.h"
+#include "string-list.h"
struct fsck_options;
struct ref_transaction;
@@ -123,6 +124,12 @@ struct ref_update {
uint64_t index;
/*
+ * Used in batched reference updates to mark if a given update
+ * was rejected.
+ */
+ enum ref_transaction_error rejection_err;
+
+ /*
* If this ref_update was split off of a symref update via
* split_symref_update(), then this member points at that
* update. This is used for two purposes:
@@ -142,12 +149,11 @@ int refs_read_raw_ref(struct ref_store *ref_store, const char *refname,
unsigned int *type, int *failure_errno);
/*
- * Write an error to `err` and return a nonzero value iff the same
- * refname appears multiple times in `refnames`. `refnames` must be
- * sorted on entry to this function.
+ * Mark a given update as rejected with a given reason.
*/
-int ref_update_reject_duplicates(struct string_list *refnames,
- struct strbuf *err);
+int ref_transaction_maybe_set_rejected(struct ref_transaction *transaction,
+ size_t update_idx,
+ enum ref_transaction_error err);
/*
* Add a ref_update with the specified properties to transaction, and
@@ -191,6 +197,18 @@ enum ref_transaction_state {
};
/*
+ * Data structure to hold indices of updates which were rejected, for batched
+ * reference updates. While the updates themselves hold the rejection error,
+ * this structure allows a transaction to iterate only over the rejected
+ * updates.
+ */
+struct ref_transaction_rejections {
+ size_t *update_indices;
+ size_t alloc;
+ size_t nr;
+};
+
+/*
* Data structure for holding a reference transaction, which can
* consist of checks and updates to multiple references, carried out
* as atomically as possible. This structure is opaque to callers.
@@ -198,9 +216,11 @@ enum ref_transaction_state {
struct ref_transaction {
struct ref_store *ref_store;
struct ref_update **updates;
+ struct string_list refnames;
size_t alloc;
size_t nr;
enum ref_transaction_state state;
+ struct ref_transaction_rejections *rejections;
void *backend_data;
unsigned int flags;
uint64_t max_index;
@@ -776,8 +796,9 @@ int ref_update_has_null_new_value(struct ref_update *update);
* If everything is OK, return 0; otherwise, write an error message to
* err and return -1.
*/
-int ref_update_check_old_target(const char *referent, struct ref_update *update,
- struct strbuf *err);
+enum ref_transaction_error ref_update_check_old_target(const char *referent,
+ struct ref_update *update,
+ struct strbuf *err);
/*
* Check if the ref must exist, this means that the old_oid or
@@ -785,4 +806,20 @@ int ref_update_check_old_target(const char *referent, struct ref_update *update,
*/
int ref_update_expects_existing_old_ref(struct ref_update *update);
+/*
+ * Same as `refs_verify_refname_available()`, but checking for a list of
+ * refnames instead of only a single item. This is more efficient in the case
+ * where one needs to check multiple refnames.
+ *
+ * If using batched updates, then individual updates are marked rejected,
+ * reference backends are then in charge of not committing those updates.
+ */
+enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs,
+ const struct string_list *refnames,
+ const struct string_list *extras,
+ const struct string_list *skip,
+ struct ref_transaction *transaction,
+ unsigned int initial_transaction,
+ struct strbuf *err);
+
#endif /* REFS_REFS_INTERNAL_H */
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index ae434cd248..4c3817f4ec 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1069,6 +1069,244 @@ static int queue_transaction_update(struct reftable_ref_store *refs,
return 0;
}
+static enum ref_transaction_error prepare_single_update(struct reftable_ref_store *refs,
+ struct reftable_transaction_data *tx_data,
+ struct ref_transaction *transaction,
+ struct reftable_backend *be,
+ struct ref_update *u,
+ size_t update_idx,
+ struct string_list *refnames_to_check,
+ unsigned int head_type,
+ struct strbuf *head_referent,
+ struct strbuf *referent,
+ struct strbuf *err)
+{
+ enum ref_transaction_error ret = 0;
+ struct object_id current_oid = {0};
+ const char *rewritten_ref;
+
+ /*
+ * There is no need to reload the respective backends here as
+ * we have already reloaded them when preparing the transaction
+ * update. And given that the stacks have been locked there
+ * shouldn't have been any concurrent modifications of the
+ * stack.
+ */
+ ret = backend_for(&be, refs, u->refname, &rewritten_ref, 0);
+ if (ret)
+ return REF_TRANSACTION_ERROR_GENERIC;
+
+ /* Verify that the new object ID is valid. */
+ if ((u->flags & REF_HAVE_NEW) && !is_null_oid(&u->new_oid) &&
+ !(u->flags & REF_SKIP_OID_VERIFICATION) &&
+ !(u->flags & REF_LOG_ONLY)) {
+ struct object *o = parse_object(refs->base.repo, &u->new_oid);
+ if (!o) {
+ strbuf_addf(err,
+ _("trying to write ref '%s' with nonexistent object %s"),
+ u->refname, oid_to_hex(&u->new_oid));
+ return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
+ }
+
+ if (o->type != OBJ_COMMIT && is_branch(u->refname)) {
+ strbuf_addf(err, _("trying to write non-commit object %s to branch '%s'"),
+ oid_to_hex(&u->new_oid), u->refname);
+ return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
+ }
+ }
+
+ /*
+ * When we update the reference that HEAD points to we enqueue
+ * a second log-only update for HEAD so that its reflog is
+ * updated accordingly.
+ */
+ if (head_type == REF_ISSYMREF &&
+ !(u->flags & REF_LOG_ONLY) &&
+ !(u->flags & REF_UPDATE_VIA_HEAD) &&
+ !strcmp(rewritten_ref, head_referent->buf)) {
+ /*
+ * First make sure that HEAD is not already in the
+ * transaction. This check is O(lg N) in the transaction
+ * size, but it happens at most once per transaction.
+ */
+ if (string_list_has_string(&transaction->refnames, "HEAD")) {
+ /* An entry already existed */
+ strbuf_addf(err,
+ _("multiple updates for 'HEAD' (including one "
+ "via its referent '%s') are not allowed"),
+ u->refname);
+ return REF_TRANSACTION_ERROR_NAME_CONFLICT;
+ }
+
+ ref_transaction_add_update(
+ transaction, "HEAD",
+ u->flags | REF_LOG_ONLY | REF_NO_DEREF,
+ &u->new_oid, &u->old_oid, NULL, NULL, NULL,
+ u->msg);
+ }
+
+ ret = reftable_backend_read_ref(be, rewritten_ref,
+ &current_oid, referent, &u->type);
+ if (ret < 0)
+ return REF_TRANSACTION_ERROR_GENERIC;
+ if (ret > 0 && !ref_update_expects_existing_old_ref(u)) {
+ struct string_list_item *item;
+ /*
+ * The reference does not exist, and we either have no
+ * old object ID or expect the reference to not exist.
+ * We can thus skip below safety checks as well as the
+ * symref splitting. But we do want to verify that
+ * there is no conflicting reference here so that we
+ * can output a proper error message instead of failing
+ * at a later point.
+ */
+ item = string_list_append(refnames_to_check, u->refname);
+ item->util = xmalloc(sizeof(update_idx));
+ memcpy(item->util, &update_idx, sizeof(update_idx));
+
+ /*
+ * There is no need to write the reference deletion
+ * when the reference in question doesn't exist.
+ */
+ if ((u->flags & REF_HAVE_NEW) && !ref_update_has_null_new_value(u)) {
+ ret = queue_transaction_update(refs, tx_data, u,
+ &current_oid, err);
+ if (ret)
+ return REF_TRANSACTION_ERROR_GENERIC;
+ }
+
+ return 0;
+ }
+ if (ret > 0) {
+ /* The reference does not exist, but we expected it to. */
+ strbuf_addf(err, _("cannot lock ref '%s': "
+
+
+ "unable to resolve reference '%s'"),
+ ref_update_original_update_refname(u), u->refname);
+ return REF_TRANSACTION_ERROR_NONEXISTENT_REF;
+ }
+
+ if (u->type & REF_ISSYMREF) {
+ /*
+ * The reftable stack is locked at this point already,
+ * so it is safe to call `refs_resolve_ref_unsafe()`
+ * here without causing races.
+ */
+ const char *resolved = refs_resolve_ref_unsafe(&refs->base, u->refname, 0,
+ &current_oid, NULL);
+
+ if (u->flags & REF_NO_DEREF) {
+ if (u->flags & REF_HAVE_OLD && !resolved) {
+ strbuf_addf(err, _("cannot lock ref '%s': "
+ "error reading reference"), u->refname);
+ return REF_TRANSACTION_ERROR_GENERIC;
+ }
+ } else {
+ struct ref_update *new_update;
+ int new_flags;
+
+ new_flags = u->flags;
+ if (!strcmp(rewritten_ref, "HEAD"))
+ new_flags |= REF_UPDATE_VIA_HEAD;
+
+ if (string_list_has_string(&transaction->refnames, referent->buf)) {
+ strbuf_addf(err,
+ _("multiple updates for '%s' (including one "
+ "via symref '%s') are not allowed"),
+ referent->buf, u->refname);
+ return REF_TRANSACTION_ERROR_NAME_CONFLICT;
+ }
+
+ /*
+ * If we are updating a symref (eg. HEAD), we should also
+ * update the branch that the symref points to.
+ *
+ * This is generic functionality, and would be better
+ * done in refs.c, but the current implementation is
+ * intertwined with the locking in files-backend.c.
+ */
+ new_update = ref_transaction_add_update(
+ transaction, referent->buf, new_flags,
+ u->new_target ? NULL : &u->new_oid,
+ u->old_target ? NULL : &u->old_oid,
+ u->new_target, u->old_target,
+ u->committer_info, u->msg);
+
+ new_update->parent_update = u;
+
+ /*
+ * Change the symbolic ref update to log only. Also, it
+ * doesn't need to check its old OID value, as that will be
+ * done when new_update is processed.
+ */
+ u->flags |= REF_LOG_ONLY | REF_NO_DEREF;
+ u->flags &= ~REF_HAVE_OLD;
+ }
+ }
+
+ /*
+ * Verify that the old object matches our expectations. Note
+ * that the error messages here do not make a lot of sense in
+ * the context of the reftable backend as we never lock
+ * individual refs. But the error messages match what the files
+ * backend returns, which keeps our tests happy.
+ */
+ if (u->old_target) {
+ if (!(u->type & REF_ISSYMREF)) {
+ strbuf_addf(err, _("cannot lock ref '%s': "
+ "expected symref with target '%s': "
+ "but is a regular ref"),
+ ref_update_original_update_refname(u),
+ u->old_target);
+ return REF_TRANSACTION_ERROR_EXPECTED_SYMREF;
+ }
+
+ ret = ref_update_check_old_target(referent->buf, u, err);
+ if (ret)
+ return ret;
+ } else if ((u->flags & REF_HAVE_OLD) && !oideq(&current_oid, &u->old_oid)) {
+ if (is_null_oid(&u->old_oid)) {
+ strbuf_addf(err, _("cannot lock ref '%s': "
+ "reference already exists"),
+ ref_update_original_update_refname(u));
+ return REF_TRANSACTION_ERROR_CREATE_EXISTS;
+ } else if (is_null_oid(&current_oid)) {
+ strbuf_addf(err, _("cannot lock ref '%s': "
+ "reference is missing but expected %s"),
+ ref_update_original_update_refname(u),
+ oid_to_hex(&u->old_oid));
+ return REF_TRANSACTION_ERROR_NONEXISTENT_REF;
+ } else {
+ strbuf_addf(err, _("cannot lock ref '%s': "
+ "is at %s but expected %s"),
+ ref_update_original_update_refname(u),
+ oid_to_hex(&current_oid),
+ oid_to_hex(&u->old_oid));
+ return REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE;
+ }
+ }
+
+ /*
+ * If all of the following conditions are true:
+ *
+ * - We're not about to write a symref.
+ * - We're not about to write a log-only entry.
+ * - Old and new object ID are different.
+ *
+ * Then we're essentially doing a no-op update that can be
+ * skipped. This is not only for the sake of efficiency, but
+ * also skips writing unneeded reflog entries.
+ */
+ if ((u->type & REF_ISSYMREF) ||
+ (u->flags & REF_LOG_ONLY) ||
+ (u->flags & REF_HAVE_NEW && !oideq(&current_oid, &u->new_oid)))
+ if (queue_transaction_update(refs, tx_data, u, &current_oid, err))
+ return REF_TRANSACTION_ERROR_GENERIC;
+
+ return 0;
+}
+
static int reftable_be_transaction_prepare(struct ref_store *ref_store,
struct ref_transaction *transaction,
struct strbuf *err)
@@ -1076,7 +1314,6 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
struct reftable_ref_store *refs =
reftable_be_downcast(ref_store, REF_STORE_WRITE|REF_STORE_MAIN, "ref_transaction_prepare");
struct strbuf referent = STRBUF_INIT, head_referent = STRBUF_INIT;
- struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
struct string_list refnames_to_check = STRING_LIST_INIT_NODUP;
struct reftable_transaction_data *tx_data = NULL;
struct reftable_backend *be;
@@ -1101,10 +1338,6 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
transaction->updates[i], err);
if (ret)
goto done;
-
- if (!(transaction->updates[i]->flags & REF_LOG_ONLY))
- string_list_append(&affected_refnames,
- transaction->updates[i]->refname);
}
/*
@@ -1117,17 +1350,6 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
}
/*
- * Fail if a refname appears more than once in the transaction.
- * This code is taken from the files backend and is a good candidate to
- * be moved into the generic layer.
- */
- string_list_sort(&affected_refnames);
- if (ref_update_reject_duplicates(&affected_refnames, err)) {
- ret = TRANSACTION_GENERIC_ERROR;
- goto done;
- }
-
- /*
* TODO: it's dubious whether we should reload the stack that "HEAD"
* belongs to or not. In theory, it may happen that we only modify
* stacks which are _not_ part of the "HEAD" stack. In that case we
@@ -1149,241 +1371,24 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
ret = 0;
for (i = 0; i < transaction->nr; i++) {
- struct ref_update *u = transaction->updates[i];
- struct object_id current_oid = {0};
- const char *rewritten_ref;
-
- /*
- * There is no need to reload the respective backends here as
- * we have already reloaded them when preparing the transaction
- * update. And given that the stacks have been locked there
- * shouldn't have been any concurrent modifications of the
- * stack.
- */
- ret = backend_for(&be, refs, u->refname, &rewritten_ref, 0);
- if (ret)
- goto done;
-
- /* Verify that the new object ID is valid. */
- if ((u->flags & REF_HAVE_NEW) && !is_null_oid(&u->new_oid) &&
- !(u->flags & REF_SKIP_OID_VERIFICATION) &&
- !(u->flags & REF_LOG_ONLY)) {
- struct object *o = parse_object(refs->base.repo, &u->new_oid);
- if (!o) {
- strbuf_addf(err,
- _("trying to write ref '%s' with nonexistent object %s"),
- u->refname, oid_to_hex(&u->new_oid));
- ret = -1;
- goto done;
- }
-
- if (o->type != OBJ_COMMIT && is_branch(u->refname)) {
- strbuf_addf(err, _("trying to write non-commit object %s to branch '%s'"),
- oid_to_hex(&u->new_oid), u->refname);
- ret = -1;
- goto done;
- }
- }
-
- /*
- * When we update the reference that HEAD points to we enqueue
- * a second log-only update for HEAD so that its reflog is
- * updated accordingly.
- */
- if (head_type == REF_ISSYMREF &&
- !(u->flags & REF_LOG_ONLY) &&
- !(u->flags & REF_UPDATE_VIA_HEAD) &&
- !strcmp(rewritten_ref, head_referent.buf)) {
- struct ref_update *new_update;
-
- /*
- * First make sure that HEAD is not already in the
- * transaction. This check is O(lg N) in the transaction
- * size, but it happens at most once per transaction.
- */
- if (string_list_has_string(&affected_refnames, "HEAD")) {
- /* An entry already existed */
- strbuf_addf(err,
- _("multiple updates for 'HEAD' (including one "
- "via its referent '%s') are not allowed"),
- u->refname);
- ret = TRANSACTION_NAME_CONFLICT;
- goto done;
- }
-
- new_update = ref_transaction_add_update(
- transaction, "HEAD",
- u->flags | REF_LOG_ONLY | REF_NO_DEREF,
- &u->new_oid, &u->old_oid, NULL, NULL, NULL,
- u->msg);
- string_list_insert(&affected_refnames, new_update->refname);
- }
-
- ret = reftable_backend_read_ref(be, rewritten_ref,
- &current_oid, &referent, &u->type);
- if (ret < 0)
- goto done;
- if (ret > 0 && !ref_update_expects_existing_old_ref(u)) {
- /*
- * The reference does not exist, and we either have no
- * old object ID or expect the reference to not exist.
- * We can thus skip below safety checks as well as the
- * symref splitting. But we do want to verify that
- * there is no conflicting reference here so that we
- * can output a proper error message instead of failing
- * at a later point.
- */
- string_list_append(&refnames_to_check, u->refname);
-
- /*
- * There is no need to write the reference deletion
- * when the reference in question doesn't exist.
- */
- if ((u->flags & REF_HAVE_NEW) && !ref_update_has_null_new_value(u)) {
- ret = queue_transaction_update(refs, tx_data, u,
- &current_oid, err);
- if (ret)
- goto done;
- }
-
- continue;
- }
- if (ret > 0) {
- /* The reference does not exist, but we expected it to. */
- strbuf_addf(err, _("cannot lock ref '%s': "
- "unable to resolve reference '%s'"),
- ref_update_original_update_refname(u), u->refname);
- ret = -1;
- goto done;
- }
-
- if (u->type & REF_ISSYMREF) {
- /*
- * The reftable stack is locked at this point already,
- * so it is safe to call `refs_resolve_ref_unsafe()`
- * here without causing races.
- */
- const char *resolved = refs_resolve_ref_unsafe(&refs->base, u->refname, 0,
- &current_oid, NULL);
-
- if (u->flags & REF_NO_DEREF) {
- if (u->flags & REF_HAVE_OLD && !resolved) {
- strbuf_addf(err, _("cannot lock ref '%s': "
- "error reading reference"), u->refname);
- ret = -1;
- goto done;
- }
- } else {
- struct ref_update *new_update;
- int new_flags;
-
- new_flags = u->flags;
- if (!strcmp(rewritten_ref, "HEAD"))
- new_flags |= REF_UPDATE_VIA_HEAD;
-
- /*
- * If we are updating a symref (eg. HEAD), we should also
- * update the branch that the symref points to.
- *
- * This is generic functionality, and would be better
- * done in refs.c, but the current implementation is
- * intertwined with the locking in files-backend.c.
- */
- new_update = ref_transaction_add_update(
- transaction, referent.buf, new_flags,
- u->new_target ? NULL : &u->new_oid,
- u->old_target ? NULL : &u->old_oid,
- u->new_target, u->old_target,
- u->committer_info, u->msg);
-
- new_update->parent_update = u;
-
- /*
- * Change the symbolic ref update to log only. Also, it
- * doesn't need to check its old OID value, as that will be
- * done when new_update is processed.
- */
- u->flags |= REF_LOG_ONLY | REF_NO_DEREF;
- u->flags &= ~REF_HAVE_OLD;
-
- if (string_list_has_string(&affected_refnames, new_update->refname)) {
- strbuf_addf(err,
- _("multiple updates for '%s' (including one "
- "via symref '%s') are not allowed"),
- referent.buf, u->refname);
- ret = TRANSACTION_NAME_CONFLICT;
- goto done;
- }
- string_list_insert(&affected_refnames, new_update->refname);
- }
- }
-
- /*
- * Verify that the old object matches our expectations. Note
- * that the error messages here do not make a lot of sense in
- * the context of the reftable backend as we never lock
- * individual refs. But the error messages match what the files
- * backend returns, which keeps our tests happy.
- */
- if (u->old_target) {
- if (!(u->type & REF_ISSYMREF)) {
- strbuf_addf(err, _("cannot lock ref '%s': "
- "expected symref with target '%s': "
- "but is a regular ref"),
- ref_update_original_update_refname(u),
- u->old_target);
- ret = -1;
- goto done;
- }
+ ret = prepare_single_update(refs, tx_data, transaction, be,
+ transaction->updates[i], i,
+ &refnames_to_check, head_type,
+ &head_referent, &referent, err);
+ if (ret) {
+ if (ref_transaction_maybe_set_rejected(transaction, i, ret)) {
+ strbuf_reset(err);
+ ret = 0;
- if (ref_update_check_old_target(referent.buf, u, err)) {
- ret = -1;
- goto done;
- }
- } else if ((u->flags & REF_HAVE_OLD) && !oideq(&current_oid, &u->old_oid)) {
- ret = TRANSACTION_NAME_CONFLICT;
- if (is_null_oid(&u->old_oid)) {
- strbuf_addf(err, _("cannot lock ref '%s': "
- "reference already exists"),
- ref_update_original_update_refname(u));
- ret = TRANSACTION_CREATE_EXISTS;
+ continue;
}
- else if (is_null_oid(&current_oid))
- strbuf_addf(err, _("cannot lock ref '%s': "
- "reference is missing but expected %s"),
- ref_update_original_update_refname(u),
- oid_to_hex(&u->old_oid));
- else
- strbuf_addf(err, _("cannot lock ref '%s': "
- "is at %s but expected %s"),
- ref_update_original_update_refname(u),
- oid_to_hex(&current_oid),
- oid_to_hex(&u->old_oid));
goto done;
}
-
- /*
- * If all of the following conditions are true:
- *
- * - We're not about to write a symref.
- * - We're not about to write a log-only entry.
- * - Old and new object ID are different.
- *
- * Then we're essentially doing a no-op update that can be
- * skipped. This is not only for the sake of efficiency, but
- * also skips writing unneeded reflog entries.
- */
- if ((u->type & REF_ISSYMREF) ||
- (u->flags & REF_LOG_ONLY) ||
- (u->flags & REF_HAVE_NEW && !oideq(&current_oid, &u->new_oid))) {
- ret = queue_transaction_update(refs, tx_data, u,
- &current_oid, err);
- if (ret)
- goto done;
- }
}
- ret = refs_verify_refnames_available(ref_store, &refnames_to_check, &affected_refnames, NULL,
+ ret = refs_verify_refnames_available(ref_store, &refnames_to_check,
+ &transaction->refnames, NULL,
+ transaction,
transaction->flags & REF_TRANSACTION_FLAG_INITIAL,
err);
if (ret < 0)
@@ -1393,7 +1398,6 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
transaction->state = REF_TRANSACTION_PREPARED;
done:
- assert(ret != REFTABLE_API_ERROR);
if (ret < 0) {
free_transaction_data(tx_data);
transaction->state = REF_TRANSACTION_CLOSED;
@@ -1401,10 +1405,9 @@ done:
strbuf_addf(err, _("reftable: transaction prepare: %s"),
reftable_error_str(ret));
}
- string_list_clear(&affected_refnames, 0);
strbuf_release(&referent);
strbuf_release(&head_referent);
- string_list_clear(&refnames_to_check, 0);
+ string_list_clear(&refnames_to_check, 1);
return ret;
}
@@ -1463,6 +1466,9 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
struct reftable_transaction_update *tx_update = &arg->updates[i];
struct ref_update *u = tx_update->update;
+ if (u->rejection_err)
+ continue;
+
/*
* Write a reflog entry when updating a ref to point to
* something new in either of the following cases:
diff --git a/refspec.c b/refspec.c
index c6ad515f04..0775358d96 100644
--- a/refspec.c
+++ b/refspec.c
@@ -153,18 +153,22 @@ static int parse_refspec(struct refspec_item *item, const char *refspec, int fet
return 1;
}
-int refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)
+static int refspec_item_init(struct refspec_item *item, const char *refspec,
+ int fetch)
{
memset(item, 0, sizeof(*item));
item->raw = xstrdup(refspec);
return parse_refspec(item, refspec, fetch);
}
-void refspec_item_init_or_die(struct refspec_item *item, const char *refspec,
- int fetch)
+int refspec_item_init_fetch(struct refspec_item *item, const char *refspec)
{
- if (!refspec_item_init(item, refspec, fetch))
- die(_("invalid refspec '%s'"), refspec);
+ return refspec_item_init(item, refspec, 1);
+}
+
+int refspec_item_init_push(struct refspec_item *item, const char *refspec)
+{
+ return refspec_item_init(item, refspec, 0);
}
void refspec_item_clear(struct refspec_item *item)
@@ -178,17 +182,29 @@ void refspec_item_clear(struct refspec_item *item)
item->exact_sha1 = 0;
}
-void refspec_init(struct refspec *rs, int fetch)
+void refspec_init_fetch(struct refspec *rs)
+{
+ struct refspec blank = REFSPEC_INIT_FETCH;
+ memcpy(rs, &blank, sizeof(*rs));
+}
+
+void refspec_init_push(struct refspec *rs)
{
- memset(rs, 0, sizeof(*rs));
- rs->fetch = fetch;
+ struct refspec blank = REFSPEC_INIT_PUSH;
+ memcpy(rs, &blank, sizeof(*rs));
}
void refspec_append(struct refspec *rs, const char *refspec)
{
struct refspec_item item;
+ int ret;
- refspec_item_init_or_die(&item, refspec, rs->fetch);
+ if (rs->fetch)
+ ret = refspec_item_init_fetch(&item, refspec);
+ else
+ ret = refspec_item_init_push(&item, refspec);
+ if (!ret)
+ die(_("invalid refspec '%s'"), refspec);
ALLOC_GROW(rs->items, rs->nr + 1, rs->alloc);
rs->items[rs->nr] = item;
@@ -233,7 +249,7 @@ void refspec_clear(struct refspec *rs)
int valid_fetch_refspec(const char *fetch_refspec_str)
{
struct refspec_item refspec;
- int ret = refspec_item_init(&refspec, fetch_refspec_str, REFSPEC_FETCH);
+ int ret = refspec_item_init_fetch(&refspec, fetch_refspec_str);
refspec_item_clear(&refspec);
return ret;
}
@@ -249,7 +265,7 @@ void refspec_ref_prefixes(const struct refspec *rs,
if (item->negative)
continue;
- if (rs->fetch == REFSPEC_FETCH) {
+ if (rs->fetch) {
if (item->exact_sha1)
continue;
prefix = item->src;
diff --git a/refspec.h b/refspec.h
index e2b5cc54ef..8b04f9995e 100644
--- a/refspec.h
+++ b/refspec.h
@@ -32,11 +32,8 @@ struct refspec_item {
struct string_list;
-#define REFSPEC_FETCH 1
-#define REFSPEC_PUSH 0
-
-#define REFSPEC_INIT_FETCH { .fetch = REFSPEC_FETCH }
-#define REFSPEC_INIT_PUSH { .fetch = REFSPEC_PUSH }
+#define REFSPEC_INIT_FETCH { .fetch = 1 }
+#define REFSPEC_INIT_PUSH { .fetch = 0 }
/**
* An array of strings can be parsed into a struct refspec using
@@ -47,15 +44,14 @@ struct refspec {
int alloc;
int nr;
- int fetch;
+ unsigned fetch : 1;
};
-int refspec_item_init(struct refspec_item *item, const char *refspec,
- int fetch);
-void refspec_item_init_or_die(struct refspec_item *item, const char *refspec,
- int fetch);
+int refspec_item_init_fetch(struct refspec_item *item, const char *refspec);
+int refspec_item_init_push(struct refspec_item *item, const char *refspec);
void refspec_item_clear(struct refspec_item *item);
-void refspec_init(struct refspec *rs, int fetch);
+void refspec_init_fetch(struct refspec *rs);
+void refspec_init_push(struct refspec *rs);
void refspec_append(struct refspec *rs, const char *refspec);
__attribute__((format (printf,2,3)))
void refspec_appendf(struct refspec *rs, const char *fmt, ...);
diff --git a/reftable/basics.c b/reftable/basics.c
index 3b5ea27bbd..8c4a4433e4 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -147,25 +147,6 @@ char *reftable_buf_detach(struct reftable_buf *buf)
return result;
}
-void put_be24(uint8_t *out, uint32_t i)
-{
- out[0] = (uint8_t)((i >> 16) & 0xff);
- out[1] = (uint8_t)((i >> 8) & 0xff);
- out[2] = (uint8_t)(i & 0xff);
-}
-
-uint32_t get_be24(uint8_t *in)
-{
- return (uint32_t)(in[0]) << 16 | (uint32_t)(in[1]) << 8 |
- (uint32_t)(in[2]);
-}
-
-void put_be16(uint8_t *out, uint16_t i)
-{
- out[0] = (uint8_t)((i >> 8) & 0xff);
- out[1] = (uint8_t)(i & 0xff);
-}
-
size_t binsearch(size_t sz, int (*f)(size_t k, void *args), void *args)
{
size_t lo = 0;
diff --git a/reftable/basics.h b/reftable/basics.h
index a2a010a0e1..fd59cbb772 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -16,6 +16,8 @@ https://developers.google.com/open-source/licenses/bsd
#include "system.h"
#include "reftable-basics.h"
+#define REFTABLE_UNUSED __attribute__((__unused__))
+
struct reftable_buf {
size_t alloc;
size_t len;
@@ -76,9 +78,79 @@ char *reftable_buf_detach(struct reftable_buf *buf);
/* Bigendian en/decoding of integers */
-void put_be24(uint8_t *out, uint32_t i);
-uint32_t get_be24(uint8_t *in);
-void put_be16(uint8_t *out, uint16_t i);
+static inline void reftable_put_be16(void *out, uint16_t i)
+{
+ unsigned char *p = out;
+ p[0] = (uint8_t)((i >> 8) & 0xff);
+ p[1] = (uint8_t)((i >> 0) & 0xff);
+}
+
+static inline void reftable_put_be24(void *out, uint32_t i)
+{
+ unsigned char *p = out;
+ p[0] = (uint8_t)((i >> 16) & 0xff);
+ p[1] = (uint8_t)((i >> 8) & 0xff);
+ p[2] = (uint8_t)((i >> 0) & 0xff);
+}
+
+static inline void reftable_put_be32(void *out, uint32_t i)
+{
+ unsigned char *p = out;
+ p[0] = (uint8_t)((i >> 24) & 0xff);
+ p[1] = (uint8_t)((i >> 16) & 0xff);
+ p[2] = (uint8_t)((i >> 8) & 0xff);
+ p[3] = (uint8_t)((i >> 0) & 0xff);
+}
+
+static inline void reftable_put_be64(void *out, uint64_t i)
+{
+ unsigned char *p = out;
+ p[0] = (uint8_t)((i >> 56) & 0xff);
+ p[1] = (uint8_t)((i >> 48) & 0xff);
+ p[2] = (uint8_t)((i >> 40) & 0xff);
+ p[3] = (uint8_t)((i >> 32) & 0xff);
+ p[4] = (uint8_t)((i >> 24) & 0xff);
+ p[5] = (uint8_t)((i >> 16) & 0xff);
+ p[6] = (uint8_t)((i >> 8) & 0xff);
+ p[7] = (uint8_t)((i >> 0) & 0xff);
+}
+
+static inline uint16_t reftable_get_be16(const void *in)
+{
+ const unsigned char *p = in;
+ return (uint16_t)(p[0]) << 8 |
+ (uint16_t)(p[1]) << 0;
+}
+
+static inline uint32_t reftable_get_be24(const void *in)
+{
+ const unsigned char *p = in;
+ return (uint32_t)(p[0]) << 16 |
+ (uint32_t)(p[1]) << 8 |
+ (uint32_t)(p[2]) << 0;
+}
+
+static inline uint32_t reftable_get_be32(const void *in)
+{
+ const unsigned char *p = in;
+ return (uint32_t)(p[0]) << 24 |
+ (uint32_t)(p[1]) << 16 |
+ (uint32_t)(p[2]) << 8|
+ (uint32_t)(p[3]) << 0;
+}
+
+static inline uint64_t reftable_get_be64(const void *in)
+{
+ const unsigned char *p = in;
+ return (uint64_t)(p[0]) << 56 |
+ (uint64_t)(p[1]) << 48 |
+ (uint64_t)(p[2]) << 40 |
+ (uint64_t)(p[3]) << 32 |
+ (uint64_t)(p[4]) << 24 |
+ (uint64_t)(p[5]) << 16 |
+ (uint64_t)(p[6]) << 8 |
+ (uint64_t)(p[7]) << 0;
+}
/*
* find smallest index i in [0, sz) at which `f(i) > 0`, assuming that f is
@@ -117,18 +189,46 @@ void reftable_free(void *p);
void *reftable_calloc(size_t nelem, size_t elsize);
char *reftable_strdup(const char *str);
-#define REFTABLE_ALLOC_ARRAY(x, alloc) (x) = reftable_malloc(st_mult(sizeof(*(x)), (alloc)))
+static inline int reftable_alloc_size(size_t nelem, size_t elsize, size_t *out)
+{
+ if (nelem && elsize > SIZE_MAX / nelem)
+ return -1;
+ *out = nelem * elsize;
+ return 0;
+}
+
+#define REFTABLE_ALLOC_ARRAY(x, alloc) do { \
+ size_t alloc_size; \
+ if (reftable_alloc_size(sizeof(*(x)), (alloc), &alloc_size) < 0) { \
+ errno = ENOMEM; \
+ (x) = NULL; \
+ } else { \
+ (x) = reftable_malloc(alloc_size); \
+ } \
+ } while (0)
#define REFTABLE_CALLOC_ARRAY(x, alloc) (x) = reftable_calloc((alloc), sizeof(*(x)))
-#define REFTABLE_REALLOC_ARRAY(x, alloc) (x) = reftable_realloc((x), st_mult(sizeof(*(x)), (alloc)))
+#define REFTABLE_REALLOC_ARRAY(x, alloc) do { \
+ size_t alloc_size; \
+ if (reftable_alloc_size(sizeof(*(x)), (alloc), &alloc_size) < 0) { \
+ errno = ENOMEM; \
+ (x) = NULL; \
+ } else { \
+ (x) = reftable_realloc((x), alloc_size); \
+ } \
+ } while (0)
static inline void *reftable_alloc_grow(void *p, size_t nelem, size_t elsize,
size_t *allocp)
{
void *new_p;
- size_t alloc = *allocp * 2 + 1;
+ size_t alloc = *allocp * 2 + 1, alloc_bytes;
if (alloc < nelem)
alloc = nelem;
- new_p = reftable_realloc(p, st_mult(elsize, alloc));
+ if (reftable_alloc_size(elsize, alloc, &alloc_bytes) < 0) {
+ errno = ENOMEM;
+ return p;
+ }
+ new_p = reftable_realloc(p, alloc_bytes);
if (!new_p)
return p;
*allocp = alloc;
@@ -168,6 +268,15 @@ static inline void *reftable_alloc_grow(void *p, size_t nelem, size_t elsize,
# define strdup(str) REFTABLE_BANNED(strdup)
#endif
+#define REFTABLE_SWAP(a, b) do { \
+ void *_swap_a_ptr = &(a); \
+ void *_swap_b_ptr = &(b); \
+ unsigned char _swap_buffer[sizeof(a) - 2 * sizeof(a) * (sizeof(a) != sizeof(b))]; \
+ memcpy(_swap_buffer, _swap_a_ptr, sizeof(a)); \
+ memcpy(_swap_a_ptr, _swap_b_ptr, sizeof(a)); \
+ memcpy(_swap_b_ptr, _swap_buffer, sizeof(a)); \
+} while (0)
+
/* Find the longest shared prefix size of `a` and `b` */
size_t common_prefix_size(struct reftable_buf *a, struct reftable_buf *b);
diff --git a/reftable/block.c b/reftable/block.c
index b14a8f1259..251a8e9fd3 100644
--- a/reftable/block.c
+++ b/reftable/block.c
@@ -49,7 +49,7 @@ static int block_writer_register_restart(struct block_writer *w, int n,
if (is_restart)
rlen++;
if (2 + 3 * rlen + n > w->block_size - w->next)
- return -1;
+ return REFTABLE_ENTRY_TOO_BIG_ERROR;
if (is_restart) {
REFTABLE_ALLOC_GROW_OR_NULL(w->restarts, w->restart_len + 1,
w->restart_cap);
@@ -97,9 +97,10 @@ uint8_t block_writer_type(struct block_writer *bw)
return bw->block[bw->header_off];
}
-/* Adds the reftable_record to the block. Returns -1 if it does not fit, 0 on
- success. Returns REFTABLE_API_ERROR if attempting to write a record with
- empty key. */
+/*
+ * Adds the reftable_record to the block. Returns 0 on success and
+ * appropriate error codes on failure.
+ */
int block_writer_add(struct block_writer *w, struct reftable_record *rec)
{
struct reftable_buf empty = REFTABLE_BUF_INIT;
@@ -126,14 +127,14 @@ int block_writer_add(struct block_writer *w, struct reftable_record *rec)
n = reftable_encode_key(&is_restart, out, last, w->scratch,
reftable_record_val_type(rec));
if (n < 0) {
- err = -1;
+ err = n;
goto done;
}
string_view_consume(&out, n);
n = reftable_record_encode(rec, out, w->hash_size);
if (n < 0) {
- err = -1;
+ err = n;
goto done;
}
string_view_consume(&out, n);
@@ -147,13 +148,13 @@ done:
int block_writer_finish(struct block_writer *w)
{
for (uint32_t i = 0; i < w->restart_len; i++) {
- put_be24(w->block + w->next, w->restarts[i]);
+ reftable_put_be24(w->block + w->next, w->restarts[i]);
w->next += 3;
}
- put_be16(w->block + w->next, w->restart_len);
+ reftable_put_be16(w->block + w->next, w->restart_len);
w->next += 2;
- put_be24(w->block + 1 + w->header_off, w->next);
+ reftable_put_be24(w->block + 1 + w->header_off, w->next);
/*
* Log records are stored zlib-compressed. Note that the compression
@@ -215,7 +216,7 @@ int block_reader_init(struct block_reader *br, struct reftable_block *block,
{
uint32_t full_block_size = table_block_size;
uint8_t typ = block->data[header_off];
- uint32_t sz = get_be24(block->data + header_off + 1);
+ uint32_t sz = reftable_get_be24(block->data + header_off + 1);
int err = 0;
uint16_t restart_count = 0;
uint32_t restart_start = 0;
@@ -299,7 +300,7 @@ int block_reader_init(struct block_reader *br, struct reftable_block *block,
full_block_size = sz;
}
- restart_count = get_be16(block->data + sz - 2);
+ restart_count = reftable_get_be16(block->data + sz - 2);
restart_start = sz - 2 - 3 * restart_count;
restart_bytes = block->data + restart_start;
@@ -354,7 +355,7 @@ int block_reader_first_key(const struct block_reader *br, struct reftable_buf *k
static uint32_t block_reader_restart_offset(const struct block_reader *br, size_t idx)
{
- return get_be24(br->restart_bytes + 3 * idx);
+ return reftable_get_be24(br->restart_bytes + 3 * idx);
}
void block_iter_seek_start(struct block_iter *it, const struct block_reader *br)
@@ -508,7 +509,9 @@ int block_iter_seek_key(struct block_iter *it, const struct block_reader *br,
it->block_len = br->block_len;
it->hash_size = br->hash_size;
- reftable_record_init(&rec, block_reader_type(br));
+ err = reftable_record_init(&rec, block_reader_type(br));
+ if (err < 0)
+ goto done;
/*
* We're looking for the last entry less than the wanted key so that
diff --git a/reftable/block.h b/reftable/block.h
index bef2b8a4c5..64732eba7d 100644
--- a/reftable/block.h
+++ b/reftable/block.h
@@ -53,7 +53,7 @@ int block_writer_init(struct block_writer *bw, uint8_t typ, uint8_t *block,
/* returns the block type (eg. 'r' for ref records. */
uint8_t block_writer_type(struct block_writer *bw);
-/* appends the record, or -1 if it doesn't fit. */
+/* Attempts to append the record. Returns 0 on success or error code on failure. */
int block_writer_add(struct block_writer *w, struct reftable_record *rec);
/* appends the key restarts, and compress the block if necessary. */
diff --git a/reftable/blocksource.c b/reftable/blocksource.c
index bba4a45b98..78c1be2337 100644
--- a/reftable/blocksource.c
+++ b/reftable/blocksource.c
@@ -13,14 +13,14 @@ https://developers.google.com/open-source/licenses/bsd
#include "reftable-blocksource.h"
#include "reftable-error.h"
-static void reftable_buf_return_block(void *b UNUSED, struct reftable_block *dest)
+static void reftable_buf_return_block(void *b REFTABLE_UNUSED, struct reftable_block *dest)
{
if (dest->len)
memset(dest->data, 0xff, dest->len);
reftable_free(dest->data);
}
-static void reftable_buf_close(void *b UNUSED)
+static void reftable_buf_close(void *b REFTABLE_UNUSED)
{
}
@@ -67,7 +67,7 @@ static uint64_t file_size(void *b)
return ((struct file_block_source *)b)->size;
}
-static void file_return_block(void *b UNUSED, struct reftable_block *dest UNUSED)
+static void file_return_block(void *b REFTABLE_UNUSED, struct reftable_block *dest REFTABLE_UNUSED)
{
}
@@ -98,7 +98,7 @@ static struct reftable_block_source_vtable file_vtable = {
int reftable_block_source_from_file(struct reftable_block_source *bs,
const char *name)
{
- struct file_block_source *p;
+ struct file_block_source *p = NULL;
struct stat st;
int fd, err;
@@ -122,7 +122,12 @@ int reftable_block_source_from_file(struct reftable_block_source *bs,
}
p->size = st.st_size;
- p->data = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ p->data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (p->data == MAP_FAILED) {
+ err = REFTABLE_IO_ERROR;
+ p->data = NULL;
+ goto out;
+ }
assert(!bs->ops);
bs->ops = &file_vtable;
@@ -135,5 +140,5 @@ out:
close(fd);
if (err < 0)
reftable_free(p);
- return 0;
+ return err;
}
diff --git a/reftable/iter.c b/reftable/iter.c
index 86e801ca9f..f520382e70 100644
--- a/reftable/iter.c
+++ b/reftable/iter.c
@@ -25,17 +25,17 @@ int iterator_next(struct reftable_iterator *it, struct reftable_record *rec)
return it->ops->next(it->iter_arg, rec);
}
-static int empty_iterator_seek(void *arg UNUSED, struct reftable_record *want UNUSED)
+static int empty_iterator_seek(void *arg REFTABLE_UNUSED, struct reftable_record *want REFTABLE_UNUSED)
{
return 0;
}
-static int empty_iterator_next(void *arg UNUSED, struct reftable_record *rec UNUSED)
+static int empty_iterator_next(void *arg REFTABLE_UNUSED, struct reftable_record *rec REFTABLE_UNUSED)
{
return 1;
}
-static void empty_iterator_close(void *arg UNUSED)
+static void empty_iterator_close(void *arg REFTABLE_UNUSED)
{
}
@@ -143,11 +143,10 @@ static int indexed_table_ref_iter_next_block(struct indexed_table_ref_iter *it)
return 0;
}
-static int indexed_table_ref_iter_seek(void *p UNUSED,
- struct reftable_record *want UNUSED)
+static int indexed_table_ref_iter_seek(void *p REFTABLE_UNUSED,
+ struct reftable_record *want REFTABLE_UNUSED)
{
- BUG("seeking indexed table is not supported");
- return -1;
+ return REFTABLE_API_ERROR;
}
static int indexed_table_ref_iter_next(void *p, struct reftable_record *rec)
diff --git a/reftable/merged.c b/reftable/merged.c
index e72b39e178..4ff1553772 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -66,8 +66,11 @@ static int merged_iter_seek(struct merged_iter *mi, struct reftable_record *want
int err;
mi->advance_index = -1;
- while (!merged_iter_pqueue_is_empty(mi->pq))
- merged_iter_pqueue_remove(&mi->pq);
+ while (!merged_iter_pqueue_is_empty(mi->pq)) {
+ err = merged_iter_pqueue_remove(&mi->pq, NULL);
+ if (err < 0)
+ return err;
+ }
for (size_t i = 0; i < mi->subiters_len; i++) {
err = iterator_seek(&mi->subiters[i].iter, want);
@@ -120,7 +123,9 @@ static int merged_iter_next_entry(struct merged_iter *mi,
if (empty)
return 1;
- entry = merged_iter_pqueue_remove(&mi->pq);
+ err = merged_iter_pqueue_remove(&mi->pq, &entry);
+ if (err < 0)
+ return err;
/*
One can also use reftable as datacenter-local storage, where the ref
@@ -134,18 +139,23 @@ static int merged_iter_next_entry(struct merged_iter *mi,
struct pq_entry top = merged_iter_pqueue_top(mi->pq);
int cmp;
- cmp = reftable_record_cmp(top.rec, entry.rec);
+ err = reftable_record_cmp(top.rec, entry.rec, &cmp);
+ if (err < 0)
+ return err;
if (cmp > 0)
break;
- merged_iter_pqueue_remove(&mi->pq);
+ err = merged_iter_pqueue_remove(&mi->pq, NULL);
+ if (err < 0)
+ return err;
+
err = merged_iter_advance_subiter(mi, top.index);
if (err < 0)
return err;
}
mi->advance_index = entry.index;
- SWAP(*rec, *entry.rec);
+ REFTABLE_SWAP(*rec, *entry.rec);
return 0;
}
@@ -253,7 +263,10 @@ int merged_table_init_iter(struct reftable_merged_table *mt,
}
for (size_t i = 0; i < mt->readers_len; i++) {
- reftable_record_init(&subiters[i].rec, typ);
+ ret = reftable_record_init(&subiters[i].rec, typ);
+ if (ret < 0)
+ goto out;
+
ret = reader_init_iter(mt->readers[i], &subiters[i].iter, typ);
if (ret < 0)
goto out;
diff --git a/reftable/pq.c b/reftable/pq.c
index 5591e875e1..82394a972d 100644
--- a/reftable/pq.c
+++ b/reftable/pq.c
@@ -15,13 +15,18 @@ https://developers.google.com/open-source/licenses/bsd
int pq_less(struct pq_entry *a, struct pq_entry *b)
{
- int cmp = reftable_record_cmp(a->rec, b->rec);
+ int cmp, err;
+
+ err = reftable_record_cmp(a->rec, b->rec, &cmp);
+ if (err < 0)
+ return err;
+
if (cmp == 0)
return a->index > b->index;
return cmp < 0;
}
-struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq)
+int merged_iter_pqueue_remove(struct merged_iter_pqueue *pq, struct pq_entry *out)
{
size_t i = 0;
struct pq_entry e = pq->heap[0];
@@ -32,17 +37,34 @@ struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq)
size_t min = i;
size_t j = 2 * i + 1;
size_t k = 2 * i + 2;
- if (j < pq->len && pq_less(&pq->heap[j], &pq->heap[i]))
- min = j;
- if (k < pq->len && pq_less(&pq->heap[k], &pq->heap[min]))
- min = k;
+ int cmp;
+
+ if (j < pq->len) {
+ cmp = pq_less(&pq->heap[j], &pq->heap[i]);
+ if (cmp < 0)
+ return -1;
+ else if (cmp)
+ min = j;
+ }
+
+ if (k < pq->len) {
+ cmp = pq_less(&pq->heap[k], &pq->heap[min]);
+ if (cmp < 0)
+ return -1;
+ else if (cmp)
+ min = k;
+ }
+
if (min == i)
break;
- SWAP(pq->heap[i], pq->heap[min]);
+ REFTABLE_SWAP(pq->heap[i], pq->heap[min]);
i = min;
}
- return e;
+ if (out)
+ *out = e;
+
+ return 0;
}
int merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry *e)
@@ -59,7 +81,7 @@ int merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry
size_t j = (i - 1) / 2;
if (pq_less(&pq->heap[j], &pq->heap[i]))
break;
- SWAP(pq->heap[j], pq->heap[i]);
+ REFTABLE_SWAP(pq->heap[j], pq->heap[i]);
i = j;
}
diff --git a/reftable/pq.h b/reftable/pq.h
index 83c062eeca..ff39016445 100644
--- a/reftable/pq.h
+++ b/reftable/pq.h
@@ -22,7 +22,7 @@ struct merged_iter_pqueue {
size_t cap;
};
-struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq);
+int merged_iter_pqueue_remove(struct merged_iter_pqueue *pq, struct pq_entry *out);
int merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry *e);
void merged_iter_pqueue_release(struct merged_iter_pqueue *pq);
int pq_less(struct pq_entry *a, struct pq_entry *b);
diff --git a/reftable/reader.c b/reftable/reader.c
index 24bae50ac2..172aff2c10 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -101,18 +101,18 @@ static int parse_footer(struct reftable_reader *r, uint8_t *footer,
}
f++;
- r->block_size = get_be24(f);
+ r->block_size = reftable_get_be24(f);
f += 3;
- r->min_update_index = get_be64(f);
+ r->min_update_index = reftable_get_be64(f);
f += 8;
- r->max_update_index = get_be64(f);
+ r->max_update_index = reftable_get_be64(f);
f += 8;
if (r->version == 1) {
r->hash_id = REFTABLE_HASH_SHA1;
} else {
- switch (get_be32(f)) {
+ switch (reftable_get_be32(f)) {
case REFTABLE_FORMAT_ID_SHA1:
r->hash_id = REFTABLE_HASH_SHA1;
break;
@@ -127,24 +127,24 @@ static int parse_footer(struct reftable_reader *r, uint8_t *footer,
f += 4;
}
- r->ref_offsets.index_offset = get_be64(f);
+ r->ref_offsets.index_offset = reftable_get_be64(f);
f += 8;
- r->obj_offsets.offset = get_be64(f);
+ r->obj_offsets.offset = reftable_get_be64(f);
f += 8;
r->object_id_len = r->obj_offsets.offset & ((1 << 5) - 1);
r->obj_offsets.offset >>= 5;
- r->obj_offsets.index_offset = get_be64(f);
+ r->obj_offsets.index_offset = reftable_get_be64(f);
f += 8;
- r->log_offsets.offset = get_be64(f);
+ r->log_offsets.offset = reftable_get_be64(f);
f += 8;
- r->log_offsets.index_offset = get_be64(f);
+ r->log_offsets.index_offset = reftable_get_be64(f);
f += 8;
computed_crc = crc32(0, footer, f - footer);
- file_crc = get_be32(f);
+ file_crc = reftable_get_be32(f);
f += 4;
if (computed_crc != file_crc) {
err = REFTABLE_FORMAT_ERROR;
@@ -214,7 +214,7 @@ static int32_t extract_block_size(uint8_t *data, uint8_t *typ, uint64_t off,
*typ = data[0];
if (reftable_is_block_type(*typ)) {
- result = get_be24(data + 1);
+ result = reftable_get_be24(data + 1);
}
return result;
}
@@ -360,7 +360,10 @@ static int table_iter_seek_linear(struct table_iter *ti,
struct reftable_record rec;
int err;
- reftable_record_init(&rec, reftable_record_type(want));
+ err = reftable_record_init(&rec, reftable_record_type(want));
+ if (err < 0)
+ goto done;
+
err = reftable_record_key(want, &want_key);
if (err < 0)
goto done;
@@ -676,8 +679,6 @@ done:
void reftable_reader_incref(struct reftable_reader *r)
{
- if (!r->refcount)
- BUG("cannot increment ref counter of dead reader");
r->refcount++;
}
@@ -685,8 +686,6 @@ void reftable_reader_decref(struct reftable_reader *r)
{
if (!r)
return;
- if (!r->refcount)
- BUG("cannot decrement ref counter of dead reader");
if (--r->refcount)
return;
block_source_close(&r->source);
@@ -852,7 +851,7 @@ int reftable_reader_print_blocks(const char *tablename)
printf("header:\n");
printf(" block_size: %d\n", r->block_size);
- for (i = 0; i < ARRAY_SIZE(sections); i++) {
+ for (i = 0; i < sizeof(sections) / sizeof(*sections); i++) {
err = table_iter_seek_start(&ti, sections[i].type, 0);
if (err < 0)
goto done;
diff --git a/reftable/record.c b/reftable/record.c
index 8919df8a4d..c0080024ed 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -61,7 +61,7 @@ int put_var_int(struct string_view *dest, uint64_t value)
while (value >>= 7)
varint[--pos] = 0x80 | (--value & 0x7f);
if (dest->len < sizeof(varint) - pos)
- return -1;
+ return REFTABLE_ENTRY_TOO_BIG_ERROR;
memcpy(dest->buf, varint + pos, sizeof(varint) - pos);
return sizeof(varint) - pos;
}
@@ -129,10 +129,10 @@ static int encode_string(const char *str, struct string_view s)
size_t l = strlen(str);
int n = put_var_int(&s, l);
if (n < 0)
- return -1;
+ return n;
string_view_consume(&s, n);
if (s.len < l)
- return -1;
+ return REFTABLE_ENTRY_TOO_BIG_ERROR;
memcpy(s.buf, str, l);
string_view_consume(&s, l);
@@ -148,18 +148,18 @@ int reftable_encode_key(int *restart, struct string_view dest,
uint64_t suffix_len = key.len - prefix_len;
int n = put_var_int(&dest, prefix_len);
if (n < 0)
- return -1;
+ return n;
string_view_consume(&dest, n);
*restart = (prefix_len == 0);
n = put_var_int(&dest, suffix_len << 3 | (uint64_t)extra);
if (n < 0)
- return -1;
+ return n;
string_view_consume(&dest, n);
if (dest.len < suffix_len)
- return -1;
+ return REFTABLE_ENTRY_TOO_BIG_ERROR;
memcpy(dest.buf, key.buf + prefix_len, suffix_len);
string_view_consume(&dest, suffix_len);
@@ -237,11 +237,11 @@ static int reftable_ref_record_copy_from(void *rec, const void *src_rec,
size_t refname_cap = 0;
int err;
- SWAP(refname, ref->refname);
- SWAP(refname_cap, ref->refname_cap);
+ REFTABLE_SWAP(refname, ref->refname);
+ REFTABLE_SWAP(refname_cap, ref->refname_cap);
reftable_ref_record_release(ref);
- SWAP(ref->refname, refname);
- SWAP(ref->refname_cap, refname_cap);
+ REFTABLE_SWAP(ref->refname, refname);
+ REFTABLE_SWAP(ref->refname_cap, refname_cap);
if (src->refname) {
size_t refname_len = strlen(src->refname);
@@ -324,30 +324,27 @@ static int reftable_ref_record_encode(const void *rec, struct string_view s,
struct string_view start = s;
int n = put_var_int(&s, r->update_index);
if (n < 0)
- return -1;
+ return n;
string_view_consume(&s, n);
switch (r->value_type) {
case REFTABLE_REF_SYMREF:
n = encode_string(r->value.symref, s);
- if (n < 0) {
- return -1;
- }
+ if (n < 0)
+ return n;
string_view_consume(&s, n);
break;
case REFTABLE_REF_VAL2:
- if (s.len < 2 * hash_size) {
- return -1;
- }
+ if (s.len < 2 * hash_size)
+ return REFTABLE_ENTRY_TOO_BIG_ERROR;
memcpy(s.buf, r->value.val2.value, hash_size);
string_view_consume(&s, hash_size);
memcpy(s.buf, r->value.val2.target_value, hash_size);
string_view_consume(&s, hash_size);
break;
case REFTABLE_REF_VAL1:
- if (s.len < hash_size) {
- return -1;
- }
+ if (s.len < hash_size)
+ return REFTABLE_ENTRY_TOO_BIG_ERROR;
memcpy(s.buf, r->value.val1, hash_size);
string_view_consume(&s, hash_size);
break;
@@ -376,11 +373,11 @@ static int reftable_ref_record_decode(void *rec, struct reftable_buf key,
return n;
string_view_consume(&in, n);
- SWAP(refname, r->refname);
- SWAP(refname_cap, r->refname_cap);
+ REFTABLE_SWAP(refname, r->refname);
+ REFTABLE_SWAP(refname_cap, r->refname_cap);
reftable_ref_record_release(r);
- SWAP(r->refname, refname);
- SWAP(r->refname_cap, refname_cap);
+ REFTABLE_SWAP(r->refname, refname);
+ REFTABLE_SWAP(r->refname_cap, refname_cap);
REFTABLE_ALLOC_GROW_OR_NULL(r->refname, key.len + 1, r->refname_cap);
if (!r->refname) {
@@ -490,7 +487,7 @@ static void reftable_obj_record_release(void *rec)
}
static int reftable_obj_record_copy_from(void *rec, const void *src_rec,
- uint32_t hash_size UNUSED)
+ uint32_t hash_size REFTABLE_UNUSED)
{
struct reftable_obj_record *obj = rec;
const struct reftable_obj_record *src = src_rec;
@@ -504,11 +501,17 @@ static int reftable_obj_record_copy_from(void *rec, const void *src_rec,
if (src->hash_prefix_len)
memcpy(obj->hash_prefix, src->hash_prefix, obj->hash_prefix_len);
- REFTABLE_ALLOC_ARRAY(obj->offsets, src->offset_len);
- if (!obj->offsets)
- return REFTABLE_OUT_OF_MEMORY_ERROR;
- obj->offset_len = src->offset_len;
- COPY_ARRAY(obj->offsets, src->offsets, src->offset_len);
+ if (src->offset_len) {
+ if (sizeof(*src->offsets) > SIZE_MAX / src->offset_len)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
+
+ REFTABLE_ALLOC_ARRAY(obj->offsets, src->offset_len);
+ if (!obj->offsets)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
+
+ memcpy(obj->offsets, src->offsets, sizeof(*src->offsets) * src->offset_len);
+ obj->offset_len = src->offset_len;
+ }
return 0;
}
@@ -522,7 +525,7 @@ static uint8_t reftable_obj_record_val_type(const void *rec)
}
static int reftable_obj_record_encode(const void *rec, struct string_view s,
- uint32_t hash_size UNUSED)
+ uint32_t hash_size REFTABLE_UNUSED)
{
const struct reftable_obj_record *r = rec;
struct string_view start = s;
@@ -531,24 +534,22 @@ static int reftable_obj_record_encode(const void *rec, struct string_view s,
uint64_t last = 0;
if (r->offset_len == 0 || r->offset_len >= 8) {
n = put_var_int(&s, r->offset_len);
- if (n < 0) {
- return -1;
- }
+ if (n < 0)
+ return n;
string_view_consume(&s, n);
}
if (r->offset_len == 0)
return start.len - s.len;
n = put_var_int(&s, r->offsets[0]);
if (n < 0)
- return -1;
+ return n;
string_view_consume(&s, n);
last = r->offsets[0];
for (i = 1; i < r->offset_len; i++) {
int n = put_var_int(&s, r->offsets[i] - last);
- if (n < 0) {
- return -1;
- }
+ if (n < 0)
+ return n;
string_view_consume(&s, n);
last = r->offsets[i];
}
@@ -557,8 +558,8 @@ static int reftable_obj_record_encode(const void *rec, struct string_view s,
static int reftable_obj_record_decode(void *rec, struct reftable_buf key,
uint8_t val_type, struct string_view in,
- uint32_t hash_size UNUSED,
- struct reftable_buf *scratch UNUSED)
+ uint32_t hash_size REFTABLE_UNUSED,
+ struct reftable_buf *scratch REFTABLE_UNUSED)
{
struct string_view start = in;
struct reftable_obj_record *r = rec;
@@ -612,13 +613,13 @@ static int reftable_obj_record_decode(void *rec, struct reftable_buf key,
return start.len - in.len;
}
-static int not_a_deletion(const void *p UNUSED)
+static int not_a_deletion(const void *p REFTABLE_UNUSED)
{
return 0;
}
static int reftable_obj_record_equal_void(const void *a, const void *b,
- uint32_t hash_size UNUSED)
+ uint32_t hash_size REFTABLE_UNUSED)
{
struct reftable_obj_record *ra = (struct reftable_obj_record *) a;
struct reftable_obj_record *rb = (struct reftable_obj_record *) b;
@@ -683,7 +684,7 @@ static int reftable_log_record_key(const void *r, struct reftable_buf *dest)
return err;
ts = (~ts) - rec->update_index;
- put_be64(&i64[0], ts);
+ reftable_put_be64(&i64[0], ts);
err = reftable_buf_add(dest, i64, sizeof(i64));
if (err < 0)
@@ -783,7 +784,7 @@ static int reftable_log_record_encode(const void *rec, struct string_view s,
return 0;
if (s.len < 2 * hash_size)
- return -1;
+ return REFTABLE_ENTRY_TOO_BIG_ERROR;
memcpy(s.buf, r->value.update.old_hash, hash_size);
memcpy(s.buf + hash_size, r->value.update.new_hash, hash_size);
@@ -791,30 +792,30 @@ static int reftable_log_record_encode(const void *rec, struct string_view s,
n = encode_string(r->value.update.name ? r->value.update.name : "", s);
if (n < 0)
- return -1;
+ return n;
string_view_consume(&s, n);
n = encode_string(r->value.update.email ? r->value.update.email : "",
s);
if (n < 0)
- return -1;
+ return n;
string_view_consume(&s, n);
n = put_var_int(&s, r->value.update.time);
if (n < 0)
- return -1;
+ return n;
string_view_consume(&s, n);
if (s.len < 2)
- return -1;
+ return REFTABLE_ENTRY_TOO_BIG_ERROR;
- put_be16(s.buf, r->value.update.tz_offset);
+ reftable_put_be16(s.buf, r->value.update.tz_offset);
string_view_consume(&s, 2);
n = encode_string(
r->value.update.message ? r->value.update.message : "", s);
if (n < 0)
- return -1;
+ return n;
string_view_consume(&s, n);
return start.len - s.len;
@@ -840,7 +841,7 @@ static int reftable_log_record_decode(void *rec, struct reftable_buf key,
}
memcpy(r->refname, key.buf, key.len - 8);
- ts = get_be64(key.buf + key.len - 8);
+ ts = reftable_get_be64((unsigned char *)key.buf + key.len - 8);
r->update_index = (~max) - ts;
@@ -931,7 +932,7 @@ static int reftable_log_record_decode(void *rec, struct reftable_buf key,
goto done;
}
- r->value.update.tz_offset = get_be16(in.buf);
+ r->value.update.tz_offset = reftable_get_be16(in.buf);
string_view_consume(&in, 2);
n = decode_string(scratch, in);
@@ -1048,7 +1049,7 @@ static int reftable_index_record_key(const void *r, struct reftable_buf *dest)
}
static int reftable_index_record_copy_from(void *rec, const void *src_rec,
- uint32_t hash_size UNUSED)
+ uint32_t hash_size REFTABLE_UNUSED)
{
struct reftable_index_record *dst = rec;
const struct reftable_index_record *src = src_rec;
@@ -1069,13 +1070,13 @@ static void reftable_index_record_release(void *rec)
reftable_buf_release(&idx->last_key);
}
-static uint8_t reftable_index_record_val_type(const void *rec UNUSED)
+static uint8_t reftable_index_record_val_type(const void *rec REFTABLE_UNUSED)
{
return 0;
}
static int reftable_index_record_encode(const void *rec, struct string_view out,
- uint32_t hash_size UNUSED)
+ uint32_t hash_size REFTABLE_UNUSED)
{
const struct reftable_index_record *r =
(const struct reftable_index_record *)rec;
@@ -1091,10 +1092,10 @@ static int reftable_index_record_encode(const void *rec, struct string_view out,
}
static int reftable_index_record_decode(void *rec, struct reftable_buf key,
- uint8_t val_type UNUSED,
+ uint8_t val_type REFTABLE_UNUSED,
struct string_view in,
- uint32_t hash_size UNUSED,
- struct reftable_buf *scratch UNUSED)
+ uint32_t hash_size REFTABLE_UNUSED,
+ struct reftable_buf *scratch REFTABLE_UNUSED)
{
struct string_view start = in;
struct reftable_index_record *r = rec;
@@ -1114,7 +1115,7 @@ static int reftable_index_record_decode(void *rec, struct reftable_buf key,
}
static int reftable_index_record_equal(const void *a, const void *b,
- uint32_t hash_size UNUSED)
+ uint32_t hash_size REFTABLE_UNUSED)
{
struct reftable_index_record *ia = (struct reftable_index_record *) a;
struct reftable_index_record *ib = (struct reftable_index_record *) b;
@@ -1189,12 +1190,14 @@ int reftable_record_is_deletion(struct reftable_record *rec)
reftable_record_data(rec));
}
-int reftable_record_cmp(struct reftable_record *a, struct reftable_record *b)
+int reftable_record_cmp(struct reftable_record *a, struct reftable_record *b,
+ int *cmp)
{
if (a->type != b->type)
- BUG("cannot compare reftable records of different type");
- return reftable_record_vtable(a)->cmp(
- reftable_record_data(a), reftable_record_data(b));
+ return -1;
+ *cmp = reftable_record_vtable(a)->cmp(reftable_record_data(a),
+ reftable_record_data(b));
+ return 0;
}
int reftable_record_equal(struct reftable_record *a, struct reftable_record *b, uint32_t hash_size)
@@ -1300,7 +1303,7 @@ reftable_record_vtable(struct reftable_record *rec)
abort();
}
-void reftable_record_init(struct reftable_record *rec, uint8_t typ)
+int reftable_record_init(struct reftable_record *rec, uint8_t typ)
{
memset(rec, 0, sizeof(*rec));
rec->type = typ;
@@ -1309,11 +1312,11 @@ void reftable_record_init(struct reftable_record *rec, uint8_t typ)
case BLOCK_TYPE_REF:
case BLOCK_TYPE_LOG:
case BLOCK_TYPE_OBJ:
- return;
+ return 0;
case BLOCK_TYPE_INDEX:
reftable_buf_init(&rec->u.idx.last_key);
- return;
+ return 0;
default:
- BUG("unhandled record type");
+ return REFTABLE_API_ERROR;
}
}
diff --git a/reftable/record.h b/reftable/record.h
index c7755a4d75..867810a932 100644
--- a/reftable/record.h
+++ b/reftable/record.h
@@ -130,11 +130,11 @@ struct reftable_record {
} u;
};
-/* Initialize the reftable record for the given type */
-void reftable_record_init(struct reftable_record *rec, uint8_t typ);
+/* Initialize the reftable record for the given type. */
+int reftable_record_init(struct reftable_record *rec, uint8_t typ);
/* see struct record_vtable */
-int reftable_record_cmp(struct reftable_record *a, struct reftable_record *b);
+int reftable_record_cmp(struct reftable_record *a, struct reftable_record *b, int *cmp);
int reftable_record_equal(struct reftable_record *a, struct reftable_record *b, uint32_t hash_size);
int reftable_record_key(struct reftable_record *rec, struct reftable_buf *dest);
int reftable_record_copy_from(struct reftable_record *rec,
diff --git a/reftable/stack.c b/reftable/stack.c
index 6c4e8be19b..6dac015b47 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -48,6 +48,25 @@ static int stack_fsync(const struct reftable_write_options *opts, int fd)
return fsync(fd);
}
+static ssize_t reftable_write_data(int fd, const void *data, size_t size)
+{
+ size_t total_written = 0;
+ const char *p = data;
+
+ while (total_written < size) {
+ ssize_t bytes_written = write(fd, p, size - total_written);
+ if (bytes_written < 0 && (errno == EAGAIN || errno == EINTR))
+ continue;
+ if (bytes_written < 0)
+ return REFTABLE_IO_ERROR;
+
+ total_written += bytes_written;
+ p += bytes_written;
+ }
+
+ return total_written;
+}
+
struct fd_writer {
const struct reftable_write_options *opts;
int fd;
@@ -56,7 +75,7 @@ struct fd_writer {
static ssize_t fd_writer_write(void *arg, const void *data, size_t sz)
{
struct fd_writer *writer = arg;
- return write_in_full(writer->fd, data, sz);
+ return reftable_write_data(writer->fd, data, sz);
}
static int fd_writer_flush(void *arg)
@@ -115,13 +134,16 @@ out:
static int fd_read_lines(int fd, char ***namesp)
{
- off_t size = lseek(fd, 0, SEEK_END);
char *buf = NULL;
int err = 0;
+ off_t size;
+
+ size = lseek(fd, 0, SEEK_END);
if (size < 0) {
err = REFTABLE_IO_ERROR;
goto done;
}
+
err = lseek(fd, 0, SEEK_SET);
if (err < 0) {
err = REFTABLE_IO_ERROR;
@@ -134,9 +156,16 @@ static int fd_read_lines(int fd, char ***namesp)
goto done;
}
- if (read_in_full(fd, buf, size) != size) {
- err = REFTABLE_IO_ERROR;
- goto done;
+ for (off_t total_read = 0; total_read < size; ) {
+ ssize_t bytes_read = read(fd, buf + total_read, size - total_read);
+ if (bytes_read < 0 && (errno == EAGAIN || errno == EINTR))
+ continue;
+ if (bytes_read < 0 || !bytes_read) {
+ err = REFTABLE_IO_ERROR;
+ goto done;
+ }
+
+ total_read += bytes_read;
}
buf[size] = 0;
@@ -494,8 +523,8 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
close(fd);
fd = -1;
- delay = delay + (delay * git_rand(CSPRNG_BYTES_INSECURE)) / UINT32_MAX + 1;
- sleep_millisec(delay);
+ delay = delay + (delay * reftable_rand()) / UINT32_MAX + 1;
+ poll(NULL, 0, delay);
}
out:
@@ -659,7 +688,7 @@ int reftable_stack_add(struct reftable_stack *st,
static int format_name(struct reftable_buf *dest, uint64_t min, uint64_t max)
{
char buf[100];
- uint32_t rnd = git_rand(CSPRNG_BYTES_INSECURE);
+ uint32_t rnd = reftable_rand();
snprintf(buf, sizeof(buf), "0x%012" PRIx64 "-0x%012" PRIx64 "-%08x",
min, max, rnd);
reftable_buf_reset(dest);
@@ -774,7 +803,8 @@ int reftable_addition_commit(struct reftable_addition *add)
goto done;
}
- err = write_in_full(add->tables_list_lock.fd, table_list.buf, table_list.len);
+ err = reftable_write_data(add->tables_list_lock.fd,
+ table_list.buf, table_list.len);
reftable_buf_release(&table_list);
if (err < 0) {
err = REFTABLE_IO_ERROR;
@@ -1460,8 +1490,8 @@ static int stack_compact_range(struct reftable_stack *st,
goto done;
}
- err = write_in_full(tables_list_lock.fd,
- tables_list_buf.buf, tables_list_buf.len);
+ err = reftable_write_data(tables_list_lock.fd,
+ tables_list_buf.buf, tables_list_buf.len);
if (err < 0) {
err = REFTABLE_IO_ERROR;
unlink(new_table_path.buf);
diff --git a/reftable/system.c b/reftable/system.c
index adf8e4d30b..1ee268b125 100644
--- a/reftable/system.c
+++ b/reftable/system.c
@@ -1,9 +1,16 @@
+#include "../git-compat-util.h"
+
#include "system.h"
#include "basics.h"
#include "reftable-error.h"
#include "../lockfile.h"
#include "../tempfile.h"
+uint32_t reftable_rand(void)
+{
+ return git_rand(CSPRNG_BYTES_INSECURE);
+}
+
int tmpfile_from_pattern(struct reftable_tmpfile *out, const char *pattern)
{
struct tempfile *tempfile;
diff --git a/reftable/system.h b/reftable/system.h
index d02eacea8f..072d9daea0 100644
--- a/reftable/system.h
+++ b/reftable/system.h
@@ -11,10 +11,17 @@ https://developers.google.com/open-source/licenses/bsd
/* This header glues the reftable library to the rest of Git */
-#include "git-compat-util.h"
+#define MINGW_DONT_HANDLE_IN_USE_ERROR
+#include "compat/posix.h"
#include "compat/zlib-compat.h"
/*
+ * Return a random 32 bit integer. This function is expected to return
+ * pre-seeded data.
+ */
+uint32_t reftable_rand(void);
+
+/*
* An implementation-specific temporary file. By making this specific to the
* implementation it becomes possible to tie temporary files into any kind of
* signal or atexit handlers for cleanup on abnormal situations.
diff --git a/reftable/writer.c b/reftable/writer.c
index f3ab1035d6..075ea8661b 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -99,9 +99,9 @@ static int writer_write_header(struct reftable_writer *w, uint8_t *dest)
dest[4] = writer_version(w);
- put_be24(dest + 5, w->opts.block_size);
- put_be64(dest + 8, w->min_update_index);
- put_be64(dest + 16, w->max_update_index);
+ reftable_put_be24(dest + 5, w->opts.block_size);
+ reftable_put_be64(dest + 8, w->min_update_index);
+ reftable_put_be64(dest + 16, w->max_update_index);
if (writer_version(w) == 2) {
uint32_t hash_id;
@@ -116,7 +116,7 @@ static int writer_write_header(struct reftable_writer *w, uint8_t *dest)
return -1;
}
- put_be32(dest + 24, hash_id);
+ reftable_put_be32(dest + 24, hash_id);
}
return header_size(writer_version(w));
@@ -158,7 +158,7 @@ int reftable_writer_new(struct reftable_writer **out,
opts = *_opts;
options_set_defaults(&opts);
if (opts.block_size >= (1 << 24))
- BUG("configured block size exceeds 16MB");
+ return REFTABLE_API_ERROR;
reftable_buf_init(&wp->block_writer_data.last_key);
reftable_buf_init(&wp->last_key);
@@ -302,19 +302,19 @@ static int writer_add_record(struct reftable_writer *w,
}
if (block_writer_type(w->block_writer) != reftable_record_type(rec))
- BUG("record of type %d added to writer of type %d",
- reftable_record_type(rec), block_writer_type(w->block_writer));
+ return REFTABLE_API_ERROR;
/*
* Try to add the record to the writer. If this succeeds then we're
* done. Otherwise the block writer may have hit the block size limit
* and needs to be flushed.
*/
- if (!block_writer_add(w->block_writer, rec)) {
- err = 0;
+ err = block_writer_add(w->block_writer, rec);
+ if (err == 0)
goto done;
- }
+ if (err != REFTABLE_ENTRY_TOO_BIG_ERROR)
+ goto done;
/*
* The current block is full, so we need to flush and reinitialize the
* writer to start writing the next block.
@@ -329,16 +329,10 @@ static int writer_add_record(struct reftable_writer *w,
/*
* Try to add the record to the writer again. If this still fails then
* the record does not fit into the block size.
- *
- * TODO: it would be great to have `block_writer_add()` return proper
- * error codes so that we don't have to second-guess the failure
- * mode here.
*/
err = block_writer_add(w->block_writer, rec);
- if (err) {
- err = REFTABLE_ENTRY_TOO_BIG_ERROR;
+ if (err)
goto done;
- }
done:
return err;
@@ -625,10 +619,22 @@ static void write_object_record(void *void_arg, void *key)
if (arg->err < 0)
goto done;
+ /*
+ * Try to add the record to the writer. If this succeeds then we're
+ * done. Otherwise the block writer may have hit the block size limit
+ * and needs to be flushed.
+ */
arg->err = block_writer_add(arg->w->block_writer, &rec);
if (arg->err == 0)
goto done;
+ if (arg->err != REFTABLE_ENTRY_TOO_BIG_ERROR)
+ goto done;
+
+ /*
+ * The current block is full, so we need to flush and reinitialize the
+ * writer to start writing the next block.
+ */
arg->err = writer_flush_block(arg->w);
if (arg->err < 0)
goto done;
@@ -637,10 +643,17 @@ static void write_object_record(void *void_arg, void *key)
if (arg->err < 0)
goto done;
+ /*
+ * If this still fails then we may need to reset record's offset
+ * length to reduce the data size to be written.
+ */
arg->err = block_writer_add(arg->w->block_writer, &rec);
if (arg->err == 0)
goto done;
+ if (arg->err != REFTABLE_ENTRY_TOO_BIG_ERROR)
+ goto done;
+
rec.u.obj.offset_len = 0;
arg->err = block_writer_add(arg->w->block_writer, &rec);
@@ -650,7 +663,7 @@ static void write_object_record(void *void_arg, void *key)
done:;
}
-static void object_record_free(void *void_arg UNUSED, void *key)
+static void object_record_free(void *void_arg REFTABLE_UNUSED, void *key)
{
struct obj_index_tree_node *entry = key;
@@ -731,19 +744,19 @@ int reftable_writer_close(struct reftable_writer *w)
}
p += writer_write_header(w, footer);
- put_be64(p, w->stats.ref_stats.index_offset);
+ reftable_put_be64(p, w->stats.ref_stats.index_offset);
p += 8;
- put_be64(p, (w->stats.obj_stats.offset) << 5 | w->stats.object_id_len);
+ reftable_put_be64(p, (w->stats.obj_stats.offset) << 5 | w->stats.object_id_len);
p += 8;
- put_be64(p, w->stats.obj_stats.index_offset);
+ reftable_put_be64(p, w->stats.obj_stats.index_offset);
p += 8;
- put_be64(p, w->stats.log_stats.offset);
+ reftable_put_be64(p, w->stats.log_stats.offset);
p += 8;
- put_be64(p, w->stats.log_stats.index_offset);
+ reftable_put_be64(p, w->stats.log_stats.index_offset);
p += 8;
- put_be32(p, crc32(0, footer, p - footer));
+ reftable_put_be32(p, crc32(0, footer, p - footer));
p += 4;
err = w->flush(w->write_arg);
diff --git a/remote-curl.c b/remote-curl.c
index 1273507a96..590b228f67 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -1239,7 +1239,7 @@ static int fetch_git(struct discovery *heads,
packet_buf_flush(&preamble);
memset(&rpc, 0, sizeof(rpc));
- rpc.service_name = "git-upload-pack",
+ rpc.service_name = "git-upload-pack";
rpc.gzip_request = 1;
err = rpc_service(&rpc, heads, args.v, &preamble, &rpc_result);
@@ -1401,7 +1401,7 @@ static int push_git(struct discovery *heads, int nr_spec, const char **specs)
packet_buf_flush(&preamble);
memset(&rpc, 0, sizeof(rpc));
- rpc.service_name = "git-receive-pack",
+ rpc.service_name = "git-receive-pack";
err = rpc_service(&rpc, heads, args.v, &preamble, &rpc_result);
if (rpc_result.len)
diff --git a/remote.c b/remote.c
index e609cf5c56..9fa3614e7a 100644
--- a/remote.c
+++ b/remote.c
@@ -12,7 +12,7 @@
#include "refs.h"
#include "refspec.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "path.h"
#include "commit.h"
#include "diff.h"
@@ -143,8 +143,8 @@ static struct remote *make_remote(struct remote_state *remote_state,
ret->prune = -1; /* unspecified */
ret->prune_tags = -1; /* unspecified */
ret->name = xstrndup(name, len);
- refspec_init(&ret->push, REFSPEC_PUSH);
- refspec_init(&ret->fetch, REFSPEC_FETCH);
+ refspec_init_push(&ret->push);
+ refspec_init_fetch(&ret->fetch);
string_list_init_dup(&ret->server_options);
ALLOC_GROW(remote_state->remotes, remote_state->remotes_nr + 1,
@@ -2297,7 +2297,7 @@ struct ref *get_local_heads(void)
struct ref *guess_remote_head(const struct ref *head,
const struct ref *refs,
- int all)
+ unsigned flags)
{
const struct ref *r;
struct ref *list = NULL;
@@ -2315,8 +2315,10 @@ struct ref *guess_remote_head(const struct ref *head,
return copy_ref(find_ref_by_name(refs, head->symref));
/* If a remote branch exists with the default branch name, let's use it. */
- if (!all) {
- char *default_branch = repo_default_branch_name(the_repository, 0);
+ if (!(flags & REMOTE_GUESS_HEAD_ALL)) {
+ char *default_branch =
+ repo_default_branch_name(the_repository,
+ flags & REMOTE_GUESS_HEAD_QUIET);
char *ref = xstrfmt("refs/heads/%s", default_branch);
r = find_ref_by_name(refs, ref);
@@ -2339,7 +2341,7 @@ struct ref *guess_remote_head(const struct ref *head,
oideq(&r->old_oid, &head->old_oid)) {
*tail = copy_ref(r);
tail = &((*tail)->next);
- if (!all)
+ if (!(flags & REMOTE_GUESS_HEAD_ALL))
break;
}
}
diff --git a/remote.h b/remote.h
index 6be5031f64..7e4943ae3a 100644
--- a/remote.h
+++ b/remote.h
@@ -387,15 +387,18 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
int show_divergence_advice);
struct ref *get_local_heads(void);
+
/*
* Find refs from a list which are likely to be pointed to by the given HEAD
- * ref. If 'all' is false, returns the most likely ref; otherwise, returns a
- * list of all candidate refs. If no match is found (or 'head' is NULL),
- * returns NULL. All returns are newly allocated and should be freed.
+ * ref. If REMOTE_GUESS_HEAD_ALL is set, return a list of all candidate refs;
+ * otherwise, return the most likely ref. If no match is found (or 'head' is
+ * NULL), returns NULL. All returns are newly allocated and should be freed.
*/
+#define REMOTE_GUESS_HEAD_ALL (1 << 0)
+#define REMOTE_GUESS_HEAD_QUIET (1 << 1)
struct ref *guess_remote_head(const struct ref *head,
const struct ref *refs,
- int all);
+ unsigned flags);
/* Return refs which no longer exist on remote */
struct ref *get_stale_heads(struct refspec *rs, struct ref *fetch_map);
diff --git a/replace-object.c b/replace-object.c
index 9a3cdd809a..7b8a09b5cb 100644
--- a/replace-object.c
+++ b/replace-object.c
@@ -2,7 +2,7 @@
#include "gettext.h"
#include "hex.h"
#include "oidmap.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "replace-object.h"
#include "refs.h"
#include "repository.h"
diff --git a/replace-object.h b/replace-object.h
index 66c41b938b..ba478eb30c 100644
--- a/replace-object.h
+++ b/replace-object.h
@@ -3,7 +3,7 @@
#include "oidmap.h"
#include "repository.h"
-#include "object-store-ll.h"
+#include "object-store.h"
struct replace_object {
struct oidmap_entry original;
diff --git a/repo-settings.c b/repo-settings.c
index 67e9cfd2e6..4129f8fb2b 100644
--- a/repo-settings.c
+++ b/repo-settings.c
@@ -20,6 +20,13 @@ static void repo_cfg_int(struct repository *r, const char *key, int *dest,
*dest = def;
}
+static void repo_cfg_ulong(struct repository *r, const char *key, unsigned long *dest,
+ unsigned long def)
+{
+ if (repo_config_get_ulong(r, key, dest))
+ *dest = def;
+}
+
void prepare_repo_settings(struct repository *r)
{
int experimental;
@@ -151,6 +158,19 @@ void repo_settings_clear(struct repository *r)
r->settings = empty;
}
+unsigned long repo_settings_get_big_file_threshold(struct repository *repo)
+{
+ if (!repo->settings.big_file_threshold)
+ repo_cfg_ulong(repo, "core.bigfilethreshold",
+ &repo->settings.big_file_threshold, 512 * 1024 * 1024);
+ return repo->settings.big_file_threshold;
+}
+
+void repo_settings_set_big_file_threshold(struct repository *repo, unsigned long value)
+{
+ repo->settings.big_file_threshold = value;
+}
+
enum log_refs_config repo_settings_get_log_all_ref_updates(struct repository *repo)
{
const char *value;
diff --git a/repo-settings.h b/repo-settings.h
index ddc11967e0..2bf24b2597 100644
--- a/repo-settings.h
+++ b/repo-settings.h
@@ -64,6 +64,7 @@ struct repo_settings {
size_t delta_base_cache_limit;
size_t packed_git_window_size;
size_t packed_git_limit;
+ unsigned long big_file_threshold;
char *hooks_path;
};
@@ -88,6 +89,10 @@ int repo_settings_get_warn_ambiguous_refs(struct repository *repo);
/* Read the value for "core.hooksPath". */
const char *repo_settings_get_hooks_path(struct repository *repo);
+/* Read and set the value for "core.bigFileThreshold". */
+unsigned long repo_settings_get_big_file_threshold(struct repository *repo);
+void repo_settings_set_big_file_threshold(struct repository *repo, unsigned long value);
+
/* Read, set or reset the value for "core.sharedRepository". */
int repo_settings_get_shared_repository(struct repository *repo);
void repo_settings_set_shared_repository(struct repository *repo, int value);
diff --git a/repository.c b/repository.c
index 6cbaf2e3da..9b3d6665fc 100644
--- a/repository.c
+++ b/repository.c
@@ -1,7 +1,7 @@
#include "git-compat-util.h"
#include "abspath.h"
#include "repository.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "config.h"
#include "object.h"
#include "lockfile.h"
diff --git a/rerere.c b/rerere.c
index 740e8ad1a0..3cd37c5f0a 100644
--- a/rerere.c
+++ b/rerere.c
@@ -18,7 +18,7 @@
#include "path.h"
#include "pathspec.h"
#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "strmap.h"
#define RESOLVED 0
@@ -860,7 +860,7 @@ static int do_plain_rerere(struct repository *r,
string_list_insert(rr, path)->util = id;
/* Ensure that the directory exists. */
- mkdir_in_gitdir(rerere_path(&buf, id, NULL));
+ safe_create_dir_in_gitdir(the_repository, rerere_path(&buf, id, NULL));
}
for (i = 0; i < rr->nr; i++)
@@ -895,7 +895,8 @@ static int is_rerere_enabled(void)
if (rerere_enabled < 0)
return rr_cache_exists;
- if (!rr_cache_exists && mkdir_in_gitdir(git_path_rr_cache()))
+ if (!rr_cache_exists &&
+ safe_create_dir_in_gitdir(the_repository, git_path_rr_cache()))
die(_("could not create directory '%s'"), git_path_rr_cache());
return 1;
}
diff --git a/reset.c b/reset.c
index b22b1be792..bb59027181 100644
--- a/reset.c
+++ b/reset.c
@@ -80,7 +80,7 @@ static int update_refs(const struct reset_head_opts *opts,
}
if (!ret && run_hook)
run_hooks_l(the_repository, "post-checkout",
- oid_to_hex(head ? head : null_oid()),
+ oid_to_hex(head ? head : null_oid(the_hash_algo)),
oid_to_hex(oid), "1", NULL);
strbuf_release(&msg);
return ret;
diff --git a/revision.c b/revision.c
index c4390f0938..2c36a9c179 100644
--- a/revision.c
+++ b/revision.c
@@ -8,7 +8,7 @@
#include "hex.h"
#include "object-name.h"
#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "oidset.h"
#include "tag.h"
#include "blob.h"
@@ -59,14 +59,6 @@ implement_shared_commit_slab(revision_sources, char *);
static inline int want_ancestry(const struct rev_info *revs);
-void show_object_with_name(FILE *out, struct object *obj, const char *name)
-{
- fprintf(out, "%s ", oid_to_hex(&obj->oid));
- for (const char *p = name; *p && *p != '\n'; p++)
- fputc(*p, out);
- fputc('\n', out);
-}
-
static void mark_blob_uninteresting(struct blob *blob)
{
if (!blob)
@@ -2488,10 +2480,12 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
die(_("options '%s' and '%s' cannot be used together"),
"--left-only", "--right-only/--cherry");
revs->left_only = 1;
+ revs->limited = 1;
} else if (!strcmp(arg, "--right-only")) {
if (revs->left_only)
die(_("options '%s' and '%s' cannot be used together"), "--right-only", "--left-only");
revs->right_only = 1;
+ revs->limited = 1;
} else if (!strcmp(arg, "--cherry")) {
if (revs->left_only)
die(_("options '%s' and '%s' cannot be used together"), "--cherry", "--left-only");
@@ -3612,7 +3606,8 @@ static void set_children(struct rev_info *revs)
void reset_revision_walk(void)
{
- clear_object_flags(SEEN | ADDED | SHOWN | TOPO_WALK_EXPLORED | TOPO_WALK_INDEGREE);
+ clear_object_flags(the_repository,
+ SEEN | ADDED | SHOWN | TOPO_WALK_EXPLORED | TOPO_WALK_INDEGREE);
}
static int mark_uninteresting(const struct object_id *oid,
diff --git a/revision.h b/revision.h
index 71e984c452..21c6a69899 100644
--- a/revision.h
+++ b/revision.h
@@ -489,8 +489,6 @@ void mark_parents_uninteresting(struct rev_info *revs, struct commit *commit);
void mark_tree_uninteresting(struct repository *r, struct tree *tree);
void mark_trees_uninteresting_sparse(struct repository *r, struct oidset *trees);
-void show_object_with_name(FILE *, struct object *, const char *);
-
/**
* Helpers to check if a reference should be excluded.
*/
diff --git a/scalar.c b/scalar.c
index da42b4be0c..d359f08bb8 100644
--- a/scalar.c
+++ b/scalar.c
@@ -241,7 +241,7 @@ static int add_or_remove_enlistment(int add)
static int start_fsmonitor_daemon(void)
{
- assert(have_fsmonitor_support());
+ ASSERT(have_fsmonitor_support());
if (fsmonitor_ipc__get_state() != IPC_STATE__LISTENING)
return run_git("fsmonitor--daemon", "start", NULL);
@@ -251,7 +251,7 @@ static int start_fsmonitor_daemon(void)
static int stop_fsmonitor_daemon(void)
{
- assert(have_fsmonitor_support());
+ ASSERT(have_fsmonitor_support());
if (fsmonitor_ipc__get_state() == IPC_STATE__LISTENING)
return run_git("fsmonitor--daemon", "stop", NULL);
diff --git a/send-pack.c b/send-pack.c
index 856a65d5f5..5005689cb5 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -4,7 +4,7 @@
#include "date.h"
#include "gettext.h"
#include "hex.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "pkt-line.h"
#include "sideband.h"
#include "run-command.h"
diff --git a/sequencer.c b/sequencer.c
index ad0ab75c8d..b5c4043757 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -13,7 +13,7 @@
#include "dir.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "object.h"
#include "pager.h"
#include "commit.h"
@@ -265,8 +265,8 @@ static struct update_ref_record *init_update_ref_record(const char *ref)
CALLOC_ARRAY(rec, 1);
- oidcpy(&rec->before, null_oid());
- oidcpy(&rec->after, null_oid());
+ oidcpy(&rec->before, null_oid(the_hash_algo));
+ oidcpy(&rec->after, null_oid(the_hash_algo));
/* This may fail, but that's fine, we will keep the null OID. */
refs_read_ref(get_main_ref_store(the_repository), ref, &rec->before);
@@ -667,7 +667,7 @@ static int fast_forward_to(struct repository *r,
if (!transaction ||
ref_transaction_update(transaction, "HEAD",
to, unborn && !is_rebase_i(opts) ?
- null_oid() : from, NULL, NULL,
+ null_oid(the_hash_algo) : from, NULL, NULL,
0, sb.buf, &err) ||
ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
@@ -781,28 +781,19 @@ static int do_recursive_merge(struct repository *r,
for (i = 0; i < opts->xopts.nr; i++)
parse_merge_opt(&o, opts->xopts.v[i]);
- if (!opts->strategy || !strcmp(opts->strategy, "ort")) {
- memset(&result, 0, sizeof(result));
- merge_incore_nonrecursive(&o, base_tree, head_tree, next_tree,
- &result);
- show_output = !is_rebase_i(opts) || !result.clean;
- /*
- * TODO: merge_switch_to_result will update index/working tree;
- * we only really want to do that if !result.clean || this is
- * the final patch to be picked. But determining this is the
- * final patch would take some work, and "head_tree" would need
- * to be replace with the tree the index matched before we
- * started doing any picks.
- */
- merge_switch_to_result(&o, head_tree, &result, 1, show_output);
- clean = result.clean;
- } else {
- ensure_full_index(r->index);
- clean = merge_trees(&o, head_tree, next_tree, base_tree);
- if (is_rebase_i(opts) && clean <= 0)
- fputs(o.obuf.buf, stdout);
- strbuf_release(&o.obuf);
- }
+ memset(&result, 0, sizeof(result));
+ merge_incore_nonrecursive(&o, base_tree, head_tree, next_tree, &result);
+ show_output = !is_rebase_i(opts) || !result.clean;
+ /*
+ * TODO: merge_switch_to_result will update index/working tree;
+ * we only really want to do that if !result.clean || this is
+ * the final patch to be picked. But determining this is the
+ * final patch would take some work, and "head_tree" would need
+ * to be replace with the tree the index matched before we
+ * started doing any picks.
+ */
+ merge_switch_to_result(&o, head_tree, &result, 1, show_output);
+ clean = result.clean;
if (clean < 0) {
rollback_lock_file(&index_lock);
return clean;
@@ -1301,7 +1292,7 @@ int update_head_with_reflog(const struct commit *old_head,
0, err);
if (!transaction ||
ref_transaction_update(transaction, "HEAD", new_head,
- old_head ? &old_head->object.oid : null_oid(),
+ old_head ? &old_head->object.oid : null_oid(the_hash_algo),
NULL, NULL, 0, sb.buf, err) ||
ref_transaction_commit(transaction, err)) {
ret = -1;
@@ -4328,20 +4319,13 @@ static int do_merge(struct repository *r,
o.branch2 = ref_name.buf;
o.buffer_output = 2;
- if (!opts->strategy || !strcmp(opts->strategy, "ort")) {
- /*
- * TODO: Should use merge_incore_recursive() and
- * merge_switch_to_result(), skipping the call to
- * merge_switch_to_result() when we don't actually need to
- * update the index and working copy immediately.
- */
- ret = merge_ort_recursive(&o,
- head_commit, merge_commit, bases,
- &i);
- } else {
- ret = merge_recursive(&o, head_commit, merge_commit, bases,
- &i);
- }
+ /*
+ * TODO: Should use merge_incore_recursive() and
+ * merge_switch_to_result(), skipping the call to
+ * merge_switch_to_result() when we don't actually need to
+ * update the index and working copy immediately.
+ */
+ ret = merge_ort_recursive(&o, head_commit, merge_commit, bases, &i);
if (ret <= 0)
fputs(o.obuf.buf, stdout);
strbuf_release(&o.obuf);
@@ -4352,7 +4336,7 @@ static int do_merge(struct repository *r,
goto leave_merge;
}
/*
- * The return value of merge_recursive() is 1 on clean, and 0 on
+ * The return value of merge_ort_recursive() is 1 on clean, and 0 on
* unclean merge.
*
* Let's reverse that, so that do_merge() returns 0 upon success and
@@ -4411,7 +4395,7 @@ static int write_update_refs_state(struct string_list *refs_to_oids)
goto cleanup;
}
- if (safe_create_leading_directories(path)) {
+ if (safe_create_leading_directories(the_repository, path)) {
result = error(_("unable to create leading directories of %s"),
path);
goto cleanup;
@@ -4677,13 +4661,13 @@ static void create_autostash_internal(struct repository *r,
strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
if (path) {
- if (safe_create_leading_directories_const(path))
+ if (safe_create_leading_directories_const(the_repository, 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);
+ &oid, null_oid(the_hash_algo), 0, UPDATE_REFS_DIE_ON_ERR);
}
printf(_("Created autostash: %s\n"), buf.buf);
@@ -4965,7 +4949,7 @@ static int pick_commits(struct repository *r,
ctx->reflog_message = sequencer_reflog_action(opts);
if (opts->allow_ff)
- assert(!(opts->signoff || opts->no_commit ||
+ ASSERT(!(opts->signoff || opts->no_commit ||
opts->record_origin || should_edit(opts) ||
opts->committer_date_is_author_date ||
opts->ignore_date));
diff --git a/server-info.c b/server-info.c
index 1ca0e00d51..d6cd20a39d 100644
--- a/server-info.c
+++ b/server-info.c
@@ -11,7 +11,7 @@
#include "packfile.h"
#include "path.h"
#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "server-info.h"
#include "strbuf.h"
#include "tempfile.h"
@@ -88,7 +88,7 @@ static int update_info_file(struct repository *r, char *path,
.old_sb = STRBUF_INIT
};
- safe_create_leading_directories(path);
+ safe_create_leading_directories(r, path);
f = mks_tempfile_m(tmp, 0666);
if (!f)
goto out;
diff --git a/shallow.c b/shallow.c
index 4bd9342c9a..2f82ebd6e3 100644
--- a/shallow.c
+++ b/shallow.c
@@ -5,7 +5,7 @@
#include "repository.h"
#include "tempfile.h"
#include "lockfile.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "commit.h"
#include "tag.h"
#include "pkt-line.h"
@@ -226,7 +226,7 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,
* SHALLOW (excluded) and NOT_SHALLOW (included) should not be
* set at this point. But better be safe than sorry.
*/
- clear_object_flags(both_flags);
+ clear_object_flags(the_repository, both_flags);
is_repository_shallow(the_repository); /* make sure shallows are read */
@@ -613,9 +613,9 @@ static void paint_down(struct paint_info *info, const struct object_id *oid,
}
}
- nr = get_max_object_index();
+ nr = get_max_object_index(the_repository);
for (i = 0; i < nr; i++) {
- struct object *o = get_indexed_object(i);
+ struct object *o = get_indexed_object(the_repository, i);
if (o && o->type == OBJ_COMMIT)
o->flags &= ~SEEN;
}
@@ -675,9 +675,9 @@ void assign_shallow_commits_to_refs(struct shallow_info *info,
* Prepare the commit graph to track what refs can reach what
* (new) shallow commits.
*/
- nr = get_max_object_index();
+ nr = get_max_object_index(the_repository);
for (i = 0; i < nr; i++) {
- struct object *o = get_indexed_object(i);
+ struct object *o = get_indexed_object(the_repository, i);
if (!o || o->type != OBJ_COMMIT)
continue;
diff --git a/streaming.c b/streaming.c
index 38839511af..127d6b5d6a 100644
--- a/streaming.c
+++ b/streaming.c
@@ -10,7 +10,7 @@
#include "streaming.h"
#include "repository.h"
#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "replace-object.h"
#include "packfile.h"
@@ -431,7 +431,8 @@ static int istream_source(struct git_istream *st,
st->open = open_istream_loose;
return 0;
case OI_PACKED:
- if (!oi.u.packed.is_delta && big_file_threshold < size) {
+ if (!oi.u.packed.is_delta &&
+ repo_settings_get_big_file_threshold(the_repository) < size) {
st->u.in_pack.pack = oi.u.packed.pack;
st->u.in_pack.pos = oi.u.packed.offset;
st->open = open_istream_pack_non_delta;
diff --git a/submodule-config.c b/submodule-config.c
index a25059ed7f..8630e27947 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -13,7 +13,7 @@
#include "submodule.h"
#include "strbuf.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "parse-options.h"
#include "thread-utils.h"
#include "tree-walk.h"
@@ -831,7 +831,7 @@ static int gitmodules_cb(const char *var, const char *value,
parameter.cache = repo->submodule_cache;
parameter.treeish_name = NULL;
- parameter.gitmodules_oid = null_oid();
+ parameter.gitmodules_oid = null_oid(the_hash_algo);
parameter.overwrite = 1;
return parse_config(var, value, ctx, &parameter);
diff --git a/submodule.c b/submodule.c
index 0530e8cf24..ead3fb5dad 100644
--- a/submodule.c
+++ b/submodule.c
@@ -27,7 +27,7 @@
#include "parse-options.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "commit-reach.h"
#include "read-cache-ll.h"
#include "setup.h"
@@ -124,7 +124,7 @@ int update_path_in_gitmodules(const char *oldpath, const char *newpath)
if (is_gitmodules_unmerged(the_repository->index))
die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
- submodule = submodule_from_path(the_repository, null_oid(), oldpath);
+ submodule = submodule_from_path(the_repository, null_oid(the_hash_algo), oldpath);
if (!submodule || !submodule->name) {
warning(_("Could not find section in .gitmodules where path=%s"), oldpath);
return -1;
@@ -153,7 +153,7 @@ int remove_path_from_gitmodules(const char *path)
if (is_gitmodules_unmerged(the_repository->index))
die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
- submodule = submodule_from_path(the_repository, null_oid(), path);
+ submodule = submodule_from_path(the_repository, null_oid(the_hash_algo), path);
if (!submodule || !submodule->name) {
warning(_("Could not find section in .gitmodules where path=%s"), path);
return -1;
@@ -204,7 +204,7 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
const char *path)
{
const struct submodule *submodule = submodule_from_path(the_repository,
- null_oid(),
+ null_oid(the_hash_algo),
path);
if (submodule) {
const char *ignore;
@@ -312,7 +312,7 @@ int is_tree_submodule_active(struct repository *repo,
int is_submodule_active(struct repository *repo, const char *path)
{
- return is_tree_submodule_active(repo, null_oid(), path);
+ return is_tree_submodule_active(repo, null_oid(the_hash_algo), path);
}
int is_submodule_populated_gently(const char *path, int *return_error_code)
@@ -778,7 +778,7 @@ const struct submodule *submodule_from_ce(const struct cache_entry *ce)
if (!should_update_submodules())
return NULL;
- return submodule_from_path(the_repository, null_oid(), ce->name);
+ return submodule_from_path(the_repository, null_oid(the_hash_algo), ce->name);
}
@@ -1062,7 +1062,7 @@ static int submodule_needs_pushing(struct repository *r,
const char *path,
struct oid_array *commits)
{
- if (!submodule_has_commits(r, path, null_oid(), commits))
+ if (!submodule_has_commits(r, path, null_oid(the_hash_algo), commits))
/*
* NOTE: We do consider it safe to return "no" here. The
* correct answer would be "We do not know" instead of
@@ -1126,7 +1126,7 @@ int find_unpushed_submodules(struct repository *r,
const struct submodule *submodule;
const char *path = NULL;
- submodule = submodule_from_name(r, null_oid(), name->string);
+ submodule = submodule_from_name(r, null_oid(the_hash_algo), name->string);
if (submodule)
path = submodule->path;
else
@@ -1351,7 +1351,7 @@ static void calculate_changed_submodule_paths(struct repository *r,
const struct submodule *submodule;
const char *path = NULL;
- submodule = submodule_from_name(r, null_oid(), name->string);
+ submodule = submodule_from_name(r, null_oid(the_hash_algo), name->string);
if (submodule)
path = submodule->path;
else
@@ -1360,7 +1360,7 @@ static void calculate_changed_submodule_paths(struct repository *r,
if (!path)
continue;
- if (submodule_has_commits(r, path, null_oid(), &cs_data->new_commits)) {
+ if (submodule_has_commits(r, path, null_oid(the_hash_algo), &cs_data->new_commits)) {
changed_submodule_data_clear(cs_data);
*name->string = '\0';
}
@@ -1602,7 +1602,7 @@ get_fetch_task_from_index(struct submodule_parallel_fetch *spf,
if (!S_ISGITLINK(ce->ce_mode))
continue;
- task = fetch_task_create(spf, ce->name, null_oid());
+ task = fetch_task_create(spf, ce->name, null_oid(the_hash_algo));
if (!task)
continue;
@@ -2166,7 +2166,7 @@ int submodule_move_head(const char *path, const char *super_prefix,
if (old_head && !is_submodule_populated_gently(path, error_code_ptr))
return 0;
- sub = submodule_from_path(the_repository, null_oid(), path);
+ sub = submodule_from_path(the_repository, null_oid(the_hash_algo), path);
if (!sub)
BUG("could not get submodule information for '%s'", path);
@@ -2376,7 +2376,7 @@ static void relocate_single_git_dir_into_superproject(const char *path,
real_old_git_dir = real_pathdup(old_git_dir, 1);
- sub = submodule_from_path(the_repository, null_oid(), path);
+ sub = submodule_from_path(the_repository, null_oid(the_hash_algo), path);
if (!sub)
die(_("could not lookup name for submodule '%s'"), path);
@@ -2384,7 +2384,7 @@ static void relocate_single_git_dir_into_superproject(const char *path,
if (validate_submodule_git_dir(new_gitdir.buf, sub->name) < 0)
die(_("refusing to move '%s' into an existing git dir"),
real_old_git_dir);
- if (safe_create_leading_directories_const(new_gitdir.buf) < 0)
+ if (safe_create_leading_directories_const(the_repository, new_gitdir.buf) < 0)
die(_("could not create directory '%s'"), new_gitdir.buf);
real_new_git_dir = real_pathdup(new_gitdir.buf, 1);
@@ -2462,7 +2462,7 @@ void absorb_git_dir_into_superproject(const char *path,
* superproject did not rewrite the git file links yet,
* fix it now.
*/
- sub = submodule_from_path(the_repository, null_oid(), path);
+ sub = submodule_from_path(the_repository, null_oid(the_hash_algo), path);
if (!sub)
die(_("could not lookup name for submodule '%s'"), path);
submodule_name_to_gitdir(&sub_gitdir, the_repository, sub->name);
@@ -2594,7 +2594,7 @@ int submodule_to_gitdir(struct repository *repo,
strbuf_addstr(buf, git_dir);
}
if (!is_git_directory(buf->buf)) {
- sub = submodule_from_path(repo, null_oid(), submodule);
+ sub = submodule_from_path(repo, null_oid(the_hash_algo), submodule);
if (!sub) {
ret = -1;
goto cleanup;
diff --git a/t/Makefile b/t/Makefile
index 2994eb5fa9..791e0a0978 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -59,16 +59,21 @@ CHAINLINTSUPPRESS = GIT_TEST_EXT_CHAIN_LINT=0 && export GIT_TEST_EXT_CHAIN_LINT
all:: $(DEFAULT_TEST_TARGET)
-test: pre-clean check-chainlint check-meson $(TEST_LINT)
+test: pre-clean check-meson $(TEST_LINT)
$(CHAINLINTSUPPRESS) $(MAKE) aggregate-results-and-cleanup
+ifneq ($(PERL_PATH),)
+test: check-chainlint
+prove: check-chainlint
+endif
+
failed:
@failed=$$(cd '$(TEST_RESULTS_DIRECTORY_SQ)' && \
grep -l '^failed [1-9]' *.counts | \
sed -n 's/\.counts$$/.sh/p') && \
test -z "$$failed" || $(MAKE) $$failed
-prove: pre-clean check-chainlint $(TEST_LINT)
+prove: pre-clean $(TEST_LINT)
@echo "*** prove (shell & unit tests) ***"
@$(CHAINLINTSUPPRESS) TEST_OPTIONS='$(GIT_TEST_OPTS)' TEST_SHELL_PATH='$(TEST_SHELL_PATH_SQ)' $(PROVE) --exec ./run-test.sh $(GIT_PROVE_OPTS) $(T) $(UNIT_TESTS)
$(MAKE) clean-except-prove-cache
@@ -132,8 +137,13 @@ check-meson:
fi; \
done
-test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax \
+test-lint: test-lint-duplicates test-lint-executable \
test-lint-filenames
+ifneq ($(PERL_PATH),)
+test-lint: test-lint-shell-syntax
+else
+GIT_TEST_CHAIN_LINT = 0
+endif
ifneq ($(GIT_TEST_CHAIN_LINT),0)
test-lint: test-chainlint
endif
diff --git a/t/README b/t/README
index 53e5b4a710..e9ffd9a81c 100644
--- a/t/README
+++ b/t/README
@@ -818,7 +818,7 @@ Skipping tests
--------------
If you need to skip tests you should do so by using the three-arg form
-of the test_* functions (see the "Test harness library" section
+of the test_expect_* functions (see the "Test harness library" section
below), e.g.:
test_expect_success PERL 'I need Perl' '
@@ -965,6 +965,29 @@ see test-lib-functions.sh for the full list and their options.
test_done
fi
+ - test_lazy_prereq <prereq> <script>
+
+ Declare the way to determine if a test prerequisite <prereq> is
+ satisified or not, but delay the actual determination until the
+ prerequisite is actually used by "test_have_prereq" or the
+ three-arg form of the test_expect_* functions. For example, this
+ is how the SYMLINKS prerequisite is declared to see if the platform
+ supports symbolic links:
+
+ test_lazy_prereq SYMLINKS '
+ ln -s x y && test -h y
+ '
+
+ The script is lazily invoked when SYMLINKS prerequisite is first
+ queried by either "test_have_prereq SYMLINKS" or "test_expect_*
+ SYMLINKS ...". The script is run in a temporary directory inside
+ a subshell, so you do not have to worry about removing temporary
+ files you create there. When the script exits with status 0, the
+ prerequisite is set. Exiting with non-zero status other than 125
+ makes the prerequisite unsatisified. Exiting the script with 125
+ signals a programming error and is used to mark a prerequisite that
+ should not be used by test scripts.
+
- test_expect_code <exit-code> <command>
Run a command and ensure that it exits with the given exit code.
diff --git a/t/helper/test-pack-mtimes.c b/t/helper/test-pack-mtimes.c
index f8f9afbb5b..50f5941bff 100644
--- a/t/helper/test-pack-mtimes.c
+++ b/t/helper/test-pack-mtimes.c
@@ -3,7 +3,7 @@
#include "test-tool.h"
#include "hex.h"
#include "strbuf.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "packfile.h"
#include "pack-mtimes.h"
#include "setup.h"
diff --git a/t/helper/test-partial-clone.c b/t/helper/test-partial-clone.c
index a1af9710c3..34f1aee558 100644
--- a/t/helper/test-partial-clone.c
+++ b/t/helper/test-partial-clone.c
@@ -1,7 +1,7 @@
#include "test-tool.h"
#include "hex.h"
#include "repository.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "setup.h"
/*
diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c
index 72ac8d1b1b..086238c826 100644
--- a/t/helper/test-path-utils.c
+++ b/t/helper/test-path-utils.c
@@ -323,6 +323,19 @@ int cmd__path_utils(int argc, const char **argv)
return 0;
}
+ if (argc >= 2 && !strcmp(argv[1], "readlink")) {
+ struct strbuf target = STRBUF_INIT;
+ while (argc > 2) {
+ if (strbuf_readlink(&target, argv[2], 0) < 0)
+ die_errno("cannot read link at '%s'", argv[2]);
+ puts(target.buf);
+ argc--;
+ argv++;
+ }
+ strbuf_release(&target);
+ return 0;
+ }
+
if (argc >= 2 && !strcmp(argv[1], "absolute_path")) {
while (argc > 2) {
puts(absolute_path(argv[2]));
@@ -504,6 +517,25 @@ int cmd__path_utils(int argc, const char **argv)
return !!res;
}
+ if (argc > 1 && !strcmp(argv[1], "is_path_owned_by_current_user")) {
+ int res = 0;
+
+ for (int i = 2; i < argc; i++) {
+ struct strbuf buf = STRBUF_INIT;
+
+ if (is_path_owned_by_current_user(argv[i], &buf))
+ printf("'%s' is owned by current SID\n", argv[i]);
+ else {
+ printf("'%s' is not owned by current SID: %s\n", argv[i], buf.buf);
+ res = 1;
+ }
+
+ strbuf_release(&buf);
+ }
+
+ return res;
+ }
+
fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
argv[1] ? argv[1] : "(there was none)");
return 1;
diff --git a/t/helper/test-read-graph.c b/t/helper/test-read-graph.c
index 811dde1cb3..8b413b644b 100644
--- a/t/helper/test-read-graph.c
+++ b/t/helper/test-read-graph.c
@@ -3,7 +3,7 @@
#include "test-tool.h"
#include "commit-graph.h"
#include "repository.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "bloom.h"
#include "setup.h"
diff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c
index fc63236961..ac81390899 100644
--- a/t/helper/test-read-midx.c
+++ b/t/helper/test-read-midx.c
@@ -4,7 +4,7 @@
#include "hex.h"
#include "midx.h"
#include "repository.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "pack-bitmap.h"
#include "packfile.h"
#include "setup.h"
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index e00fce592b..4cfc7c90b5 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -5,7 +5,7 @@
#include "refs.h"
#include "setup.h"
#include "worktree.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "path.h"
#include "repository.h"
#include "strbuf.h"
@@ -179,7 +179,7 @@ static int cmd_for_each_ref__exclude(struct ref_store *refs, const char **argv)
static int cmd_resolve_ref(struct ref_store *refs, const char **argv)
{
- struct object_id oid = *null_oid();
+ struct object_id oid = *null_oid(the_hash_algo);
const char *refname = notnull(*argv++, "refname");
int resolve_flags = arg_flags(*argv++, "resolve-flags", empty_flags);
int flags;
diff --git a/t/helper/test-sha1.sh b/t/helper/test-sha1.sh
index bf387d3db1..f03b784ddc 100755
--- a/t/helper/test-sha1.sh
+++ b/t/helper/test-sha1.sh
@@ -15,7 +15,7 @@ do
{
test -z "$pfx" || echo "$pfx"
dd if=/dev/zero bs=1048576 count=$cnt 2>/dev/null |
- perl -pe 'y/\000/g/'
+ tr "\000" "g"
} | ./t/helper/test-tool $sha1 $cnt
)
if test "$expect" = "$actual"
@@ -61,7 +61,7 @@ do
{
test -z "$pfx" || echo "$pfx"
dd if=/dev/zero bs=1048576 count=$cnt 2>/dev/null |
- perl -pe 'y/\000/g/'
+ tr "\000" "g"
} | sha1sum |
sed -e 's/ .*//'
)
diff --git a/t/helper/test-submodule-nested-repo-config.c b/t/helper/test-submodule-nested-repo-config.c
index 6dce957153..2710341cd5 100644
--- a/t/helper/test-submodule-nested-repo-config.c
+++ b/t/helper/test-submodule-nested-repo-config.c
@@ -21,7 +21,7 @@ int cmd__submodule_nested_repo_config(int argc, const char **argv)
setup_git_directory();
- if (repo_submodule_init(&subrepo, the_repository, argv[1], null_oid())) {
+ if (repo_submodule_init(&subrepo, the_repository, argv[1], null_oid(the_hash_algo))) {
die_usage(argv, "Submodule not found.");
}
diff --git a/t/lib-diff.sh b/t/lib-diff.sh
index c4606bd4b7..12b3c8fcc6 100644
--- a/t/lib-diff.sh
+++ b/t/lib-diff.sh
@@ -21,8 +21,8 @@ compare_diff_raw_z () {
# Also we do not check SHA1 hash generation in this test, which
# is a job for t0000-basic.sh
- perl -pe 'y/\000/\012/' <"$1" | sed -e "$sanitize_diff_raw_z" >.tmp-1
- perl -pe 'y/\000/\012/' <"$2" | sed -e "$sanitize_diff_raw_z" >.tmp-2
+ tr "\000" "\012" <"$1" | sed -e "$sanitize_diff_raw_z" >.tmp-1
+ tr "\000" "\012" <"$2" | sed -e "$sanitize_diff_raw_z" >.tmp-2
test_cmp .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
}
diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh
index 3845b6ac44..937b876bd0 100644
--- a/t/lib-gpg.sh
+++ b/t/lib-gpg.sh
@@ -192,9 +192,5 @@ test_lazy_prereq GPGSSH_VERIFYTIME '
'
sanitize_pgp() {
- perl -ne '
- /^-----END PGP/ and $in_pgp = 0;
- print unless $in_pgp;
- /^-----BEGIN PGP/ and $in_pgp = 1;
- '
+ sed "/^-----BEGIN PGP/,/^-----END PGP/{/^-/p;d;}"
}
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index d83bafeab3..5091db949b 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -165,7 +165,7 @@ prepare_httpd() {
install_script broken-smart-http.sh
install_script error-smart-http.sh
install_script error.sh
- install_script apply-one-time-perl.sh
+ install_script apply-one-time-script.sh
install_script nph-custom-auth.sh
ln -s "$LIB_HTTPD_MODULE_PATH" "$HTTPD_ROOT_PATH/modules"
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
index 022276a6b9..e631ab0eb5 100644
--- a/t/lib-httpd/apache.conf
+++ b/t/lib-httpd/apache.conf
@@ -135,7 +135,7 @@ SetEnv PERL_PATH ${PERL_PATH}
SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
SetEnv GIT_HTTP_EXPORT_ALL
</LocationMatch>
-<LocationMatch /one_time_perl/>
+<LocationMatch /one_time_script/>
SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
SetEnv GIT_HTTP_EXPORT_ALL
</LocationMatch>
@@ -159,7 +159,7 @@ ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
ScriptAlias /broken_smart/ broken-smart-http.sh/
ScriptAlias /error_smart/ error-smart-http.sh/
ScriptAlias /error/ error.sh/
-ScriptAliasMatch /one_time_perl/(.*) apply-one-time-perl.sh/$1
+ScriptAliasMatch /one_time_script/(.*) apply-one-time-script.sh/$1
ScriptAliasMatch /custom_auth/(.*) nph-custom-auth.sh/$1
<Directory ${GIT_EXEC_PATH}>
Options FollowSymlinks
@@ -182,7 +182,7 @@ ScriptAliasMatch /custom_auth/(.*) nph-custom-auth.sh/$1
<Files error.sh>
Options ExecCGI
</Files>
-<Files apply-one-time-perl.sh>
+<Files apply-one-time-script.sh>
Options ExecCGI
</Files>
<Files ${GIT_EXEC_PATH}/git-http-backend>
diff --git a/t/lib-httpd/apply-one-time-perl.sh b/t/lib-httpd/apply-one-time-perl.sh
deleted file mode 100644
index d7f9fed6ae..0000000000
--- a/t/lib-httpd/apply-one-time-perl.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/sh
-
-# If "one-time-perl" exists in $HTTPD_ROOT_PATH, run perl on the HTTP response,
-# using the contents of "one-time-perl" as the perl command to be run. If the
-# response was modified as a result, delete "one-time-perl" so that subsequent
-# HTTP responses are no longer modified.
-#
-# This can be used to simulate the effects of the repository changing in
-# between HTTP request-response pairs.
-if test -f one-time-perl
-then
- LC_ALL=C
- export LC_ALL
-
- "$GIT_EXEC_PATH/git-http-backend" >out
- "$PERL_PATH" -pe "$(cat one-time-perl)" out >out_modified
-
- if cmp -s out out_modified
- then
- cat out
- else
- cat out_modified
- rm one-time-perl
- fi
-else
- "$GIT_EXEC_PATH/git-http-backend"
-fi
diff --git a/t/lib-httpd/apply-one-time-script.sh b/t/lib-httpd/apply-one-time-script.sh
new file mode 100644
index 0000000000..b1682944e2
--- /dev/null
+++ b/t/lib-httpd/apply-one-time-script.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+# If "one-time-script" exists in $HTTPD_ROOT_PATH, run the script on the HTTP
+# response. If the response was modified as a result, delete "one-time-script"
+# so that subsequent HTTP responses are no longer modified.
+#
+# This can be used to simulate the effects of the repository changing in
+# between HTTP request-response pairs.
+if test -f one-time-script
+then
+ LC_ALL=C
+ export LC_ALL
+
+ "$GIT_EXEC_PATH/git-http-backend" >out
+ ./one-time-script out >out_modified
+
+ if cmp -s out out_modified
+ then
+ cat out
+ else
+ cat out_modified
+ rm one-time-script
+ fi
+else
+ "$GIT_EXEC_PATH/git-http-backend"
+fi
diff --git a/t/lib-merge.sh b/t/lib-merge.sh
deleted file mode 100644
index 8734ebfc17..0000000000
--- a/t/lib-merge.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-# Helper functions used by merge tests.
-
-test_expect_merge_algorithm () {
- status_for_recursive=$1 status_for_ort=$2
- shift 2
-
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_expect_${status_for_ort} "$@"
- else
- test_expect_${status_for_recursive} "$@"
- fi
-}
diff --git a/t/lib-t6000.sh b/t/lib-t6000.sh
index fba6778ca3..35c5472465 100644
--- a/t/lib-t6000.sh
+++ b/t/lib-t6000.sh
@@ -109,13 +109,12 @@ check_output () {
# All alphanums translated into -'s which are then compressed and stripped
# from front and back.
name_from_description () {
- perl -pe '
- s/[^A-Za-z0-9.]/-/g;
- s/-+/-/g;
- s/-$//;
- s/^-//;
- y/A-Z/a-z/;
- '
+ sed \
+ -e 's/[^A-Za-z0-9.]/-/g' \
+ -e 's/--*/-/g' \
+ -e 's/-$//' \
+ -e 's/^-//' \
+ -e 'y/A-Z/a-z/'
}
diff --git a/t/meson.build b/t/meson.build
index 8b3aed14ea..bfb744e886 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -12,6 +12,8 @@ clar_test_suites = [
'unit-tests/u-strbuf.c',
'unit-tests/u-strcmp-offset.c',
'unit-tests/u-strvec.c',
+ 'unit-tests/u-trailer.c',
+ 'unit-tests/u-urlmatch-normalization.c',
]
clar_sources = [
@@ -60,8 +62,6 @@ unit_test_programs = [
'unit-tests/t-reftable-readwrite.c',
'unit-tests/t-reftable-record.c',
'unit-tests/t-reftable-stack.c',
- 'unit-tests/t-trailer.c',
- 'unit-tests/t-urlmatch-normalization.c',
]
foreach unit_test_program : unit_test_programs
diff --git a/t/perf/p7821-grep-engines-fixed.sh b/t/perf/p7821-grep-engines-fixed.sh
index 61e41b82cf..66bec284e3 100755
--- a/t/perf/p7821-grep-engines-fixed.sh
+++ b/t/perf/p7821-grep-engines-fixed.sh
@@ -7,7 +7,7 @@ git-grep. Make sure to include a leading space,
e.g. GIT_PERF_7821_GREP_OPTS=' -w'. See p7820-grep-engines.sh for more
options to try.
-If GIT_PERF_7821_THREADS is set to a list of threads (e.g. '1 4 8'
+If GIT_PERF_GREP_THREADS is set to a list of threads (e.g. '1 4 8'
etc.) we will test the patterns under those numbers of threads.
"
@@ -33,13 +33,13 @@ do
fi
if ! test_have_prereq PERF_GREP_ENGINES_THREADS
then
- test_perf $prereq "$engine grep$GIT_PERF_7821_GREP_OPTS $pattern" "
+ test_perf "$engine grep$GIT_PERF_7821_GREP_OPTS $pattern" --prereq "$prereq" "
git -c grep.patternType=$engine grep$GIT_PERF_7821_GREP_OPTS $pattern >'out.$engine' || :
"
else
for threads in $GIT_PERF_GREP_THREADS
do
- test_perf PTHREADS,$prereq "$engine grep$GIT_PERF_7821_GREP_OPTS $pattern with $threads threads" "
+ test_perf "$engine grep$GIT_PERF_7821_GREP_OPTS $pattern with $threads threads" --prereq "PTHREADS,$prereq" "
git -c grep.patternType=$engine -c grep.threads=$threads grep$GIT_PERF_7821_GREP_OPTS $pattern >'out.$engine.$threads' || :
"
done
diff --git a/t/perf/p9210-scalar.sh b/t/perf/p9210-scalar.sh
index 265f7cd1fe..56b075e906 100755
--- a/t/perf/p9210-scalar.sh
+++ b/t/perf/p9210-scalar.sh
@@ -7,7 +7,8 @@ test_perf_large_repo "$TRASH_DIRECTORY/to-clone"
test_expect_success 'enable server-side partial clone' '
git -C to-clone config uploadpack.allowFilter true &&
- git -C to-clone config uploadpack.allowAnySHA1InWant true
+ git -C to-clone config uploadpack.allowAnySHA1InWant true &&
+ git -C to-clone checkout -B test-branch
'
test_perf 'scalar clone' '
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index c49d9e0d38..f11a40811f 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -830,6 +830,14 @@ test_expect_success 'advice on unconfigured init.defaultBranch' '
test_grep "<YELLOW>hint: " decoded
'
+test_expect_success 'advice on unconfigured init.defaultBranch disabled' '
+ test_when_finished "rm -rf no-advice" &&
+
+ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
+ git -c advice.defaultBranchName=false init no-advice 2>err &&
+ test_grep ! "hint: " err
+'
+
test_expect_success 'overridden default main branch name (env)' '
test_config_global init.defaultBranch nmb &&
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=env git init main-branch-env &&
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index c9376dffb5..273d71411f 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -39,11 +39,11 @@ test_stderr () {
}
broken_c_unquote () {
- "$PERL_PATH" -pe 's/^"//; s/\\//; s/"$//; tr/\n/\0/' "$@"
+ sed -e 's/^"//' -e 's/\\//' -e 's/"$//' "$1" | tr '\n' '\0'
}
broken_c_unquote_verbose () {
- "$PERL_PATH" -pe 's/ "/ /; s/\\//; s/"$//; tr/:\t\n/\0/' "$@"
+ sed -e 's/ "/ /' -e 's/\\//' -e 's/"$//' "$1" | tr ':\t\n' '\000'
}
stderr_contains () {
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index 3f6433d304..bf10d253ec 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -20,8 +20,7 @@ EOF
generate_random_characters () {
LEN=$1
NAME=$2
- test-tool genrandom some-seed $LEN |
- perl -pe "s/./chr((ord($&) % 26) + ord('a'))/sge" >"$TEST_ROOT/$NAME"
+ test-tool genrandom some-seed | tr -dc 'a-z' | test_copy_bytes "$LEN" >"$TEST_ROOT/$NAME"
}
filter_git () {
@@ -841,7 +840,7 @@ test_expect_success 'process filter abort stops processing of all further files'
)
'
-test_expect_success PERL 'invalid process filter must fail (and not hang!)' '
+test_expect_success 'invalid process filter must fail (and not hang!)' '
test_config_global filter.protocol.process cat &&
test_config_global filter.protocol.required true &&
rm -rf repo &&
@@ -1111,19 +1110,19 @@ do
branch) opt='-f HEAD' ;;
esac
- test_expect_success PERL,TTY "delayed checkout shows progress by default on tty ($mode checkout)" '
+ test_expect_success TTY "delayed checkout shows progress by default on tty ($mode checkout)" '
test_delayed_checkout_progress test_terminal git checkout $opt
'
- test_expect_success PERL "delayed checkout omits progress on non-tty ($mode checkout)" '
+ test_expect_success "delayed checkout omits progress on non-tty ($mode checkout)" '
test_delayed_checkout_progress ! git checkout $opt
'
- test_expect_success PERL,TTY "delayed checkout omits progress with --quiet ($mode checkout)" '
+ test_expect_success TTY "delayed checkout omits progress with --quiet ($mode checkout)" '
test_delayed_checkout_progress ! test_terminal git checkout --quiet $opt
'
- test_expect_success PERL,TTY "delayed checkout honors --[no]-progress ($mode checkout)" '
+ test_expect_success TTY "delayed checkout honors --[no]-progress ($mode checkout)" '
test_delayed_checkout_progress ! test_terminal git checkout --no-progress $opt &&
test_delayed_checkout_progress test_terminal git checkout --quiet --progress $opt
'
diff --git a/t/t0090-cache-tree.sh b/t/t0090-cache-tree.sh
index ab80c9ef13..d901588294 100755
--- a/t/t0090-cache-tree.sh
+++ b/t/t0090-cache-tree.sh
@@ -128,7 +128,7 @@ test_expect_success 'second commit has cache-tree' '
test_cache_tree
'
-test_expect_success PERL 'commit --interactive gives cache-tree on partial commit' '
+test_expect_success 'commit --interactive gives cache-tree on partial commit' '
test_when_finished "git reset --hard" &&
cat <<-\EOT >foo.c &&
int foo()
@@ -162,7 +162,7 @@ test_expect_success PERL 'commit --interactive gives cache-tree on partial commi
test_cache_tree expected.status
'
-test_expect_success PERL 'commit -p with shrinking cache-tree' '
+test_expect_success 'commit -p with shrinking cache-tree' '
mkdir -p deep/very-long-subdir &&
echo content >deep/very-long-subdir/file &&
git add deep &&
diff --git a/t/t0210-trace2-normal.sh b/t/t0210-trace2-normal.sh
index 4287ed3fbb..96c68f65df 100755
--- a/t/t0210-trace2-normal.sh
+++ b/t/t0210-trace2-normal.sh
@@ -53,10 +53,41 @@ GIT_TRACE2_BRIEF=1 && export GIT_TRACE2_BRIEF
#
# Implicit return from cmd_<verb> function propagates <code>.
+scrub_normal () {
+ # Scrub the variable fields from the normal trace2 output to make
+ # testing easier:
+ #
+ # 1. Various messages include an elapsed time in the middle of the
+ # message. Replace the time with a placeholder to simplify our
+ # HEREDOC in the test script.
+ #
+ # 2. We expect:
+ #
+ # start <argv0> [<argv1> [<argv2> [...]]]
+ #
+ # where argv0 might be a relative or absolute path, with or
+ # without quotes, and platform dependent. Replace argv0 with a
+ # token for HEREDOC matching in the test script.
+ #
+ # 3. Likewise, the 'cmd_path' message breaks out argv[0].
+ #
+ # This line is only emitted when RUNTIME_PREFIX is defined,
+ # so just omit it for testing purposes.
+ #
+ # 4. 'cmd_ancestry' is not implemented everywhere, so for portability's
+ # sake, skip it when parsing normal.
+ sed \
+ -e 's/elapsed:[0-9]*\.[0-9][0-9]*\([eE][-+]\{0,1\}[0-9][0-9]*\)\{0,1\}/elapsed:_TIME_/g' \
+ -e "s/^start '[^']*' \(.*\)/start _EXE_ \1/" \
+ -e 's/^start [^ ][^ ]* \(.*\)/start _EXE_ \1/' \
+ -e '/^cmd_path/d' \
+ -e '/^cmd_ancestry/d'
+}
+
test_expect_success 'normal stream, return code 0' '
test_when_finished "rm trace.normal actual expect" &&
GIT_TRACE2="$(pwd)/trace.normal" test-tool trace2 001return 0 &&
- perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ scrub_normal <trace.normal >actual &&
cat >expect <<-EOF &&
version $V
start _EXE_ trace2 001return 0
@@ -70,7 +101,7 @@ test_expect_success 'normal stream, return code 0' '
test_expect_success 'normal stream, return code 1' '
test_when_finished "rm trace.normal actual expect" &&
test_must_fail env GIT_TRACE2="$(pwd)/trace.normal" test-tool trace2 001return 1 &&
- perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ scrub_normal <trace.normal >actual &&
cat >expect <<-EOF &&
version $V
start _EXE_ trace2 001return 1
@@ -85,7 +116,7 @@ test_expect_success 'automatic filename' '
test_when_finished "rm -r traces actual expect" &&
mkdir traces &&
GIT_TRACE2="$(pwd)/traces" test-tool trace2 001return 0 &&
- perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <"$(ls traces/*)" >actual &&
+ scrub_normal <"$(ls traces/*)" >actual &&
cat >expect <<-EOF &&
version $V
start _EXE_ trace2 001return 0
@@ -103,7 +134,7 @@ test_expect_success 'automatic filename' '
test_expect_success 'normal stream, exit code 0' '
test_when_finished "rm trace.normal actual expect" &&
GIT_TRACE2="$(pwd)/trace.normal" test-tool trace2 002exit 0 &&
- perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ scrub_normal <trace.normal >actual &&
cat >expect <<-EOF &&
version $V
start _EXE_ trace2 002exit 0
@@ -117,7 +148,7 @@ test_expect_success 'normal stream, exit code 0' '
test_expect_success 'normal stream, exit code 1' '
test_when_finished "rm trace.normal actual expect" &&
test_must_fail env GIT_TRACE2="$(pwd)/trace.normal" test-tool trace2 002exit 1 &&
- perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ scrub_normal <trace.normal >actual &&
cat >expect <<-EOF &&
version $V
start _EXE_ trace2 002exit 1
@@ -135,7 +166,7 @@ test_expect_success 'normal stream, exit code 1' '
test_expect_success 'normal stream, error event' '
test_when_finished "rm trace.normal actual expect" &&
GIT_TRACE2="$(pwd)/trace.normal" test-tool trace2 003error "hello world" "this is a test" &&
- perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ scrub_normal <trace.normal >actual &&
cat >expect <<-EOF &&
version $V
start _EXE_ trace2 003error '\''hello world'\'' '\''this is a test'\''
@@ -155,7 +186,7 @@ test_expect_success 'normal stream, error event' '
test_expect_success 'BUG messages are written to trace2' '
test_when_finished "rm trace.normal actual expect" &&
test_must_fail env GIT_TRACE2="$(pwd)/trace.normal" test-tool trace2 007bug &&
- perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ scrub_normal <trace.normal >actual &&
cat >expect <<-EOF &&
version $V
start _EXE_ trace2 007bug
@@ -179,7 +210,7 @@ test_expect_success 'bug messages with BUG_if_bug() are written to trace2' '
sed "s/^.*: //" <err >actual &&
test_cmp expect actual &&
- perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ scrub_normal <trace.normal >actual &&
cat >expect <<-EOF &&
version $V
start _EXE_ trace2 008bug
@@ -205,7 +236,7 @@ test_expect_success 'bug messages without explicit BUG_if_bug() are written to t
sed "s/^.*: //" <err >actual &&
test_cmp expect actual &&
- perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ scrub_normal <trace.normal >actual &&
cat >expect <<-EOF &&
version $V
start _EXE_ trace2 009bug_BUG
@@ -230,7 +261,7 @@ test_expect_success 'bug messages followed by BUG() are written to trace2' '
sed "s/^.*: //" <err >actual &&
test_cmp expect actual &&
- perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ scrub_normal <trace.normal >actual &&
cat >expect <<-EOF &&
version $V
start _EXE_ trace2 010bug_BUG
@@ -262,7 +293,7 @@ test_expect_success 'using global config, normal stream, return code 0' '
test_config_global trace2.normalBrief 1 &&
test_config_global trace2.normalTarget "$(pwd)/trace.normal" &&
test-tool trace2 001return 0 &&
- perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ scrub_normal <trace.normal >actual &&
cat >expect <<-EOF &&
version $V
start _EXE_ trace2 001return 0
@@ -280,7 +311,7 @@ test_expect_success 'using global config with include' '
mv "$(pwd)/.gitconfig" "$(pwd)/real.gitconfig" &&
test_config_global include.path "$(pwd)/real.gitconfig" &&
test-tool trace2 001return 0 &&
- perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ scrub_normal <trace.normal >actual &&
cat >expect <<-EOF &&
version $V
start _EXE_ trace2 001return 0
diff --git a/t/t0210/scrub_normal.perl b/t/t0210/scrub_normal.perl
deleted file mode 100644
index 7cc4de392a..0000000000
--- a/t/t0210/scrub_normal.perl
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/usr/bin/perl
-#
-# Scrub the variable fields from the normal trace2 output to
-# make testing easier.
-
-use strict;
-use warnings;
-
-my $float = '[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?';
-
-# This code assumes that the trace2 data was written with bare
-# turned on (which omits the "<clock> <file>:<line>" prefix.
-
-while (<>) {
- # Various messages include an elapsed time in the middle
- # of the message. Replace the time with a placeholder to
- # simplify our HEREDOC in the test script.
- s/elapsed:$float/elapsed:_TIME_/g;
-
- my $line = $_;
-
- # we expect:
- # start <argv0> [<argv1> [<argv2> [...]]]
- #
- # where argv0 might be a relative or absolute path, with
- # or without quotes, and platform dependent. Replace argv0
- # with a token for HEREDOC matching in the test script.
-
- if ($line =~ m/^start/) {
- $line =~ /^start\s+(.*)/;
- my $argv = $1;
- $argv =~ m/(\'[^\']*\'|[^ ]+)\s+(.*)/;
- my $argv_0 = $1;
- my $argv_rest = $2;
-
- print "start _EXE_ $argv_rest\n";
- }
- elsif ($line =~ m/^cmd_path/) {
- # Likewise, the 'cmd_path' message breaks out argv[0].
- #
- # This line is only emitted when RUNTIME_PREFIX is defined,
- # so just omit it for testing purposes.
- # print "cmd_path _EXE_\n";
- }
- elsif ($line =~ m/^cmd_ancestry/) {
- # 'cmd_ancestry' is not implemented everywhere, so for portability's
- # sake, skip it when parsing normal.
- #
- # print "$line";
- }
- else {
- print "$line";
- }
-}
diff --git a/t/t0211-trace2-perf.sh b/t/t0211-trace2-perf.sh
index bac9046540..760cf69087 100755
--- a/t/t0211-trace2-perf.sh
+++ b/t/t0211-trace2-perf.sh
@@ -4,6 +4,12 @@ test_description='test trace2 facility (perf target)'
. ./test-lib.sh
+if ! test_have_prereq PERL_TEST_HELPERS
+then
+ skip_all='skipping trace2 tests; Perl not available'
+ test_done
+fi
+
# Turn off any inherited trace2 settings for this test.
sane_unset GIT_TRACE2 GIT_TRACE2_PERF GIT_TRACE2_EVENT
sane_unset GIT_TRACE2_PERF_BRIEF
diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh
index 002a75dee8..1be534a895 100755
--- a/t/t0610-reftable-basics.sh
+++ b/t/t0610-reftable-basics.sh
@@ -653,9 +653,8 @@ test_expect_success 'basic: commit and list refs' '
test_expect_success 'basic: can write large commit message' '
test_when_finished "rm -rf repo" &&
git init repo &&
- perl -e "
- print \"this is a long commit message\" x 50000
- " >commit-msg &&
+
+ awk "BEGIN { for (i = 0; i < 50000; i++) printf \"%s\", \"this is a long commit message\" }" >commit-msg &&
git -C repo commit --allow-empty --file=../commit-msg
'
diff --git a/t/t0613-reftable-write-options.sh b/t/t0613-reftable-write-options.sh
index e2708e11d5..42aa1592f8 100755
--- a/t/t0613-reftable-write-options.sh
+++ b/t/t0613-reftable-write-options.sh
@@ -145,7 +145,7 @@ test_expect_success 'small block size fails with large reflog message' '
(
cd repo &&
test_commit A &&
- perl -e "print \"a\" x 500" >logmsg &&
+ test-tool genzeros 500 | tr "\000" "a" >logmsg &&
cat >expect <<-EOF &&
fatal: update_ref failed for ref ${SQ}refs/heads/logme${SQ}: reftable: transaction failure: entry too large
EOF
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index 398865d6eb..ce8b27bf54 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -903,6 +903,59 @@ test_expect_success 'cat-file -t and -s on corrupt loose object' '
)
'
+test_expect_success 'truncated object with --allow-unknown-type' - <<\EOT
+ objtype='a really long type name that exceeds the 32-byte limit' &&
+ blob=$(git hash-object -w --literally -t "$objtype" /dev/null) &&
+ objpath=.git/objects/$(test_oid_to_path "$blob") &&
+
+ # We want to truncate the object far enough in that we don't hit the
+ # end while inflating the first 32 bytes (since we want to have to dig
+ # for the trailing NUL of the header). But we don't want to go too far,
+ # since our header isn't very big. And of course we are counting
+ # deflated zlib bytes in the on-disk file, so it's a bit of a guess.
+ # Empirically 50 seems to work.
+ mv "$objpath" obj.bak &&
+ test_when_finished 'mv obj.bak "$objpath"' &&
+ test_copy_bytes 50 <obj.bak >"$objpath" &&
+
+ test_must_fail git cat-file --allow-unknown-type -t $blob 2>err &&
+ test_grep "unable to unpack $blob header" err
+EOT
+
+test_expect_success 'object reading handles zlib dictionary' - <<\EOT
+ echo 'content that will be recompressed' >file &&
+ blob=$(git hash-object -w file) &&
+ objpath=.git/objects/$(test_oid_to_path "$blob") &&
+
+ # Recompress a loose object using a precomputed zlib dictionary.
+ # This was originally done with:
+ #
+ # perl -MCompress::Raw::Zlib -e '
+ # binmode STDIN;
+ # binmode STDOUT;
+ # my $data = do { local $/; <STDIN> };
+ # my $in = new Compress::Raw::Zlib::Inflate;
+ # my $de = new Compress::Raw::Zlib::Deflate(
+ # -Dictionary => "anything"
+ # );
+ # $in->inflate($data, $raw);
+ # $de->deflate($raw, $out);
+ # print $out;
+ # ' <obj.bak >$objpath
+ #
+ # but we do not want to require the perl module for all test runs (nor
+ # carry a custom t/helper program that uses zlib features we don't
+ # otherwise care about).
+ mv "$objpath" obj.bak &&
+ test_when_finished 'mv obj.bak "$objpath"' &&
+ printf '\170\273\017\112\003\143' >$objpath &&
+
+ test_must_fail git cat-file blob $blob 2>err &&
+ test_grep ! 'too long' err &&
+ test_grep 'error: unable to unpack' err &&
+ test_grep 'error: inflate: needs dictionary' err
+EOT
+
# Tests for git cat-file --follow-symlinks
test_expect_success 'prep for symlink tests' '
echo_without_newline "$hello_content" >morx &&
@@ -1270,7 +1323,7 @@ extract_batch_output () {
' "$@"
}
-test_expect_success 'cat-file --batch-all-objects --batch ignores replace' '
+test_expect_success PERL_TEST_HELPERS 'cat-file --batch-all-objects --batch ignores replace' '
git cat-file --batch-all-objects --batch >actual.raw &&
extract_batch_output $orig <actual.raw >actual &&
{
@@ -1323,7 +1376,7 @@ test_expect_success 'batch-command flush without --buffer' '
grep "^fatal:.*flush is only for --buffer mode.*" err
'
-script='
+perl_script='
use warnings;
use strict;
use IPC::Open2;
@@ -1345,12 +1398,115 @@ $? == 0 or die "\$?=$?";
expect="$hello_oid blob $hello_size"
-test_expect_success PERL '--batch-check is unbuffered by default' '
- perl -e "$script" -- --batch-check $hello_oid "$expect"
+test_lazy_prereq PERL_IPC_OPEN2 '
+ perl -MIPC::Open2 -e "exit 0"
+'
+
+test_expect_success PERL_IPC_OPEN2 '--batch-check is unbuffered by default' '
+ perl -e "$perl_script" -- --batch-check $hello_oid "$expect"
+'
+
+test_expect_success PERL_IPC_OPEN2 '--batch-command info is unbuffered by default' '
+ perl -e "$perl_script" -- --batch-command $hello_oid "$expect" "info "
+'
+
+test_expect_success 'setup for objects filter' '
+ git init repo &&
+ (
+ # Seed the repository with four different sets of objects:
+ #
+ # - The first set is fully packed and has a bitmap.
+ # - The second set is packed, but has no bitmap.
+ # - The third set is loose.
+ # - The fourth set is loose and contains big objects.
+ #
+ # This ensures that we cover all these types as expected.
+ cd repo &&
+ test_commit first &&
+ git repack -Adb &&
+ test_commit second &&
+ git repack -d &&
+ test_commit third &&
+
+ for n in 1000 10000
+ do
+ printf "%"$n"s" X >large.$n || return 1
+ done &&
+ git add large.* &&
+ git commit -m fourth
+ )
+'
+
+test_expect_success 'objects filter with unknown option' '
+ cat >expect <<-EOF &&
+ fatal: invalid filter-spec ${SQ}unknown${SQ}
+ EOF
+ test_must_fail git -C repo cat-file --filter=unknown 2>err &&
+ test_cmp expect err
'
-test_expect_success PERL '--batch-command info is unbuffered by default' '
- perl -e "$script" -- --batch-command $hello_oid "$expect" "info "
+for option in sparse:oid=1234 tree:1 sparse:path=x
+do
+ test_expect_success "objects filter with unsupported option $option" '
+ case "$option" in
+ tree:1)
+ echo "usage: objects filter not supported: ${SQ}tree${SQ}" >expect
+ ;;
+ sparse:path=x)
+ echo "fatal: sparse:path filters support has been dropped" >expect
+ ;;
+ *)
+ option_name=$(echo "$option" | cut -d= -f1) &&
+ printf "usage: objects filter not supported: ${SQ}%s${SQ}\n" "$option_name" >expect
+ ;;
+ esac &&
+ test_must_fail git -C repo cat-file --filter=$option 2>err &&
+ test_cmp expect err
+ '
+done
+
+test_expect_success 'objects filter: disabled' '
+ git -C repo cat-file --batch-check="%(objectname)" --batch-all-objects --no-filter >actual &&
+ sort actual >actual.sorted &&
+ git -C repo rev-list --objects --no-object-names --all >expect &&
+ sort expect >expect.sorted &&
+ test_cmp expect.sorted actual.sorted
'
+test_objects_filter () {
+ filter="$1"
+
+ test_expect_success "objects filter: $filter" '
+ git -C repo cat-file --batch-check="%(objectname)" --batch-all-objects --filter="$filter" >actual &&
+ sort actual >actual.sorted &&
+ git -C repo rev-list --objects --no-object-names --all --filter="$filter" --filter-provided-objects >expect &&
+ sort expect >expect.sorted &&
+ test_cmp expect.sorted actual.sorted
+ '
+
+ test_expect_success "objects filter prints excluded objects: $filter" '
+ # Find all objects that would be excluded by the current filter.
+ git -C repo rev-list --objects --no-object-names --all >all &&
+ git -C repo rev-list --objects --no-object-names --all --filter="$filter" --filter-provided-objects >filtered &&
+ sort all >all.sorted &&
+ sort filtered >filtered.sorted &&
+ comm -23 all.sorted filtered.sorted >expected.excluded &&
+ test_line_count -gt 0 expected.excluded &&
+
+ git -C repo cat-file --batch-check="%(objectname)" --filter="$filter" <expected.excluded >actual &&
+ awk "/excluded/{ print \$1 }" actual | sort >actual.excluded &&
+ test_cmp expected.excluded actual.excluded
+ '
+}
+
+test_objects_filter "blob:none"
+test_objects_filter "blob:limit=1"
+test_objects_filter "blob:limit=500"
+test_objects_filter "blob:limit=1000"
+test_objects_filter "blob:limit=1k"
+test_objects_filter "object:type=blob"
+test_objects_filter "object:type=commit"
+test_objects_filter "object:type=tag"
+test_objects_filter "object:type=tree"
+
test_done
diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh
index a0481139de..b3cf53ff8c 100755
--- a/t/t1007-hash-object.sh
+++ b/t/t1007-hash-object.sh
@@ -205,7 +205,7 @@ test_expect_success 'too-short tree' '
grep "too-short tree object" err
'
-test_expect_success 'malformed mode in tree' '
+test_expect_success PERL_TEST_HELPERS 'malformed mode in tree' '
hex_oid=$(echo foo | git hash-object --stdin -w) &&
bin_oid=$(echo $hex_oid | hex2oct) &&
printf "9100644 \0$bin_oid" >tree-with-malformed-mode &&
@@ -213,7 +213,7 @@ test_expect_success 'malformed mode in tree' '
grep "malformed mode in tree entry" err
'
-test_expect_success 'empty filename in tree' '
+test_expect_success PERL_TEST_HELPERS 'empty filename in tree' '
hex_oid=$(echo foo | git hash-object --stdin -w) &&
bin_oid=$(echo $hex_oid | hex2oct) &&
printf "100644 \0$bin_oid" >tree-with-empty-filename &&
@@ -221,7 +221,7 @@ test_expect_success 'empty filename in tree' '
grep "empty filename in tree entry" err
'
-test_expect_success 'duplicate filename in tree' '
+test_expect_success PERL_TEST_HELPERS 'duplicate filename in tree' '
hex_oid=$(echo foo | git hash-object --stdin -w) &&
bin_oid=$(echo $hex_oid | hex2oct) &&
{
diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh
index c291a2b33d..e9973f7494 100755
--- a/t/t1010-mktree.sh
+++ b/t/t1010-mktree.sh
@@ -42,13 +42,13 @@ test_expect_success 'ls-tree piped to mktree (2)' '
'
test_expect_success 'ls-tree output in wrong order given to mktree (1)' '
- perl -e "print reverse <>" <top |
+ sort -r <top |
git mktree >actual &&
test_cmp tree actual
'
test_expect_success 'ls-tree output in wrong order given to mktree (2)' '
- perl -e "print reverse <>" <top.withsub |
+ sort -r <top.withsub |
git mktree >actual &&
test_cmp tree.withsub actual
'
diff --git a/t/t1050-large.sh b/t/t1050-large.sh
index c71932b024..5be273611a 100755
--- a/t/t1050-large.sh
+++ b/t/t1050-large.sh
@@ -6,7 +6,8 @@ test_description='adding and checking out large blobs'
. ./test-lib.sh
test_expect_success 'core.bigFileThreshold must be non-negative' '
- test_must_fail git -c core.bigFileThreshold=-1 rev-parse >out 2>err &&
+ : >input &&
+ test_must_fail git -c core.bigFileThreshold=-1 hash-object input >out 2>err &&
grep "bad numeric config value" err &&
test_must_be_empty out
'
diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
index a4c7c41fc0..f9b448792c 100755
--- a/t/t1092-sparse-checkout-compatibility.sh
+++ b/t/t1092-sparse-checkout-compatibility.sh
@@ -1486,7 +1486,6 @@ test_expect_success 'sparse-index is not expanded' '
ensure_not_expanded checkout -f update-deep &&
test_config -C sparse-index pull.twohead ort &&
(
- sane_unset GIT_TEST_MERGE_ALGORITHM &&
for OPERATION in "merge -m merge" cherry-pick rebase
do
ensure_not_expanded merge -m merge update-folder1 &&
@@ -1506,7 +1505,6 @@ test_expect_success 'sparse-index is not expanded: merge conflict in cone' '
done &&
(
- sane_unset GIT_TEST_MERGE_ALGORITHM &&
git -C sparse-index config pull.twohead ort &&
ensure_not_expanded ! merge -m merged expand-right
)
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 29045aad43..d29d23cb89 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -2066,6 +2066,239 @@ do
grep "$(git rev-parse $a) $(git rev-parse $a)" actual
'
+ test_expect_success "stdin $type batch-updates" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ test_commit commit &&
+ head=$(git rev-parse HEAD) &&
+
+ format_command $type "update refs/heads/ref1" "$head" "$Z" >stdin &&
+ format_command $type "update refs/heads/ref2" "$head" "$Z" >>stdin &&
+ git update-ref $type --stdin --batch-updates <stdin &&
+ echo $head >expect &&
+ git rev-parse refs/heads/ref1 >actual &&
+ test_cmp expect actual &&
+ git rev-parse refs/heads/ref2 >actual &&
+ test_cmp expect actual
+ )
+ '
+
+ test_expect_success "stdin $type batch-updates with invalid new_oid" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ test_commit one &&
+ old_head=$(git rev-parse HEAD) &&
+ test_commit two &&
+ head=$(git rev-parse HEAD) &&
+ git update-ref refs/heads/ref1 $head &&
+ git update-ref refs/heads/ref2 $head &&
+
+ format_command $type "update refs/heads/ref1" "$old_head" "$head" >stdin &&
+ format_command $type "update refs/heads/ref2" "$(test_oid 001)" "$head" >>stdin &&
+ git update-ref $type --stdin --batch-updates <stdin >stdout &&
+ echo $old_head >expect &&
+ git rev-parse refs/heads/ref1 >actual &&
+ test_cmp expect actual &&
+ echo $head >expect &&
+ git rev-parse refs/heads/ref2 >actual &&
+ test_cmp expect actual &&
+ test_grep -q "invalid new value provided" stdout
+ )
+ '
+
+ test_expect_success "stdin $type batch-updates with non-commit new_oid" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ test_commit one &&
+ old_head=$(git rev-parse HEAD) &&
+ test_commit two &&
+ head=$(git rev-parse HEAD) &&
+ head_tree=$(git rev-parse HEAD^{tree}) &&
+ git update-ref refs/heads/ref1 $head &&
+ git update-ref refs/heads/ref2 $head &&
+
+ format_command $type "update refs/heads/ref1" "$old_head" "$head" >stdin &&
+ format_command $type "update refs/heads/ref2" "$head_tree" "$head" >>stdin &&
+ git update-ref $type --stdin --batch-updates <stdin >stdout &&
+ echo $old_head >expect &&
+ git rev-parse refs/heads/ref1 >actual &&
+ test_cmp expect actual &&
+ echo $head >expect &&
+ git rev-parse refs/heads/ref2 >actual &&
+ test_cmp expect actual &&
+ test_grep -q "invalid new value provided" stdout
+ )
+ '
+
+ test_expect_success "stdin $type batch-updates with non-existent ref" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ test_commit one &&
+ old_head=$(git rev-parse HEAD) &&
+ test_commit two &&
+ head=$(git rev-parse HEAD) &&
+ git update-ref refs/heads/ref1 $head &&
+
+ format_command $type "update refs/heads/ref1" "$old_head" "$head" >stdin &&
+ format_command $type "update refs/heads/ref2" "$old_head" "$head" >>stdin &&
+ git update-ref $type --stdin --batch-updates <stdin >stdout &&
+ echo $old_head >expect &&
+ git rev-parse refs/heads/ref1 >actual &&
+ test_cmp expect actual &&
+ test_must_fail git rev-parse refs/heads/ref2 &&
+ test_grep -q "reference does not exist" stdout
+ )
+ '
+
+ test_expect_success "stdin $type batch-updates with dangling symref" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ test_commit one &&
+ old_head=$(git rev-parse HEAD) &&
+ test_commit two &&
+ head=$(git rev-parse HEAD) &&
+ git update-ref refs/heads/ref1 $head &&
+ git symbolic-ref refs/heads/ref2 refs/heads/nonexistent &&
+
+ format_command $type "update refs/heads/ref1" "$old_head" "$head" >stdin &&
+ format_command $type "update refs/heads/ref2" "$old_head" "$head" >>stdin &&
+ git update-ref $type --no-deref --stdin --batch-updates <stdin >stdout &&
+ echo $old_head >expect &&
+ git rev-parse refs/heads/ref1 >actual &&
+ test_cmp expect actual &&
+ echo $head >expect &&
+ test_must_fail git rev-parse refs/heads/ref2 &&
+ test_grep -q "reference does not exist" stdout
+ )
+ '
+
+ test_expect_success "stdin $type batch-updates with regular ref as symref" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ test_commit one &&
+ old_head=$(git rev-parse HEAD) &&
+ test_commit two &&
+ head=$(git rev-parse HEAD) &&
+ git update-ref refs/heads/ref1 $head &&
+ git update-ref refs/heads/ref2 $head &&
+
+ format_command $type "update refs/heads/ref1" "$old_head" "$head" >stdin &&
+ format_command $type "symref-update refs/heads/ref2" "$old_head" "ref" "refs/heads/nonexistent" >>stdin &&
+ git update-ref $type --no-deref --stdin --batch-updates <stdin >stdout &&
+ echo $old_head >expect &&
+ git rev-parse refs/heads/ref1 >actual &&
+ test_cmp expect actual &&
+ echo $head >expect &&
+ echo $head >expect &&
+ git rev-parse refs/heads/ref2 >actual &&
+ test_cmp expect actual &&
+ test_grep -q "expected symref but found regular ref" stdout
+ )
+ '
+
+ test_expect_success "stdin $type batch-updates with invalid old_oid" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ test_commit one &&
+ old_head=$(git rev-parse HEAD) &&
+ test_commit two &&
+ head=$(git rev-parse HEAD) &&
+ git update-ref refs/heads/ref1 $head &&
+ git update-ref refs/heads/ref2 $head &&
+
+ format_command $type "update refs/heads/ref1" "$old_head" "$head" >stdin &&
+ format_command $type "update refs/heads/ref2" "$old_head" "$Z" >>stdin &&
+ git update-ref $type --stdin --batch-updates <stdin >stdout &&
+ echo $old_head >expect &&
+ git rev-parse refs/heads/ref1 >actual &&
+ test_cmp expect actual &&
+ echo $head >expect &&
+ git rev-parse refs/heads/ref2 >actual &&
+ test_cmp expect actual &&
+ test_grep -q "reference already exists" stdout
+ )
+ '
+
+ test_expect_success "stdin $type batch-updates with incorrect old oid" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ test_commit one &&
+ old_head=$(git rev-parse HEAD) &&
+ test_commit two &&
+ head=$(git rev-parse HEAD) &&
+ git update-ref refs/heads/ref1 $head &&
+ git update-ref refs/heads/ref2 $head &&
+
+ format_command $type "update refs/heads/ref1" "$old_head" "$head" >stdin &&
+ format_command $type "update refs/heads/ref2" "$head" "$old_head" >>stdin &&
+ git update-ref $type --stdin --batch-updates <stdin >stdout &&
+ echo $old_head >expect &&
+ git rev-parse refs/heads/ref1 >actual &&
+ test_cmp expect actual &&
+ echo $head >expect &&
+ git rev-parse refs/heads/ref2 >actual &&
+ test_cmp expect actual &&
+ test_grep -q "incorrect old value provided" stdout
+ )
+ '
+
+ test_expect_success "stdin $type batch-updates refname conflict" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ test_commit one &&
+ old_head=$(git rev-parse HEAD) &&
+ test_commit two &&
+ head=$(git rev-parse HEAD) &&
+ git update-ref refs/heads/ref/foo $head &&
+
+ format_command $type "update refs/heads/ref/foo" "$old_head" "$head" >stdin &&
+ format_command $type "update refs/heads/ref" "$old_head" "" >>stdin &&
+ git update-ref $type --stdin --batch-updates <stdin >stdout &&
+ echo $old_head >expect &&
+ git rev-parse refs/heads/ref/foo >actual &&
+ test_cmp expect actual &&
+ test_grep -q "refname conflict" stdout
+ )
+ '
+
+ test_expect_success "stdin $type batch-updates refname conflict new ref" '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+ test_commit one &&
+ old_head=$(git rev-parse HEAD) &&
+ test_commit two &&
+ head=$(git rev-parse HEAD) &&
+ git update-ref refs/heads/ref/foo $head &&
+
+ format_command $type "update refs/heads/foo" "$old_head" "" >stdin &&
+ format_command $type "update refs/heads/ref" "$old_head" "" >>stdin &&
+ git update-ref $type --stdin --batch-updates <stdin >stdout &&
+ echo $old_head >expect &&
+ git rev-parse refs/heads/foo >actual &&
+ test_cmp expect actual &&
+ test_grep -q "refname conflict" stdout
+ )
+ '
done
test_expect_success 'update-ref should also create reflog for HEAD' '
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh
index 9d698b3cc3..9da3650e91 100755
--- a/t/t1403-show-ref.sh
+++ b/t/t1403-show-ref.sh
@@ -196,7 +196,7 @@ test_expect_success 'show-ref --verify with dangling ref' '
remove_object() {
file=$(sha1_file "$*") &&
- test -e "$file" &&
+ test_path_is_file "$file" &&
rm -f "$file"
} &&
diff --git a/t/t1408-packed-refs.sh b/t/t1408-packed-refs.sh
index 41ba1f1d7f..833477f0fa 100755
--- a/t/t1408-packed-refs.sh
+++ b/t/t1408-packed-refs.sh
@@ -42,4 +42,19 @@ test_expect_success 'no error from stale entry in packed-refs' '
test_cmp expect actual
'
+test_expect_success 'list packed refs with unicode characters' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit --no-tag A &&
+ git update-ref refs/heads/ HEAD &&
+ git update-ref refs/heads/z HEAD &&
+ git pack-refs --all &&
+ printf "%s commit\trefs/heads/z\n" $(git rev-parse HEAD) >expect &&
+ git for-each-ref refs/heads/z >actual &&
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index 388fdf9ae5..42b501f163 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -315,9 +315,9 @@ test_expect_success 'git reflog expire unknown reference' '
test_config gc.reflogexpireunreachable never &&
test_must_fail git reflog expire main@{123} 2>stderr &&
- test_grep "points nowhere" stderr &&
+ test_grep "error: reflog could not be found: ${SQ}main@{123}${SQ}" stderr &&
test_must_fail git reflog expire does-not-exist 2>stderr &&
- test_grep "points nowhere" stderr
+ test_grep "error: reflog could not be found: ${SQ}does-not-exist${SQ}" stderr
'
test_expect_success 'checkout should not delete log for packed ref' '
@@ -551,4 +551,126 @@ test_expect_success 'reflog with invalid object ID can be listed' '
)
'
+test_expect_success 'reflog drop non-existent ref' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_must_fail git reflog exists refs/heads/non-existent &&
+ test_must_fail git reflog drop refs/heads/non-existent 2>stderr &&
+ test_grep "error: reflog could not be found: ${SQ}refs/heads/non-existent${SQ}" stderr
+ )
+'
+
+test_expect_success 'reflog drop' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ test_commit_bulk --ref=refs/heads/branch 1 &&
+ git reflog exists refs/heads/main &&
+ git reflog exists refs/heads/branch &&
+ git reflog drop refs/heads/main &&
+ test_must_fail git reflog exists refs/heads/main &&
+ git reflog exists refs/heads/branch
+ )
+'
+
+test_expect_success 'reflog drop multiple references' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ test_commit_bulk --ref=refs/heads/branch 1 &&
+ git reflog exists refs/heads/main &&
+ git reflog exists refs/heads/branch &&
+ git reflog drop refs/heads/main refs/heads/branch &&
+ test_must_fail git reflog exists refs/heads/main &&
+ test_must_fail git reflog exists refs/heads/branch
+ )
+'
+
+test_expect_success 'reflog drop multiple references some non-existent' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ test_commit_bulk --ref=refs/heads/branch 1 &&
+ git reflog exists refs/heads/main &&
+ git reflog exists refs/heads/branch &&
+ test_must_fail git reflog exists refs/heads/non-existent &&
+ test_must_fail git reflog drop refs/heads/main refs/heads/non-existent refs/heads/branch 2>stderr &&
+ test_must_fail git reflog exists refs/heads/main &&
+ test_must_fail git reflog exists refs/heads/branch &&
+ test_must_fail git reflog exists refs/heads/non-existent &&
+ test_grep "error: reflog could not be found: ${SQ}refs/heads/non-existent${SQ}" stderr
+ )
+'
+
+test_expect_success 'reflog drop --all' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ test_commit_bulk --ref=refs/heads/branch 1 &&
+ git reflog exists refs/heads/main &&
+ git reflog exists refs/heads/branch &&
+ git reflog drop --all &&
+ test_must_fail git reflog exists refs/heads/main &&
+ test_must_fail git reflog exists refs/heads/branch
+ )
+'
+
+test_expect_success 'reflog drop --all multiple worktrees' '
+ test_when_finished "rm -rf repo" &&
+ test_when_finished "rm -rf wt" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ git worktree add ../wt &&
+ test_commit_bulk -C ../wt --ref=refs/heads/branch 1 &&
+ git reflog exists refs/heads/main &&
+ git reflog exists refs/heads/branch &&
+ git reflog drop --all &&
+ test_must_fail git reflog exists refs/heads/main &&
+ test_must_fail git reflog exists refs/heads/branch
+ )
+'
+
+test_expect_success 'reflog drop --all --single-worktree' '
+ test_when_finished "rm -rf repo" &&
+ test_when_finished "rm -rf wt" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ git worktree add ../wt &&
+ test_commit -C ../wt foobar &&
+ git reflog exists refs/heads/main &&
+ git reflog exists refs/heads/wt &&
+ test-tool ref-store worktree:wt reflog-exists HEAD &&
+ git reflog drop --all --single-worktree &&
+ test_must_fail git reflog exists refs/heads/main &&
+ test_must_fail git reflog exists refs/heads/wt &&
+ test_must_fail test-tool ref-store worktree:main reflog-exists HEAD &&
+ test-tool ref-store worktree:wt reflog-exists HEAD
+ )
+'
+
+test_expect_success 'reflog drop --all with reference' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ test_must_fail git reflog drop --all refs/heads/main 2>stderr &&
+ test_grep "usage: references specified along with --all" stderr
+ )
+'
+
test_done
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index 8a456b1142..0105045376 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -346,7 +346,7 @@ test_expect_success 'unparseable tree object' '
test_grep ! "fatal: empty filename in tree entry" out
'
-test_expect_success 'tree entry with type mismatch' '
+test_expect_success PERL_TEST_HELPERS 'tree entry with type mismatch' '
test_when_finished "remove_object \$blob" &&
test_when_finished "remove_object \$tree" &&
test_when_finished "remove_object \$commit" &&
@@ -364,7 +364,7 @@ test_expect_success 'tree entry with type mismatch' '
test_grep ! "dangling blob" out
'
-test_expect_success 'tree entry with bogus mode' '
+test_expect_success PERL_TEST_HELPERS 'tree entry with bogus mode' '
test_when_finished "remove_object \$blob" &&
test_when_finished "remove_object \$tree" &&
blob=$(echo blob | git hash-object -w --stdin) &&
@@ -984,7 +984,7 @@ corrupt_index_checksum () {
# Corrupt the checksum on the index and then
# verify that only fsck notices.
-test_expect_success 'detect corrupt index file in fsck' '
+test_expect_success PERL_TEST_HELPERS 'detect corrupt index file in fsck' '
cp .git/index .git/index.backup &&
test_when_finished "mv .git/index.backup .git/index" &&
corrupt_index_checksum &&
diff --git a/t/t1517-outside-repo.sh b/t/t1517-outside-repo.sh
index dbd8cd6906..6824581317 100755
--- a/t/t1517-outside-repo.sh
+++ b/t/t1517-outside-repo.sh
@@ -107,4 +107,11 @@ test_expect_success LIBCURL 'remote-http outside repository' '
test_grep "^error: remote-curl" actual
'
+test_expect_success 'update-server-info does not crash with -h' '
+ test_expect_code 129 git update-server-info -h >usage &&
+ test_grep "[Uu]sage: git update-server-info " usage &&
+ test_expect_code 129 nongit git update-server-info -h >usage &&
+ test_grep "[Uu]sage: git update-server-info " usage
+'
+
test_done
diff --git a/t/t2501-cwd-empty.sh b/t/t2501-cwd-empty.sh
index f6d8d7d03d..be9140bbaa 100755
--- a/t/t2501-cwd-empty.sh
+++ b/t/t2501-cwd-empty.sh
@@ -117,8 +117,6 @@ test_expect_success 'merge fails if cwd needs to be removed; recursive friendly'
grep "Refusing to remove the current working directory" error
'
-GIT_TEST_MERGE_ALGORITHM=ort
-
test_expect_success 'merge fails if cwd needs to be removed' '
test_required_dir_removal git merge fd_conflict
'
diff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh
index f5bf16abcd..dd0586b007 100755
--- a/t/t3300-funny-names.sh
+++ b/t/t3300-funny-names.sh
@@ -70,7 +70,7 @@ test_expect_success 'ls-files -z does not quote funny filename' '
tabs ," (dq) and spaces
EOF
git ls-files -z >ls-files.z &&
- perl -pe "y/\000/\012/" <ls-files.z >current &&
+ tr "\000" "\012" <ls-files.z >current &&
test_cmp expected current
'
@@ -107,7 +107,7 @@ test_expect_success 'diff-index -z does not quote funny filename' '
tabs ," (dq) and spaces
EOF
git diff-index -z --name-status $t0 >diff-index.z &&
- perl -pe "y/\000/\012/" <diff-index.z >current &&
+ tr "\000" "\012" <diff-index.z >current &&
test_cmp expected current
'
@@ -117,7 +117,7 @@ test_expect_success 'diff-tree -z does not quote funny filename' '
tabs ," (dq) and spaces
EOF
git diff-tree -z --name-status $t0 $t1 >diff-tree.z &&
- perl -pe y/\\000/\\012/ <diff-tree.z >current &&
+ tr "\000" "\012" <diff-tree.z >current &&
test_cmp expected current
'
diff --git a/t/t3512-cherry-pick-submodule.sh b/t/t3512-cherry-pick-submodule.sh
index f22d1ddead..85a7932697 100755
--- a/t/t3512-cherry-pick-submodule.sh
+++ b/t/t3512-cherry-pick-submodule.sh
@@ -8,11 +8,6 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
-if test "$GIT_TEST_MERGE_ALGORITHM" != ort
-then
- KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
- KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
-fi
test_submodule_switch "cherry-pick"
test_expect_success 'unrelated submodule/file conflict is ignored' '
diff --git a/t/t3513-revert-submodule.sh b/t/t3513-revert-submodule.sh
index 8bfe3ed246..32e15c72ee 100755
--- a/t/t3513-revert-submodule.sh
+++ b/t/t3513-revert-submodule.sh
@@ -30,10 +30,6 @@ git_revert () {
git revert HEAD
}
-if test "$GIT_TEST_MERGE_ALGORITHM" != ort
-then
- KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
-fi
test_submodule_switch_func "git_revert"
test_done
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 3855d68dbc..782d97fb7d 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -11,6 +11,12 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff.sh
+if ! test_have_prereq PERL_TEST_HELPERS
+then
+ skip_all='skipping diff various tests; Perl not available'
+ test_done
+fi
+
test_expect_success setup '
GIT_AUTHOR_DATE="2006-06-26 00:00:00 +0000" &&
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 884f83fb8a..2782b1fc18 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -448,7 +448,7 @@ cat >>expect.no-threading <<EOF
---
EOF
-test_expect_success 'no threading' '
+test_expect_success PERL_TEST_HELPERS 'no threading' '
git checkout side &&
check_threading expect.no-threading main
'
@@ -466,11 +466,11 @@ In-Reply-To: <0>
References: <0>
EOF
-test_expect_success 'thread' '
+test_expect_success PERL_TEST_HELPERS 'thread' '
check_threading expect.thread --thread main
'
-test_expect_success '--thread overrides format.thread=deep' '
+test_expect_success PERL_TEST_HELPERS '--thread overrides format.thread=deep' '
test_config format.thread deep &&
check_threading expect.thread --thread main
'
@@ -490,7 +490,7 @@ In-Reply-To: <1>
References: <1>
EOF
-test_expect_success 'thread in-reply-to' '
+test_expect_success PERL_TEST_HELPERS 'thread in-reply-to' '
check_threading expect.in-reply-to --in-reply-to="<test.message>" \
--thread main
'
@@ -512,7 +512,7 @@ In-Reply-To: <0>
References: <0>
EOF
-test_expect_success 'thread cover-letter' '
+test_expect_success PERL_TEST_HELPERS 'thread cover-letter' '
check_threading expect.cover-letter --cover-letter --thread main
'
@@ -538,12 +538,12 @@ References: <1>
<0>
EOF
-test_expect_success 'thread cover-letter in-reply-to' '
+test_expect_success PERL_TEST_HELPERS 'thread cover-letter in-reply-to' '
check_threading expect.cl-irt --cover-letter \
--in-reply-to="<test.message>" --thread main
'
-test_expect_success 'thread explicit shallow' '
+test_expect_success PERL_TEST_HELPERS 'thread explicit shallow' '
check_threading expect.cl-irt --cover-letter \
--in-reply-to="<test.message>" --thread=shallow main
'
@@ -562,7 +562,7 @@ References: <0>
<1>
EOF
-test_expect_success 'thread deep' '
+test_expect_success PERL_TEST_HELPERS 'thread deep' '
check_threading expect.deep --thread=deep main
'
@@ -584,7 +584,7 @@ References: <1>
<2>
EOF
-test_expect_success 'thread deep in-reply-to' '
+test_expect_success PERL_TEST_HELPERS 'thread deep in-reply-to' '
check_threading expect.deep-irt --thread=deep \
--in-reply-to="<test.message>" main
'
@@ -609,7 +609,7 @@ References: <0>
<2>
EOF
-test_expect_success 'thread deep cover-letter' '
+test_expect_success PERL_TEST_HELPERS 'thread deep cover-letter' '
check_threading expect.deep-cl --cover-letter --thread=deep main
'
@@ -638,27 +638,27 @@ References: <1>
<3>
EOF
-test_expect_success 'thread deep cover-letter in-reply-to' '
+test_expect_success PERL_TEST_HELPERS 'thread deep cover-letter in-reply-to' '
check_threading expect.deep-cl-irt --cover-letter \
--in-reply-to="<test.message>" --thread=deep main
'
-test_expect_success 'thread via config' '
+test_expect_success PERL_TEST_HELPERS 'thread via config' '
test_config format.thread true &&
check_threading expect.thread main
'
-test_expect_success 'thread deep via config' '
+test_expect_success PERL_TEST_HELPERS 'thread deep via config' '
test_config format.thread deep &&
check_threading expect.deep main
'
-test_expect_success 'thread config + override' '
+test_expect_success PERL_TEST_HELPERS 'thread config + override' '
test_config format.thread deep &&
check_threading expect.thread --thread main
'
-test_expect_success 'thread config + --no-thread' '
+test_expect_success PERL_TEST_HELPERS 'thread config + --no-thread' '
test_config format.thread deep &&
check_threading expect.no-threading --no-thread main
'
diff --git a/t/t4018/ini-section b/t/t4018/ini-section
new file mode 100644
index 0000000000..c895ad9b4f
--- /dev/null
+++ b/t/t4018/ini-section
@@ -0,0 +1,5 @@
+[RIGHT]
+ # comment
+ ; comment
+ name = value
+ ChangeMe
diff --git a/t/t4018/ini-section-noindent b/t/t4018/ini-section-noindent
new file mode 100644
index 0000000000..733d23c801
--- /dev/null
+++ b/t/t4018/ini-section-noindent
@@ -0,0 +1,5 @@
+[RIGHT]
+# comment
+; comment
+name = value
+ChangeMe
diff --git a/t/t4018/ini-section-same-line b/t/t4018/ini-section-same-line
new file mode 100644
index 0000000000..522a1fa4a1
--- /dev/null
+++ b/t/t4018/ini-section-same-line
@@ -0,0 +1,4 @@
+[RIGHT] name = value
+ # comment
+ ; comment
+ ChangeMe
diff --git a/t/t4018/ini-subsection b/t/t4018/ini-subsection
new file mode 100644
index 0000000000..3d47349e60
--- /dev/null
+++ b/t/t4018/ini-subsection
@@ -0,0 +1,12 @@
+[LEFT]
+
+ [LEFT "CENTER"]
+ # comment
+ ; comment
+ name = value
+
+ [LEFT "RIGHT"]
+ # comment
+ ; comment
+ name = value
+ ChangeMe
diff --git a/t/t4018/ini-subsection-noindent b/t/t4018/ini-subsection-noindent
new file mode 100644
index 0000000000..698ea00ea3
--- /dev/null
+++ b/t/t4018/ini-subsection-noindent
@@ -0,0 +1,12 @@
+[LEFT]
+
+[LEFT "CENTER"]
+# comment
+; comment
+name = value
+
+[LEFT "RIGHT"]
+# comment
+; comment
+name = value
+ChangeMe
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
index f1efe482a5..c8a23d5148 100755
--- a/t/t4020-diff-external.sh
+++ b/t/t4020-diff-external.sh
@@ -237,7 +237,7 @@ check_external_diff 0 empty empty 0 on --quiet
check_external_diff 1 empty empty 1 on --quiet
check_external_diff 128 empty error 2 on --quiet
-echo NULZbetweenZwords | perl -pe 'y/Z/\000/' > file
+echo NULZbetweenZwords | tr "Z" "\000" > file
test_expect_success 'force diff with "diff"' '
after=$(git hash-object file) &&
diff --git a/t/t4029-diff-trailing-space.sh b/t/t4029-diff-trailing-space.sh
index 32b6e9a4e7..90cdde88d8 100755
--- a/t/t4029-diff-trailing-space.sh
+++ b/t/t4029-diff-trailing-space.sh
@@ -31,7 +31,8 @@ test_expect_success "$test_description" '
git config --bool diff.suppressBlankEmpty true &&
git diff f > actual &&
test_cmp exp actual &&
- perl -i.bak -p -e "s/^\$/ /" exp &&
+ sed "s/^\$/ /" exp >exp.munged &&
+ mv exp.munged exp &&
git config --bool diff.suppressBlankEmpty false &&
git diff f > actual &&
test_cmp exp actual &&
diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh
index daebf9796f..f904fc19f6 100755
--- a/t/t4030-diff-textconv.sh
+++ b/t/t4030-diff-textconv.sh
@@ -20,13 +20,10 @@ cat >expect.text <<'EOF'
+1
EOF
-cat >hexdump <<'EOF'
-#!/bin/sh
-"$PERL_PATH" -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1"
-EOF
-chmod +x hexdump
-
test_expect_success 'setup binary file with history' '
+ write_script hexdump <<-\EOF &&
+ tr "\000\001" "01" <"$1"
+ EOF
test_commit --printf one file "\\0\\n" &&
test_commit --printf --append two file "\\01\\n"
'
diff --git a/t/t4031-diff-rewrite-binary.sh b/t/t4031-diff-rewrite-binary.sh
index c4394a27b5..15e012ccc7 100755
--- a/t/t4031-diff-rewrite-binary.sh
+++ b/t/t4031-diff-rewrite-binary.sh
@@ -57,24 +57,19 @@ test_expect_success 'diff --stat counts binary rewrite as 0 lines' '
grep " rewrite file" diff
'
-{
- echo "#!$SHELL_PATH"
- cat <<'EOF'
-"$PERL_PATH" -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1"
-EOF
-} >dump
-chmod +x dump
-
test_expect_success 'setup textconv' '
+ write_script dump <<-\EOF &&
+ test-tool hexdump <"$1"
+ EOF
echo file diff=foo >.gitattributes &&
git config diff.foo.textconv "\"$(pwd)\""/dump
'
test_expect_success 'rewrite diff respects textconv' '
git diff -B >diff &&
- grep "dissimilarity index" diff &&
- grep "^-61" diff &&
- grep "^-0" diff
+ test_grep "dissimilarity index" diff &&
+ test_grep "^-3d 0a 00" diff &&
+ test_grep "^+3d 0a 01" diff
'
test_done
diff --git a/t/t4058-diff-duplicates.sh b/t/t4058-diff-duplicates.sh
index 2fce4a9897..16266dff2a 100755
--- a/t/t4058-diff-duplicates.sh
+++ b/t/t4058-diff-duplicates.sh
@@ -13,6 +13,12 @@ test_description='test tree diff when trees have duplicate entries'
. ./test-lib.sh
+if ! test_have_prereq PERL_TEST_HELPERS
+then
+ skip_all='skipping diff duplicates tests; Perl not available'
+ test_done
+fi
+
# make_tree_entry <mode> <mode> <sha1>
#
# We have to rely on perl here because not all printfs understand
diff --git a/t/t4069-remerge-diff.sh b/t/t4069-remerge-diff.sh
index c6c94aef14..966882ce91 100755
--- a/t/t4069-remerge-diff.sh
+++ b/t/t4069-remerge-diff.sh
@@ -4,13 +4,6 @@ test_description='remerge-diff handling'
. ./test-lib.sh
-# This test is ort-specific
-if test "${GIT_TEST_MERGE_ALGORITHM}" != ort
-then
- skip_all="GIT_TEST_MERGE_ALGORITHM != ort"
- test_done
-fi
-
test_expect_success 'setup basic merges' '
test_write_lines 1 2 3 4 5 6 7 8 9 >numbers &&
git add numbers &&
diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh
index d370ecfe0d..8e302a5a57 100755
--- a/t/t4103-apply-binary.sh
+++ b/t/t4103-apply-binary.sh
@@ -26,10 +26,10 @@ test_expect_success 'setup' '
git commit -m "Initial Version" 2>/dev/null &&
git checkout -b binary &&
- perl -pe "y/x/\000/" <file1 >file3 &&
+ tr "x" "\000" <file1 >file3 &&
cat file3 >file4 &&
git add file2 &&
- perl -pe "y/\000/v/" <file3 >file1 &&
+ tr "y" "\000" <file3 >file1 &&
rm -f file2 &&
git update-index --add --remove file1 file2 file3 file4 &&
git commit -m "Second Version" &&
@@ -158,7 +158,7 @@ test_expect_success 'apply binary -p0 diff' '
test -z "$(git diff --name-status binary -- file3)"
'
-test_expect_success 'reject truncated binary diff' '
+test_expect_success PERL_TEST_HELPERS 'reject truncated binary diff' '
do_reset &&
# this length is calculated to get us very close to
diff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh
index 0784ba033a..1e7beab001 100755
--- a/t/t4116-apply-reverse.sh
+++ b/t/t4116-apply-reverse.sh
@@ -13,14 +13,14 @@ test_description='git apply in reverse
test_expect_success setup '
test_write_lines a b c d e f g h i j k l m n >file1 &&
- perl -pe "y/ijk/\\000\\001\\002/" <file1 >file2 &&
+ tr "ijk" "\000\001\002" <file1 >file2 &&
git add file1 file2 &&
git commit -m initial &&
git tag initial &&
test_write_lines a b c g h i J K L m o n p q >file1 &&
- perl -pe "y/mon/\\000\\001\\002/" <file1 >file2 &&
+ tr "mon" "\000\001\002" <file1 >file2 &&
git commit -a -m second &&
git tag second &&
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 5e2b6c80ea..2ae93d3c96 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -1084,13 +1084,13 @@ test_expect_success 'am works with multi-line in-body headers' '
Body test" --author="$LONG <long@example.com>" &&
git format-patch --stdout -1 >patch &&
# bump from, date, and subject down to in-body header
- perl -lpe "
- if (/^From:/) {
+ awk "
+ /^From:/{
print \"From: x <x\@example.com>\";
print \"Date: Sat, 1 Jan 2000 00:00:00 +0000\";
print \"Subject: x\n\";
- }
- " patch >msg &&
+ }; 1
+ " <patch >msg &&
git checkout HEAD^ &&
git am msg &&
# Ensure that the author and full message are present
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
index b0a3e84984..204325f4d5 100755
--- a/t/t4200-rerere.sh
+++ b/t/t4200-rerere.sh
@@ -81,7 +81,7 @@ test_expect_success 'activate rerere, old style (conflicting merge)' '
test_might_fail git config --unset rerere.enabled &&
test_must_fail git merge first &&
- sha1=$(perl -pe "s/ .*//" .git/MERGE_RR) &&
+ sha1=$(sed "s/ .*//" .git/MERGE_RR) &&
rr=.git/rr-cache/$sha1 &&
grep "^=======\$" $rr/preimage &&
! test -f $rr/postimage &&
@@ -94,7 +94,7 @@ test_expect_success 'rerere.enabled works, too' '
git reset --hard &&
test_must_fail git merge first &&
- sha1=$(perl -pe "s/ .*//" .git/MERGE_RR) &&
+ sha1=$(sed "s/ .*//" .git/MERGE_RR) &&
rr=.git/rr-cache/$sha1 &&
grep ^=======$ $rr/preimage
'
@@ -104,7 +104,7 @@ test_expect_success 'set up rr-cache' '
git config rerere.enabled true &&
git reset --hard &&
test_must_fail git merge first &&
- sha1=$(perl -pe "s/ .*//" .git/MERGE_RR) &&
+ sha1=$(sed "s/ .*//" .git/MERGE_RR) &&
rr=.git/rr-cache/$sha1
'
@@ -188,7 +188,7 @@ test_expect_success 'rerere updates postimage timestamp' '
test_expect_success 'rerere clear' '
mv $rr/postimage .git/post-saved &&
- echo "$sha1 a1" | perl -pe "y/\012/\000/" >.git/MERGE_RR &&
+ echo "$sha1 a1" | tr "\012" "\000" >.git/MERGE_RR &&
git rerere clear &&
! test -d $rr
'
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
index f81e42a84d..8f2ba98963 100755
--- a/t/t4205-log-pretty-formats.sh
+++ b/t/t4205-log-pretty-formats.sh
@@ -698,7 +698,7 @@ test_expect_success '%(trailers:only=no,only=true) shows only "key: value" trail
test_cmp expect actual
'
-test_expect_success '%(trailers:unfold) unfolds trailers' '
+test_expect_success PERL_TEST_HELPERS '%(trailers:unfold) unfolds trailers' '
git log --no-walk --pretty="%(trailers:unfold)" >actual &&
{
unfold <trailers &&
@@ -707,7 +707,7 @@ test_expect_success '%(trailers:unfold) unfolds trailers' '
test_cmp expect actual
'
-test_expect_success ':only and :unfold work together' '
+test_expect_success PERL_TEST_HELPERS ':only and :unfold work together' '
git log --no-walk --pretty="%(trailers:only,unfold)" >actual &&
git log --no-walk --pretty="%(trailers:unfold,only)" >reverse &&
test_cmp actual reverse &&
@@ -754,7 +754,7 @@ test_expect_success '%(trailers:key=foo) handles multiple lines even if folded'
test_cmp expect actual
'
-test_expect_success '%(trailers:key=foo,unfold) properly unfolds' '
+test_expect_success PERL_TEST_HELPERS '%(trailers:key=foo,unfold) properly unfolds' '
git log --no-walk --pretty="format:%(trailers:key=Signed-Off-by,unfold)" >actual &&
unfold <trailers | grep Signed-off-by >expect &&
test_cmp expect actual
diff --git a/t/t4216-log-bloom.sh b/t/t4216-log-bloom.sh
index 3f163dc396..8910d53cac 100755
--- a/t/t4216-log-bloom.sh
+++ b/t/t4216-log-bloom.sh
@@ -738,20 +738,20 @@ check_corrupt_graph () {
test_cmp expect.out out
}
-test_expect_success 'Bloom reader notices too-small data chunk' '
+test_expect_success PERL_TEST_HELPERS 'Bloom reader notices too-small data chunk' '
check_corrupt_graph BDAT clear 00000000 &&
echo "warning: ignoring too-small changed-path chunk" \
"(4 < 12) in commit-graph file" >expect.err &&
test_cmp expect.err err
'
-test_expect_success 'Bloom reader notices out-of-bounds filter offsets' '
+test_expect_success PERL_TEST_HELPERS 'Bloom reader notices out-of-bounds filter offsets' '
check_corrupt_graph BIDX 12 FFFFFFFF &&
# use grep to avoid depending on exact chunk size
grep "warning: ignoring out-of-range offset (4294967295) for changed-path filter at pos 3 of .git/objects/info/commit-graph" err
'
-test_expect_success 'Bloom reader notices too-small index chunk' '
+test_expect_success PERL_TEST_HELPERS 'Bloom reader notices too-small index chunk' '
# replace the index with a single entry, making most
# lookups out-of-bounds
check_corrupt_graph BIDX clear 00000000 &&
@@ -760,7 +760,7 @@ test_expect_success 'Bloom reader notices too-small index chunk' '
test_cmp expect.err err
'
-test_expect_success 'Bloom reader notices out-of-order index offsets' '
+test_expect_success PERL_TEST_HELPERS 'Bloom reader notices out-of-order index offsets' '
# we do not know any real offsets, but we can pick
# something plausible; we should not get to the point of
# actually reading from the bogus offsets anyway.
diff --git a/t/t4301-merge-tree-write-tree.sh b/t/t4301-merge-tree-write-tree.sh
index 44f7d07759..f9c5883a7f 100755
--- a/t/t4301-merge-tree-write-tree.sh
+++ b/t/t4301-merge-tree-write-tree.sh
@@ -4,13 +4,6 @@ test_description='git merge-tree --write-tree'
. ./test-lib.sh
-# This test is ort-specific
-if test "$GIT_TEST_MERGE_ALGORITHM" != "ort"
-then
- skip_all="GIT_TEST_MERGE_ALGORITHM != ort"
- test_done
-fi
-
test_expect_success setup '
test_write_lines 1 2 3 4 5 >numbers &&
echo hello >greeting &&
diff --git a/t/t5004-archive-corner-cases.sh b/t/t5004-archive-corner-cases.sh
index 50344e17ca..5174995191 100755
--- a/t/t5004-archive-corner-cases.sh
+++ b/t/t5004-archive-corner-cases.sh
@@ -4,6 +4,12 @@ test_description='test corner cases of git-archive'
. ./test-lib.sh
+if ! test_have_prereq PERL_TEST_HELPERS
+then
+ skip_all='skipping archive corner cases tests; Perl not available'
+ test_done
+fi
+
# the 10knuls.tar file is used to test for an empty git generated tar
# without having to invoke tar because an otherwise valid empty GNU tar
# will be considered broken by {Open,Net}BSD tar
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index 5ac8d39094..a5932b6a8b 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -9,9 +9,9 @@ test_description='git pack-object'
test_expect_success 'setup' '
rm -f .git/index* &&
- perl -e "print \"a\" x 4096;" >a &&
- perl -e "print \"b\" x 4096;" >b &&
- perl -e "print \"c\" x 4096;" >c &&
+ test-tool genzeros 4096 | tr "\000" "a" >a &&
+ test-tool genzeros 4096 | tr "\000" "b" >b &&
+ test-tool genzeros 4096 | tr "\000" "c" >c &&
test-tool genrandom "seed a" 2097152 >a_big &&
test-tool genrandom "seed b" 2097152 >b_big &&
git update-index --add a a_big b b_big c &&
@@ -140,7 +140,7 @@ test_expect_success 'pack-object <stdin parsing: --stdin-packs handles garbage'
# usage: check_deltas <stderr_from_pack_objects> <cmp_op> <nr_deltas>
# e.g.: check_deltas stderr -gt 0
check_deltas() {
- deltas=$(perl -lne '/delta (\d+)/ and print $1' "$1") &&
+ deltas=$(sed -n 's/Total [0-9][0-9]* (delta \([0-9][0-9]*\)).*/\1/p' "$1") &&
shift &&
if ! test "$deltas" "$@"
then
@@ -215,7 +215,7 @@ test_expect_success 'unpack with OFS_DELTA (core.fsyncmethod=batch)' '
check_unpack test-3-${packname_3} obj-list "$BATCH_CONFIGURATION"
'
-test_expect_success 'compare delta flavors' '
+test_expect_success PERL_TEST_HELPERS 'compare delta flavors' '
perl -e '\''
defined($_ = -s $_) or die for @ARGV;
exit 1 if $ARGV[0] <= $ARGV[1];
diff --git a/t/t5303-pack-corruption-resilience.sh b/t/t5303-pack-corruption-resilience.sh
index de58ca654a..ab99c8b685 100755
--- a/t/t5303-pack-corruption-resilience.sh
+++ b/t/t5303-pack-corruption-resilience.sh
@@ -103,7 +103,8 @@ test_expect_success 'create corruption in data of first object' '
create_new_pack &&
git prune-packed &&
chmod +w ${pack}.pack &&
- perl -i.bak -pe "s/ base /abcdef/" ${pack}.pack &&
+ sed "s/ base /abcdef/" ${pack}.pack >${pack}.pack.munged &&
+ mv ${pack}.pack.munged ${pack}.pack &&
test_must_fail git cat-file blob $blob_1 > /dev/null &&
test_must_fail git cat-file blob $blob_2 > /dev/null &&
test_must_fail git cat-file blob $blob_3 > /dev/null
@@ -160,7 +161,8 @@ test_expect_success 'create corruption in data of first delta' '
create_new_pack &&
git prune-packed &&
chmod +w ${pack}.pack &&
- perl -i.bak -pe "s/ delta1 /abcdefgh/" ${pack}.pack &&
+ sed "s/ delta1 /abcdefgh/" ${pack}.pack >${pack}.pack.munged &&
+ mv ${pack}.pack.munged ${pack}.pack &&
git cat-file blob $blob_1 > /dev/null &&
test_must_fail git cat-file blob $blob_2 > /dev/null &&
test_must_fail git cat-file blob $blob_3 > /dev/null
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
index 621bbbdd26..a62b463eaf 100755
--- a/t/t5310-pack-bitmaps.sh
+++ b/t/t5310-pack-bitmaps.sh
@@ -421,7 +421,7 @@ test_bitmap_cases () {
# mark the commits which did not receive bitmaps as preferred,
# and generate the bitmap again
- perl -pe "s{^}{create refs/tags/include/$. }" <before |
+ sed "s|\(.*\)|create refs/tags/include/\1 \1|" before |
git update-ref --stdin &&
git -c pack.preferBitmapTips=refs/tags/include repack -adb &&
diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh
index 32cf422745..defaa06d65 100755
--- a/t/t5316-pack-delta-depth.sh
+++ b/t/t5316-pack-delta-depth.sh
@@ -76,11 +76,11 @@ test_expect_success 'create series of packs' '
max_chain() {
git index-pack --verify-stat-only "$1" >output &&
- perl -lne '
- BEGIN { $len = 0 }
- /chain length = (\d+)/ and $len = $1;
- END { print $len }
- ' output
+ awk '
+ BEGIN { len=0 }
+ /chain length = [0-9]+:/{ len=$4 }
+ END { print len }
+ ' <output | tr -d ':'
}
# Note that this whole setup is pretty reliant on the current
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh
index f68f64cd85..0b3404f58f 100755
--- a/t/t5318-commit-graph.sh
+++ b/t/t5318-commit-graph.sh
@@ -837,7 +837,7 @@ check_corrupt_chunk () {
test_cmp expect.out out
}
-test_expect_success 'reader notices too-small oid fanout chunk' '
+test_expect_success PERL_TEST_HELPERS 'reader notices too-small oid fanout chunk' '
# make it big enough that the graph file is plausible,
# otherwise we hit an earlier check
check_corrupt_chunk OIDF clear $(printf "000000%02x" $(test_seq 250)) &&
@@ -848,7 +848,7 @@ test_expect_success 'reader notices too-small oid fanout chunk' '
test_cmp expect.err err
'
-test_expect_success 'reader notices fanout/lookup table mismatch' '
+test_expect_success PERL_TEST_HELPERS 'reader notices fanout/lookup table mismatch' '
check_corrupt_chunk OIDF 1020 "FFFFFFFF" &&
cat >expect.err <<-\EOF &&
error: commit-graph OID lookup chunk is the wrong size
@@ -857,7 +857,7 @@ test_expect_success 'reader notices fanout/lookup table mismatch' '
test_cmp expect.err err
'
-test_expect_success 'reader notices out-of-bounds fanout' '
+test_expect_success PERL_TEST_HELPERS 'reader notices out-of-bounds fanout' '
# Rather than try to corrupt a specific hash, we will just
# wreck them all. But we cannot just set them all to 0xFFFFFFFF or
# similar, as they are used for hi/lo starts in a binary search (so if
@@ -873,7 +873,7 @@ test_expect_success 'reader notices out-of-bounds fanout' '
test_cmp expect.err err
'
-test_expect_success 'reader notices too-small commit data chunk' '
+test_expect_success PERL_TEST_HELPERS '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
@@ -882,7 +882,7 @@ test_expect_success 'reader notices too-small commit data chunk' '
test_cmp expect.err err
'
-test_expect_success 'reader notices out-of-bounds extra edge' '
+test_expect_success PERL_TEST_HELPERS 'reader notices out-of-bounds extra edge' '
check_corrupt_chunk EDGE clear &&
cat >expect.err <<-\EOF &&
error: commit-graph extra-edges pointer out of bounds
@@ -890,7 +890,7 @@ test_expect_success 'reader notices out-of-bounds extra edge' '
test_cmp expect.err err
'
-test_expect_success 'reader notices too-small generations chunk' '
+test_expect_success PERL_TEST_HELPERS 'reader notices too-small generations chunk' '
check_corrupt_chunk GDA2 clear 00000000 &&
cat >expect.err <<-\EOF &&
error: commit-graph generations chunk is wrong size
diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh
index 0f215ad2e8..bd75dea950 100755
--- a/t/t5319-multi-pack-index.sh
+++ b/t/t5319-multi-pack-index.sh
@@ -1120,7 +1120,7 @@ corrupt_chunk () {
corrupt_chunk_file $midx "$@"
}
-test_expect_success 'reader notices too-small oid fanout chunk' '
+test_expect_success PERL_TEST_HELPERS 'reader notices too-small oid fanout chunk' '
corrupt_chunk OIDF clear 00000000 &&
test_must_fail git log 2>err &&
cat >expect <<-\EOF &&
@@ -1130,7 +1130,7 @@ test_expect_success 'reader notices too-small oid fanout chunk' '
test_cmp expect err
'
-test_expect_success 'reader notices too-small oid lookup chunk' '
+test_expect_success PERL_TEST_HELPERS 'reader notices too-small oid lookup chunk' '
corrupt_chunk OIDL clear 00000000 &&
test_must_fail git log 2>err &&
cat >expect <<-\EOF &&
@@ -1140,7 +1140,7 @@ test_expect_success 'reader notices too-small oid lookup chunk' '
test_cmp expect err
'
-test_expect_success 'reader notices too-small pack names chunk' '
+test_expect_success PERL_TEST_HELPERS 'reader notices too-small pack names chunk' '
# There is no NUL to terminate the name here, so the
# chunk is too short.
corrupt_chunk PNAM clear 70656666 &&
@@ -1151,7 +1151,7 @@ test_expect_success 'reader notices too-small pack names chunk' '
test_cmp expect err
'
-test_expect_success 'reader handles unaligned chunks' '
+test_expect_success PERL_TEST_HELPERS 'reader handles unaligned chunks' '
# A 9-byte PNAM means all of the subsequent chunks
# will no longer be 4-byte aligned, but it is still
# a valid one-pack chunk on its own (it is "foo.pack\0").
@@ -1165,7 +1165,7 @@ test_expect_success 'reader handles unaligned chunks' '
test_cmp expect.err err
'
-test_expect_success 'reader notices too-small object offset chunk' '
+test_expect_success PERL_TEST_HELPERS 'reader notices too-small object offset chunk' '
corrupt_chunk OOFF clear 00000000 &&
test_must_fail git log 2>err &&
cat >expect <<-\EOF &&
@@ -1175,7 +1175,7 @@ test_expect_success 'reader notices too-small object offset chunk' '
test_cmp expect err
'
-test_expect_success 'reader bounds-checks large offset table' '
+test_expect_success PERL_TEST_HELPERS 'reader bounds-checks large offset table' '
# re-use the objects64 dir here to cheaply get access to a midx
# with large offsets.
git init repo &&
@@ -1197,7 +1197,7 @@ test_expect_success 'reader bounds-checks large offset table' '
)
'
-test_expect_success 'reader notices too-small revindex chunk' '
+test_expect_success PERL_TEST_HELPERS 'reader notices too-small revindex chunk' '
# We only get a revindex with bitmaps (and likewise only
# load it when they are asked for).
test_config repack.writeBitmaps true &&
@@ -1214,7 +1214,7 @@ test_expect_success 'reader notices too-small revindex chunk' '
test_cmp expect.err err
'
-test_expect_success 'reader notices out-of-bounds fanout' '
+test_expect_success PERL_TEST_HELPERS '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
diff --git a/t/t5323-pack-redundant.sh b/t/t5323-pack-redundant.sh
index 688cd9706c..bc30bc9652 100755
--- a/t/t5323-pack-redundant.sh
+++ b/t/t5323-pack-redundant.sh
@@ -36,7 +36,7 @@ relationship between packs and objects is as follows:
. ./test-lib.sh
-if ! test_have_prereq WITHOUT_BREAKING_CHANGES
+if test_have_prereq WITH_BREAKING_CHANGES
then
skip_all='skipping git-pack-redundant tests; built with breaking changes'
test_done
diff --git a/t/t5324-split-commit-graph.sh b/t/t5324-split-commit-graph.sh
index a32be3867d..49a057cc2e 100755
--- a/t/t5324-split-commit-graph.sh
+++ b/t/t5324-split-commit-graph.sh
@@ -401,7 +401,7 @@ test_expect_success 'verify across alternates' '
)
'
-test_expect_success 'reader bounds-checks base-graph chunk' '
+test_expect_success PERL_TEST_HELPERS 'reader bounds-checks base-graph chunk' '
git clone --no-hardlinks . corrupt-base-chunk &&
(
cd corrupt-base-chunk &&
diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh
index d27557b9b0..892aeb09e4 100755
--- a/t/t5326-multi-pack-bitmaps.sh
+++ b/t/t5326-multi-pack-bitmaps.sh
@@ -176,8 +176,8 @@ test_midx_bitmap_cases () {
comm -13 bitmaps commits >before &&
test_line_count = 1 before &&
- perl -ne "printf(\"create refs/tags/include/%d \", $.); print" \
- <before | git update-ref --stdin &&
+ sed "s|\(.*\)|create refs/tags/include/\1 \1|" before |
+ git update-ref --stdin &&
rm -fr $midx-$(midx_checksum $objdir).bitmap &&
rm -fr $midx &&
diff --git a/t/t5328-commit-graph-64bit-time.sh b/t/t5328-commit-graph-64bit-time.sh
index a766a3e3f8..d8891e6a92 100755
--- a/t/t5328-commit-graph-64bit-time.sh
+++ b/t/t5328-commit-graph-64bit-time.sh
@@ -74,7 +74,7 @@ test_expect_success 'single commit with generation data exceeding UINT32_MAX' '
git -C repo-uint32-max commit-graph verify
'
-test_expect_success 'reader notices out-of-bounds generation overflow' '
+test_expect_success PERL_TEST_HELPERS 'reader notices out-of-bounds generation overflow' '
graph=.git/objects/info/commit-graph &&
test_when_finished "rm -rf $graph" &&
git commit-graph write --reachable &&
diff --git a/t/t5329-pack-objects-cruft.sh b/t/t5329-pack-objects-cruft.sh
index b71a0aef40..25ddda5cf3 100755
--- a/t/t5329-pack-objects-cruft.sh
+++ b/t/t5329-pack-objects-cruft.sh
@@ -360,43 +360,6 @@ test_expect_success 'expired objects are pruned' '
)
'
-test_expect_success 'repack --cruft generates a cruft pack' '
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
-
- test_commit reachable &&
- git branch -M main &&
- git checkout --orphan other &&
- test_commit unreachable &&
-
- git checkout main &&
- git branch -D other &&
- git tag -d unreachable &&
- # objects are not cruft if they are contained in the reflogs
- git reflog expire --all --expire=all &&
-
- git rev-list --objects --all --no-object-names >reachable.raw &&
- git cat-file --batch-all-objects --batch-check="%(objectname)" >objects &&
- sort <reachable.raw >reachable &&
- comm -13 reachable objects >unreachable &&
-
- git repack --cruft -d &&
-
- cruft=$(basename $(ls $packdir/pack-*.mtimes) .mtimes) &&
- pack=$(basename $(ls $packdir/pack-*.pack | grep -v $cruft) .pack) &&
-
- git show-index <$packdir/$pack.idx >actual.raw &&
- cut -f2 -d" " actual.raw | sort >actual &&
- test_cmp reachable actual &&
-
- git show-index <$packdir/$cruft.idx >actual.raw &&
- cut -f2 -d" " actual.raw | sort >actual &&
- test_cmp unreachable actual
- )
-'
-
test_expect_success 'loose objects mtimes upsert others' '
git init repo &&
test_when_finished "rm -fr repo" &&
@@ -470,219 +433,6 @@ test_expect_success 'expiring cruft objects with git gc' '
)
'
-test_expect_success 'cruft packs are not included in geometric repack' '
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
-
- test_commit reachable &&
- git repack -Ad &&
- git branch -M main &&
-
- git checkout --orphan other &&
- test_commit cruft &&
- git repack -d &&
-
- git checkout main &&
- git branch -D other &&
- git tag -d cruft &&
- git reflog expire --all --expire=all &&
-
- git repack --cruft &&
-
- find $packdir -type f | sort >before &&
- git repack --geometric=2 -d &&
- find $packdir -type f | sort >after &&
-
- test_cmp before after
- )
-'
-
-test_expect_success 'repack --geometric collects once-cruft objects' '
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
-
- test_commit reachable &&
- git repack -Ad &&
- git branch -M main &&
-
- git checkout --orphan other &&
- git rm -rf . &&
- test_commit --no-tag cruft &&
- cruft="$(git rev-parse HEAD)" &&
-
- git checkout main &&
- git branch -D other &&
- git reflog expire --all --expire=all &&
-
- # Pack the objects created in the previous step into a cruft
- # pack. Intentionally leave loose copies of those objects
- # around so we can pick them up in a subsequent --geometric
- # reapack.
- git repack --cruft &&
-
- # Now make those objects reachable, and ensure that they are
- # packed into the new pack created via a --geometric repack.
- git update-ref refs/heads/other $cruft &&
-
- # Without this object, the set of unpacked objects is exactly
- # the set of objects already in the cruft pack. Tweak that set
- # to ensure we do not overwrite the cruft pack entirely.
- test_commit reachable2 &&
-
- find $packdir -name "pack-*.idx" | sort >before &&
- git repack --geometric=2 -d &&
- find $packdir -name "pack-*.idx" | sort >after &&
-
- {
- git rev-list --objects --no-object-names $cruft &&
- git rev-list --objects --no-object-names reachable..reachable2
- } >want.raw &&
- sort want.raw >want &&
-
- pack=$(comm -13 before after) &&
- git show-index <$pack >objects.raw &&
-
- cut -d" " -f2 objects.raw | sort >got &&
-
- test_cmp want got
- )
-'
-
-test_expect_success 'cruft repack with no reachable objects' '
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
-
- test_commit base &&
- git repack -ad &&
-
- base="$(git rev-parse base)" &&
-
- git for-each-ref --format="delete %(refname)" >in &&
- git update-ref --stdin <in &&
- git reflog expire --all --expire=all &&
- rm -fr .git/index &&
-
- git repack --cruft -d &&
-
- git cat-file -t $base
- )
-'
-
-write_blob () {
- test-tool genrandom "$@" >in &&
- git hash-object -w -t blob in
-}
-
-find_pack () {
- for idx in $(ls $packdir/pack-*.idx)
- do
- git show-index <$idx >out &&
- if grep -q "$1" out
- then
- echo $idx
- fi || return 1
- done
-}
-
-test_expect_success 'cruft repack with --max-pack-size' '
- git init max-pack-size &&
- (
- cd max-pack-size &&
- test_commit base &&
-
- # two cruft objects which exceed the maximum pack size
- foo=$(write_blob foo 1048576) &&
- bar=$(write_blob bar 1048576) &&
- test-tool chmtime --get -1000 \
- "$objdir/$(test_oid_to_path $foo)" >foo.mtime &&
- test-tool chmtime --get -2000 \
- "$objdir/$(test_oid_to_path $bar)" >bar.mtime &&
- git repack --cruft --max-pack-size=1M &&
- find $packdir -name "*.mtimes" >cruft &&
- test_line_count = 2 cruft &&
-
- foo_mtimes="$(basename $(find_pack $foo) .idx).mtimes" &&
- bar_mtimes="$(basename $(find_pack $bar) .idx).mtimes" &&
- test-tool pack-mtimes $foo_mtimes >foo.actual &&
- test-tool pack-mtimes $bar_mtimes >bar.actual &&
-
- echo "$foo $(cat foo.mtime)" >foo.expect &&
- echo "$bar $(cat bar.mtime)" >bar.expect &&
-
- test_cmp foo.expect foo.actual &&
- test_cmp bar.expect bar.actual &&
- test "$foo_mtimes" != "$bar_mtimes"
- )
-'
-
-test_expect_success 'cruft repack with pack.packSizeLimit' '
- (
- cd max-pack-size &&
- # repack everything back together to remove the existing cruft
- # pack (but to keep its objects)
- git repack -adk &&
- git -c pack.packSizeLimit=1M repack --cruft &&
- # ensure the same post condition is met when --max-pack-size
- # would otherwise be inferred from the configuration
- find $packdir -name "*.mtimes" >cruft &&
- test_line_count = 2 cruft &&
- for pack in $(cat cruft)
- do
- test-tool pack-mtimes "$(basename $pack)" >objects &&
- test_line_count = 1 objects || return 1
- done
- )
-'
-
-test_expect_success 'cruft repack respects repack.cruftWindow' '
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
-
- test_commit base &&
-
- GIT_TRACE2_EVENT=$(pwd)/event.trace \
- git -c pack.window=1 -c repack.cruftWindow=2 repack \
- --cruft --window=3 &&
-
- grep "pack-objects.*--window=2.*--cruft" event.trace
- )
-'
-
-test_expect_success 'cruft repack respects --window by default' '
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
-
- test_commit base &&
-
- GIT_TRACE2_EVENT=$(pwd)/event.trace \
- git -c pack.window=2 repack --cruft --window=3 &&
-
- grep "pack-objects.*--window=3.*--cruft" event.trace
- )
-'
-
-test_expect_success 'cruft repack respects --quiet' '
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
-
- test_commit base &&
- GIT_PROGRESS_DELAY=0 git repack --cruft --quiet 2>err &&
- test_must_be_empty err
- )
-'
-
test_expect_success 'cruft --local drops unreachable objects' '
git init alternate &&
git init repo &&
@@ -945,4 +695,56 @@ test_expect_success 'additional cruft blobs via gc.recentObjectsHook' '
)
'
+test_expect_success 'split cruft packs with --max-cruft-size' '
+ repo=cruft-with--max-cruft-size &&
+ test_when_finished "rm -fr $repo" &&
+
+ git init "$repo" &&
+
+ (
+ cd "$repo" &&
+
+ git config core.compression 0 &&
+
+ sz=$((1024 * 1024)) && # 1MiB
+ test-tool genrandom foo $sz >foo &&
+ test-tool genrandom bar $sz >bar &&
+ foo="$(git hash-object -w -t blob foo)" &&
+ bar="$(git hash-object -w -t blob bar)" &&
+
+ to=$packdir/pack &&
+ # Pack together foo and bar into a single 2MiB pack.
+ pack="$(git pack-objects $to <<-EOF
+ $foo
+ $bar
+ EOF
+ )" &&
+
+ # Then generate a cruft pack containing foo and bar.
+ #
+ # Generate the pack with --max-pack-size equal to the
+ # size of one object, forcing us to write two cruft
+ # packs.
+ git pack-objects --cruft --max-pack-size=$sz $to <<-EOF &&
+ -pack-$pack.pack
+ EOF
+
+ ls $packdir/pack-*.mtimes >crufts &&
+ test_line_count = 2 crufts &&
+
+ for cruft in $(cat crufts)
+ do
+ test-tool pack-mtimes "$(basename "$cruft")" || return 1
+ done >actual.raw &&
+
+ cut -d" " -f1 <actual.raw | sort >actual &&
+ sort >expect <<-EOF &&
+ $foo
+ $bar
+ EOF
+
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t5333-pseudo-merge-bitmaps.sh b/t/t5333-pseudo-merge-bitmaps.sh
index 3905cb6e4f..56674db562 100755
--- a/t/t5333-pseudo-merge-bitmaps.sh
+++ b/t/t5333-pseudo-merge-bitmaps.sh
@@ -28,9 +28,8 @@ test_pseudo_merges_reused () {
tag_everything () {
git rev-list --all --no-object-names >in &&
- perl -lne '
- print "create refs/tags/" . $. . " " . $1 if /([0-9a-f]+)/
- ' <in | git update-ref --stdin
+ sed 's|\(.*\)|create refs/tags/\1 \1|' in |
+ git update-ref --stdin
}
test_expect_success 'setup' '
@@ -102,7 +101,7 @@ test_expect_success 'stale bitmap traversal with pseudo-merges' '
test_cmp expect actual
'
-test_expect_success 'bitmapPseudoMerge.sampleRate adjusts commit selection rate' '
+test_expect_success PERL_TEST_HELPERS 'bitmapPseudoMerge.sampleRate adjusts commit selection rate' '
test_config bitmapPseudoMerge.test.pattern "refs/tags/" &&
test_config bitmapPseudoMerge.test.maxMerges 1 &&
test_config bitmapPseudoMerge.test.stableThreshold never &&
@@ -235,8 +234,7 @@ test_expect_success 'pseudo-merge pattern with capture groups' '
test_commit_bulk 16 &&
git rev-list HEAD~16.. >in &&
-
- perl -lne "print \"create refs/remotes/$r/tags/\$. \$_\"" <in |
+ sed "s|\(.*\)|create refs/remotes/$r/tags/\1 \1" in |
git update-ref --stdin || return 1
done &&
@@ -252,7 +250,7 @@ test_expect_success 'pseudo-merge pattern with capture groups' '
do
test_pseudo_merge_commits $m >oids &&
grep -f oids refs |
- perl -lne "print \$1 if /refs\/remotes\/([0-9]+)/" |
+ sed -n "s|refs/remotes/\([0-9][0-9]*\)/|\1|p" &&
sort -u || return 1
done >remotes &&
diff --git a/t/t5334-incremental-multi-pack-index.sh b/t/t5334-incremental-multi-pack-index.sh
index 26257e5660..d30d7253d6 100755
--- a/t/t5334-incremental-multi-pack-index.sh
+++ b/t/t5334-incremental-multi-pack-index.sh
@@ -44,4 +44,91 @@ test_expect_success 'convert incremental to non-incremental' '
compare_results_with_midx 'non-incremental MIDX conversion'
+write_midx_layer () {
+ n=1
+ if test -f $midx_chain
+ then
+ n="$(($(wc -l <$midx_chain) + 1))"
+ fi
+
+ for i in 1 2
+ do
+ test_commit $n.$i &&
+ git repack -d || return 1
+ done &&
+ git multi-pack-index write --bitmap --incremental
+}
+
+test_expect_success 'write initial MIDX layer' '
+ git repack -ad &&
+ write_midx_layer
+'
+
+test_expect_success 'read bitmap from first MIDX layer' '
+ git rev-list --test-bitmap 1.2
+'
+
+test_expect_success 'write another MIDX layer' '
+ write_midx_layer
+'
+
+test_expect_success 'midx verify with multiple layers' '
+ test_path_is_file "$midx_chain" &&
+ test_line_count = 2 "$midx_chain" &&
+
+ git multi-pack-index verify
+'
+
+test_expect_success 'read bitmap from second MIDX layer' '
+ git rev-list --test-bitmap 2.2
+'
+
+test_expect_success 'read earlier bitmap from second MIDX layer' '
+ git rev-list --test-bitmap 1.2
+'
+
+test_expect_success 'show object from first pack' '
+ git cat-file -p 1.1
+'
+
+test_expect_success 'show object from second pack' '
+ git cat-file -p 2.2
+'
+
+for reuse in false single multi
+do
+ test_expect_success "full clone (pack.allowPackReuse=$reuse)" '
+ rm -fr clone.git &&
+
+ git config pack.allowPackReuse $reuse &&
+ git clone --no-local --bare . clone.git
+ '
+done
+
+test_expect_success 'relink existing MIDX layer' '
+ rm -fr "$midxdir" &&
+
+ GIT_TEST_MIDX_WRITE_REV=1 git multi-pack-index write --bitmap &&
+
+ midx_hash="$(test-tool read-midx --checksum $objdir)" &&
+
+ test_path_is_file "$packdir/multi-pack-index" &&
+ test_path_is_file "$packdir/multi-pack-index-$midx_hash.bitmap" &&
+ test_path_is_file "$packdir/multi-pack-index-$midx_hash.rev" &&
+
+ test_commit another &&
+ git repack -d &&
+ git multi-pack-index write --bitmap --incremental &&
+
+ test_path_is_missing "$packdir/multi-pack-index" &&
+ test_path_is_missing "$packdir/multi-pack-index-$midx_hash.bitmap" &&
+ test_path_is_missing "$packdir/multi-pack-index-$midx_hash.rev" &&
+
+ test_path_is_file "$midxdir/multi-pack-index-$midx_hash.midx" &&
+ test_path_is_file "$midxdir/multi-pack-index-$midx_hash.bitmap" &&
+ test_path_is_file "$midxdir/multi-pack-index-$midx_hash.rev" &&
+ test_line_count = 2 "$midx_chain"
+
+'
+
test_done
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index 8f018d2f23..83b42ff073 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -275,7 +275,7 @@ extract_ref_advertisement () {
'
}
-test_expect_success 'receive-pack de-dupes .have lines' '
+test_expect_success PERL_TEST_HELPERS 'receive-pack de-dupes .have lines' '
git init shared &&
git -C shared commit --allow-empty -m both &&
git clone -s shared fork &&
diff --git a/t/t5410-receive-pack-alternates.sh b/t/t5410-receive-pack-alternates.sh
index 0b28e4e452..4e82fd102e 100755
--- a/t/t5410-receive-pack-alternates.sh
+++ b/t/t5410-receive-pack-alternates.sh
@@ -17,7 +17,7 @@ test_expect_success 'setup' '
'
extract_haves () {
- depacketize | perl -lne '/^(\S+) \.have/ and print $1'
+ depacketize | sed -n 's/^\([^ ][^ ]*\) \.have/\1/p'
}
test_expect_success 'with core.alternateRefsCommand' '
diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh
index 845ca43ea0..febe441041 100755
--- a/t/t5503-tagfollow.sh
+++ b/t/t5503-tagfollow.sh
@@ -7,6 +7,12 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+if ! test_have_prereq PERL_TEST_HELPERS
+then
+ skip_all='skipping tagfollow tests; Perl not available'
+ test_done
+fi
+
# End state of the repository:
#
# T - tag1 S - tag2
diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh
index 58074506c5..438250c75e 100755
--- a/t/t5504-fetch-receive-strict.sh
+++ b/t/t5504-fetch-receive-strict.sh
@@ -359,7 +359,7 @@ test_expect_success \
grep "Cannot demote unterminatedheader" act
'
-test_expect_success 'badFilemode is not a strict error' '
+test_expect_success PERL_TEST_HELPERS 'badFilemode is not a strict error' '
git init --bare badmode.git &&
tree=$(
cd badmode.git &&
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index bb7e0c6879..bef0250e89 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -499,7 +499,7 @@ test_expect_success 'set-head --auto has no problem w/multiple HEADs' '
cd test &&
git fetch two "refs/heads/*:refs/remotes/two/*" &&
git remote set-head --auto two >output 2>&1 &&
- echo "${SQ}two/HEAD${SQ} is unchanged and points to ${SQ}main${SQ}" >expect &&
+ echo "${SQ}two/HEAD${SQ} is now created and points to ${SQ}main${SQ}" >expect &&
test_cmp expect output
)
'
@@ -1123,7 +1123,7 @@ Pull: refs/heads/main:refs/heads/origin
Pull: refs/heads/next:refs/heads/origin2
EOF
-test_expect_success WITHOUT_BREAKING_CHANGES 'migrate a remote from named file in $GIT_DIR/remotes' '
+test_expect_success !WITH_BREAKING_CHANGES 'migrate a remote from named file in $GIT_DIR/remotes' '
git clone one five &&
origin_url=$(pwd)/one &&
(
@@ -1149,7 +1149,7 @@ test_expect_success WITHOUT_BREAKING_CHANGES 'migrate a remote from named file i
)
'
-test_expect_success WITHOUT_BREAKING_CHANGES 'migrate a remote from named file in $GIT_DIR/branches' '
+test_expect_success !WITH_BREAKING_CHANGES 'migrate a remote from named file in $GIT_DIR/branches' '
git clone --template= one six &&
origin_url=$(pwd)/one &&
(
@@ -1165,7 +1165,7 @@ test_expect_success WITHOUT_BREAKING_CHANGES 'migrate a remote from named file i
)
'
-test_expect_success WITHOUT_BREAKING_CHANGES 'migrate a remote from named file in $GIT_DIR/branches (2)' '
+test_expect_success !WITH_BREAKING_CHANGES 'migrate a remote from named file in $GIT_DIR/branches (2)' '
git clone --template= one seven &&
(
cd seven &&
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 5f350facf5..ebc696546b 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -8,6 +8,12 @@ test_description='Per branch config variables affects "git fetch".
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-bundle.sh
+if ! test_have_prereq PERL_TEST_HELPERS
+then
+ skip_all='skipping fetch tests; Perl not available'
+ test_done
+fi
+
D=$(pwd)
test_expect_success setup '
@@ -119,7 +125,10 @@ test_expect_success "fetch test followRemoteHEAD never" '
cd two &&
git update-ref --no-deref -d refs/remotes/origin/HEAD &&
git config set remote.origin.followRemoteHEAD "never" &&
- git fetch &&
+ GIT_TRACE_PACKET=$PWD/trace.out git fetch &&
+ # Confirm that we do not even ask for HEAD when we are
+ # not going to act on it.
+ test_grep ! "ref-prefix HEAD" trace.out &&
test_must_fail git rev-parse --verify refs/remotes/origin/HEAD
)
'
@@ -250,6 +259,20 @@ test_expect_success "fetch test followRemoteHEAD always" '
)
'
+test_expect_success 'followRemoteHEAD does not kick in with refspecs' '
+ test_when_finished "git config unset remote.origin.followRemoteHEAD" &&
+ (
+ cd "$D" &&
+ cd two &&
+ git remote set-head origin other &&
+ git config set remote.origin.followRemoteHEAD always &&
+ git fetch origin refs/heads/main:refs/remotes/origin/main &&
+ echo refs/remotes/origin/other >expect &&
+ git symbolic-ref refs/remotes/origin/HEAD >actual &&
+ test_cmp expect actual
+ )
+'
+
test_expect_success 'fetch --prune on its own works as expected' '
cd "$D" &&
git clone . prune &&
@@ -537,6 +560,19 @@ test_expect_success 'fetch --atomic --append appends to FETCH_HEAD' '
test_cmp expected atomic/.git/FETCH_HEAD
'
+test_expect_success REFFILES 'fetch --atomic fails transaction if reference locked' '
+ test_when_finished "rm -rf upstream repo" &&
+
+ git init upstream &&
+ git -C upstream commit --allow-empty -m 1 &&
+ git -C upstream switch -c foobar &&
+ git clone --mirror upstream repo &&
+ git -C upstream commit --allow-empty -m 2 &&
+ touch repo/refs/heads/foobar.lock &&
+
+ test_must_fail git -C repo fetch --atomic origin
+'
+
test_expect_success '--refmap="" ignores configured refspec' '
cd "$TRASH_DIRECTORY" &&
git clone "$D" remote-refs &&
diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh
index 4e6026c611..8ac04d742c 100755
--- a/t/t5515-fetch-merge-logic.sh
+++ b/t/t5515-fetch-merge-logic.sh
@@ -104,7 +104,7 @@ test_expect_success setup '
git config remote.config-glob.fetch refs/heads/*:refs/remotes/rem/* &&
remotes="$remotes config-glob" &&
- if test_have_prereq WITHOUT_BREAKING_CHANGES
+ if ! test_have_prereq WITH_BREAKING_CHANGES
then
mkdir -p .git/remotes &&
cat >.git/remotes/remote-explicit <<-\EOF &&
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 2904399e97..dabcc5f811 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -975,7 +975,7 @@ test_expect_success 'allow push to HEAD of non-bare repository (config)' '
! grep "warning: updating the current branch" stderr
'
-test_expect_success WITHOUT_BREAKING_CHANGES 'fetch with branches' '
+test_expect_success !WITH_BREAKING_CHANGES 'fetch with branches' '
mk_empty testrepo &&
git branch second $the_first_commit &&
git checkout second &&
@@ -991,7 +991,7 @@ test_expect_success WITHOUT_BREAKING_CHANGES 'fetch with branches' '
git checkout main
'
-test_expect_success WITHOUT_BREAKING_CHANGES 'fetch with branches containing #' '
+test_expect_success !WITH_BREAKING_CHANGES 'fetch with branches containing #' '
mk_empty testrepo &&
mkdir testrepo/.git/branches &&
echo "..#second" > testrepo/.git/branches/branch2 &&
@@ -1005,7 +1005,7 @@ test_expect_success WITHOUT_BREAKING_CHANGES 'fetch with branches containing #'
git checkout main
'
-test_expect_success WITHOUT_BREAKING_CHANGES 'push with branches' '
+test_expect_success !WITH_BREAKING_CHANGES 'push with branches' '
mk_empty testrepo &&
git checkout second &&
@@ -1022,7 +1022,7 @@ test_expect_success WITHOUT_BREAKING_CHANGES 'push with branches' '
)
'
-test_expect_success WITHOUT_BREAKING_CHANGES 'push with branches containing #' '
+test_expect_success !WITH_BREAKING_CHANGES 'push with branches containing #' '
mk_empty testrepo &&
test_when_finished "rm -rf .git/branches" &&
diff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh
index 3755822629..95d0f33b29 100755
--- a/t/t5532-fetch-proxy.sh
+++ b/t/t5532-fetch-proxy.sh
@@ -4,6 +4,12 @@ test_description='fetching via git:// using core.gitproxy'
. ./test-lib.sh
+if ! test_have_prereq PERL_TEST_HELPERS
+then
+ skip_all='skipping fetch proxy tests; Perl not available'
+ test_done
+fi
+
test_expect_success 'setup remote repo' '
git init remote &&
(cd remote &&
diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh
index c91a62b77a..2a782214ee 100755
--- a/t/t5534-push-signed.sh
+++ b/t/t5534-push-signed.sh
@@ -205,7 +205,7 @@ test_expect_success GPG 'inconsistent push options in signed push not allowed' '
# Tweak the push output to make the push option outside the cert
# different, then replay it on a fresh dst, checking that ff is not
# deleted.
- perl -pe "s/([^ ])bar/\$1baz/" push >push.tweak &&
+ sed "s/\([^ ]\)bar/\1baz/" push >push.tweak &&
prepare_dst &&
git -C dst config receive.certnonceseed sekrit &&
git -C dst config receive.advertisepushoptions 1 &&
diff --git a/t/t5537-fetch-shallow.sh b/t/t5537-fetch-shallow.sh
index 37f7547a4c..6588ce6226 100755
--- a/t/t5537-fetch-shallow.sh
+++ b/t/t5537-fetch-shallow.sh
@@ -271,22 +271,21 @@ test_expect_success 'shallow fetches check connectivity before writing shallow f
git -C "$REPO" config protocol.version 2 &&
git -C client config protocol.version 2 &&
- git -C client fetch --depth=2 "$HTTPD_URL/one_time_perl/repo" main:a_branch &&
+ git -C client fetch --depth=2 "$HTTPD_URL/one_time_script/repo" main:a_branch &&
# Craft a situation in which the server sends back an unshallow request
# with an empty packfile. This is done by refetching with a shorter
# depth (to ensure that the packfile is empty), and overwriting the
# shallow line in the response with the unshallow line we want.
- printf "$(test_oid perl)" \
- "$(git -C "$REPO" rev-parse HEAD)" \
- "$(git -C "$REPO" rev-parse HEAD^)" \
- >"$HTTPD_ROOT_PATH/one-time-perl" &&
+ write_script "$HTTPD_ROOT_PATH/one-time-script" <<-EOF &&
+ sed "$(printf "$(test_oid perl)" "$(git -C "$REPO" rev-parse HEAD)" "$(git -C "$REPO" rev-parse HEAD^)")" "\$1"
+ EOF
test_must_fail env GIT_TEST_SIDEBAND_ALL=0 git -C client \
- fetch --depth=1 "$HTTPD_URL/one_time_perl/repo" \
+ fetch --depth=1 "$HTTPD_URL/one_time_script/repo" \
main:a_branch &&
- # Ensure that the one-time-perl script was used.
- ! test -e "$HTTPD_ROOT_PATH/one-time-perl" &&
+ # Ensure that the one-time-script script was used.
+ ! test -e "$HTTPD_ROOT_PATH/one-time-script" &&
# Ensure that the resulting repo is consistent, despite our failure to
# fetch.
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index 761fdfcfe6..b0d4ea7801 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -7,6 +7,13 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-httpd.sh
+
+if ! test_have_prereq PERL_TEST_HELPERS
+then
+ skip_all='skipping http fetch smart tests; Perl not available'
+ test_done
+fi
+
test "$HTTP_PROTO" = "HTTP/2" && enable_http2
start_httpd
diff --git a/t/t5562-http-backend-content-length.sh b/t/t5562-http-backend-content-length.sh
index f3b158274c..b6ee06f5c8 100755
--- a/t/t5562-http-backend-content-length.sh
+++ b/t/t5562-http-backend-content-length.sh
@@ -4,6 +4,12 @@ test_description='test git-http-backend respects CONTENT_LENGTH'
. ./test-lib.sh
+if ! test_have_prereq PERL_TEST_HELPERS
+then
+ skip_all='skipping http backend content tests; Perl not available'
+ test_done
+fi
+
test_lazy_prereq GZIP 'gzip --version'
verify_http_result() {
diff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh
index f7650e8475..45f384dd32 100755
--- a/t/t5572-pull-submodule.sh
+++ b/t/t5572-pull-submodule.sh
@@ -45,11 +45,6 @@ git_pull_noff () {
$2 git pull --no-ff
}
-if test "$GIT_TEST_MERGE_ALGORITHM" != ort
-then
- KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
- KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
-fi
test_submodule_switch_func "git_pull_noff"
test_expect_success 'setup' '
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index d0c18660e3..d743d986c4 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -649,7 +649,7 @@ test_expect_success 'GIT_TRACE_PACKFILE produces a usable pack' '
git -C replay.git index-pack -v --stdin <tmp.pack
'
-test_expect_success 'clone on case-insensitive fs' '
+test_expect_success PERL_TEST_HELPERS 'clone on case-insensitive fs' '
git init icasefs &&
(
cd icasefs &&
@@ -662,7 +662,7 @@ test_expect_success 'clone on case-insensitive fs' '
)
'
-test_expect_success CASE_INSENSITIVE_FS 'colliding file detection' '
+test_expect_success PERL_TEST_HELPERS,CASE_INSENSITIVE_FS 'colliding file detection' '
grep X icasefs/warning &&
grep x icasefs/warning &&
test_grep "the following paths have collided" icasefs/warning
diff --git a/t/t5605-clone-local.sh b/t/t5605-clone-local.sh
index 4605703496..2397f8fa61 100755
--- a/t/t5605-clone-local.sh
+++ b/t/t5605-clone-local.sh
@@ -156,11 +156,10 @@ test_expect_success 'cloning a local path with --no-local does not hardlink' '
test_expect_success 'cloning a local path with --no-local from a different user succeeds' '
git clone --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \
--no-local a nonlocal-otheruser 2>err &&
- ! repo_is_hardlinked nonlocal-otheruser &&
+ ! repo_is_hardlinked nonlocal-otheruser/.git &&
# Verify that this is a git repository.
git -C nonlocal-otheruser rev-parse --show-toplevel &&
- ! test_grep "detected dubious ownership" err
-
+ test_grep ! "detected dubious ownership" err
'
test_expect_success 'cloning locally respects "-u" for fetching refs' '
diff --git a/t/t5607-clone-bundle.sh b/t/t5607-clone-bundle.sh
index 82e3621ec5..d709bea753 100755
--- a/t/t5607-clone-bundle.sh
+++ b/t/t5607-clone-bundle.sh
@@ -211,4 +211,16 @@ test_expect_success 'git bundle v3 rejects unknown capabilities' '
test_grep "unknown capability .unknown=silly." output
'
+test_expect_success 'cloning bundle suppresses default branch name advice' '
+ test_when_finished "rm -rf bundle-repo clone-repo" &&
+
+ git init bundle-repo &&
+ git -C bundle-repo commit --allow-empty -m init &&
+ git -C bundle-repo bundle create repo.bundle --all &&
+ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
+ git clone --single-branch bundle-repo/repo.bundle clone-repo 2>err &&
+
+ test_grep ! "hint: " err
+'
+
test_done
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index 4650451964..1e354e057f 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -737,18 +737,22 @@ intersperse () {
sed 's/\(..\)/'$1'\1/g'
}
-# Create a one-time-perl command to replace the existing packfile with $1.
+# Create a one-time-script command to replace the existing packfile with $1.
replace_packfile () {
- # The protocol requires that the packfile be sent in sideband 1, hence
- # the extra \x01 byte at the beginning.
- cp $1 "$HTTPD_ROOT_PATH/one-time-pack" &&
- echo 'if (/packfile/) {
- print;
- my $length = -s "one-time-pack";
- printf "%04x\x01", $length + 5;
- print `cat one-time-pack` . "0000";
- last
- }' >"$HTTPD_ROOT_PATH/one-time-perl"
+ cp "$1" one-time-pack &&
+ write_script "$HTTPD_ROOT_PATH/one-time-script" <<-EOF
+ if grep packfile "\$1" >/dev/null
+ then
+ sed '/packfile/q' "\$1" &&
+ # The protocol requires that the packfile be sent in sideband
+ # 1, hence the extra \001 byte at the beginning.
+ printf "%04x\001" \$((\$(wc -c <"$PWD/one-time-pack") + 5)) &&
+ cat "$PWD/one-time-pack" &&
+ printf "0000"
+ else
+ cat "\$1"
+ fi
+ EOF
}
test_expect_success 'upon cloning, check that all refs point to objects' '
@@ -776,12 +780,12 @@ test_expect_success 'upon cloning, check that all refs point to objects' '
# section header.
test_config -C "$SERVER" protocol.version 2 &&
test_must_fail git -c protocol.version=2 clone \
- --filter=blob:none $HTTPD_URL/one_time_perl/server repo 2>err &&
+ --filter=blob:none $HTTPD_URL/one_time_script/server repo 2>err &&
test_grep "did not send all necessary objects" err &&
- # Ensure that the one-time-perl script was used.
- ! test -e "$HTTPD_ROOT_PATH/one-time-perl"
+ # Ensure that the one-time-script script was used.
+ ! test -e "$HTTPD_ROOT_PATH/one-time-script"
'
test_expect_success 'when partial cloning, tolerate server not sending target of tag' '
@@ -818,14 +822,14 @@ test_expect_success 'when partial cloning, tolerate server not sending target of
# Exercise to make sure it works.
git -c protocol.version=2 clone \
- --filter=blob:none $HTTPD_URL/one_time_perl/server repo 2> err &&
+ --filter=blob:none $HTTPD_URL/one_time_script/server repo 2> err &&
! grep "missing object referenced by" err &&
- # Ensure that the one-time-perl script was used.
- ! test -e "$HTTPD_ROOT_PATH/one-time-perl"
+ # Ensure that the one-time-script script was used.
+ ! test -e "$HTTPD_ROOT_PATH/one-time-script"
'
-test_expect_success 'tolerate server sending REF_DELTA against missing promisor objects' '
+test_expect_success PERL_TEST_HELPERS 'tolerate server sending REF_DELTA against missing promisor objects' '
SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server" &&
rm -rf "$SERVER" repo &&
test_create_repo "$SERVER" &&
@@ -845,7 +849,7 @@ test_expect_success 'tolerate server sending REF_DELTA against missing promisor
# Clone. The client has deltabase_have but not deltabase_missing.
git -c protocol.version=2 clone --no-checkout \
- --filter=blob:none $HTTPD_URL/one_time_perl/server repo &&
+ --filter=blob:none $HTTPD_URL/one_time_script/server repo &&
git -C repo hash-object -w -- "$SERVER/have.txt" &&
# Sanity check to ensure that the client does not have
@@ -899,8 +903,8 @@ test_expect_success 'tolerate server sending REF_DELTA against missing promisor
grep "want $(cat deltabase_missing)" trace &&
! grep "want $(cat deltabase_have)" trace &&
- # Ensure that the one-time-perl script was used.
- ! test -e "$HTTPD_ROOT_PATH/one-time-perl"
+ # Ensure that the one-time-script script was used.
+ ! test -e "$HTTPD_ROOT_PATH/one-time-script"
'
# DO NOT add non-httpd-specific tests here, because the last part of this
diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh
index 678a346ed0..d4c28bae39 100755
--- a/t/t5701-git-serve.sh
+++ b/t/t5701-git-serve.sh
@@ -228,7 +228,10 @@ test_expect_success 'ignore very large set of prefixes' '
echo command=ls-refs &&
echo object-format=$(test_oid algo) &&
echo 0001 &&
- perl -le "print \"ref-prefix refs/heads/\$_\" for (1..65536)" &&
+ awk "{
+ for (i = 1; i <= 65536; i++)
+ print \"ref-prefix refs/heads/\", \$i
+ }" &&
echo 0000
} |
test-tool pkt-line pack >in &&
diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh
index 4d0cbe9872..f826ac46a5 100755
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -1174,11 +1174,12 @@ test_expect_success 'when server sends "ready", expect DELIM' '
# After "ready" in the acknowledgments section, pretend that a FLUSH
# (0000) was sent instead of a DELIM (0001).
- printf "\$ready = 1 if /ready/; \$ready && s/0001/0000/" \
- >"$HTTPD_ROOT_PATH/one-time-perl" &&
+ write_script "$HTTPD_ROOT_PATH/one-time-script" <<-\EOF &&
+ sed "/ready/{n;s/0001/0000/;}" "$1"
+ EOF
test_must_fail git -C http_child -c protocol.version=2 \
- fetch "$HTTPD_URL/one_time_perl/http_parent" 2> err &&
+ fetch "$HTTPD_URL/one_time_script/http_parent" 2> err &&
test_grep "expected packfile to be sent after .ready." err
'
@@ -1199,12 +1200,13 @@ test_expect_success 'when server does not send "ready", expect FLUSH' '
# After the acknowledgments section, pretend that a DELIM
# (0001) was sent instead of a FLUSH (0000).
- printf "\$ack = 1 if /acknowledgments/; \$ack && s/0000/0001/" \
- >"$HTTPD_ROOT_PATH/one-time-perl" &&
+ write_script "$HTTPD_ROOT_PATH/one-time-script" <<-\EOF &&
+ sed "/acknowledgments/,//{s/0000/0001/;}" "$1"
+ EOF
test_must_fail env GIT_TRACE_PACKET="$(pwd)/log" git -C http_child \
-c protocol.version=2 \
- fetch "$HTTPD_URL/one_time_perl/http_parent" 2> err &&
+ fetch "$HTTPD_URL/one_time_script/http_parent" 2> err &&
grep "fetch< .*acknowledgments" log &&
! grep "fetch< .*ready" log &&
test_grep "expected no other sections to be sent after no .ready." err
@@ -1490,12 +1492,13 @@ test_expect_success 'http:// --negotiate-only' '
test_expect_success 'http:// --negotiate-only without wait-for-done support' '
SERVER="server" &&
- URI="$HTTPD_URL/one_time_perl/server" &&
+ URI="$HTTPD_URL/one_time_script/server" &&
setup_negotiate_only "$SERVER" "$URI" &&
- echo "s/ wait-for-done/ xxxx-xxx-xxxx/" \
- >"$HTTPD_ROOT_PATH/one-time-perl" &&
+ write_script "$HTTPD_ROOT_PATH/one-time-script" <<-\EOF &&
+ sed "s/ wait-for-done/ xxxx-xxx-xxxx/" "$1"
+ EOF
test_must_fail git -c protocol.version=2 -C client fetch \
--no-tags \
diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh
index 191097171b..249137b467 100755
--- a/t/t5703-upload-pack-ref-in-want.sh
+++ b/t/t5703-upload-pack-ref-in-want.sh
@@ -83,18 +83,15 @@ test_expect_success 'setup repository' '
test_expect_success 'config controls ref-in-want advertisement' '
test-tool serve-v2 --advertise-capabilities >out &&
- perl -ne "/ref-in-want/ and print" out >out.filter &&
- test_must_be_empty out.filter &&
+ test_grep ! "ref-in-want" out &&
git config uploadpack.allowRefInWant false &&
test-tool serve-v2 --advertise-capabilities >out &&
- perl -ne "/ref-in-want/ and print" out >out.filter &&
- test_must_be_empty out.filter &&
+ test_grep ! "ref-in-want" out &&
git config uploadpack.allowRefInWant true &&
test-tool serve-v2 --advertise-capabilities >out &&
- perl -ne "/ref-in-want/ and print" out >out.filter &&
- test_file_not_empty out.filter
+ test_grep "ref-in-want" out
'
test_expect_success 'invalid want-ref line' '
@@ -462,7 +459,7 @@ test_expect_success 'setup repos for change-while-negotiating test' '
test_commit m3 &&
git tag -d m2 m3
) &&
- git -C "$LOCAL_PRISTINE" remote set-url origin "http://127.0.0.1:$LIB_HTTPD_PORT/one_time_perl/repo" &&
+ git -C "$LOCAL_PRISTINE" remote set-url origin "http://127.0.0.1:$LIB_HTTPD_PORT/one_time_script/repo" &&
git -C "$LOCAL_PRISTINE" config protocol.version 2
'
@@ -475,10 +472,12 @@ inconsistency () {
# RPCs during a single negotiation.
oid1=$(git -C "$REPO" rev-parse $1) &&
oid2=$(git -C "$REPO" rev-parse $2) &&
- echo "s/$oid1/$oid2/" >"$HTTPD_ROOT_PATH/one-time-perl"
+ write_script "$HTTPD_ROOT_PATH/one-time-script" <<-EOF
+ sed "s/$oid1/$oid2/" "\$1"
+ EOF
}
-test_expect_success 'server is initially ahead - no ref in want' '
+test_expect_success PERL_TEST_HELPERS 'server is initially ahead - no ref in want' '
git -C "$REPO" config uploadpack.allowRefInWant false &&
rm -rf local &&
cp -r "$LOCAL_PRISTINE" local &&
@@ -487,7 +486,7 @@ test_expect_success 'server is initially ahead - no ref in want' '
test_grep "fatal: remote error: upload-pack: not our ref" err
'
-test_expect_success 'server is initially ahead - ref in want' '
+test_expect_success PERL_TEST_HELPERS 'server is initially ahead - ref in want' '
git -C "$REPO" config uploadpack.allowRefInWant true &&
rm -rf local &&
cp -r "$LOCAL_PRISTINE" local &&
@@ -499,7 +498,7 @@ test_expect_success 'server is initially ahead - ref in want' '
test_cmp expected actual
'
-test_expect_success 'server is initially behind - no ref in want' '
+test_expect_success PERL_TEST_HELPERS 'server is initially behind - no ref in want' '
git -C "$REPO" config uploadpack.allowRefInWant false &&
rm -rf local &&
cp -r "$LOCAL_PRISTINE" local &&
@@ -511,7 +510,7 @@ test_expect_success 'server is initially behind - no ref in want' '
test_cmp expected actual
'
-test_expect_success 'server is initially behind - ref in want' '
+test_expect_success PERL_TEST_HELPERS 'server is initially behind - ref in want' '
git -C "$REPO" config uploadpack.allowRefInWant true &&
rm -rf local &&
cp -r "$LOCAL_PRISTINE" local &&
@@ -523,11 +522,13 @@ test_expect_success 'server is initially behind - ref in want' '
test_cmp expected actual
'
-test_expect_success 'server loses a ref - ref in want' '
+test_expect_success PERL_TEST_HELPERS 'server loses a ref - ref in want' '
git -C "$REPO" config uploadpack.allowRefInWant true &&
rm -rf local &&
cp -r "$LOCAL_PRISTINE" local &&
- echo "s/main/rain/" >"$HTTPD_ROOT_PATH/one-time-perl" &&
+ write_script "$HTTPD_ROOT_PATH/one-time-script" <<-\EOF &&
+ sed "s/main/rain/" "$1"
+ EOF
test_must_fail git -C local fetch 2>err &&
test_grep "fatal: remote error: unknown ref refs/heads/rain" err
diff --git a/t/t5710-promisor-remote-capability.sh b/t/t5710-promisor-remote-capability.sh
index d2cc69a17e..cb061b1f35 100755
--- a/t/t5710-promisor-remote-capability.sh
+++ b/t/t5710-promisor-remote-capability.sh
@@ -4,6 +4,12 @@ test_description='handling of promisor remote advertisement'
. ./test-lib.sh
+if ! test_have_prereq PERL_TEST_HELPERS
+then
+ skip_all='skipping promisor remote capabilities tests; Perl not available'
+ test_done
+fi
+
GIT_TEST_MULTI_PACK_INDEX=0
GIT_TEST_MULTI_PACK_INDEX_WRITE_INCREMENTAL=0
@@ -93,6 +99,7 @@ test_expect_success "setup for testing promisor remote advertisement" '
test_expect_success "clone with promisor.advertise set to 'true'" '
git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
# Clone from server to create a client
GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
@@ -100,7 +107,6 @@ test_expect_success "clone with promisor.advertise set to 'true'" '
-c remote.lop.url="file://$(pwd)/lop" \
-c promisor.acceptfromserver=All \
--no-local --filter="blob:limit=5k" server client &&
- test_when_finished "rm -rf client" &&
# Check that the largest object is still missing on the server
check_missing_objects server 1 "$oid"
@@ -108,6 +114,7 @@ test_expect_success "clone with promisor.advertise set to 'true'" '
test_expect_success "clone with promisor.advertise set to 'false'" '
git -C server config promisor.advertise false &&
+ test_when_finished "rm -rf client" &&
# Clone from server to create a client
GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
@@ -115,7 +122,6 @@ test_expect_success "clone with promisor.advertise set to 'false'" '
-c remote.lop.url="file://$(pwd)/lop" \
-c promisor.acceptfromserver=All \
--no-local --filter="blob:limit=5k" server client &&
- test_when_finished "rm -rf client" &&
# Check that the largest object is not missing on the server
check_missing_objects server 0 "" &&
@@ -126,6 +132,7 @@ test_expect_success "clone with promisor.advertise set to 'false'" '
test_expect_success "clone with promisor.acceptfromserver set to 'None'" '
git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
# Clone from server to create a client
GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
@@ -133,7 +140,6 @@ test_expect_success "clone with promisor.acceptfromserver set to 'None'" '
-c remote.lop.url="file://$(pwd)/lop" \
-c promisor.acceptfromserver=None \
--no-local --filter="blob:limit=5k" server client &&
- test_when_finished "rm -rf client" &&
# Check that the largest object is not missing on the server
check_missing_objects server 0 "" &&
@@ -144,8 +150,8 @@ test_expect_success "clone with promisor.acceptfromserver set to 'None'" '
test_expect_success "init + fetch with promisor.advertise set to 'true'" '
git -C server config promisor.advertise true &&
-
test_when_finished "rm -rf client" &&
+
mkdir client &&
git -C client init &&
git -C client config remote.lop.promisor true &&
@@ -162,6 +168,7 @@ test_expect_success "init + fetch with promisor.advertise set to 'true'" '
test_expect_success "clone with promisor.acceptfromserver set to 'KnownName'" '
git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
# Clone from server to create a client
GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
@@ -169,7 +176,6 @@ test_expect_success "clone with promisor.acceptfromserver set to 'KnownName'" '
-c remote.lop.url="file://$(pwd)/lop" \
-c promisor.acceptfromserver=KnownName \
--no-local --filter="blob:limit=5k" server client &&
- test_when_finished "rm -rf client" &&
# Check that the largest object is still missing on the server
check_missing_objects server 1 "$oid"
@@ -177,6 +183,7 @@ test_expect_success "clone with promisor.acceptfromserver set to 'KnownName'" '
test_expect_success "clone with 'KnownName' and different remote names" '
git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
# Clone from server to create a client
GIT_NO_LAZY_FETCH=0 git clone -c remote.serverTwo.promisor=true \
@@ -184,8 +191,26 @@ test_expect_success "clone with 'KnownName' and different remote names" '
-c remote.serverTwo.url="file://$(pwd)/lop" \
-c promisor.acceptfromserver=KnownName \
--no-local --filter="blob:limit=5k" server client &&
+
+ # Check that the largest object is not missing on the server
+ check_missing_objects server 0 "" &&
+
+ # Reinitialize server so that the largest object is missing again
+ initialize_server 1 "$oid"
+'
+
+test_expect_success "clone with 'KnownName' and missing URL in the config" '
+ git -C server config promisor.advertise true &&
test_when_finished "rm -rf client" &&
+ # Clone from server to create a client
+ # Lazy fetching by the client from the LOP will fail because of the
+ # missing URL in the client config, so the server will have to lazy
+ # fetch from the LOP.
+ GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
+ -c promisor.acceptfromserver=KnownName \
+ --no-local --filter="blob:limit=5k" server client &&
+
# Check that the largest object is not missing on the server
check_missing_objects server 0 "" &&
@@ -195,6 +220,7 @@ test_expect_success "clone with 'KnownName' and different remote names" '
test_expect_success "clone with promisor.acceptfromserver set to 'KnownUrl'" '
git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
# Clone from server to create a client
GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
@@ -202,7 +228,6 @@ test_expect_success "clone with promisor.acceptfromserver set to 'KnownUrl'" '
-c remote.lop.url="file://$(pwd)/lop" \
-c promisor.acceptfromserver=KnownUrl \
--no-local --filter="blob:limit=5k" server client &&
- test_when_finished "rm -rf client" &&
# Check that the largest object is still missing on the server
check_missing_objects server 1 "$oid"
@@ -212,6 +237,7 @@ test_expect_success "clone with 'KnownUrl' and different remote urls" '
ln -s lop serverTwo &&
git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
# Clone from server to create a client
GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
@@ -219,7 +245,6 @@ test_expect_success "clone with 'KnownUrl' and different remote urls" '
-c remote.lop.url="file://$(pwd)/serverTwo" \
-c promisor.acceptfromserver=KnownUrl \
--no-local --filter="blob:limit=5k" server client &&
- test_when_finished "rm -rf client" &&
# Check that the largest object is not missing on the server
check_missing_objects server 0 "" &&
@@ -228,6 +253,48 @@ test_expect_success "clone with 'KnownUrl' and different remote urls" '
initialize_server 1 "$oid"
'
+test_expect_success "clone with 'KnownUrl' and url not configured on the server" '
+ git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
+
+ test_when_finished "git -C server config set remote.lop.url \"file://$(pwd)/lop\"" &&
+ git -C server config unset remote.lop.url &&
+
+ # Clone from server to create a client
+ # It should fail because the client will reject the LOP as URLs are
+ # different, and the server cannot lazy fetch as the LOP URL is
+ # missing, so the remote name will be used instead which will fail.
+ test_must_fail env GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
+ -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+ -c remote.lop.url="file://$(pwd)/lop" \
+ -c promisor.acceptfromserver=KnownUrl \
+ --no-local --filter="blob:limit=5k" server client &&
+
+ # Check that the largest object is still missing on the server
+ check_missing_objects server 1 "$oid"
+'
+
+test_expect_success "clone with 'KnownUrl' and empty url, so not advertised" '
+ git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
+
+ test_when_finished "git -C server config set remote.lop.url \"file://$(pwd)/lop\"" &&
+ git -C server config set remote.lop.url "" &&
+
+ # Clone from server to create a client
+ # It should fail because the client will reject the LOP as an empty URL is
+ # not advertised, and the server cannot lazy fetch as the LOP URL is empty,
+ # so the remote name will be used instead which will fail.
+ test_must_fail env GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
+ -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+ -c remote.lop.url="file://$(pwd)/lop" \
+ -c promisor.acceptfromserver=KnownUrl \
+ --no-local --filter="blob:limit=5k" server client &&
+
+ # Check that the largest object is still missing on the server
+ check_missing_objects server 1 "$oid"
+'
+
test_expect_success "clone with promisor.advertise set to 'true' but don't delete the client" '
git -C server config promisor.advertise true &&
diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh
index 6289a2e8b0..fec16448cf 100755
--- a/t/t6000-rev-list-misc.sh
+++ b/t/t6000-rev-list-misc.sh
@@ -182,4 +182,70 @@ test_expect_success 'rev-list --unpacked' '
test_cmp expect actual
'
+test_expect_success 'rev-list one-sided unrelated symmetric diff' '
+ test_tick &&
+ git commit --allow-empty -m xyz &&
+ git branch cmp &&
+ git rebase --force-rebase --root &&
+
+ git rev-list --left-only HEAD...cmp >head &&
+ git rev-list --right-only HEAD...cmp >cmp &&
+
+ sort head >head.sorted &&
+ sort cmp >cmp.sorted &&
+ comm -12 head.sorted cmp.sorted >actual &&
+ test_line_count = 0 actual
+'
+
+test_expect_success 'rev-list -z' '
+ test_when_finished rm -rf repo &&
+
+ git init repo &&
+ test_commit -C repo 1 &&
+ test_commit -C repo 2 &&
+
+ oid1=$(git -C repo rev-parse HEAD~) &&
+ oid2=$(git -C repo rev-parse HEAD) &&
+
+ printf "%s\0%s\0" "$oid2" "$oid1" >expect &&
+ git -C repo rev-list -z HEAD >actual &&
+
+ test_cmp expect actual
+'
+
+test_expect_success 'rev-list -z --objects' '
+ test_when_finished rm -rf repo &&
+
+ git init repo &&
+ test_commit -C repo 1 &&
+ test_commit -C repo 2 &&
+
+ oid1=$(git -C repo rev-parse HEAD:1.t) &&
+ oid2=$(git -C repo rev-parse HEAD:2.t) &&
+ path1=1.t &&
+ path2=2.t &&
+
+ printf "%s\0path=%s\0%s\0path=%s\0" "$oid1" "$path1" "$oid2" "$path2" \
+ >expect &&
+ git -C repo rev-list -z --objects HEAD:1.t HEAD:2.t >actual &&
+
+ test_cmp expect actual
+'
+
+test_expect_success 'rev-list -z --boundary' '
+ test_when_finished rm -rf repo &&
+
+ git init repo &&
+ test_commit -C repo 1 &&
+ test_commit -C repo 2 &&
+
+ oid1=$(git -C repo rev-parse HEAD~) &&
+ oid2=$(git -C repo rev-parse HEAD) &&
+
+ printf "%s\0%s\0boundary=yes\0" "$oid2" "$oid1" >expect &&
+ git -C repo rev-list -z --boundary HEAD~.. >actual &&
+
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t6011-rev-list-with-bad-commit.sh b/t/t6011-rev-list-with-bad-commit.sh
index bad02cf5b8..b6f3344dbf 100755
--- a/t/t6011-rev-list-with-bad-commit.sh
+++ b/t/t6011-rev-list-with-bad-commit.sh
@@ -35,11 +35,15 @@ test_expect_success 'verify number of revisions' \
first_commit=$(git rev-parse HEAD~3)
'
-test_expect_success 'corrupt second commit object' \
- '
- perl -i.bak -pe "s/second commit/socond commit/" .git/objects/pack/*.pack &&
- test_must_fail git fsck --full
- '
+test_expect_success 'corrupt second commit object' '
+ for p in .git/objects/pack/*.pack
+ do
+ sed "s/second commit/socond commit/" "$p" >"$p.munged" &&
+ mv "$p.munged" "$p" ||
+ return 1
+ done &&
+ test_must_fail git fsck --full
+'
test_expect_success 'rev-list should fail' '
test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git -c core.commitGraph=false rev-list --all > /dev/null
diff --git a/t/t6013-rev-list-reverse-parents.sh b/t/t6013-rev-list-reverse-parents.sh
index 39793cbbd6..273196f52b 100755
--- a/t/t6013-rev-list-reverse-parents.sh
+++ b/t/t6013-rev-list-reverse-parents.sh
@@ -26,17 +26,19 @@ test_expect_success 'set up --reverse example' '
commit five
'
+reverse () {
+ awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--] }'
+}
+
test_expect_success '--reverse --parents --full-history combines correctly' '
- git rev-list --parents --full-history main -- foo |
- perl -e "print reverse <>" > expected &&
+ git rev-list --parents --full-history main -- foo | reverse >expected &&
git rev-list --reverse --parents --full-history main -- foo \
> actual &&
test_cmp expected actual
'
test_expect_success '--boundary does too' '
- git rev-list --boundary --parents --full-history main ^root -- foo |
- perl -e "print reverse <>" > expected &&
+ git rev-list --boundary --parents --full-history main ^root -- foo | reverse >expected &&
git rev-list --boundary --reverse --parents --full-history \
main ^root -- foo > actual &&
test_cmp expected actual
diff --git a/t/t6020-bundle-misc.sh b/t/t6020-bundle-misc.sh
index b3807e8f35..500c81b8a1 100755
--- a/t/t6020-bundle-misc.sh
+++ b/t/t6020-bundle-misc.sh
@@ -673,6 +673,59 @@ test_expect_success 'bundle progress with --no-quiet' '
grep "%" err
'
+test_expect_success 'create bundle with duplicate refnames' '
+ git bundle create out.bdl "main" "main" &&
+
+ git bundle list-heads out.bdl |
+ make_user_friendly_and_stable_output >actual &&
+ cat >expect <<-\EOF &&
+ <COMMIT-P> refs/heads/main
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'create bundle with duplicate refnames and --all' '
+ git bundle create out.bdl --all "main" "main" &&
+
+ git bundle list-heads out.bdl |
+ make_user_friendly_and_stable_output >actual &&
+ cat >expect <<-\EOF &&
+ <COMMIT-P> refs/heads/main
+ <COMMIT-N> refs/heads/release
+ <COMMIT-D> refs/heads/topic/1
+ <COMMIT-H> refs/heads/topic/2
+ <COMMIT-D> refs/pull/1/head
+ <COMMIT-G> refs/pull/2/head
+ <TAG-1> refs/tags/v1
+ <TAG-2> refs/tags/v2
+ <TAG-3> refs/tags/v3
+ <COMMIT-P> HEAD
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'create bundle with duplicate exlusion refnames' '
+ git bundle create out.bdl "main" "main^!" &&
+
+ git bundle list-heads out.bdl |
+ make_user_friendly_and_stable_output >actual &&
+ cat >expect <<-\EOF &&
+ <COMMIT-P> refs/heads/main
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'create bundle with duplicate refname short-form' '
+ git bundle create out.bdl "main" "main" "refs/heads/main" "refs/heads/main" &&
+
+ git bundle list-heads out.bdl |
+ make_user_friendly_and_stable_output >actual &&
+ cat >expect <<-\EOF &&
+ <COMMIT-P> refs/heads/main
+ EOF
+ test_cmp expect actual
+'
+
test_expect_success 'read bundle over stdin' '
git bundle create some.bundle HEAD &&
diff --git a/t/t6022-rev-list-missing.sh b/t/t6022-rev-list-missing.sh
index 3e2790d4c8..08e92dd002 100755
--- a/t/t6022-rev-list-missing.sh
+++ b/t/t6022-rev-list-missing.sh
@@ -198,4 +198,35 @@ do
'
done
+test_expect_success "-z nul-delimited --missing" '
+ test_when_finished rm -rf repo &&
+
+ git init repo &&
+ (
+ cd repo &&
+ git commit --allow-empty -m first &&
+
+ path="foo bar" &&
+ echo foobar >"$path" &&
+ git add -A &&
+ git commit -m second &&
+
+ oid=$(git rev-parse "HEAD:$path") &&
+ type="$(git cat-file -t $oid)" &&
+
+ obj_path=".git/objects/$(test_oid_to_path $oid)" &&
+
+ git rev-list -z --objects --no-object-names \
+ HEAD ^"$oid" >expect &&
+ printf "%s\0missing=yes\0path=%s\0type=%s\0" "$oid" "$path" \
+ "$type" >>expect &&
+
+ mv "$obj_path" "$obj_path.hidden" &&
+ git rev-list -z --objects --no-object-names \
+ --missing=print-info HEAD >actual &&
+
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t6102-rev-list-unexpected-objects.sh b/t/t6102-rev-list-unexpected-objects.sh
index 22dfd6d978..eb98b3919c 100755
--- a/t/t6102-rev-list-unexpected-objects.sh
+++ b/t/t6102-rev-list-unexpected-objects.sh
@@ -4,6 +4,12 @@ test_description='git rev-list should handle unexpected object types'
. ./test-lib.sh
+if ! test_have_prereq PERL_TEST_HELPERS
+then
+ skip_all='skipping rev-list unexpected objects tests; Perl not available'
+ test_done
+fi
+
test_expect_success 'setup well-formed objects' '
blob="$(printf "foo" | git hash-object -w --stdin)" &&
tree="$(printf "100644 blob $blob\tfoo" | git mktree)" &&
diff --git a/t/t6115-rev-list-du.sh b/t/t6115-rev-list-du.sh
index 3385fe9f13..04c577dad6 100755
--- a/t/t6115-rev-list-du.sh
+++ b/t/t6115-rev-list-du.sh
@@ -22,7 +22,7 @@ test_expect_success 'set up repository' '
disk_usage_slow () {
git rev-list --no-object-names "$@" |
git cat-file --batch-check="%(objectsize:disk)" |
- perl -lne '$total += $_; END { print $total}'
+ awk '{ i += $1 } END { print i }'
}
# check behavior with given rev-list options; note that
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 76843a6169..256ccaefb7 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -292,15 +292,23 @@ test_expect_success 'name-rev --annotate-stdin' '
echo "$rev ($name)" >>expect.unsorted || return 1
done &&
sort <expect.unsorted >expect &&
- git rev-list --all | git name-rev --annotate-stdin >actual.unsorted &&
+ git rev-list --all >list &&
+ git name-rev --annotate-stdin <list >actual.unsorted &&
sort <actual.unsorted >actual &&
test_cmp expect actual
'
-test_expect_success 'name-rev --stdin deprecated' "
- git rev-list --all | git name-rev --stdin 2>actual &&
- grep -E 'warning: --stdin is deprecated' actual
-"
+test_expect_success 'name-rev --stdin deprecated' '
+ git rev-list --all >list &&
+ if ! test_have_prereq WITH_BREAKING_CHANGES
+ then
+ git name-rev --stdin <list 2>actual &&
+ test_grep "warning: --stdin is deprecated" actual
+ else
+ test_must_fail git name-rev --stdin <list 2>actual &&
+ test_grep "unknown option .stdin." actual
+ fi
+'
test_expect_success 'describe --contains with the exact tags' '
echo "A^0" >expect &&
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 9b4f4306c4..ce9af79ab1 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -1216,7 +1216,7 @@ test_expect_success '%(raw) with --tcl must fail' '
test_must_fail git for-each-ref --format="%(raw)" --tcl
'
-test_expect_success '%(raw) with --perl' '
+test_expect_success PERL_TEST_HELPERS '%(raw) with --perl' '
git for-each-ref --format="\$name= %(raw);
print \"\$name\"" refs/myblobs/blob1 --perl | perl >actual &&
cmp blob1 actual &&
@@ -1443,9 +1443,14 @@ test_expect_success 'set up trailers for next test' '
'
test_trailer_option () {
+ if test "$#" -eq 3
+ then
+ prereq="$1"
+ shift
+ fi &&
title=$1 option=$2
cat >expect
- test_expect_success "$title" '
+ test_expect_success $prereq "$title" '
git for-each-ref --format="%($option)" refs/heads/main >actual &&
test_cmp expect actual &&
git for-each-ref --format="%(contents:$option)" refs/heads/main >actual &&
@@ -1453,7 +1458,7 @@ test_trailer_option () {
'
}
-test_trailer_option '%(trailers:unfold) unfolds trailers' \
+test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) unfolds trailers' \
'trailers:unfold' <<-EOF
$(unfold <trailers)
@@ -1483,13 +1488,13 @@ test_trailer_option '%(trailers:only=no) shows all trailers' \
EOF
-test_trailer_option '%(trailers:only) and %(trailers:unfold) work together' \
+test_trailer_option PERL_TEST_HELPERS '%(trailers:only) and %(trailers:unfold) work together' \
'trailers:only,unfold' <<-EOF
$(grep -v patch.description <trailers | unfold)
EOF
-test_trailer_option '%(trailers:unfold) and %(trailers:only) work together' \
+test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) and %(trailers:only) work together' \
'trailers:unfold,only' <<-EOF
$(grep -v patch.description <trailers | unfold)
diff --git a/t/t6400-merge-df.sh b/t/t6400-merge-df.sh
index 3de4ef6bd9..b46fe49440 100755
--- a/t/t6400-merge-df.sh
+++ b/t/t6400-merge-df.sh
@@ -84,12 +84,7 @@ test_expect_success 'modify/delete + directory/file conflict' '
test_stdout_line_count = 5 git ls-files -s &&
test_stdout_line_count = 4 git ls-files -u &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_stdout_line_count = 0 git ls-files -o
- else
- test_stdout_line_count = 1 git ls-files -o
- fi &&
+ test_stdout_line_count = 0 git ls-files -o &&
test_path_is_file letters/file &&
test_path_is_file letters.txt &&
@@ -105,12 +100,7 @@ test_expect_success 'modify/delete + directory/file conflict; other way' '
test_stdout_line_count = 5 git ls-files -s &&
test_stdout_line_count = 4 git ls-files -u &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_stdout_line_count = 0 git ls-files -o
- else
- test_stdout_line_count = 1 git ls-files -o
- fi &&
+ test_stdout_line_count = 0 git ls-files -o &&
test_path_is_file letters/file &&
test_path_is_file letters.txt &&
diff --git a/t/t6402-merge-rename.sh b/t/t6402-merge-rename.sh
index 2738b50c2a..ff00b74e9c 100755
--- a/t/t6402-merge-rename.sh
+++ b/t/t6402-merge-rename.sh
@@ -313,12 +313,7 @@ test_expect_success 'Rename+D/F conflict; renamed file merges but dir in way' '
test_grep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
test_grep "Auto-merging dir" output &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_grep "moving it to dir~HEAD instead" output
- else
- test_grep "Adding as dir~HEAD instead" output
- fi &&
+ test_grep "moving it to dir~HEAD instead" output &&
test_stdout_line_count = 3 git ls-files -u &&
test_stdout_line_count = 2 git ls-files -u dir/file-in-the-way &&
@@ -340,12 +335,7 @@ test_expect_success 'Same as previous, but merged other way' '
! grep "error: refusing to lose untracked file at" errors &&
test_grep "CONFLICT (modify/delete): dir/file-in-the-way" output &&
test_grep "Auto-merging dir" output &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_grep "moving it to dir~renamed-file-has-no-conflicts instead" output
- else
- test_grep "Adding as dir~renamed-file-has-no-conflicts instead" output
- fi &&
+ test_grep "moving it to dir~renamed-file-has-no-conflicts instead" output &&
test_stdout_line_count = 3 git ls-files -u &&
test_stdout_line_count = 2 git ls-files -u dir/file-in-the-way &&
@@ -400,14 +390,7 @@ test_expect_success 'Rename+D/F conflict; renamed file cannot merge and dir in t
test_must_fail git merge --strategy=recursive dir-in-way &&
test_stdout_line_count = 5 git ls-files -u &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_stdout_line_count = 3 git ls-files -u dir~HEAD
- else
- git ls-files -u dir >out &&
- test 3 -eq $(grep -v file-in-the-way out | wc -l) &&
- rm -f out
- fi &&
+ test_stdout_line_count = 3 git ls-files -u dir~HEAD &&
test_stdout_line_count = 2 git ls-files -u dir/file-in-the-way &&
test_must_fail git diff --quiet &&
@@ -425,14 +408,7 @@ test_expect_success 'Same as previous, but merged other way' '
test_must_fail git merge --strategy=recursive renamed-file-has-conflicts &&
test_stdout_line_count = 5 git ls-files -u &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_stdout_line_count = 3 git ls-files -u dir~renamed-file-has-conflicts
- else
- git ls-files -u dir >out &&
- test 3 -eq $(grep -v file-in-the-way out | wc -l) &&
- rm -f out
- fi &&
+ test_stdout_line_count = 3 git ls-files -u dir~renamed-file-has-conflicts &&
test_stdout_line_count = 2 git ls-files -u dir/file-in-the-way &&
test_must_fail git diff --quiet &&
@@ -488,12 +464,7 @@ test_expect_success 'both rename source and destination involved in D/F conflict
git checkout -q rename-dest^0 &&
test_must_fail git merge --strategy=recursive source-conflict &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_stdout_line_count = 2 git ls-files -u
- else
- test_stdout_line_count = 1 git ls-files -u
- fi &&
+ test_stdout_line_count = 2 git ls-files -u &&
test_must_fail git diff --quiet &&
@@ -527,63 +498,33 @@ test_expect_success 'setup pair rename to parent of other (D/F conflicts)' '
git commit -m "Rename one/file -> two"
'
-if test "$GIT_TEST_MERGE_ALGORITHM" = ort
-then
- test_expect_success 'pair rename to parent of other (D/F conflicts) w/ untracked dir' '
- git checkout -q rename-one^0 &&
- mkdir one &&
- test_must_fail git merge --strategy=recursive rename-two &&
-
- test_stdout_line_count = 4 git ls-files -u &&
- test_stdout_line_count = 2 git ls-files -u one &&
- test_stdout_line_count = 2 git ls-files -u two &&
-
- test_must_fail git diff --quiet &&
-
- test 3 -eq $(find . | grep -v .git | wc -l) &&
-
- test_path_is_file one &&
- test_path_is_file two &&
- test "other" = $(cat one) &&
- test "stuff" = $(cat two)
- '
-else
- test_expect_success 'pair rename to parent of other (D/F conflicts) w/ untracked dir' '
- git checkout -q rename-one^0 &&
- mkdir one &&
- test_must_fail git merge --strategy=recursive rename-two &&
+test_expect_success 'pair rename to parent of other (D/F conflicts) w/ untracked dir' '
+ git checkout -q rename-one^0 &&
+ mkdir one &&
+ test_must_fail git merge --strategy=recursive rename-two &&
- test_stdout_line_count = 2 git ls-files -u &&
- test_stdout_line_count = 1 git ls-files -u one &&
- test_stdout_line_count = 1 git ls-files -u two &&
+ test_stdout_line_count = 4 git ls-files -u &&
+ test_stdout_line_count = 2 git ls-files -u one &&
+ test_stdout_line_count = 2 git ls-files -u two &&
- test_must_fail git diff --quiet &&
+ test_must_fail git diff --quiet &&
- test 4 -eq $(find . | grep -v .git | wc -l) &&
+ test 3 -eq $(find . | grep -v .git | wc -l) &&
- test_path_is_dir one &&
- test_path_is_file one~rename-two &&
- test_path_is_file two &&
- test "other" = $(cat one~rename-two) &&
- test "stuff" = $(cat two)
- '
-fi
+ test_path_is_file one &&
+ test_path_is_file two &&
+ test "other" = $(cat one) &&
+ test "stuff" = $(cat two)
+'
test_expect_success 'pair rename to parent of other (D/F conflicts) w/ clean start' '
git reset --hard &&
git clean -fdqx &&
test_must_fail git merge --strategy=recursive rename-two &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_stdout_line_count = 4 git ls-files -u &&
- test_stdout_line_count = 2 git ls-files -u one &&
- test_stdout_line_count = 2 git ls-files -u two
- else
- test_stdout_line_count = 2 git ls-files -u &&
- test_stdout_line_count = 1 git ls-files -u one &&
- test_stdout_line_count = 1 git ls-files -u two
- fi &&
+ test_stdout_line_count = 4 git ls-files -u &&
+ test_stdout_line_count = 2 git ls-files -u one &&
+ test_stdout_line_count = 2 git ls-files -u two &&
test_must_fail git diff --quiet &&
@@ -623,22 +564,12 @@ test_expect_success 'check handling of differently renamed file with D/F conflic
git checkout -q first-rename^0 &&
test_must_fail git merge --strategy=recursive second-rename &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_stdout_line_count = 5 git ls-files -s &&
- test_stdout_line_count = 3 git ls-files -u &&
- test_stdout_line_count = 1 git ls-files -u one~HEAD &&
- test_stdout_line_count = 1 git ls-files -u two~second-rename &&
- test_stdout_line_count = 1 git ls-files -u original &&
- test_stdout_line_count = 0 git ls-files -o
- else
- test_stdout_line_count = 5 git ls-files -s &&
- test_stdout_line_count = 3 git ls-files -u &&
- test_stdout_line_count = 1 git ls-files -u one &&
- test_stdout_line_count = 1 git ls-files -u two &&
- test_stdout_line_count = 1 git ls-files -u original &&
- test_stdout_line_count = 2 git ls-files -o
- fi &&
+ test_stdout_line_count = 5 git ls-files -s &&
+ test_stdout_line_count = 3 git ls-files -u &&
+ test_stdout_line_count = 1 git ls-files -u one~HEAD &&
+ test_stdout_line_count = 1 git ls-files -u two~second-rename &&
+ test_stdout_line_count = 1 git ls-files -u original &&
+ test_stdout_line_count = 0 git ls-files -o &&
test_path_is_file one/file &&
test_path_is_file two/file &&
diff --git a/t/t6404-recursive-merge.sh b/t/t6404-recursive-merge.sh
index ae687f2ce5..346f3608b9 100755
--- a/t/t6404-recursive-merge.sh
+++ b/t/t6404-recursive-merge.sh
@@ -108,12 +108,7 @@ test_expect_success 'refuse to merge binary files' '
printf "\0\0" >binary-file &&
git add binary-file &&
git commit -m binary2 &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_must_fail git merge F >merge_output
- else
- test_must_fail git merge F 2>merge_output
- fi &&
+ test_must_fail git merge F >merge_output &&
grep "Cannot merge binary files: binary-file (HEAD vs. F)" merge_output
'
@@ -129,22 +124,12 @@ test_expect_success 'mark rename/delete as unmerged' '
test_tick &&
git commit -m rename &&
test_must_fail git merge delete &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test 2 = $(git ls-files --unmerged | wc -l)
- else
- test 1 = $(git ls-files --unmerged | wc -l)
- fi &&
+ test 2 = $(git ls-files --unmerged | wc -l) &&
git rev-parse --verify :2:a2 &&
test_must_fail git rev-parse --verify :3:a2 &&
git checkout -f delete &&
test_must_fail git merge rename &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test 2 = $(git ls-files --unmerged | wc -l)
- else
- test 1 = $(git ls-files --unmerged | wc -l)
- fi &&
+ test 2 = $(git ls-files --unmerged | wc -l) &&
test_must_fail git rev-parse --verify :2:a2 &&
git rev-parse --verify :3:a2
'
diff --git a/t/t6406-merge-attr.sh b/t/t6406-merge-attr.sh
index 66e01464b5..8f6fbef002 100755
--- a/t/t6406-merge-attr.sh
+++ b/t/t6406-merge-attr.sh
@@ -259,12 +259,7 @@ test_expect_success 'binary files with union attribute' '
printf "two\0" >bin.txt &&
git commit -am two &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_must_fail git merge bin-main >output
- else
- test_must_fail git merge bin-main 2>output
- fi &&
+ test_must_fail git merge bin-main >output &&
grep -i "warning.*cannot merge.*HEAD vs. bin-main" output
'
diff --git a/t/t6416-recursive-corner-cases.sh b/t/t6416-recursive-corner-cases.sh
index 17b54d625d..ed20de8ea2 100755
--- a/t/t6416-recursive-corner-cases.sh
+++ b/t/t6416-recursive-corner-cases.sh
@@ -6,7 +6,6 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-merge.sh
#
# L1 L2
@@ -529,15 +528,8 @@ test_expect_success 'setup differently handled merges of directory/file conflict
git checkout B^0 &&
test_must_fail git merge C^0 &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- git rm -rf a/ &&
- git rm a~HEAD
- else
- git clean -fd &&
- git rm -rf a/ &&
- git rm a
- fi &&
+ git rm -rf a/ &&
+ git rm a~HEAD &&
git cat-file -p B:a >a2 &&
git add a2 &&
git commit -m D2 &&
@@ -556,12 +548,7 @@ test_expect_success 'setup differently handled merges of directory/file conflict
git checkout C^0 &&
test_must_fail git merge B^0 &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- git rm a~B^0
- else
- git clean -fd
- fi &&
+ git rm a~B^0 &&
git rm -rf a/ &&
test_write_lines 1 2 3 4 5 6 7 8 >a &&
git add a &&
@@ -570,15 +557,8 @@ test_expect_success 'setup differently handled merges of directory/file conflict
git checkout C^0 &&
test_must_fail git merge B^0 &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- git rm -rf a/ &&
- git rm a~B^0
- else
- git clean -fd &&
- git rm -rf a/ &&
- git rm a
- fi &&
+ git rm -rf a/ &&
+ git rm a~B^0 &&
test_write_lines 1 2 3 4 5 6 7 8 >a2 &&
git add a2 &&
git commit -m E4 &&
@@ -596,34 +576,16 @@ test_expect_success 'merge of D1 & E1 fails but has appropriate contents' '
test_must_fail git merge -s recursive E1^0 &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- git ls-files -s >out &&
- test_line_count = 3 out &&
- git ls-files -u >out &&
- test_line_count = 2 out &&
- git ls-files -o >out &&
- test_line_count = 1 out &&
-
- git rev-parse >expect \
- A:ignore-me B:a D1:a &&
- git rev-parse >actual \
- :0:ignore-me :1:a :2:a &&
- test_cmp expect actual
- else
- git ls-files -s >out &&
- test_line_count = 2 out &&
- git ls-files -u >out &&
- test_line_count = 1 out &&
- git ls-files -o >out &&
- test_line_count = 1 out &&
-
- git rev-parse >expect \
- A:ignore-me B:a &&
- git rev-parse >actual \
- :0:ignore-me :2:a &&
- test_cmp expect actual
- fi
+ git ls-files -s >out &&
+ test_line_count = 3 out &&
+ git ls-files -u >out &&
+ test_line_count = 2 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out &&
+
+ git rev-parse >expect A:ignore-me B:a D1:a &&
+ git rev-parse >actual :0:ignore-me :1:a :2:a &&
+ test_cmp expect actual
)
'
@@ -637,34 +599,18 @@ test_expect_success 'merge of E1 & D1 fails but has appropriate contents' '
test_must_fail git merge -s recursive D1^0 &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- git ls-files -s >out &&
- test_line_count = 3 out &&
- git ls-files -u >out &&
- test_line_count = 2 out &&
- git ls-files -o >out &&
- test_line_count = 1 out &&
-
- git rev-parse >expect \
- A:ignore-me B:a D1:a &&
- git rev-parse >actual \
- :0:ignore-me :1:a :3:a &&
- test_cmp expect actual
- else
- git ls-files -s >out &&
- test_line_count = 2 out &&
- git ls-files -u >out &&
- test_line_count = 1 out &&
- git ls-files -o >out &&
- test_line_count = 1 out &&
-
- git rev-parse >expect \
- A:ignore-me B:a &&
- git rev-parse >actual \
- :0:ignore-me :3:a &&
- test_cmp expect actual
- fi
+ git ls-files -s >out &&
+ test_line_count = 3 out &&
+ git ls-files -u >out &&
+ test_line_count = 2 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out &&
+
+ git rev-parse >expect \
+ A:ignore-me B:a D1:a &&
+ git rev-parse >actual \
+ :0:ignore-me :1:a :3:a &&
+ test_cmp expect actual
)
'
@@ -678,32 +624,17 @@ test_expect_success 'merge of D1 & E2 fails but has appropriate contents' '
test_must_fail git merge -s recursive E2^0 &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- git ls-files -s >out &&
- test_line_count = 5 out &&
- git ls-files -u >out &&
- test_line_count = 4 out &&
- git ls-files -o >out &&
- test_line_count = 1 out &&
-
- git rev-parse >expect \
- B:a D1:a E2:a/file C:a/file A:ignore-me &&
- git rev-parse >actual \
- :1:a~HEAD :2:a~HEAD :3:a/file :1:a/file :0:ignore-me
- else
- git ls-files -s >out &&
- test_line_count = 4 out &&
- git ls-files -u >out &&
- test_line_count = 3 out &&
- git ls-files -o >out &&
- test_line_count = 2 out &&
-
- git rev-parse >expect \
- B:a E2:a/file C:a/file A:ignore-me &&
- git rev-parse >actual \
- :2:a :3:a/file :1:a/file :0:ignore-me
- fi &&
+ git ls-files -s >out &&
+ test_line_count = 5 out &&
+ git ls-files -u >out &&
+ test_line_count = 4 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out &&
+
+ git rev-parse >expect \
+ B:a D1:a E2:a/file C:a/file A:ignore-me &&
+ git rev-parse >actual \
+ :1:a~HEAD :2:a~HEAD :3:a/file :1:a/file :0:ignore-me &&
test_cmp expect actual &&
test_path_is_file a~HEAD
@@ -720,32 +651,17 @@ test_expect_success 'merge of E2 & D1 fails but has appropriate contents' '
test_must_fail git merge -s recursive D1^0 &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- git ls-files -s >out &&
- test_line_count = 5 out &&
- git ls-files -u >out &&
- test_line_count = 4 out &&
- git ls-files -o >out &&
- test_line_count = 1 out &&
-
- git rev-parse >expect \
- B:a D1:a E2:a/file C:a/file A:ignore-me &&
- git rev-parse >actual \
- :1:a~D1^0 :3:a~D1^0 :2:a/file :1:a/file :0:ignore-me
- else
- git ls-files -s >out &&
- test_line_count = 4 out &&
- git ls-files -u >out &&
- test_line_count = 3 out &&
- git ls-files -o >out &&
- test_line_count = 2 out &&
-
- git rev-parse >expect \
- B:a E2:a/file C:a/file A:ignore-me &&
- git rev-parse >actual \
- :3:a :2:a/file :1:a/file :0:ignore-me
- fi &&
+ git ls-files -s >out &&
+ test_line_count = 5 out &&
+ git ls-files -u >out &&
+ test_line_count = 4 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out &&
+
+ git rev-parse >expect \
+ B:a D1:a E2:a/file C:a/file A:ignore-me &&
+ git rev-parse >actual \
+ :1:a~D1^0 :3:a~D1^0 :2:a/file :1:a/file :0:ignore-me &&
test_cmp expect actual &&
test_path_is_file a~D1^0
@@ -777,7 +693,7 @@ test_expect_success 'merge of D1 & E3 succeeds' '
)
'
-test_expect_merge_algorithm failure success 'merge of D1 & E4 puts merge of a and a2 in both a and a2' '
+test_expect_success 'merge of D1 & E4 puts merge of a and a2 in both a and a2' '
test_when_finished "git -C directory-file reset --hard" &&
test_when_finished "git -C directory-file clean -fdqx" &&
(
@@ -1140,7 +1056,7 @@ test_expect_success 'setup symlink modify/modify' '
)
'
-test_expect_merge_algorithm failure success 'check symlink modify/modify' '
+test_expect_success 'check symlink modify/modify' '
(
cd symlink-modify-modify &&
@@ -1206,7 +1122,7 @@ test_expect_success 'setup symlink add/add' '
)
'
-test_expect_merge_algorithm failure success 'check symlink add/add' '
+test_expect_success 'check symlink add/add' '
(
cd symlink-add-add &&
@@ -1294,7 +1210,7 @@ test_expect_success 'setup submodule modify/modify' '
)
'
-test_expect_merge_algorithm failure success 'check submodule modify/modify' '
+test_expect_success 'check submodule modify/modify' '
(
cd submodule-modify-modify &&
@@ -1382,7 +1298,7 @@ test_expect_success 'setup submodule add/add' '
)
'
-test_expect_merge_algorithm failure success 'check submodule add/add' '
+test_expect_success 'check submodule add/add' '
(
cd submodule-add-add &&
@@ -1457,7 +1373,7 @@ test_expect_success 'setup conflicting entry types (submodule vs symlink)' '
)
'
-test_expect_merge_algorithm failure success 'check conflicting entry types (submodule vs symlink)' '
+test_expect_success 'check conflicting entry types (submodule vs symlink)' '
(
cd submodule-symlink-add-add &&
diff --git a/t/t6421-merge-partial-clone.sh b/t/t6421-merge-partial-clone.sh
index b99f29ef9b..6eb51285a0 100755
--- a/t/t6421-merge-partial-clone.sh
+++ b/t/t6421-merge-partial-clone.sh
@@ -27,7 +27,6 @@ test_description="limiting blob downloads when merging with partial clones"
# files that might be renamed into each other's paths.)
. ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-merge.sh
test_setup_repo () {
test -d server && return
@@ -207,7 +206,7 @@ test_setup_repo () {
#
# Summary: 2 fetches (1 for 2 objects, 1 for 1 object)
#
-test_expect_merge_algorithm failure success 'Objects downloaded for single relevant rename' '
+test_expect_success 'Objects downloaded for single relevant rename' '
test_setup_repo &&
git clone --sparse --filter=blob:none "file://$(pwd)/server" objects-single &&
(
@@ -297,7 +296,7 @@ test_expect_merge_algorithm failure success 'Objects downloaded for single relev
# this are not all that common.)
# Summary: 1 fetches for 6 objects
#
-test_expect_merge_algorithm failure success 'Objects downloaded when a directory rename triggered' '
+test_expect_success 'Objects downloaded when a directory rename triggered' '
test_setup_repo &&
git clone --sparse --filter=blob:none "file://$(pwd)/server" objects-dir &&
(
@@ -399,7 +398,7 @@ test_expect_merge_algorithm failure success 'Objects downloaded when a directory
#
# Summary: 4 fetches (1 for 6 objects, 1 for 8, 1 for 3, 1 for 2)
#
-test_expect_merge_algorithm failure success 'Objects downloaded with lots of renames and modifications' '
+test_expect_success 'Objects downloaded with lots of renames and modifications' '
test_setup_repo &&
git clone --sparse --filter=blob:none "file://$(pwd)/server" objects-many &&
(
diff --git a/t/t6422-merge-rename-corner-cases.sh b/t/t6422-merge-rename-corner-cases.sh
index 62b49c67e2..9cbe7ca782 100755
--- a/t/t6422-merge-rename-corner-cases.sh
+++ b/t/t6422-merge-rename-corner-cases.sh
@@ -7,7 +7,6 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-merge.sh
test_setup_rename_delete_untracked () {
git init rename-delete-untracked &&
@@ -316,12 +315,7 @@ test_expect_success 'rename/directory conflict + clean content merge' '
git ls-files -u >out &&
test_line_count = 1 out &&
git ls-files -o >out &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_line_count = 1 out
- else
- test_line_count = 2 out
- fi &&
+ test_line_count = 1 out &&
echo 0 >expect &&
git cat-file -p base:file >>expect &&
@@ -350,12 +344,7 @@ test_expect_success 'rename/directory conflict + content merge conflict' '
git ls-files -u >out &&
test_line_count = 3 out &&
git ls-files -o >out &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_line_count = 1 out
- else
- test_line_count = 2 out
- fi &&
+ test_line_count = 1 out &&
git cat-file -p left-conflict:newfile >left &&
git cat-file -p base:file >base &&
@@ -369,14 +358,8 @@ test_expect_success 'rename/directory conflict + content merge conflict' '
git rev-parse >expect \
base:file left-conflict:newfile right:file &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- git rev-parse >actual \
- :1:newfile~HEAD :2:newfile~HEAD :3:newfile~HEAD
- else
- git rev-parse >actual \
- :1:newfile :2:newfile :3:newfile
- fi &&
+ git rev-parse >actual \
+ :1:newfile~HEAD :2:newfile~HEAD :3:newfile~HEAD &&
test_cmp expect actual &&
test_path_is_file newfile/realfile &&
@@ -896,7 +879,7 @@ test_setup_rad () {
)
}
-test_expect_merge_algorithm failure success 'rad-check: rename/add/delete conflict' '
+test_expect_success 'rad-check: rename/add/delete conflict' '
test_setup_rad &&
(
cd rad &&
@@ -969,7 +952,7 @@ test_setup_rrdd () {
)
}
-test_expect_merge_algorithm failure success 'rrdd-check: rename/rename(2to1)/delete/delete conflict' '
+test_expect_success 'rrdd-check: rename/rename(2to1)/delete/delete conflict' '
test_setup_rrdd &&
(
cd rrdd &&
@@ -1058,7 +1041,7 @@ test_setup_mod6 () {
)
}
-test_expect_merge_algorithm failure success 'mod6-check: chains of rename/rename(1to2) and rename/rename(2to1)' '
+test_expect_success 'mod6-check: chains of rename/rename(1to2) and rename/rename(2to1)' '
test_setup_mod6 &&
(
cd mod6 &&
diff --git a/t/t6423-merge-rename-directories.sh b/t/t6423-merge-rename-directories.sh
index e0785410cd..f48ed6d035 100755
--- a/t/t6423-merge-rename-directories.sh
+++ b/t/t6423-merge-rename-directories.sh
@@ -26,8 +26,6 @@ test_description="recursive merge with directory renames"
# files that might be renamed into each other's paths.)
. ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-merge.sh
-
###########################################################################
# SECTION 1: Basic cases we should be able to handle
@@ -302,20 +300,11 @@ test_expect_success '1d: Directory renames cause a rename/rename(2to1) conflict'
git cat-file -p :2:x/wham >expect &&
git cat-file -p :3:x/wham >other &&
>empty &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_must_fail git merge-file \
- -L "HEAD:y/wham" \
- -L "" \
- -L "B^0:z/wham" \
- expect empty other
- else
- test_must_fail git merge-file \
- -L "HEAD" \
- -L "" \
- -L "B^0" \
- expect empty other
- fi &&
+ test_must_fail git merge-file \
+ -L "HEAD:y/wham" \
+ -L "" \
+ -L "B^0:z/wham" \
+ expect empty other &&
test_cmp expect x/wham
)
'
@@ -1186,18 +1175,10 @@ test_expect_success '5d: Directory/file/file conflict due to directory rename' '
git ls-files -u >out &&
test_line_count = 1 out &&
git ls-files -o >out &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_line_count = 1 out &&
-
- git rev-parse >actual \
- :0:y/b :0:y/c :0:z/d :0:y/f :2:y/d~HEAD :0:y/d/e
- else
- test_line_count = 2 out &&
-
- git rev-parse >actual \
- :0:y/b :0:y/c :0:z/d :0:y/f :2:y/d :0:y/d/e
- fi &&
+ test_line_count = 1 out &&
+
+ git rev-parse >actual \
+ :0:y/b :0:y/c :0:z/d :0:y/f :2:y/d~HEAD :0:y/d/e &&
git rev-parse >expect \
O:z/b O:z/c B:z/d B:z/f A:y/d B:y/d/e &&
test_cmp expect actual &&
@@ -1280,32 +1261,17 @@ test_expect_success '6a: Tricky rename/delete' '
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
test_grep "CONFLICT (rename/delete).*z/c.*y/c" out &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- git ls-files -s >out &&
- test_line_count = 3 out &&
- git ls-files -u >out &&
- test_line_count = 2 out &&
- git ls-files -o >out &&
- test_line_count = 1 out &&
-
- git rev-parse >actual \
- :0:y/b :1:y/c :3:y/c &&
- git rev-parse >expect \
- O:z/b O:z/c O:z/c
- else
- git ls-files -s >out &&
- test_line_count = 2 out &&
- git ls-files -u >out &&
- test_line_count = 1 out &&
- git ls-files -o >out &&
- test_line_count = 1 out &&
-
- git rev-parse >actual \
- :0:y/b :3:y/c &&
- git rev-parse >expect \
- O:z/b O:z/c
- fi &&
+ git ls-files -s >out &&
+ test_line_count = 3 out &&
+ git ls-files -u >out &&
+ test_line_count = 2 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out &&
+
+ git rev-parse >actual \
+ :0:y/b :1:y/c :3:y/c &&
+ git rev-parse >expect \
+ O:z/b O:z/c O:z/c &&
test_cmp expect actual
)
'
@@ -1372,7 +1338,7 @@ test_setup_6b1 () {
)
}
-test_expect_merge_algorithm failure success '6b1: Same renames done on both sides, plus another rename' '
+test_expect_success '6b1: Same renames done on both sides, plus another rename' '
test_setup_6b1 &&
(
cd 6b1 &&
@@ -1445,7 +1411,7 @@ test_setup_6b2 () {
)
}
-test_expect_merge_algorithm failure success '6b2: Same rename done on both sides' '
+test_expect_success '6b2: Same rename done on both sides' '
test_setup_6b2 &&
(
cd 6b2 &&
@@ -1832,20 +1798,11 @@ test_expect_success '7b: rename/rename(2to1), but only due to transitive rename'
git cat-file -p :2:y/d >expect &&
git cat-file -p :3:y/d >other &&
>empty &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_must_fail git merge-file \
- -L "HEAD:y/d" \
- -L "" \
- -L "B^0:z/d" \
- expect empty other
- else
- test_must_fail git merge-file \
- -L "HEAD" \
- -L "" \
- -L "B^0" \
- expect empty other
- fi &&
+ test_must_fail git merge-file \
+ -L "HEAD:y/d" \
+ -L "" \
+ -L "B^0:z/d" \
+ expect empty other &&
test_cmp expect y/d
)
'
@@ -1967,32 +1924,17 @@ test_expect_success '7d: transitive rename involved in rename/delete; how is it
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
test_grep "CONFLICT (rename/delete).*x/d.*y/d" out &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- git ls-files -s >out &&
- test_line_count = 4 out &&
- git ls-files -u >out &&
- test_line_count = 2 out &&
- git ls-files -o >out &&
- test_line_count = 1 out &&
-
- git rev-parse >actual \
- :0:y/b :0:y/c :1:y/d :3:y/d &&
- git rev-parse >expect \
- O:z/b O:z/c O:x/d O:x/d
- else
- git ls-files -s >out &&
- test_line_count = 3 out &&
- git ls-files -u >out &&
- test_line_count = 1 out &&
- git ls-files -o >out &&
- test_line_count = 1 out &&
-
- git rev-parse >actual \
- :0:y/b :0:y/c :3:y/d &&
- git rev-parse >expect \
- O:z/b O:z/c O:x/d
- fi &&
+ git ls-files -s >out &&
+ test_line_count = 4 out &&
+ git ls-files -u >out &&
+ test_line_count = 2 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out &&
+
+ git rev-parse >actual \
+ :0:y/b :0:y/c :1:y/d :3:y/d &&
+ git rev-parse >expect \
+ O:z/b O:z/c O:x/d O:x/d &&
test_cmp expect actual
)
'
@@ -2073,32 +2015,17 @@ test_expect_success '7e: transitive rename in rename/delete AND dirs in the way'
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out &&
test_grep "CONFLICT (rename/delete).*x/d.*y/d" out &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- git ls-files -s >out &&
- test_line_count = 6 out &&
- git ls-files -u >out &&
- test_line_count = 2 out &&
- git ls-files -o >out &&
- test_line_count = 1 out &&
-
- git rev-parse >actual \
- :0:x/d/f :0:y/d/g :0:y/b :0:y/c :1:y/d~B^0 :3:y/d~B^0 &&
- git rev-parse >expect \
- A:x/d/f A:y/d/g O:z/b O:z/c O:x/d O:x/d
- else
- git ls-files -s >out &&
- test_line_count = 5 out &&
- git ls-files -u >out &&
- test_line_count = 1 out &&
- git ls-files -o >out &&
- test_line_count = 2 out &&
-
- git rev-parse >actual \
- :0:x/d/f :0:y/d/g :0:y/b :0:y/c :3:y/d &&
- git rev-parse >expect \
- A:x/d/f A:y/d/g O:z/b O:z/c O:x/d
- fi &&
+ git ls-files -s >out &&
+ test_line_count = 6 out &&
+ git ls-files -u >out &&
+ test_line_count = 2 out &&
+ git ls-files -o >out &&
+ test_line_count = 1 out &&
+
+ git rev-parse >actual \
+ :0:x/d/f :0:y/d/g :0:y/b :0:y/c :1:y/d~B^0 :3:y/d~B^0 &&
+ git rev-parse >expect \
+ A:x/d/f A:y/d/g O:z/b O:z/c O:x/d O:x/d &&
test_cmp expect actual &&
git hash-object y/d~B^0 >actual &&
@@ -3284,34 +3211,15 @@ test_expect_success '10b: Overwrite untracked with dir rename + delete' '
echo contents >y/e &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_path_is_missing .git/MERGE_HEAD &&
- test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
-
- git ls-files -s >out &&
- test_line_count = 1 out &&
- git ls-files -u >out &&
- test_line_count = 0 out &&
- git ls-files -o >out &&
- test_line_count = 5 out
- else
- test_grep "CONFLICT (rename/delete).*Version B\^0 of y/d left in tree at y/d~B\^0" out &&
- test_grep "Error: Refusing to lose untracked file at y/e; writing to y/e~B\^0 instead" out &&
-
- git ls-files -s >out &&
- test_line_count = 3 out &&
- git ls-files -u >out &&
- test_line_count = 2 out &&
- git ls-files -o >out &&
- test_line_count = 5 out &&
-
- git rev-parse >actual \
- :0:y/b :3:y/d :3:y/e &&
- git rev-parse >expect \
- O:z/b O:z/c B:z/e &&
- test_cmp expect actual
- fi &&
+ test_path_is_missing .git/MERGE_HEAD &&
+ test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
+
+ git ls-files -s >out &&
+ test_line_count = 1 out &&
+ git ls-files -u >out &&
+ test_line_count = 0 out &&
+ git ls-files -o >out &&
+ test_line_count = 5 out &&
echo very >expect &&
test_cmp expect y/c &&
@@ -3374,38 +3282,15 @@ test_expect_success '10c1: Overwrite untracked with dir rename/rename(1to2)' '
echo important >y/c &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_path_is_missing .git/MERGE_HEAD &&
- test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
-
- git ls-files -s >out &&
- test_line_count = 4 out &&
- git ls-files -u >out &&
- test_line_count = 0 out &&
- git ls-files -o >out &&
- test_line_count = 3 out
- else
- test_grep "CONFLICT (rename/rename)" out &&
- test_grep "Refusing to lose untracked file at y/c; adding as y/c~B\^0 instead" out &&
-
- git ls-files -s >out &&
- test_line_count = 6 out &&
- git ls-files -u >out &&
- test_line_count = 3 out &&
- git ls-files -o >out &&
- test_line_count = 3 out &&
-
- git rev-parse >actual \
- :0:y/a :0:y/b :0:x/d :1:x/c :2:w/c :3:y/c &&
- git rev-parse >expect \
- O:z/a O:z/b O:x/d O:x/c O:x/c O:x/c &&
- test_cmp expect actual &&
-
- git hash-object y/c~B^0 >actual &&
- git rev-parse O:x/c >expect &&
- test_cmp expect actual
- fi &&
+ test_path_is_missing .git/MERGE_HEAD &&
+ test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
+
+ git ls-files -s >out &&
+ test_line_count = 4 out &&
+ git ls-files -u >out &&
+ test_line_count = 0 out &&
+ git ls-files -o >out &&
+ test_line_count = 3 out &&
echo important >expect &&
test_cmp expect y/c
@@ -3425,38 +3310,15 @@ test_expect_success '10c2: Overwrite untracked with dir rename/rename(1to2), oth
echo important >y/c &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive A^0 >out 2>err &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_path_is_missing .git/MERGE_HEAD &&
- test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
-
- git ls-files -s >out &&
- test_line_count = 4 out &&
- git ls-files -u >out &&
- test_line_count = 0 out &&
- git ls-files -o >out &&
- test_line_count = 3 out
- else
- test_grep "CONFLICT (rename/rename)" out &&
- test_grep "Refusing to lose untracked file at y/c; adding as y/c~HEAD instead" out &&
-
- git ls-files -s >out &&
- test_line_count = 6 out &&
- git ls-files -u >out &&
- test_line_count = 3 out &&
- git ls-files -o >out &&
- test_line_count = 3 out &&
-
- git rev-parse >actual \
- :0:y/a :0:y/b :0:x/d :1:x/c :3:w/c :2:y/c &&
- git rev-parse >expect \
- O:z/a O:z/b O:x/d O:x/c O:x/c O:x/c &&
- test_cmp expect actual &&
-
- git hash-object y/c~HEAD >actual &&
- git rev-parse O:x/c >expect &&
- test_cmp expect actual
- fi &&
+ test_path_is_missing .git/MERGE_HEAD &&
+ test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
+
+ git ls-files -s >out &&
+ test_line_count = 4 out &&
+ git ls-files -u >out &&
+ test_line_count = 0 out &&
+ git ls-files -o >out &&
+ test_line_count = 3 out &&
echo important >expect &&
test_cmp expect y/c
@@ -3514,47 +3376,15 @@ test_expect_success '10d: Delete untracked with dir rename/rename(2to1)' '
echo important >y/wham &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_path_is_missing .git/MERGE_HEAD &&
- test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
-
- git ls-files -s >out &&
- test_line_count = 6 out &&
- git ls-files -u >out &&
- test_line_count = 0 out &&
- git ls-files -o >out &&
- test_line_count = 3 out
- else
- test_grep "CONFLICT (rename/rename)" out &&
- test_grep "Refusing to lose untracked file at y/wham" out &&
-
- git ls-files -s >out &&
- test_line_count = 6 out &&
- git ls-files -u >out &&
- test_line_count = 2 out &&
- git ls-files -o >out &&
- test_line_count = 3 out &&
-
- git rev-parse >actual \
- :0:y/a :0:y/b :0:y/d :0:y/e :2:y/wham :3:y/wham &&
- git rev-parse >expect \
- O:z/a O:z/b O:x/d O:x/e O:z/c O:x/f &&
- test_cmp expect actual &&
-
- test_must_fail git rev-parse :1:y/wham &&
-
- # Test that two-way merge in y/wham~merged is as expected
- git cat-file -p :2:y/wham >expect &&
- git cat-file -p :3:y/wham >other &&
- >empty &&
- test_must_fail git merge-file \
- -L "HEAD" \
- -L "" \
- -L "B^0" \
- expect empty other &&
- test_cmp expect y/wham~merged
- fi &&
+ test_path_is_missing .git/MERGE_HEAD &&
+ test_grep "error: The following untracked working tree files would be overwritten by merge" err &&
+
+ git ls-files -s >out &&
+ test_line_count = 6 out &&
+ git ls-files -u >out &&
+ test_line_count = 0 out &&
+ git ls-files -o >out &&
+ test_line_count = 3 out &&
echo important >expect &&
test_cmp expect y/wham
@@ -3596,7 +3426,7 @@ test_setup_10e () {
)
}
-test_expect_merge_algorithm failure success '10e: Does git complain about untracked file that is not really in the way?' '
+test_expect_success '10e: Does git complain about untracked file that is not really in the way?' '
test_setup_10e &&
(
cd 10e &&
@@ -3687,30 +3517,8 @@ test_expect_success '11a: Avoid losing dirty contents with simple rename' '
echo stuff >>z/c &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_path_is_missing .git/MERGE_HEAD &&
- test_grep "error: Your local changes to the following files would be overwritten by merge" err
- else
- test_grep "Refusing to lose dirty file at z/c" out &&
-
- git ls-files -s >out &&
- test_line_count = 2 out &&
- git ls-files -u >out &&
- test_line_count = 1 out &&
- git ls-files -o >out &&
- test_line_count = 3 out &&
-
- git rev-parse >actual \
- :0:z/a :2:z/c &&
- git rev-parse >expect \
- O:z/a B:z/b &&
- test_cmp expect actual &&
-
- git hash-object z/c~HEAD >actual &&
- git rev-parse B:z/b >expect &&
- test_cmp expect actual
- fi &&
+ test_path_is_missing .git/MERGE_HEAD &&
+ test_grep "error: Your local changes to the following files would be overwritten by merge" err &&
test_seq 1 10 >expected &&
echo stuff >>expected &&
@@ -3766,34 +3574,9 @@ test_expect_success '11b: Avoid losing dirty file involved in directory rename'
git checkout A^0 &&
echo stuff >>z/c &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
- test_path_is_missing .git/MERGE_HEAD &&
- test_grep "error: Your local changes to the following files would be overwritten by merge" err
- else
- git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
- test_grep "Refusing to lose dirty file at z/c" out &&
-
- git ls-files -s >out &&
- test_line_count = 3 out &&
- git ls-files -u >out &&
- test_line_count = 0 out &&
- git ls-files -m >out &&
- test_line_count = 0 out &&
- git ls-files -o >out &&
- test_line_count = 3 out &&
-
- git rev-parse >actual \
- :0:x/b :0:y/a :0:y/c &&
- git rev-parse >expect \
- O:x/b O:z/a B:x/c &&
- test_cmp expect actual &&
-
- git hash-object y/c >actual &&
- git rev-parse B:x/c >expect &&
- test_cmp expect actual
- fi &&
+ test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
+ test_path_is_missing .git/MERGE_HEAD &&
+ test_grep "error: Your local changes to the following files would be overwritten by merge" err &&
grep -q stuff z/c &&
test_seq 1 10 >expected &&
@@ -3850,13 +3633,8 @@ test_expect_success '11c: Avoid losing not-uptodate with rename + D/F conflict'
echo stuff >>y/c &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_path_is_missing .git/MERGE_HEAD &&
- test_grep "error: Your local changes to the following files would be overwritten by merge" err
- else
- test_grep "following files would be overwritten by merge" err
- fi &&
+ test_path_is_missing .git/MERGE_HEAD &&
+ test_grep "error: Your local changes to the following files would be overwritten by merge" err &&
grep -q stuff y/c &&
test_seq 1 10 >expected &&
@@ -3924,30 +3702,8 @@ test_expect_success '11d: Avoid losing not-uptodate with rename + D/F conflict'
echo stuff >>z/c &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_path_is_missing .git/MERGE_HEAD &&
- test_grep "error: Your local changes to the following files would be overwritten by merge" err
- else
- test_grep "Refusing to lose dirty file at z/c" out &&
-
- git ls-files -s >out &&
- test_line_count = 4 out &&
- git ls-files -u >out &&
- test_line_count = 1 out &&
- git ls-files -o >out &&
- test_line_count = 4 out &&
-
- git rev-parse >actual \
- :0:x/b :0:y/a :0:y/c/d :3:y/c &&
- git rev-parse >expect \
- O:x/b O:z/a B:y/c/d B:x/c &&
- test_cmp expect actual &&
-
- git hash-object y/c~HEAD >actual &&
- git rev-parse B:x/c >expect &&
- test_cmp expect actual
- fi &&
+ test_path_is_missing .git/MERGE_HEAD &&
+ test_grep "error: Your local changes to the following files would be overwritten by merge" err &&
grep -q stuff z/c &&
test_seq 1 10 >expected &&
@@ -4010,39 +3766,8 @@ test_expect_success '11e: Avoid deleting not-uptodate with dir rename/rename(1to
echo mods >>y/c &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_path_is_missing .git/MERGE_HEAD &&
- test_grep "error: Your local changes to the following files would be overwritten by merge" err
- else
- test_grep "CONFLICT (rename/rename)" out &&
- test_grep "Refusing to lose dirty file at y/c" out &&
-
- git ls-files -s >out &&
- test_line_count = 7 out &&
- git ls-files -u >out &&
- test_line_count = 4 out &&
- git ls-files -o >out &&
- test_line_count = 3 out &&
-
- git rev-parse >actual \
- :0:y/a :0:y/b :0:x/d :1:x/c :2:w/c :2:y/c :3:y/c &&
- git rev-parse >expect \
- O:z/a O:z/b O:x/d O:x/c O:x/c A:y/c O:x/c &&
- test_cmp expect actual &&
-
- # See if y/c~merged has expected contents; requires manually
- # doing the expected file merge
- git cat-file -p A:y/c >c1 &&
- git cat-file -p B:z/c >c2 &&
- >empty &&
- test_must_fail git merge-file \
- -L "HEAD" \
- -L "" \
- -L "B^0" \
- c1 empty c2 &&
- test_cmp c1 y/c~merged
- fi &&
+ test_path_is_missing .git/MERGE_HEAD &&
+ test_grep "error: Your local changes to the following files would be overwritten by merge" err &&
echo different >expected &&
echo mods >>expected &&
@@ -4099,40 +3824,8 @@ test_expect_success '11f: Avoid deleting not-uptodate with dir rename/rename(2to
echo important >>y/wham &&
test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out 2>err &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_path_is_missing .git/MERGE_HEAD &&
- test_grep "error: Your local changes to the following files would be overwritten by merge" err
- else
- test_grep "CONFLICT (rename/rename)" out &&
- test_grep "Refusing to lose dirty file at y/wham" out &&
-
- git ls-files -s >out &&
- test_line_count = 4 out &&
- git ls-files -u >out &&
- test_line_count = 2 out &&
- git ls-files -o >out &&
- test_line_count = 3 out &&
-
- test_must_fail git rev-parse :1:y/wham &&
-
- git rev-parse >actual \
- :0:y/a :0:y/b :2:y/wham :3:y/wham &&
- git rev-parse >expect \
- O:z/a O:z/b O:x/c O:x/d &&
- test_cmp expect actual &&
-
- # Test that two-way merge in y/wham~merged is as expected
- git cat-file -p :2:y/wham >expect &&
- git cat-file -p :3:y/wham >other &&
- >empty &&
- test_must_fail git merge-file \
- -L "HEAD" \
- -L "" \
- -L "B^0" \
- expect empty other &&
- test_cmp expect y/wham~merged
- fi &&
+ test_path_is_missing .git/MERGE_HEAD &&
+ test_grep "error: Your local changes to the following files would be overwritten by merge" err &&
test_seq 1 10 >expected &&
echo important >>expected &&
@@ -4267,7 +3960,7 @@ test_setup_12b1 () {
)
}
-test_expect_merge_algorithm failure success '12b1: Moving two directory hierarchies into each other' '
+test_expect_success '12b1: Moving two directory hierarchies into each other' '
test_setup_12b1 &&
(
cd 12b1 &&
@@ -4435,7 +4128,7 @@ test_setup_12c1 () {
)
}
-test_expect_merge_algorithm failure success '12c1: Moving one directory hierarchy into another w/ content merge' '
+test_expect_success '12c1: Moving one directory hierarchy into another w/ content merge' '
test_setup_12c1 &&
(
cd 12c1 &&
@@ -4797,7 +4490,7 @@ test_setup_12f () {
)
}
-test_expect_merge_algorithm failure success '12f: Trivial directory resolve, caching, all kinds of fun' '
+test_expect_success '12f: Trivial directory resolve, caching, all kinds of fun' '
test_setup_12f &&
(
cd 12f &&
@@ -5253,7 +4946,7 @@ test_setup_12l () {
)
}
-test_expect_merge_algorithm failure success '12l (B into A): Rename into each other + add/add conflict' '
+test_expect_success '12l (B into A): Rename into each other + add/add conflict' '
test_setup_12l BintoA &&
(
cd 12l_BintoA &&
@@ -5280,7 +4973,7 @@ test_expect_merge_algorithm failure success '12l (B into A): Rename into each ot
)
'
-test_expect_merge_algorithm failure success '12l (A into B): Rename into each other + add/add conflict' '
+test_expect_success '12l (A into B): Rename into each other + add/add conflict' '
test_setup_12l AintoB &&
(
cd 12l_AintoB &&
@@ -5348,7 +5041,7 @@ test_setup_12m () {
)
}
-test_expect_merge_algorithm failure success '12m: Change parent of renamed-dir to symlink on other side' '
+test_expect_success '12m: Change parent of renamed-dir to symlink on other side' '
test_setup_12m &&
(
cd 12m &&
diff --git a/t/t6424-merge-unrelated-index-changes.sh b/t/t6424-merge-unrelated-index-changes.sh
index 7677c5f08d..ddc7524f6c 100755
--- a/t/t6424-merge-unrelated-index-changes.sh
+++ b/t/t6424-merge-unrelated-index-changes.sh
@@ -176,9 +176,11 @@ test_expect_success 'merge-recursive, when index==head but head!=HEAD' '
# Make index match B
git diff C B -- | git apply --cached &&
test_when_finished "git clean -fd" && # Do not leave untracked around
+ git write-tree >index-before &&
# Merge B & F, with B as "head"
git merge-recursive A -- B F > out &&
- test_grep "Already up to date" out
+ git write-tree >index-after &&
+ test_cmp index-before index-after
'
test_expect_success 'recursive, when file has staged changes not matching HEAD nor what a merge would give' '
@@ -294,10 +296,8 @@ test_expect_success 'with multiple strategies, recursive or ort failure do not e
git add a &&
git rev-parse :a >expect &&
- sane_unset GIT_TEST_MERGE_ALGORITHM &&
- test_must_fail git merge -s recursive -s ort -s octopus C^0 >output 2>&1 &&
+ test_must_fail git merge -s ort -s octopus C^0 >output 2>&1 &&
- grep "Trying merge strategy recursive..." output &&
grep "Trying merge strategy ort..." output &&
grep "Trying merge strategy octopus..." output &&
grep "No merge strategy handled the merge." output &&
diff --git a/t/t6426-merge-skip-unneeded-updates.sh b/t/t6426-merge-skip-unneeded-updates.sh
index b059475ed0..404cd3f2ca 100755
--- a/t/t6426-merge-skip-unneeded-updates.sh
+++ b/t/t6426-merge-skip-unneeded-updates.sh
@@ -23,8 +23,6 @@ test_description="merge cases"
# files that might be renamed into each other's paths.)
. ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-merge.sh
-
###########################################################################
# SECTION 1: Cases involving no renames (one side has subset of changes of
@@ -663,7 +661,7 @@ test_setup_4a () {
# correct requires doing the merge in-memory first, then realizing that no
# updates to the file are necessary, and thus that we can just leave the path
# alone.
-test_expect_merge_algorithm failure success '4a: Change on A, change on B subset of A, dirty mods present' '
+test_expect_success '4a: Change on A, change on B subset of A, dirty mods present' '
test_setup_4a &&
(
cd 4a &&
diff --git a/t/t6428-merge-conflicts-sparse.sh b/t/t6428-merge-conflicts-sparse.sh
index 9919c3fa7c..c86e02c6e2 100755
--- a/t/t6428-merge-conflicts-sparse.sh
+++ b/t/t6428-merge-conflicts-sparse.sh
@@ -23,8 +23,6 @@ test_description="merge cases"
# files that might be renamed into each other's paths.)
. ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-merge.sh
-
# Testcase basic, conflicting changes in 'numerals'
diff --git a/t/t6430-merge-recursive.sh b/t/t6430-merge-recursive.sh
index ca15e6dd6d..e59560a5dd 100755
--- a/t/t6430-merge-recursive.sh
+++ b/t/t6430-merge-recursive.sh
@@ -6,7 +6,6 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-merge.sh
test_expect_success 'setup 1' '
@@ -373,9 +372,9 @@ test_expect_success 'merge-recursive d/f conflict result' '
git ls-files -s >actual &&
(
- echo "100644 $o0 1 a" &&
- echo "100644 $o1 2 a" &&
echo "100644 $o4 0 a/c" &&
+ echo "100644 $o0 1 a~$c1" &&
+ echo "100644 $o1 2 a~$c1" &&
echo "100644 $o0 0 b" &&
echo "100644 $o0 0 c" &&
echo "100644 $o1 0 d/e"
@@ -397,9 +396,9 @@ test_expect_success 'merge-recursive d/f conflict result the other way' '
git ls-files -s >actual &&
(
- echo "100644 $o0 1 a" &&
- echo "100644 $o1 3 a" &&
echo "100644 $o4 0 a/c" &&
+ echo "100644 $o0 1 a~$c1" &&
+ echo "100644 $o1 3 a~$c1" &&
echo "100644 $o0 0 b" &&
echo "100644 $o0 0 c" &&
echo "100644 $o1 0 d/e"
@@ -424,9 +423,9 @@ test_expect_success 'merge-recursive d/f conflict result' '
echo "100644 $o1 0 a" &&
echo "100644 $o0 0 b" &&
echo "100644 $o0 0 c" &&
- echo "100644 $o6 3 d" &&
echo "100644 $o0 1 d/e" &&
- echo "100644 $o1 2 d/e"
+ echo "100644 $o1 2 d/e" &&
+ echo "100644 $o6 3 d~$c6"
) >expected &&
test_cmp expected actual
@@ -448,9 +447,9 @@ test_expect_success 'merge-recursive d/f conflict result' '
echo "100644 $o1 0 a" &&
echo "100644 $o0 0 b" &&
echo "100644 $o0 0 c" &&
- echo "100644 $o6 2 d" &&
echo "100644 $o0 1 d/e" &&
- echo "100644 $o1 3 d/e"
+ echo "100644 $o1 3 d/e" &&
+ echo "100644 $o6 2 d~$c6"
) >expected &&
test_cmp expected actual
@@ -645,7 +644,7 @@ test_expect_success 'merge-recursive copy vs. rename' '
test_cmp expected actual
'
-test_expect_merge_algorithm failure success 'merge-recursive rename vs. rename/symlink' '
+test_expect_success 'merge-recursive rename vs. rename/symlink' '
git checkout -f rename &&
git merge rename-ln &&
@@ -696,33 +695,6 @@ test_expect_success 'merging with triple rename across D/F conflict' '
git merge other
'
-test_expect_success 'merge-recursive remembers the names of all base trees' '
- git reset --hard HEAD &&
-
- # make the index match $c1 so that merge-recursive below does not
- # fail early
- git diff --binary HEAD $c1 -- | git apply --cached &&
-
- # more trees than static slots used by oid_to_hex()
- for commit in $c0 $c2 $c4 $c5 $c6 $c7
- do
- git rev-parse "$commit^{tree}" || return 1
- done >trees &&
-
- # ignore the return code; it only fails because the input is weird...
- test_must_fail git -c merge.verbosity=5 merge-recursive $(cat trees) -- $c1 $c3 >out &&
-
- # ...but make sure it fails in the expected way
- test_grep CONFLICT.*rename/rename out &&
-
- # merge-recursive prints in reverse order, but we do not care
- sort <trees >expect &&
- sed -n "s/^virtual //p" out | sort >actual &&
- test_cmp expect actual &&
-
- git clean -fd
-'
-
test_expect_success 'merge-recursive internal merge resolves to the sameness' '
git reset --hard HEAD &&
diff --git a/t/t6434-merge-recursive-rename-options.sh b/t/t6434-merge-recursive-rename-options.sh
index 6e913c30a1..5a6f74839c 100755
--- a/t/t6434-merge-recursive-rename-options.sh
+++ b/t/t6434-merge-recursive-rename-options.sh
@@ -34,7 +34,9 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
get_expected_stages () {
git checkout rename -- $1-new &&
git ls-files --stage $1-new >expected-stages-undetected-$1 &&
- sed "s/ 0 / 2 /" <expected-stages-undetected-$1 \
+ git ls-tree HEAD^ $1-old >tmp &&
+ git ls-tree HEAD $1-new >>tmp &&
+ cat tmp | awk '{print $1 " " $3 " " NR "\t" '$1'"-new"}' \
>expected-stages-detected-$1 &&
git read-tree -u --reset HEAD
}
@@ -51,11 +53,11 @@ rename_undetected () {
check_common () {
git ls-files --stage >stages-actual &&
- test_line_count = 4 stages-actual
+ test_line_count = $1 stages-actual
}
check_threshold_0 () {
- check_common &&
+ check_common 8 &&
rename_detected 0 &&
rename_detected 1 &&
rename_detected 2 &&
@@ -63,7 +65,7 @@ check_threshold_0 () {
}
check_threshold_1 () {
- check_common &&
+ check_common 7 &&
rename_undetected 0 &&
rename_detected 1 &&
rename_detected 2 &&
@@ -71,7 +73,7 @@ check_threshold_1 () {
}
check_threshold_2 () {
- check_common &&
+ check_common 6 &&
rename_undetected 0 &&
rename_undetected 1 &&
rename_detected 2 &&
@@ -79,7 +81,7 @@ check_threshold_2 () {
}
check_exact_renames () {
- check_common &&
+ check_common 5 &&
rename_undetected 0 &&
rename_undetected 1 &&
rename_undetected 2 &&
@@ -87,7 +89,7 @@ check_exact_renames () {
}
check_no_renames () {
- check_common &&
+ check_common 4 &&
rename_undetected 0 &&
rename_undetected 1 &&
rename_undetected 2 &&
diff --git a/t/t6436-merge-overwrite.sh b/t/t6436-merge-overwrite.sh
index 4f4376421e..70b5d2d694 100755
--- a/t/t6436-merge-overwrite.sh
+++ b/t/t6436-merge-overwrite.sh
@@ -101,19 +101,10 @@ test_expect_success 'will not overwrite unstaged changes in renamed file' '
git mv c1.c other.c &&
git commit -m rename &&
cp important other.c &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_must_fail git merge c1a >out 2>err &&
- test_grep "would be overwritten by merge" err &&
- test_cmp important other.c &&
- test_path_is_missing .git/MERGE_HEAD
- else
- test_must_fail git merge c1a >out &&
- test_grep "Refusing to lose dirty file at other.c" out &&
- test_path_is_file other.c~HEAD &&
- test $(git hash-object other.c~HEAD) = $(git rev-parse c1a:c1.c) &&
- test_cmp important other.c
- fi
+ test_must_fail git merge c1a >out 2>err &&
+ test_grep "would be overwritten by merge" err &&
+ test_cmp important other.c &&
+ test_path_is_missing .git/MERGE_HEAD
'
test_expect_success 'will not overwrite untracked subtree' '
diff --git a/t/t6437-submodule-merge.sh b/t/t6437-submodule-merge.sh
index 4815559157..a564758f52 100755
--- a/t/t6437-submodule-merge.sh
+++ b/t/t6437-submodule-merge.sh
@@ -9,7 +9,6 @@ GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
. ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-merge.sh
#
# history
@@ -110,14 +109,10 @@ test_expect_success 'merging should conflict for non fast-forward' '
test_when_finished "git -C merge-search reset --hard" &&
(cd merge-search &&
git checkout -b test-nonforward-a b &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_must_fail git merge c 2>actual &&
- sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
- grep "$sub_expect" actual
- else
- test_must_fail git merge c 2> actual
- fi)
+ test_must_fail git merge c 2>actual &&
+ sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
+ grep "$sub_expect" actual
+ )
'
test_expect_success 'finish setup for merge-search' '
@@ -151,14 +146,9 @@ test_expect_success 'merging should conflict for non fast-forward (resolution ex
git checkout -b test-nonforward-b b &&
(cd sub &&
git rev-parse --short sub-d > ../expect) &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_must_fail git merge c >actual 2>sub-actual &&
- sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
- grep "$sub_expect" sub-actual
- else
- test_must_fail git merge c 2> actual
- fi &&
+ test_must_fail git merge c >actual 2>sub-actual &&
+ sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
+ grep "$sub_expect" sub-actual &&
grep $(cat expect) actual > /dev/null &&
git reset --hard)
'
@@ -169,23 +159,12 @@ test_expect_success 'merging should fail for ambiguous common parent' '
(cd sub &&
git checkout -b ambiguous sub-b &&
git merge sub-c &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- git rev-parse --short sub-d >../expect1 &&
- git rev-parse --short ambiguous >../expect2
- else
- git rev-parse sub-d > ../expect1 &&
- git rev-parse ambiguous > ../expect2
- fi
+ git rev-parse --short sub-d >../expect1 &&
+ git rev-parse --short ambiguous >../expect2
) &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- test_must_fail git merge c >actual 2>sub-actual &&
- sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
- grep "$sub_expect" sub-actual
- else
- test_must_fail git merge c 2> actual
- fi &&
+ test_must_fail git merge c >actual 2>sub-actual &&
+ sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-c)" &&
+ grep "$sub_expect" sub-actual &&
grep $(cat expect1) actual > /dev/null &&
grep $(cat expect2) actual > /dev/null &&
git reset --hard)
@@ -227,11 +206,9 @@ test_expect_success 'merging should fail for changes that are backwards' '
git checkout -b test-backward e &&
test_must_fail git merge f 2>actual &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-d)" &&
- grep "$sub_expect" actual
- fi)
+ sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short sub-d)" &&
+ grep "$sub_expect" actual
+ )
'
@@ -358,7 +335,7 @@ test_expect_success 'setup file/submodule conflict' '
)
'
-test_expect_merge_algorithm failure success 'file/submodule conflict' '
+test_expect_success 'file/submodule conflict' '
test_when_finished "git -C file-submodule reset --hard" &&
(
cd file-submodule &&
@@ -467,7 +444,7 @@ test_expect_failure 'directory/submodule conflict; keep submodule clean' '
)
'
-test_expect_merge_algorithm failure success !FAIL_PREREQS 'directory/submodule conflict; should not treat submodule files as untracked or in the way' '
+test_expect_success !FAIL_PREREQS 'directory/submodule conflict; should not treat submodule files as untracked or in the way' '
test_when_finished "git -C directory-submodule/path reset --hard" &&
test_when_finished "git -C directory-submodule reset --hard" &&
(
@@ -535,11 +512,9 @@ test_expect_success 'merging should fail with no merge base' '
git add sub &&
git commit -m "b" &&
test_must_fail git merge a 2>actual &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short HEAD^1)" &&
- grep "$sub_expect" actual
- fi)
+ sub_expect="go to submodule (sub), and either merge commit $(git -C sub rev-parse --short HEAD^1)" &&
+ grep "$sub_expect" actual
+ )
'
test_done
diff --git a/t/t6438-submodule-directory-file-conflicts.sh b/t/t6438-submodule-directory-file-conflicts.sh
index 8df67a0ef9..53d83c828a 100755
--- a/t/t6438-submodule-directory-file-conflicts.sh
+++ b/t/t6438-submodule-directory-file-conflicts.sh
@@ -12,11 +12,6 @@ test_submodule_switch "merge --ff"
test_submodule_switch "merge --ff-only"
-if test "$GIT_TEST_MERGE_ALGORITHM" != ort
-then
- KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
- KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
-fi
test_submodule_switch "merge --no-ff"
test_done
diff --git a/t/t6439-merge-co-error-msgs.sh b/t/t6439-merge-co-error-msgs.sh
index 55bd744a3f..643c9368e0 100755
--- a/t/t6439-merge-co-error-msgs.sh
+++ b/t/t6439-merge-co-error-msgs.sh
@@ -47,7 +47,7 @@ test_expect_success 'untracked files overwritten by merge (fast and non-fast for
export GIT_MERGE_VERBOSITY &&
test_must_fail git merge branch 2>out2
) &&
- echo "Merge with strategy ${GIT_TEST_MERGE_ALGORITHM:-ort} failed." >>expect &&
+ echo "Merge with strategy ort failed." >>expect &&
test_cmp out2 expect &&
git reset --hard HEAD^
'
diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh
index 932c26cb45..9717e825f0 100755
--- a/t/t7006-pager.sh
+++ b/t/t7006-pager.sh
@@ -662,9 +662,9 @@ test_expect_success 'setup trace2' '
'
test_expect_success 'setup large log output' '
- perl -e "
- print \"this is a long commit message\" x 50000
- " >commit-msg &&
+ test-tool genzeros 50000 |
+ tr "\000" "a" |
+ sed "s/a/this is a long commit message/g" >commit-msg &&
git commit --allow-empty -F commit-msg
'
diff --git a/t/t7402-submodule-rebase.sh b/t/t7402-submodule-rebase.sh
index 25b33a1e87..06cee3432f 100755
--- a/t/t7402-submodule-rebase.sh
+++ b/t/t7402-submodule-rebase.sh
@@ -124,11 +124,8 @@ test_expect_success 'rebasing submodule that should conflict' '
echo "160000 $(git rev-parse HEAD) 3 submodule"
) >expect &&
test_cmp expect actual &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- sub_expect="go to submodule (submodule), and either merge commit $(git -C submodule rev-parse --short HEAD^0)" &&
- grep "$sub_expect" actual_output
- fi
+ sub_expect="go to submodule (submodule), and either merge commit $(git -C submodule rev-parse --short HEAD^0)" &&
+ grep "$sub_expect" actual_output
'
test_done
diff --git a/t/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh
index 0c605fd271..3d944a00e0 100755
--- a/t/t7416-submodule-dash-url.sh
+++ b/t/t7416-submodule-dash-url.sh
@@ -33,7 +33,8 @@ test_expect_success 'fsck accepts protected dash' '
'
test_expect_success 'remove ./ protection from .gitmodules url' '
- perl -i -pe "s{\./}{}" .gitmodules &&
+ sed "s|\./||" .gitmodules >.gitmodules.munged &&
+ mv .gitmodules.munged .gitmodules &&
git commit -am "drop protection"
'
diff --git a/t/t7501-commit-basic-functionality.sh b/t/t7501-commit-basic-functionality.sh
index cc12f99f11..a37509f004 100755
--- a/t/t7501-commit-basic-functionality.sh
+++ b/t/t7501-commit-basic-functionality.sh
@@ -46,7 +46,7 @@ test_expect_success 'paths and -a do not mix' '
test_must_fail git commit -m foo -a file
'
-test_expect_success PERL 'can use paths with --interactive' '
+test_expect_success 'can use paths with --interactive' '
echo bong-o-bong >file &&
# 2: update, 1:st path, that is all, 7: quit
test_write_lines 2 1 "" 7 |
@@ -345,12 +345,12 @@ test_expect_success 'overriding author from command line' '
grep Rubber.Duck output
'
-test_expect_success PERL 'interactive add' '
+test_expect_success 'interactive add' '
echo 7 | test_must_fail git commit --interactive >out &&
grep "What now" out
'
-test_expect_success PERL "commit --interactive doesn't change index if editor aborts" '
+test_expect_success "commit --interactive doesn't change index if editor aborts" '
echo zoo >file &&
test_must_fail git diff --exit-code >diff1 &&
test_write_lines u "*" q |
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index b2070d4e39..cdc1d6fcc7 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -1066,7 +1066,7 @@ test_expect_success 'status -s submodule summary (clean submodule)' '
test_expect_success 'status -z implies porcelain' '
git status --porcelain |
- perl -pe "s/\012/\000/g" >expect &&
+ tr "\012" "\000" >expect &&
git status -z >output &&
test_cmp expect output
'
diff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh
index ff085b086c..42f675b739 100755
--- a/t/t7602-merge-octopus-many.sh
+++ b/t/t7602-merge-octopus-many.sh
@@ -77,12 +77,9 @@ Merge made by the 'recursive' strategy.
EOF
test_expect_success 'merge reduces irrelevant remote heads' '
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- mv expected expected.tmp &&
- sed s/recursive/ort/ expected.tmp >expected &&
- rm expected.tmp
- fi &&
+ mv expected expected.tmp &&
+ sed s/recursive/ort/ expected.tmp >expected &&
+ rm expected.tmp &&
GIT_MERGE_VERBOSITY=0 git merge c4 c5 >actual &&
test_cmp expected actual
'
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
index c077aba7ce..957f8e20ba 100755
--- a/t/t7610-mergetool.sh
+++ b/t/t7610-mergetool.sh
@@ -535,14 +535,9 @@ test_expect_success 'file vs modified submodule' '
yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
yes "" | git mergetool both &&
yes "d" | git mergetool file11 file12 &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- yes "c" | git mergetool submod~HEAD &&
- git rm submod &&
- git mv submod~HEAD submod
- else
- yes "l" | git mergetool submod
- fi &&
+ yes "c" | git mergetool submod~HEAD &&
+ git rm submod &&
+ git mv submod~HEAD submod &&
git submodule update -N &&
echo "not a submodule" >expect &&
test_cmp expect submod &&
@@ -559,15 +554,10 @@ test_expect_success 'file vs modified submodule' '
yes "" | git mergetool file1 file2 spaced\ name subdir/file3 &&
yes "" | git mergetool both &&
yes "d" | git mergetool file11 file12 &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- mv submod submod.orig &&
- git rm --cached submod &&
- yes "c" | git mergetool submod~test19 &&
- git mv submod~test19 submod
- else
- yes "r" | git mergetool submod
- fi &&
+ mv submod submod.orig &&
+ git rm --cached submod &&
+ yes "c" | git mergetool submod~test19 &&
+ git mv submod~test19 submod &&
test -d submod.orig &&
git submodule update -N &&
echo "not a submodule" >expect &&
@@ -585,10 +575,7 @@ test_expect_success 'file vs modified submodule' '
yes "" | git mergetool both &&
yes "d" | git mergetool file11 file12 &&
yes "l" | git mergetool submod &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- yes "d" | git mergetool submod~test19
- fi &&
+ yes "d" | git mergetool submod~test19 &&
echo "main submodule" >expect &&
test_cmp expect submod/bar &&
git submodule update -N &&
@@ -686,14 +673,9 @@ test_expect_success 'directory vs modified submodule' '
test_must_fail git merge main &&
test -n "$(git ls-files -u)" &&
test ! -e submod.orig &&
- if test "$GIT_TEST_MERGE_ALGORITHM" = ort
- then
- yes "r" | git mergetool submod~main &&
- git mv submod submod.orig &&
- git mv submod~main submod
- else
- yes "r" | git mergetool submod
- fi &&
+ yes "r" | git mergetool submod~main &&
+ git mv submod submod.orig &&
+ git mv submod~main submod &&
test -d submod.orig &&
echo "not a submodule" >expect &&
test_cmp expect submod.orig/file16 &&
diff --git a/t/t7615-diff-algo-with-mergy-operations.sh b/t/t7615-diff-algo-with-mergy-operations.sh
index ac5863e788..5822d02d51 100755
--- a/t/t7615-diff-algo-with-mergy-operations.sh
+++ b/t/t7615-diff-algo-with-mergy-operations.sh
@@ -22,8 +22,6 @@ test_expect_success 'setup' '
git tag c2
'
-GIT_TEST_MERGE_ALGORITHM=recursive
-
test_expect_success 'merge c2 to c1 with recursive merge strategy fails with the current default myers diff algorithm' '
git reset --hard c1 &&
test_must_fail git merge -s recursive -Xdiff-algorithm=myers c2
diff --git a/t/t7704-repack-cruft.sh b/t/t7704-repack-cruft.sh
index 43d2947d28..8aebfb45f5 100755
--- a/t/t7704-repack-cruft.sh
+++ b/t/t7704-repack-cruft.sh
@@ -149,7 +149,7 @@ generate_cruft_pack () {
echo "$packdir/pack-$pack.mtimes"
}
-test_expect_success '--max-cruft-size creates new packs when above threshold' '
+test_expect_success '--max-cruft-size creates new packs when too large' '
git init max-cruft-size-large &&
(
cd max-cruft-size-large &&
@@ -173,7 +173,7 @@ test_expect_success '--max-cruft-size creates new packs when above threshold' '
)
'
-test_expect_success '--max-cruft-size combines existing packs when below threshold' '
+test_expect_success '--max-cruft-size combines existing packs when not too large' '
git init max-cruft-size-small &&
(
cd max-cruft-size-small &&
@@ -194,10 +194,13 @@ test_expect_success '--max-cruft-size combines existing packs when below thresho
)
'
-test_expect_success '--max-cruft-size combines smaller packs first' '
- git init max-cruft-size-consume-small &&
+test_expect_success '--combine-cruft-below-size combines packs' '
+ repo=combine-cruft-below-size &&
+ test_when_finished "rm -fr $repo" &&
+
+ git init "$repo" &&
(
- cd max-cruft-size-consume-small &&
+ cd "$repo" &&
test_commit base &&
git repack -ad &&
@@ -211,11 +214,11 @@ test_expect_success '--max-cruft-size combines smaller packs first' '
test-tool pack-mtimes "$(basename $cruft_bar)" >>expect.raw &&
sort expect.raw >expect.objects &&
- # repacking with `--max-cruft-size=2M` should combine
- # both 0.5 MiB packs together, instead of, say, one of
- # the 0.5 MiB packs with the 1.0 MiB pack
+ # Repacking with `--combine-cruft-below-size=1M`
+ # should combine both 0.5 MiB packs together, but
+ # ignore the two packs which are >= 1.0 MiB.
ls $packdir/pack-*.mtimes | sort >cruft.before &&
- git repack -d --cruft --max-cruft-size=2M &&
+ git repack -d --cruft --combine-cruft-below-size=1M &&
ls $packdir/pack-*.mtimes | sort >cruft.after &&
comm -13 cruft.before cruft.after >cruft.new &&
@@ -224,11 +227,12 @@ test_expect_success '--max-cruft-size combines smaller packs first' '
test_line_count = 1 cruft.new &&
test_line_count = 2 cruft.removed &&
- # the two smaller packs should be rolled up first
+ # The two packs smaller than 1.0MiB should be repacked
+ # together.
printf "%s\n" $cruft_foo $cruft_bar | sort >expect.removed &&
test_cmp expect.removed cruft.removed &&
- # ...and contain the set of objects rolled up
+ # ...and contain the set of objects rolled up.
test-tool pack-mtimes "$(basename $(cat cruft.new))" >actual.raw &&
sort actual.raw >actual.objects &&
@@ -236,10 +240,10 @@ test_expect_success '--max-cruft-size combines smaller packs first' '
)
'
-test_expect_success 'setup --max-cruft-size with freshened objects' '
- git init max-cruft-size-freshen &&
+test_expect_success 'setup cruft with freshened objects' '
+ git init cruft-freshen &&
(
- cd max-cruft-size-freshen &&
+ cd cruft-freshen &&
test_commit base &&
git repack -ad &&
@@ -257,9 +261,9 @@ test_expect_success 'setup --max-cruft-size with freshened objects' '
)
'
-test_expect_success '--max-cruft-size with freshened objects (loose)' '
+test_expect_success 'cruft with freshened objects (loose)' '
(
- cd max-cruft-size-freshen &&
+ cd cruft-freshen &&
# regenerate the object, setting its mtime to be more recent
foo="$(generate_random_blob foo 64)" &&
@@ -275,9 +279,9 @@ test_expect_success '--max-cruft-size with freshened objects (loose)' '
)
'
-test_expect_success '--max-cruft-size with freshened objects (packed)' '
+test_expect_success 'cruft with freshened objects (packed)' '
(
- cd max-cruft-size-freshen &&
+ cd cruft-freshen &&
# regenerate the object and store it in a packfile,
# setting its mtime to be more recent
@@ -304,7 +308,7 @@ test_expect_success '--max-cruft-size with freshened objects (packed)' '
)
'
-test_expect_success '--max-cruft-size with freshened objects (previously cruft)' '
+test_expect_success 'multi-cruft with freshened objects (previously cruft)' '
repo="max-cruft-size-threshold" &&
test_when_finished "rm -fr $repo" &&
@@ -354,13 +358,11 @@ test_expect_success '--max-cruft-size with freshened objects (previously cruft)'
done >actual.raw &&
sort actual.raw >actual &&
- # Among the set of all cruft packs, we should see both
- # mtimes for object $foo and $bar, as well as the
+ # Among the set of all cruft packs, we should see the
+ # new mtimes for object $foo and $bar, as well as the
# single new copy of $baz.
sort >expect <<-EOF &&
- $foo $(cat foo.old)
$foo $(cat foo.new)
- $bar $(cat bar.old)
$bar $(cat bar.new)
$baz $(cat baz.old)
$quux $(cat quux.new)
@@ -477,4 +479,249 @@ test_expect_success 'reachable packs are preferred over cruft ones' '
)
'
+test_expect_success 'repack --cruft generates a cruft pack' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ git branch -M main &&
+ git checkout --orphan other &&
+ test_commit unreachable &&
+
+ git checkout main &&
+ git branch -D other &&
+ git tag -d unreachable &&
+ # objects are not cruft if they are contained in the reflogs
+ git reflog expire --all --expire=all &&
+
+ git rev-list --objects --all --no-object-names >reachable.raw &&
+ git cat-file --batch-all-objects --batch-check="%(objectname)" >objects &&
+ sort <reachable.raw >reachable &&
+ comm -13 reachable objects >unreachable &&
+
+ git repack --cruft -d &&
+
+ cruft=$(basename $(ls $packdir/pack-*.mtimes) .mtimes) &&
+ pack=$(basename $(ls $packdir/pack-*.pack | grep -v $cruft) .pack) &&
+
+ git show-index <$packdir/$pack.idx >actual.raw &&
+ cut -f2 -d" " actual.raw | sort >actual &&
+ test_cmp reachable actual &&
+
+ git show-index <$packdir/$cruft.idx >actual.raw &&
+ cut -f2 -d" " actual.raw | sort >actual &&
+ test_cmp unreachable actual
+ )
+'
+
+test_expect_success 'cruft packs are not included in geometric repack' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ git repack -Ad &&
+ git branch -M main &&
+
+ git checkout --orphan other &&
+ test_commit cruft &&
+ git repack -d &&
+
+ git checkout main &&
+ git branch -D other &&
+ git tag -d cruft &&
+ git reflog expire --all --expire=all &&
+
+ git repack --cruft &&
+
+ find $packdir -type f | sort >before &&
+ git repack --geometric=2 -d &&
+ find $packdir -type f | sort >after &&
+
+ test_cmp before after
+ )
+'
+
+test_expect_success 'repack --geometric collects once-cruft objects' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ git repack -Ad &&
+ git branch -M main &&
+
+ git checkout --orphan other &&
+ git rm -rf . &&
+ test_commit --no-tag cruft &&
+ cruft="$(git rev-parse HEAD)" &&
+
+ git checkout main &&
+ git branch -D other &&
+ git reflog expire --all --expire=all &&
+
+ # Pack the objects created in the previous step into a cruft
+ # pack. Intentionally leave loose copies of those objects
+ # around so we can pick them up in a subsequent --geometric
+ # reapack.
+ git repack --cruft &&
+
+ # Now make those objects reachable, and ensure that they are
+ # packed into the new pack created via a --geometric repack.
+ git update-ref refs/heads/other $cruft &&
+
+ # Without this object, the set of unpacked objects is exactly
+ # the set of objects already in the cruft pack. Tweak that set
+ # to ensure we do not overwrite the cruft pack entirely.
+ test_commit reachable2 &&
+
+ find $packdir -name "pack-*.idx" | sort >before &&
+ git repack --geometric=2 -d &&
+ find $packdir -name "pack-*.idx" | sort >after &&
+
+ {
+ git rev-list --objects --no-object-names $cruft &&
+ git rev-list --objects --no-object-names reachable..reachable2
+ } >want.raw &&
+ sort want.raw >want &&
+
+ pack=$(comm -13 before after) &&
+ git show-index <$pack >objects.raw &&
+
+ cut -d" " -f2 objects.raw | sort >got &&
+
+ test_cmp want got
+ )
+'
+
+test_expect_success 'cruft repack with no reachable objects' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+ git repack -ad &&
+
+ base="$(git rev-parse base)" &&
+
+ git for-each-ref --format="delete %(refname)" >in &&
+ git update-ref --stdin <in &&
+ git reflog expire --all --expire=all &&
+ rm -fr .git/index &&
+
+ git repack --cruft -d &&
+
+ git cat-file -t $base
+ )
+'
+
+find_pack () {
+ for idx in $(ls $packdir/pack-*.idx)
+ do
+ git show-index <$idx >out &&
+ if grep -q "$1" out
+ then
+ echo $idx
+ fi || return 1
+ done
+}
+
+test_expect_success 'cruft repack with --max-pack-size' '
+ git init max-pack-size &&
+ (
+ cd max-pack-size &&
+ test_commit base &&
+
+ # two cruft objects which exceed the maximum pack size
+ foo=$(generate_random_blob foo 1048576) &&
+ bar=$(generate_random_blob bar 1048576) &&
+ test-tool chmtime --get -1000 \
+ "$objdir/$(test_oid_to_path $foo)" >foo.mtime &&
+ test-tool chmtime --get -2000 \
+ "$objdir/$(test_oid_to_path $bar)" >bar.mtime &&
+ git repack --cruft --max-pack-size=1M &&
+ find $packdir -name "*.mtimes" >cruft &&
+ test_line_count = 2 cruft &&
+
+ foo_mtimes="$(basename $(find_pack $foo) .idx).mtimes" &&
+ bar_mtimes="$(basename $(find_pack $bar) .idx).mtimes" &&
+ test-tool pack-mtimes $foo_mtimes >foo.actual &&
+ test-tool pack-mtimes $bar_mtimes >bar.actual &&
+
+ echo "$foo $(cat foo.mtime)" >foo.expect &&
+ echo "$bar $(cat bar.mtime)" >bar.expect &&
+
+ test_cmp foo.expect foo.actual &&
+ test_cmp bar.expect bar.actual &&
+ test "$foo_mtimes" != "$bar_mtimes"
+ )
+'
+
+test_expect_success 'cruft repack with pack.packSizeLimit' '
+ (
+ cd max-pack-size &&
+ # repack everything back together to remove the existing cruft
+ # pack (but to keep its objects)
+ git repack -adk &&
+ git -c pack.packSizeLimit=1M repack --cruft &&
+ # ensure the same post condition is met when --max-pack-size
+ # would otherwise be inferred from the configuration
+ find $packdir -name "*.mtimes" >cruft &&
+ test_line_count = 2 cruft &&
+ for pack in $(cat cruft)
+ do
+ test-tool pack-mtimes "$(basename $pack)" >objects &&
+ test_line_count = 1 objects || return 1
+ done
+ )
+'
+
+test_expect_success 'cruft repack respects repack.cruftWindow' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+
+ GIT_TRACE2_EVENT=$(pwd)/event.trace \
+ git -c pack.window=1 -c repack.cruftWindow=2 repack \
+ --cruft --window=3 &&
+
+ grep "pack-objects.*--window=2.*--cruft" event.trace
+ )
+'
+
+test_expect_success 'cruft repack respects --window by default' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+
+ GIT_TRACE2_EVENT=$(pwd)/event.trace \
+ git -c pack.window=2 repack --cruft --window=3 &&
+
+ grep "pack-objects.*--window=3.*--cruft" event.trace
+ )
+'
+
+test_expect_success 'cruft repack respects --quiet' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+ GIT_PROGRESS_DELAY=0 git repack --cruft --quiet 2>err &&
+ test_must_be_empty err
+ )
+'
+
test_done
diff --git a/t/t7815-grep-binary.sh b/t/t7815-grep-binary.sh
index 90ebb64f46..3bd91da970 100755
--- a/t/t7815-grep-binary.sh
+++ b/t/t7815-grep-binary.sh
@@ -114,13 +114,10 @@ test_expect_success 'grep respects not-binary diff attribute' '
test_cmp expect actual
'
-cat >nul_to_q_textconv <<'EOF'
-#!/bin/sh
-"$PERL_PATH" -pe 'y/\000/Q/' < "$1"
-EOF
-chmod +x nul_to_q_textconv
-
test_expect_success 'setup textconv filters' '
+ write_script nul_to_q_textconv <<-\EOF &&
+ tr "\000" "Q" <"$1"
+ EOF
echo a diff=foo >.gitattributes &&
git config diff.foo.textconv "\"$(pwd)\""/nul_to_q_textconv
'
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index 1909aed95e..9b82e11c10 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -306,6 +306,34 @@ test_expect_success 'maintenance.loose-objects.auto' '
test_subcommand git prune-packed --quiet <trace-loC
'
+test_expect_success 'maintenance.loose-objects.batchSize' '
+ git init loose-batch &&
+
+ # This creates three objects per commit.
+ test_commit_bulk -C loose-batch 34 &&
+ pack=$(ls loose-batch/.git/objects/pack/pack-*.pack) &&
+ index="${pack%pack}idx" &&
+ rm "$index" &&
+ git -C loose-batch unpack-objects <"$pack" &&
+ git -C loose-batch config maintenance.loose-objects.batchSize 50 &&
+
+ GIT_PROGRESS_DELAY=0 \
+ git -C loose-batch maintenance run --no-quiet --task=loose-objects 2>err &&
+ grep "Enumerating objects: 50, done." err &&
+
+ GIT_PROGRESS_DELAY=0 \
+ git -C loose-batch maintenance run --no-quiet --task=loose-objects 2>err &&
+ grep "Enumerating objects: 50, done." err &&
+
+ GIT_PROGRESS_DELAY=0 \
+ git -C loose-batch maintenance run --no-quiet --task=loose-objects 2>err &&
+ grep "Enumerating objects: 2, done." err &&
+
+ GIT_PROGRESS_DELAY=0 \
+ git -C loose-batch maintenance run --no-quiet --task=loose-objects 2>err &&
+ test_must_be_empty err
+'
+
test_expect_success 'incremental-repack task' '
packDir=.git/objects/pack &&
for i in $(test_seq 1 5)
@@ -447,6 +475,24 @@ test_expect_success 'pack-refs task' '
test_subcommand git pack-refs --all --prune <pack-refs.txt
'
+test_expect_success 'reflog-expire task' '
+ GIT_TRACE2_EVENT="$(pwd)/reflog-expire.txt" \
+ git maintenance run --task=reflog-expire &&
+ test_subcommand git reflog expire --all <reflog-expire.txt
+'
+
+test_expect_success 'reflog-expire task --auto only packs when exceeding limits' '
+ git reflog expire --all --expire=now &&
+ test_commit reflog-one &&
+ test_commit reflog-two &&
+ GIT_TRACE2_EVENT="$(pwd)/reflog-expire-auto.txt" \
+ git -c maintenance.reflog-expire.auto=3 maintenance run --auto --task=reflog-expire &&
+ test_subcommand ! git reflog expire --all <reflog-expire-auto.txt &&
+ GIT_TRACE2_EVENT="$(pwd)/reflog-expire-auto.txt" \
+ git -c maintenance.reflog-expire.auto=2 maintenance run --auto --task=reflog-expire &&
+ test_subcommand git reflog expire --all <reflog-expire-auto.txt
+'
+
test_expect_success '--auto and --schedule incompatible' '
test_must_fail git maintenance run --auto --schedule=daily 2>err &&
test_grep "at most one" err
diff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh
index d7167f5539..609845aeb1 100755
--- a/t/t8001-annotate.sh
+++ b/t/t8001-annotate.sh
@@ -7,6 +7,12 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
+if ! test_have_prereq PERL_TEST_HELPERS
+then
+ skip_all='skipping annotate tests; Perl not available'
+ test_done
+fi
+
PROG='git annotate'
. "$TEST_DIRECTORY"/annotate-tests.sh
diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh
index e98993276a..7822947f02 100755
--- a/t/t8002-blame.sh
+++ b/t/t8002-blame.sh
@@ -7,6 +7,12 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
+if ! test_have_prereq PERL_TEST_HELPERS
+then
+ skip_all='skipping blame colors tests; Perl not available'
+ test_done
+fi
+
PROG='git blame -c'
. "$TEST_DIRECTORY"/annotate-tests.sh
@@ -101,7 +107,7 @@ test_expect_success 'set up abbrev tests' '
expect=$1 && shift &&
echo $sha1 | cut -c 1-$expect >expect &&
git blame "$@" abbrev.t >actual &&
- perl -lne "/[0-9a-f]+/ and print \$&" <actual >actual.sha &&
+ sed -n "s/^[\^]\{0,1\}\([0-9a-f][0-9a-f]*\).*/\1/p" actual >actual.sha &&
test_cmp expect actual.sha
}
'
diff --git a/t/t8006-blame-textconv.sh b/t/t8006-blame-textconv.sh
index 07a287ffd3..db1e2afb2c 100755
--- a/t/t8006-blame-textconv.sh
+++ b/t/t8006-blame-textconv.sh
@@ -11,7 +11,7 @@ find_blame() {
cat >helper <<'EOF'
#!/bin/sh
grep -q '^bin: ' "$1" || { echo "E: $1 is not \"binary\" file" 1>&2; exit 1; }
-"$PERL_PATH" -p -e 's/^bin: /converted: /' "$1"
+sed 's/^bin: /converted: /' "$1"
EOF
chmod +x helper
diff --git a/t/t8011-blame-split-file.sh b/t/t8011-blame-split-file.sh
index c66494f5ba..388057245c 100755
--- a/t/t8011-blame-split-file.sh
+++ b/t/t8011-blame-split-file.sh
@@ -81,7 +81,7 @@ do
git blame --root -C --$output combined >output
'
- test_expect_success "$output output finds correct commits" '
+ test_expect_success PERL_TEST_HELPERS "$output output finds correct commits" '
generate_expect >expect <<-\EOF &&
5 base
1 modified
@@ -93,7 +93,7 @@ do
test_cmp expect actual
'
- test_expect_success "$output output shows correct filenames" '
+ test_expect_success PERL_TEST_HELPERS "$output output shows correct filenames" '
generate_expect >expect <<-\EOF &&
11 one
11 two
@@ -102,7 +102,7 @@ do
test_cmp expect actual
'
- test_expect_success "$output output shows correct previous pointer" '
+ test_expect_success PERL_TEST_HELPERS "$output output shows correct previous pointer" '
generate_expect >expect <<-EOF &&
5 NONE
1 $(git rev-parse modified^) one
diff --git a/t/t8012-blame-colors.sh b/t/t8012-blame-colors.sh
index c3a5f6d01f..3d77352650 100755
--- a/t/t8012-blame-colors.sh
+++ b/t/t8012-blame-colors.sh
@@ -7,6 +7,12 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
TEST_CREATE_REPO_NO_TEMPLATE=1
. ./test-lib.sh
+if ! test_have_prereq PERL_TEST_HELPERS
+then
+ skip_all='skipping blame colors tests; Perl not available'
+ test_done
+fi
+
PROG='git blame -c'
. "$TEST_DIRECTORY"/annotate-tests.sh
diff --git a/t/t8013-blame-ignore-revs.sh b/t/t8013-blame-ignore-revs.sh
index 370b768149..cace00ae8d 100755
--- a/t/t8013-blame-ignore-revs.sh
+++ b/t/t8013-blame-ignore-revs.sh
@@ -158,6 +158,25 @@ test_expect_success mark_unblamable_lines '
test_cmp expect actual
'
+for opt in --porcelain --line-porcelain
+do
+ test_expect_success "mark_unblamable_lines with $opt" "
+ sha=$(git rev-parse Y) &&
+
+ git -c blame.markUnblamableLines=false blame $opt --ignore-rev Y file >raw &&
+ cat > sedscript <<- 'EOF' &&
+ /^ y3/i\\
+ unblamable
+ /^ y4/i\\
+ unblamable
+ EOF
+ sed -f sedscript raw >expect &&
+
+ git -c blame.markUnblamableLines=true blame $opt --ignore-rev Y file >actual &&
+ test_cmp expect actual
+ "
+done
+
# Commit Z will touch the first two lines. Y touched all four.
# A--B--X--Y--Z
# The blame output when ignoring Z should be:
@@ -191,6 +210,25 @@ test_expect_success mark_ignored_lines '
! test_cmp expect actual
'
+for opt in --porcelain --line-porcelain
+do
+ test_expect_success "mark_ignored_lines with $opt" "
+ sha=$(git rev-parse Y) &&
+
+ git -c blame.markIgnoredLines=false blame $opt --ignore-rev Z file >raw &&
+ cat > sedscript <<- 'EOF' &&
+ /^ line-one-Z/i\\
+ ignored
+ /^ line-two-Z/i\\
+ ignored
+ EOF
+ sed -f sedscript raw >expect &&
+
+ git -c blame.markIgnoredLines=true blame $opt --ignore-rev Z file >actual &&
+ test_cmp expect actual
+ "
+done
+
# For ignored revs that added 'unblamable' lines and more recent commits changed
# the blamable lines, mark the unblamable lines with a
# '*'
diff --git a/t/t9137-git-svn-dcommit-clobber-series.sh b/t/t9137-git-svn-dcommit-clobber-series.sh
index 067b15bad2..b57a362bb9 100755
--- a/t/t9137-git-svn-dcommit-clobber-series.sh
+++ b/t/t9137-git-svn-dcommit-clobber-series.sh
@@ -20,8 +20,8 @@ test_expect_success '(supposedly) non-conflicting change from SVN' '
test x"$(sed -n -e 61p < file)" = x61 &&
svn_cmd co "$svnrepo" tmp &&
(cd tmp &&
- perl -i.bak -p -e "s/^58$/5588/" file &&
- perl -i.bak -p -e "s/^61$/6611/" file &&
+ sed -e "s/^58$/5588/" -e "s/^61$/6611/" file >file.munged &&
+ mv file.munged file &&
poke file &&
test x"$(sed -n -e 58p < file)" = x5588 &&
test x"$(sed -n -e 61p < file)" = x6611 &&
@@ -40,8 +40,10 @@ test_expect_success 'some unrelated changes to git' "
test_expect_success 'change file but in unrelated area' "
test x\"\$(sed -n -e 4p < file)\" = x4 &&
test x\"\$(sed -n -e 7p < file)\" = x7 &&
- perl -i.bak -p -e 's/^4\$/4444/' file &&
- perl -i.bak -p -e 's/^7\$/7777/' file &&
+ sed -e 's/^4\$/4444/' \
+ -e 's/^7\$/7777/' \
+ file >file.munged &&
+ mv file.munged file &&
test x\"\$(sed -n -e 4p < file)\" = x4444 &&
test x\"\$(sed -n -e 7p < file)\" = x7777 &&
git commit -m '4 => 4444, 7 => 7777' file &&
diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh
index 304bac5b1d..dda9e7c3e7 100755
--- a/t/t9350-fast-export.sh
+++ b/t/t9350-fast-export.sh
@@ -726,7 +726,7 @@ test_expect_success 'directory becomes symlink' '
(cd result && git show main:foo)
'
-test_expect_success 'fast-export quotes pathnames' '
+test_expect_success PERL_TEST_HELPERS 'fast-export quotes pathnames' '
git init crazy-paths &&
test_config -C crazy-paths core.protectNTFS false &&
(cd crazy-paths &&
diff --git a/t/t9811-git-p4-label-import.sh b/t/t9811-git-p4-label-import.sh
index 5ac5383fb7..7614dfbd95 100755
--- a/t/t9811-git-p4-label-import.sh
+++ b/t/t9811-git-p4-label-import.sh
@@ -95,9 +95,8 @@ test_expect_success 'two labels on the same changelist' '
cd "$git" &&
git p4 sync --import-labels &&
- git tag | grep TAG_F1 &&
- git tag | grep -q TAG_F1_1 &&
- git tag | grep -q TAG_F1_2 &&
+ git show-ref --verify refs/tags/TAG_F1_1 &&
+ git show-ref --verify refs/tags/TAG_F1_2 &&
cd main &&
@@ -207,8 +206,7 @@ test_expect_success 'use git config to enable import/export of tags' '
git tag CFG_A_GIT_TAG &&
git p4 rebase --verbose &&
git p4 submit --verbose &&
- git tag &&
- git tag | grep TAG_F1_1
+ git show-ref --verify refs/tags/TAG_F1_1
) &&
(
cd "$cli" &&
diff --git a/t/t9850-shell.sh b/t/t9850-shell.sh
index 36566ace21..21c3af48bd 100755
--- a/t/t9850-shell.sh
+++ b/t/t9850-shell.sh
@@ -30,7 +30,7 @@ test_expect_success 'shell allows interactive command' '
'
test_expect_success 'shell complains of overlong commands' '
- perl -e "print \"a\" x 2**12 for (0..2**19)" |
+ test-tool genzeros | tr "\000" "a" |
test_must_fail git shell 2>err &&
grep "too long" err
'
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 51bd750837..343b8cd191 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -149,7 +149,8 @@ fi
test_expect_success 'setup for __git_find_repo_path/__gitdir tests' '
mkdir -p subdir/subsubdir &&
mkdir -p non-repo &&
- git init -b main otherrepo
+ git init -b main otherrepo &&
+ git init -b main slashrepo
'
test_expect_success '__git_find_repo_path - from command line (through $__git_dir)' '
@@ -455,6 +456,32 @@ test_expect_success '__git_dequote - open double quote' '
'
+test_expect_success '__git_count_path_components - no slashes' '
+ echo 1 >expected &&
+ __git_count_path_components a >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__git_count_path_components - relative' '
+ echo 3 >expected &&
+ __git_count_path_components a/b/c >"$actual" &&
+ test_cmp expected "$actual"
+
+'
+
+test_expect_success '__git_count_path_components - absolute' '
+ echo 3 >expected &&
+ __git_count_path_components /a/b/c >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__git_count_path_components - trailing slash' '
+ echo 3 >expected &&
+ __git_count_path_components a/b/c/ >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+
test_expect_success '__gitcomp_direct - puts everything into COMPREPLY as-is' '
sed -e "s/Z$//g" >expected <<-EOF &&
with-trailing-space Z
@@ -648,6 +675,13 @@ test_expect_success 'setup for ref completion' '
) &&
git remote add other "$ROOT/otherrepo/.git" &&
git fetch --no-tags other &&
+ (
+ cd slashrepo &&
+ git commit --allow-empty -m initial &&
+ git branch -m main branch/with/slash
+ ) &&
+ git remote add remote/with/slash "$ROOT/slashrepo/.git" &&
+ git fetch --no-tags remote/with/slash &&
rm -f .git/FETCH_HEAD &&
git init thirdrepo
'
@@ -660,6 +694,8 @@ test_expect_success '__git_refs - simple' '
other/HEAD
other/branch-in-other
other/main-in-other
+ remote/with/slash/HEAD
+ remote/with/slash/branch/with/slash
matching-tag
EOF
(
@@ -676,6 +712,8 @@ test_expect_success '__git_refs - full refs' '
refs/remotes/other/HEAD
refs/remotes/other/branch-in-other
refs/remotes/other/main-in-other
+ refs/remotes/remote/with/slash/HEAD
+ refs/remotes/remote/with/slash/branch/with/slash
refs/tags/matching-tag
EOF
(
@@ -741,6 +779,19 @@ test_expect_success '__git_refs - configured remote' '
test_cmp expected "$actual"
'
+test_expect_success '__git_refs - configured remote - with slash' '
+ cat >expected <<-EOF &&
+ HEAD
+ HEAD
+ branch/with/slash
+ EOF
+ (
+ cur= &&
+ __git_refs remote/with/slash >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
test_expect_success '__git_refs - configured remote - full refs' '
cat >expected <<-EOF &&
HEAD
@@ -883,17 +934,19 @@ test_expect_success '__git_refs - unique remote branches for git checkout DWIMer
other/ambiguous
other/branch-in-other
other/main-in-other
- remote/ambiguous
- remote/branch-in-remote
+ remote/with/slash/HEAD
+ remote/with/slash/ambiguous
+ remote/with/slash/branch-in-remote
+ remote/with/slash/branch/with/slash
matching-tag
- HEAD
branch-in-other
branch-in-remote
+ branch/with/slash
main-in-other
EOF
for remote_ref in refs/remotes/other/ambiguous \
- refs/remotes/remote/ambiguous \
- refs/remotes/remote/branch-in-remote
+ refs/remotes/remote/with/slash/ambiguous \
+ refs/remotes/remote/with/slash/branch-in-remote
do
git update-ref $remote_ref main &&
test_when_finished "git update-ref -d $remote_ref" || return 1
@@ -913,6 +966,8 @@ test_expect_success '__git_refs - after --opt=' '
other/HEAD
other/branch-in-other
other/main-in-other
+ remote/with/slash/HEAD
+ remote/with/slash/branch/with/slash
matching-tag
EOF
(
@@ -929,6 +984,8 @@ test_expect_success '__git_refs - after --opt= - full refs' '
refs/remotes/other/HEAD
refs/remotes/other/branch-in-other
refs/remotes/other/main-in-other
+ refs/remotes/remote/with/slash/HEAD
+ refs/remotes/remote/with/slash/branch/with/slash
refs/tags/matching-tag
EOF
(
@@ -946,6 +1003,8 @@ test_expect_success '__git refs - excluding refs' '
^other/HEAD
^other/branch-in-other
^other/main-in-other
+ ^remote/with/slash/HEAD
+ ^remote/with/slash/branch/with/slash
^matching-tag
EOF
(
@@ -962,6 +1021,8 @@ test_expect_success '__git refs - excluding full refs' '
^refs/remotes/other/HEAD
^refs/remotes/other/branch-in-other
^refs/remotes/other/main-in-other
+ ^refs/remotes/remote/with/slash/HEAD
+ ^refs/remotes/remote/with/slash/branch/with/slash
^refs/tags/matching-tag
EOF
(
@@ -989,6 +1050,8 @@ test_expect_success '__git_refs - do not filter refs unless told so' '
other/branch-in-other
other/main-in-other
other/matching/branch-in-other
+ remote/with/slash/HEAD
+ remote/with/slash/branch/with/slash
matching-tag
matching/tag
EOF
@@ -1109,6 +1172,8 @@ test_expect_success '__git_complete_refs - simple' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
matching-tag Z
EOF
(
@@ -1147,6 +1212,20 @@ test_expect_success '__git_complete_refs - remote' '
test_cmp expected out
'
+test_expect_success '__git_complete_refs - remote - with slash' '
+ sed -e "s/Z$//" >expected <<-EOF &&
+ HEAD Z
+ HEAD Z
+ branch/with/slash Z
+ EOF
+ (
+ cur= &&
+ __git_complete_refs --remote=remote/with/slash &&
+ print_comp
+ ) &&
+ test_cmp expected out
+'
+
test_expect_success '__git_complete_refs - track' '
sed -e "s/Z$//" >expected <<-EOF &&
HEAD Z
@@ -1155,9 +1234,11 @@ test_expect_success '__git_complete_refs - track' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
matching-tag Z
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main-in-other Z
EOF
(
@@ -1202,6 +1283,8 @@ test_expect_success '__git_complete_refs - suffix' '
other/HEAD.
other/branch-in-other.
other/main-in-other.
+ remote/with/slash/HEAD.
+ remote/with/slash/branch/with/slash.
matching-tag.
EOF
(
@@ -1227,6 +1310,20 @@ test_expect_success '__git_complete_fetch_refspecs - simple' '
test_cmp expected out
'
+test_expect_success '__git_complete_fetch_refspecs - with slash' '
+ sed -e "s/Z$//" >expected <<-EOF &&
+ HEAD:HEAD Z
+ HEAD:HEAD Z
+ branch/with/slash:branch/with/slash Z
+ EOF
+ (
+ cur= &&
+ __git_complete_fetch_refspecs remote/with/slash &&
+ print_comp
+ ) &&
+ test_cmp expected out
+'
+
test_expect_success '__git_complete_fetch_refspecs - matching' '
sed -e "s/Z$//" >expected <<-EOF &&
branch-in-other:branch-in-other Z
@@ -1307,8 +1404,8 @@ test_expect_success '__git_complete_worktree_paths with -C' '
test_expect_success 'git switch - with no options, complete local branches and unique remote branch names for DWIM logic' '
test_completion "git switch " <<-\EOF
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -1454,8 +1551,8 @@ test_expect_success 'git-bisect - existing view subcommand is recognized and ena
test_expect_success 'git checkout - completes refs and unique remote branches for DWIM' '
test_completion "git checkout " <<-\EOF
HEAD Z
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -1463,6 +1560,8 @@ test_expect_success 'git checkout - completes refs and unique remote branches fo
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1482,8 +1581,8 @@ test_expect_success 'git switch - with GIT_COMPLETION_CHECKOUT_NO_GUESS=1, compl
test_expect_success 'git switch - --guess overrides GIT_COMPLETION_CHECKOUT_NO_GUESS=1, complete local branches and unique remote names for DWIM logic' '
GIT_COMPLETION_CHECKOUT_NO_GUESS=1 test_completion "git switch --guess " <<-\EOF
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -1492,8 +1591,8 @@ test_expect_success 'git switch - --guess overrides GIT_COMPLETION_CHECKOUT_NO_G
test_expect_success 'git switch - a later --guess overrides previous --no-guess, complete local and remote unique branches for DWIM' '
test_completion "git switch --no-guess --guess " <<-\EOF
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -1516,14 +1615,16 @@ test_expect_success 'git checkout - with GIT_COMPLETION_NO_GUESS=1 only complete
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
test_expect_success 'git checkout - --guess overrides GIT_COMPLETION_NO_GUESS=1, complete refs and unique remote branches for DWIM' '
GIT_COMPLETION_CHECKOUT_NO_GUESS=1 test_completion "git checkout --guess " <<-\EOF
HEAD Z
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -1531,6 +1632,8 @@ test_expect_success 'git checkout - --guess overrides GIT_COMPLETION_NO_GUESS=1,
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1543,14 +1646,16 @@ test_expect_success 'git checkout - with --no-guess, only completes refs' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
test_expect_success 'git checkout - a later --guess overrides previous --no-guess, complete refs and unique remote branches for DWIM' '
test_completion "git checkout --no-guess --guess " <<-\EOF
HEAD Z
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -1558,6 +1663,8 @@ test_expect_success 'git checkout - a later --guess overrides previous --no-gues
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1570,6 +1677,8 @@ test_expect_success 'git checkout - a later --no-guess overrides previous --gues
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1583,6 +1692,8 @@ test_expect_success 'git checkout - with checkout.guess = false, only completes
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1590,8 +1701,8 @@ test_expect_success 'git checkout - with checkout.guess = true, completes refs a
test_config checkout.guess true &&
test_completion "git checkout " <<-\EOF
HEAD Z
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -1599,6 +1710,8 @@ test_expect_success 'git checkout - with checkout.guess = true, completes refs a
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1606,8 +1719,8 @@ test_expect_success 'git checkout - a later --guess overrides previous checkout.
test_config checkout.guess false &&
test_completion "git checkout --guess " <<-\EOF
HEAD Z
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -1615,6 +1728,8 @@ test_expect_success 'git checkout - a later --guess overrides previous checkout.
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1628,6 +1743,8 @@ test_expect_success 'git checkout - a later --no-guess overrides previous checko
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1640,6 +1757,8 @@ test_expect_success 'git switch - with --detach, complete all references' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1652,6 +1771,8 @@ test_expect_success 'git checkout - with --detach, complete only references' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1824,6 +1945,8 @@ test_expect_success 'git switch - with -d, complete all references' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1836,6 +1959,8 @@ test_expect_success 'git checkout - with -d, complete only references' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1844,11 +1969,15 @@ test_expect_success 'git switch - with --track, complete only remote branches' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
test_completion "git switch -t " <<-\EOF
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1857,11 +1986,15 @@ test_expect_success 'git checkout - with --track, complete only remote branches'
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
test_completion "git checkout -t " <<-\EOF
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1881,6 +2014,8 @@ test_expect_success 'git checkout - with --no-track, complete only local referen
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1893,6 +2028,8 @@ test_expect_success 'git switch - with -c, complete all references' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1905,6 +2042,8 @@ test_expect_success 'git switch - with -C, complete all references' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1917,6 +2056,8 @@ test_expect_success 'git switch - with -c and --track, complete all references'
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1929,6 +2070,8 @@ test_expect_success 'git switch - with -C and --track, complete all references'
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1941,6 +2084,8 @@ test_expect_success 'git switch - with -c and --no-track, complete all reference
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1953,6 +2098,8 @@ test_expect_success 'git switch - with -C and --no-track, complete all reference
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1965,6 +2112,8 @@ test_expect_success 'git checkout - with -b, complete all references' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1977,6 +2126,8 @@ test_expect_success 'git checkout - with -B, complete all references' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1989,6 +2140,8 @@ test_expect_success 'git checkout - with -b and --track, complete all references
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -2001,6 +2154,8 @@ test_expect_success 'git checkout - with -B and --track, complete all references
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -2013,6 +2168,8 @@ test_expect_success 'git checkout - with -b and --no-track, complete all referen
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -2025,13 +2182,15 @@ test_expect_success 'git checkout - with -B and --no-track, complete all referen
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
test_expect_success 'git switch - for -c, complete local branches and unique remote branches' '
test_completion "git switch -c " <<-\EOF
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -2040,8 +2199,8 @@ test_expect_success 'git switch - for -c, complete local branches and unique rem
test_expect_success 'git switch - for -C, complete local branches and unique remote branches' '
test_completion "git switch -C " <<-\EOF
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -2078,8 +2237,8 @@ test_expect_success 'git switch - for -C with --no-track, complete local branche
test_expect_success 'git checkout - for -b, complete local branches and unique remote branches' '
test_completion "git checkout -b " <<-\EOF
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -2088,8 +2247,8 @@ test_expect_success 'git checkout - for -b, complete local branches and unique r
test_expect_success 'git checkout - for -B, complete local branches and unique remote branches' '
test_completion "git checkout -B " <<-\EOF
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -2126,8 +2285,8 @@ test_expect_success 'git checkout - for -B with --no-track, complete local branc
test_expect_success 'git switch - with --orphan completes local branch names and unique remote branch names' '
test_completion "git switch --orphan " <<-\EOF
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -2142,8 +2301,8 @@ test_expect_success 'git switch - --orphan with branch already provided complete
test_expect_success 'git checkout - with --orphan completes local branch names and unique remote branch names' '
test_completion "git checkout --orphan " <<-\EOF
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -2159,6 +2318,8 @@ test_expect_success 'git checkout - --orphan with branch already provided comple
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -2173,7 +2334,8 @@ test_expect_success 'git restore completes modified files' '
test_expect_success 'teardown after ref completion' '
git branch -d matching-branch &&
git tag -d matching-tag &&
- git remote remove other
+ git remote remove other &&
+ git remote remove remote/with/slash
'
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 79377bc0fc..bee4a2ca34 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -88,15 +88,15 @@ test_decode_color () {
}
lf_to_nul () {
- perl -pe 'y/\012/\000/'
+ tr '\012' '\000'
}
nul_to_q () {
- perl -pe 'y/\000/Q/'
+ tr '\000' 'Q'
}
q_to_nul () {
- perl -pe 'y/Q/\000/'
+ tr 'Q' '\000'
}
q_to_cr () {
@@ -773,6 +773,8 @@ mkdir -p "$TRASH_DIRECTORY/prereq-test-dir-'"$1"'" &&
rm -rf "$TRASH_DIRECTORY/prereq-test-dir-$1"
if test "$eval_ret" = 0; then
say >&3 "prerequisite $1 ok"
+ elif test "$eval_ret" = 125; then
+ :;
else
say >&3 "prerequisite $1 not satisfied"
fi
@@ -811,6 +813,9 @@ test_have_prereq () {
if test_run_lazy_prereq_ "$prerequisite" "$script"
then
test_set_prereq $prerequisite
+ elif test $? = 125
+ then
+ BUG "Do not use $prerequisite"
fi
lazily_tested_prereq="$lazily_tested_prereq$prerequisite "
esac
@@ -1640,17 +1645,7 @@ test_match_signal () {
# Read up to "$1" bytes (or to EOF) from stdin and write them to stdout.
test_copy_bytes () {
- perl -e '
- my $len = $ARGV[1];
- while ($len > 0) {
- my $s;
- my $nread = sysread(STDIN, $s, $len);
- die "cannot read: $!" unless defined($nread);
- last unless $nread;
- print $s;
- $len -= $nread;
- }
- ' - "$1"
+ dd ibs=1 count="$1" 2>/dev/null
}
# run "$@" inside a non-git directory
@@ -1989,7 +1984,7 @@ test_remote_https_urls() {
# Print the destination of symlink(s) provided as arguments. Basically
# the same as the readlink command, but it's not available everywhere.
test_readlink () {
- perl -le 'print readlink($_) for @ARGV' "$@"
+ test-tool path-utils readlink "$@"
}
# Set mtime to a fixed "magic" timestamp in mid February 2009, before we
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 9001ed3a64..af722d383d 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -499,24 +499,20 @@ EDITOR=:
# /usr/xpg4/bin/sh and /bin/ksh to bail out. So keep the unsets
# deriving from the command substitution clustered with the other
# ones.
-unset VISUAL EMAIL LANGUAGE $("$PERL_PATH" -e '
- my @env = keys %ENV;
- my $ok = join("|", qw(
- TRACE
- DEBUG
- TEST
- .*_TEST
- PROVE
- VALGRIND
- UNZIP
- PERF_
- CURL_VERBOSE
- TRACE_CURL
- BUILD_DIR
- ));
- my @vars = grep(/^GIT_/ && !/^GIT_($ok)/o, @env);
- print join("\n", @vars);
-')
+unset VISUAL EMAIL LANGUAGE $(env | sed -n \
+ -e '/^GIT_TRACE/d' \
+ -e '/^GIT_DEBUG/d' \
+ -e '/^GIT_TEST/d' \
+ -e '/^GIT_.*_TEST/d' \
+ -e '/^GIT_PROVE/d' \
+ -e '/^GIT_VALGRIND/d' \
+ -e '/^GIT_UNZIP/d' \
+ -e '/^GIT_PERF_/d' \
+ -e '/^GIT_CURL_VERBOSE/d' \
+ -e '/^GIT_TRACE_CURL/d' \
+ -e '/^GIT_BUILD_DIR/d' \
+ -e 's/^\(GIT_[^=]*\)=.*/\1/p'
+)
unset XDG_CACHE_HOME
unset XDG_CONFIG_HOME
unset GITPERLLIB
@@ -544,8 +540,6 @@ 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
# Tests using GIT_TRACE typically don't want <timestamp> <file>:<line> output
GIT_TRACE_BARE=1
@@ -1523,6 +1517,22 @@ then
export LSAN_OPTIONS
fi
+if test -z "$PERL_PATH"
+then
+ case "${GIT_TEST_CHAIN_LINT:-unset}" in
+ unset)
+ GIT_TEST_CHAIN_LINT=0
+ ;;
+ 0)
+ # The user has explicitly disabled the chain linter, so we
+ # don't have anything to worry about.
+ ;;
+ *)
+ BAIL_OUT 'You need Perl for the chain linter'
+ ;;
+ esac
+fi
+
if test "${GIT_TEST_CHAIN_LINT:-1}" != 0 &&
test "${GIT_TEST_EXT_CHAIN_LINT:-1}" != 0
then
@@ -1694,6 +1704,7 @@ test -n "$USE_LIBPCRE2" && test_set_prereq LIBPCRE2
test -z "$NO_GETTEXT" && test_set_prereq GETTEXT
test -n "$SANITIZE_LEAK" && test_set_prereq SANITIZE_LEAK
test -n "$GIT_VALGRIND_ENABLED" && test_set_prereq VALGRIND
+test -n "$PERL_PATH" && test_set_prereq PERL_TEST_HELPERS
if test -z "$GIT_TEST_CHECK_CACHE_TREE"
then
@@ -1862,8 +1873,13 @@ test_lazy_prereq CURL '
curl --version
'
+test_lazy_prereq WITH_BREAKING_CHANGES '
+ test -n "$WITH_BREAKING_CHANGES"
+'
+
test_lazy_prereq WITHOUT_BREAKING_CHANGES '
- test -z "$WITH_BREAKING_CHANGES"
+ # Signal that this prereq should not be used.
+ exit 125
'
# SHA1 is a test if the hash algorithm in use is SHA-1. This is both for tests
diff --git a/t/unit-tests/clar/clar/fs.h b/t/unit-tests/clar/clar/fs.h
index 8b206179fc..2203743fb4 100644
--- a/t/unit-tests/clar/clar/fs.h
+++ b/t/unit-tests/clar/clar/fs.h
@@ -376,9 +376,12 @@ fs_copydir_helper(const char *source, const char *dest, int dest_mode)
mkdir(dest, dest_mode);
cl_assert_(source_dir = opendir(source), "Could not open source dir");
- while ((d = (errno = 0, readdir(source_dir))) != NULL) {
+ for (;;) {
char *child;
+ errno = 0;
+ if ((d = readdir(source_dir)) == NULL)
+ break;
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue;
@@ -479,9 +482,12 @@ fs_rmdir_helper(const char *path)
struct dirent *d;
cl_assert_(dir = opendir(path), "Could not open dir");
- while ((d = (errno = 0, readdir(dir))) != NULL) {
+ for (;;) {
char *child;
+ errno = 0;
+ if ((d = readdir(dir)) == NULL)
+ break;
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue;
diff --git a/t/unit-tests/t-reftable-basics.c b/t/unit-tests/t-reftable-basics.c
index 9ba7eb05ad..c9e751e49e 100644
--- a/t/unit-tests/t-reftable-basics.c
+++ b/t/unit-tests/t-reftable-basics.c
@@ -128,12 +128,30 @@ int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
reftable_buf_release(&b);
}
- if_test ("put_be24 and get_be24 work") {
+ if_test ("reftable_put_be64 and reftable_get_be64 work") {
+ uint64_t in = 0x1122334455667788;
+ uint8_t dest[8];
+ uint64_t out;
+ reftable_put_be64(dest, in);
+ out = reftable_get_be64(dest);
+ check_int(in, ==, out);
+ }
+
+ if_test ("reftable_put_be32 and reftable_get_be32 work") {
+ uint32_t in = 0x11223344;
+ uint8_t dest[4];
+ uint32_t out;
+ reftable_put_be32(dest, in);
+ out = reftable_get_be32(dest);
+ check_int(in, ==, out);
+ }
+
+ if_test ("reftable_put_be24 and reftable_get_be24 work") {
uint32_t in = 0x112233;
uint8_t dest[3];
uint32_t out;
- put_be24(dest, in);
- out = get_be24(dest);
+ reftable_put_be24(dest, in);
+ out = reftable_get_be24(dest);
check_int(in, ==, out);
}
@@ -141,8 +159,8 @@ int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
uint32_t in = 0xfef1;
uint8_t dest[3];
uint32_t out;
- put_be16(dest, in);
- out = get_be16(dest);
+ reftable_put_be16(dest, in);
+ out = reftable_get_be16(dest);
check_int(in, ==, out);
}
diff --git a/t/unit-tests/t-reftable-pq.c b/t/unit-tests/t-reftable-pq.c
index f3f8a0cdf3..c128fe8616 100644
--- a/t/unit-tests/t-reftable-pq.c
+++ b/t/unit-tests/t-reftable-pq.c
@@ -21,7 +21,9 @@ static void merged_iter_pqueue_check(const struct merged_iter_pqueue *pq)
static int pq_entry_equal(struct pq_entry *a, struct pq_entry *b)
{
- return !reftable_record_cmp(a->rec, b->rec) && (a->index == b->index);
+ int cmp;
+ check(!reftable_record_cmp(a->rec, b->rec, &cmp));
+ return !cmp && (a->index == b->index);
}
static void t_pq_record(void)
@@ -32,7 +34,7 @@ static void t_pq_record(void)
char *last = NULL;
for (i = 0; i < N; i++) {
- reftable_record_init(&recs[i], BLOCK_TYPE_REF);
+ check(!reftable_record_init(&recs[i], BLOCK_TYPE_REF));
recs[i].u.ref.refname = xstrfmt("%02"PRIuMAX, (uintmax_t)i);
}
@@ -49,7 +51,9 @@ static void t_pq_record(void)
while (!merged_iter_pqueue_is_empty(pq)) {
struct pq_entry top = merged_iter_pqueue_top(pq);
- struct pq_entry e = merged_iter_pqueue_remove(&pq);
+ struct pq_entry e;
+
+ check(!merged_iter_pqueue_remove(&pq, &e));
merged_iter_pqueue_check(&pq);
check(pq_entry_equal(&top, &e));
@@ -72,7 +76,7 @@ static void t_pq_index(void)
size_t N = ARRAY_SIZE(recs), i;
for (i = 0; i < N; i++) {
- reftable_record_init(&recs[i], BLOCK_TYPE_REF);
+ check(!reftable_record_init(&recs[i], BLOCK_TYPE_REF));
recs[i].u.ref.refname = (char *) "refs/heads/master";
}
@@ -90,7 +94,9 @@ static void t_pq_index(void)
for (i = N - 1; i > 0; i--) {
struct pq_entry top = merged_iter_pqueue_top(pq);
- struct pq_entry e = merged_iter_pqueue_remove(&pq);
+ struct pq_entry e;
+
+ check(!merged_iter_pqueue_remove(&pq, &e));
merged_iter_pqueue_check(&pq);
check(pq_entry_equal(&top, &e));
@@ -111,7 +117,7 @@ static void t_merged_iter_pqueue_top(void)
size_t N = ARRAY_SIZE(recs), i;
for (i = 0; i < N; i++) {
- reftable_record_init(&recs[i], BLOCK_TYPE_REF);
+ check(!reftable_record_init(&recs[i], BLOCK_TYPE_REF));
recs[i].u.ref.refname = (char *) "refs/heads/master";
}
@@ -129,7 +135,9 @@ static void t_merged_iter_pqueue_top(void)
for (i = N - 1; i > 0; i--) {
struct pq_entry top = merged_iter_pqueue_top(pq);
- struct pq_entry e = merged_iter_pqueue_remove(&pq);
+ struct pq_entry e;
+
+ check(!merged_iter_pqueue_remove(&pq, &e));
merged_iter_pqueue_check(&pq);
check(pq_entry_equal(&top, &e));
diff --git a/t/unit-tests/t-reftable-record.c b/t/unit-tests/t-reftable-record.c
index d49d2a2729..5954966373 100644
--- a/t/unit-tests/t-reftable-record.c
+++ b/t/unit-tests/t-reftable-record.c
@@ -17,7 +17,7 @@ static void t_copy(struct reftable_record *rec)
uint8_t typ;
typ = reftable_record_type(rec);
- reftable_record_init(&copy, typ);
+ check(!reftable_record_init(&copy, typ));
reftable_record_copy_from(&copy, rec, REFTABLE_HASH_SIZE_SHA1);
/* do it twice to catch memory leaks */
reftable_record_copy_from(&copy, rec, REFTABLE_HASH_SIZE_SHA1);
@@ -100,16 +100,20 @@ static void t_reftable_ref_record_comparison(void)
.u.ref.value.symref = (char *) "refs/heads/master",
},
};
+ int cmp;
check(!reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1]));
+ check(!reftable_record_cmp(&in[0], &in[1], &cmp));
+ check(!cmp);
check(!reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1));
- check_int(reftable_record_cmp(&in[1], &in[2]), >, 0);
+ check(!reftable_record_cmp(&in[1], &in[2], &cmp));
+ check_int(cmp, >, 0);
in[1].u.ref.value_type = in[0].u.ref.value_type;
check(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1]));
+ check(!reftable_record_cmp(&in[0], &in[1], &cmp));
+ check(!cmp);
}
static void t_reftable_ref_record_compare_name(void)
@@ -209,17 +213,20 @@ static void t_reftable_log_record_comparison(void)
.u.log.update_index = 22,
},
};
+ int cmp;
check(!reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
check(!reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1));
- check_int(reftable_record_cmp(&in[1], &in[2]), >, 0);
+ check(!reftable_record_cmp(&in[1], &in[2], &cmp));
+ check_int(cmp, >, 0);
/* comparison should be reversed for equal keys, because
* comparison is now performed on the basis of update indices */
- check_int(reftable_record_cmp(&in[0], &in[1]), <, 0);
+ check(!reftable_record_cmp(&in[0], &in[1], &cmp));
+ check_int(cmp, <, 0);
in[1].u.log.update_index = in[0].u.log.update_index;
check(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1]));
+ check(!reftable_record_cmp(&in[0], &in[1], &cmp));
}
static void t_reftable_log_record_compare_key(void)
@@ -396,16 +403,20 @@ static void t_reftable_obj_record_comparison(void)
.u.obj.hash_prefix_len = 5,
},
};
+ int cmp;
check(!reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1]));
+ check(!reftable_record_cmp(&in[0], &in[1], &cmp));
+ check(!cmp);
check(!reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1));
- check_int(reftable_record_cmp(&in[1], &in[2]), >, 0);
+ check(!reftable_record_cmp(&in[1], &in[2], &cmp));
+ check_int(cmp, >, 0);
in[1].u.obj.offset_len = in[0].u.obj.offset_len;
check(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1]));
+ check(!reftable_record_cmp(&in[0], &in[1], &cmp));
+ check(!cmp);
}
static void t_reftable_obj_record_roundtrip(void)
@@ -486,19 +497,24 @@ static void t_reftable_index_record_comparison(void)
.u.idx.last_key = REFTABLE_BUF_INIT,
},
};
+ int cmp;
+
check(!reftable_buf_addstr(&in[0].u.idx.last_key, "refs/heads/master"));
check(!reftable_buf_addstr(&in[1].u.idx.last_key, "refs/heads/master"));
check(!reftable_buf_addstr(&in[2].u.idx.last_key, "refs/heads/branch"));
check(!reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1]));
+ check(!reftable_record_cmp(&in[0], &in[1], &cmp));
+ check(!cmp);
check(!reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1));
- check_int(reftable_record_cmp(&in[1], &in[2]), >, 0);
+ check(!reftable_record_cmp(&in[1], &in[2], &cmp));
+ check_int(cmp, >, 0);
in[1].u.idx.offset = in[0].u.idx.offset;
check(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1]));
+ check(!reftable_record_cmp(&in[0], &in[1], &cmp));
+ check(!cmp);
for (size_t i = 0; i < ARRAY_SIZE(in); i++)
reftable_record_release(&in[i]);
diff --git a/t/unit-tests/t-trailer.c b/t/unit-tests/t-trailer.c
deleted file mode 100644
index 184593e73d..0000000000
--- a/t/unit-tests/t-trailer.c
+++ /dev/null
@@ -1,317 +0,0 @@
-#define DISABLE_SIGN_COMPARE_WARNINGS
-
-#include "test-lib.h"
-#include "trailer.h"
-
-struct contents {
- const char *raw;
- const char *key;
- const char *val;
-};
-
-static void t_trailer_iterator(const char *msg, size_t num_expected,
- struct contents *contents)
-{
- struct trailer_iterator iter;
- size_t i = 0;
-
- trailer_iterator_init(&iter, msg);
- while (trailer_iterator_advance(&iter)) {
- if (num_expected) {
- check_str(iter.raw, contents[i].raw);
- check_str(iter.key.buf, contents[i].key);
- check_str(iter.val.buf, contents[i].val);
- }
- i++;
- }
- trailer_iterator_release(&iter);
-
- check_uint(i, ==, num_expected);
-}
-
-static void run_t_trailer_iterator(void)
-{
-
- static struct test_cases {
- const char *name;
- const char *msg;
- size_t num_expected;
- struct contents contents[10];
- } tc[] = {
- {
- "empty input",
- "",
- 0,
- {{0}},
- },
- {
- "no newline at beginning",
- "Fixes: x\n"
- "Acked-by: x\n"
- "Reviewed-by: x\n",
- 0,
- {{0}},
- },
- {
- "newline at beginning",
- "\n"
- "Fixes: x\n"
- "Acked-by: x\n"
- "Reviewed-by: x\n",
- 3,
- {
- {
- .raw = "Fixes: x\n",
- .key = "Fixes",
- .val = "x",
- },
- {
- .raw = "Acked-by: x\n",
- .key = "Acked-by",
- .val = "x",
- },
- {
- .raw = "Reviewed-by: x\n",
- .key = "Reviewed-by",
- .val = "x",
- },
- {
- 0
- },
- },
- },
- {
- "without body text",
- "subject: foo bar\n"
- "\n"
- "Fixes: x\n"
- "Acked-by: x\n"
- "Reviewed-by: x\n",
- 3,
- {
- {
- .raw = "Fixes: x\n",
- .key = "Fixes",
- .val = "x",
- },
- {
- .raw = "Acked-by: x\n",
- .key = "Acked-by",
- .val = "x",
- },
- {
- .raw = "Reviewed-by: x\n",
- .key = "Reviewed-by",
- .val = "x",
- },
- {
- 0
- },
- },
- },
- {
- "with body text, without divider",
- "my subject\n"
- "\n"
- "my body which is long\n"
- "and contains some special\n"
- "chars like : = ? !\n"
- "hello\n"
- "\n"
- "Fixes: x\n"
- "Acked-by: x\n"
- "Reviewed-by: x\n"
- "Signed-off-by: x\n",
- 4,
- {
- {
- .raw = "Fixes: x\n",
- .key = "Fixes",
- .val = "x",
- },
- {
- .raw = "Acked-by: x\n",
- .key = "Acked-by",
- .val = "x",
- },
- {
- .raw = "Reviewed-by: x\n",
- .key = "Reviewed-by",
- .val = "x",
- },
- {
- .raw = "Signed-off-by: x\n",
- .key = "Signed-off-by",
- .val = "x",
- },
- {
- 0
- },
- },
- },
- {
- "with body text, without divider (second trailer block)",
- "my subject\n"
- "\n"
- "my body which is long\n"
- "and contains some special\n"
- "chars like : = ? !\n"
- "hello\n"
- "\n"
- "Fixes: x\n"
- "Acked-by: x\n"
- "Reviewed-by: x\n"
- "Signed-off-by: x\n"
- "\n"
- /*
- * Because this is the last trailer block, it takes
- * precedence over the first one encountered above.
- */
- "Helped-by: x\n"
- "Signed-off-by: x\n",
- 2,
- {
- {
- .raw = "Helped-by: x\n",
- .key = "Helped-by",
- .val = "x",
- },
- {
- .raw = "Signed-off-by: x\n",
- .key = "Signed-off-by",
- .val = "x",
- },
- {
- 0
- },
- },
- },
- {
- "with body text, with divider",
- "my subject\n"
- "\n"
- "my body which is long\n"
- "and contains some special\n"
- "chars like : = ? !\n"
- "hello\n"
- "\n"
- "---\n"
- "\n"
- /*
- * This trailer still counts because the iterator
- * always ignores the divider.
- */
- "Signed-off-by: x\n",
- 1,
- {
- {
- .raw = "Signed-off-by: x\n",
- .key = "Signed-off-by",
- .val = "x",
- },
- {
- 0
- },
- },
- },
- {
- "with non-trailer lines in trailer block",
- "subject: foo bar\n"
- "\n"
- /*
- * Even though this trailer block has a non-trailer line
- * in it, it's still a valid trailer block because it's
- * at least 25% trailers and is Git-generated (see
- * git_generated_prefixes[] in trailer.c).
- */
- "not a trailer line\n"
- "not a trailer line\n"
- "not a trailer line\n"
- "Signed-off-by: x\n",
- /*
- * Even though there is only really 1 real "trailer"
- * (Signed-off-by), we still have 4 trailer objects
- * because we still want to iterate through the entire
- * block.
- */
- 4,
- {
- {
- .raw = "not a trailer line\n",
- .key = "not a trailer line",
- .val = "",
- },
- {
- .raw = "not a trailer line\n",
- .key = "not a trailer line",
- .val = "",
- },
- {
- .raw = "not a trailer line\n",
- .key = "not a trailer line",
- .val = "",
- },
- {
- .raw = "Signed-off-by: x\n",
- .key = "Signed-off-by",
- .val = "x",
- },
- {
- 0
- },
- },
- },
- {
- "with non-trailer lines (one too many) in trailer block",
- "subject: foo bar\n"
- "\n"
- /*
- * This block has only 20% trailers, so it's below the
- * 25% threshold.
- */
- "not a trailer line\n"
- "not a trailer line\n"
- "not a trailer line\n"
- "not a trailer line\n"
- "Signed-off-by: x\n",
- 0,
- {{0}},
- },
- {
- "with non-trailer lines (only 1) in trailer block, but no Git-generated trailers",
- "subject: foo bar\n"
- "\n"
- /*
- * This block has only 1 non-trailer out of 10 (IOW, 90%
- * trailers) but is not considered a trailer block
- * because the 25% threshold only applies to cases where
- * there was a Git-generated trailer.
- */
- "Reviewed-by: x\n"
- "Reviewed-by: x\n"
- "Reviewed-by: x\n"
- "Helped-by: x\n"
- "Helped-by: x\n"
- "Helped-by: x\n"
- "Acked-by: x\n"
- "Acked-by: x\n"
- "Acked-by: x\n"
- "not a trailer line\n",
- 0,
- {{0}},
- },
- };
-
- for (int i = 0; i < sizeof(tc) / sizeof(tc[0]); i++) {
- TEST(t_trailer_iterator(tc[i].msg,
- tc[i].num_expected,
- tc[i].contents),
- "%s", tc[i].name);
- }
-}
-
-int cmd_main(int argc UNUSED, const char **argv UNUSED)
-{
- run_t_trailer_iterator();
- return test_done();
-}
diff --git a/t/unit-tests/u-trailer.c b/t/unit-tests/u-trailer.c
new file mode 100644
index 0000000000..3d60ea1603
--- /dev/null
+++ b/t/unit-tests/u-trailer.c
@@ -0,0 +1,320 @@
+#define DISABLE_SIGN_COMPARE_WARNINGS
+
+#include "unit-test.h"
+#include "trailer.h"
+
+struct contents {
+ const char *raw;
+ const char *key;
+ const char *val;
+};
+
+static void t_trailer_iterator(const char *msg, size_t num_expected,
+ struct contents *contents)
+{
+ struct trailer_iterator iter;
+ size_t i = 0;
+
+ trailer_iterator_init(&iter, msg);
+ while (trailer_iterator_advance(&iter)) {
+ if (num_expected) {
+ cl_assert_equal_s(iter.raw, contents[i].raw);
+ cl_assert_equal_s(iter.key.buf, contents[i].key);
+ cl_assert_equal_s(iter.val.buf, contents[i].val);
+ }
+ i++;
+ }
+ trailer_iterator_release(&iter);
+
+ cl_assert_equal_i(i, num_expected);
+}
+
+void test_trailer__empty_input(void)
+{
+ struct contents expected_contents[] = { 0 };
+ t_trailer_iterator("", 0, expected_contents);
+}
+
+void test_trailer__no_newline_start(void)
+{
+ struct contents expected_contents[] = { 0 };
+
+ t_trailer_iterator("Fixes: x\n"
+ "Acked-by: x\n"
+ "Reviewed-by: x\n",
+ 0,
+ expected_contents);
+}
+
+void test_trailer__newline_start(void)
+{
+ struct contents expected_contents[] = {
+ {
+ .raw = "Fixes: x\n",
+ .key = "Fixes",
+ .val = "x",
+ },
+ {
+ .raw = "Acked-by: x\n",
+ .key = "Acked-by",
+ .val = "x",
+ },
+ {
+ .raw = "Reviewed-by: x\n",
+ .key = "Reviewed-by",
+ .val = "x",
+ },
+ {
+ 0
+ },
+ };
+
+ t_trailer_iterator("\n"
+ "Fixes: x\n"
+ "Acked-by: x\n"
+ "Reviewed-by: x\n",
+ 3,
+ expected_contents);
+}
+
+void test_trailer__no_body_text(void)
+{
+ struct contents expected_contents[] = {
+
+ {
+ .raw = "Fixes: x\n",
+ .key = "Fixes",
+ .val = "x",
+ },
+ {
+ .raw = "Acked-by: x\n",
+ .key = "Acked-by",
+ .val = "x",
+ },
+ {
+ .raw = "Reviewed-by: x\n",
+ .key = "Reviewed-by",
+ .val = "x",
+ },
+ {
+ 0
+ },
+ };
+
+ t_trailer_iterator("subject: foo bar\n"
+ "\n"
+ "Fixes: x\n"
+ "Acked-by: x\n"
+ "Reviewed-by: x\n",
+ 3,
+ expected_contents);
+}
+
+void test_trailer__body_text_no_divider(void)
+{
+ struct contents expected_contents[] = {
+ {
+ .raw = "Fixes: x\n",
+ .key = "Fixes",
+ .val = "x",
+ },
+ {
+ .raw = "Acked-by: x\n",
+ .key = "Acked-by",
+ .val = "x",
+ },
+ {
+ .raw = "Reviewed-by: x\n",
+ .key = "Reviewed-by",
+ .val = "x",
+ },
+ {
+ .raw = "Signed-off-by: x\n",
+ .key = "Signed-off-by",
+ .val = "x",
+ },
+ {
+ 0
+ },
+ };
+
+ t_trailer_iterator("my subject\n"
+ "\n"
+ "my body which is long\n"
+ "and contains some special\n"
+ "chars like : = ? !\n"
+ "hello\n"
+ "\n"
+ "Fixes: x\n"
+ "Acked-by: x\n"
+ "Reviewed-by: x\n"
+ "Signed-off-by: x\n",
+ 4,
+ expected_contents);
+}
+
+void test_trailer__body_no_divider_2nd_block(void)
+{
+ struct contents expected_contents[] = {
+ {
+ .raw = "Helped-by: x\n",
+ .key = "Helped-by",
+ .val = "x",
+ },
+ {
+ .raw = "Signed-off-by: x\n",
+ .key = "Signed-off-by",
+ .val = "x",
+ },
+ {
+ 0
+ },
+ };
+
+ t_trailer_iterator("my subject\n"
+ "\n"
+ "my body which is long\n"
+ "and contains some special\n"
+ "chars like : = ? !\n"
+ "hello\n"
+ "\n"
+ "Fixes: x\n"
+ "Acked-by: x\n"
+ "Reviewed-by: x\n"
+ "Signed-off-by: x\n"
+ "\n"
+ /*
+ * Because this is the last trailer block, it takes
+ * precedence over the first one encountered above.
+ */
+ "Helped-by: x\n"
+ "Signed-off-by: x\n",
+ 2,
+ expected_contents);
+}
+
+void test_trailer__body_and_divider(void)
+{
+ struct contents expected_contents[] = {
+ {
+ .raw = "Signed-off-by: x\n",
+ .key = "Signed-off-by",
+ .val = "x",
+ },
+ {
+ 0
+ },
+ };
+
+ t_trailer_iterator("my subject\n"
+ "\n"
+ "my body which is long\n"
+ "and contains some special\n"
+ "chars like : = ? !\n"
+ "hello\n"
+ "\n"
+ "---\n"
+ "\n"
+ /*
+ * This trailer still counts because the iterator
+ * always ignores the divider.
+ */
+ "Signed-off-by: x\n",
+ 1,
+ expected_contents);
+}
+
+void test_trailer__non_trailer_in_block(void)
+{
+ struct contents expected_contents[] = {
+ {
+ .raw = "not a trailer line\n",
+ .key = "not a trailer line",
+ .val = "",
+ },
+ {
+ .raw = "not a trailer line\n",
+ .key = "not a trailer line",
+ .val = "",
+ },
+ {
+ .raw = "not a trailer line\n",
+ .key = "not a trailer line",
+ .val = "",
+ },
+ {
+ .raw = "Signed-off-by: x\n",
+ .key = "Signed-off-by",
+ .val = "x",
+ },
+ {
+ 0
+ },
+ };
+
+ t_trailer_iterator("subject: foo bar\n"
+ "\n"
+ /*
+ * Even though this trailer block has a non-trailer line
+ * in it, it's still a valid trailer block because it's
+ * at least 25% trailers and is Git-generated (see
+ * git_generated_prefixes[] in trailer.c).
+ */
+ "not a trailer line\n"
+ "not a trailer line\n"
+ "not a trailer line\n"
+ "Signed-off-by: x\n",
+ /*
+ * Even though there is only really 1 real "trailer"
+ * (Signed-off-by), we still have 4 trailer objects
+ * because we still want to iterate through the entire
+ * block.
+ */
+ 4,
+ expected_contents);
+}
+
+void test_trailer__too_many_non_trailers(void)
+{
+ struct contents expected_contents[] = { 0 };
+
+ t_trailer_iterator("subject: foo bar\n"
+ "\n"
+ /*
+ * This block has only 20% trailers, so it's below the
+ * 25% threshold.
+ */
+ "not a trailer line\n"
+ "not a trailer line\n"
+ "not a trailer line\n"
+ "not a trailer line\n"
+ "Signed-off-by: x\n",
+ 0,
+ expected_contents);
+}
+
+void test_trailer__one_non_trailer_no_git_trailers(void)
+{
+ struct contents expected_contents[] = { 0 };
+
+ t_trailer_iterator("subject: foo bar\n"
+ "\n"
+ /*
+ * This block has only 1 non-trailer out of 10 (IOW, 90%
+ * trailers) but is not considered a trailer block
+ * because the 25% threshold only applies to cases where
+ * there was a Git-generated trailer.
+ */
+ "Reviewed-by: x\n"
+ "Reviewed-by: x\n"
+ "Reviewed-by: x\n"
+ "Helped-by: x\n"
+ "Helped-by: x\n"
+ "Helped-by: x\n"
+ "Acked-by: x\n"
+ "Acked-by: x\n"
+ "Acked-by: x\n"
+ "not a trailer line\n",
+ 0,
+ expected_contents);
+}
diff --git a/t/unit-tests/t-urlmatch-normalization.c b/t/unit-tests/u-urlmatch-normalization.c
index 1769c357b9..39f6e1ba26 100644
--- a/t/unit-tests/t-urlmatch-normalization.c
+++ b/t/unit-tests/u-urlmatch-normalization.c
@@ -1,12 +1,11 @@
-#include "test-lib.h"
+#include "unit-test.h"
#include "urlmatch.h"
static void check_url_normalizable(const char *url, unsigned int normalizable)
{
char *url_norm = url_normalize(url, NULL);
- if (!check_int(normalizable, ==, url_norm ? 1 : 0))
- test_msg("input url: %s", url);
+ cl_assert_equal_i(normalizable, url_norm ? 1 : 0);
free(url_norm);
}
@@ -14,8 +13,7 @@ static void check_normalized_url(const char *url, const char *expect)
{
char *url_norm = url_normalize(url, NULL);
- if (!check_str(url_norm, expect))
- test_msg("input url: %s", url);
+ cl_assert_equal_s(url_norm, expect);
free(url_norm);
}
@@ -26,13 +24,9 @@ static void compare_normalized_urls(const char *url1, const char *url2,
char *url2_norm = url_normalize(url2, NULL);
if (equal) {
- if (!check_str(url1_norm, url2_norm))
- test_msg("input url1: %s\n input url2: %s", url1,
- url2);
- } else if (!check_int(strcmp(url1_norm, url2_norm), !=, 0)) {
- test_msg(" normalized url1: %s\n normalized url2: %s\n"
- " input url1: %s\n input url2: %s",
- url1_norm, url2_norm, url1, url2);
+ cl_assert_equal_s(url1_norm, url2_norm);
+ } else {
+ cl_assert(strcmp(url1_norm, url2_norm) != 0);
}
free(url1_norm);
free(url2_norm);
@@ -43,14 +37,12 @@ static void check_normalized_url_length(const char *url, size_t len)
struct url_info info;
char *url_norm = url_normalize(url, &info);
- if (!check_int(info.url_len, ==, len))
- test_msg(" input url: %s\n normalized url: %s", url,
- url_norm);
+ cl_assert_equal_i(info.url_len, len);
free(url_norm);
}
/* Note that only "file:" URLs should be allowed without a host */
-static void t_url_scheme(void)
+void test_urlmatch_normalization__scheme(void)
{
check_url_normalizable("", 0);
check_url_normalizable("_", 0);
@@ -73,7 +65,7 @@ static void t_url_scheme(void)
check_normalized_url("AbCdeF://x.Y", "abcdef://x.y/");
}
-static void t_url_authority(void)
+void test_urlmatch_normalization__authority(void)
{
check_url_normalizable("scheme://user:pass@", 0);
check_url_normalizable("scheme://?", 0);
@@ -109,7 +101,7 @@ static void t_url_authority(void)
check_url_normalizable("scheme://invalid....:[", 0);
}
-static void t_url_port(void)
+void test_urlmatch_normalization__port(void)
{
check_url_normalizable("xyz://q@some.host:", 1);
check_url_normalizable("xyz://q@some.host:456/", 1);
@@ -139,7 +131,7 @@ static void t_url_port(void)
check_url_normalizable("xyz://[::1]:030f/", 0);
}
-static void t_url_port_normalization(void)
+void test_urlmatch_normalization__port_normalization(void)
{
check_normalized_url("http://x:800", "http://x:800/");
check_normalized_url("http://x:0800", "http://x:800/");
@@ -154,7 +146,7 @@ static void t_url_port_normalization(void)
check_normalized_url("https://x:000000443", "https://x/");
}
-static void t_url_general_escape(void)
+void test_urlmatch_normalization__general_escape(void)
{
check_url_normalizable("http://x.y?%fg", 0);
check_normalized_url("X://W/%7e%41^%3a", "x://w/~A%5E%3A");
@@ -164,7 +156,7 @@ static void t_url_general_escape(void)
check_normalized_url("X://W?!", "x://w/?!");
}
-static void t_url_high_bit(void)
+void test_urlmatch_normalization__high_bit(void)
{
check_normalized_url(
"x://q/\x01\x02\x03\x04\x05\x06\x07\x08\x0e\x0f\x10\x11\x12",
@@ -198,26 +190,26 @@ static void t_url_high_bit(void)
"x://q/%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF");
}
-static void t_url_utf8_escape(void)
+void test_urlmatch_normalization__utf8_escape(void)
{
check_normalized_url(
"x://q/\xc2\x80\xdf\xbf\xe0\xa0\x80\xef\xbf\xbd\xf0\x90\x80\x80\xf0\xaf\xbf\xbd",
"x://q/%C2%80%DF%BF%E0%A0%80%EF%BF%BD%F0%90%80%80%F0%AF%BF%BD");
}
-static void t_url_username_pass(void)
+void test_urlmatch_normalization__username_pass(void)
{
check_normalized_url("x://%41%62(^):%70+d@foo", "x://Ab(%5E):p+d@foo/");
}
-static void t_url_length(void)
+void test_urlmatch_normalization__length(void)
{
check_normalized_url_length("Http://%4d%65:%4d^%70@The.Host", 25);
check_normalized_url_length("http://%41:%42@x.y/%61/", 17);
check_normalized_url_length("http://@x.y/^", 15);
}
-static void t_url_dots(void)
+void test_urlmatch_normalization__dots(void)
{
check_normalized_url("x://y/.", "x://y/");
check_normalized_url("x://y/./", "x://y/");
@@ -244,7 +236,7 @@ static void t_url_dots(void)
* "http://foo" specifies neither a user name nor a password.
* So they should not be equivalent.
*/
-static void t_url_equivalents(void)
+void test_urlmatch_normalization__equivalents(void)
{
compare_normalized_urls("httP://x", "Http://X/", 1);
compare_normalized_urls("Http://%4d%65:%4d^%70@The.Host", "hTTP://Me:%4D^p@the.HOST:80/", 1);
@@ -253,19 +245,3 @@ static void t_url_equivalents(void)
compare_normalized_urls("https://@x.y/^/../abc", "httpS://@x.y:0443/abc", 1);
compare_normalized_urls("https://@x.y/^/..", "httpS://@x.y:0443/", 1);
}
-
-int cmd_main(int argc UNUSED, const char **argv UNUSED)
-{
- TEST(t_url_scheme(), "url scheme");
- TEST(t_url_authority(), "url authority");
- TEST(t_url_port(), "url port checks");
- TEST(t_url_port_normalization(), "url port normalization");
- TEST(t_url_general_escape(), "url general escapes");
- TEST(t_url_high_bit(), "url high-bit escapes");
- TEST(t_url_utf8_escape(), "url utf8 escapes");
- TEST(t_url_username_pass(), "url username/password escapes");
- TEST(t_url_length(), "url normalized lengths");
- TEST(t_url_dots(), "url . and .. segments");
- TEST(t_url_equivalents(), "url equivalents");
- return test_done();
-}
diff --git a/tag.c b/tag.c
index 8d9e9e2930..05be39067c 100644
--- a/tag.c
+++ b/tag.c
@@ -5,7 +5,7 @@
#include "environment.h"
#include "tag.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "commit.h"
#include "tree.h"
#include "blob.h"
diff --git a/templates/meson.build b/templates/meson.build
index 1faf9a44ce..02e6eebe80 100644
--- a/templates/meson.build
+++ b/templates/meson.build
@@ -1,6 +1,6 @@
template_config = configuration_data()
-template_config.set('PERL_PATH', perl.found() ? fs.as_posix(perl.full_path()) : '')
-template_config.set('SHELL_PATH', fs.as_posix(shell.full_path()))
+template_config.set('PERL_PATH', target_perl.found() ? fs.as_posix(target_perl.full_path()) : '')
+template_config.set('SHELL_PATH', fs.as_posix(target_shell.full_path()))
template_config.set('GITWEBDIR', fs.as_posix(get_option('prefix') / get_option('datadir') / 'gitweb'))
configure_file(
diff --git a/tmp-objdir.c b/tmp-objdir.c
index 31d16a4c2c..c38fbeb5e8 100644
--- a/tmp-objdir.c
+++ b/tmp-objdir.c
@@ -10,7 +10,7 @@
#include "strbuf.h"
#include "strvec.h"
#include "quote.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "repository.h"
struct tmp_objdir {
diff --git a/transport-helper.c b/transport-helper.c
index d457b42550..69391ee7d2 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -162,7 +162,7 @@ static struct child_process *get_helper(struct transport *transport)
data->helper = helper;
data->no_disconnect_req = 0;
- refspec_init(&data->rs, REFSPEC_FETCH);
+ refspec_init_fetch(&data->rs);
/*
* Open the output as FILE* so strbuf_getline_*() family of
diff --git a/tree-diff.c b/tree-diff.c
index 60c558c2b5..e00fc2f450 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -181,7 +181,7 @@ static void emit_path(struct combine_diff_path ***tail,
strbuf_add(base, path, pathlen);
p = combine_diff_path_new(base->buf, base->len, mode,
- oid ? oid : null_oid(),
+ oid ? oid : null_oid(the_hash_algo),
nparent);
strbuf_setlen(base, old_baselen);
@@ -206,7 +206,7 @@ static void emit_path(struct combine_diff_path ***tail,
mode_i = tp[i].entry.mode;
}
else {
- oid_i = null_oid();
+ oid_i = null_oid(the_hash_algo);
mode_i = 0;
}
diff --git a/tree-walk.c b/tree-walk.c
index a033397965..90655d5237 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -6,7 +6,7 @@
#include "gettext.h"
#include "hex.h"
#include "object-file.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "trace2.h"
#include "tree.h"
#include "pathspec.h"
diff --git a/tree.c b/tree.c
index ad86ad1ba9..b85f56267f 100644
--- a/tree.c
+++ b/tree.c
@@ -4,7 +4,7 @@
#include "hex.h"
#include "tree.h"
#include "object-name.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "commit.h"
#include "alloc.h"
#include "tree-walk.h"
diff --git a/unpack-trees.c b/unpack-trees.c
index cf5b73c84b..471837f032 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -26,7 +26,7 @@
#include "symlinks.h"
#include "trace2.h"
#include "fsmonitor.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "promisor-remote.h"
#include "entry.h"
#include "parallel-checkout.h"
diff --git a/upload-pack.c b/upload-pack.c
index 7498b45e2e..30e4630f3a 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -10,7 +10,7 @@
#include "pkt-line.h"
#include "sideband.h"
#include "repository.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "oid-array.h"
#include "object.h"
#include "commit.h"
@@ -665,8 +665,8 @@ static int do_reachable_revlist(struct child_process *cmd,
cmd_in = xfdopen(cmd->in, "w");
- for (i = get_max_object_index(); 0 < i; ) {
- o = get_indexed_object(--i);
+ for (i = get_max_object_index(the_repository); 0 < i; ) {
+ o = get_indexed_object(the_repository, --i);
if (!o)
continue;
if (reachable && o->type == OBJ_COMMIT)
@@ -734,8 +734,8 @@ static int get_reachable_list(struct upload_pack_data *data,
o->flags &= ~TMP_MARK;
}
}
- for (i = get_max_object_index(); 0 < i; i--) {
- o = get_indexed_object(i - 1);
+ for (i = get_max_object_index(the_repository); 0 < i; i--) {
+ o = get_indexed_object(the_repository, i - 1);
if (o && o->type == OBJ_COMMIT &&
(o->flags & TMP_MARK)) {
add_object_array(o, NULL, reachable);
@@ -1449,7 +1449,7 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
for_each_namespaced_ref_1(send_ref, &data);
if (!data.sent_capabilities) {
const char *refname = "capabilities^{}";
- write_v0_ref(&data, refname, refname, null_oid());
+ write_v0_ref(&data, refname, refname, null_oid(the_hash_algo));
}
/*
* fflush stdout before calling advertise_shallow_grafts because send_ref
@@ -1557,7 +1557,7 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,
}
if (!o)
- o = parse_object_or_die(&oid, refname_nons);
+ o = parse_object_or_die(the_repository, &oid, refname_nons);
if (!(o->flags & WANTED)) {
o->flags |= WANTED;
@@ -1793,7 +1793,7 @@ int upload_pack_v2(struct repository *r, struct packet_reader *request)
enum fetch_state state = FETCH_PROCESS_ARGS;
struct upload_pack_data data;
- clear_object_flags(ALL_FLAGS);
+ clear_object_flags(the_repository, ALL_FLAGS);
upload_pack_data_init(&data);
data.use_sideband = LARGE_PACKET_MAX;
diff --git a/userdiff.c b/userdiff.c
index 340c4eb4f7..da75625020 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -211,6 +211,10 @@ PATTERNS("html",
"^[ \t]*(<[Hh][1-6]([ \t].*)?>.*)$",
/* -- */
"[^<>= \t]+"),
+PATTERNS("ini",
+ "^[ \t]*\\[[^]]+\\]",
+ /* -- */
+ "[^ \t]+"),
PATTERNS("java",
"!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
/* Class, enum, interface, and record declarations */
diff --git a/walker.c b/walker.c
index 1cf3da0219..4fedc19f34 100644
--- a/walker.c
+++ b/walker.c
@@ -5,7 +5,7 @@
#include "hex.h"
#include "walker.h"
#include "repository.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "commit.h"
#include "strbuf.h"
#include "tree.h"
diff --git a/wildmatch.c b/wildmatch.c
index 8ea29141bd..69a2ae7000 100644
--- a/wildmatch.c
+++ b/wildmatch.c
@@ -223,7 +223,7 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
p_ch = '[';
if (t_ch == p_ch)
matched = 1;
- continue;
+ goto next;
}
if (CC_EQ(s,i, "alnum")) {
if (ISALNUM(t_ch))
@@ -268,7 +268,10 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
p_ch = 0; /* This makes "prev_ch" get set to 0. */
} else if (t_ch == p_ch)
matched = 1;
- } while (prev_ch = p_ch, (p_ch = *++p) != ']');
+next:
+ prev_ch = p_ch;
+ p_ch = *++p;
+ } while (p_ch != ']');
if (matched == negated ||
((flags & WM_PATHNAME) && t_ch == '/'))
return WM_NOMATCH;
diff --git a/wrapper.c b/wrapper.c
index 8b98593149..3c79778055 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -829,3 +829,51 @@ uint32_t git_rand(unsigned flags)
return result;
}
+
+static void mmap_limit_check(size_t length)
+{
+ static size_t limit = 0;
+ if (!limit) {
+ limit = git_env_ulong("GIT_MMAP_LIMIT", 0);
+ if (!limit)
+ limit = SIZE_MAX;
+ }
+ if (length > limit)
+ die(_("attempting to mmap %"PRIuMAX" over limit %"PRIuMAX),
+ (uintmax_t)length, (uintmax_t)limit);
+}
+
+void *xmmap_gently(void *start, size_t length,
+ int prot, int flags, int fd, off_t offset)
+{
+ void *ret;
+
+ mmap_limit_check(length);
+ ret = mmap(start, length, prot, flags, fd, offset);
+ if (ret == MAP_FAILED && !length)
+ ret = NULL;
+ return ret;
+}
+
+const char *mmap_os_err(void)
+{
+ static const char blank[] = "";
+#if defined(__linux__)
+ if (errno == ENOMEM) {
+ /* this continues an existing error message: */
+ static const char enomem[] =
+", check sys.vm.max_map_count and/or RLIMIT_DATA";
+ return enomem;
+ }
+#endif /* OS-specific bits */
+ return blank;
+}
+
+void *xmmap(void *start, size_t length,
+ int prot, int flags, int fd, off_t offset)
+{
+ void *ret = xmmap_gently(start, length, prot, flags, fd, offset);
+ if (ret == MAP_FAILED)
+ die_errno(_("mmap failed%s"), mmap_os_err());
+ return ret;
+}
diff --git a/wt-status.c b/wt-status.c
index 1da5732f57..454601afa1 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1824,10 +1824,10 @@ void wt_status_get_state(struct repository *r,
if (!sequencer_get_last_command(r, &action)) {
if (action == REPLAY_PICK && !state->cherry_pick_in_progress) {
state->cherry_pick_in_progress = 1;
- oidcpy(&state->cherry_pick_head_oid, null_oid());
+ oidcpy(&state->cherry_pick_head_oid, null_oid(the_hash_algo));
} else if (action == REPLAY_REVERT && !state->revert_in_progress) {
state->revert_in_progress = 1;
- oidcpy(&state->revert_head_oid, null_oid());
+ oidcpy(&state->revert_head_oid, null_oid(the_hash_algo));
}
}
if (get_detached_from)
diff --git a/xdiff-interface.c b/xdiff-interface.c
index 3bd61f26e9..1edcd319e6 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -5,7 +5,7 @@
#include "gettext.h"
#include "config.h"
#include "hex.h"
-#include "object-store-ll.h"
+#include "object-store.h"
#include "strbuf.h"
#include "xdiff-interface.h"
#include "xdiff/xtypes.h"
@@ -181,7 +181,7 @@ void read_mmblob(mmfile_t *ptr, const struct object_id *oid)
unsigned long size;
enum object_type type;
- if (oideq(oid, null_oid())) {
+ if (oideq(oid, null_oid(the_hash_algo))) {
ptr->ptr = xstrdup("");
ptr->size = 0;
return;
diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index 8889b8b62a..5a96e36dfb 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -211,8 +211,10 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,
for (d = fmax; d >= fmin; d -= 2) {
i1 = XDL_MIN(kvdf[d], lim1);
i2 = i1 - d;
- if (lim2 < i2)
- i1 = lim2 + d, i2 = lim2;
+ if (lim2 < i2) {
+ i1 = lim2 + d;
+ i2 = lim2;
+ }
if (fbest < i1 + i2) {
fbest = i1 + i2;
fbest1 = i1;
@@ -223,8 +225,10 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,
for (d = bmax; d >= bmin; d -= 2) {
i1 = XDL_MAX(off1, kvdb[d]);
i2 = i1 - d;
- if (i2 < off2)
- i1 = off2 + d, i2 = off2;
+ if (i2 < off2) {
+ i1 = off2 + d;
+ i2 = off2;
+ }
if (i1 + i2 < bbest) {
bbest = i1 + i2;
bbest1 = i1;