summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/CodingGuidelines2
-rw-r--r--Documentation/MyFirstObjectWalk.txt2
-rw-r--r--Documentation/RelNotes/2.41.0.txt95
-rw-r--r--Documentation/config/advice.txt4
-rw-r--r--Documentation/config/feature.txt3
-rw-r--r--Documentation/config/gc.txt13
-rw-r--r--Documentation/config/http.txt5
-rw-r--r--Documentation/config/imap.txt8
-rw-r--r--Documentation/config/log.txt9
-rw-r--r--Documentation/config/pack.txt17
-rw-r--r--Documentation/config/sendemail.txt1
-rw-r--r--Documentation/config/submodule.txt12
-rw-r--r--Documentation/diff-options.txt113
-rwxr-xr-xDocumentation/doc-diff2
-rw-r--r--Documentation/fetch-options.txt7
-rw-r--r--Documentation/git-credential.txt6
-rw-r--r--Documentation/git-diff.txt8
-rw-r--r--Documentation/git-fetch.txt9
-rw-r--r--Documentation/git-for-each-ref.txt6
-rw-r--r--Documentation/git-imap-send.txt11
-rw-r--r--Documentation/git-interpret-trailers.txt216
-rw-r--r--Documentation/git-ls-files.txt6
-rw-r--r--Documentation/git-ls-remote.txt46
-rw-r--r--Documentation/git-merge.txt11
-rw-r--r--Documentation/git-name-rev.txt11
-rw-r--r--Documentation/git-notes.txt42
-rw-r--r--Documentation/git-pack-refs.txt29
-rw-r--r--Documentation/git-push.txt3
-rw-r--r--Documentation/git-revert.txt6
-rw-r--r--Documentation/git-send-email.txt19
-rw-r--r--Documentation/git-show-branch.txt16
-rw-r--r--Documentation/git-show-ref.txt40
-rw-r--r--Documentation/git-tag.txt10
-rw-r--r--Documentation/git-worktree.txt16
-rw-r--r--Documentation/git.txt14
-rw-r--r--Documentation/gitattributes.txt59
-rw-r--r--Documentation/githooks.txt27
-rw-r--r--Documentation/gitignore.txt4
-rw-r--r--Documentation/gittutorial.txt129
-rw-r--r--Documentation/manpage-normal.xsl16
-rw-r--r--Documentation/revisions.txt15
-rw-r--r--Documentation/technical/api-merge.txt4
-rw-r--r--Documentation/user-manual.txt45
-rw-r--r--INSTALL8
-rw-r--r--Makefile23
-rw-r--r--abspath.c36
-rw-r--r--abspath.h21
-rw-r--r--add-interactive.c7
-rw-r--r--add-patch.c16
-rw-r--r--advice.c1
-rw-r--r--advice.h1
-rw-r--r--alloc.c1
-rw-r--r--apply.c13
-rw-r--r--apply.h2
-rw-r--r--archive-tar.c2
-rw-r--r--archive-zip.c4
-rw-r--r--archive.c7
-rw-r--r--attr.c44
-rw-r--r--attr.h13
-rw-r--r--base85.c3
-rw-r--r--base85.h7
-rw-r--r--bisect.c3
-rw-r--r--blame.c6
-rw-r--r--blame.h1
-rw-r--r--blob.c11
-rw-r--r--blob.h3
-rw-r--r--bloom.c1
-rw-r--r--branch.c29
-rw-r--r--branch.h8
-rw-r--r--builtin.h3
-rw-r--r--builtin/add.c105
-rw-r--r--builtin/am.c7
-rw-r--r--builtin/apply.c2
-rw-r--r--builtin/archive.c2
-rw-r--r--builtin/bisect.c3
-rw-r--r--builtin/blame.c2
-rw-r--r--builtin/branch.c83
-rw-r--r--builtin/bundle.c3
-rw-r--r--builtin/cat-file.c24
-rw-r--r--builtin/check-attr.c19
-rw-r--r--builtin/check-ignore.c2
-rw-r--r--builtin/check-mailmap.c1
-rw-r--r--builtin/check-ref-format.c3
-rw-r--r--builtin/checkout--worker.c1
-rw-r--r--builtin/checkout-index.c3
-rw-r--r--builtin/checkout.c56
-rw-r--r--builtin/clean.c4
-rw-r--r--builtin/clone.c8
-rw-r--r--builtin/column.c1
-rw-r--r--builtin/commit-graph.c3
-rw-r--r--builtin/commit-tree.c5
-rw-r--r--builtin/commit.c12
-rw-r--r--builtin/config.c2
-rw-r--r--builtin/count-objects.c6
-rw-r--r--builtin/credential-cache--daemon.c3
-rw-r--r--builtin/credential-cache.c2
-rw-r--r--builtin/credential-store.c20
-rw-r--r--builtin/describe.c7
-rw-r--r--builtin/diff-files.c9
-rw-r--r--builtin/diff-index.c15
-rw-r--r--builtin/diff-tree.c6
-rw-r--r--builtin/diff.c6
-rw-r--r--builtin/difftool.c8
-rw-r--r--builtin/fast-export.c3
-rw-r--r--builtin/fast-import.c4
-rw-r--r--builtin/fetch.c596
-rw-r--r--builtin/for-each-ref.c9
-rw-r--r--builtin/for-each-repo.c5
-rw-r--r--builtin/fsck.c16
-rw-r--r--builtin/fsmonitor--daemon.c4
-rw-r--r--builtin/gc.c4
-rw-r--r--builtin/get-tar-commit-id.c3
-rw-r--r--builtin/grep.c7
-rw-r--r--builtin/hash-object.c3
-rw-r--r--builtin/help.c4
-rw-r--r--builtin/hook.c1
-rw-r--r--builtin/index-pack.c3
-rw-r--r--builtin/init-db.c474
-rw-r--r--builtin/interpret-trailers.c1
-rw-r--r--builtin/log.c7
-rw-r--r--builtin/ls-files.c170
-rw-r--r--builtin/ls-remote.c3
-rw-r--r--builtin/ls-tree.c6
-rw-r--r--builtin/mailinfo.c3
-rw-r--r--builtin/mailsplit.c1
-rw-r--r--builtin/merge-base.c1
-rw-r--r--builtin/merge-file.c1
-rw-r--r--builtin/merge-index.c3
-rw-r--r--builtin/merge-ours.c1
-rw-r--r--builtin/merge-recursive.c3
-rw-r--r--builtin/merge-tree.c6
-rw-r--r--builtin/merge.c15
-rw-r--r--builtin/mktag.c3
-rw-r--r--builtin/mktree.c3
-rw-r--r--builtin/multi-pack-index.c4
-rw-r--r--builtin/mv.c3
-rw-r--r--builtin/name-rev.c7
-rw-r--r--builtin/notes.c157
-rw-r--r--builtin/pack-objects.c4
-rw-r--r--builtin/pack-redundant.c2
-rw-r--r--builtin/pack-refs.c31
-rw-r--r--builtin/patch-id.c2
-rw-r--r--builtin/prune.c7
-rw-r--r--builtin/pull.c16
-rw-r--r--builtin/push.c12
-rw-r--r--builtin/range-diff.c2
-rw-r--r--builtin/read-tree.c15
-rw-r--r--builtin/rebase.c3
-rw-r--r--builtin/receive-pack.c11
-rw-r--r--builtin/reflog.c2
-rw-r--r--builtin/remote.c3
-rw-r--r--builtin/repack.c4
-rw-r--r--builtin/replace.c7
-rw-r--r--builtin/rerere.c2
-rw-r--r--builtin/reset.c13
-rw-r--r--builtin/rev-list.c5
-rw-r--r--builtin/rev-parse.c6
-rw-r--r--builtin/revert.c1
-rw-r--r--builtin/rm.c4
-rw-r--r--builtin/shortlog.c2
-rw-r--r--builtin/show-branch.c6
-rw-r--r--builtin/show-index.c3
-rw-r--r--builtin/show-ref.c3
-rw-r--r--builtin/sparse-checkout.c1
-rw-r--r--builtin/stash.c4
-rw-r--r--builtin/stripspace.c7
-rw-r--r--builtin/submodule--helper.c7
-rw-r--r--builtin/symbolic-ref.c1
-rw-r--r--builtin/tag.c43
-rw-r--r--builtin/unpack-file.c2
-rw-r--r--builtin/unpack-objects.c3
-rw-r--r--builtin/update-index.c8
-rw-r--r--builtin/update-ref.c5
-rw-r--r--builtin/update-server-info.c4
-rw-r--r--builtin/upload-archive.c3
-rw-r--r--builtin/upload-pack.c2
-rw-r--r--builtin/verify-commit.c5
-rw-r--r--builtin/verify-pack.c2
-rw-r--r--builtin/verify-tag.c3
-rw-r--r--builtin/worktree.c234
-rw-r--r--builtin/write-tree.c2
-rw-r--r--bulk-checkin.c2
-rw-r--r--bundle-uri.c5
-rw-r--r--bundle.c2
-rw-r--r--cache-tree.c5
-rw-r--r--checkout.c1
-rw-r--r--checkout.h2
-rw-r--r--chunk-format.c1
-rw-r--r--chunk-format.h2
-rwxr-xr-xci/run-build-and-tests.sh1
-rw-r--r--color.c3
-rw-r--r--combine-diff.c5
-rw-r--r--commit-graph.c4
-rw-r--r--commit-graph.h2
-rw-r--r--commit.c71
-rw-r--r--commit.h13
-rw-r--r--common-main.c4
-rw-r--r--compat/fsmonitor/fsm-health-darwin.c4
-rw-r--r--compat/fsmonitor/fsm-health-win32.c4
-rw-r--r--compat/fsmonitor/fsm-ipc-darwin.c7
-rw-r--r--compat/fsmonitor/fsm-ipc-win32.c1
-rw-r--r--compat/fsmonitor/fsm-listen-darwin.c6
-rw-r--r--compat/fsmonitor/fsm-listen-win32.c4
-rw-r--r--compat/fsmonitor/fsm-path-utils-darwin.c4
-rw-r--r--compat/fsmonitor/fsm-path-utils-win32.c5
-rw-r--r--compat/fsmonitor/fsm-settings-darwin.c2
-rw-r--r--compat/fsmonitor/fsm-settings-win32.c4
-rw-r--r--compat/mingw.c2
-rw-r--r--compat/precompose_utf8.c4
-rw-r--r--compat/sha1-chunked.c3
-rw-r--r--compat/simple-ipc/ipc-win32.c35
-rw-r--r--compat/win32/trace2_win32_process_info.c3
-rw-r--r--config.c8
-rw-r--r--connect.c1
-rw-r--r--connected.c2
-rw-r--r--contrib/coccinelle/README40
-rw-r--r--contrib/coccinelle/tests/unused.c82
-rw-r--r--contrib/coccinelle/tests/unused.res45
-rw-r--r--contrib/coccinelle/unused.cocci43
-rw-r--r--contrib/completion/git-completion.bash2
-rw-r--r--contrib/credential/gnome-keyring/.gitignore1
-rw-r--r--contrib/credential/gnome-keyring/Makefile25
-rw-r--r--contrib/credential/gnome-keyring/git-credential-gnome-keyring.c470
-rw-r--r--contrib/credential/libsecret/.gitignore1
-rw-r--r--contrib/credential/libsecret/git-credential-libsecret.c93
-rw-r--r--contrib/credential/osxkeychain/git-credential-osxkeychain.c10
-rw-r--r--contrib/credential/wincred/git-credential-wincred.c107
-rwxr-xr-xcontrib/subtree/git-subtree.sh4
-rw-r--r--convert.c10
-rw-r--r--convert.h2
-rw-r--r--copy.c4
-rw-r--r--copy.h10
-rw-r--r--credential.c11
-rw-r--r--credential.h1
-rw-r--r--csum-file.c1
-rw-r--r--csum-file.h2
-rw-r--r--daemon.c3
-rw-r--r--date.c3
-rw-r--r--decorate.c1
-rwxr-xr-xdetect-compiler10
-rw-r--r--diagnose.c4
-rw-r--r--diff-lib.c129
-rw-r--r--diff-merges.c80
-rw-r--r--diff-merges.h4
-rw-r--r--diff.c67
-rw-r--r--diff.h7
-rw-r--r--diffcore-break.c4
-rw-r--r--diffcore-order.c1
-rw-r--r--diffcore-pickaxe.c1
-rw-r--r--diffcore-rename.c2
-rw-r--r--diffcore.h2
-rw-r--r--dir.c10
-rw-r--r--dir.h18
-rw-r--r--editor.c1
-rw-r--r--entry.c7
-rw-r--r--environment.c8
-rw-r--r--environment.h4
-rw-r--r--exec-cmd.c4
-rw-r--r--fetch-pack.c4
-rw-r--r--fmt-merge-msg.c12
-rw-r--r--fsck.c5
-rw-r--r--fsmonitor--daemon.h1
-rw-r--r--fsmonitor-ipc.c5
-rw-r--r--fsmonitor-ll.h52
-rw-r--r--fsmonitor.c2
-rw-r--r--fsmonitor.h49
-rw-r--r--git-compat-util.h2
-rwxr-xr-xgit-send-email.perl186
-rw-r--r--git.c17
-rwxr-xr-xgitk-git/gitk36
-rw-r--r--gpg-interface.c7
-rw-r--r--grep.c3
-rw-r--r--hash-ll.h295
-rw-r--r--hash-lookup.c4
-rw-r--r--hash.h273
-rw-r--r--hashmap.h21
-rw-r--r--hex.c1
-rw-r--r--hex.h2
-rw-r--r--hook.c2
-rw-r--r--http-backend.c5
-rw-r--r--http-push.c4
-rw-r--r--http-walker.c2
-rw-r--r--http.c2
-rw-r--r--imap-send.c183
-rw-r--r--khash.h4
-rw-r--r--line-log.c2
-rw-r--r--list-objects-filter-options.h1
-rw-r--r--list-objects-filter.c2
-rw-r--r--list-objects.c2
-rw-r--r--log-tree.c5
-rw-r--r--log-tree.h2
-rw-r--r--ls-refs.c9
-rw-r--r--mailmap.c2
-rw-r--r--match-trees.c6
-rw-r--r--match-trees.h10
-rw-r--r--merge-blobs.c4
-rw-r--r--merge-ll.c (renamed from ll-merge.c)9
-rw-r--r--merge-ll.h (renamed from ll-merge.h)0
-rw-r--r--merge-ort-wrappers.c5
-rw-r--r--merge-ort.c10
-rw-r--r--merge-ort.h2
-rw-r--r--merge-recursive.c11
-rw-r--r--merge.c6
-rw-r--r--merge.h17
-rw-r--r--midx.c4
-rw-r--r--name-hash.c5
-rw-r--r--name-hash.h16
-rw-r--r--negotiator/default.c40
-rw-r--r--negotiator/skipping.c23
-rw-r--r--notes-cache.c4
-rw-r--r--notes-merge.c5
-rw-r--r--notes-utils.c1
-rw-r--r--notes.c2
-rw-r--r--object-file.c4
-rw-r--r--object-file.h2
-rw-r--r--object-name.c22
-rw-r--r--object-name.h9
-rw-r--r--object-store-ll.h533
-rw-r--r--object-store.h528
-rw-r--r--object.c29
-rw-r--r--object.h7
-rw-r--r--oidmap.c1
-rw-r--r--oidmap.h1
-rw-r--r--oidtree.h2
-rw-r--r--oss-fuzz/fuzz-pack-idx.c2
-rw-r--r--pack-bitmap-write.c5
-rw-r--r--pack-bitmap.c372
-rw-r--r--pack-bitmap.h6
-rw-r--r--pack-check.c2
-rw-r--r--pack-mtimes.c3
-rw-r--r--pack-objects.h2
-rw-r--r--pack-revindex.c3
-rw-r--r--pack-write.c2
-rw-r--r--packfile.c4
-rw-r--r--packfile.h16
-rw-r--r--parallel-checkout.c5
-rw-r--r--parse-options-cb.c1
-rw-r--r--parse-options.c1
-rw-r--r--patch-ids.c1
-rw-r--r--path.c22
-rw-r--r--path.h5
-rw-r--r--pathspec.c8
-rw-r--r--pkt-line.c3
-rw-r--r--pkt-line.h2
-rw-r--r--preload-index.c5
-rw-r--r--preload-index.h15
-rw-r--r--pretty.c2
-rw-r--r--progress.c1
-rw-r--r--promisor-remote.c2
-rw-r--r--protocol-caps.c4
-rw-r--r--prune-packed.c2
-rw-r--r--range-diff.c3
-rw-r--r--reachable.c87
-rw-r--r--read-cache-ll.h (renamed from cache.h)270
-rw-r--r--read-cache.c412
-rw-r--r--read-cache.h45
-rw-r--r--rebase-interactive.c16
-rw-r--r--ref-filter.c69
-rw-r--r--ref-filter.h12
-rw-r--r--reflog-walk.c1
-rw-r--r--reflog.c4
-rw-r--r--refs.c69
-rw-r--r--refs.h23
-rw-r--r--refs/debug.c10
-rw-r--r--refs/files-backend.c37
-rw-r--r--refs/packed-backend.c232
-rw-r--r--refs/ref-cache.c2
-rw-r--r--refs/ref-cache.h3
-rw-r--r--refs/refs-internal.h10
-rw-r--r--refspec.c2
-rw-r--r--reftable/dump.c2
-rw-r--r--reftable/error.c1
-rw-r--r--reftable/publicbasics.c2
-rw-r--r--reftable/system.h2
-rw-r--r--reftable/tree.c2
-rw-r--r--reftable/tree_test.c1
-rw-r--r--remote.c3
-rw-r--r--remote.h1
-rw-r--r--replace-object.c2
-rw-r--r--replace-object.h2
-rw-r--r--repo-settings.c7
-rw-r--r--repository.c6
-rw-r--r--repository.h9
-rw-r--r--rerere.c9
-rw-r--r--reset.h2
-rw-r--r--resolve-undo.c5
-rw-r--r--resolve-undo.h2
-rw-r--r--revision.c11
-rw-r--r--revision.h8
-rw-r--r--run-command.c20
-rw-r--r--run-command.h29
-rw-r--r--send-pack.c3
-rw-r--r--sequencer.c235
-rw-r--r--serve.c1
-rw-r--r--server-info.c6
-rw-r--r--server-info.h7
-rw-r--r--setup.c498
-rw-r--r--setup.h9
-rw-r--r--shallow.c6
-rw-r--r--sparse-index.c6
-rw-r--r--sparse-index.h2
-rw-r--r--split-index.c5
-rw-r--r--split-index.h2
-rw-r--r--statinfo.c87
-rw-r--r--statinfo.h51
-rw-r--r--strbuf.c113
-rw-r--r--strbuf.h61
-rw-r--r--streaming.c2
-rw-r--r--submodule-config.c24
-rw-r--r--submodule-config.h1
-rw-r--r--submodule.c279
-rw-r--r--submodule.h15
-rw-r--r--symlinks.c3
-rw-r--r--symlinks.h28
-rw-r--r--t/README4
-rw-r--r--t/helper/test-bloom.c1
-rw-r--r--t/helper/test-cache-tree.c3
-rw-r--r--t/helper/test-ctype.c2
-rw-r--r--t/helper/test-dump-cache-tree.c4
-rw-r--r--t/helper/test-dump-fsmonitor.c3
-rw-r--r--t/helper/test-dump-split-index.c3
-rw-r--r--t/helper/test-dump-untracked-cache.c3
-rw-r--r--t/helper/test-example-decorate.c1
-rw-r--r--t/helper/test-fsmonitor-client.c3
-rw-r--r--t/helper/test-hash-speed.c2
-rw-r--r--t/helper/test-index-version.c2
-rw-r--r--t/helper/test-lazy-init-name-hash.c4
-rw-r--r--t/helper/test-match-trees.c3
-rw-r--r--t/helper/test-mergesort.c2
-rw-r--r--t/helper/test-oid-array.c2
-rw-r--r--t/helper/test-oidmap.c1
-rw-r--r--t/helper/test-oidtree.c2
-rw-r--r--t/helper/test-pack-mtimes.c2
-rw-r--r--t/helper/test-parse-options.c2
-rw-r--r--t/helper/test-partial-clone.c2
-rw-r--r--t/helper/test-path-utils.c3
-rw-r--r--t/helper/test-reach.c2
-rw-r--r--t/helper/test-read-cache.c3
-rw-r--r--t/helper/test-read-graph.c2
-rw-r--r--t/helper/test-read-midx.c4
-rw-r--r--t/helper/test-ref-store.c25
-rw-r--r--t/helper/test-reftable.c1
-rw-r--r--t/helper/test-repository.c2
-rw-r--r--t/helper/test-revision-walking.c1
-rw-r--r--t/helper/test-run-command.c20
-rw-r--r--t/helper/test-scrap-cache-tree.c3
-rw-r--r--t/helper/test-sha1.c2
-rw-r--r--t/helper/test-sha256.c2
-rw-r--r--t/helper/test-strcmp-offset.c2
-rw-r--r--t/helper/test-string-list.c2
-rw-r--r--t/helper/test-submodule-config.c2
-rw-r--r--t/helper/test-submodule-nested-repo-config.c1
-rw-r--r--t/helper/test-submodule.c1
-rw-r--r--t/helper/test-trace2.c1
-rw-r--r--t/helper/test-wildmatch.c1
-rw-r--r--t/helper/test-write-cache.c3
-rw-r--r--t/lib-credential.sh89
-rw-r--r--t/lib-diff-alternative.sh31
-rw-r--r--t/lib-submodule-update.sh2
-rwxr-xr-xt/perf/p2000-sparse-operations.sh5
-rwxr-xr-xt/t0003-attributes.sh11
-rwxr-xr-xt/t0020-crlf.sh38
-rwxr-xr-xt/t0035-safe-bare-repository.sh32
-rwxr-xr-xt/t0041-usage.sh1
-rwxr-xr-xt/t0061-run-command.sh39
-rwxr-xr-xt/t0300-credentials.sh20
-rwxr-xr-xt/t0301-credential-cache.sh2
-rwxr-xr-xt/t0303-credential-external.sh2
-rwxr-xr-xt/t1006-cat-file.sh27
-rwxr-xr-xt/t1092-sparse-checkout-compatibility.sh128
-rwxr-xr-xt/t1300-config.sh43
-rwxr-xr-xt/t1301-shared-repo.sh26
-rwxr-xr-xt/t1419-exclude-refs.sh131
-rwxr-xr-xt/t1450-fsck.sh5
-rwxr-xr-xt/t1502-rev-parse-parseopt.sh6
-rwxr-xr-xt/t1507-rev-parse-upstream.sh3
-rwxr-xr-xt/t2019-checkout-ambiguous-ref.sh4
-rwxr-xr-xt/t2021-checkout-overwrite.sh2
-rwxr-xr-xt/t2400-worktree-add.sh541
-rwxr-xr-xt/t3013-ls-files-format.sh35
-rwxr-xr-xt/t3200-branch.sh29
-rwxr-xr-xt/t3202-show-branch.sh18
-rwxr-xr-xt/t3210-pack-refs.sh37
-rwxr-xr-xt/t3301-notes.sh126
-rwxr-xr-xt/t3321-notes-stripspace.sh577
-rwxr-xr-xt/t3402-rebase-merge.sh1
-rwxr-xr-xt/t3404-rebase-interactive.sh49
-rwxr-xr-xt/t3427-rebase-subtree.sh12
-rwxr-xr-xt/t3430-rebase-merges.sh43
-rwxr-xr-xt/t3501-revert-cherry-pick.sh18
-rwxr-xr-xt/t3515-revert-subjects.sh32
-rwxr-xr-xt/t4000-diff-format.sh34
-rwxr-xr-xt/t4013-diff-various.sh71
-rw-r--r--t/t4013/diff.log_--diff-merges=first-parent_--diff-merges=hide_master34
-rwxr-xr-xt/t4018-diff-funcname.sh19
-rwxr-xr-xt/t4022-diff-rewrite.sh2
-rwxr-xr-xt/t4027-diff-submodule.sh31
-rwxr-xr-xt/t4047-diff-dirstat.sh2
-rwxr-xr-xt/t4062-diff-pickaxe.sh2
-rwxr-xr-xt/t4067-diff-partial-clone.sh4
-rwxr-xr-xt/t4115-apply-symlink.sh2
-rwxr-xr-xt/t4212-log-corrupt.sh51
-rwxr-xr-xt/t4300-merge-tree.sh18
-rwxr-xr-xt/t5304-prune.sh30
-rwxr-xr-xt/t5310-pack-bitmaps.sh38
-rwxr-xr-xt/t5326-multi-pack-bitmaps.sh44
-rwxr-xr-xt/t5329-pack-objects-cruft.sh171
-rwxr-xr-xt/t5407-post-rewrite-hook.sh11
-rwxr-xr-xt/t5510-fetch.sh53
-rwxr-xr-xt/t5523-push-upstream.sh12
-rwxr-xr-xt/t5526-fetch-submodules.sh13
-rwxr-xr-xt/t5543-atomic-push.sh5
-rwxr-xr-xt/t5551-http-fetch-smart.sh27
-rwxr-xr-xt/t5572-pull-submodule.sh4
-rwxr-xr-xt/t5574-fetch-output.sh293
-rwxr-xr-xt/t5583-push-branches.sh115
-rwxr-xr-xt/t5606-clone-options.sh10
-rwxr-xr-xt/t5700-protocol-v1.sh31
-rwxr-xr-xt/t6006-rev-list-format.sh2
-rwxr-xr-xt/t6102-rev-list-unexpected-objects.sh146
-rwxr-xr-xt/t6300-for-each-ref.sh35
-rwxr-xr-xt/t6501-freshen-objects.sh2
-rwxr-xr-xt/t7001-mv.sh37
-rwxr-xr-xt/t7004-tag.sh19
-rwxr-xr-xt/t7300-clean.sh4
-rwxr-xr-xt/t7506-status-submodule.sh25
-rwxr-xr-xt/t7701-repack-unpack-unreachable.sh31
-rwxr-xr-xt/t9001-send-email.sh115
-rwxr-xr-xt/t9800-git-p4-basic.sh2
-rwxr-xr-xt/t9902-completion.sh36
-rw-r--r--t/test-lib-functions.sh9
-rw-r--r--t/test-lib.sh4
-rw-r--r--tag.c25
-rw-r--r--tag.h2
-rw-r--r--tempfile.c1
-rw-r--r--tmp-objdir.c3
-rw-r--r--trace2.c1
-rw-r--r--trace2.h2
-rw-r--r--trace2/tr2_cfg.c1
-rw-r--r--trace2/tr2_ctr.c5
-rw-r--r--trace2/tr2_tgt_event.c1
-rw-r--r--trace2/tr2_tgt_normal.c1
-rw-r--r--trace2/tr2_tgt_perf.c1
-rw-r--r--transport-helper.c1
-rw-r--r--transport.c2
-rw-r--r--tree-diff.c17
-rw-r--r--tree-walk.c4
-rw-r--r--tree-walk.h3
-rw-r--r--tree.c85
-rw-r--r--tree.h13
-rw-r--r--unpack-trees.c7
-rw-r--r--unpack-trees.h2
-rw-r--r--upload-pack.c67
-rw-r--r--userdiff.c2
-rw-r--r--versioncmp.c3
-rw-r--r--versioncmp.h6
-rw-r--r--walker.c3
-rw-r--r--worktree.c1
-rw-r--r--wrapper.c5
-rw-r--r--ws.c8
-rw-r--r--ws.h33
-rw-r--r--wt-status.c12
-rw-r--r--xdiff-interface.c3
-rw-r--r--xdiff-interface.h2
564 files changed, 10439 insertions, 5087 deletions
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index 003393ed16..2b472df29d 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -444,7 +444,7 @@ For C programs:
- The first #include in C files, except in platform specific compat/
implementations and sha1dc/, must be either "git-compat-util.h" or
one of the approved headers that includes it first for you. (The
- approved headers currently include "cache.h", "builtin.h",
+ approved headers currently include "builtin.h",
"t/helper/test-tool.h", "xdiff/xinclude.h", or
"reftable/system.h"). You do not have to include more than one of
these.
diff --git a/Documentation/MyFirstObjectWalk.txt b/Documentation/MyFirstObjectWalk.txt
index eee513e86f..200e628e30 100644
--- a/Documentation/MyFirstObjectWalk.txt
+++ b/Documentation/MyFirstObjectWalk.txt
@@ -124,7 +124,7 @@ parameters provided by the user over the CLI.
`nr` represents the number of `rev_cmdline_entry` present in the array.
-`alloc` is used by the `ALLOC_GROW` macro. Check `cache.h` - this variable is
+`alloc` is used by the `ALLOC_GROW` macro. Check `alloc.h` - this variable is
used to track the allocated size of the list.
Per entry, we find:
diff --git a/Documentation/RelNotes/2.41.0.txt b/Documentation/RelNotes/2.41.0.txt
index 52bfa08511..a4b4b1d826 100644
--- a/Documentation/RelNotes/2.41.0.txt
+++ b/Documentation/RelNotes/2.41.0.txt
@@ -3,7 +3,7 @@ Git v2.41 Release Notes
UI, Workflows & Features
- * Allow information carried on the WWW-AUthenticate header to be
+ * Allow information carried on the WWW-Authenticate header to be
passed to the credential helpers.
* A new "fetch.hideRefs" option can be used to exclude specified refs
@@ -83,6 +83,40 @@ UI, Workflows & Features
taken from the file specified by the "--contents" option shows it
differently from a line attributed to the working tree file.
+ * "git send-email" learned to give the e-mail headers to the validate
+ hook by passing an extra argument from the command line.
+
+ * The credential subsystem learns to help OAuth framework.
+
+ * The titles of manual pages used to be chomped at an unreasonably
+ short limit, which has been removed.
+
+ * Error messages given when working on an unborn branch that is
+ checked out in another worktree have been improved.
+
+ * The documentation was misleading about the interaction between
+ GIT_DEFAULT_HASH and "git clone", which has been clarified to
+ stress that the variable is to be ignored by the command.
+
+ * "git send-email" learned "--header-cmd=<cmd>" that can inject
+ arbitrary e-mail header lines to the outgoing messages.
+
+ * "git fsck" learned to detect bit-flip breakages in the reachability
+ bitmap files.
+
+ * The "--stdin" option of "git name-rev" has been replaced with
+ the "--annotate-stdin" option more than a year ago. We stop
+ advertising it in the "git name-rev -h" output.
+
+ * "git push --all" gained an alias "git push --branches".
+
+ * "git fetch" learned the "--porcelain" option that emits what it did
+ in a machine-parseable format.
+
+ * "git --attr-source=<tree> cmd $args" is a new way to have any
+ command to read attributes not from the working tree but from the
+ given tree object.
+
Performance, Internal Implementation, Development Support etc.
@@ -131,6 +165,35 @@ Performance, Internal Implementation, Development Support etc.
* strtok() and strtok_r() are banned in this codebase.
+ * The detect-compilers script to help auto-tweaking the build system
+ had trouble working with compilers whose version number has extra
+ suffixes. The script has been taught that certain suffixes (like
+ "-win32" in "gcc 10-win32") can be safely stripped as they share
+ the same features and bugs with the version without the suffix.
+
+ * ctype tests have been taught to test EOF, too.
+
+ * The implementation of credential helpers used fgets() over fixed
+ size buffers to read protocol messages, causing the remainder of
+ the folded long line to trigger unexpected behaviour, which has
+ been corrected.
+
+ * The implementation of the default "negotiator", used to find common
+ ancestor over the network for object tranfer, used to be recursive;
+ it was updated to be iterative to conserve stackspace usage.
+
+ * Our custom callout formatter is no longer used in the documentation
+ formatting toolchain, as the upstream default ones give better
+ output these days.
+
+ * The tracing mechanism learned to notice and report when
+ auto-discovered bare repositories are being used, as allowing so
+ without explicitly stating the user intends to do so (with setting
+ GIT_DIR for example) can be used with social engineering as an
+ attack vector.
+
+ * "git diff-files" learned not to expand sparse-index unless needed.
+
Fixes since v2.40
-----------------
@@ -284,6 +347,28 @@ Fixes since v2.40
* A small API fix to the ort merge strategy backend.
(merge 000c4ceca7 en/ort-finalize-after-0-merges-fix later to maint).
+ * The commit object parser has been taught to be a bit more lenient
+ to parse timestamps on the author/committer line with a malformed
+ author/committer ident.
+ (merge 90ef0f14eb jk/parse-commit-with-malformed-ident later to maint).
+
+ * Retitle a test script with an overly narrow name.
+ (merge 8bb19c14fb ob/t3501-retitle later to maint).
+
+ * Doc update to clarify how text and eol attributes interact to
+ specify the end-of-line conversion.
+ (merge 6696077ace ah/doc-attributes-text later to maint).
+
+ * Gitk updates from GfW project.
+ (merge 99e70f3077 js/gitk-fixes-from-gfw later to maint).
+
+ * "git diff --dirstat" leaked memory, which has been plugged.
+ (merge 83973981eb jc/dirstat-plug-leaks later to maint).
+
+ * "git merge-tree" reads the basic configuration, which can be used
+ by git forges to disable replace-refs feature.
+ (merge b6551feadf ds/merge-tree-use-config later to maint).
+
* Other code cleanup, docfix, build fix, etc.
(merge f7111175df as/doc-markup-fix later to maint).
(merge 90ff7c9898 fc/test-aggregation-clean-up later to maint).
@@ -300,3 +385,11 @@ Fixes since v2.40
(merge 4833b08426 ow/ref-format-remove-unused-member later to maint).
(merge d0ea2ca1cf dw/doc-submittingpatches-grammofix later to maint).
(merge fd72637423 ar/t2024-checkout-output-fix later to maint).
+ (merge d45cbe3fe0 ob/sequencer-i18n-fix later to maint).
+ (merge b734fe49fd ob/messages-capitalize-exception later to maint).
+ (merge ad353d7e77 ma/gittutorial-fixes later to maint).
+ (merge a5855fd8d4 ar/test-cleanup-unused-file-creation-part2 later to maint).
+ (merge 0c5308af30 sd/doc-gitignore-and-rm-cached later to maint).
+ (merge cbb83daeaf kh/doc-interpret-trailers-updates later to maint).
+ (merge 3d77fbb664 ar/config-count-tests-updates later to maint).
+ (merge b7cf25c8f4 jc/t9800-fix-use-of-show-s-raw later to maint).
diff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt
index c96b5b2e5d..c548a91e67 100644
--- a/Documentation/config/advice.txt
+++ b/Documentation/config/advice.txt
@@ -138,4 +138,8 @@ advice.*::
checkout.
diverging::
Advice shown when a fast-forward is not possible.
+ worktreeAddOrphan::
+ Advice shown when a user tries to create a worktree from an
+ invalid reference, to instruct how to create a new orphan
+ branch instead.
--
diff --git a/Documentation/config/feature.txt b/Documentation/config/feature.txt
index 17b4d39f89..bf9546fca4 100644
--- a/Documentation/config/feature.txt
+++ b/Documentation/config/feature.txt
@@ -14,6 +14,9 @@ feature.experimental::
+
* `fetch.negotiationAlgorithm=skipping` may improve fetch negotiation times by
skipping more commits at a time, reducing the number of round trips.
++
+* `pack.useBitmapBoundaryTraversal=true` may improve bitmap traversal times by
+walking fewer objects.
feature.manyFiles::
Enable config options that optimize for repos with many files in the
diff --git a/Documentation/config/gc.txt b/Documentation/config/gc.txt
index 7f95c866e1..16190be877 100644
--- a/Documentation/config/gc.txt
+++ b/Documentation/config/gc.txt
@@ -130,6 +130,19 @@ or rebase occurring. Since these changes are not part of the current
project most users will want to expire them sooner, which is why the
default is more aggressive than `gc.reflogExpire`.
+gc.recentObjectsHook::
+ When considering the recency of an object (e.g., when generating
+ a cruft pack or storing unreachable objects as loose), use the
+ shell to execute the specified command(s). Interpret their
+ output as object IDs which Git will consider as "recent",
+ regardless of their age.
++
+Output must contain exactly one hex object ID per line, and nothing
+else. Objects which cannot be found in the repository are ignored.
+Multiple hooks are supported, but all must exit successfully, else the
+operation (either generating a cruft pack or unpacking unreachable
+objects) will be halted.
+
gc.rerereResolved::
Records of conflicted merge you resolved earlier are
kept for this many days when 'git rerere gc' is run.
diff --git a/Documentation/config/http.txt b/Documentation/config/http.txt
index afeeccfbfa..51a70781e5 100644
--- a/Documentation/config/http.txt
+++ b/Documentation/config/http.txt
@@ -246,8 +246,9 @@ significantly since the entire buffer is allocated even for small
pushes.
http.lowSpeedLimit, http.lowSpeedTime::
- If the HTTP transfer speed is less than 'http.lowSpeedLimit'
- for longer than 'http.lowSpeedTime' seconds, the transfer is aborted.
+ If the HTTP transfer speed, in bytes per second, is less than
+ 'http.lowSpeedLimit' for longer than 'http.lowSpeedTime' seconds,
+ the transfer is aborted.
Can be overridden by the `GIT_HTTP_LOW_SPEED_LIMIT` and
`GIT_HTTP_LOW_SPEED_TIME` environment variables.
diff --git a/Documentation/config/imap.txt b/Documentation/config/imap.txt
index 06166fb5c0..5cc46d8721 100644
--- a/Documentation/config/imap.txt
+++ b/Documentation/config/imap.txt
@@ -26,8 +26,7 @@ imap.port::
imap.sslverify::
A boolean to enable/disable verification of the server certificate
- used by the SSL/TLS connection. Default is `true`. Ignored when
- imap.tunnel is set.
+ used by the SSL/TLS connection.
imap.preformattedHTML::
A boolean to enable/disable the use of html encoding when sending
@@ -38,7 +37,6 @@ imap.preformattedHTML::
imap.authMethod::
Specify authenticate method for authentication with IMAP server.
- If Git was built with the NO_CURL option, or if your curl version is older
- than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
+ If you're using imap.tunnel, the only supported method is 'CRAM-MD5'.
+ If this is not set
then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
diff --git a/Documentation/config/log.txt b/Documentation/config/log.txt
index 5f96cf87fb..e1b1f723b9 100644
--- a/Documentation/config/log.txt
+++ b/Documentation/config/log.txt
@@ -37,6 +37,15 @@ log.diffMerges::
Set diff format to be used when `--diff-merges=on` is
specified, see `--diff-merges` in linkgit:git-log[1] for
details. Defaults to `separate`.
++
+When used with 'hide' or 'no-hide', sets the default hiding of
+diffs for merge commits when `-p` option is not used.
+
+log.diffMergesHide::
+ `true` implies `--diff-merges=hide` option.
+
+log.diffMerges-m-imply-p::
+ `true` enables implication of `-p` by `-m`.
log.follow::
If `true`, `git log` will act as if the `--follow` option was used when
diff --git a/Documentation/config/pack.txt b/Documentation/config/pack.txt
index d4c7c9d4e4..3748136d14 100644
--- a/Documentation/config/pack.txt
+++ b/Documentation/config/pack.txt
@@ -123,6 +123,23 @@ pack.useBitmaps::
true. You should not generally need to turn this off unless
you are debugging pack bitmaps.
+pack.useBitmapBoundaryTraversal::
+ When true, Git will use an experimental algorithm for computing
+ reachability queries with bitmaps. Instead of building up
+ complete bitmaps for all of the negated tips and then OR-ing
+ them together, consider negated tips with existing bitmaps as
+ additive (i.e. OR-ing them into the result if they exist,
+ ignoring them otherwise), and build up a bitmap at the boundary
+ instead.
++
+When using this algorithm, Git may include too many objects as a result
+of not opening up trees belonging to certain UNINTERESTING commits. This
+inexactness matches the non-bitmap traversal algorithm.
++
+In many cases, this can provide a speed-up over the exact algorithm,
+particularly when there is poor bitmap coverage of the negated side of
+the query.
+
pack.useSparse::
When true, git will default to using the '--sparse' option in
'git pack-objects' when the '--revs' option is present. This
diff --git a/Documentation/config/sendemail.txt b/Documentation/config/sendemail.txt
index 51da7088a8..92a9ebe98c 100644
--- a/Documentation/config/sendemail.txt
+++ b/Documentation/config/sendemail.txt
@@ -61,6 +61,7 @@ sendemail.ccCmd::
sendemail.chainReplyTo::
sendemail.envelopeSender::
sendemail.from::
+sendemail.headerCmd::
sendemail.signedoffbycc::
sendemail.smtpPass::
sendemail.suppresscc::
diff --git a/Documentation/config/submodule.txt b/Documentation/config/submodule.txt
index 6490527b45..3209eb8117 100644
--- a/Documentation/config/submodule.txt
+++ b/Documentation/config/submodule.txt
@@ -93,6 +93,18 @@ submodule.fetchJobs::
in parallel. A value of 0 will give some reasonable default.
If unset, it defaults to 1.
+submodule.diffJobs::
+ Specifies how many submodules are diffed at the same time. A
+ positive integer allows up to that number of submodules diffed
+ in parallel. A value of 0 will give some reasonable default.
+ If unset, it defaults to 1. The diff operation is used by many
+ other git commands such as add, merge, diff, status, stash and
+ more. Note that the expensive part of the diff operation is
+ reading the index from cache or memory. Therefore multiple jobs
+ may be detrimental to performance if your hardware does not
+ support parallel reads or if the number of jobs greatly exceeds
+ the amount of supported reads.
+
submodule.alternateLocation::
Specifies how the submodules obtain alternates when submodules are
cloned. Possible values are `no`, `superproject`.
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 08ab86189a..0b5d832ed6 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -35,71 +35,94 @@ endif::git-diff[]
-s::
--no-patch::
- Suppress diff output. Useful for commands like `git show` that
- show the patch by default, or to cancel the effect of `--patch`.
+ Suppress all output from the diff machinery. Useful for
+ commands like `git show` that show the patch by default to
+ squelch their output, or to cancel the effect of options like
+ `--patch`, `--stat` earlier on the command line in an alias.
+
endif::git-format-patch[]
ifdef::git-log[]
---diff-merges=(off|none|on|first-parent|1|separate|m|combined|c|dense-combined|cc|remerge|r)::
+-m::
+ Show diffs for merge commits in the default format.
+ Shortcut for '--diff-merges=on,hide' unless
+ `log.diffMerges-m-imply-p` configuration is active, in which
+ case it's a shortcut for '--diff-merges=on -p'.
+
+-c::
+ Shortcut for '--diff-merges=combined -p'.
+
+--cc::
+ Shortcut for '--diff-merges=dense-combined -p'.
+
+--remerge-diff::
+ Shortcut for '--diff-merges=remerge -p'.
+
--no-diff-merges::
- Specify diff format to be used for merge commits. Default is
+ Synonym for '--diff-merges=off'.
+
+--diff-merges=(<format>|<flag>)[,(<format>|<flag>),...]::
+ Specify diff format and flags to be used for merge commits. Default is
{diff-merges-default} unless `--first-parent` is in use, in which case
`first-parent` is the default.
+
---diff-merges=(off|none):::
---no-diff-merges:::
+The last format specified has precedence, whereas flags are
+cumulative. Comma-separated list is handy to provide flag(s) along
+with format, e.g.: `--diff-merges=first-parent,hide` is handy form of
+`--diff-merges=first-parent --diff-merges=hide`.
++
+The following formats are supported:
++
+--
+off, none::
Disable output of diffs for merge commits. Useful to override
implied value.
+
---diff-merges=on:::
---diff-merges=m:::
--m:::
- This option makes diff output for merge commits to be shown in
- the default format. `-m` will produce the output only if `-p`
- is given as well. The default format could be changed using
+on, m::
+ Make diff output for merge commits to be shown in the default
+ format. The default format could be changed using
`log.diffMerges` configuration parameter, which default value
is `separate`.
+
---diff-merges=first-parent:::
---diff-merges=1:::
- This option makes merge commits show the full diff with
- respect to the first parent only.
+first-parent, 1::
+ Show full diff with respect to first parent. This is the same
+ format as `--patch` produces for non-merge commits.
+
---diff-merges=separate:::
- This makes merge commits show the full diff with respect to
- each of the parents. Separate log entry and diff is generated
- for each parent.
+separate::
+ Show full diff with respect to each of parents.
+ Separate log entry and diff is generated for each parent.
+
---diff-merges=remerge:::
---diff-merges=r:::
---remerge-diff:::
- With this option, two-parent merge commits are remerged to
- create a temporary tree object -- potentially containing files
- with conflict markers and such. A diff is then shown between
- that temporary tree and the actual merge commit.
+remerge, r::
+ Remerge two-parent merge commits to create a temporary tree
+ object--potentially containing files with conflict markers
+ and such. A diff is then shown between that temporary tree
+ and the actual merge commit.
+
The output emitted when this option is used is subject to change, and
so is its interaction with other options (unless explicitly
documented).
+
---diff-merges=combined:::
---diff-merges=c:::
--c:::
- With this option, diff output for a merge commit shows the
- differences from each of the parents to the merge result
- simultaneously instead of showing pairwise diff between a
- parent and the result one at a time. Furthermore, it lists
- only files which were modified from all parents. `-c` implies
- `-p`.
-+
---diff-merges=dense-combined:::
---diff-merges=cc:::
---cc:::
- With this option the output produced by
- `--diff-merges=combined` is further compressed by omitting
- uninteresting hunks whose contents in the parents have only
- two variants and the merge result picks one of them without
- modification. `--cc` implies `-p`.
+combined, c::
+ Show differences from each of the parents to the merge
+ result simultaneously instead of showing pairwise diff between
+ a parent and the result one at a time. Furthermore, it lists
+ only files which were modified from all parents.
++
+dense-combined, cc::
+ Further compress output produced by `--diff-merges=combined`
+ by omitting uninteresting hunks whose contents in the parents
+ have only two variants and the merge result picks one of them
+ without modification.
+--
++
+The following flags are supported:
++
+--
+[no-]hide::
+ Hide diff for merge commits unless `-p` options is given as
+ well. The default `no-hide` could be changed using
+ `log.diffMerges` configuration parameter.
+--
--combined-all-paths::
This flag causes combined diffs (used for merge commits) to
diff --git a/Documentation/doc-diff b/Documentation/doc-diff
index 1694300e50..fb09e0ac0e 100755
--- a/Documentation/doc-diff
+++ b/Documentation/doc-diff
@@ -153,7 +153,7 @@ render_tree () {
make -j$parallel -C "$tmp/worktree" \
$makemanflags \
GIT_VERSION=omitted \
- SOURCE_DATE_EPOCH=0 \
+ GIT_DATE=1970-01-01 \
DESTDIR="$tmp/installed/$dname+" \
install-man &&
mv "$tmp/installed/$dname+" "$tmp/installed/$dname"
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index 622bd84768..41fc7ca3c6 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -78,6 +78,13 @@ linkgit:git-config[1].
--dry-run::
Show what would be done, without making any changes.
+--porcelain::
+ Print the output to standard output in an easy-to-parse format for
+ scripts. See section OUTPUT in linkgit:git-fetch[1] for details.
++
+This is incompatible with `--recurse-submodules=[yes|on-demand]` and takes
+precedence over the `fetch.output` config option.
+
ifndef::git-pull[]
--[no-]write-fetch-head::
Write the list of remote refs fetched in the `FETCH_HEAD`
diff --git a/Documentation/git-credential.txt b/Documentation/git-credential.txt
index 3394c03611..0e6d9e85ec 100644
--- a/Documentation/git-credential.txt
+++ b/Documentation/git-credential.txt
@@ -156,6 +156,12 @@ Git understands the following attributes:
When reading credentials from helpers, `git credential fill` ignores expired
passwords. Represented as Unix time UTC, seconds since 1970.
+`oauth_refresh_token`::
+
+ An OAuth refresh token may accompany a password that is an OAuth access
+ token. Helpers must treat this attribute as confidential like the password
+ attribute. Git itself has no special behaviour for this attribute.
+
`url`::
When this special attribute is read by `git credential`, the
diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index 52b679256c..a85cc756eb 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -102,7 +102,11 @@ If --merge-base is given, use the merge base of the two commits for the
Just in case you are doing something exotic, it should be
noted that all of the <commit> in the above description, except
in the `--merge-base` case and in the last two forms that use `..`
-notations, can be any <tree>.
+notations, can be any <tree>. A tree of interest is the one pointed to
+by the special ref `AUTO_MERGE`, which is written by the 'ort' merge
+strategy upon hitting merge conflicts (see linkgit:git-merge[1]).
+Comparing the working tree with `AUTO_MERGE` shows changes you've made
+so far to resolve conflicts (see the examples below).
For a more complete list of ways to spell <commit>, see
"SPECIFYING REVISIONS" section in linkgit:gitrevisions[7].
@@ -152,6 +156,7 @@ Various ways to check your working tree::
$ git diff <1>
$ git diff --cached <2>
$ git diff HEAD <3>
+$ git diff AUTO_MERGE <4>
------------
+
<1> Changes in the working tree not yet staged for the next commit.
@@ -159,6 +164,7 @@ $ git diff HEAD <3>
would be committing if you run `git commit` without `-a` option.
<3> Changes in the working tree since your last commit; what you
would be committing if you run `git commit -a`
+<4> Changes in the working tree you've made to resolve conflicts so far.
Comparing with arbitrary commits::
+
diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt
index fba66f1460..f123139c58 100644
--- a/Documentation/git-fetch.txt
+++ b/Documentation/git-fetch.txt
@@ -204,6 +204,15 @@ representing the status of a single ref. Each line is of the form:
<flag> <summary> <from> -> <to> [<reason>]
-------------------------------
+When using `--porcelain`, the output format is intended to be
+machine-parseable. In contrast to the human-readable output formats it
+thus prints to standard output instead of standard error. Each line is
+of the form:
+
+-------------------------------
+<flag> <old-object-id> <new-object-id> <local-reference>
+-------------------------------
+
The status of up-to-date refs is shown only if the --verbose option is
used.
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 1e215d4e73..5743eb5def 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -14,6 +14,7 @@ SYNOPSIS
[--points-at=<object>]
[--merged[=<object>]] [--no-merged[=<object>]]
[--contains[=<object>]] [--no-contains[=<object>]]
+ [--exclude=<pattern> ...]
DESCRIPTION
-----------
@@ -102,6 +103,11 @@ OPTIONS
Do not print a newline after formatted refs where the format expands
to the empty string.
+--exclude=<pattern>::
+ If one or more patterns are given, only refs which do not match
+ any excluded pattern(s) are shown. Matching is done using the
+ same rules as `<pattern>` above.
+
FIELD NAMES
-----------
diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
index f7b1851514..ddbbe81931 100644
--- a/Documentation/git-imap-send.txt
+++ b/Documentation/git-imap-send.txt
@@ -37,17 +37,6 @@ OPTIONS
--quiet::
Be quiet.
---curl::
- Use libcurl to communicate with the IMAP server, unless tunneling
- into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
- option set.
-
---no-curl::
- Talk to the IMAP server using git's own IMAP routines instead of
- using libcurl. Ignored if Git was built with the NO_OPENSSL option
- set.
-
-
CONFIGURATION
-------------
diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt
index 22ff3a603e..a45eb0a4bb 100644
--- a/Documentation/git-interpret-trailers.txt
+++ b/Documentation/git-interpret-trailers.txt
@@ -14,21 +14,37 @@ SYNOPSIS
DESCRIPTION
-----------
-Help parsing or adding 'trailers' lines, that look similar to RFC 822 e-mail
+Add or parse 'trailer' lines that look similar to RFC 822 e-mail
headers, at the end of the otherwise free-form part of a commit
-message.
+message. For example, in the following commit message
-This command reads some patches or commit messages from either the
-<file> arguments or the standard input if no <file> is specified. If
-`--parse` is specified, the output consists of the parsed trailers.
+------------------------------------------------
+subject
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+
+Signed-off-by: Alice <alice@example.com>
+Signed-off-by: Bob <bob@example.com>
+------------------------------------------------
+the last two lines starting with "Signed-off-by" are trailers.
+
+This command reads commit messages from either the
+<file> arguments or the standard input if no <file> is specified.
+If `--parse` is specified, the output consists of the parsed trailers.
Otherwise, this command applies the arguments passed using the
-`--trailer` option, if any, to the commit message part of each input
-file. The result is emitted on the standard output.
+`--trailer` option, if any, to each input file. The result is emitted on the
+standard output.
+
+This command can also operate on the output of linkgit:git-format-patch[1],
+which is more elaborate than a plain commit message. Namely, such output
+includes a commit message (as above), a "---" divider line, and a patch part.
+For these inputs, the divider and patch parts are ignored, unless `--no-divider`
+is specified.
Some configuration variables control the way the `--trailer` arguments
-are applied to each commit message and the way any existing trailer in
-the commit message is changed. They also make it possible to
+are applied to each input and the way any existing trailer in
+the input is changed. They also make it possible to
automatically add some trailers.
By default, a '<token>=<value>' or '<token>:<value>' argument given
@@ -36,41 +52,45 @@ using `--trailer` will be appended after the existing trailers only if
the last trailer has a different (<token>, <value>) pair (or if there
is no existing trailer). The <token> and <value> parts will be trimmed
to remove starting and trailing whitespace, and the resulting trimmed
-<token> and <value> will appear in the message like this:
+<token> and <value> will appear in the output like this:
------------------------------------------------
token: value
------------------------------------------------
This means that the trimmed <token> and <value> will be separated by
-`': '` (one colon followed by one space).
+`': '` (one colon followed by one space). If the <token> should have a different
+string representation than itself, then the 'key' can be configured with
+'trailer.<token>.key'.
By default the new trailer will appear at the end of all the existing
trailers. If there is no existing trailer, the new trailer will appear
-after the commit message part of the output, and, if there is no line
-with only spaces at the end of the commit message part, one blank line
-will be added before the new trailer.
+at the end of the input. A blank line will be added before the new
+trailer if there isn't one already.
-Existing trailers are extracted from the input message by looking for
+Existing trailers are extracted from the input by looking for
a group of one or more lines that (i) is all trailers, or (ii) contains at
least one Git-generated or user-configured trailer and consists of at
least 25% trailers.
The group must be preceded by one or more empty (or whitespace-only) lines.
-The group must either be at the end of the message or be the last
+The group must either be at the end of the input or be the last
non-whitespace lines before a line that starts with '---' (followed by a
-space or the end of the line). Such three minus signs start the patch
-part of the message. See also `--no-divider` below.
+space or the end of the line).
When reading trailers, there can be no whitespace before or inside the
-token, but any number of regular space and tab characters are allowed
-between the token and the separator. There can be whitespaces before,
-inside or after the value. The value may be split over multiple lines
+<token>, but any number of regular space and tab characters are allowed
+between the <token> and the separator. There can be whitespaces before,
+inside or after the <value>. The <value> may be split over multiple lines
with each subsequent line starting with at least one whitespace, like
-the "folding" in RFC 822.
+the "folding" in RFC 822. Example:
+
+------------------------------------------------
+token: This is a very long value, with spaces and
+ newlines in it.
+------------------------------------------------
-Note that 'trailers' do not follow and are not intended to follow many
-rules for RFC 822 headers. For example they do not follow
-the encoding rules and probably many other rules.
+Note that trailers do not follow (nor are they intended to follow) many of the
+rules for RFC 822 headers. For example they do not follow the encoding rule.
OPTIONS
-------
@@ -79,12 +99,12 @@ OPTIONS
--trim-empty::
If the <value> part of any trailer contains only whitespace,
- the whole trailer will be removed from the resulting message.
+ the whole trailer will be removed from the output.
This applies to existing trailers as well as new trailers.
--trailer <token>[(=|:)<value>]::
Specify a (<token>, <value>) pair that should be applied as a
- trailer to the input messages. See the description of this
+ trailer to the inputs. See the description of this
command.
--where <placement>::
@@ -98,7 +118,7 @@ OPTIONS
--if-exists <action>::
--no-if-exists::
Specify what action will be performed when there is already at
- least one trailer with the same <token> in the message. A setting
+ least one trailer with the same <token> in the input. A setting
provided with '--if-exists' overrides all configuration variables
and applies to all '--trailer' options until the next occurrence of
'--if-exists' or '--no-if-exists'. Possible actions are `addIfDifferent`,
@@ -107,7 +127,7 @@ OPTIONS
--if-missing <action>::
--no-if-missing::
Specify what action will be performed when there is no other
- trailer with the same <token> in the message. A setting
+ trailer with the same <token> in the input. A setting
provided with '--if-missing' overrides all configuration variables
and applies to all '--trailer' options until the next occurrence of
'--if-missing' or '--no-if-missing'. Possible actions are `doNothing`
@@ -174,7 +194,7 @@ first trailer with the same <token>.
trailer.ifexists::
This option makes it possible to choose what action will be
performed when there is already at least one trailer with the
- same <token> in the message.
+ same <token> in the input.
+
The valid values for this option are: `addIfDifferentNeighbor` (this
is the default), `addIfDifferent`, `add`, `replace` or `doNothing`.
@@ -184,10 +204,10 @@ trailer with the same (<token>, <value>) pair is above or below the line
where the new trailer will be added.
+
With `addIfDifferent`, a new trailer will be added only if no trailer
-with the same (<token>, <value>) pair is already in the message.
+with the same (<token>, <value>) pair is already in the input.
+
With `add`, a new trailer will be added, even if some trailers with
-the same (<token>, <value>) pair are already in the message.
+the same (<token>, <value>) pair are already in the input.
+
With `replace`, an existing trailer with the same <token> will be
deleted and the new trailer will be added. The deleted trailer will be
@@ -195,12 +215,12 @@ the closest one (with the same <token>) to the place where the new one
will be added.
+
With `doNothing`, nothing will be done; that is no new trailer will be
-added if there is already one with the same <token> in the message.
+added if there is already one with the same <token> in the input.
trailer.ifmissing::
This option makes it possible to choose what action will be
performed when there is not yet any trailer with the same
- <token> in the message.
+ <token> in the input.
+
The valid values for this option are: `add` (this is the default) and
`doNothing`.
@@ -235,13 +255,13 @@ trailer.<token>.ifmissing::
that option for trailers with the specified <token>.
trailer.<token>.command::
+ Deprecated in favor of 'trailer.<token>.cmd'.
This option behaves in the same way as 'trailer.<token>.cmd', except
that it doesn't pass anything as argument to the specified command.
Instead the first occurrence of substring $ARG is replaced by the
- value that would be passed as argument.
+ <value> that would be passed as argument.
+
-The 'trailer.<token>.command' option has been deprecated in favor of
-'trailer.<token>.cmd' due to the fact that $ARG in the user's command is
+Note that $ARG in the user's command is
only replaced once and that the original way of replacing $ARG is not safe.
+
When both 'trailer.<token>.cmd' and 'trailer.<token>.command' are given
@@ -249,10 +269,10 @@ for the same <token>, 'trailer.<token>.cmd' is used and
'trailer.<token>.command' is ignored.
trailer.<token>.cmd::
- This option can be used to specify a shell command that will be called:
+ This option can be used to specify a shell command that will be called
once to automatically add a trailer with the specified <token>, and then
- each time a '--trailer <token>=<value>' argument to modify the <value> of
- the trailer that this option would produce.
+ called each time a '--trailer <token>=<value>' argument is specified to
+ modify the <value> of the trailer that this option would produce.
+
When the specified command is first called to add a trailer
with the specified <token>, the behavior is as if a special
@@ -272,37 +292,37 @@ EXAMPLES
--------
* Configure a 'sign' trailer with a 'Signed-off-by' key, and then
- add two of these trailers to a message:
+ add two of these trailers to a commit message file:
+
------------
$ git config trailer.sign.key "Signed-off-by"
$ cat msg.txt
subject
-message
-$ cat msg.txt | git interpret-trailers --trailer 'sign: Alice <alice@example.com>' --trailer 'sign: Bob <bob@example.com>'
+Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+$ git interpret-trailers --trailer 'sign: Alice <alice@example.com>' --trailer 'sign: Bob <bob@example.com>' <msg.txt
subject
-message
+Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Signed-off-by: Alice <alice@example.com>
Signed-off-by: Bob <bob@example.com>
------------
-* Use the `--in-place` option to edit a message file in place:
+* Use the `--in-place` option to edit a commit message file in place:
+
------------
$ cat msg.txt
subject
-message
+Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Signed-off-by: Bob <bob@example.com>
$ git interpret-trailers --trailer 'Acked-by: Alice <alice@example.com>' --in-place msg.txt
$ cat msg.txt
subject
-message
+Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Signed-off-by: Bob <bob@example.com>
Acked-by: Alice <alice@example.com>
@@ -322,17 +342,30 @@ $ git interpret-trailers --trailer 'Cc: Alice <alice@example.com>' --trailer 'Re
'Signed-off-by: ' already, and show how it works:
+
------------
+$ cat msg1.txt
+subject
+
+message
$ git config trailer.sign.key "Signed-off-by: "
$ git config trailer.sign.ifmissing add
$ git config trailer.sign.ifexists doNothing
-$ git config trailer.sign.command 'echo "$(git config user.name) <$(git config user.email)>"'
-$ git interpret-trailers <<EOF
-> EOF
+$ git config trailer.sign.cmd 'echo "$(git config user.name) <$(git config user.email)>"'
+$ git interpret-trailers --trailer sign <msg1.txt
+subject
+
+message
Signed-off-by: Bob <bob@example.com>
-$ git interpret-trailers <<EOF
-> Signed-off-by: Alice <alice@example.com>
-> EOF
+$ cat msg2.txt
+subject
+
+message
+
+Signed-off-by: Alice <alice@example.com>
+$ git interpret-trailers --trailer sign <msg2.txt
+subject
+
+message
Signed-off-by: Alice <alice@example.com>
------------
@@ -357,18 +390,18 @@ Fix #42
$ cat ~/bin/glog-find-author
#!/bin/sh
test -n "$1" && git log --author="$1" --pretty="%an <%ae>" -1 || true
+$ cat msg.txt
+subject
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+
$ git config trailer.help.key "Helped-by: "
$ git config trailer.help.ifExists "addIfDifferentNeighbor"
$ git config trailer.help.cmd "~/bin/glog-find-author"
-$ git interpret-trailers --trailer="help:Junio" --trailer="help:Couder" <<EOF
-> subject
->
-> message
->
-> EOF
+$ git interpret-trailers --trailer="help:Junio" --trailer="help:Couder" <msg.txt
subject
-message
+Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Christian Couder <christian.couder@gmail.com>
@@ -382,18 +415,17 @@ Helped-by: Christian Couder <christian.couder@gmail.com>
$ cat ~/bin/glog-grep
#!/bin/sh
test -n "$1" && git log --grep "$1" --pretty=reference -1 || true
+$ cat msg.txt
+subject
+
+message
$ git config trailer.ref.key "Reference-to: "
$ git config trailer.ref.ifExists "replace"
$ git config trailer.ref.cmd "~/bin/glog-grep"
-$ git interpret-trailers --trailer="ref:Add copyright notices." <<EOF
-> subject
->
-> message
->
-> EOF
+$ git interpret-trailers --trailer="ref:Add copyright notices." <msg.txt
subject
-message
+Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Reference-to: 8bc9a0c769 (Add copyright notices., 2005-04-07)
------------
@@ -402,20 +434,23 @@ Reference-to: 8bc9a0c769 (Add copyright notices., 2005-04-07)
commit that is related, and show how it works:
+
------------
+$ cat msg.txt
+subject
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+
+see: HEAD~2
+$ cat ~/bin/glog-ref
+#!/bin/sh
+git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14
$ git config trailer.see.key "See-also: "
$ git config trailer.see.ifExists "replace"
$ git config trailer.see.ifMissing "doNothing"
-$ git config trailer.see.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG"
-$ git interpret-trailers <<EOF
-> subject
->
-> message
->
-> see: HEAD~2
-> EOF
+$ git config trailer.see.cmd "glog-ref"
+$ git interpret-trailers --trailer=see <msg.txt
subject
-message
+Lorem ipsum dolor sit amet, consectetur adipiscing elit.
See-also: fe3187489d69c4 (subject of related commit)
------------
@@ -427,22 +462,21 @@ See-also: fe3187489d69c4 (subject of related commit)
to add a 'git-version' trailer:
+
------------
-$ sed -e 's/ Z$/ /' >commit_template.txt <<EOF
-> ***subject***
->
-> ***message***
->
-> Fixes: Z
-> Cc: Z
-> Reviewed-by: Z
-> Signed-off-by: Z
-> EOF
+$ cat temp.txt
+***subject***
+
+***message***
+
+Fixes: Z
+Cc: Z
+Reviewed-by: Z
+Signed-off-by: Z
+$ sed -e 's/ Z$/ /' temp.txt > commit_template.txt
$ git config commit.template commit_template.txt
-$ cat >.git/hooks/commit-msg <<EOF
-> #!/bin/sh
-> git interpret-trailers --trim-empty --trailer "git-version: \$(git describe)" "\$1" > "\$1.new"
-> mv "\$1.new" "\$1"
-> EOF
+$ cat .git/hooks/commit-msg
+#!/bin/sh
+git interpret-trailers --trim-empty --trailer "git-version: \$(git describe)" "\$1" > "\$1.new"
+mv "\$1.new" "\$1"
$ chmod +x .git/hooks/commit-msg
------------
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 1abdd3c21c..1bc0328bb7 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -270,8 +270,14 @@ interpolated. The following "fieldname" are understood:
objectmode::
The mode of the file which is recorded in the index.
+objecttype::
+ The object type of the file which is recorded in the index.
objectname::
The name of the file which is recorded in the index.
+objectsize[:padded]::
+ The object size of the file which is recorded in the index
+ ("-" if the object is a `commit` or `tree`).
+ It also supports a padded format of size with "%(objectsize:padded)".
stage::
The stage of the file which is recorded in the index.
eolinfo:index::
diff --git a/Documentation/git-ls-remote.txt b/Documentation/git-ls-remote.txt
index ff3da547dd..15313f2b10 100644
--- a/Documentation/git-ls-remote.txt
+++ b/Documentation/git-ls-remote.txt
@@ -96,27 +96,49 @@ OPTIONS
separator (so `bar` matches `refs/heads/bar` but not
`refs/heads/foobar`).
+OUTPUT
+------
+
+The output is in the format:
+
+------------
+<oid> TAB <ref> LF
+------------
+
+When `<ref>` is a tag, it may be followed by `^{}` to show its peeled
+representation.
+
EXAMPLES
--------
+* List all references (including symbolics and pseudorefs), peeling
+ tags:
++
+----
+$ git ls-remote
+27d43aaaf50ef0ae014b88bba294f93658016a2e HEAD
+950264636c68591989456e3ba0a5442f93152c1a refs/heads/main
+d9ab777d41f92a8c1684c91cfb02053d7dd1046b refs/heads/next
+d4ca2e3147b409459955613c152220f4db848ee1 refs/tags/v2.40.0
+73876f4861cd3d187a4682290ab75c9dccadbc56 refs/tags/v2.40.0^{}
----
-$ git ls-remote --tags .
-d6602ec5194c87b0fc87103ca4d67251c76f233a refs/tags/v0.99
-f25a265a342aed6041ab0cc484224d9ca54b6f41 refs/tags/v0.99.1
-7ceca275d047c90c0c7d5afb13ab97efdf51bd6e refs/tags/v0.99.3
-c5db5456ae3b0873fc659c19fafdde22313cc441 refs/tags/v0.99.2
-0918385dbd9656cab0d1d81ba7453d49bbc16250 refs/tags/junio-gpg-pub
+* List all references matching given patterns:
++
+----
$ git ls-remote http://www.kernel.org/pub/scm/git/git.git master seen rc
5fe978a5381f1fbad26a80e682ddd2a401966740 refs/heads/master
c781a84b5204fb294c9ccc79f8b3baceeb32c061 refs/heads/seen
+----
-$ git remote add korg http://www.kernel.org/pub/scm/git/git.git
-$ git ls-remote --tags korg v\*
-d6602ec5194c87b0fc87103ca4d67251c76f233a refs/tags/v0.99
-f25a265a342aed6041ab0cc484224d9ca54b6f41 refs/tags/v0.99.1
-c5db5456ae3b0873fc659c19fafdde22313cc441 refs/tags/v0.99.2
-7ceca275d047c90c0c7d5afb13ab97efdf51bd6e refs/tags/v0.99.3
+* List only tags matching a given wildcard pattern:
++
+----
+$ git ls-remote --tags http://www.kernel.org/pub/scm/git/git.git v\*
+485a869c64a68cc5795dd99689797c5900f4716d refs/tags/v2.39.2
+cbf04937d5b9fcf0a76c28f69e6294e9e3ecd7e6 refs/tags/v2.39.2^{}
+d4ca2e3147b409459955613c152220f4db848ee1 refs/tags/v2.40.0
+73876f4861cd3d187a4682290ab75c9dccadbc56 refs/tags/v2.40.0^{}
----
SEE ALSO
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 0aeff572a5..3f61389c1c 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -194,9 +194,13 @@ happens:
versions: stage 1 stores the version from the common ancestor,
stage 2 from `HEAD`, and stage 3 from `MERGE_HEAD` (you
can inspect the stages with `git ls-files -u`). The working
- tree files contain the result of the "merge" program; i.e. 3-way
+ tree files contain the result of the merge operation; i.e. 3-way
merge results with familiar conflict markers `<<<` `===` `>>>`.
-5. No other changes are made. In particular, the local
+5. A special ref `AUTO_MERGE` is written, pointing to a tree corresponding
+ to the current content of the working tree (including conflict markers).
+ Note that this ref is only written when the 'ort' merge strategy
+ is used (the default).
+6. No other changes are made. In particular, the local
modifications you had before you started merge will stay the
same and the index entries for them stay as they were,
i.e. matching `HEAD`.
@@ -336,7 +340,8 @@ You can work through the conflict with a number of tools:
* Look at the diffs. `git diff` will show a three-way diff,
highlighting changes from both the `HEAD` and `MERGE_HEAD`
- versions.
+ versions. `git diff AUTO_MERGE` will show what changes you've
+ made so far to resolve conlicts.
* Look at the diffs from each branch. `git log --merge -p <path>`
will show diffs first for the `HEAD` version and then the
diff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt
index ec8a27ce8b..5c56c87025 100644
--- a/Documentation/git-name-rev.txt
+++ b/Documentation/git-name-rev.txt
@@ -10,7 +10,7 @@ SYNOPSIS
--------
[verse]
'git name-rev' [--tags] [--refs=<pattern>]
- ( --all | --stdin | <commit-ish>... )
+ ( --all | --annotate-stdin | <commit-ish>... )
DESCRIPTION
-----------
@@ -46,7 +46,8 @@ OPTIONS
Transform stdin by substituting all the 40-character SHA-1
hexes (say $hex) with "$hex ($rev_name)". When used with
--name-only, substitute with "$rev_name", omitting $hex
- altogether.
+ altogether. This option was called `--stdin` in older versions
+ of Git.
+
For example:
+
@@ -70,10 +71,6 @@ The full name after substitution is master,
while its tree object is 70d105cc79e63b81cfdcb08a15297c23e60b07ad
-----------
---stdin::
- This option is deprecated in favor of 'git name-rev --annotate-stdin'.
- They are functionally equivalent.
-
--name-only::
Instead of printing both the SHA-1 and the name, print only
the name. If given with --tags the usual tag prefix of
@@ -107,7 +104,7 @@ Now you are wiser, because you know that it happened 940 revisions before v0.99.
Another nice thing you can do is:
------------
-% git log | git name-rev --stdin
+% git log | git name-rev --annotate-stdin
------------
GIT
diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
index efbc10f0f5..5f3a9479a9 100644
--- a/Documentation/git-notes.txt
+++ b/Documentation/git-notes.txt
@@ -9,10 +9,10 @@ SYNOPSIS
--------
[verse]
'git notes' [list [<object>]]
-'git notes' add [-f] [--allow-empty] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
+'git notes' add [-f] [--allow-empty] [--separator=<paragraph-break>] [--[no-]stripspace] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
'git notes' copy [-f] ( --stdin | <from-object> [<to-object>] )
-'git notes' append [--allow-empty] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
-'git notes' edit [--allow-empty] [<object>]
+'git notes' append [--allow-empty] [--separator=<paragraph-break>] [--[no-]stripspace] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
+'git notes' edit [--allow-empty] [<object>] [--[no-]stripspace]
'git notes' show [<object>]
'git notes' merge [-v | -q] [-s <strategy> ] <notes-ref>
'git notes' merge --commit [-v | -q]
@@ -65,7 +65,9 @@ add::
However, if you're using `add` interactively (using an editor
to supply the notes contents), then - instead of aborting -
the existing notes will be opened in the editor (like the `edit`
- subcommand).
+ subcommand). If you specify multiple `-m` and `-F`, a blank
+ line will be inserted between the messages. Use the `--separator`
+ option to insert other delimiters.
copy::
Copy the notes for the first object onto the second object (defaults to
@@ -85,8 +87,12 @@ corresponding <to-object>. (The optional `<rest>` is ignored so that
the command can read the input given to the `post-rewrite` hook.)
append::
- Append to the notes of an existing object (defaults to HEAD).
- Creates a new notes object if needed.
+ Append new message(s) given by `-m` or `-F` options to an
+ existing note, or add them as a new note if one does not
+ exist, for the object (defaults to HEAD). When appending to
+ an existing note, a blank line is added before each new
+ message as an inter-paragraph separator. The separator can
+ be customized with the `--separator` option.
edit::
Edit the notes for a given object (defaults to HEAD).
@@ -135,20 +141,26 @@ OPTIONS
If multiple `-m` options are given, their values
are concatenated as separate paragraphs.
Lines starting with `#` and empty lines other than a
- single line between paragraphs will be stripped out.
+ single line between paragraphs will be stripped out,
+ if you wish to keep them verbatim, use `--no-stripspace`.
-F <file>::
--file=<file>::
Take the note message from the given file. Use '-' to
read the note message from the standard input.
Lines starting with `#` and empty lines other than a
- single line between paragraphs will be stripped out.
+ single line between paragraphs will be stripped out,
+ if you wish to keep them verbatim, use with
+ `--no-stripspace` option.
-C <object>::
--reuse-message=<object>::
Take the given blob object (for example, another note) as the
note message. (Use `git notes copy <object>` instead to
- copy notes between objects.)
+ copy notes between objects.). By default, message will be
+ copied verbatim, but if you wish to strip out the lines
+ starting with `#` and empty lines other than a single line
+ between paragraphs, use with`--stripspace` option.
-c <object>::
--reedit-message=<object>::
@@ -159,6 +171,18 @@ OPTIONS
Allow an empty note object to be stored. The default behavior is
to automatically remove empty notes.
+--separator <paragraph-break>::
+ Specify a string used as a custom inter-paragraph separator
+ (a newline is added at the end as needed). Defaults to a
+ blank line.
+
+--[no-]stripspace::
+ Strip leading and trailing whitespace from the note message.
+ Also strip out empty lines other than a single line between
+ paragraphs. For lines starting with `#` will be stripped out
+ in non-editor cases like "-m", "-F" and "-C", but not in
+ editor case like "git notes edit", "-c", etc.
+
--ref <ref>::
Manipulate the notes tree in <ref>. This overrides
`GIT_NOTES_REF` and the "core.notesRef" configuration. The ref
diff --git a/Documentation/git-pack-refs.txt b/Documentation/git-pack-refs.txt
index 154081f2de..284956acb3 100644
--- a/Documentation/git-pack-refs.txt
+++ b/Documentation/git-pack-refs.txt
@@ -8,7 +8,7 @@ git-pack-refs - Pack heads and tags for efficient repository access
SYNOPSIS
--------
[verse]
-'git pack-refs' [--all] [--no-prune]
+'git pack-refs' [--all] [--no-prune] [--include <pattern>] [--exclude <pattern>]
DESCRIPTION
-----------
@@ -51,14 +51,37 @@ The command by default packs all tags and refs that are already
packed, and leaves other refs
alone. This is because branches are expected to be actively
developed and packing their tips does not help performance.
-This option causes branch tips to be packed as well. Useful for
-a repository with many branches of historical interests.
+This option causes all refs to be packed as well, with the exception
+of hidden refs, broken refs, and symbolic refs. Useful for a repository
+with many branches of historical interests.
--no-prune::
The command usually removes loose refs under `$GIT_DIR/refs`
hierarchy after packing them. This option tells it not to.
+--include <pattern>::
+
+Pack refs based on a `glob(7)` pattern. Repetitions of this option
+accumulate inclusion patterns. If a ref is both included in `--include` and
+`--exclude`, `--exclude` takes precedence. Using `--include` will preclude all
+tags from being included by default. Symbolic refs and broken refs will never
+be packed. When used with `--all`, it will be a noop. Use `--no-include` to clear
+and reset the list of patterns.
+
+--exclude <pattern>::
+
+Do not pack refs matching the given `glob(7)` pattern. Repetitions of this option
+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.
+
BUGS
----
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index 5bb1d5aae2..297927d866 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -9,7 +9,7 @@ git-push - Update remote refs along with associated objects
SYNOPSIS
--------
[verse]
-'git push' [--all | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
+'git push' [--all | --branches | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
[--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-v | --verbose]
[-u | --set-upstream] [-o <string> | --push-option=<string>]
[--[no-]signed|--signed=(true|false|if-asked)]
@@ -147,6 +147,7 @@ already exists on the remote side.
`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
--all::
+--branches::
Push all branches (i.e. refs under `refs/heads/`); cannot be
used with other <refspec>.
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index d2e10d3dce..e8fa513607 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -31,6 +31,12 @@ both will discard uncommitted changes in your working directory.
See "Reset, restore and revert" in linkgit:git[1] for the differences
between the three commands.
+The command generates the subject 'Revert "<title>"' for the resulting
+commit, assuming the original commit's subject is '<title>'. Reverting
+such a reversion commit in turn yields the subject 'Reapply "<title>"'.
+These can of course be modified in the editor when the reason for
+reverting is described.
+
OPTIONS
-------
<commit>...::
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index b0f438ec99..492a82323d 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -320,6 +320,17 @@ Automating
Output of this command must be single email address per line.
Default is the value of `sendemail.ccCmd` configuration value.
+--header-cmd=<command>::
+ Specify a command that is executed once per outgoing message
+ and output RFC 2822 style header lines to be inserted into
+ them. When the `sendemail.headerCmd` configuration variable is
+ set, its value is always used. When --header-cmd is provided
+ at the command line, its value takes precedence over the
+ `sendemail.headerCmd` configuration variable.
+
+--no-header-cmd::
+ Disable any header command in use.
+
--[no-]chain-reply-to::
If this is set, each email will be sent as a reply to the previous
email sent. If disabled with "--no-chain-reply-to", all emails after
@@ -484,14 +495,10 @@ edit ~/.gitconfig to specify your account settings:
smtpServerPort = 587
----
-If you have multi-factor authentication set up on your Gmail account, you will
-need to generate an app-specific password for use with 'git send-email'. Visit
+If you have multi-factor authentication set up on your Gmail account, you can
+generate an app-specific password for use with 'git send-email'. Visit
https://security.google.com/settings/security/apppasswords to create it.
-If you do not have multi-factor authentication set up on your Gmail account,
-you will need to allow less secure app access. Visit
-https://myaccount.google.com/lesssecureapps to enable it.
-
Once your commits are ready to be sent to the mailing list, run the
following commands:
diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt
index 71f608b1ff..58cf6210cd 100644
--- a/Documentation/git-show-branch.txt
+++ b/Documentation/git-show-branch.txt
@@ -74,8 +74,7 @@ OPTIONS
that is the common ancestor of all the branches. This
flag tells the command to go <n> more common commits
beyond that. When <n> is negative, display only the
- <reference>s given, without showing the commit ancestry
- tree.
+ <ref>s given, without showing the commit ancestry tree.
--list::
Synonym to `--more=-1`
@@ -88,8 +87,8 @@ OPTIONS
the case of three or more commits.
--independent::
- Among the <reference>s given, display only the ones that
- cannot be reached from any other <reference>.
+ Among the <ref>s given, display only the ones that cannot be
+ reached from any other <ref>.
--no-name::
Do not show naming strings for each commit.
@@ -132,10 +131,11 @@ are mutually exclusive.
OUTPUT
------
-Given N <references>, the first N lines are the one-line
-description from their commit message. The branch head that is
-pointed at by $GIT_DIR/HEAD is prefixed with an asterisk `*`
-character while other heads are prefixed with a `!` character.
+
+Given N <ref>s, the first N lines are the one-line description from
+their commit message. The branch head that is pointed at by
+$GIT_DIR/HEAD is prefixed with an asterisk `*` character while other
+heads are prefixed with a `!` character.
Following these N lines, one-line log for each commit is
displayed, indented N places. If a commit is on the I-th
diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt
index d1d56f68b4..be048bf181 100644
--- a/Documentation/git-show-ref.txt
+++ b/Documentation/git-show-ref.txt
@@ -23,7 +23,7 @@ particular ref exists.
By default, shows the tags, heads, and remote refs.
-The --exclude-existing form is a filter that does the inverse. It reads
+The `--exclude-existing` form is a filter that does the inverse. It reads
refs from stdin, one ref per line, and shows those that don't exist in
the local repository.
@@ -47,14 +47,14 @@ OPTIONS
-d::
--dereference::
- Dereference tags into object IDs as well. They will be shown with "{caret}{}"
+ Dereference tags into object IDs as well. They will be shown with `{caret}{}`
appended.
-s::
--hash[=<n>]::
- Only show the SHA-1 hash, not the reference name. When combined with
- --dereference the dereferenced tag will still be shown after the SHA-1.
+ Only show the OID, not the reference name. When combined with
+ `--dereference`, the dereferenced tag will still be shown after the OID.
--verify::
@@ -70,15 +70,15 @@ OPTIONS
-q::
--quiet::
- Do not print any results to stdout. When combined with `--verify` this
+ Do not print any results to stdout. When combined with `--verify`, this
can be used to silently check if a reference exists.
--exclude-existing[=<pattern>]::
- Make 'git show-ref' act as a filter that reads refs from stdin of the
- form "`^(?:<anything>\s)?<refname>(?:\^{})?$`"
+ Make `git show-ref` act as a filter that reads refs from stdin of the
+ form `^(?:<anything>\s)?<refname>(?:\^{})?$`
and performs the following actions on each:
- (1) strip "{caret}{}" at the end of line if any;
+ (1) strip `{caret}{}` at the end of line if any;
(2) ignore if pattern is provided and does not head-match refname;
(3) warn if refname is not a well-formed refname and skip;
(4) ignore if refname is a ref that exists in the local repository;
@@ -96,7 +96,13 @@ OPTIONS
OUTPUT
------
-The output is in the format: '<SHA-1 ID>' '<space>' '<reference name>'.
+The output is in the format:
+
+------------
+<oid> SP <ref> LF
+------------
+
+For example,
-----------------------------------------------------------------------------
$ git show-ref --head --dereference
@@ -110,7 +116,13 @@ $ git show-ref --head --dereference
...
-----------------------------------------------------------------------------
-When using --hash (and not --dereference) the output format is: '<SHA-1 ID>'
+When using `--hash` (and not `--dereference`), the output is in the format:
+
+------------
+<OID> LF
+------------
+
+For example,
-----------------------------------------------------------------------------
$ git show-ref --heads --hash
@@ -142,10 +154,10 @@ When using the `--verify` flag, the command requires an exact path:
will only match the exact branch called "master".
-If nothing matches, 'git show-ref' will return an error code of 1,
+If nothing matches, `git show-ref` will return an error code of 1,
and in the case of verification, it will show an error message.
-For scripting, you can ask it to be quiet with the "--quiet" flag, which
+For scripting, you can ask it to be quiet with the `--quiet` flag, which
allows you to do things like
-----------------------------------------------------------------------------
@@ -157,11 +169,11 @@ to check whether a particular branch exists or not (notice how we don't
actually want to show any results, and we want to use the full refname for it
in order to not trigger the problem with ambiguous partial matches).
-To show only tags, or only proper branch heads, use "--tags" and/or "--heads"
+To show only tags, or only proper branch heads, use `--tags` and/or `--heads`
respectively (using both means that it shows tags and heads, but not other
random references under the refs/ subdirectory).
-To do automatic tag object dereferencing, use the "-d" or "--dereference"
+To do automatic tag object dereferencing, use the `-d` or `--dereference`
flag, so you can do
-----------------------------------------------------------------------------
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 7f61c1edb3..d42efb3112 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -381,6 +381,16 @@ $ GIT_COMMITTER_DATE="2006-10-02 10:31" git tag -s v1.0.1
include::date-formats.txt[]
+FILES
+-----
+
+`$GIT_DIR/TAG_EDITMSG`::
+ This file contains the message of an in-progress annotated
+ tag. If `git tag` exits due to an error before creating an
+ annotated tag then the tag message that has been provided by the
+ user in an editor session will be available in this file, but
+ may be overwritten by the next invocation of `git tag`.
+
NOTES
-----
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 063d6eeb99..a4fbf5e838 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -10,7 +10,7 @@ SYNOPSIS
--------
[verse]
'git worktree add' [-f] [--detach] [--checkout] [--lock [--reason <string>]]
- [-b <new-branch>] <path> [<commit-ish>]
+ [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]
'git worktree list' [-v | --porcelain [-z]]
'git worktree lock' [--reason <string>] <worktree>
'git worktree move' <worktree> <new-path>
@@ -95,6 +95,16 @@ exist, a new branch based on `HEAD` is automatically created as if
`-b <branch>` was given. If `<branch>` does exist, it will be checked out
in the new worktree, if it's not checked out anywhere else, otherwise the
command will refuse to create the worktree (unless `--force` is used).
++
+If `<commit-ish>` is omitted, neither `--detach`, or `--orphan` is
+used, and there are no valid local branches (or remote branches if
+`--guess-remote` is specified) then, as a convenience, the new worktree is
+associated with a new orphan branch named `<branch>` (after
+`$(basename <path>)` if neither `-b` or `-B` is used) as if `--orphan` was
+passed to the command. In the event the repository has a remote and
+`--guess-remote` is used, but no remote or local branches exist, then the
+command fails with a warning reminding the user to fetch from their remote
+first (or override by using `-f/--force`).
list::
@@ -222,6 +232,10 @@ This can also be set up as the default behaviour by using the
With `prune`, do not remove anything; just report what it would
remove.
+--orphan::
+ With `add`, make the new worktree and index empty, associating
+ the worktree with a new orphan/unborn branch named `<new-branch>`.
+
--porcelain::
With `list`, output in an easy-to-parse format for scripts.
This format will remain stable across Git versions and regardless of user
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 74973d3cc4..f0cafa2290 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -212,6 +212,11 @@ If you just want to run git as if it was started in `<path>` then use
nohelpers (exclude helper commands), alias and config
(retrieve command list from config variable completion.commands)
+--attr-source=<tree-ish>::
+ Read gitattributes from <tree-ish> instead of the worktree. See
+ linkgit:gitattributes[5]. This is equivalent to setting the
+ `GIT_ATTR_SOURCE` environment variable.
+
GIT COMMANDS
------------
@@ -546,9 +551,9 @@ double-quotes and respecting backslash escapes. E.g., the value
`GIT_DEFAULT_HASH`::
If this variable is set, the default hash algorithm for new
- repositories will be set to this value. This value is currently
- ignored when cloning; the setting of the remote repository
- is used instead. The default is "sha1". THIS VARIABLE IS
+ repositories will be set to this value. This value is
+ ignored when cloning and the setting of the remote repository
+ is always used. The default is "sha1". THIS VARIABLE IS
EXPERIMENTAL! See `--object-format` in linkgit:git-init[1].
Git Commits
@@ -686,6 +691,9 @@ for further details.
tells Git not to verify the SSL certificate when fetching or
pushing over HTTPS.
+`GIT_ATTR_SOURCE`::
+ Sets the treeish that gitattributes will be read from.
+
`GIT_ASKPASS`::
If this environment variable is set, then Git commands which need to
acquire passwords or passphrases (e.g. for HTTP or IMAP authentication)
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 39bfbca1ff..02a3ec83e4 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -120,20 +120,19 @@ repository upon 'git add' and 'git commit'.
`text`
^^^^^^
-This attribute enables and controls end-of-line normalization. When a
-text file is normalized, its line endings are converted to LF in the
-repository. To control what line ending style is used in the working
-directory, use the `eol` attribute for a single file and the
-`core.eol` configuration variable for all text files.
-Note that setting `core.autocrlf` to `true` or `input` overrides
-`core.eol` (see the definitions of those options in
-linkgit:git-config[1]).
+This attribute marks the path as a text file, which enables end-of-line
+conversion: When a matching file is added to the index, the file's line
+endings are normalized to LF in the index. Conversely, when the file is
+copied from the index to the working directory, its line endings may be
+converted from LF to CRLF depending on the `eol` attribute, the Git
+config, and the platform (see explanation of `eol` below).
Set::
Setting the `text` attribute on a path enables end-of-line
- normalization and marks the path as a text file. End-of-line
- conversion takes place without guessing the content type.
+ conversion on checkin and checkout as described above. Line endings
+ are normalized to LF in the index every time the file is checked in,
+ even if the file was previously added to Git with CRLF line endings.
Unset::
@@ -142,10 +141,11 @@ Unset::
Set to string value "auto"::
- When `text` is set to "auto", the path is marked for automatic
- end-of-line conversion. If Git decides that the content is
- text, its line endings are converted to LF on checkin.
- When the file has been committed with CRLF, no conversion is done.
+ When `text` is set to "auto", Git decides by itself whether the file
+ is text or binary. If it is text and the file was not already in
+ Git with CRLF endings, line endings are converted on checkin and
+ checkout as described above. Otherwise, no conversion is done on
+ checkin or checkout.
Unspecified::
@@ -159,26 +159,29 @@ unspecified.
`eol`
^^^^^
-This attribute sets a specific line-ending style to be used in the
-working directory. This attribute has effect only if the `text`
-attribute is set or unspecified, or if it is set to `auto`, the file is
-detected as text, and it is stored with LF endings in the index. Note
-that setting this attribute on paths which are in the index with CRLF
-line endings may make the paths to be considered dirty unless
-`text=auto` is set. Adding the path to the index again will normalize
-the line endings in the index.
+This attribute marks a path to use a specific line-ending style in the
+working tree when it is checked out. It has effect only if `text` or
+`text=auto` is set (see above), but specifying `eol` automatically sets
+`text` if `text` was left unspecified.
Set to string value "crlf"::
- This setting forces Git to normalize line endings for this
- file on checkin and convert them to CRLF when the file is
- checked out.
+ This setting converts the file's line endings in the working
+ directory to CRLF when the file is checked out.
Set to string value "lf"::
- This setting forces Git to normalize line endings to LF on
- checkin and prevents conversion to CRLF when the file is
- checked out.
+ This setting uses the same line endings in the working directory as
+ in the index when the file is checked out.
+
+Unspecified::
+
+ If the `eol` attribute is unspecified for a file, its line endings
+ in the working directory are determined by the `core.autocrlf` or
+ `core.eol` configuration variable (see the definitions of those
+ options in linkgit:git-config[1]). If `text` is set but neither of
+ those variables is, the default is `eol=crlf` on Windows and
+ `eol=lf` on all other platforms.
Backwards compatibility with `crlf` attribute
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index c8e55b2613..86f804720a 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -595,10 +595,29 @@ processed by rebase.
sendemail-validate
~~~~~~~~~~~~~~~~~~
-This hook is invoked by linkgit:git-send-email[1]. It takes a single parameter,
-the name of the file that holds the e-mail to be sent. Exiting with a
-non-zero status causes `git send-email` to abort before sending any
-e-mails.
+This hook is invoked by linkgit:git-send-email[1].
+
+It takes these command line arguments. They are,
+1. the name of the file which holds the contents of the email to be sent.
+2. The name of the file which holds the SMTP headers of the email.
+
+The SMTP headers are passed in the exact same way as they are passed to the
+user's Mail Transport Agent (MTA). In effect, the email given to the user's
+MTA, is the contents of $2 followed by the contents of $1.
+
+An example of a few common headers is shown below. Take notice of the
+capitalization and multi-line tab structure.
+
+ From: Example <from@example.com>
+ To: to@example.com
+ Cc: cc@example.com,
+ A <author@example.com>,
+ One <one@example.com>,
+ two@example.com
+ Subject: PATCH-STRING
+
+Exiting with a non-zero status causes `git send-email` to abort
+before sending any e-mails.
The following environment variables are set when executing the hook.
diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt
index f2738b10db..4c17f2356c 100644
--- a/Documentation/gitignore.txt
+++ b/Documentation/gitignore.txt
@@ -146,7 +146,9 @@ The purpose of gitignore files is to ensure that certain files
not tracked by Git remain untracked.
To stop tracking a file that is currently tracked, use
-'git rm --cached'.
+'git rm --cached' to remove the file from the index. The filename
+can then be added to the `.gitignore` file to stop the file from
+being reintroduced in later commits.
Git does not follow symbolic links when accessing a `.gitignore` file in
the working tree. This keeps behavior consistent when the file is
diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt
index 0e0b863105..c7cadd8aaf 100644
--- a/Documentation/gittutorial.txt
+++ b/Documentation/gittutorial.txt
@@ -49,7 +49,7 @@ $ git config --global user.email you@yourdomain.example.com
Importing a new project
-----------------------
-Assume you have a tarball project.tar.gz with your initial work. You
+Assume you have a tarball `project.tar.gz` with your initial work. You
can place it under Git revision control as follows.
------------------------------------------------
@@ -65,10 +65,10 @@ Initialized empty Git repository in .git/
------------------------------------------------
You've now initialized the working directory--you may notice a new
-directory created, named ".git".
+directory created, named `.git`.
Next, tell Git to take a snapshot of the contents of all files under the
-current directory (note the '.'), with 'git add':
+current directory (note the `.`), with `git add`:
------------------------------------------------
$ git add .
@@ -76,7 +76,7 @@ $ git add .
This snapshot is now stored in a temporary staging area which Git calls
the "index". You can permanently store the contents of the index in the
-repository with 'git commit':
+repository with `git commit`:
------------------------------------------------
$ git commit
@@ -95,21 +95,20 @@ $ git add file1 file2 file3
------------------------------------------------
You are now ready to commit. You can see what is about to be committed
-using 'git diff' with the --cached option:
+using `git diff` with the `--cached` option:
------------------------------------------------
$ git diff --cached
------------------------------------------------
-(Without --cached, 'git diff' will show you any changes that
+(Without `--cached`, `git diff` will show you any changes that
you've made but not yet added to the index.) You can also get a brief
-summary of the situation with 'git status':
+summary of the situation with `git status`:
------------------------------------------------
$ git status
On branch master
Changes to be committed:
-Your branch is up to date with 'origin/master'.
(use "git restore --staged <file>..." to unstage)
modified: file1
@@ -128,7 +127,7 @@ $ git commit
This will again prompt you for a message describing the change, and then
record a new version of the project.
-Alternatively, instead of running 'git add' beforehand, you can use
+Alternatively, instead of running `git add` beforehand, you can use
------------------------------------------------
$ git commit -a
@@ -151,7 +150,7 @@ Git tracks content not files
Many revision control systems provide an `add` command that tells the
system to start tracking changes to a new file. Git's `add` command
-does something simpler and more powerful: 'git add' is used both for new
+does something simpler and more powerful: `git add` is used both for new
and newly modified files, and in both cases it takes a snapshot of the
given files and stages that content in the index, ready for inclusion in
the next commit.
@@ -182,7 +181,7 @@ Managing branches
-----------------
A single Git repository can maintain multiple branches of
-development. To create a new branch named "experimental", use
+development. To create a new branch named `experimental`, use
------------------------------------------------
$ git branch experimental
@@ -201,8 +200,8 @@ you'll get a list of all existing branches:
* master
------------------------------------------------
-The "experimental" branch is the one you just created, and the
-"master" branch is a default branch that was created for you
+The `experimental` branch is the one you just created, and the
+`master` branch is a default branch that was created for you
automatically. The asterisk marks the branch you are currently on;
type
@@ -210,8 +209,8 @@ type
$ git switch experimental
------------------------------------------------
-to switch to the experimental branch. Now edit a file, commit the
-change, and switch back to the master branch:
+to switch to the `experimental` branch. Now edit a file, commit the
+change, and switch back to the `master` branch:
------------------------------------------------
(edit file)
@@ -220,9 +219,9 @@ $ git switch master
------------------------------------------------
Check that the change you made is no longer visible, since it was
-made on the experimental branch and you're back on the master branch.
+made on the `experimental` branch and you're back on the `master` branch.
-You can make a different change on the master branch:
+You can make a different change on the `master` branch:
------------------------------------------------
(edit file)
@@ -230,7 +229,7 @@ $ git commit -a
------------------------------------------------
at this point the two branches have diverged, with different changes
-made in each. To merge the changes made in experimental into master, run
+made in each. To merge the changes made in `experimental` into `master`, run
------------------------------------------------
$ git merge experimental
@@ -258,16 +257,16 @@ $ gitk
will show a nice graphical representation of the resulting history.
-At this point you could delete the experimental branch with
+At this point you could delete the `experimental` branch with
------------------------------------------------
$ git branch -d experimental
------------------------------------------------
-This command ensures that the changes in the experimental branch are
+This command ensures that the changes in the `experimental` branch are
already in the current branch.
-If you develop on a branch crazy-idea, then regret it, you can always
+If you develop on a branch `crazy-idea`, then regret it, you can always
delete the branch with
-------------------------------------
@@ -281,7 +280,7 @@ Using Git for collaboration
---------------------------
Suppose that Alice has started a new project with a Git repository in
-/home/alice/project, and that Bob, who has a home directory on the
+`/home/alice/project`, and that Bob, who has a home directory on the
same machine, wants to contribute.
Bob begins with:
@@ -290,7 +289,7 @@ Bob begins with:
bob$ git clone /home/alice/project myrepo
------------------------------------------------
-This creates a new directory "myrepo" containing a clone of Alice's
+This creates a new directory `myrepo` containing a clone of Alice's
repository. The clone is on an equal footing with the original
project, possessing its own copy of the original project's history.
@@ -303,31 +302,31 @@ bob$ git commit -a
------------------------------------------------
When he's ready, he tells Alice to pull changes from the repository
-at /home/bob/myrepo. She does this with:
+at `/home/bob/myrepo`. She does this with:
------------------------------------------------
alice$ cd /home/alice/project
alice$ git pull /home/bob/myrepo master
------------------------------------------------
-This merges the changes from Bob's "master" branch into Alice's
+This merges the changes from Bob's `master` branch into Alice's
current branch. If Alice has made her own changes in the meantime,
then she may need to manually fix any conflicts.
-The "pull" command thus performs two operations: it fetches changes
+The `pull` command thus performs two operations: it fetches changes
from a remote branch, then merges them into the current branch.
Note that in general, Alice would want her local changes committed before
-initiating this "pull". If Bob's work conflicts with what Alice did since
+initiating this `pull`. If Bob's work conflicts with what Alice did since
their histories forked, Alice will use her working tree and the index to
resolve conflicts, and existing local changes will interfere with the
conflict resolution process (Git will still perform the fetch but will
refuse to merge -- Alice will have to get rid of her local changes in
some way and pull again when this happens).
-Alice can peek at what Bob did without merging first, using the "fetch"
+Alice can peek at what Bob did without merging first, using the `fetch`
command; this allows Alice to inspect what Bob did, using a special
-symbol "FETCH_HEAD", in order to determine if he has anything worth
+symbol `FETCH_HEAD`, in order to determine if he has anything worth
pulling, like this:
------------------------------------------------
@@ -336,10 +335,10 @@ alice$ git log -p HEAD..FETCH_HEAD
------------------------------------------------
This operation is safe even if Alice has uncommitted local changes.
-The range notation "HEAD..FETCH_HEAD" means "show everything that is reachable
-from the FETCH_HEAD but exclude anything that is reachable from HEAD".
-Alice already knows everything that leads to her current state (HEAD),
-and reviews what Bob has in his state (FETCH_HEAD) that she has not
+The range notation `HEAD..FETCH_HEAD` means "show everything that is reachable
+from the `FETCH_HEAD` but exclude anything that is reachable from `HEAD`".
+Alice already knows everything that leads to her current state (`HEAD`),
+and reviews what Bob has in his state (`FETCH_HEAD`) that she has not
seen with this command.
If Alice wants to visualize what Bob did since their histories forked
@@ -349,7 +348,7 @@ she can issue the following command:
$ gitk HEAD..FETCH_HEAD
------------------------------------------------
-This uses the same two-dot range notation we saw earlier with 'git log'.
+This uses the same two-dot range notation we saw earlier with `git log`.
Alice may want to view what both of them did since they forked.
She can use three-dot form instead of the two-dot form:
@@ -361,13 +360,13 @@ $ gitk HEAD...FETCH_HEAD
This means "show everything that is reachable from either one, but
exclude anything that is reachable from both of them".
-Please note that these range notation can be used with both gitk
-and "git log".
+Please note that these range notation can be used with both `gitk`
+and `git log`.
After inspecting what Bob did, if there is nothing urgent, Alice may
decide to continue working without pulling from Bob. If Bob's history
does have something Alice would immediately need, Alice may choose to
-stash her work-in-progress first, do a "pull", and then finally unstash
+stash her work-in-progress first, do a `pull`, and then finally unstash
her work-in-progress on top of the resulting history.
When you are working in a small closely knit group, it is not
@@ -379,8 +378,8 @@ it easier:
alice$ git remote add bob /home/bob/myrepo
------------------------------------------------
-With this, Alice can perform the first part of the "pull" operation
-alone using the 'git fetch' command without merging them with her own
+With this, Alice can perform the first part of the `pull` operation
+alone using the `git fetch` command without merging them with her own
branch, using:
-------------------------------------
@@ -388,7 +387,7 @@ alice$ git fetch bob
-------------------------------------
Unlike the longhand form, when Alice fetches from Bob using a
-remote repository shorthand set up with 'git remote', what was
+remote repository shorthand set up with `git remote`, what was
fetched is stored in a remote-tracking branch, in this case
`bob/master`. So after this:
@@ -397,10 +396,10 @@ alice$ git log -p master..bob/master
-------------------------------------
shows a list of all the changes that Bob made since he branched from
-Alice's master branch.
+Alice's `master` branch.
After examining those changes, Alice
-could merge the changes into her master branch:
+could merge the changes into her `master` branch:
-------------------------------------
alice$ git merge bob/master
@@ -432,12 +431,12 @@ bob$ git config --get remote.origin.url
/home/alice/project
-------------------------------------
-(The complete configuration created by 'git clone' is visible using
+(The complete configuration created by `git clone` is visible using
`git config -l`, and the linkgit:git-config[1] man page
explains the meaning of each option.)
-Git also keeps a pristine copy of Alice's master branch under the
-name "origin/master":
+Git also keeps a pristine copy of Alice's `master` branch under the
+name `origin/master`:
-------------------------------------
bob$ git branch -r
@@ -462,8 +461,8 @@ Exploring history
-----------------
Git history is represented as a series of interrelated commits. We
-have already seen that the 'git log' command can list those commits.
-Note that first line of each git log entry also gives a name for the
+have already seen that the `git log` command can list those commits.
+Note that first line of each `git log` entry also gives a name for the
commit:
-------------------------------------
@@ -475,7 +474,7 @@ Date: Tue May 16 17:18:22 2006 -0700
merge-base: Clarify the comments on post processing.
-------------------------------------
-We can give this name to 'git show' to see the details about this
+We can give this name to `git show` to see the details about this
commit.
-------------------------------------
@@ -514,7 +513,7 @@ You can also give commits names of your own; after running
$ git tag v2.5 1b2e1d63ff
-------------------------------------
-you can refer to 1b2e1d63ff by the name "v2.5". If you intend to
+you can refer to `1b2e1d63ff` by the name `v2.5`. If you intend to
share this name with other people (for example, to identify a release
version), you should create a "tag" object, and perhaps sign it; see
linkgit:git-tag[1] for details.
@@ -533,22 +532,22 @@ $ git reset --hard HEAD^ # reset your current branch and working
Be careful with that last command: in addition to losing any changes
in the working directory, it will also remove all later commits from
this branch. If this branch is the only branch containing those
-commits, they will be lost. Also, don't use 'git reset' on a
+commits, they will be lost. Also, don't use `git reset` on a
publicly-visible branch that other developers pull from, as it will
force needless merges on other developers to clean up the history.
-If you need to undo changes that you have pushed, use 'git revert'
+If you need to undo changes that you have pushed, use `git revert`
instead.
-The 'git grep' command can search for strings in any version of your
+The `git grep` command can search for strings in any version of your
project, so
-------------------------------------
$ git grep "hello" v2.5
-------------------------------------
-searches for all occurrences of "hello" in v2.5.
+searches for all occurrences of "hello" in `v2.5`.
-If you leave out the commit name, 'git grep' will search any of the
+If you leave out the commit name, `git grep` will search any of the
files it manages in your current directory. So
-------------------------------------
@@ -558,7 +557,7 @@ $ git grep "hello"
is a quick way to search just the files that are tracked by Git.
Many Git commands also take sets of commits, which can be specified
-in a number of ways. Here are some examples with 'git log':
+in a number of ways. Here are some examples with `git log`:
-------------------------------------
$ git log v2.5..v2.6 # commits between v2.5 and v2.6
@@ -568,16 +567,16 @@ $ git log v2.5.. Makefile # commits since v2.5 which modify
# Makefile
-------------------------------------
-You can also give 'git log' a "range" of commits where the first is not
+You can also give `git log` a "range" of commits where the first is not
necessarily an ancestor of the second; for example, if the tips of
-the branches "stable" and "master" diverged from a common
+the branches `stable` and `master` diverged from a common
commit some time ago, then
-------------------------------------
$ git log stable..master
-------------------------------------
-will list commits made in the master branch but not in the
+will list commits made in the `master` branch but not in the
stable branch, while
-------------------------------------
@@ -585,15 +584,15 @@ $ git log master..stable
-------------------------------------
will show the list of commits made on the stable branch but not
-the master branch.
+the `master` branch.
-The 'git log' command has a weakness: it must present commits in a
+The `git log` command has a weakness: it must present commits in a
list. When the history has lines of development that diverged and
-then merged back together, the order in which 'git log' presents
+then merged back together, the order in which `git log` presents
those commits is meaningless.
Most projects with multiple contributors (such as the Linux kernel,
-or Git itself) have frequent merges, and 'gitk' does a better job of
+or Git itself) have frequent merges, and `gitk` does a better job of
visualizing their history. For example,
-------------------------------------
@@ -601,7 +600,7 @@ $ gitk --since="2 weeks ago" drivers/
-------------------------------------
allows you to browse any commits from the last 2 weeks of commits
-that modified files under the "drivers" directory. (Note: you can
+that modified files under the `drivers` directory. (Note: you can
adjust gitk's fonts by holding down the control key while pressing
"-" or "+".)
@@ -613,7 +612,7 @@ of the file:
$ git diff v2.5:Makefile HEAD:Makefile.in
-------------------------------------
-You can also use 'git show' to see any such file:
+You can also use `git show` to see any such file:
-------------------------------------
$ git show v2.5:Makefile
@@ -649,7 +648,7 @@ digressions that may be interesting at this point are:
* linkgit:git-bisect[1]: When there is a regression in your
project, one way to track down the bug is by searching through
- the history to find the exact commit that's to blame. Git bisect
+ the history to find the exact commit that's to blame. `git bisect`
can help you perform a binary search for that commit. It is
smart enough to perform a close-to-optimal search even in the
case of complex non-linear history with lots of merged branches.
diff --git a/Documentation/manpage-normal.xsl b/Documentation/manpage-normal.xsl
index a9c7ec69f4..beb5ff8ec2 100644
--- a/Documentation/manpage-normal.xsl
+++ b/Documentation/manpage-normal.xsl
@@ -8,19 +8,7 @@
<xsl:param name="man.output.quietly" select="1"/>
<xsl:param name="refentry.meta.get.quietly" select="1"/>
-<!-- convert asciidoc callouts to man page format -->
-<xsl:template match="co">
- <xsl:value-of select="concat('\fB(',substring-after(@id,'-'),')\fR')"/>
-</xsl:template>
-<xsl:template match="calloutlist">
- <xsl:text>.sp&#10;</xsl:text>
- <xsl:apply-templates/>
- <xsl:text>&#10;</xsl:text>
-</xsl:template>
-<xsl:template match="callout">
- <xsl:value-of select="concat('\fB',substring-after(@arearefs,'-'),'. \fR')"/>
- <xsl:apply-templates/>
- <xsl:text>.br&#10;</xsl:text>
-</xsl:template>
+<!-- unset maximum length of title -->
+<xsl:param name="man.th.title.max.length"/>
</xsl:stylesheet>
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 9aa58052bc..b42a2bb30a 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -32,8 +32,9 @@ characters and to avoid word splitting.
first match in the following rules:
. If '$GIT_DIR/<refname>' exists, that is what you mean (this is usually
- useful only for `HEAD`, `FETCH_HEAD`, `ORIG_HEAD`, `MERGE_HEAD`
- and `CHERRY_PICK_HEAD`);
+ useful only for `HEAD`, `FETCH_HEAD`, `ORIG_HEAD`, `MERGE_HEAD`,
+ `REBASE_HEAD`, `REVERT_HEAD`, `CHERRY_PICK_HEAD`, `BISECT_HEAD`
+ and `AUTO_MERGE`);
. otherwise, 'refs/<refname>' if it exists;
@@ -55,8 +56,18 @@ you can easily change the tip of the branch back to the state before you ran
them.
`MERGE_HEAD` records the commit(s) which you are merging into your branch
when you run `git merge`.
+`REBASE_HEAD`, during a rebase, records the commit at which the
+operation is currently stopped, either because of conflicts or an `edit`
+command in an interactive rebase.
+`REVERT_HEAD` records the commit which you are reverting when you
+run `git revert`.
`CHERRY_PICK_HEAD` records the commit which you are cherry-picking
when you run `git cherry-pick`.
+`BISECT_HEAD` records the current commit to be tested when you
+run `git bisect --no-checkout`.
+`AUTO_MERGE` records a tree object corresponding to the state the
+'ort' merge strategy wrote to the working tree when a merge operation
+resulted in conflicts.
+
Note that any of the 'refs/*' cases above may come either from
the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
diff --git a/Documentation/technical/api-merge.txt b/Documentation/technical/api-merge.txt
index 487d4d83ff..c2ba01828c 100644
--- a/Documentation/technical/api-merge.txt
+++ b/Documentation/technical/api-merge.txt
@@ -28,9 +28,9 @@ and `diff.c` for examples.
* `struct ll_merge_options`
-Check ll-merge.h for details.
+Check merge-ll.h for details.
Low-level (single file) merge
-----------------------------
-Check ll-merge.h for details.
+Check merge-ll.h for details.
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index dc9c6a663a..510ab4508d 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -1343,6 +1343,33 @@ $ git diff -3 file.txt # diff against stage 3
$ git diff --theirs file.txt # same as the above.
-------------------------------------------------
+When using the 'ort' merge strategy (the default), before updating the working
+tree with the result of the merge, Git writes a special ref named AUTO_MERGE
+reflecting the state of the tree it is about to write. Conflicted paths that
+could not be automatically merged are written to this tree with conflict
+markers, just as in the working tree. AUTO_MERGE can thus be used with
+linkgit:git-diff[1] to show the changes you've made so far to resolve
+conflicts. Using the same example as above, after resolving the conflict we
+get:
+
+-------------------------------------------------
+$ git diff AUTO_MERGE
+diff --git a/file.txt b/file.txt
+index cd10406..8bf5ae7 100644
+--- a/file.txt
++++ b/file.txt
+@@ -1,5 +1 @@
+-<<<<<<< HEAD:file.txt
+-Hello world
+-=======
+-Goodbye
+->>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt
++Goodbye world
+-------------------------------------------------
+
+Notice that the diff shows we deleted the conflict markers and both versions,
+and wrote "Goodbye world" instead.
+
The linkgit:git-log[1] and linkgit:gitk[1] commands also provide special help
for merges:
@@ -4102,13 +4129,11 @@ Note that terminology has changed since that revision. For example, the
README in that revision uses the word "changeset" to describe what we
now call a <<def_commit_object,commit>>.
-Also, we do not call it "cache" any more, but rather "index"; however, the
-file is still called `cache.h`. Remark: Not much reason to change it now,
-especially since there is no good single name for it anyway, because it is
-basically _the_ header file which is included by _all_ of Git's C sources.
+Also, we do not call it "cache" any more, but rather "index"; however,
+the file is still called `read-cache.h`.
If you grasp the ideas in that initial commit, you should check out a
-more recent version and skim `cache.h`, `object.h` and `commit.h`.
+more recent version and skim `read-cache-ll.h`, `object.h` and `commit.h`.
In the early days, Git (in the tradition of UNIX) was a bunch of programs
which were extremely simple, and which you used in scripts, piping the
@@ -4119,11 +4144,11 @@ many of these parts have become builtins, and some of the core has been
and to avoid code duplication.
By now, you know what the index is (and find the corresponding data
-structures in `cache.h`), and that there are just a couple of object types
-(blobs, trees, commits and tags) which inherit their common structure from
-`struct object`, which is their first member (and thus, you can cast e.g.
-`(struct object *)commit` to achieve the _same_ as `&commit->object`, i.e.
-get at the object name and flags).
+structures in `read-cache-ll.h`), and that there are just a couple of
+object types (blobs, trees, commits and tags) which inherit their
+common structure from `struct object`, which is their first member
+(and thus, you can cast e.g. `(struct object *)commit` to achieve the
+_same_ as `&commit->object`, i.e. get at the object name and flags).
Now is a good point to take a break to let this information sink in.
diff --git a/INSTALL b/INSTALL
index 4b42288882..c98ef9da7b 100644
--- a/INSTALL
+++ b/INSTALL
@@ -129,13 +129,13 @@ Issues of note:
itself, e.g. Digest::MD5, File::Spec, File::Temp, Net::Domain,
Net::SMTP, and Time::HiRes.
- - git-imap-send needs the OpenSSL library to talk IMAP over SSL if
- you are using libcurl older than 7.34.0. Otherwise you can use
- NO_OPENSSL without losing git-imap-send.
+ - git-imap-send needs libcurl 7.34.0 or newer, in addition
+ OpenSSL is needed if using the "imap.tunnel" open to tunnel
+ over SSL. Define NO_OPENSSL to omit the OpenSSL prerequisite.
- "libcurl" library is used for fetching and pushing
repositories over http:// or https://, as well as by
- git-imap-send if the curl version is >= 7.34.0. If you do
+ git-imap-send. If you do
not need that functionality, use NO_CURL to build without
it.
diff --git a/Makefile b/Makefile
index a6ab78840f..b26fa28c94 100644
--- a/Makefile
+++ b/Makefile
@@ -768,7 +768,9 @@ PROGRAMS += $(EXTRA_PROGRAMS)
PROGRAM_OBJS += daemon.o
PROGRAM_OBJS += http-backend.o
+ifndef NO_CURL
PROGRAM_OBJS += imap-send.o
+endif
PROGRAM_OBJS += sh-i18n--envsubst.o
PROGRAM_OBJS += shell.o
.PHONY: program-objs
@@ -1050,7 +1052,6 @@ LIB_OBJS += linear-assignment.o
LIB_OBJS += list-objects-filter-options.o
LIB_OBJS += list-objects-filter.o
LIB_OBJS += list-objects.o
-LIB_OBJS += ll-merge.o
LIB_OBJS += lockfile.o
LIB_OBJS += log-tree.o
LIB_OBJS += ls-refs.o
@@ -1059,6 +1060,7 @@ LIB_OBJS += mailmap.o
LIB_OBJS += match-trees.o
LIB_OBJS += mem-pool.o
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
@@ -1141,6 +1143,7 @@ LIB_OBJS += sigchain.o
LIB_OBJS += sparse-index.o
LIB_OBJS += split-index.o
LIB_OBJS += stable-qsort.o
+LIB_OBJS += statinfo.o
LIB_OBJS += strbuf.o
LIB_OBJS += streaming.o
LIB_OBJS += string-list.o
@@ -1578,7 +1581,6 @@ ifdef HAVE_ALLOCA_H
BASIC_CFLAGS += -DHAVE_ALLOCA_H
endif
-IMAP_SEND_BUILDDEPS =
IMAP_SEND_LDFLAGS =
ifdef NO_CURL
@@ -1587,6 +1589,7 @@ ifdef NO_CURL
REMOTE_CURL_ALIASES =
REMOTE_CURL_NAMES =
EXCLUDED_PROGRAMS += git-http-fetch git-http-push
+ EXCLUDED_PROGRAMS += git-imap-send
else
ifdef CURLDIR
# Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case.
@@ -1612,19 +1615,9 @@ else
REMOTE_CURL_NAMES = $(REMOTE_CURL_PRIMARY) $(REMOTE_CURL_ALIASES)
PROGRAM_OBJS += http-fetch.o
PROGRAMS += $(REMOTE_CURL_NAMES)
+ IMAP_SEND_LDFLAGS += $(CURL_LIBCURL)
ifndef NO_EXPAT
PROGRAM_OBJS += http-push.o
- endif
- curl_check := $(shell (echo 072200; $(CURL_CONFIG) --vernum | sed -e '/^70[BC]/s/^/0/') 2>/dev/null | sort -r | sed -ne 2p)
- ifeq "$(curl_check)" "072200"
- USE_CURL_FOR_IMAP_SEND = YesPlease
- endif
- ifdef USE_CURL_FOR_IMAP_SEND
- BASIC_CFLAGS += -DUSE_CURL_FOR_IMAP_SEND
- IMAP_SEND_BUILDDEPS = http.o
- IMAP_SEND_LDFLAGS += $(CURL_LIBCURL)
- endif
- ifndef NO_EXPAT
ifdef EXPATDIR
BASIC_CFLAGS += -I$(EXPATDIR)/include
EXPAT_LIBEXPAT = -L$(EXPATDIR)/$(lib) $(CC_LD_DYNPATH)$(EXPATDIR)/$(lib) -lexpat
@@ -1951,7 +1944,7 @@ endif
BASIC_CFLAGS += \
-DSHA1DC_NO_STANDARD_INCLUDES \
-DSHA1DC_INIT_SAFE_HASH_DEFAULT=0 \
- -DSHA1DC_CUSTOM_INCLUDE_SHA1_C="\"cache.h\"" \
+ -DSHA1DC_CUSTOM_INCLUDE_SHA1_C="\"git-compat-util.h\"" \
-DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C="\"git-compat-util.h\""
endif
endif
@@ -2781,7 +2774,7 @@ endif
git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
-git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS)
+git-imap-send$X: imap-send.o http.o GIT-LDFLAGS $(GITLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(IMAP_SEND_LDFLAGS) $(LIBS)
diff --git a/abspath.c b/abspath.c
index d032f5dce5..1202cde23d 100644
--- a/abspath.c
+++ b/abspath.c
@@ -289,3 +289,39 @@ char *prefix_filename_except_for_dash(const char *pfx, const char *arg)
return xstrdup(arg);
return prefix_filename(pfx, arg);
}
+
+void strbuf_add_absolute_path(struct strbuf *sb, const char *path)
+{
+ if (!*path)
+ die("The empty string is not a valid path");
+ if (!is_absolute_path(path)) {
+ struct stat cwd_stat, pwd_stat;
+ size_t orig_len = sb->len;
+ char *cwd = xgetcwd();
+ char *pwd = getenv("PWD");
+ if (pwd && strcmp(pwd, cwd) &&
+ !stat(cwd, &cwd_stat) &&
+ (cwd_stat.st_dev || cwd_stat.st_ino) &&
+ !stat(pwd, &pwd_stat) &&
+ pwd_stat.st_dev == cwd_stat.st_dev &&
+ pwd_stat.st_ino == cwd_stat.st_ino)
+ strbuf_addstr(sb, pwd);
+ else
+ strbuf_addstr(sb, cwd);
+ if (sb->len > orig_len && !is_dir_sep(sb->buf[sb->len - 1]))
+ strbuf_addch(sb, '/');
+ free(cwd);
+ }
+ strbuf_addstr(sb, path);
+}
+
+void strbuf_add_real_path(struct strbuf *sb, const char *path)
+{
+ if (sb->len) {
+ struct strbuf resolved = STRBUF_INIT;
+ strbuf_realpath(&resolved, path, 1);
+ strbuf_addbuf(sb, &resolved);
+ strbuf_release(&resolved);
+ } else
+ strbuf_realpath(sb, path, 1);
+}
diff --git a/abspath.h b/abspath.h
index 7cd3de5e9d..4653080d5e 100644
--- a/abspath.h
+++ b/abspath.h
@@ -30,4 +30,25 @@ static inline int is_absolute_path(const char *path)
return is_dir_sep(path[0]) || has_dos_drive_prefix(path);
}
+/**
+ * Add a path to a buffer, converting a relative path to an
+ * absolute one in the process. Symbolic links are not
+ * resolved.
+ */
+void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
+
+/**
+ * Canonize `path` (make it absolute, resolve symlinks, remove extra
+ * slashes) and append it to `sb`. Die with an informative error
+ * message if there is a problem.
+ *
+ * The directory part of `path` (i.e., everything up to the last
+ * dir_sep) must denote a valid, existing directory, but the last
+ * component need not exist.
+ *
+ * Callers that don't mind links should use the more lightweight
+ * strbuf_add_absolute_path() instead.
+ */
+void strbuf_add_real_path(struct strbuf *sb, const char *path);
+
#endif /* ABSPATH_H */
diff --git a/add-interactive.c b/add-interactive.c
index 757a9929d4..add9a1ad43 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -1,10 +1,14 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "add-interactive.h"
#include "color.h"
#include "config.h"
#include "diffcore.h"
#include "gettext.h"
+#include "hash.h"
#include "hex.h"
+#include "preload-index.h"
+#include "read-cache-ll.h"
+#include "repository.h"
#include "revision.h"
#include "refs.h"
#include "string-list.h"
@@ -12,6 +16,7 @@
#include "dir.h"
#include "run-command.h"
#include "prompt.h"
+#include "tree.h"
static void init_color(struct repository *r, struct add_i_state *s,
const char *section_and_slot, char *dst,
diff --git a/add-patch.c b/add-patch.c
index 8d770d203f..ba629add62 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "add-interactive.h"
#include "advice.h"
#include "alloc.h"
@@ -6,6 +6,8 @@
#include "environment.h"
#include "gettext.h"
#include "object-name.h"
+#include "read-cache-ll.h"
+#include "repository.h"
#include "strbuf.h"
#include "run-command.h"
#include "strvec.h"
@@ -1105,10 +1107,11 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
size_t i;
strbuf_reset(&s->buf);
- strbuf_commented_addf(&s->buf, _("Manual hunk edit mode -- see bottom for "
- "a quick guide.\n"));
+ strbuf_commented_addf(&s->buf, comment_line_char,
+ _("Manual hunk edit mode -- see bottom for "
+ "a quick guide.\n"));
render_hunk(s, hunk, 0, 0, &s->buf);
- strbuf_commented_addf(&s->buf,
+ strbuf_commented_addf(&s->buf, comment_line_char,
_("---\n"
"To remove '%c' lines, make them ' ' lines "
"(context).\n"
@@ -1117,12 +1120,13 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
s->mode->is_reverse ? '+' : '-',
s->mode->is_reverse ? '-' : '+',
comment_line_char);
- strbuf_commented_addf(&s->buf, "%s", _(s->mode->edit_hunk_hint));
+ strbuf_commented_addf(&s->buf, comment_line_char, "%s",
+ _(s->mode->edit_hunk_hint));
/*
* TRANSLATORS: 'it' refers to the patch mentioned in the previous
* messages.
*/
- strbuf_commented_addf(&s->buf,
+ strbuf_commented_addf(&s->buf, comment_line_char,
_("If it does not apply cleanly, you will be "
"given an opportunity to\n"
"edit again. If all lines of the hunk are "
diff --git a/advice.c b/advice.c
index d6232439c3..e5a9bb9b44 100644
--- a/advice.c
+++ b/advice.c
@@ -78,6 +78,7 @@ static struct {
[ADVICE_SUBMODULES_NOT_UPDATED] = { "submodulesNotUpdated", 1 },
[ADVICE_UPDATE_SPARSE_PATH] = { "updateSparsePath", 1 },
[ADVICE_WAITING_FOR_EDITOR] = { "waitingForEditor", 1 },
+ [ADVICE_WORKTREE_ADD_ORPHAN] = { "worktreeAddOrphan", 1 },
};
static const char turn_off_instructions[] =
diff --git a/advice.h b/advice.h
index 0f584163f5..2affbe1426 100644
--- a/advice.h
+++ b/advice.h
@@ -49,6 +49,7 @@ struct string_list;
ADVICE_UPDATE_SPARSE_PATH,
ADVICE_WAITING_FOR_EDITOR,
ADVICE_SKIPPED_CHERRY_PICKS,
+ ADVICE_WORKTREE_ADD_ORPHAN,
};
int git_default_advice_config(const char *var, const char *value);
diff --git a/alloc.c b/alloc.c
index 2886aa9354..377e80f5dd 100644
--- a/alloc.c
+++ b/alloc.c
@@ -13,6 +13,7 @@
#include "blob.h"
#include "tree.h"
#include "commit.h"
+#include "repository.h"
#include "tag.h"
#include "alloc.h"
diff --git a/apply.c b/apply.c
index 3636bc14c2..8bd0109fcc 100644
--- a/apply.c
+++ b/apply.c
@@ -7,11 +7,12 @@
*
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "abspath.h"
#include "alloc.h"
+#include "base85.h"
#include "config.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "blob.h"
#include "delta.h"
#include "diff.h"
@@ -20,16 +21,22 @@
#include "gettext.h"
#include "hex.h"
#include "xdiff-interface.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
#include "lockfile.h"
+#include "name-hash.h"
#include "object-name.h"
#include "object-file.h"
#include "parse-options.h"
+#include "path.h"
#include "quote.h"
+#include "read-cache.h"
#include "rerere.h"
#include "apply.h"
#include "entry.h"
#include "setup.h"
+#include "symlinks.h"
+#include "wildmatch.h"
+#include "ws.h"
#include "wrapper.h"
struct gitdiff_data {
diff --git a/apply.h b/apply.h
index b9f18ce87d..7cd38b1443 100644
--- a/apply.h
+++ b/apply.h
@@ -1,7 +1,7 @@
#ifndef APPLY_H
#define APPLY_H
-#include "hash.h"
+#include "hash-ll.h"
#include "lockfile.h"
#include "string-list.h"
#include "strmap.h"
diff --git a/archive-tar.c b/archive-tar.c
index 4cd81d8161..fc06ff4c5d 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -9,7 +9,7 @@
#include "hex.h"
#include "tar.h"
#include "archive.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "streaming.h"
#include "run-command.h"
#include "write-or-die.h"
diff --git a/archive-zip.c b/archive-zip.c
index ef538a90df..b438264096 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2006 Rene Scharfe
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "config.h"
#include "archive.h"
#include "gettext.h"
@@ -9,7 +9,7 @@
#include "hex.h"
#include "streaming.h"
#include "utf8.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "userdiff.h"
#include "write-or-die.h"
#include "xdiff-interface.h"
diff --git a/archive.c b/archive.c
index 8570cf37ff..1817cca9f4 100644
--- a/archive.c
+++ b/archive.c
@@ -6,10 +6,13 @@
#include "environment.h"
#include "gettext.h"
#include "hex.h"
+#include "path.h"
+#include "pretty.h"
#include "setup.h"
#include "refs.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "commit.h"
+#include "tree.h"
#include "tree-walk.h"
#include "attr.h"
#include "archive.h"
@@ -128,7 +131,7 @@ static const struct attr_check *get_archive_attrs(struct index_state *istate,
static struct attr_check *check;
if (!check)
check = attr_check_initl("export-ignore", "export-subst", NULL);
- git_check_attr(istate, NULL, path, check);
+ git_check_attr(istate, path, check);
return check;
}
diff --git a/attr.c b/attr.c
index 2d8aeb8b58..7d39ac4a29 100644
--- a/attr.c
+++ b/attr.c
@@ -6,7 +6,7 @@
* an insanely large number of attributes.
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "alloc.h"
#include "config.h"
#include "environment.h"
@@ -14,12 +14,16 @@
#include "attr.h"
#include "dir.h"
#include "gettext.h"
+#include "path.h"
#include "utf8.h"
#include "quote.h"
+#include "read-cache-ll.h"
#include "revision.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "setup.h"
#include "thread-utils.h"
+#include "tree-walk.h"
+#include "object-name.h"
const char git_attr__true[] = "(builtin)true";
const char git_attr__false[] = "\0(builtin)false";
@@ -1169,11 +1173,42 @@ static void collect_some_attrs(struct index_state *istate,
fill(path, pathlen, basename_offset, check->stack, check->all_attrs, rem);
}
+static const char *default_attr_source_tree_object_name;
+
+void set_git_attr_source(const char *tree_object_name)
+{
+ default_attr_source_tree_object_name = xstrdup(tree_object_name);
+}
+
+static void compute_default_attr_source(struct object_id *attr_source)
+{
+ if (!default_attr_source_tree_object_name)
+ default_attr_source_tree_object_name = getenv(GIT_ATTR_SOURCE_ENVIRONMENT);
+
+ if (!default_attr_source_tree_object_name || !is_null_oid(attr_source))
+ return;
+
+ if (repo_get_oid_treeish(the_repository, default_attr_source_tree_object_name, attr_source))
+ die(_("bad --attr-source or GIT_ATTR_SOURCE"));
+}
+
+static struct object_id *default_attr_source(void)
+{
+ static struct object_id attr_source;
+
+ if (is_null_oid(&attr_source))
+ compute_default_attr_source(&attr_source);
+ if (is_null_oid(&attr_source))
+ return NULL;
+ return &attr_source;
+}
+
void git_check_attr(struct index_state *istate,
- const struct object_id *tree_oid, const char *path,
+ const char *path,
struct attr_check *check)
{
int i;
+ const struct object_id *tree_oid = default_attr_source();
collect_some_attrs(istate, tree_oid, path, check);
@@ -1186,10 +1221,11 @@ void git_check_attr(struct index_state *istate,
}
}
-void git_all_attrs(struct index_state *istate, const struct object_id *tree_oid,
+void git_all_attrs(struct index_state *istate,
const char *path, struct attr_check *check)
{
int i;
+ const struct object_id *tree_oid = default_attr_source();
attr_check_reset(check);
collect_some_attrs(istate, tree_oid, path, check);
diff --git a/attr.h b/attr.h
index 9884ea2bc6..676bd17ce2 100644
--- a/attr.h
+++ b/attr.h
@@ -45,7 +45,7 @@
* const char *path;
*
* setup_check();
- * git_check_attr(&the_index, tree_oid, path, check);
+ * git_check_attr(&the_index, path, check);
* ------------
*
* - Act on `.value` member of the result, left in `check->items[]`:
@@ -120,7 +120,6 @@
#define ATTR_MAX_FILE_SIZE (100 * 1024 * 1024)
struct index_state;
-struct object_id;
/**
* An attribute is an opaque object that is identified by its name. Pass the
@@ -136,6 +135,12 @@ struct all_attrs_item;
struct attr_stack;
/*
+ * The textual object name for the tree-ish used by git_check_attr()
+ * to read attributes from (instead of from the working tree).
+ */
+void set_git_attr_source(const char *);
+
+/*
* Given a string, return the gitattribute object that
* corresponds to it.
*/
@@ -203,14 +208,14 @@ void attr_check_free(struct attr_check *check);
const char *git_attr_name(const struct git_attr *);
void git_check_attr(struct index_state *istate,
- const struct object_id *tree_oid, const char *path,
+ const char *path,
struct attr_check *check);
/*
* Retrieve all attributes that apply to the specified path.
* check holds the attributes and their values.
*/
-void git_all_attrs(struct index_state *istate, const struct object_id *tree_oid,
+void git_all_attrs(struct index_state *istate,
const char *path, struct attr_check *check);
enum git_attr_direction {
diff --git a/base85.c b/base85.c
index 5ca601ee14..bbacdca31b 100644
--- a/base85.c
+++ b/base85.c
@@ -1,4 +1,5 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "base85.h"
#undef DEBUG_85
diff --git a/base85.h b/base85.h
new file mode 100644
index 0000000000..c835086e09
--- /dev/null
+++ b/base85.h
@@ -0,0 +1,7 @@
+#ifndef BASE85_H
+#define BASE85_H
+
+int decode_85(char *dst, const char *line, int linelen);
+void encode_85(char *buf, const unsigned char *data, int bytes);
+
+#endif /* BASE85_H */
diff --git a/bisect.c b/bisect.c
index 8d5f8e5885..1be8e0a271 100644
--- a/bisect.c
+++ b/bisect.c
@@ -18,7 +18,8 @@
#include "commit-slab.h"
#include "commit-reach.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "path.h"
#include "dir.h"
static struct oid_array good_revs;
diff --git a/blame.c b/blame.c
index b830654062..d12bd9f97b 100644
--- a/blame.c
+++ b/blame.c
@@ -1,6 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "refs.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "cache-tree.h"
#include "mergesort.h"
#include "convert.h"
@@ -8,6 +8,8 @@
#include "diffcore.h"
#include "gettext.h"
#include "hex.h"
+#include "path.h"
+#include "read-cache.h"
#include "setup.h"
#include "tag.h"
#include "trace2.h"
diff --git a/blame.h b/blame.h
index b60d1d81e3..31ddc85f19 100644
--- a/blame.h
+++ b/blame.h
@@ -2,6 +2,7 @@
#define BLAME_H
#include "commit.h"
+#include "oidset.h"
#include "xdiff-interface.h"
#include "revision.h"
#include "prio-queue.h"
diff --git a/blob.c b/blob.c
index 888e28a559..08f9b47f77 100644
--- a/blob.c
+++ b/blob.c
@@ -5,12 +5,19 @@
const char *blob_type = "blob";
-struct blob *lookup_blob(struct repository *r, const struct object_id *oid)
+struct blob *lookup_blob_type(struct repository *r,
+ const struct object_id *oid,
+ enum object_type type)
{
struct object *obj = lookup_object(r, oid);
if (!obj)
return create_object(r, oid, alloc_blob_node(r));
- return object_as_type(obj, OBJ_BLOB, 0);
+ return object_as_type_hint(obj, OBJ_BLOB, type);
+}
+
+struct blob *lookup_blob(struct repository *r, const struct object_id *oid)
+{
+ return lookup_blob_type(r, oid, OBJ_NONE);
}
void parse_blob_buffer(struct blob *item)
diff --git a/blob.h b/blob.h
index 74555c90c4..b91b1600bd 100644
--- a/blob.h
+++ b/blob.h
@@ -10,6 +10,9 @@ struct blob {
};
struct blob *lookup_blob(struct repository *r, const struct object_id *oid);
+struct blob *lookup_blob_type(struct repository *r,
+ const struct object_id *oid,
+ enum object_type type);
/**
* Blobs do not contain references to other objects and do not have
diff --git a/bloom.c b/bloom.c
index d0730525da..aef6b5fea2 100644
--- a/bloom.c
+++ b/bloom.c
@@ -6,6 +6,7 @@
#include "hashmap.h"
#include "commit-graph.h"
#include "commit.h"
+#include "commit-slab.h"
define_commit_slab(bloom_filter_slab, struct bloom_filter);
diff --git a/branch.c b/branch.c
index 7df982693a..20073284c8 100644
--- a/branch.c
+++ b/branch.c
@@ -6,9 +6,11 @@
#include "gettext.h"
#include "hex.h"
#include "object-name.h"
+#include "path.h"
#include "refs.h"
#include "refspec.h"
#include "remote.h"
+#include "repository.h"
#include "sequencer.h"
#include "commit.h"
#include "worktree.h"
@@ -839,30 +841,3 @@ void die_if_checked_out(const char *branch, int ignore_current_worktree)
free_worktrees(worktrees);
}
-
-int replace_each_worktree_head_symref(const char *oldref, const char *newref,
- const char *logmsg)
-{
- int ret = 0;
- struct worktree **worktrees = get_worktrees();
- int i;
-
- for (i = 0; worktrees[i]; i++) {
- struct ref_store *refs;
-
- if (worktrees[i]->is_detached)
- continue;
- if (!worktrees[i]->head_ref)
- continue;
- if (strcmp(oldref, worktrees[i]->head_ref))
- continue;
-
- refs = get_worktree_ref_store(worktrees[i]);
- if (refs_create_symref(refs, "HEAD", newref, logmsg))
- ret = error(_("HEAD of working tree %s is not updated"),
- worktrees[i]->path);
- }
-
- free_worktrees(worktrees);
- return ret;
-}
diff --git a/branch.h b/branch.h
index ef56103c05..30c01aed73 100644
--- a/branch.h
+++ b/branch.h
@@ -155,12 +155,4 @@ int read_branch_desc(struct strbuf *, const char *branch_name);
*/
void die_if_checked_out(const char *branch, int ignore_current_worktree);
-/*
- * Update all per-worktree HEADs pointing at the old ref to point the new ref.
- * This will be used when renaming a branch. Returns 0 if successful, non-zero
- * otherwise.
- */
-int replace_each_worktree_head_symref(const char *oldref, const char *newref,
- const char *logmsg);
-
#endif
diff --git a/builtin.h b/builtin.h
index c3f0b56915..28280636da 100644
--- a/builtin.h
+++ b/builtin.h
@@ -2,9 +2,6 @@
#define BUILTIN_H
#include "git-compat-util.h"
-#include "strbuf.h"
-#include "cache.h"
-#include "commit.h"
/*
* builtin API
diff --git a/builtin/add.c b/builtin/add.c
index 76cc026a68..e3ca3e4edb 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -4,10 +4,9 @@
* Copyright (C) 2006 Linus Torvalds
*/
#define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
#include "advice.h"
#include "config.h"
-#include "builtin.h"
#include "lockfile.h"
#include "editor.h"
#include "dir.h"
@@ -17,8 +16,12 @@
#include "cache-tree.h"
#include "run-command.h"
#include "parse-options.h"
+#include "path.h"
+#include "preload-index.h"
#include "diff.h"
#include "diffcore.h"
+#include "read-cache.h"
+#include "repository.h"
#include "revision.h"
#include "bulk-checkin.h"
#include "strvec.h"
@@ -36,11 +39,6 @@ static int pathspec_file_nul;
static int include_sparse;
static const char *pathspec_from_file;
-struct update_callback_data {
- int flags;
- int add_errors;
-};
-
static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only)
{
int i, ret = 0;
@@ -69,95 +67,6 @@ static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only)
return ret;
}
-static int fix_unmerged_status(struct diff_filepair *p,
- struct update_callback_data *data)
-{
- if (p->status != DIFF_STATUS_UNMERGED)
- return p->status;
- if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL) && !p->two->mode)
- /*
- * This is not an explicit add request, and the
- * path is missing from the working tree (deleted)
- */
- return DIFF_STATUS_DELETED;
- else
- /*
- * Either an explicit add request, or path exists
- * in the working tree. An attempt to explicitly
- * add a path that does not exist in the working tree
- * will be caught as an error by the caller immediately.
- */
- return DIFF_STATUS_MODIFIED;
-}
-
-static void update_callback(struct diff_queue_struct *q,
- struct diff_options *opt UNUSED, void *cbdata)
-{
- int i;
- struct update_callback_data *data = cbdata;
-
- for (i = 0; i < q->nr; i++) {
- struct diff_filepair *p = q->queue[i];
- const char *path = p->one->path;
-
- if (!include_sparse && !path_in_sparse_checkout(path, &the_index))
- continue;
-
- switch (fix_unmerged_status(p, data)) {
- default:
- die(_("unexpected diff status %c"), p->status);
- case DIFF_STATUS_MODIFIED:
- case DIFF_STATUS_TYPE_CHANGED:
- if (add_file_to_index(&the_index, path, data->flags)) {
- if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
- die(_("updating files failed"));
- data->add_errors++;
- }
- break;
- case DIFF_STATUS_DELETED:
- if (data->flags & ADD_CACHE_IGNORE_REMOVAL)
- break;
- if (!(data->flags & ADD_CACHE_PRETEND))
- remove_file_from_index(&the_index, path);
- if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
- printf(_("remove '%s'\n"), path);
- break;
- }
- }
-}
-
-int add_files_to_cache(const char *prefix,
- const struct pathspec *pathspec, int flags)
-{
- struct update_callback_data data;
- struct rev_info rev;
-
- memset(&data, 0, sizeof(data));
- data.flags = flags;
-
- repo_init_revisions(the_repository, &rev, prefix);
- setup_revisions(0, NULL, &rev, NULL);
- if (pathspec)
- copy_pathspec(&rev.prune_data, pathspec);
- rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
- rev.diffopt.format_callback = update_callback;
- rev.diffopt.format_callback_data = &data;
- rev.diffopt.flags.override_submodule_config = 1;
- rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
-
- /*
- * Use an ODB transaction to optimize adding multiple objects.
- * This function is invoked from commands other than 'add', which
- * may not have their own transaction active.
- */
- begin_odb_transaction();
- run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
- end_odb_transaction();
-
- release_revisions(&rev);
- return !!data.add_errors;
-}
-
static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
{
int i, retval = 0;
@@ -640,7 +549,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (add_renormalize)
exit_status |= renormalize_tracked_files(&pathspec, flags);
else
- exit_status |= add_files_to_cache(prefix, &pathspec, flags);
+ exit_status |= add_files_to_cache(the_repository, prefix,
+ &pathspec, include_sparse,
+ flags);
if (add_new_files)
exit_status |= add_files(&dir, flags);
diff --git a/builtin/am.c b/builtin/am.c
index 5c83f2e003..5fab159599 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -4,11 +4,10 @@
* Based on git-am.sh by Junio C Hamano.
*/
#define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
#include "abspath.h"
#include "advice.h"
#include "config.h"
-#include "builtin.h"
#include "editor.h"
#include "environment.h"
#include "exec-cmd.h"
@@ -29,6 +28,7 @@
#include "unpack-trees.h"
#include "branch.h"
#include "object-name.h"
+#include "preload-index.h"
#include "sequencer.h"
#include "revision.h"
#include "merge-recursive.h"
@@ -41,6 +41,7 @@
#include "string-list.h"
#include "packfile.h"
#include "pager.h"
+#include "path.h"
#include "repository.h"
#include "pretty.h"
#include "wrapper.h"
@@ -1283,7 +1284,7 @@ static int parse_mail(struct am_state *state, const char *mail)
strbuf_addstr(&msg, "\n\n");
strbuf_addbuf(&msg, &mi.log_message);
- strbuf_stripspace(&msg, 0);
+ strbuf_stripspace(&msg, '\0');
assert(!state->author_name);
state->author_name = strbuf_detach(&author_name, NULL);
diff --git a/builtin/apply.c b/builtin/apply.c
index fe72c0ec3e..c18b7ea5d3 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -1,7 +1,7 @@
-#include "cache.h"
#include "builtin.h"
#include "gettext.h"
#include "parse-options.h"
+#include "repository.h"
#include "apply.h"
static const char * const apply_usage[] = {
diff --git a/builtin/archive.c b/builtin/archive.c
index d13934f1a8..90761fdfee 100644
--- a/builtin/archive.c
+++ b/builtin/archive.c
@@ -2,13 +2,13 @@
* Copyright (c) 2006 Franck Bui-Huu
* Copyright (c) 2006 Rene Scharfe
*/
-#include "cache.h"
#include "builtin.h"
#include "archive.h"
#include "gettext.h"
#include "transport.h"
#include "parse-options.h"
#include "pkt-line.h"
+#include "repository.h"
#include "sideband.h"
static void create_output_file(const char *output_file)
diff --git a/builtin/bisect.c b/builtin/bisect.c
index 4b2143d455..6478df3489 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -1,5 +1,5 @@
#include "builtin.h"
-#include "cache.h"
+#include "copy.h"
#include "environment.h"
#include "gettext.h"
#include "hex.h"
@@ -11,6 +11,7 @@
#include "strvec.h"
#include "run-command.h"
#include "oid-array.h"
+#include "path.h"
#include "prompt.h"
#include "quote.h"
#include "revision.h"
diff --git a/builtin/blame.c b/builtin/blame.c
index 2df6039a6e..e811e7fbfb 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -29,7 +29,7 @@
#include "dir.h"
#include "progress.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "pager.h"
#include "blame.h"
#include "refs.h"
diff --git a/builtin/branch.c b/builtin/branch.c
index 501c47657c..6249fa1809 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -5,20 +5,20 @@
* Based on git-branch.sh by Junio C Hamano.
*/
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
#include "color.h"
#include "editor.h"
#include "environment.h"
#include "refs.h"
#include "commit.h"
-#include "builtin.h"
#include "gettext.h"
#include "object-name.h"
#include "remote.h"
#include "parse-options.h"
#include "branch.h"
#include "diff.h"
+#include "path.h"
#include "revision.h"
#include "string-list.h"
#include "column.h"
@@ -512,9 +512,9 @@ static void print_current_branch_name(void)
die(_("HEAD (%s) points outside of refs/heads/"), refname);
}
-static void reject_rebase_or_bisect_branch(const char *target)
+static void reject_rebase_or_bisect_branch(struct worktree **worktrees,
+ const char *target)
{
- struct worktree **worktrees = get_worktrees();
int i;
for (i = 0; worktrees[i]; i++) {
@@ -531,17 +531,50 @@ static void reject_rebase_or_bisect_branch(const char *target)
die(_("Branch %s is being bisected at %s"),
target, wt->path);
}
+}
- free_worktrees(worktrees);
+/*
+ * Update all per-worktree HEADs pointing at the old ref to point the new ref.
+ * This will be used when renaming a branch. Returns 0 if successful, non-zero
+ * otherwise.
+ */
+static int replace_each_worktree_head_symref(struct worktree **worktrees,
+ const char *oldref, const char *newref,
+ const char *logmsg)
+{
+ int ret = 0;
+ int i;
+
+ for (i = 0; worktrees[i]; i++) {
+ struct ref_store *refs;
+
+ if (worktrees[i]->is_detached)
+ continue;
+ if (!worktrees[i]->head_ref)
+ continue;
+ if (strcmp(oldref, worktrees[i]->head_ref))
+ continue;
+
+ refs = get_worktree_ref_store(worktrees[i]);
+ if (refs_create_symref(refs, "HEAD", newref, logmsg))
+ ret = error(_("HEAD of working tree %s is not updated"),
+ worktrees[i]->path);
+ }
+
+ return ret;
}
+#define IS_HEAD 1
+#define IS_ORPHAN 2
+
static void copy_or_rename_branch(const char *oldname, const char *newname, int copy, int force)
{
struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
const char *interpreted_oldname = NULL;
const char *interpreted_newname = NULL;
- int recovery = 0;
+ int recovery = 0, oldref_usage = 0;
+ struct worktree **worktrees = get_worktrees();
if (strbuf_check_branch_ref(&oldref, oldname)) {
/*
@@ -554,8 +587,19 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
die(_("Invalid branch name: '%s'"), oldname);
}
- if ((copy || strcmp(head, oldname)) && !ref_exists(oldref.buf)) {
- if (copy && !strcmp(head, oldname))
+ for (int i = 0; worktrees[i]; i++) {
+ struct worktree *wt = worktrees[i];
+
+ if (wt->head_ref && !strcmp(oldref.buf, wt->head_ref)) {
+ oldref_usage |= IS_HEAD;
+ if (is_null_oid(&wt->head_oid))
+ oldref_usage |= IS_ORPHAN;
+ break;
+ }
+ }
+
+ if ((copy || !(oldref_usage & IS_HEAD)) && !ref_exists(oldref.buf)) {
+ if (oldref_usage & IS_HEAD)
die(_("No commit on branch '%s' yet."), oldname);
else
die(_("No branch named '%s'."), oldname);
@@ -570,7 +614,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
else
validate_new_branchname(newname, &newref, force);
- reject_rebase_or_bisect_branch(oldref.buf);
+ reject_rebase_or_bisect_branch(worktrees, oldref.buf);
if (!skip_prefix(oldref.buf, "refs/heads/", &interpreted_oldname) ||
!skip_prefix(newref.buf, "refs/heads/", &interpreted_newname)) {
@@ -584,8 +628,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
strbuf_addf(&logmsg, "Branch: renamed %s to %s",
oldref.buf, newref.buf);
- if (!copy &&
- (!head || strcmp(oldname, head) || !is_null_oid(&head_oid)) &&
+ if (!copy && !(oldref_usage & IS_ORPHAN) &&
rename_ref(oldref.buf, newref.buf, logmsg.buf))
die(_("Branch rename failed"));
if (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf))
@@ -600,8 +643,9 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
interpreted_oldname);
}
- if (!copy &&
- replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
+ if (!copy && (oldref_usage & IS_HEAD) &&
+ replace_each_worktree_head_symref(worktrees, oldref.buf, newref.buf,
+ logmsg.buf))
die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
strbuf_release(&logmsg);
@@ -616,6 +660,7 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
strbuf_release(&newref);
strbuf_release(&oldsection);
strbuf_release(&newsection);
+ free_worktrees(worktrees);
}
static GIT_PATH_FUNC(edit_description, "EDIT_DESCRIPTION")
@@ -629,7 +674,7 @@ static int edit_branch_description(const char *branch_name)
exists = !read_branch_desc(&buf, branch_name);
if (!buf.len || buf.buf[buf.len-1] != '\n')
strbuf_addch(&buf, '\n');
- strbuf_commented_addf(&buf,
+ strbuf_commented_addf(&buf, comment_line_char,
_("Please edit the description for the branch\n"
" %s\n"
"Lines starting with '%c' will be stripped.\n"),
@@ -640,7 +685,7 @@ static int edit_branch_description(const char *branch_name)
strbuf_release(&buf);
return -1;
}
- strbuf_stripspace(&buf, 1);
+ strbuf_stripspace(&buf, comment_line_char);
strbuf_addf(&name, "branch.%s.description", branch_name);
if (buf.len || exists)
@@ -662,7 +707,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
int reflog = 0, quiet = 0, icase = 0, force = 0,
recurse_submodules_explicit = 0;
enum branch_track track;
- struct ref_filter filter;
+ struct ref_filter filter = REF_FILTER_INIT;
static struct ref_sorting *sorting;
struct string_list sorting_options = STRING_LIST_INIT_DUP;
struct ref_format format = REF_FORMAT_INIT;
@@ -720,7 +765,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
setup_ref_filter_porcelain_msg();
- memset(&filter, 0, sizeof(filter));
filter.kind = FILTER_REFS_BRANCHES;
filter.abbrev = -1;
@@ -814,6 +858,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
print_columns(&output, colopts, NULL);
string_list_clear(&output, 0);
ref_sorting_release(sorting);
+ ref_filter_clear(&filter);
return 0;
} else if (edit_description) {
const char *branch_name;
@@ -834,7 +879,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
strbuf_addf(&branch_ref, "refs/heads/%s", branch_name);
if (!ref_exists(branch_ref.buf))
- error((!argc || !strcmp(head, branch_name))
+ error((!argc || branch_checked_out(branch_ref.buf))
? _("No commit on branch '%s' yet.")
: _("No branch named '%s'."),
branch_name);
@@ -879,7 +924,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
}
if (!ref_exists(branch->refname)) {
- if (!argc || !strcmp(head, branch->name))
+ if (!argc || branch_checked_out(branch->refname))
die(_("No commit on branch '%s' yet."), branch->name);
die(_("branch '%s' does not exist"), branch->name);
}
diff --git a/builtin/bundle.c b/builtin/bundle.c
index e68fc83d94..3f63631c03 100644
--- a/builtin/bundle.c
+++ b/builtin/bundle.c
@@ -4,7 +4,8 @@
#include "setup.h"
#include "strvec.h"
#include "parse-options.h"
-#include "cache.h"
+#include "pkt-line.h"
+#include "repository.h"
#include "bundle.h"
/*
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 0bafc14e6c..8ce5492d55 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -4,11 +4,10 @@
* Copyright (C) Linus Torvalds, 2005
*/
#define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
#include "alloc.h"
#include "config.h"
#include "convert.h"
-#include "builtin.h"
#include "diff.h"
#include "environment.h"
#include "gettext.h"
@@ -22,9 +21,10 @@
#include "packfile.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "replace-object.h"
#include "promisor-remote.h"
+#include "quote.h"
#include "mailmap.h"
#include "write-or-die.h"
@@ -470,8 +470,17 @@ static void batch_object_write(const char *obj_name,
&data->oid, &data->info,
OBJECT_INFO_LOOKUP_REPLACE);
if (ret < 0) {
+ struct strbuf quoted = STRBUF_INIT;
+
+ if (opt->nul_terminated &&
+ obj_name) {
+ quote_c_style(obj_name, &quoted, NULL, 0);
+ obj_name = quoted.buf;
+ }
+
printf("%s missing\n",
obj_name ? obj_name : oid_to_hex(&data->oid));
+ strbuf_release(&quoted);
fflush(stdout);
return;
}
@@ -518,6 +527,13 @@ static void batch_one_object(const char *obj_name,
result = get_oid_with_context(the_repository, obj_name,
flags, &data->oid, &ctx);
if (result != FOUND) {
+ struct strbuf quoted = STRBUF_INIT;
+
+ if (opt->nul_terminated) {
+ quote_c_style(obj_name, &quoted, NULL, 0);
+ obj_name = quoted.buf;
+ }
+
switch (result) {
case MISSING_OBJECT:
printf("%s missing\n", obj_name);
@@ -542,6 +558,8 @@ static void batch_one_object(const char *obj_name,
result);
break;
}
+
+ strbuf_release(&quoted);
fflush(stdout);
return;
}
diff --git a/builtin/check-attr.c b/builtin/check-attr.c
index 037bf1aaa2..b22ff748c3 100644
--- a/builtin/check-attr.c
+++ b/builtin/check-attr.c
@@ -1,12 +1,12 @@
#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
-#include "cache.h"
#include "config.h"
#include "attr.h"
#include "environment.h"
#include "gettext.h"
#include "object-name.h"
#include "quote.h"
+#include "repository.h"
#include "setup.h"
#include "parse-options.h"
#include "write-or-die.h"
@@ -63,7 +63,7 @@ static void output_attr(struct attr_check *check, const char *file)
}
static void check_attr(const char *prefix, struct attr_check *check,
- const struct object_id *tree_oid, int collect_all,
+ int collect_all,
const char *file)
{
@@ -71,9 +71,9 @@ static void check_attr(const char *prefix, struct attr_check *check,
prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
if (collect_all) {
- git_all_attrs(&the_index, tree_oid, full_path, check);
+ git_all_attrs(&the_index, full_path, check);
} else {
- git_check_attr(&the_index, tree_oid, full_path, check);
+ git_check_attr(&the_index, full_path, check);
}
output_attr(check, file);
@@ -81,7 +81,7 @@ static void check_attr(const char *prefix, struct attr_check *check,
}
static void check_attr_stdin_paths(const char *prefix, struct attr_check *check,
- const struct object_id *tree_oid, int collect_all)
+ int collect_all)
{
struct strbuf buf = STRBUF_INIT;
struct strbuf unquoted = STRBUF_INIT;
@@ -95,7 +95,7 @@ static void check_attr_stdin_paths(const char *prefix, struct attr_check *check,
die("line is badly quoted");
strbuf_swap(&buf, &unquoted);
}
- check_attr(prefix, check, tree_oid, collect_all, buf.buf);
+ check_attr(prefix, check, collect_all, buf.buf);
maybe_flush_or_die(stdout, "attribute to stdout");
}
strbuf_release(&buf);
@@ -111,7 +111,6 @@ static NORETURN void error_with_usage(const char *msg)
int cmd_check_attr(int argc, const char **argv, const char *prefix)
{
struct attr_check *check;
- struct object_id *tree_oid = NULL;
struct object_id initialized_oid;
int cnt, i, doubledash, filei;
@@ -187,14 +186,14 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
if (source) {
if (repo_get_oid_tree(the_repository, source, &initialized_oid))
die("%s: not a valid tree-ish source", source);
- tree_oid = &initialized_oid;
+ set_git_attr_source(source);
}
if (stdin_paths)
- check_attr_stdin_paths(prefix, check, tree_oid, all_attrs);
+ check_attr_stdin_paths(prefix, check, all_attrs);
else {
for (i = filei; i < argc; i++)
- check_attr(prefix, check, tree_oid, all_attrs, argv[i]);
+ check_attr(prefix, check, all_attrs, argv[i]);
maybe_flush_or_die(stdout, "attribute to stdout");
}
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 9401dad007..906cd96753 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -1,12 +1,12 @@
#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
-#include "cache.h"
#include "config.h"
#include "dir.h"
#include "gettext.h"
#include "quote.h"
#include "pathspec.h"
#include "parse-options.h"
+#include "repository.h"
#include "submodule.h"
#include "write-or-die.h"
diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c
index 002d2941e9..b8a05b8e07 100644
--- a/builtin/check-mailmap.c
+++ b/builtin/check-mailmap.c
@@ -4,6 +4,7 @@
#include "ident.h"
#include "mailmap.h"
#include "parse-options.h"
+#include "strbuf.h"
#include "string-list.h"
#include "write-or-die.h"
diff --git a/builtin/check-ref-format.c b/builtin/check-ref-format.c
index 57f0505070..5eb6bdc3f6 100644
--- a/builtin/check-ref-format.c
+++ b/builtin/check-ref-format.c
@@ -2,9 +2,8 @@
* GIT - The information manager from hell
*/
-#include "cache.h"
-#include "refs.h"
#include "builtin.h"
+#include "refs.h"
#include "setup.h"
#include "strbuf.h"
diff --git a/builtin/checkout--worker.c b/builtin/checkout--worker.c
index 2120dd1d30..c655dc4b13 100644
--- a/builtin/checkout--worker.c
+++ b/builtin/checkout--worker.c
@@ -6,6 +6,7 @@
#include "parallel-checkout.h"
#include "parse-options.h"
#include "pkt-line.h"
+#include "read-cache-ll.h"
static void packet_to_pc_item(const char *buffer, int len,
struct parallel_checkout_item *pc_item)
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index 7df673e3e7..f62f13f2b5 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -11,11 +11,14 @@
#include "gettext.h"
#include "lockfile.h"
#include "quote.h"
+#include "repository.h"
#include "cache-tree.h"
#include "parse-options.h"
#include "entry.h"
#include "parallel-checkout.h"
+#include "read-cache-ll.h"
#include "setup.h"
+#include "sparse-index.h"
#define CHECKOUT_ALL 4
static int nul_term_line;
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 6f5d82ed3d..d6a8ed9ab8 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -13,13 +13,16 @@
#include "gettext.h"
#include "hex.h"
#include "hook.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
#include "lockfile.h"
#include "mem-pool.h"
#include "merge-recursive.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "parse-options.h"
+#include "path.h"
+#include "preload-index.h"
+#include "read-cache.h"
#include "refs.h"
#include "remote.h"
#include "resolve-undo.h"
@@ -28,6 +31,7 @@
#include "setup.h"
#include "submodule.h"
#include "submodule-config.h"
+#include "symlinks.h"
#include "trace2.h"
#include "tree.h"
#include "tree-walk.h"
@@ -83,6 +87,7 @@ struct checkout_opts {
int ignore_unmerged;
int pathspec_file_nul;
char *pathspec_from_file;
+ int recurse_submodules;
const char *new_branch;
const char *new_branch_force;
@@ -860,7 +865,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
* entries in the index.
*/
- add_files_to_cache(NULL, NULL, 0);
+ add_files_to_cache(the_repository, NULL, NULL, 0, 0);
init_merge_options(&o, the_repository);
o.verbosity = 0;
work = write_in_core_index_as_tree(the_repository);
@@ -1505,7 +1510,8 @@ static void die_if_some_operation_in_progress(void)
}
static int checkout_branch(struct checkout_opts *opts,
- struct branch_info *new_branch_info)
+ struct branch_info *new_branch_info,
+ char *check_branch_path)
{
if (opts->pathspec.nr)
die(_("paths cannot be used with switching branches"));
@@ -1564,13 +1570,13 @@ static int checkout_branch(struct checkout_opts *opts,
if (!opts->can_switch_when_in_progress)
die_if_some_operation_in_progress();
- if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
- !opts->ignore_other_worktrees) {
+ if (!opts->ignore_other_worktrees && !opts->force_detach &&
+ check_branch_path && ref_exists(check_branch_path)) {
int flag;
char *head_ref = resolve_refdup("HEAD", 0, NULL, &flag);
- if (head_ref &&
- (!(flag & REF_ISSYMREF) || strcmp(head_ref, new_branch_info->path)))
- die_if_checked_out(new_branch_info->path, 1);
+ if (opts->new_branch_force || (head_ref &&
+ (!(flag & REF_ISSYMREF) || strcmp(head_ref, check_branch_path))))
+ die_if_checked_out(check_branch_path, 1);
free(head_ref);
}
@@ -1590,9 +1596,8 @@ static struct option *add_common_options(struct checkout_opts *opts,
{
struct option options[] = {
OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
- OPT_CALLBACK_F(0, "recurse-submodules", NULL,
- "checkout", "control recursive updating of submodules",
- PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater),
+ OPT_BOOL(0, "recurse-submodules", &opts->recurse_submodules,
+ N_("control recursive updating of submodules")),
OPT_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
@@ -1600,6 +1605,11 @@ static struct option *add_common_options(struct checkout_opts *opts,
OPT_END()
};
struct option *newopts = parse_options_concat(prevopts, options);
+ /*
+ * we only want to act on --recurse-submodules if it was set explicitly,
+ * so put it into unset third state.
+ */
+ opts->recurse_submodules = -1;
free(prevopts);
return newopts;
}
@@ -1658,7 +1668,9 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
const char * const usagestr[],
struct branch_info *new_branch_info)
{
+ int ret;
int parseopt_flags = 0;
+ char *check_branch_path = NULL;
opts->overwrite_ignore = 1;
opts->prefix = prefix;
@@ -1680,6 +1692,9 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
argc = parse_options(argc, argv, prefix, options,
usagestr, parseopt_flags);
+ if (opts->recurse_submodules >= 0)
+ set_config_update_recurse_submodules(opts->recurse_submodules);
+
if (opts->show_progress < 0) {
if (opts->quiet)
opts->show_progress = 0;
@@ -1748,6 +1763,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
opts->new_branch = argv0 + 1;
}
+ if (opts->new_branch && !opts->ignore_other_worktrees) {
+ struct strbuf buf = STRBUF_INIT;
+
+ strbuf_branchname(&buf, opts->new_branch, INTERPRET_BRANCH_LOCAL);
+ strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
+ check_branch_path = strbuf_detach(&buf, NULL);
+ }
/*
* Extract branch name from command line arguments, so
* all that is left is pathspecs.
@@ -1772,6 +1794,9 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
new_branch_info, opts, &rev);
argv += n;
argc -= n;
+
+ if (!opts->ignore_other_worktrees && !check_branch_path && new_branch_info->path)
+ check_branch_path = xstrdup(new_branch_info->path);
} else if (!opts->accept_ref && opts->from_treeish) {
struct object_id rev;
@@ -1848,9 +1873,12 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
}
if (opts->patch_mode || opts->pathspec.nr)
- return checkout_paths(opts, new_branch_info);
+ ret = checkout_paths(opts, new_branch_info);
else
- return checkout_branch(opts, new_branch_info);
+ ret = checkout_branch(opts, new_branch_info, check_branch_path);
+
+ free(check_branch_path);
+ return ret;
}
int cmd_checkout(int argc, const char **argv, const char *prefix)
diff --git a/builtin/clean.c b/builtin/clean.c
index 14c0d555ea..1bb6b7965c 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -9,11 +9,13 @@
#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "abspath.h"
-#include "cache.h"
#include "config.h"
#include "dir.h"
#include "gettext.h"
#include "parse-options.h"
+#include "path.h"
+#include "read-cache-ll.h"
+#include "repository.h"
#include "setup.h"
#include "string-list.h"
#include "quote.h"
diff --git a/builtin/clone.c b/builtin/clone.c
index 186845ef0b..687a686269 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -13,6 +13,7 @@
#include "abspath.h"
#include "advice.h"
#include "config.h"
+#include "copy.h"
#include "environment.h"
#include "gettext.h"
#include "hex.h"
@@ -22,7 +23,7 @@
#include "refs.h"
#include "refspec.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "tree.h"
#include "tree-walk.h"
#include "unpack-trees.h"
@@ -38,6 +39,8 @@
#include "setup.h"
#include "connected.h"
#include "packfile.h"
+#include "path.h"
+#include "pkt-line.h"
#include "list-objects-filter-options.h"
#include "hook.h"
#include "bundle.h"
@@ -928,6 +931,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
int submodule_progress;
int filter_submodules = 0;
int hash_algo;
+ const int do_not_override_repo_unix_permissions = -1;
struct transport_ls_refs_options transport_ls_refs_options =
TRANSPORT_LS_REFS_OPTIONS_INIT;
@@ -1095,7 +1099,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
}
init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, NULL,
- INIT_DB_QUIET);
+ do_not_override_repo_unix_permissions, INIT_DB_QUIET);
if (real_git_dir) {
free((char *)git_dir);
diff --git a/builtin/column.c b/builtin/column.c
index de623a16c2..ce13ff0baa 100644
--- a/builtin/column.c
+++ b/builtin/column.c
@@ -1,5 +1,4 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
#include "gettext.h"
#include "strbuf.h"
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index a3d00fa232..48fa9f20c9 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -1,4 +1,5 @@
#include "builtin.h"
+#include "commit.h"
#include "config.h"
#include "dir.h"
#include "environment.h"
@@ -8,7 +9,7 @@
#include "parse-options.h"
#include "repository.h"
#include "commit-graph.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "progress.h"
#include "replace-object.h"
#include "tag.h"
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index d1d251c3de..02625e7176 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -3,16 +3,15 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
#include "gettext.h"
#include "hex.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "repository.h"
#include "commit.h"
#include "tree.h"
-#include "builtin.h"
#include "utf8.h"
#include "gpg-interface.h"
#include "parse-options.h"
diff --git a/builtin/commit.c b/builtin/commit.c
index e67c4be221..56a3602ffa 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -6,7 +6,7 @@
*/
#define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
#include "advice.h"
#include "config.h"
#include "lockfile.h"
@@ -15,7 +15,6 @@
#include "dir.h"
#include "editor.h"
#include "environment.h"
-#include "builtin.h"
#include "diff.h"
#include "diffcore.h"
#include "commit.h"
@@ -30,6 +29,9 @@
#include "utf8.h"
#include "object-name.h"
#include "parse-options.h"
+#include "path.h"
+#include "preload-index.h"
+#include "read-cache.h"
#include "string-list.h"
#include "rerere.h"
#include "unpack-trees.h"
@@ -38,6 +40,7 @@
#include "gpg-interface.h"
#include "column.h"
#include "sequencer.h"
+#include "sparse-index.h"
#include "mailmap.h"
#include "help.h"
#include "commit-reach.h"
@@ -447,7 +450,8 @@ static const char *prepare_index(const char **argv, const char *prefix,
if (all || (also && pathspec.nr)) {
repo_hold_locked_index(the_repository, &index_lock,
LOCK_DIE_ON_ERROR);
- add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
+ add_files_to_cache(the_repository, also ? prefix : NULL,
+ &pathspec, 0, 0);
refresh_cache_or_die(refresh_flags);
cache_tree_update(&the_index, WRITE_TREE_SILENT);
if (write_locked_index(&the_index, &index_lock, 0))
@@ -893,7 +897,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
s->hints = 0;
if (clean_message_contents)
- strbuf_stripspace(&sb, 0);
+ strbuf_stripspace(&sb, '\0');
if (signoff)
append_signoff(&sb, ignore_non_trailer(sb.buf, sb.len), 0);
diff --git a/builtin/config.c b/builtin/config.c
index 9401f1e5e3..30f6d9fe67 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -9,8 +9,10 @@
#include "ident.h"
#include "parse-options.h"
#include "urlmatch.h"
+#include "path.h"
#include "quote.h"
#include "setup.h"
+#include "strbuf.h"
#include "worktree.h"
#include "wrapper.h"
diff --git a/builtin/count-objects.c b/builtin/count-objects.c
index f3d8f1bcbb..97cdfb0ac5 100644
--- a/builtin/count-objects.c
+++ b/builtin/count-objects.c
@@ -4,17 +4,17 @@
* Copyright (c) 2006 Junio C Hamano
*/
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
#include "dir.h"
#include "environment.h"
#include "gettext.h"
+#include "path.h"
#include "repository.h"
-#include "builtin.h"
#include "parse-options.h"
#include "quote.h"
#include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.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 4e571d9951..756c5f02ae 100644
--- a/builtin/credential-cache--daemon.c
+++ b/builtin/credential-cache--daemon.c
@@ -134,6 +134,9 @@ static void serve_one_client(FILE *in, FILE *out)
if (e->item.password_expiry_utc != TIME_MAX)
fprintf(out, "password_expiry_utc=%"PRItime"\n",
e->item.password_expiry_utc);
+ if (e->item.oauth_refresh_token)
+ fprintf(out, "oauth_refresh_token=%s\n",
+ e->item.oauth_refresh_token);
}
}
else if (!strcmp(action.buf, "exit")) {
diff --git a/builtin/credential-cache.c b/builtin/credential-cache.c
index 508da4c6e4..ff3a47badb 100644
--- a/builtin/credential-cache.c
+++ b/builtin/credential-cache.c
@@ -1,6 +1,8 @@
#include "builtin.h"
#include "gettext.h"
#include "parse-options.h"
+#include "path.h"
+#include "strbuf.h"
#include "wrapper.h"
#include "write-or-die.h"
diff --git a/builtin/credential-store.c b/builtin/credential-store.c
index 8977604eb9..5eea5bdb58 100644
--- a/builtin/credential-store.c
+++ b/builtin/credential-store.c
@@ -3,6 +3,7 @@
#include "gettext.h"
#include "lockfile.h"
#include "credential.h"
+#include "path.h"
#include "string-list.h"
#include "parse-options.h"
#include "write-or-die.h"
@@ -73,6 +74,25 @@ static void rewrite_credential_file(const char *fn, struct credential *c,
die_errno("unable to write credential store");
}
+static int is_rfc3986_unreserved(char ch)
+{
+ return isalnum(ch) ||
+ ch == '-' || ch == '_' || ch == '.' || ch == '~';
+}
+
+static int is_rfc3986_reserved_or_unreserved(char ch)
+{
+ if (is_rfc3986_unreserved(ch))
+ return 1;
+ switch (ch) {
+ case '!': case '*': case '\'': case '(': case ')': case ';':
+ case ':': case '@': case '&': case '=': case '+': case '$':
+ case ',': case '/': case '?': case '#': case '[': case ']':
+ return 1;
+ }
+ return 0;
+}
+
static void store_credential_file(const char *fn, struct credential *c)
{
struct strbuf buf = STRBUF_INIT;
diff --git a/builtin/describe.c b/builtin/describe.c
index 55b4baaa22..7ce23e1b8e 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -1,5 +1,5 @@
#define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
#include "environment.h"
#include "gettext.h"
@@ -9,19 +9,20 @@
#include "tag.h"
#include "blob.h"
#include "refs.h"
-#include "builtin.h"
#include "exec-cmd.h"
#include "object-name.h"
#include "parse-options.h"
+#include "read-cache-ll.h"
#include "revision.h"
#include "diff.h"
#include "hashmap.h"
#include "setup.h"
#include "strvec.h"
#include "run-command.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "list-objects.h"
#include "commit-slab.h"
+#include "wildmatch.h"
#define MAX_TAGS (FLAG_BITS - 1)
diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index dc991f753b..50330b8dd2 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -3,13 +3,14 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
#include "diff.h"
#include "diff-merges.h"
#include "commit.h"
+#include "preload-index.h"
+#include "repository.h"
#include "revision.h"
-#include "builtin.h"
#include "submodule.h"
static const char diff_files_usage[] =
@@ -27,6 +28,10 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
usage(diff_files_usage);
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
repo_init_revisions(the_repository, &rev, prefix);
rev.abbrev = 0;
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index b9a19bb7d3..871658f828 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -1,11 +1,13 @@
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
#include "diff.h"
#include "diff-merges.h"
#include "commit.h"
+#include "preload-index.h"
+#include "repository.h"
#include "revision.h"
-#include "builtin.h"
#include "setup.h"
+#include "sparse-index.h"
#include "submodule.h"
static const char diff_cache_usage[] =
@@ -25,6 +27,14 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
usage(diff_cache_usage);
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
+
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
+ if (pathspec_needs_expanded_index(the_repository->index,
+ &rev.diffopt.pathspec))
+ ensure_full_index(the_repository->index);
+
repo_init_revisions(the_repository, &rev, prefix);
rev.abbrev = 0;
prefix = precompose_argv_prefix(argc, argv, prefix);
@@ -70,6 +80,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
perror("repo_read_index");
return -1;
}
+
result = run_diff_index(&rev, option);
result = diff_result_code(&rev.diffopt, result);
release_revisions(&rev);
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index 385c2d0230..4ad55ecc3f 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -1,14 +1,16 @@
#define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
#include "diff.h"
#include "commit.h"
#include "gettext.h"
#include "hex.h"
#include "log-tree.h"
-#include "builtin.h"
#include "submodule.h"
+#include "read-cache-ll.h"
#include "repository.h"
+#include "revision.h"
+#include "tree.h"
static struct rev_info log_tree_opt;
diff --git a/builtin/diff.c b/builtin/diff.c
index 5a6a5d7f4b..b19530c996 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -4,7 +4,7 @@
* Copyright (c) 2006 Junio C Hamano
*/
#define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
#include "ewah/ewok.h"
#include "lockfile.h"
@@ -16,12 +16,14 @@
#include "diff.h"
#include "diff-merges.h"
#include "diffcore.h"
+#include "preload-index.h"
+#include "read-cache-ll.h"
#include "revision.h"
#include "log-tree.h"
-#include "builtin.h"
#include "setup.h"
#include "submodule.h"
#include "oid-array.h"
+#include "tree.h"
#define DIFF_NO_INDEX_EXPLICIT 1
#define DIFF_NO_INDEX_IMPLICIT 2
diff --git a/builtin/difftool.c b/builtin/difftool.c
index f09d24d37f..e2c9ab7d5d 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -12,21 +12,23 @@
* Copyright (C) 2016 Johannes Schindelin
*/
#define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
#include "abspath.h"
#include "config.h"
-#include "builtin.h"
+#include "copy.h"
#include "run-command.h"
#include "environment.h"
#include "exec-cmd.h"
#include "gettext.h"
#include "hex.h"
#include "parse-options.h"
+#include "read-cache-ll.h"
+#include "sparse-index.h"
#include "strvec.h"
#include "strbuf.h"
#include "lockfile.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "dir.h"
#include "entry.h"
#include "setup.h"
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 9a95f6a1a8..56dc69fac1 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -4,14 +4,13 @@
* Copyright (C) 2007 Johannes E. Schindelin
*/
#include "builtin.h"
-#include "cache.h"
#include "config.h"
#include "gettext.h"
#include "hex.h"
#include "refs.h"
#include "refspec.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "commit.h"
#include "object.h"
#include "tag.h"
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index bbd9b2b3e7..2ee19c7373 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -1,6 +1,5 @@
#include "builtin.h"
#include "abspath.h"
-#include "cache.h"
#include "environment.h"
#include "gettext.h"
#include "hex.h"
@@ -13,6 +12,7 @@
#include "commit.h"
#include "delta.h"
#include "pack.h"
+#include "path.h"
#include "refs.h"
#include "csum-file.h"
#include "quote.h"
@@ -21,7 +21,7 @@
#include "packfile.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "mem-pool.h"
#include "commit-reach.h"
#include "khash.h"
diff --git a/builtin/fetch.c b/builtin/fetch.c
index ab623f41b4..951a23d733 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -1,7 +1,7 @@
/*
* "git fetch"
*/
-#include "cache.h"
+#include "builtin.h"
#include "advice.h"
#include "config.h"
#include "gettext.h"
@@ -11,11 +11,10 @@
#include "refs.h"
#include "refspec.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "oidset.h"
#include "oid-array.h"
#include "commit.h"
-#include "builtin.h"
#include "string-list.h"
#include "remote.h"
#include "transport.h"
@@ -29,6 +28,8 @@
#include "utf8.h"
#include "packfile.h"
#include "pager.h"
+#include "path.h"
+#include "pkt-line.h"
#include "list-objects-filter-options.h"
#include "commit-reach.h"
#include "branch.h"
@@ -56,35 +57,35 @@ enum {
TAGS_SET = 2
};
+enum display_format {
+ DISPLAY_FORMAT_FULL,
+ DISPLAY_FORMAT_COMPACT,
+ DISPLAY_FORMAT_PORCELAIN,
+};
+
struct display_state {
struct strbuf buf;
int refcol_width;
- int compact_format;
+ enum display_format format;
char *url;
int url_len, shown_url;
};
-static int fetch_prune_config = -1; /* unspecified */
-static int fetch_show_forced_updates = 1;
static uint64_t forced_updates_ms = 0;
static int prefetch = 0;
static int prune = -1; /* unspecified */
#define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
-static int fetch_prune_tags_config = -1; /* unspecified */
static int prune_tags = -1; /* unspecified */
#define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */
-static int all, append, dry_run, force, keep, multiple, update_head_ok;
+static int append, dry_run, force, keep, update_head_ok;
static int write_fetch_head = 1;
static int verbosity, deepen_relative, set_upstream, refetch;
static int progress = -1;
-static int enable_auto_gc = 1;
-static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
-static int max_jobs = -1, submodule_fetch_jobs_config = -1;
-static int fetch_parallel_config = 1;
+static int tags = TAGS_DEFAULT, update_shallow, deepen;
static int atomic_fetch;
static enum transport_family family;
static const char *depth;
@@ -94,58 +95,75 @@ static struct string_list deepen_not = STRING_LIST_INIT_NODUP;
static struct strbuf default_rla = STRBUF_INIT;
static struct transport *gtransport;
static struct transport *gsecondary;
-static const char *submodule_prefix = "";
-static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
-static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
-static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
static struct refspec refmap = REFSPEC_INIT_FETCH;
static struct list_objects_filter_options filter_options = LIST_OBJECTS_FILTER_INIT;
static struct string_list server_options = STRING_LIST_INIT_DUP;
static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP;
-static int fetch_write_commit_graph = -1;
-static int stdin_refspecs = 0;
-static int negotiate_only;
+
+struct fetch_config {
+ enum display_format display_format;
+ int prune;
+ int prune_tags;
+ int show_forced_updates;
+ int recurse_submodules;
+ int parallel;
+ int submodule_fetch_jobs;
+};
static int git_fetch_config(const char *k, const char *v, void *cb)
{
+ struct fetch_config *fetch_config = cb;
+
if (!strcmp(k, "fetch.prune")) {
- fetch_prune_config = git_config_bool(k, v);
+ fetch_config->prune = git_config_bool(k, v);
return 0;
}
if (!strcmp(k, "fetch.prunetags")) {
- fetch_prune_tags_config = git_config_bool(k, v);
+ fetch_config->prune_tags = git_config_bool(k, v);
return 0;
}
if (!strcmp(k, "fetch.showforcedupdates")) {
- fetch_show_forced_updates = git_config_bool(k, v);
+ fetch_config->show_forced_updates = git_config_bool(k, v);
return 0;
}
if (!strcmp(k, "submodule.recurse")) {
int r = git_config_bool(k, v) ?
RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
- recurse_submodules = r;
+ fetch_config->recurse_submodules = r;
}
if (!strcmp(k, "submodule.fetchjobs")) {
- submodule_fetch_jobs_config = parse_submodule_fetchjobs(k, v);
+ fetch_config->submodule_fetch_jobs = parse_submodule_fetchjobs(k, v);
return 0;
} else if (!strcmp(k, "fetch.recursesubmodules")) {
- recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
+ fetch_config->recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
return 0;
}
if (!strcmp(k, "fetch.parallel")) {
- fetch_parallel_config = git_config_int(k, v);
- if (fetch_parallel_config < 0)
+ fetch_config->parallel = git_config_int(k, v);
+ if (fetch_config->parallel < 0)
die(_("fetch.parallel cannot be negative"));
- if (!fetch_parallel_config)
- fetch_parallel_config = online_cpus();
+ if (!fetch_config->parallel)
+ fetch_config->parallel = online_cpus();
return 0;
}
+ if (!strcmp(k, "fetch.output")) {
+ if (!v)
+ return config_error_nonbool(k);
+ else if (!strcasecmp(v, "full"))
+ fetch_config->display_format = DISPLAY_FORMAT_FULL;
+ else if (!strcasecmp(v, "compact"))
+ fetch_config->display_format = DISPLAY_FORMAT_COMPACT;
+ else
+ die(_("invalid value for '%s': '%s'"),
+ "fetch.output", v);
+ }
+
return git_default_config(k, v, cb);
}
@@ -162,92 +180,6 @@ static int parse_refmap_arg(const struct option *opt, const char *arg, int unset
return 0;
}
-static struct option builtin_fetch_options[] = {
- OPT__VERBOSITY(&verbosity),
- OPT_BOOL(0, "all", &all,
- N_("fetch from all remotes")),
- OPT_BOOL(0, "set-upstream", &set_upstream,
- N_("set upstream for git pull/fetch")),
- OPT_BOOL('a', "append", &append,
- N_("append to .git/FETCH_HEAD instead of overwriting")),
- OPT_BOOL(0, "atomic", &atomic_fetch,
- N_("use atomic transaction to update references")),
- OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
- N_("path to upload pack on remote end")),
- OPT__FORCE(&force, N_("force overwrite of local reference"), 0),
- OPT_BOOL('m', "multiple", &multiple,
- N_("fetch from multiple remotes")),
- OPT_SET_INT('t', "tags", &tags,
- N_("fetch all tags and associated objects"), TAGS_SET),
- OPT_SET_INT('n', NULL, &tags,
- N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
- OPT_INTEGER('j', "jobs", &max_jobs,
- N_("number of submodules fetched in parallel")),
- OPT_BOOL(0, "prefetch", &prefetch,
- N_("modify the refspec to place all refs within refs/prefetch/")),
- OPT_BOOL('p', "prune", &prune,
- N_("prune remote-tracking branches no longer on remote")),
- OPT_BOOL('P', "prune-tags", &prune_tags,
- N_("prune local tags no longer on remote and clobber changed tags")),
- OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules_cli, N_("on-demand"),
- N_("control recursive fetching of submodules"),
- PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
- OPT_BOOL(0, "dry-run", &dry_run,
- N_("dry run")),
- OPT_BOOL(0, "write-fetch-head", &write_fetch_head,
- N_("write fetched references to the FETCH_HEAD file")),
- OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
- OPT_BOOL('u', "update-head-ok", &update_head_ok,
- N_("allow updating of HEAD ref")),
- OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
- OPT_STRING(0, "depth", &depth, N_("depth"),
- N_("deepen history of shallow clone")),
- OPT_STRING(0, "shallow-since", &deepen_since, N_("time"),
- N_("deepen history of shallow repository based on time")),
- OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"),
- N_("deepen history of shallow clone, excluding rev")),
- OPT_INTEGER(0, "deepen", &deepen_relative,
- N_("deepen history of shallow clone")),
- OPT_SET_INT_F(0, "unshallow", &unshallow,
- N_("convert to a complete repository"),
- 1, PARSE_OPT_NONEG),
- OPT_SET_INT_F(0, "refetch", &refetch,
- N_("re-fetch without negotiating common commits"),
- 1, PARSE_OPT_NONEG),
- { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"),
- N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN },
- OPT_CALLBACK_F(0, "recurse-submodules-default",
- &recurse_submodules_default, N_("on-demand"),
- N_("default for recursive fetching of submodules "
- "(lower priority than config files)"),
- PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules),
- OPT_BOOL(0, "update-shallow", &update_shallow,
- N_("accept refs that update .git/shallow")),
- OPT_CALLBACK_F(0, "refmap", NULL, N_("refmap"),
- N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg),
- OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")),
- OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
- TRANSPORT_FAMILY_IPV4),
- OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
- TRANSPORT_FAMILY_IPV6),
- OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
- N_("report that we have only objects reachable from this object")),
- OPT_BOOL(0, "negotiate-only", &negotiate_only,
- N_("do not fetch a packfile; instead, print ancestors of negotiation tips")),
- OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
- OPT_BOOL(0, "auto-maintenance", &enable_auto_gc,
- N_("run 'maintenance --auto' after fetching")),
- OPT_BOOL(0, "auto-gc", &enable_auto_gc,
- N_("run 'maintenance --auto' after fetching")),
- OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates,
- N_("check for forced-updates on all updated branches")),
- OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph,
- N_("write the commit-graph after fetching")),
- OPT_BOOL(0, "stdin", &stdin_refspecs,
- N_("accept refspecs from stdin")),
- OPT_END()
-};
-
static void unlock_pack(unsigned int flags)
{
if (gtransport)
@@ -759,46 +691,56 @@ out:
return ret;
}
-static int refcol_width(const struct ref *ref, int compact_format)
+static int refcol_width(const struct ref *ref_map, int compact_format)
{
- int max, rlen, llen, len;
+ const struct ref *ref;
+ int max, width = 10;
- /* uptodate lines are only shown on high verbosity level */
- if (verbosity <= 0 && oideq(&ref->peer_ref->old_oid, &ref->old_oid))
- return 0;
+ max = term_columns();
+ if (compact_format)
+ max = max * 2 / 3;
- max = term_columns();
- rlen = utf8_strwidth(prettify_refname(ref->name));
+ for (ref = ref_map; ref; ref = ref->next) {
+ int rlen, llen = 0, len;
- llen = utf8_strwidth(prettify_refname(ref->peer_ref->name));
+ if (ref->status == REF_STATUS_REJECT_SHALLOW ||
+ !ref->peer_ref ||
+ !strcmp(ref->name, "HEAD"))
+ continue;
- /*
- * rough estimation to see if the output line is too long and
- * should not be counted (we can't do precise calculation
- * anyway because we don't know if the error explanation part
- * will be printed in update_local_ref)
- */
- if (compact_format) {
- llen = 0;
- max = max * 2 / 3;
+ /* uptodate lines are only shown on high verbosity level */
+ if (verbosity <= 0 && oideq(&ref->peer_ref->old_oid, &ref->old_oid))
+ continue;
+
+ rlen = utf8_strwidth(prettify_refname(ref->name));
+ if (!compact_format)
+ llen = utf8_strwidth(prettify_refname(ref->peer_ref->name));
+
+ /*
+ * rough estimation to see if the output line is too long and
+ * should not be counted (we can't do precise calculation
+ * anyway because we don't know if the error explanation part
+ * will be printed in update_local_ref)
+ */
+ len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;
+ if (len >= max)
+ continue;
+
+ if (width < rlen)
+ width = rlen;
}
- len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;
- if (len >= max)
- return 0;
- return rlen;
+ return width;
}
static void display_state_init(struct display_state *display_state, struct ref *ref_map,
- const char *raw_url)
+ const char *raw_url, enum display_format format)
{
- struct ref *rm;
- const char *format = "full";
int i;
memset(display_state, 0, sizeof(*display_state));
-
strbuf_init(&display_state->buf, 0);
+ display_state->format = format;
if (raw_url)
display_state->url = transport_anonymize_url(raw_url);
@@ -815,33 +757,17 @@ static void display_state_init(struct display_state *display_state, struct ref *
if (verbosity < 0)
return;
- git_config_get_string_tmp("fetch.output", &format);
- if (!strcasecmp(format, "full"))
- display_state->compact_format = 0;
- else if (!strcasecmp(format, "compact"))
- display_state->compact_format = 1;
- else
- die(_("invalid value for '%s': '%s'"),
- "fetch.output", format);
-
- display_state->refcol_width = 10;
- for (rm = ref_map; rm; rm = rm->next) {
- int width;
-
- if (rm->status == REF_STATUS_REJECT_SHALLOW ||
- !rm->peer_ref ||
- !strcmp(rm->name, "HEAD"))
- continue;
-
- width = refcol_width(rm, display_state->compact_format);
-
- /*
- * Not precise calculation for compact mode because '*' can
- * appear on the left hand side of '->' and shrink the column
- * back.
- */
- if (display_state->refcol_width < width)
- display_state->refcol_width = width;
+ switch (display_state->format) {
+ case DISPLAY_FORMAT_FULL:
+ case DISPLAY_FORMAT_COMPACT:
+ display_state->refcol_width = refcol_width(ref_map,
+ display_state->format == DISPLAY_FORMAT_COMPACT);
+ break;
+ case DISPLAY_FORMAT_PORCELAIN:
+ /* We don't need to precompute anything here. */
+ break;
+ default:
+ BUG("unexpected display format %d", display_state->format);
}
}
@@ -910,40 +836,63 @@ static void print_compact(struct display_state *display_state,
static void display_ref_update(struct display_state *display_state, char code,
const char *summary, const char *error,
const char *remote, const char *local,
+ const struct object_id *old_oid,
+ const struct object_id *new_oid,
int summary_width)
{
- int width;
+ FILE *f = stderr;
if (verbosity < 0)
return;
strbuf_reset(&display_state->buf);
- if (!display_state->shown_url) {
- strbuf_addf(&display_state->buf, _("From %.*s\n"),
- display_state->url_len, display_state->url);
- display_state->shown_url = 1;
- }
+ switch (display_state->format) {
+ case DISPLAY_FORMAT_FULL:
+ case DISPLAY_FORMAT_COMPACT: {
+ int width;
- width = (summary_width + strlen(summary) - gettext_width(summary));
+ if (!display_state->shown_url) {
+ strbuf_addf(&display_state->buf, _("From %.*s\n"),
+ display_state->url_len, display_state->url);
+ display_state->shown_url = 1;
+ }
- strbuf_addf(&display_state->buf, " %c %-*s ", code, width, summary);
- if (!display_state->compact_format)
- print_remote_to_local(display_state, remote, prettify_refname(local));
- else
- print_compact(display_state, remote, prettify_refname(local));
- if (error)
- strbuf_addf(&display_state->buf, " (%s)", error);
+ width = (summary_width + strlen(summary) - gettext_width(summary));
+ remote = prettify_refname(remote);
+ local = prettify_refname(local);
+
+ strbuf_addf(&display_state->buf, " %c %-*s ", code, width, summary);
+
+ if (display_state->format != DISPLAY_FORMAT_COMPACT)
+ print_remote_to_local(display_state, remote, local);
+ else
+ print_compact(display_state, remote, local);
+
+ if (error)
+ strbuf_addf(&display_state->buf, " (%s)", error);
+
+ break;
+ }
+ case DISPLAY_FORMAT_PORCELAIN:
+ strbuf_addf(&display_state->buf, "%c %s %s %s", code,
+ oid_to_hex(old_oid), oid_to_hex(new_oid), local);
+ f = stdout;
+ break;
+ default:
+ BUG("unexpected display format %d", display_state->format);
+ };
strbuf_addch(&display_state->buf, '\n');
- fputs(display_state->buf.buf, stderr);
+ fputs(display_state->buf.buf, f);
}
static int update_local_ref(struct ref *ref,
struct ref_transaction *transaction,
struct display_state *display_state,
- const char *remote, const struct ref *remote_ref,
- int summary_width)
+ const struct ref *remote_ref,
+ int summary_width,
+ const struct fetch_config *config)
{
struct commit *current = NULL, *updated;
int fast_forward = 0;
@@ -954,7 +903,8 @@ static int update_local_ref(struct ref *ref,
if (oideq(&ref->old_oid, &ref->new_oid)) {
if (verbosity > 0)
display_ref_update(display_state, '=', _("[up to date]"), NULL,
- remote, ref->name, summary_width);
+ remote_ref->name, ref->name,
+ &ref->old_oid, &ref->new_oid, summary_width);
return 0;
}
@@ -967,7 +917,8 @@ static int update_local_ref(struct ref *ref,
*/
display_ref_update(display_state, '!', _("[rejected]"),
_("can't fetch into checked-out branch"),
- remote, ref->name, summary_width);
+ remote_ref->name, ref->name,
+ &ref->old_oid, &ref->new_oid, summary_width);
return 1;
}
@@ -978,12 +929,14 @@ static int update_local_ref(struct ref *ref,
r = s_update_ref("updating tag", ref, transaction, 0);
display_ref_update(display_state, r ? '!' : 't', _("[tag update]"),
r ? _("unable to update local ref") : NULL,
- remote, ref->name, summary_width);
+ remote_ref->name, ref->name,
+ &ref->old_oid, &ref->new_oid, summary_width);
return r;
} else {
display_ref_update(display_state, '!', _("[rejected]"),
_("would clobber existing tag"),
- remote, ref->name, summary_width);
+ remote_ref->name, ref->name,
+ &ref->old_oid, &ref->new_oid, summary_width);
return 1;
}
}
@@ -1001,11 +954,10 @@ static int update_local_ref(struct ref *ref,
* Base this on the remote's ref name, as it's
* more likely to follow a standard layout.
*/
- const char *name = remote_ref ? remote_ref->name : "";
- if (starts_with(name, "refs/tags/")) {
+ if (starts_with(remote_ref->name, "refs/tags/")) {
msg = "storing tag";
what = _("[new tag]");
- } else if (starts_with(name, "refs/heads/")) {
+ } else if (starts_with(remote_ref->name, "refs/heads/")) {
msg = "storing head";
what = _("[new branch]");
} else {
@@ -1016,11 +968,12 @@ static int update_local_ref(struct ref *ref,
r = s_update_ref(msg, ref, transaction, 0);
display_ref_update(display_state, r ? '!' : '*', what,
r ? _("unable to update local ref") : NULL,
- remote, ref->name, summary_width);
+ remote_ref->name, ref->name,
+ &ref->old_oid, &ref->new_oid, summary_width);
return r;
}
- if (fetch_show_forced_updates) {
+ if (config->show_forced_updates) {
uint64_t t_before = getnanotime();
fast_forward = repo_in_merge_bases(the_repository, current,
updated);
@@ -1039,7 +992,8 @@ static int update_local_ref(struct ref *ref,
r = s_update_ref("fast-forward", ref, transaction, 1);
display_ref_update(display_state, r ? '!' : ' ', quickref.buf,
r ? _("unable to update local ref") : NULL,
- remote, ref->name, summary_width);
+ remote_ref->name, ref->name,
+ &ref->old_oid, &ref->new_oid, summary_width);
strbuf_release(&quickref);
return r;
} else if (force || ref->force) {
@@ -1051,12 +1005,14 @@ static int update_local_ref(struct ref *ref,
r = s_update_ref("forced-update", ref, transaction, 1);
display_ref_update(display_state, r ? '!' : '+', quickref.buf,
r ? _("unable to update local ref") : _("forced update"),
- remote, ref->name, summary_width);
+ remote_ref->name, ref->name,
+ &ref->old_oid, &ref->new_oid, summary_width);
strbuf_release(&quickref);
return r;
} else {
display_ref_update(display_state, '!', _("[rejected]"), _("non-fast-forward"),
- remote, ref->name, summary_width);
+ remote_ref->name, ref->name,
+ &ref->old_oid, &ref->new_oid, summary_width);
return 1;
}
}
@@ -1170,7 +1126,8 @@ static int store_updated_refs(struct display_state *display_state,
const char *remote_name,
int connectivity_checked,
struct ref_transaction *transaction, struct ref *ref_map,
- struct fetch_head *fetch_head)
+ struct fetch_head *fetch_head,
+ const struct fetch_config *config)
{
int rc = 0;
struct strbuf note = STRBUF_INIT;
@@ -1253,7 +1210,7 @@ static int store_updated_refs(struct display_state *display_state,
ref->force = rm->peer_ref->force;
}
- if (recurse_submodules != RECURSE_SUBMODULES_OFF &&
+ if (config->recurse_submodules != RECURSE_SUBMODULES_OFF &&
(!rm->peer_ref || !oideq(&ref->old_oid, &ref->new_oid))) {
check_for_new_submodule_commits(&rm->old_oid);
}
@@ -1261,14 +1218,13 @@ static int store_updated_refs(struct display_state *display_state,
if (!strcmp(rm->name, "HEAD")) {
kind = "";
what = "";
- }
- else if (skip_prefix(rm->name, "refs/heads/", &what))
+ } else if (skip_prefix(rm->name, "refs/heads/", &what)) {
kind = "branch";
- else if (skip_prefix(rm->name, "refs/tags/", &what))
+ } else if (skip_prefix(rm->name, "refs/tags/", &what)) {
kind = "tag";
- else if (skip_prefix(rm->name, "refs/remotes/", &what))
+ } else if (skip_prefix(rm->name, "refs/remotes/", &what)) {
kind = "remote-tracking branch";
- else {
+ } else {
kind = "";
what = rm->name;
}
@@ -1286,8 +1242,8 @@ static int store_updated_refs(struct display_state *display_state,
display_state->url_len);
if (ref) {
- rc |= update_local_ref(ref, transaction, display_state, what,
- rm, summary_width);
+ rc |= update_local_ref(ref, transaction, display_state,
+ rm, summary_width, config);
free(ref);
} else if (write_fetch_head || dry_run) {
/*
@@ -1297,8 +1253,10 @@ static int store_updated_refs(struct display_state *display_state,
*/
display_ref_update(display_state, '*',
*kind ? kind : "branch", NULL,
- *what ? what : "HEAD",
- "FETCH_HEAD", summary_width);
+ rm->name,
+ "FETCH_HEAD",
+ &rm->new_oid, &rm->old_oid,
+ summary_width);
}
}
}
@@ -1309,7 +1267,7 @@ static int store_updated_refs(struct display_state *display_state,
"branches"), remote_name);
if (advice_enabled(ADVICE_FETCH_SHOW_FORCED_UPDATES)) {
- if (!fetch_show_forced_updates) {
+ if (!config->show_forced_updates) {
warning(_(warn_show_forced_updates));
} else if (forced_updates_ms > FORCED_UPDATES_DELAY_WARNING_IN_MS) {
warning(_(warn_time_show_forced_updates),
@@ -1370,7 +1328,8 @@ static int fetch_and_consume_refs(struct display_state *display_state,
struct transport *transport,
struct ref_transaction *transaction,
struct ref *ref_map,
- struct fetch_head *fetch_head)
+ struct fetch_head *fetch_head,
+ const struct fetch_config *config)
{
int connectivity_checked = 1;
int ret;
@@ -1393,7 +1352,7 @@ static int fetch_and_consume_refs(struct display_state *display_state,
trace2_region_enter("fetch", "consume_refs", the_repository);
ret = store_updated_refs(display_state, transport->remote->name,
connectivity_checked, transaction, ref_map,
- fetch_head);
+ fetch_head, config);
trace2_region_leave("fetch", "consume_refs", the_repository);
out:
@@ -1438,6 +1397,7 @@ static int prune_refs(struct display_state *display_state,
for (ref = stale_refs; ref; ref = ref->next) {
display_ref_update(display_state, '-', _("[deleted]"), NULL,
_("(none)"), ref->name,
+ &ref->new_oid, &ref->old_oid,
summary_width);
warn_dangling_symref(stderr, dangling_msg, ref->name);
}
@@ -1563,7 +1523,8 @@ static int backfill_tags(struct display_state *display_state,
struct transport *transport,
struct ref_transaction *transaction,
struct ref *ref_map,
- struct fetch_head *fetch_head)
+ struct fetch_head *fetch_head,
+ const struct fetch_config *config)
{
int retcode, cannot_reuse;
@@ -1584,7 +1545,8 @@ static int backfill_tags(struct display_state *display_state,
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
- retcode = fetch_and_consume_refs(display_state, transport, transaction, ref_map, fetch_head);
+ retcode = fetch_and_consume_refs(display_state, transport, transaction, ref_map,
+ fetch_head, config);
if (gsecondary) {
transport_disconnect(gsecondary);
@@ -1595,7 +1557,8 @@ static int backfill_tags(struct display_state *display_state,
}
static int do_fetch(struct transport *transport,
- struct refspec *rs)
+ struct refspec *rs,
+ const struct fetch_config *config)
{
struct ref_transaction *transaction = NULL;
struct ref *ref_map = NULL;
@@ -1681,7 +1644,8 @@ static int do_fetch(struct transport *transport,
if (retcode)
goto cleanup;
- display_state_init(&display_state, ref_map, transport->url);
+ display_state_init(&display_state, ref_map, transport->url,
+ config->display_format);
if (atomic_fetch) {
transaction = ref_transaction_begin(&err);
@@ -1709,7 +1673,8 @@ static int do_fetch(struct transport *transport,
retcode = 1;
}
- if (fetch_and_consume_refs(&display_state, transport, transaction, ref_map, &fetch_head)) {
+ if (fetch_and_consume_refs(&display_state, transport, transaction, ref_map,
+ &fetch_head, config)) {
retcode = 1;
goto cleanup;
}
@@ -1732,7 +1697,7 @@ static int do_fetch(struct transport *transport,
* the transaction and don't commit anything.
*/
if (backfill_tags(&display_state, transport, transaction, tags_ref_map,
- &fetch_head))
+ &fetch_head, config))
retcode = 1;
}
@@ -1869,7 +1834,8 @@ static int add_remote_or_group(const char *name, struct string_list *list)
return 1;
}
-static void add_options_to_argv(struct strvec *argv)
+static void add_options_to_argv(struct strvec *argv,
+ const struct fetch_config *config)
{
if (dry_run)
strvec_push(argv, "--dry-run");
@@ -1883,9 +1849,11 @@ static void add_options_to_argv(struct strvec *argv)
strvec_push(argv, "--force");
if (keep)
strvec_push(argv, "--keep");
- if (recurse_submodules == RECURSE_SUBMODULES_ON)
+ if (config->recurse_submodules == RECURSE_SUBMODULES_ON)
strvec_push(argv, "--recurse-submodules");
- else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)
+ else if (config->recurse_submodules == RECURSE_SUBMODULES_OFF)
+ strvec_push(argv, "--no-recurse-submodules");
+ else if (config->recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)
strvec_push(argv, "--recurse-submodules=on-demand");
if (tags == TAGS_SET)
strvec_push(argv, "--tags");
@@ -1903,6 +1871,8 @@ static void add_options_to_argv(struct strvec *argv)
strvec_push(argv, "--ipv6");
if (!write_fetch_head)
strvec_push(argv, "--no-write-fetch-head");
+ if (config->display_format == DISPLAY_FORMAT_PORCELAIN)
+ strvec_pushf(argv, "--porcelain");
}
/* Fetch multiple remotes in parallel */
@@ -1911,6 +1881,7 @@ struct parallel_fetch_state {
const char **argv;
struct string_list *remotes;
int next, result;
+ const struct fetch_config *config;
};
static int fetch_next_remote(struct child_process *cp,
@@ -1930,7 +1901,7 @@ static int fetch_next_remote(struct child_process *cp,
strvec_push(&cp->args, remote);
cp->git_cmd = 1;
- if (verbosity >= 0)
+ if (verbosity >= 0 && state->config->display_format != DISPLAY_FORMAT_PORCELAIN)
printf(_("Fetching %s\n"), remote);
return 1;
@@ -1962,7 +1933,8 @@ static int fetch_finished(int result, struct strbuf *out,
return 0;
}
-static int fetch_multiple(struct string_list *list, int max_children)
+static int fetch_multiple(struct string_list *list, int max_children,
+ const struct fetch_config *config)
{
int i, result = 0;
struct strvec argv = STRVEC_INIT;
@@ -1980,10 +1952,10 @@ static int fetch_multiple(struct string_list *list, int max_children)
strvec_pushl(&argv, "-c", "fetch.bundleURI=",
"fetch", "--append", "--no-auto-gc",
"--no-write-commit-graph", NULL);
- add_options_to_argv(&argv);
+ add_options_to_argv(&argv, config);
if (max_children != 1 && list->nr != 1) {
- struct parallel_fetch_state state = { argv.v, list, 0, 0 };
+ struct parallel_fetch_state state = { argv.v, list, 0, 0, config };
const struct run_process_parallel_opts opts = {
.tr2_category = "fetch",
.tr2_label = "parallel/fetch",
@@ -2007,7 +1979,7 @@ static int fetch_multiple(struct string_list *list, int max_children)
strvec_pushv(&cmd.args, argv.v);
strvec_push(&cmd.args, name);
- if (verbosity >= 0)
+ if (verbosity >= 0 && config->display_format != DISPLAY_FORMAT_PORCELAIN)
printf(_("Fetching %s\n"), name);
cmd.git_cmd = 1;
if (run_command(&cmd)) {
@@ -2062,7 +2034,8 @@ static inline void fetch_one_setup_partial(struct remote *remote)
}
static int fetch_one(struct remote *remote, int argc, const char **argv,
- int prune_tags_ok, int use_stdin_refspecs)
+ int prune_tags_ok, int use_stdin_refspecs,
+ const struct fetch_config *config)
{
struct refspec rs = REFSPEC_INIT_FETCH;
int i;
@@ -2080,8 +2053,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
/* no command line request */
if (0 <= remote->prune)
prune = remote->prune;
- else if (0 <= fetch_prune_config)
- prune = fetch_prune_config;
+ else if (0 <= config->prune)
+ prune = config->prune;
else
prune = PRUNE_BY_DEFAULT;
}
@@ -2090,8 +2063,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
/* no command line request */
if (0 <= remote->prune_tags)
prune_tags = remote->prune_tags;
- else if (0 <= fetch_prune_tags_config)
- prune_tags = fetch_prune_tags_config;
+ else if (0 <= config->prune_tags)
+ prune_tags = config->prune_tags;
else
prune_tags = PRUNE_TAGS_BY_DEFAULT;
}
@@ -2129,7 +2102,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
sigchain_push_common(unlock_pack_on_signal);
atexit(unlock_pack_atexit);
sigchain_push(SIGPIPE, SIG_IGN);
- exit_code = do_fetch(gtransport, &rs);
+ exit_code = do_fetch(gtransport, &rs, config);
sigchain_pop(SIGPIPE);
refspec_clear(&rs);
transport_disconnect(gtransport);
@@ -2139,12 +2112,119 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
int cmd_fetch(int argc, const char **argv, const char *prefix)
{
- int i;
+ struct fetch_config config = {
+ .display_format = DISPLAY_FORMAT_FULL,
+ .prune = -1,
+ .prune_tags = -1,
+ .show_forced_updates = 1,
+ .recurse_submodules = RECURSE_SUBMODULES_DEFAULT,
+ .parallel = 1,
+ .submodule_fetch_jobs = -1,
+ };
+ const char *submodule_prefix = "";
const char *bundle_uri;
struct string_list list = STRING_LIST_INIT_DUP;
struct remote *remote = NULL;
+ int all = 0, multiple = 0;
int result = 0;
int prune_tags_ok = 1;
+ int enable_auto_gc = 1;
+ int unshallow = 0;
+ int max_jobs = -1;
+ int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
+ int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
+ int fetch_write_commit_graph = -1;
+ int stdin_refspecs = 0;
+ int negotiate_only = 0;
+ int porcelain = 0;
+ int i;
+
+ struct option builtin_fetch_options[] = {
+ OPT__VERBOSITY(&verbosity),
+ OPT_BOOL(0, "all", &all,
+ N_("fetch from all remotes")),
+ OPT_BOOL(0, "set-upstream", &set_upstream,
+ N_("set upstream for git pull/fetch")),
+ OPT_BOOL('a', "append", &append,
+ N_("append to .git/FETCH_HEAD instead of overwriting")),
+ OPT_BOOL(0, "atomic", &atomic_fetch,
+ N_("use atomic transaction to update references")),
+ OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
+ N_("path to upload pack on remote end")),
+ OPT__FORCE(&force, N_("force overwrite of local reference"), 0),
+ OPT_BOOL('m', "multiple", &multiple,
+ N_("fetch from multiple remotes")),
+ OPT_SET_INT('t', "tags", &tags,
+ N_("fetch all tags and associated objects"), TAGS_SET),
+ OPT_SET_INT('n', NULL, &tags,
+ N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
+ OPT_INTEGER('j', "jobs", &max_jobs,
+ N_("number of submodules fetched in parallel")),
+ OPT_BOOL(0, "prefetch", &prefetch,
+ N_("modify the refspec to place all refs within refs/prefetch/")),
+ OPT_BOOL('p', "prune", &prune,
+ N_("prune remote-tracking branches no longer on remote")),
+ OPT_BOOL('P', "prune-tags", &prune_tags,
+ N_("prune local tags no longer on remote and clobber changed tags")),
+ OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules_cli, N_("on-demand"),
+ N_("control recursive fetching of submodules"),
+ PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
+ OPT_BOOL(0, "dry-run", &dry_run,
+ N_("dry run")),
+ OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
+ OPT_BOOL(0, "write-fetch-head", &write_fetch_head,
+ N_("write fetched references to the FETCH_HEAD file")),
+ OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
+ OPT_BOOL('u', "update-head-ok", &update_head_ok,
+ N_("allow updating of HEAD ref")),
+ OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
+ OPT_STRING(0, "depth", &depth, N_("depth"),
+ N_("deepen history of shallow clone")),
+ OPT_STRING(0, "shallow-since", &deepen_since, N_("time"),
+ N_("deepen history of shallow repository based on time")),
+ OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"),
+ N_("deepen history of shallow clone, excluding rev")),
+ OPT_INTEGER(0, "deepen", &deepen_relative,
+ N_("deepen history of shallow clone")),
+ OPT_SET_INT_F(0, "unshallow", &unshallow,
+ N_("convert to a complete repository"),
+ 1, PARSE_OPT_NONEG),
+ OPT_SET_INT_F(0, "refetch", &refetch,
+ N_("re-fetch without negotiating common commits"),
+ 1, PARSE_OPT_NONEG),
+ { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"),
+ N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN },
+ OPT_CALLBACK_F(0, "recurse-submodules-default",
+ &recurse_submodules_default, N_("on-demand"),
+ N_("default for recursive fetching of submodules "
+ "(lower priority than config files)"),
+ PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules),
+ OPT_BOOL(0, "update-shallow", &update_shallow,
+ N_("accept refs that update .git/shallow")),
+ OPT_CALLBACK_F(0, "refmap", NULL, N_("refmap"),
+ N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg),
+ OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")),
+ OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
+ TRANSPORT_FAMILY_IPV4),
+ OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
+ TRANSPORT_FAMILY_IPV6),
+ OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
+ N_("report that we have only objects reachable from this object")),
+ OPT_BOOL(0, "negotiate-only", &negotiate_only,
+ N_("do not fetch a packfile; instead, print ancestors of negotiation tips")),
+ OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
+ OPT_BOOL(0, "auto-maintenance", &enable_auto_gc,
+ N_("run 'maintenance --auto' after fetching")),
+ OPT_BOOL(0, "auto-gc", &enable_auto_gc,
+ N_("run 'maintenance --auto' after fetching")),
+ OPT_BOOL(0, "show-forced-updates", &config.show_forced_updates,
+ N_("check for forced-updates on all updated branches")),
+ OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph,
+ N_("write the commit-graph after fetching")),
+ OPT_BOOL(0, "stdin", &stdin_refspecs,
+ N_("accept refspecs from stdin")),
+ OPT_END()
+ };
packet_trace_identity("fetch");
@@ -2158,7 +2238,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
free(anon);
}
- git_config(git_fetch_config, NULL);
+ git_config(git_fetch_config, &config);
if (the_repository->gitdir) {
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
@@ -2168,7 +2248,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
builtin_fetch_options, builtin_fetch_usage, 0);
if (recurse_submodules_cli != RECURSE_SUBMODULES_DEFAULT)
- recurse_submodules = recurse_submodules_cli;
+ config.recurse_submodules = recurse_submodules_cli;
if (negotiate_only) {
switch (recurse_submodules_cli) {
@@ -2179,7 +2259,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
* submodules. Skip it by setting recurse_submodules to
* RECURSE_SUBMODULES_OFF.
*/
- recurse_submodules = RECURSE_SUBMODULES_OFF;
+ config.recurse_submodules = RECURSE_SUBMODULES_OFF;
break;
default:
@@ -2188,15 +2268,35 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
}
}
- if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
- int *sfjc = submodule_fetch_jobs_config == -1
- ? &submodule_fetch_jobs_config : NULL;
- int *rs = recurse_submodules == RECURSE_SUBMODULES_DEFAULT
- ? &recurse_submodules : NULL;
+ if (config.recurse_submodules != RECURSE_SUBMODULES_OFF) {
+ int *sfjc = config.submodule_fetch_jobs == -1
+ ? &config.submodule_fetch_jobs : NULL;
+ int *rs = config.recurse_submodules == RECURSE_SUBMODULES_DEFAULT
+ ? &config.recurse_submodules : NULL;
fetch_config_from_gitmodules(sfjc, rs);
}
+
+ if (porcelain) {
+ switch (recurse_submodules_cli) {
+ case RECURSE_SUBMODULES_OFF:
+ case RECURSE_SUBMODULES_DEFAULT:
+ /*
+ * Reference updates in submodules would be ambiguous
+ * in porcelain mode, so we reject this combination.
+ */
+ config.recurse_submodules = RECURSE_SUBMODULES_OFF;
+ break;
+
+ default:
+ die(_("options '%s' and '%s' cannot be used together"),
+ "--porcelain", "--recurse-submodules");
+ }
+
+ config.display_format = DISPLAY_FORMAT_PORCELAIN;
+ }
+
if (negotiate_only && !negotiation_tip.nr)
die(_("--negotiate-only needs one or more --negotiation-tip=*"));
@@ -2295,7 +2395,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
} else if (remote) {
if (filter_options.choice || repo_has_promisor_remote(the_repository))
fetch_one_setup_partial(remote);
- result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs);
+ result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs,
+ &config);
} else {
int max_children = max_jobs;
@@ -2312,13 +2413,12 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
"from one remote"));
if (max_children < 0)
- max_children = fetch_parallel_config;
+ max_children = config.parallel;
/* TODO should this also die if we have a previous partial-clone? */
- result = fetch_multiple(&list, max_children);
+ result = fetch_multiple(&list, max_children, &config);
}
-
/*
* This is only needed after fetch_one(), which does not fetch
* submodules by itself.
@@ -2328,20 +2428,20 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
* the fetched history from each remote, so there is no need
* to fetch submodules from here.
*/
- if (!result && remote && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
+ if (!result && remote && (config.recurse_submodules != RECURSE_SUBMODULES_OFF)) {
struct strvec options = STRVEC_INIT;
int max_children = max_jobs;
if (max_children < 0)
- max_children = submodule_fetch_jobs_config;
+ max_children = config.submodule_fetch_jobs;
if (max_children < 0)
- max_children = fetch_parallel_config;
+ max_children = config.parallel;
- add_options_to_argv(&options);
+ add_options_to_argv(&options, &config);
result = fetch_submodules(the_repository,
&options,
submodule_prefix,
- recurse_submodules,
+ config.recurse_submodules,
recurse_submodules_default,
verbosity < 0,
max_children);
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 695fc8f4a5..350bfa6e81 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -1,11 +1,11 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
#include "gettext.h"
#include "refs.h"
#include "object.h"
#include "parse-options.h"
#include "ref-filter.h"
+#include "strbuf.h"
#include "strvec.h"
#include "commit-reach.h"
@@ -24,7 +24,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
struct string_list sorting_options = STRING_LIST_INIT_DUP;
int maxcount = 0, icase = 0, omit_empty = 0;
struct ref_array array;
- struct ref_filter filter;
+ struct ref_filter filter = REF_FILTER_INIT;
struct ref_format format = REF_FORMAT_INIT;
struct strbuf output = STRBUF_INIT;
struct strbuf err = STRBUF_INIT;
@@ -47,6 +47,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
OPT__COLOR(&format.use_color, N_("respect format colors")),
+ OPT_REF_FILTER_EXCLUDE(&filter),
OPT_REF_SORT(&sorting_options),
OPT_CALLBACK(0, "points-at", &filter.points_at,
N_("object"), N_("print only refs which points at the given object"),
@@ -61,7 +62,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
};
memset(&array, 0, sizeof(array));
- memset(&filter, 0, sizeof(filter));
format.format = "%(objectname) %(objecttype)\t%(refname)";
@@ -121,8 +121,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
strbuf_release(&err);
strbuf_release(&output);
ref_array_clear(&array);
- free_commit_list(filter.with_commit);
- free_commit_list(filter.no_commit);
+ ref_filter_clear(&filter);
ref_sorting_release(sorting);
strvec_clear(&vec);
return 0;
diff --git a/builtin/for-each-repo.c b/builtin/for-each-repo.c
index 598ca16c46..28186b30f5 100644
--- a/builtin/for-each-repo.c
+++ b/builtin/for-each-repo.c
@@ -1,8 +1,9 @@
-#include "cache.h"
-#include "config.h"
#include "builtin.h"
+#include "config.h"
#include "gettext.h"
#include "parse-options.h"
+#include "path.h"
+#include "repository.h"
#include "run-command.h"
#include "string-list.h"
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 2cd461b84c..b81f1a2010 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -1,5 +1,4 @@
#include "builtin.h"
-#include "cache.h"
#include "gettext.h"
#include "hex.h"
#include "repository.h"
@@ -21,12 +20,16 @@
#include "packfile.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "path.h"
+#include "read-cache-ll.h"
#include "replace-object.h"
#include "resolve-undo.h"
#include "run-command.h"
+#include "sparse-index.h"
#include "worktree.h"
#include "pack-revindex.h"
+#include "pack-bitmap.h"
#define REACHABLE 0x0001
#define SEEN 0x0002
@@ -57,6 +60,7 @@ static int name_objects;
#define ERROR_COMMIT_GRAPH 020
#define ERROR_MULTI_PACK_INDEX 040
#define ERROR_PACK_REV_INDEX 0100
+#define ERROR_BITMAP 0200
static const char *describe_object(const struct object_id *oid)
{
@@ -867,20 +871,20 @@ static int check_pack_rev_indexes(struct repository *r, int show_progress)
int res = 0;
if (show_progress) {
- for (struct packed_git *p = get_all_packs(the_repository); p; p = p->next)
+ for (struct packed_git *p = get_all_packs(r); p; p = p->next)
pack_count++;
progress = start_delayed_progress("Verifying reverse pack-indexes", pack_count);
pack_count = 0;
}
- for (struct packed_git *p = get_all_packs(the_repository); p; p = p->next) {
+ for (struct packed_git *p = get_all_packs(r); p; p = p->next) {
int load_error = load_pack_revindex_from_disk(p);
if (load_error < 0) {
error(_("unable to load rev-index for pack '%s'"), p->pack_name);
res = ERROR_PACK_REV_INDEX;
} else if (!load_error &&
- !load_pack_revindex(the_repository, p) &&
+ !load_pack_revindex(r, p) &&
verify_pack_revindex(p)) {
error(_("invalid rev-index for pack '%s'"), p->pack_name);
res = ERROR_PACK_REV_INDEX;
@@ -1056,6 +1060,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
}
errors_found |= check_pack_rev_indexes(the_repository, show_progress);
+ if (verify_bitmap_files(the_repository))
+ errors_found |= ERROR_BITMAP;
check_connectivity();
diff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c
index f6dd9a784c..74d1d6a585 100644
--- a/builtin/fsmonitor--daemon.c
+++ b/builtin/fsmonitor--daemon.c
@@ -5,15 +5,17 @@
#include "environment.h"
#include "gettext.h"
#include "parse-options.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
#include "fsmonitor-ipc.h"
#include "fsmonitor-path-utils.h"
+#include "fsmonitor-settings.h"
#include "compat/fsmonitor/fsm-health.h"
#include "compat/fsmonitor/fsm-listen.h"
#include "fsmonitor--daemon.h"
#include "simple-ipc.h"
#include "khash.h"
#include "pkt-line.h"
+#include "trace.h"
#include "trace2.h"
static const char * const builtin_fsmonitor__daemon_usage[] = {
diff --git a/builtin/gc.c b/builtin/gc.c
index 031d42c64c..91eec7703a 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -12,6 +12,7 @@
#include "builtin.h"
#include "abspath.h"
+#include "date.h"
#include "environment.h"
#include "hex.h"
#include "repository.h"
@@ -26,9 +27,10 @@
#include "commit-graph.h"
#include "packfile.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "pack.h"
#include "pack-objects.h"
+#include "path.h"
#include "blob.h"
#include "tree.h"
#include "promisor-remote.h"
diff --git a/builtin/get-tar-commit-id.c b/builtin/get-tar-commit-id.c
index 564cfcac4f..9303e386cc 100644
--- a/builtin/get-tar-commit-id.c
+++ b/builtin/get-tar-commit-id.c
@@ -1,10 +1,9 @@
/*
* Copyright (c) 2005, 2006 Rene Scharfe
*/
-#include "cache.h"
+#include "builtin.h"
#include "commit.h"
#include "tar.h"
-#include "builtin.h"
#include "quote.h"
#include "wrapper.h"
diff --git a/builtin/grep.c b/builtin/grep.c
index b86c754def..72e70b3a48 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2006 Junio C Hamano
*/
-#include "cache.h"
+#include "builtin.h"
#include "alloc.h"
#include "gettext.h"
#include "hex.h"
@@ -14,7 +14,6 @@
#include "commit.h"
#include "tag.h"
#include "tree-walk.h"
-#include "builtin.h"
#include "parse-options.h"
#include "string-list.h"
#include "run-command.h"
@@ -28,9 +27,11 @@
#include "submodule-config.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "packfile.h"
#include "pager.h"
+#include "path.h"
+#include "read-cache-ll.h"
#include "write-or-die.h"
static const char *grep_prefix;
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index a380121166..5ffec99dce 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -10,12 +10,13 @@
#include "gettext.h"
#include "hex.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "blob.h"
#include "quote.h"
#include "parse-options.h"
#include "exec-cmd.h"
#include "setup.h"
+#include "strbuf.h"
#include "write-or-die.h"
/*
diff --git a/builtin/help.c b/builtin/help.c
index 128aa83099..e93533fb09 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -1,13 +1,13 @@
/*
* Builtin help command
*/
-#include "cache.h"
-#include "config.h"
#include "builtin.h"
+#include "config.h"
#include "exec-cmd.h"
#include "gettext.h"
#include "pager.h"
#include "parse-options.h"
+#include "path.h"
#include "run-command.h"
#include "config-list.h"
#include "help.h"
diff --git a/builtin/hook.c b/builtin/hook.c
index 88051795c7..09b51a6487 100644
--- a/builtin/hook.c
+++ b/builtin/hook.c
@@ -1,4 +1,3 @@
-#include "cache.h"
#include "builtin.h"
#include "config.h"
#include "gettext.h"
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index bb67e16655..820860265d 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -14,12 +14,13 @@
#include "progress.h"
#include "fsck.h"
#include "exec-cmd.h"
+#include "strbuf.h"
#include "streaming.h"
#include "thread-utils.h"
#include "packfile.h"
#include "pack-revindex.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "oid-array.h"
#include "replace-object.h"
#include "promisor-remote.h"
diff --git a/builtin/init-db.c b/builtin/init-db.c
index 6183f3fb3f..0d8bd4d721 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -3,483 +3,18 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#include "cache.h"
+#include "builtin.h"
#include "abspath.h"
#include "config.h"
#include "environment.h"
#include "gettext.h"
-#include "refs.h"
-#include "builtin.h"
-#include "exec-cmd.h"
#include "object-file.h"
#include "parse-options.h"
+#include "path.h"
#include "setup.h"
-#include "worktree.h"
+#include "strbuf.h"
#include "wrapper.h"
-#ifndef DEFAULT_GIT_TEMPLATE_DIR
-#define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
-#endif
-
-#ifdef NO_TRUSTABLE_FILEMODE
-#define TEST_FILEMODE 0
-#else
-#define TEST_FILEMODE 1
-#endif
-
-#define GIT_DEFAULT_HASH_ENVIRONMENT "GIT_DEFAULT_HASH"
-
-static int init_is_bare_repository = 0;
-static int init_shared_repository = -1;
-
-static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
- DIR *dir)
-{
- size_t path_baselen = path->len;
- size_t template_baselen = template_path->len;
- struct dirent *de;
-
- /* Note: if ".git/hooks" file exists in the repository being
- * re-initialized, /etc/core-git/templates/hooks/update would
- * cause "git init" to fail here. I think this is sane but
- * it means that the set of templates we ship by default, along
- * with the way the namespace under .git/ is organized, should
- * be really carefully chosen.
- */
- safe_create_dir(path->buf, 1);
- while ((de = readdir(dir)) != NULL) {
- struct stat st_git, st_template;
- int exists = 0;
-
- strbuf_setlen(path, path_baselen);
- strbuf_setlen(template_path, template_baselen);
-
- if (de->d_name[0] == '.')
- continue;
- strbuf_addstr(path, de->d_name);
- strbuf_addstr(template_path, de->d_name);
- if (lstat(path->buf, &st_git)) {
- if (errno != ENOENT)
- die_errno(_("cannot stat '%s'"), path->buf);
- }
- else
- exists = 1;
-
- if (lstat(template_path->buf, &st_template))
- die_errno(_("cannot stat template '%s'"), template_path->buf);
-
- if (S_ISDIR(st_template.st_mode)) {
- DIR *subdir = opendir(template_path->buf);
- if (!subdir)
- die_errno(_("cannot opendir '%s'"), template_path->buf);
- strbuf_addch(path, '/');
- strbuf_addch(template_path, '/');
- copy_templates_1(path, template_path, subdir);
- closedir(subdir);
- }
- else if (exists)
- continue;
- else if (S_ISLNK(st_template.st_mode)) {
- struct strbuf lnk = STRBUF_INIT;
- if (strbuf_readlink(&lnk, template_path->buf,
- st_template.st_size) < 0)
- die_errno(_("cannot readlink '%s'"), template_path->buf);
- if (symlink(lnk.buf, path->buf))
- die_errno(_("cannot symlink '%s' '%s'"),
- lnk.buf, path->buf);
- strbuf_release(&lnk);
- }
- else if (S_ISREG(st_template.st_mode)) {
- if (copy_file(path->buf, template_path->buf, st_template.st_mode))
- die_errno(_("cannot copy '%s' to '%s'"),
- template_path->buf, path->buf);
- }
- else
- error(_("ignoring template %s"), template_path->buf);
- }
-}
-
-static void copy_templates(const char *template_dir, const char *init_template_dir)
-{
- struct strbuf path = STRBUF_INIT;
- struct strbuf template_path = STRBUF_INIT;
- size_t template_len;
- struct repository_format template_format = REPOSITORY_FORMAT_INIT;
- struct strbuf err = STRBUF_INIT;
- DIR *dir;
- char *to_free = NULL;
-
- if (!template_dir)
- template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
- if (!template_dir)
- template_dir = init_template_dir;
- if (!template_dir)
- template_dir = to_free = system_path(DEFAULT_GIT_TEMPLATE_DIR);
- if (!template_dir[0]) {
- free(to_free);
- return;
- }
-
- strbuf_addstr(&template_path, template_dir);
- strbuf_complete(&template_path, '/');
- template_len = template_path.len;
-
- dir = opendir(template_path.buf);
- if (!dir) {
- warning(_("templates not found in %s"), template_dir);
- goto free_return;
- }
-
- /* Make sure that template is from the correct vintage */
- strbuf_addstr(&template_path, "config");
- read_repository_format(&template_format, template_path.buf);
- strbuf_setlen(&template_path, template_len);
-
- /*
- * No mention of version at all is OK, but anything else should be
- * verified.
- */
- if (template_format.version >= 0 &&
- verify_repository_format(&template_format, &err) < 0) {
- warning(_("not copying templates from '%s': %s"),
- template_dir, err.buf);
- strbuf_release(&err);
- goto close_free_return;
- }
-
- strbuf_addstr(&path, get_git_common_dir());
- strbuf_complete(&path, '/');
- copy_templates_1(&path, &template_path, dir);
-close_free_return:
- closedir(dir);
-free_return:
- free(to_free);
- strbuf_release(&path);
- strbuf_release(&template_path);
- clear_repository_format(&template_format);
-}
-
-/*
- * If the git_dir is not directly inside the working tree, then git will not
- * find it by default, and we need to set the worktree explicitly.
- */
-static int needs_work_tree_config(const char *git_dir, const char *work_tree)
-{
- if (!strcmp(work_tree, "/") && !strcmp(git_dir, "/.git"))
- return 0;
- if (skip_prefix(git_dir, work_tree, &git_dir) &&
- !strcmp(git_dir, "/.git"))
- return 0;
- return 1;
-}
-
-void initialize_repository_version(int hash_algo, int reinit)
-{
- char repo_version_string[10];
- int repo_version = GIT_REPO_VERSION;
-
- if (hash_algo != GIT_HASH_SHA1)
- repo_version = GIT_REPO_VERSION_READ;
-
- /* This forces creation of new config file */
- xsnprintf(repo_version_string, sizeof(repo_version_string),
- "%d", repo_version);
- git_config_set("core.repositoryformatversion", repo_version_string);
-
- if (hash_algo != GIT_HASH_SHA1)
- git_config_set("extensions.objectformat",
- hash_algos[hash_algo].name);
- else if (reinit)
- git_config_set_gently("extensions.objectformat", NULL);
-}
-
-static int create_default_files(const char *template_path,
- const char *original_git_dir,
- const char *initial_branch,
- const struct repository_format *fmt,
- int quiet)
-{
- struct stat st1;
- struct strbuf buf = STRBUF_INIT;
- char *path;
- char junk[2];
- int reinit;
- int filemode;
- struct strbuf err = STRBUF_INIT;
- const char *init_template_dir = NULL;
- const char *work_tree = get_git_work_tree();
-
- /*
- * First copy the templates -- we might have the default
- * config file there, in which case we would want to read
- * from it after installing.
- *
- * Before reading that config, we also need to clear out any cached
- * values (since we've just potentially changed what's available on
- * disk).
- */
- git_config_get_pathname("init.templatedir", &init_template_dir);
- copy_templates(template_path, init_template_dir);
- free((char *)init_template_dir);
- git_config_clear();
- reset_shared_repository();
- git_config(git_default_config, NULL);
-
- /*
- * We must make sure command-line options continue to override any
- * values we might have just re-read from the config.
- */
- is_bare_repository_cfg = init_is_bare_repository || !work_tree;
- if (init_shared_repository != -1)
- set_shared_repository(init_shared_repository);
-
- /*
- * We would have created the above under user's umask -- under
- * shared-repository settings, we would need to fix them up.
- */
- if (get_shared_repository()) {
- adjust_shared_perm(get_git_dir());
- }
-
- /*
- * We need to create a "refs" dir in any case so that older
- * versions of git can tell that this is a repository.
- */
- safe_create_dir(git_path("refs"), 1);
- adjust_shared_perm(git_path("refs"));
-
- if (refs_init_db(&err))
- die("failed to set up refs db: %s", err.buf);
-
- /*
- * Point the HEAD symref to the initial branch with if HEAD does
- * not yet exist.
- */
- path = git_path_buf(&buf, "HEAD");
- reinit = (!access(path, R_OK)
- || readlink(path, junk, sizeof(junk)-1) != -1);
- if (!reinit) {
- char *ref;
-
- if (!initial_branch)
- initial_branch = git_default_branch_name(quiet);
-
- ref = xstrfmt("refs/heads/%s", initial_branch);
- if (check_refname_format(ref, 0) < 0)
- die(_("invalid initial branch name: '%s'"),
- initial_branch);
-
- if (create_symref("HEAD", ref, NULL) < 0)
- exit(1);
- free(ref);
- }
-
- initialize_repository_version(fmt->hash_algo, 0);
-
- /* Check filemode trustability */
- path = git_path_buf(&buf, "config");
- filemode = TEST_FILEMODE;
- if (TEST_FILEMODE && !lstat(path, &st1)) {
- struct stat st2;
- filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) &&
- !lstat(path, &st2) &&
- st1.st_mode != st2.st_mode &&
- !chmod(path, st1.st_mode));
- if (filemode && !reinit && (st1.st_mode & S_IXUSR))
- filemode = 0;
- }
- git_config_set("core.filemode", filemode ? "true" : "false");
-
- if (is_bare_repository())
- git_config_set("core.bare", "true");
- else {
- git_config_set("core.bare", "false");
- /* allow template config file to override the default */
- if (log_all_ref_updates == LOG_REFS_UNSET)
- git_config_set("core.logallrefupdates", "true");
- if (needs_work_tree_config(original_git_dir, work_tree))
- git_config_set("core.worktree", work_tree);
- }
-
- if (!reinit) {
- /* Check if symlink is supported in the work tree */
- path = git_path_buf(&buf, "tXXXXXX");
- if (!close(xmkstemp(path)) &&
- !unlink(path) &&
- !symlink("testing", path) &&
- !lstat(path, &st1) &&
- S_ISLNK(st1.st_mode))
- unlink(path); /* good */
- else
- git_config_set("core.symlinks", "false");
-
- /* Check if the filesystem is case-insensitive */
- path = git_path_buf(&buf, "CoNfIg");
- if (!access(path, F_OK))
- git_config_set("core.ignorecase", "true");
- probe_utf8_pathname_composition();
- }
-
- strbuf_release(&buf);
- return reinit;
-}
-
-static void create_object_directory(void)
-{
- struct strbuf path = STRBUF_INIT;
- size_t baselen;
-
- strbuf_addstr(&path, get_object_directory());
- baselen = path.len;
-
- safe_create_dir(path.buf, 1);
-
- strbuf_setlen(&path, baselen);
- strbuf_addstr(&path, "/pack");
- safe_create_dir(path.buf, 1);
-
- strbuf_setlen(&path, baselen);
- strbuf_addstr(&path, "/info");
- safe_create_dir(path.buf, 1);
-
- strbuf_release(&path);
-}
-
-static void separate_git_dir(const char *git_dir, const char *git_link)
-{
- struct stat st;
-
- if (!stat(git_link, &st)) {
- const char *src;
-
- if (S_ISREG(st.st_mode))
- src = read_gitfile(git_link);
- else if (S_ISDIR(st.st_mode))
- src = git_link;
- else
- die(_("unable to handle file type %d"), (int)st.st_mode);
-
- if (rename(src, git_dir))
- die_errno(_("unable to move %s to %s"), src, git_dir);
- repair_worktrees(NULL, NULL);
- }
-
- write_file(git_link, "gitdir: %s", git_dir);
-}
-
-static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash)
-{
- const char *env = getenv(GIT_DEFAULT_HASH_ENVIRONMENT);
- /*
- * If we already have an initialized repo, don't allow the user to
- * specify a different algorithm, as that could cause corruption.
- * Otherwise, if the user has specified one on the command line, use it.
- */
- if (repo_fmt->version >= 0 && hash != GIT_HASH_UNKNOWN && hash != repo_fmt->hash_algo)
- die(_("attempt to reinitialize repository with different hash"));
- else if (hash != GIT_HASH_UNKNOWN)
- repo_fmt->hash_algo = hash;
- else if (env) {
- int env_algo = hash_algo_by_name(env);
- if (env_algo == GIT_HASH_UNKNOWN)
- die(_("unknown hash algorithm '%s'"), env);
- repo_fmt->hash_algo = env_algo;
- }
-}
-
-int init_db(const char *git_dir, const char *real_git_dir,
- const char *template_dir, int hash, const char *initial_branch,
- unsigned int flags)
-{
- int reinit;
- int exist_ok = flags & INIT_DB_EXIST_OK;
- char *original_git_dir = real_pathdup(git_dir, 1);
- struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
-
- if (real_git_dir) {
- struct stat st;
-
- if (!exist_ok && !stat(git_dir, &st))
- die(_("%s already exists"), git_dir);
-
- if (!exist_ok && !stat(real_git_dir, &st))
- die(_("%s already exists"), real_git_dir);
-
- set_git_dir(real_git_dir, 1);
- git_dir = get_git_dir();
- separate_git_dir(git_dir, original_git_dir);
- }
- else {
- set_git_dir(git_dir, 1);
- git_dir = get_git_dir();
- }
- startup_info->have_repository = 1;
-
- /* Ensure `core.hidedotfiles` is processed */
- git_config(platform_core_config, NULL);
-
- safe_create_dir(git_dir, 0);
-
- init_is_bare_repository = is_bare_repository();
-
- /* Check to see if the repository version is right.
- * Note that a newly created repository does not have
- * config file, so this will not fail. What we are catching
- * is an attempt to reinitialize new repository with an old tool.
- */
- check_repository_format(&repo_fmt);
-
- validate_hash_algorithm(&repo_fmt, hash);
-
- reinit = create_default_files(template_dir, original_git_dir,
- initial_branch, &repo_fmt,
- flags & INIT_DB_QUIET);
- if (reinit && initial_branch)
- warning(_("re-init: ignored --initial-branch=%s"),
- initial_branch);
-
- create_object_directory();
-
- if (get_shared_repository()) {
- char buf[10];
- /* We do not spell "group" and such, so that
- * the configuration can be read by older version
- * of git. Note, we use octal numbers for new share modes,
- * and compatibility values for PERM_GROUP and
- * PERM_EVERYBODY.
- */
- if (get_shared_repository() < 0)
- /* force to the mode value */
- xsnprintf(buf, sizeof(buf), "0%o", -get_shared_repository());
- else if (get_shared_repository() == PERM_GROUP)
- xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_GROUP);
- else if (get_shared_repository() == PERM_EVERYBODY)
- xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY);
- else
- BUG("invalid value for shared_repository");
- git_config_set("core.sharedrepository", buf);
- git_config_set("receive.denyNonFastforwards", "true");
- }
-
- if (!(flags & INIT_DB_QUIET)) {
- int len = strlen(git_dir);
-
- if (reinit)
- printf(get_shared_repository()
- ? _("Reinitialized existing shared Git repository in %s%s\n")
- : _("Reinitialized existing Git repository in %s%s\n"),
- git_dir, len && git_dir[len-1] != '/' ? "/" : "");
- else
- printf(get_shared_repository()
- ? _("Initialized empty shared Git repository in %s%s\n")
- : _("Initialized empty Git repository in %s%s\n"),
- git_dir, len && git_dir[len-1] != '/' ? "/" : "");
- }
-
- free(original_git_dir);
- return 0;
-}
-
static int guess_repository_type(const char *git_dir)
{
const char *slash;
@@ -544,6 +79,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
const char *object_format = NULL;
const char *initial_branch = NULL;
int hash_algo = GIT_HASH_UNKNOWN;
+ int init_shared_repository = -1;
const struct option init_db_options[] = {
OPT_STRING(0, "template", &template_dir, N_("template-directory"),
N_("directory from which templates will be used")),
@@ -701,5 +237,5 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
flags |= INIT_DB_EXIST_OK;
return init_db(git_dir, real_git_dir, template_dir, hash_algo,
- initial_branch, flags);
+ initial_branch, init_shared_repository, flags);
}
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index 107ac28f0e..c5e8345265 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -5,7 +5,6 @@
*
*/
-#include "cache.h"
#include "builtin.h"
#include "gettext.h"
#include "parse-options.h"
diff --git a/builtin/log.c b/builtin/log.c
index 4f162ff4d0..4e9d779537 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -14,7 +14,7 @@
#include "refs.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "pager.h"
#include "color.h"
#include "commit.h"
@@ -44,6 +44,7 @@
#include "commit-reach.h"
#include "range-diff.h"
#include "tmp-objdir.h"
+#include "tree.h"
#include "write-or-die.h"
#define MAIL_DEFAULT_WRAP 72
@@ -593,6 +594,10 @@ static int git_log_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, "log.diffmerges"))
return diff_merges_config(value);
+ if (!strcmp(var, "log.diffmergeshide"))
+ return diff_merges_hide_config(git_config_bool(var, value));
+ if (!strcmp(var, "log.diffmerges-m-imply-p"))
+ return diff_merges_m_imply_p_config(git_config_bool(var, value));
if (!strcmp(var, "log.showroot")) {
default_show_root = git_config_bool(var, value);
return 0;
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 625f48f0d6..c1ff79c559 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -5,13 +5,12 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#include "cache.h"
+#include "builtin.h"
#include "repository.h"
#include "config.h"
#include "convert.h"
#include "quote.h"
#include "dir.h"
-#include "builtin.h"
#include "gettext.h"
#include "object-name.h"
#include "strbuf.h"
@@ -20,11 +19,17 @@
#include "parse-options.h"
#include "resolve-undo.h"
#include "string-list.h"
+#include "path.h"
#include "pathspec.h"
+#include "read-cache.h"
#include "run-command.h"
#include "setup.h"
+#include "sparse-index.h"
#include "submodule.h"
#include "submodule-config.h"
+#include "object-store.h"
+#include "hex.h"
+
static int abbrev;
static int show_deleted;
@@ -241,6 +246,24 @@ static void show_submodule(struct repository *superproject,
repo_clear(&subrepo);
}
+static void expand_objectsize(struct strbuf *line, const struct object_id *oid,
+ const enum object_type type, unsigned int padded)
+{
+ if (type == OBJ_BLOB) {
+ unsigned long size;
+ if (oid_object_info(the_repository, oid, &size) < 0)
+ die(_("could not get object info about '%s'"),
+ oid_to_hex(oid));
+ if (padded)
+ strbuf_addf(line, "%7"PRIuMAX, (uintmax_t)size);
+ else
+ strbuf_addf(line, "%"PRIuMAX, (uintmax_t)size);
+ } else if (padded) {
+ strbuf_addf(line, "%7s", "-");
+ } else {
+ strbuf_addstr(line, "-");
+ }
+}
struct show_index_data {
const char *pathname;
struct index_state *istate;
@@ -272,6 +295,12 @@ static size_t expand_show_index(struct strbuf *sb, const char *start,
strbuf_addf(sb, "%06o", data->ce->ce_mode);
else if (skip_prefix(start, "(objectname)", &p))
strbuf_add_unique_abbrev(sb, &data->ce->oid, abbrev);
+ else if (skip_prefix(start, "(objecttype)", &p))
+ strbuf_addstr(sb, type_name(object_type(data->ce->ce_mode)));
+ else if (skip_prefix(start, "(objectsize:padded)", &p))
+ expand_objectsize(sb, &data->ce->oid, object_type(data->ce->ce_mode), 1);
+ else if (skip_prefix(start, "(objectsize)", &p))
+ expand_objectsize(sb, &data->ce->oid, object_type(data->ce->ce_mode), 0);
else if (skip_prefix(start, "(stage)", &p))
strbuf_addf(sb, "%d", ce_stage(data->ce));
else if (skip_prefix(start, "(eolinfo:index)", &p))
@@ -516,143 +545,6 @@ static int get_common_prefix_len(const char *common_prefix)
return common_prefix_len;
}
-static int read_one_entry_opt(struct index_state *istate,
- const struct object_id *oid,
- struct strbuf *base,
- const char *pathname,
- unsigned mode, int opt)
-{
- int len;
- struct cache_entry *ce;
-
- if (S_ISDIR(mode))
- return READ_TREE_RECURSIVE;
-
- len = strlen(pathname);
- ce = make_empty_cache_entry(istate, base->len + len);
-
- ce->ce_mode = create_ce_mode(mode);
- ce->ce_flags = create_ce_flags(1);
- ce->ce_namelen = base->len + len;
- memcpy(ce->name, base->buf, base->len);
- memcpy(ce->name + base->len, pathname, len+1);
- oidcpy(&ce->oid, oid);
- return add_index_entry(istate, ce, opt);
-}
-
-static int read_one_entry(const struct object_id *oid, struct strbuf *base,
- const char *pathname, unsigned mode,
- void *context)
-{
- struct index_state *istate = context;
- return read_one_entry_opt(istate, oid, base, pathname,
- mode,
- ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
-}
-
-/*
- * This is used when the caller knows there is no existing entries at
- * the stage that will conflict with the entry being added.
- */
-static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,
- const char *pathname, unsigned mode,
- void *context)
-{
- struct index_state *istate = context;
- return read_one_entry_opt(istate, oid, base, pathname,
- mode, ADD_CACHE_JUST_APPEND);
-}
-
-/*
- * Read the tree specified with --with-tree option
- * (typically, HEAD) into stage #1 and then
- * squash them down to stage #0. This is used for
- * --error-unmatch to list and check the path patterns
- * that were given from the command line. We are not
- * going to write this index out.
- */
-void overlay_tree_on_index(struct index_state *istate,
- const char *tree_name, const char *prefix)
-{
- struct tree *tree;
- struct object_id oid;
- struct pathspec pathspec;
- struct cache_entry *last_stage0 = NULL;
- int i;
- read_tree_fn_t fn = NULL;
- int err;
-
- if (repo_get_oid(the_repository, tree_name, &oid))
- die("tree-ish %s not found.", tree_name);
- tree = parse_tree_indirect(&oid);
- if (!tree)
- die("bad tree-ish %s", tree_name);
-
- /* Hoist the unmerged entries up to stage #3 to make room */
- /* TODO: audit for interaction with sparse-index. */
- ensure_full_index(istate);
- for (i = 0; i < istate->cache_nr; i++) {
- struct cache_entry *ce = istate->cache[i];
- if (!ce_stage(ce))
- continue;
- ce->ce_flags |= CE_STAGEMASK;
- }
-
- if (prefix) {
- static const char *(matchbuf[1]);
- matchbuf[0] = NULL;
- parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC,
- PATHSPEC_PREFER_CWD, prefix, matchbuf);
- } else
- memset(&pathspec, 0, sizeof(pathspec));
-
- /*
- * See if we have cache entry at the stage. If so,
- * do it the original slow way, otherwise, append and then
- * sort at the end.
- */
- for (i = 0; !fn && i < istate->cache_nr; i++) {
- const struct cache_entry *ce = istate->cache[i];
- if (ce_stage(ce) == 1)
- fn = read_one_entry;
- }
-
- if (!fn)
- fn = read_one_entry_quick;
- err = read_tree(the_repository, tree, &pathspec, fn, istate);
- clear_pathspec(&pathspec);
- if (err)
- die("unable to read tree entries %s", tree_name);
-
- /*
- * Sort the cache entry -- we need to nuke the cache tree, though.
- */
- if (fn == read_one_entry_quick) {
- cache_tree_free(&istate->cache_tree);
- QSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);
- }
-
- for (i = 0; i < istate->cache_nr; i++) {
- struct cache_entry *ce = istate->cache[i];
- switch (ce_stage(ce)) {
- case 0:
- last_stage0 = ce;
- /* fallthru */
- default:
- continue;
- case 1:
- /*
- * If there is stage #0 entry for this, we do not
- * need to show it. We use CE_UPDATE bit to mark
- * such an entry.
- */
- if (last_stage0 &&
- !strcmp(last_stage0->name, ce->name))
- ce->ce_flags |= CE_UPDATE;
- }
- }
-}
-
static const char * const ls_files_usage[] = {
N_("git ls-files [<options>] [<file>...]"),
NULL
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index 3c74c4a104..fc76575430 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -1,12 +1,13 @@
#include "builtin.h"
-#include "cache.h"
#include "gettext.h"
#include "hex.h"
#include "transport.h"
+#include "pkt-line.h"
#include "ref-filter.h"
#include "remote.h"
#include "refs.h"
#include "parse-options.h"
+#include "wildmatch.h"
static const char * const ls_remote_usage[] = {
N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index 077977a461..4e17f13648 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -3,17 +3,17 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
#include "gettext.h"
#include "hex.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "blob.h"
#include "tree.h"
#include "commit.h"
+#include "path.h"
#include "quote.h"
-#include "builtin.h"
#include "parse-options.h"
#include "pathspec.h"
diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index a032a1c388..53b55dd71c 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -2,9 +2,8 @@
* Another stupid program, this one parsing the headers of an
* email to figure out authorship and subject
*/
-#include "cache.h"
-#include "abspath.h"
#include "builtin.h"
+#include "abspath.h"
#include "environment.h"
#include "gettext.h"
#include "utf8.h"
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 0b6193a091..3af9ddb8ae 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -4,7 +4,6 @@
* It just splits a mbox into a list of files: "0001" "0002" ..
* so you can process them further from there.
*/
-#include "cache.h"
#include "builtin.h"
#include "gettext.h"
#include "string-list.h"
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index 854019a32d..e68b7fe45d 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -1,5 +1,4 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
#include "commit.h"
#include "gettext.h"
diff --git a/builtin/merge-file.c b/builtin/merge-file.c
index 781818d08f..d7eb4c6540 100644
--- a/builtin/merge-file.c
+++ b/builtin/merge-file.c
@@ -1,6 +1,5 @@
#include "builtin.h"
#include "abspath.h"
-#include "cache.h"
#include "config.h"
#include "gettext.h"
#include "setup.h"
diff --git a/builtin/merge-index.c b/builtin/merge-index.c
index b747b4ed98..270d5f644a 100644
--- a/builtin/merge-index.c
+++ b/builtin/merge-index.c
@@ -1,7 +1,10 @@
#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
#include "hex.h"
+#include "read-cache-ll.h"
+#include "repository.h"
#include "run-command.h"
+#include "sparse-index.h"
static const char *pgm;
static int one_shot, quiet;
diff --git a/builtin/merge-ours.c b/builtin/merge-ours.c
index c2e519301e..932924e5d0 100644
--- a/builtin/merge-ours.c
+++ b/builtin/merge-ours.c
@@ -10,6 +10,7 @@
#include "git-compat-util.h"
#include "builtin.h"
#include "diff.h"
+#include "repository.h"
static const char builtin_merge_ours_usage[] =
"git merge-ours <base>... -- HEAD <remote>...";
diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c
index 708a8ffabe..3366699657 100644
--- a/builtin/merge-recursive.c
+++ b/builtin/merge-recursive.c
@@ -1,11 +1,12 @@
-#include "cache.h"
#include "builtin.h"
#include "advice.h"
#include "commit.h"
#include "gettext.h"
+#include "hash.h"
#include "tag.h"
#include "merge-recursive.h"
#include "object-name.h"
+#include "repository.h"
#include "xdiff-interface.h"
static const char builtin_merge_recursive_usage[] =
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 6b9f006ec1..2855792f20 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -9,13 +9,15 @@
#include "commit-reach.h"
#include "merge-ort.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "parse-options.h"
#include "repository.h"
#include "blob.h"
#include "exec-cmd.h"
#include "merge-blobs.h"
#include "quote.h"
+#include "tree.h"
+#include "config.h"
static int line_termination = '\n';
@@ -627,6 +629,8 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
if (argc != expected_remaining_argc)
usage_with_options(merge_tree_usage, mt_options);
+ git_config(git_default_config, NULL);
+
/* Do the relevant type of merge */
if (o.mode == MODE_REAL)
return real_merge(&o, merge_base, argv[0], argv[1], prefix);
diff --git a/builtin/merge.c b/builtin/merge.c
index 8da3e46abb..41fe0d8e5e 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -7,7 +7,7 @@
*/
#define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
#include "abspath.h"
#include "advice.h"
#include "alloc.h"
@@ -18,7 +18,6 @@
#include "hex.h"
#include "object-name.h"
#include "parse-options.h"
-#include "builtin.h"
#include "lockfile.h"
#include "run-command.h"
#include "hook.h"
@@ -28,6 +27,7 @@
#include "refspec.h"
#include "commit.h"
#include "diffcore.h"
+#include "path.h"
#include "revision.h"
#include "unpack-trees.h"
#include "cache-tree.h"
@@ -37,6 +37,7 @@
#include "color.h"
#include "rerere.h"
#include "help.h"
+#include "merge.h"
#include "merge-recursive.h"
#include "merge-ort-wrappers.h"
#include "resolve-undo.h"
@@ -879,13 +880,15 @@ static void prepare_to_commit(struct commit_list *remoteheads)
strbuf_addch(&msg, '\n');
if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
wt_status_append_cut_line(&msg);
- strbuf_commented_addf(&msg, "\n");
+ strbuf_commented_addf(&msg, comment_line_char, "\n");
}
- strbuf_commented_addf(&msg, _(merge_editor_comment));
+ strbuf_commented_addf(&msg, comment_line_char,
+ _(merge_editor_comment));
if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
- strbuf_commented_addf(&msg, _(scissors_editor_comment));
+ strbuf_commented_addf(&msg, comment_line_char,
+ _(scissors_editor_comment));
else
- strbuf_commented_addf(&msg,
+ strbuf_commented_addf(&msg, comment_line_char,
_(no_scissors_editor_comment), comment_line_char);
}
if (signoff)
diff --git a/builtin/mktag.c b/builtin/mktag.c
index 44fa56eff3..43e2766db4 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -2,10 +2,11 @@
#include "gettext.h"
#include "hex.h"
#include "parse-options.h"
+#include "strbuf.h"
#include "tag.h"
#include "replace-object.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "fsck.h"
#include "config.h"
diff --git a/builtin/mktree.c b/builtin/mktree.c
index 09a7bd5c5c..0eea810c7e 100644
--- a/builtin/mktree.c
+++ b/builtin/mktree.c
@@ -8,9 +8,10 @@
#include "gettext.h"
#include "hex.h"
#include "quote.h"
+#include "strbuf.h"
#include "tree.h"
#include "parse-options.h"
-#include "object-store.h"
+#include "object-store-ll.h"
static struct treeent {
unsigned mode;
diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c
index 1b5083f8b2..a31dde1db9 100644
--- a/builtin/multi-pack-index.c
+++ b/builtin/multi-pack-index.c
@@ -1,13 +1,13 @@
#include "builtin.h"
#include "abspath.h"
-#include "cache.h"
#include "config.h"
#include "environment.h"
#include "gettext.h"
#include "parse-options.h"
#include "midx.h"
+#include "strbuf.h"
#include "trace2.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#define BUILTIN_MIDX_WRITE_USAGE \
N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]" \
diff --git a/builtin/mv.c b/builtin/mv.c
index 32935af48e..ae462bd7d4 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -11,6 +11,7 @@
#include "config.h"
#include "environment.h"
#include "gettext.h"
+#include "name-hash.h"
#include "object-file.h"
#include "pathspec.h"
#include "lockfile.h"
@@ -18,6 +19,8 @@
#include "cache-tree.h"
#include "string-list.h"
#include "parse-options.h"
+#include "read-cache-ll.h"
+#include "repository.h"
#include "setup.h"
#include "submodule.h"
#include "entry.h"
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 593f0506a1..c3b722b36f 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -15,6 +15,7 @@
#include "hash-lookup.h"
#include "commit-slab.h"
#include "commit-graph.h"
+#include "wildmatch.h"
/*
* One day. See the 'name a rev shortly after epoch' test in t6120 when
@@ -573,7 +574,11 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
N_("ignore refs matching <pattern>")),
OPT_GROUP(""),
OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")),
- OPT_BOOL(0, "stdin", &transform_stdin, N_("deprecated: use --annotate-stdin instead")),
+ OPT_BOOL_F(0,
+ "stdin",
+ &transform_stdin,
+ N_("deprecated: use --annotate-stdin instead"),
+ PARSE_OPT_HIDDEN),
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,
diff --git a/builtin/notes.c b/builtin/notes.c
index d5788352b6..d0053042c9 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -7,15 +7,17 @@
* and builtin/tag.c by Kristian Høgsberg and Carlos Rica.
*/
-#include "cache.h"
-#include "config.h"
#include "builtin.h"
+#include "alloc.h"
+#include "config.h"
#include "editor.h"
+#include "environment.h"
#include "gettext.h"
#include "hex.h"
#include "notes.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "path.h"
#include "repository.h"
#include "blob.h"
#include "pretty.h"
@@ -29,11 +31,12 @@
#include "worktree.h"
#include "write-or-die.h"
+static char *separator = "\n";
static const char * const git_notes_usage[] = {
N_("git notes [--ref <notes-ref>] [list [<object>]]"),
- N_("git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
+ N_("git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--separator=<paragraph-break>] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
N_("git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"),
- N_("git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
+ N_("git notes [--ref <notes-ref>] append [--allow-empty] [--separator=<paragraph-break>] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
N_("git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"),
N_("git notes [--ref <notes-ref>] show [<object>]"),
N_("git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>"),
@@ -101,11 +104,26 @@ static const char * const git_notes_get_ref_usage[] = {
static const char note_template[] =
N_("Write/edit the notes for the following object:");
+enum notes_stripspace {
+ UNSPECIFIED = -1,
+ NO_STRIPSPACE = 0,
+ STRIPSPACE = 1,
+};
+
+struct note_msg {
+ enum notes_stripspace stripspace;
+ struct strbuf buf;
+};
+
struct note_data {
int given;
int use_editor;
+ int stripspace;
char *edit_path;
struct strbuf buf;
+ struct note_msg **messages;
+ size_t msg_nr;
+ size_t msg_alloc;
};
static void free_note_data(struct note_data *d)
@@ -115,6 +133,12 @@ static void free_note_data(struct note_data *d)
free(d->edit_path);
}
strbuf_release(&d->buf);
+
+ while (d->msg_nr--) {
+ strbuf_release(&d->messages[d->msg_nr]->buf);
+ free(d->messages[d->msg_nr]);
+ }
+ free(d->messages);
}
static int list_each_note(const struct object_id *object_oid,
@@ -157,7 +181,7 @@ static void write_commented_object(int fd, const struct object_id *object)
if (strbuf_read(&buf, show.out, 0) < 0)
die_errno(_("could not read 'show' output"));
- strbuf_add_commented_lines(&cbuf, buf.buf, buf.len);
+ strbuf_add_commented_lines(&cbuf, buf.buf, buf.len, comment_line_char);
write_or_die(fd, cbuf.buf, cbuf.len);
strbuf_release(&cbuf);
@@ -185,9 +209,10 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
copy_obj_to_fd(fd, old_note);
strbuf_addch(&buf, '\n');
- strbuf_add_commented_lines(&buf, "\n", strlen("\n"));
- strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template)));
- strbuf_add_commented_lines(&buf, "\n", strlen("\n"));
+ strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_char);
+ strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template)),
+ comment_line_char);
+ strbuf_add_commented_lines(&buf, "\n", strlen("\n"), comment_line_char);
write_or_die(fd, buf.buf, buf.len);
write_commented_object(fd, object);
@@ -199,7 +224,8 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
if (launch_editor(d->edit_path, &d->buf, NULL)) {
die(_("please supply the note contents using either -m or -F option"));
}
- strbuf_stripspace(&d->buf, 1);
+ if (d->stripspace)
+ strbuf_stripspace(&d->buf, comment_line_char);
}
}
@@ -215,66 +241,98 @@ static void write_note_data(struct note_data *d, struct object_id *oid)
}
}
+static void append_separator(struct strbuf *message)
+{
+ if (separator[strlen(separator) - 1] == '\n')
+ strbuf_addstr(message, separator);
+ else
+ strbuf_addf(message, "%s%s", separator, "\n");
+}
+
+static void concat_messages(struct note_data *d)
+{
+ struct strbuf msg = STRBUF_INIT;
+
+ size_t i;
+ for (i = 0; i < d->msg_nr ; i++) {
+ if (d->buf.len)
+ append_separator(&d->buf);
+ strbuf_add(&msg, d->messages[i]->buf.buf, d->messages[i]->buf.len);
+ strbuf_addbuf(&d->buf, &msg);
+ if ((d->stripspace == UNSPECIFIED &&
+ d->messages[i]->stripspace == STRIPSPACE) ||
+ d->stripspace == STRIPSPACE)
+ strbuf_stripspace(&d->buf, 0);
+ strbuf_reset(&msg);
+ }
+ strbuf_release(&msg);
+}
+
static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
{
struct note_data *d = opt->value;
+ struct note_msg *msg = xmalloc(sizeof(*msg));
BUG_ON_OPT_NEG(unset);
- strbuf_grow(&d->buf, strlen(arg) + 2);
- if (d->buf.len)
- strbuf_addch(&d->buf, '\n');
- strbuf_addstr(&d->buf, arg);
- strbuf_stripspace(&d->buf, 0);
-
- d->given = 1;
+ strbuf_init(&msg->buf, strlen(arg));
+ strbuf_addstr(&msg->buf, arg);
+ ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
+ d->messages[d->msg_nr - 1] = msg;
+ msg->stripspace = STRIPSPACE;
return 0;
}
static int parse_file_arg(const struct option *opt, const char *arg, int unset)
{
struct note_data *d = opt->value;
+ struct note_msg *msg = xmalloc(sizeof(*msg));
BUG_ON_OPT_NEG(unset);
- if (d->buf.len)
- strbuf_addch(&d->buf, '\n');
+ strbuf_init(&msg->buf , 0);
if (!strcmp(arg, "-")) {
- if (strbuf_read(&d->buf, 0, 1024) < 0)
+ if (strbuf_read(&msg->buf, 0, 1024) < 0)
die_errno(_("cannot read '%s'"), arg);
- } else if (strbuf_read_file(&d->buf, arg, 1024) < 0)
+ } else if (strbuf_read_file(&msg->buf, arg, 1024) < 0)
die_errno(_("could not open or read '%s'"), arg);
- strbuf_stripspace(&d->buf, 0);
- d->given = 1;
+ ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
+ d->messages[d->msg_nr - 1] = msg;
+ msg->stripspace = STRIPSPACE;
return 0;
}
static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
{
struct note_data *d = opt->value;
- char *buf;
+ struct note_msg *msg = xmalloc(sizeof(*msg));
+ char *value;
struct object_id object;
enum object_type type;
unsigned long len;
BUG_ON_OPT_NEG(unset);
- if (d->buf.len)
- strbuf_addch(&d->buf, '\n');
-
+ strbuf_init(&msg->buf, 0);
if (repo_get_oid(the_repository, arg, &object))
die(_("failed to resolve '%s' as a valid ref."), arg);
- if (!(buf = repo_read_object_file(the_repository, &object, &type, &len)))
+ if (!(value = repo_read_object_file(the_repository, &object, &type, &len)))
die(_("failed to read object '%s'."), arg);
if (type != OBJ_BLOB) {
- free(buf);
+ strbuf_release(&msg->buf);
+ free(value);
+ free(msg);
die(_("cannot read note data from non-blob object '%s'."), arg);
}
- strbuf_add(&d->buf, buf, len);
- free(buf);
- d->given = 1;
+ strbuf_add(&msg->buf, value, len);
+ free(value);
+
+ msg->buf.len = len;
+ ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
+ d->messages[d->msg_nr - 1] = msg;
+ msg->stripspace = NO_STRIPSPACE;
return 0;
}
@@ -408,7 +466,8 @@ static int add(int argc, const char **argv, const char *prefix)
struct notes_tree *t;
struct object_id object, new_note;
const struct object_id *note;
- struct note_data d = { 0, 0, NULL, STRBUF_INIT };
+ struct note_data d = { .buf = STRBUF_INIT, .stripspace = UNSPECIFIED };
+
struct option options[] = {
OPT_CALLBACK_F('m', "message", &d, N_("message"),
N_("note contents as a string"), PARSE_OPT_NONEG,
@@ -425,6 +484,10 @@ static int add(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "allow-empty", &allow_empty,
N_("allow storing empty note")),
OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE),
+ OPT_STRING(0, "separator", &separator, N_("separator"),
+ N_("insert <paragraph-break> between paragraphs")),
+ OPT_BOOL(0, "stripspace", &d.stripspace,
+ N_("remove unnecessary whitespace")),
OPT_END()
};
@@ -436,6 +499,10 @@ static int add(int argc, const char **argv, const char *prefix)
usage_with_options(git_notes_add_usage, options);
}
+ if (d.msg_nr)
+ concat_messages(&d);
+ d.given = !!d.buf.len;
+
object_ref = argc > 1 ? argv[1] : "HEAD";
if (repo_get_oid(the_repository, object_ref, &object))
@@ -574,7 +641,7 @@ static int append_edit(int argc, const char **argv, const char *prefix)
const struct object_id *note;
char *logmsg;
const char * const *usage;
- struct note_data d = { 0, 0, NULL, STRBUF_INIT };
+ struct note_data d = { .buf = STRBUF_INIT, .stripspace = UNSPECIFIED };
struct option options[] = {
OPT_CALLBACK_F('m', "message", &d, N_("message"),
N_("note contents as a string"), PARSE_OPT_NONEG,
@@ -590,6 +657,10 @@ static int append_edit(int argc, const char **argv, const char *prefix)
parse_reuse_arg),
OPT_BOOL(0, "allow-empty", &allow_empty,
N_("allow storing empty note")),
+ OPT_STRING(0, "separator", &separator, N_("separator"),
+ N_("insert <paragraph-break> between paragraphs")),
+ OPT_BOOL(0, "stripspace", &d.stripspace,
+ N_("remove unnecessary whitespace")),
OPT_END()
};
int edit = !strcmp(argv[0], "edit");
@@ -603,6 +674,10 @@ static int append_edit(int argc, const char **argv, const char *prefix)
usage_with_options(usage, options);
}
+ if (d.msg_nr)
+ concat_messages(&d);
+ d.given = !!d.buf.len;
+
if (d.given && edit)
fprintf(stderr, _("The -m/-F/-c/-C options have been deprecated "
"for the 'edit' subcommand.\n"
@@ -622,15 +697,17 @@ static int append_edit(int argc, const char **argv, const char *prefix)
/* Append buf to previous note contents */
unsigned long size;
enum object_type type;
- char *prev_buf = repo_read_object_file(the_repository, note,
- &type, &size);
+ struct strbuf buf = STRBUF_INIT;
+ char *prev_buf = repo_read_object_file(the_repository, note, &type, &size);
- strbuf_grow(&d.buf, size + 1);
- if (d.buf.len && prev_buf && size)
- strbuf_insertstr(&d.buf, 0, "\n");
if (prev_buf && size)
- strbuf_insert(&d.buf, 0, prev_buf, size);
+ strbuf_add(&buf, prev_buf, size);
+ if (d.buf.len && prev_buf && size)
+ append_separator(&buf);
+ strbuf_insert(&d.buf, 0, buf.buf, buf.len);
+
free(prev_buf);
+ strbuf_release(&buf);
}
if (d.buf.len || allow_empty) {
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index a5b466839b..af9352b228 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -34,7 +34,7 @@
#include "list.h"
#include "packfile.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "replace-object.h"
#include "dir.h"
#include "midx.h"
@@ -1331,7 +1331,7 @@ static int no_try_delta(const char *path)
if (!check)
check = attr_check_initl("delta", NULL);
- git_check_attr(the_repository->index, NULL, path, check);
+ git_check_attr(the_repository->index, path, check);
if (ATTR_FALSE(check->items[0].value))
return 1;
return 0;
diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c
index 43e9d12dfd..4c735ba069 100644
--- a/builtin/pack-redundant.c
+++ b/builtin/pack-redundant.c
@@ -11,7 +11,7 @@
#include "hex.h"
#include "repository.h"
#include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#define BLKSIZE 512
diff --git a/builtin/pack-refs.c b/builtin/pack-refs.c
index 9833815fb3..bcf383cac9 100644
--- a/builtin/pack-refs.c
+++ b/builtin/pack-refs.c
@@ -4,22 +4,45 @@
#include "parse-options.h"
#include "refs.h"
#include "repository.h"
+#include "revision.h"
static char const * const pack_refs_usage[] = {
- N_("git pack-refs [--all] [--no-prune]"),
+ N_("git pack-refs [--all] [--no-prune] [--include <pattern>] [--exclude <pattern>]"),
NULL
};
int cmd_pack_refs(int argc, const char **argv, const char *prefix)
{
unsigned int flags = PACK_REFS_PRUNE;
+ static struct ref_exclusions excludes = REF_EXCLUSIONS_INIT;
+ static struct string_list included_refs = STRING_LIST_INIT_NODUP;
+ struct pack_refs_opts pack_refs_opts = { .exclusions = &excludes,
+ .includes = &included_refs,
+ .flags = flags };
+ static struct string_list option_excluded_refs = STRING_LIST_INIT_NODUP;
+ struct string_list_item *item;
+
struct option opts[] = {
- OPT_BIT(0, "all", &flags, N_("pack everything"), PACK_REFS_ALL),
- OPT_BIT(0, "prune", &flags, N_("prune loose refs (default)"), PACK_REFS_PRUNE),
+ OPT_BIT(0, "all", &pack_refs_opts.flags, N_("pack everything"), PACK_REFS_ALL),
+ OPT_BIT(0, "prune", &pack_refs_opts.flags, N_("prune loose refs (default)"), PACK_REFS_PRUNE),
+ OPT_STRING_LIST(0, "include", pack_refs_opts.includes, N_("pattern"),
+ N_("references to include")),
+ OPT_STRING_LIST(0, "exclude", &option_excluded_refs, N_("pattern"),
+ N_("references to exclude")),
OPT_END(),
};
git_config(git_default_config, NULL);
if (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0))
usage_with_options(pack_refs_usage, opts);
- return refs_pack_refs(get_main_ref_store(the_repository), flags);
+
+ for_each_string_list_item(item, &option_excluded_refs)
+ add_ref_exclusion(pack_refs_opts.exclusions, item->string);
+
+ if (pack_refs_opts.flags & PACK_REFS_ALL)
+ string_list_append(pack_refs_opts.includes, "*");
+
+ if (!pack_refs_opts.includes->nr)
+ string_list_append(pack_refs_opts.includes, "refs/tags/*");
+
+ return refs_pack_refs(get_main_ref_store(the_repository), &pack_refs_opts);
}
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
index 9d5585d3a7..b7118b290b 100644
--- a/builtin/patch-id.c
+++ b/builtin/patch-id.c
@@ -1,8 +1,8 @@
-#include "cache.h"
#include "builtin.h"
#include "config.h"
#include "diff.h"
#include "gettext.h"
+#include "hash.h"
#include "hex.h"
#include "parse-options.h"
diff --git a/builtin/prune.c b/builtin/prune.c
index 5dc9b20720..cfb863ae84 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -1,19 +1,20 @@
-#include "cache.h"
+#include "builtin.h"
#include "commit.h"
#include "diff.h"
+#include "dir.h"
#include "environment.h"
#include "gettext.h"
#include "hex.h"
#include "revision.h"
-#include "builtin.h"
#include "reachable.h"
#include "parse-options.h"
+#include "path.h"
#include "progress.h"
#include "prune-packed.h"
#include "replace-object.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "shallow.h"
static const char * const prune_usage[] = {
diff --git a/builtin/pull.c b/builtin/pull.c
index 967368ebc6..7279b2a780 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -6,12 +6,12 @@
* Fetch one or more remote refs and merge it/them into the current HEAD.
*/
#define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
#include "advice.h"
#include "config.h"
-#include "builtin.h"
#include "gettext.h"
#include "hex.h"
+#include "merge.h"
#include "object-name.h"
#include "parse-options.h"
#include "exec-cmd.h"
@@ -19,6 +19,8 @@
#include "oid-array.h"
#include "remote.h"
#include "dir.h"
+#include "path.h"
+#include "read-cache-ll.h"
#include "rebase.h"
#include "refs.h"
#include "refspec.h"
@@ -965,13 +967,13 @@ static void show_advice_pull_non_ff(void)
"your next pull:\n"
"\n"
" git config pull.rebase false # merge\n"
- " git config pull.rebase true # rebase\n"
+ " git config pull.rebase merges # rebase\n"
" git config pull.ff only # fast-forward only\n"
"\n"
"You can replace \"git config\" with \"git config --global\" to set a default\n"
- "preference for all repositories. You can also pass --rebase, --no-rebase,\n"
- "or --ff-only on the command line to override the configured default per\n"
- "invocation.\n"));
+ "preference for all repositories. You can also pass --rebase=merges,\n"
+ "--no-rebase, or --ff-only on the command line to override the configured\n"
+ "default per invocation.\n"));
}
int cmd_pull(int argc, const char **argv, const char *prefix)
@@ -1047,7 +1049,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
if (!opt_autostash)
require_clean_work_tree(the_repository,
N_("pull with rebase"),
- _("please commit or stash them."), 1, 0);
+ _("Please commit or stash them."), 1, 0);
if (get_rebase_fork_point(&rebase_fork_point, repo, *refspecs))
oidclr(&rebase_fork_point);
diff --git a/builtin/push.c b/builtin/push.c
index 6001e4ae0a..6f8a8dc711 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -1,7 +1,7 @@
/*
* "git push"
*/
-#include "cache.h"
+#include "builtin.h"
#include "advice.h"
#include "branch.h"
#include "config.h"
@@ -10,10 +10,11 @@
#include "refs.h"
#include "refspec.h"
#include "run-command.h"
-#include "builtin.h"
#include "remote.h"
#include "transport.h"
#include "parse-options.h"
+#include "pkt-line.h"
+#include "repository.h"
#include "submodule.h"
#include "submodule-config.h"
#include "send-pack.h"
@@ -593,11 +594,12 @@ int cmd_push(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT__VERBOSITY(&verbosity),
OPT_STRING( 0 , "repo", &repo, N_("repository"), N_("repository")),
- OPT_BIT( 0 , "all", &flags, N_("push all refs"), TRANSPORT_PUSH_ALL),
+ OPT_BIT( 0 , "all", &flags, N_("push all branches"), TRANSPORT_PUSH_ALL),
+ OPT_ALIAS( 0 , "branches", "all"),
OPT_BIT( 0 , "mirror", &flags, N_("mirror all refs"),
(TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)),
OPT_BOOL('d', "delete", &deleterefs, N_("delete refs")),
- OPT_BOOL( 0 , "tags", &tags, N_("push tags (can't be used with --all or --mirror)")),
+ OPT_BOOL( 0 , "tags", &tags, N_("push tags (can't be used with --all or --branches or --mirror)")),
OPT_BIT('n' , "dry-run", &flags, N_("dry run"), TRANSPORT_PUSH_DRY_RUN),
OPT_BIT( 0, "porcelain", &flags, N_("machine-readable output"), TRANSPORT_PUSH_PORCELAIN),
OPT_BIT('f', "force", &flags, N_("force updates"), TRANSPORT_PUSH_FORCE),
@@ -640,7 +642,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
set_push_cert_flags(&flags, push_cert);
if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
- die(_("options '%s' and '%s' cannot be used together"), "--delete", "--all/--mirror/--tags");
+ die(_("options '%s' and '%s' cannot be used together"), "--delete", "--all/--branches/--mirror/--tags");
if (deleterefs && argc < 2)
die(_("--delete doesn't make sense without any refs"));
diff --git a/builtin/range-diff.c b/builtin/range-diff.c
index 04339a92ea..e455a4795c 100644
--- a/builtin/range-diff.c
+++ b/builtin/range-diff.c
@@ -1,10 +1,10 @@
-#include "cache.h"
#include "builtin.h"
#include "gettext.h"
#include "object-name.h"
#include "parse-options.h"
#include "range-diff.h"
#include "config.h"
+#include "repository.h"
#include "revision.h"
static const char * const builtin_range_diff_usage[] = {
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index d61cbad96d..6a9da00043 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -5,7 +5,7 @@
*/
#define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
#include "gettext.h"
#include "hex.h"
@@ -17,10 +17,11 @@
#include "cache-tree.h"
#include "unpack-trees.h"
#include "dir.h"
-#include "builtin.h"
#include "parse-options.h"
+#include "repository.h"
#include "resolve-undo.h"
#include "setup.h"
+#include "sparse-index.h"
#include "submodule.h"
#include "submodule-config.h"
@@ -117,6 +118,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
struct unpack_trees_options opts;
int prefix_set = 0;
struct lock_file lock_file = LOCK_INIT;
+ int recurse_submodules = -1;
const struct option read_tree_options[] = {
OPT__SUPER_PREFIX(&opts.super_prefix),
OPT_CALLBACK_F(0, "index-output", NULL, N_("file"),
@@ -150,9 +152,8 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
N_("skip applying sparse checkout filter")),
OPT_BOOL(0, "debug-unpack", &opts.internal.debug_unpack,
N_("debug unpack-trees")),
- OPT_CALLBACK_F(0, "recurse-submodules", NULL,
- "checkout", "control recursive updating of submodules",
- PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater),
+ OPT_BOOL(0, "recurse-submodules", &recurse_submodules,
+ N_("control recursive updating of submodules")),
OPT__QUIET(&opts.quiet, N_("suppress feedback messages")),
OPT_END()
};
@@ -178,6 +179,10 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
if (opts.reset)
opts.reset = UNPACK_RESET_OVERWRITE_UNTRACKED;
+ /* only modify the value if set explicitly */
+ if (recurse_submodules >= 0)
+ set_config_update_recurse_submodules(recurse_submodules);
+
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
diff --git a/builtin/rebase.c b/builtin/rebase.c
index ace1d5e8d1..555c5d3f02 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -24,6 +24,7 @@
#include "object-file.h"
#include "object-name.h"
#include "parse-options.h"
+#include "path.h"
#include "commit.h"
#include "diff.h"
#include "wt-status.h"
@@ -209,7 +210,7 @@ static int edit_todo_file(unsigned flags)
if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
return error_errno(_("could not read '%s'."), todo_file);
- strbuf_stripspace(&todo_list.buf, 1);
+ strbuf_stripspace(&todo_list.buf, comment_line_char);
res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);
if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,
NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index d22180435c..2b2faa5d18 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -30,9 +30,11 @@
#include "oidset.h"
#include "packfile.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "path.h"
#include "protocol.h"
#include "commit-reach.h"
+#include "server-info.h"
#include "trace.h"
#include "trace2.h"
#include "worktree.h"
@@ -89,7 +91,7 @@ static struct object_id push_cert_oid;
static struct signature_check sigcheck;
static const char *push_cert_nonce;
static const char *cert_nonce_seed;
-static struct string_list hidden_refs = STRING_LIST_INIT_DUP;
+static struct strvec hidden_refs = STRVEC_INIT;
static const char *NONCE_UNSOLICITED = "UNSOLICITED";
static const char *NONCE_BAD = "BAD";
@@ -336,7 +338,8 @@ static void write_head_info(void)
{
static struct oidset seen = OIDSET_INIT;
- for_each_ref(show_ref_cb, &seen);
+ refs_for_each_fullref_in(get_main_ref_store(the_repository), "",
+ hidden_refs.v, show_ref_cb, &seen);
for_each_alternate_ref(show_one_alternate_ref, &seen);
oidset_clear(&seen);
if (!sent_capabilities)
@@ -2618,7 +2621,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
packet_flush(1);
oid_array_clear(&shallow);
oid_array_clear(&ref);
- string_list_clear(&hidden_refs, 0);
+ strvec_clear(&hidden_refs);
free((void *)push_cert_nonce);
return 0;
}
diff --git a/builtin/reflog.c b/builtin/reflog.c
index a1fa0c855f..79b4ff04aa 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -1,8 +1,10 @@
#include "builtin.h"
#include "config.h"
#include "gettext.h"
+#include "repository.h"
#include "revision.h"
#include "reachable.h"
+#include "wildmatch.h"
#include "worktree.h"
#include "reflog.h"
#include "parse-options.h"
diff --git a/builtin/remote.c b/builtin/remote.c
index 1e0b137d97..7d96687ec7 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -2,6 +2,7 @@
#include "config.h"
#include "gettext.h"
#include "parse-options.h"
+#include "path.h"
#include "transport.h"
#include "remote.h"
#include "string-list.h"
@@ -10,7 +11,7 @@
#include "rebase.h"
#include "refs.h"
#include "refspec.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "strvec.h"
#include "commit-reach.h"
#include "progress.h"
diff --git a/builtin/repack.c b/builtin/repack.c
index bb7bf60e7c..6c896c9c80 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -6,7 +6,9 @@
#include "gettext.h"
#include "hex.h"
#include "parse-options.h"
+#include "path.h"
#include "run-command.h"
+#include "server-info.h"
#include "sigchain.h"
#include "strbuf.h"
#include "string-list.h"
@@ -14,7 +16,7 @@
#include "midx.h"
#include "packfile.h"
#include "prune-packed.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "promisor-remote.h"
#include "shallow.h"
#include "pack.h"
diff --git a/builtin/replace.c b/builtin/replace.c
index 981f189443..20c67a5889 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -8,22 +8,23 @@
* git-tag.sh and mktag.c by Linus Torvalds.
*/
-#include "cache.h"
-#include "config.h"
#include "builtin.h"
+#include "config.h"
#include "editor.h"
#include "environment.h"
#include "gettext.h"
#include "hex.h"
#include "refs.h"
#include "parse-options.h"
+#include "path.h"
#include "run-command.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "replace-object.h"
#include "repository.h"
#include "tag.h"
+#include "wildmatch.h"
static const char * const git_replace_usage[] = {
N_("git replace [-f] <object> <replacement>"),
diff --git a/builtin/rerere.c b/builtin/rerere.c
index d4a03707b1..0458db9cad 100644
--- a/builtin/rerere.c
+++ b/builtin/rerere.c
@@ -1,9 +1,9 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
#include "dir.h"
#include "gettext.h"
#include "parse-options.h"
+#include "repository.h"
#include "string-list.h"
#include "rerere.h"
#include "wrapper.h"
diff --git a/builtin/reset.c b/builtin/reset.c
index f99f32d580..f4d57d524e 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -13,6 +13,7 @@
#include "config.h"
#include "environment.h"
#include "gettext.h"
+#include "hash.h"
#include "hex.h"
#include "lockfile.h"
#include "tag.h"
@@ -26,9 +27,11 @@
#include "branch.h"
#include "object-name.h"
#include "parse-options.h"
+#include "path.h"
#include "unpack-trees.h"
#include "cache-tree.h"
#include "setup.h"
+#include "sparse-index.h"
#include "submodule.h"
#include "submodule-config.h"
#include "trace.h"
@@ -330,6 +333,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
struct object_id oid;
struct pathspec pathspec;
int intent_to_add = 0;
+ int recurse_submodules = -1;
const struct option options[] = {
OPT__QUIET(&quiet, N_("be quiet, only report errors")),
OPT_BOOL(0, "no-refresh", &no_refresh,
@@ -343,9 +347,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
N_("reset HEAD, index and working tree"), MERGE),
OPT_SET_INT(0, "keep", &reset_type,
N_("reset HEAD but keep local changes"), KEEP),
- OPT_CALLBACK_F(0, "recurse-submodules", NULL,
- "reset", "control recursive updating of submodules",
- PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater),
+ OPT_BOOL(0, "recurse-submodules", &recurse_submodules,
+ N_("control recursive updating of submodules")),
OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
OPT_BOOL('N', "intent-to-add", &intent_to_add,
N_("record only the fact that removed paths will be added later")),
@@ -360,6 +363,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
PARSE_OPT_KEEP_DASHDASH);
parse_args(&pathspec, argv, prefix, patch_mode, &rev);
+ /* only modify the value if set explicitly */
+ if (recurse_submodules >= 0)
+ set_config_update_recurse_submodules(recurse_submodules);
+
if (pathspec_from_file) {
if (patch_mode)
die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--patch");
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 6dc8be492a..ff715d6918 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
#include "commit.h"
#include "diff.h"
@@ -12,10 +12,9 @@
#include "object.h"
#include "object-name.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "pack.h"
#include "pack-bitmap.h"
-#include "builtin.h"
#include "log-tree.h"
#include "graph.h"
#include "bisect.h"
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 852e49e340..4a219ea93b 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -4,20 +4,22 @@
* Copyright (C) Linus Torvalds, 2005
*/
#define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
#include "abspath.h"
#include "alloc.h"
#include "config.h"
#include "commit.h"
#include "environment.h"
#include "gettext.h"
+#include "hash.h"
#include "hex.h"
#include "refs.h"
#include "quote.h"
-#include "builtin.h"
#include "object-name.h"
#include "parse-options.h"
+#include "path.h"
#include "diff.h"
+#include "read-cache-ll.h"
#include "revision.h"
#include "setup.h"
#include "split-index.h"
diff --git a/builtin/revert.c b/builtin/revert.c
index 0240ec8593..f6f07d9b53 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -5,6 +5,7 @@
#include "parse-options.h"
#include "diff.h"
#include "gettext.h"
+#include "repository.h"
#include "revision.h"
#include "rerere.h"
#include "dir.h"
diff --git a/builtin/rm.c b/builtin/rm.c
index d36072252e..463eeabcea 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -12,11 +12,15 @@
#include "dir.h"
#include "cache-tree.h"
#include "gettext.h"
+#include "hash.h"
#include "tree-walk.h"
#include "object-name.h"
#include "parse-options.h"
+#include "read-cache.h"
+#include "repository.h"
#include "string-list.h"
#include "setup.h"
+#include "sparse-index.h"
#include "submodule.h"
#include "pathspec.h"
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 46f4e0832a..1307ed2b88 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -1,11 +1,11 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
#include "commit.h"
#include "diff.h"
#include "environment.h"
#include "gettext.h"
#include "string-list.h"
+#include "repository.h"
#include "revision.h"
#include "utf8.h"
#include "mailmap.h"
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 20030b75e3..a203f13cb0 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -1,18 +1,20 @@
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
#include "environment.h"
#include "gettext.h"
+#include "hash.h"
#include "hex.h"
#include "pretty.h"
#include "refs.h"
-#include "builtin.h"
#include "color.h"
#include "strvec.h"
#include "object-name.h"
#include "parse-options.h"
+#include "repository.h"
#include "dir.h"
#include "commit-slab.h"
#include "date.h"
+#include "wildmatch.h"
static const char* show_branch_usage[] = {
N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
diff --git a/builtin/show-index.c b/builtin/show-index.c
index d4bbbbcd6c..540dc3dad1 100644
--- a/builtin/show-index.c
+++ b/builtin/show-index.c
@@ -1,9 +1,10 @@
#include "builtin.h"
-#include "cache.h"
#include "gettext.h"
+#include "hash.h"
#include "hex.h"
#include "pack.h"
#include "parse-options.h"
+#include "repository.h"
static const char *const show_index_usage[] = {
"git show-index [--object-format=<hash-algorithm>]",
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index a2243b4219..5110814f79 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -1,11 +1,10 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
#include "gettext.h"
#include "hex.h"
#include "refs.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "object.h"
#include "tag.h"
#include "string-list.h"
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 40d420f06c..5c8ffb1f75 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -1,5 +1,4 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
#include "dir.h"
#include "environment.h"
diff --git a/builtin/stash.c b/builtin/stash.c
index a7e17ffe38..84e83e0627 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -4,6 +4,7 @@
#include "config.h"
#include "environment.h"
#include "gettext.h"
+#include "hash.h"
#include "hex.h"
#include "object-name.h"
#include "parse-options.h"
@@ -17,9 +18,12 @@
#include "run-command.h"
#include "dir.h"
#include "entry.h"
+#include "preload-index.h"
+#include "read-cache.h"
#include "rerere.h"
#include "revision.h"
#include "setup.h"
+#include "sparse-index.h"
#include "log-tree.h"
#include "diffcore.h"
#include "exec-cmd.h"
diff --git a/builtin/stripspace.c b/builtin/stripspace.c
index 9451eb69ff..7b700a9fb1 100644
--- a/builtin/stripspace.c
+++ b/builtin/stripspace.c
@@ -1,6 +1,6 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
+#include "environment.h"
#include "gettext.h"
#include "parse-options.h"
#include "setup.h"
@@ -13,7 +13,7 @@ static void comment_lines(struct strbuf *buf)
size_t len;
msg = strbuf_detach(buf, &len);
- strbuf_add_commented_lines(buf, msg, len);
+ strbuf_add_commented_lines(buf, msg, len, comment_line_char);
free(msg);
}
@@ -58,7 +58,8 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
die_errno("could not read the input");
if (mode == STRIP_DEFAULT || mode == STRIP_COMMENTS)
- strbuf_stripspace(&buf, mode == STRIP_COMMENTS);
+ strbuf_stripspace(&buf,
+ mode == STRIP_COMMENTS ? comment_line_char : '\0');
else
comment_lines(&buf);
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 6bf8d666ce..baf6ff88c3 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -6,13 +6,16 @@
#include "gettext.h"
#include "hex.h"
#include "repository.h"
-#include "cache.h"
#include "config.h"
#include "parse-options.h"
#include "quote.h"
+#include "path.h"
#include "pathspec.h"
+#include "preload-index.h"
#include "dir.h"
+#include "read-cache.h"
#include "setup.h"
+#include "sparse-index.h"
#include "submodule.h"
#include "submodule-config.h"
#include "string-list.h"
@@ -26,7 +29,7 @@
#include "diff.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "advice.h"
#include "branch.h"
#include "list-objects-filter-options.h"
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
index 10198a74fa..a61fa3c0f8 100644
--- a/builtin/symbolic-ref.c
+++ b/builtin/symbolic-ref.c
@@ -1,6 +1,5 @@
#include "builtin.h"
#include "config.h"
-#include "cache.h"
#include "gettext.h"
#include "refs.h"
#include "parse-options.h"
diff --git a/builtin/tag.c b/builtin/tag.c
index 1850a6a6fd..6596997379 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -6,17 +6,17 @@
* Based on git-tag.sh and mktag.c by Linus Torvalds.
*/
-#include "cache.h"
+#include "builtin.h"
#include "advice.h"
#include "config.h"
-#include "builtin.h"
#include "editor.h"
#include "environment.h"
#include "gettext.h"
#include "hex.h"
#include "refs.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "path.h"
#include "tag.h"
#include "run-command.h"
#include "parse-options.h"
@@ -271,11 +271,10 @@ static const char message_advice_nested_tag[] =
static void create_tag(const struct object_id *object, const char *object_ref,
const char *tag,
struct strbuf *buf, struct create_tag_options *opt,
- struct object_id *prev, struct object_id *result)
+ struct object_id *prev, struct object_id *result, char *path)
{
enum object_type type;
struct strbuf header = STRBUF_INIT;
- char *path = NULL;
type = oid_object_info(the_repository, object, NULL);
if (type <= OBJ_NONE)
@@ -299,7 +298,6 @@ static void create_tag(const struct object_id *object, const char *object_ref,
int fd;
/* write the template message before editing: */
- path = git_pathdup("TAG_EDITMSG");
fd = xopen(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (opt->message_given) {
@@ -311,9 +309,11 @@ static void create_tag(const struct object_id *object, const char *object_ref,
struct strbuf buf = STRBUF_INIT;
strbuf_addch(&buf, '\n');
if (opt->cleanup_mode == CLEANUP_ALL)
- strbuf_commented_addf(&buf, _(tag_template), tag, comment_line_char);
+ strbuf_commented_addf(&buf, comment_line_char,
+ _(tag_template), tag, comment_line_char);
else
- strbuf_commented_addf(&buf, _(tag_template_nocleanup), tag, comment_line_char);
+ strbuf_commented_addf(&buf, comment_line_char,
+ _(tag_template_nocleanup), tag, comment_line_char);
write_or_die(fd, buf.buf, buf.len);
strbuf_release(&buf);
}
@@ -327,7 +327,8 @@ static void create_tag(const struct object_id *object, const char *object_ref,
}
if (opt->cleanup_mode != CLEANUP_NONE)
- strbuf_stripspace(buf, opt->cleanup_mode == CLEANUP_ALL);
+ strbuf_stripspace(buf,
+ opt->cleanup_mode == CLEANUP_ALL ? comment_line_char : '\0');
if (!opt->message_given && !buf->len)
die(_("no tag message?"));
@@ -341,10 +342,6 @@ static void create_tag(const struct object_id *object, const char *object_ref,
path);
exit(128);
}
- if (path) {
- unlink_or_warn(path);
- free(path);
- }
}
static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb)
@@ -443,7 +440,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
struct msg_arg msg = { .buf = STRBUF_INIT };
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
- struct ref_filter filter;
+ struct ref_filter filter = REF_FILTER_INIT;
struct ref_sorting *sorting;
struct string_list sorting_options = STRING_LIST_INIT_DUP;
struct ref_format format = REF_FORMAT_INIT;
@@ -495,13 +492,13 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
};
int ret = 0;
const char *only_in_list = NULL;
+ char *path = NULL;
setup_ref_filter_porcelain_msg();
git_config(git_tag_config, &sorting_options);
memset(&opt, 0, sizeof(opt));
- memset(&filter, 0, sizeof(filter));
filter.lines = -1;
opt.sign = -1;
@@ -629,7 +626,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (create_tag_object) {
if (force_sign_annotate && !annotate)
opt.sign = 1;
- create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object);
+ path = git_pathdup("TAG_EDITMSG");
+ create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object,
+ path);
}
transaction = ref_transaction_begin(&err);
@@ -637,8 +636,17 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
ref_transaction_update(transaction, ref.buf, &object, &prev,
create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
reflog_msg.buf, &err) ||
- ref_transaction_commit(transaction, &err))
+ ref_transaction_commit(transaction, &err)) {
+ if (path)
+ fprintf(stderr,
+ _("The tag message has been left in %s\n"),
+ path);
die("%s", err.buf);
+ }
+ if (path) {
+ unlink_or_warn(path);
+ free(path);
+ }
ref_transaction_free(transaction);
if (force && !is_null_oid(&prev) && !oideq(&prev, &object))
printf(_("Updated tag '%s' (was %s)\n"), tag,
@@ -646,6 +654,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
cleanup:
ref_sorting_release(sorting);
+ ref_filter_clear(&filter);
strbuf_release(&buf);
strbuf_release(&ref);
strbuf_release(&reflog_msg);
diff --git a/builtin/unpack-file.c b/builtin/unpack-file.c
index b35a4b9dfe..6842a6c499 100644
--- a/builtin/unpack-file.c
+++ b/builtin/unpack-file.c
@@ -2,7 +2,7 @@
#include "config.h"
#include "hex.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "wrapper.h"
static char *create_temp_file(struct object_id *oid)
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index 2c52c3a741..b4b46ae729 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -1,12 +1,11 @@
#include "builtin.h"
-#include "cache.h"
#include "bulk-checkin.h"
#include "config.h"
#include "environment.h"
#include "gettext.h"
#include "git-zlib.h"
#include "hex.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "object.h"
#include "delta.h"
#include "pack.h"
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 33b00cef15..aee3cb8cbd 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -4,25 +4,29 @@
* Copyright (C) Linus Torvalds, 2005
*/
#define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "builtin.h"
#include "bulk-checkin.h"
#include "config.h"
#include "environment.h"
#include "gettext.h"
+#include "hash.h"
#include "hex.h"
#include "lockfile.h"
#include "quote.h"
#include "cache-tree.h"
#include "tree-walk.h"
-#include "builtin.h"
#include "object-file.h"
#include "refs.h"
#include "resolve-undo.h"
#include "parse-options.h"
#include "pathspec.h"
#include "dir.h"
+#include "read-cache.h"
+#include "repository.h"
#include "setup.h"
+#include "sparse-index.h"
#include "split-index.h"
+#include "symlinks.h"
#include "fsmonitor.h"
#include "write-or-die.h"
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 6ca85420c3..242102273e 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -1,11 +1,12 @@
-#include "cache.h"
+#include "builtin.h"
#include "config.h"
#include "gettext.h"
+#include "hash.h"
#include "refs.h"
-#include "builtin.h"
#include "object-name.h"
#include "parse-options.h"
#include "quote.h"
+#include "repository.h"
#include "strvec.h"
static const char * const git_update_ref_usage[] = {
diff --git a/builtin/update-server-info.c b/builtin/update-server-info.c
index e7bff27ae4..1dc3971ede 100644
--- a/builtin/update-server-info.c
+++ b/builtin/update-server-info.c
@@ -1,8 +1,8 @@
-#include "cache.h"
-#include "config.h"
#include "builtin.h"
+#include "config.h"
#include "gettext.h"
#include "parse-options.h"
+#include "server-info.h"
static const char * const update_server_info_usage[] = {
"git update-server-info [-f | --force]",
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
index 7f9320ac6d..1b09e5e1aa 100644
--- a/builtin/upload-archive.c
+++ b/builtin/upload-archive.c
@@ -1,11 +1,12 @@
/*
* Copyright (c) 2006 Franck Bui-Huu
*/
-#include "cache.h"
#include "builtin.h"
#include "archive.h"
+#include "path.h"
#include "pkt-line.h"
#include "sideband.h"
+#include "repository.h"
#include "run-command.h"
#include "strvec.h"
diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c
index beb9dd0861..b02d479248 100644
--- a/builtin/upload-pack.c
+++ b/builtin/upload-pack.c
@@ -1,9 +1,9 @@
-#include "cache.h"
#include "builtin.h"
#include "exec-cmd.h"
#include "gettext.h"
#include "pkt-line.h"
#include "parse-options.h"
+#include "path.h"
#include "protocol.h"
#include "replace-object.h"
#include "upload-pack.h"
diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c
index 5d99b82a64..9680b58701 100644
--- a/builtin/verify-commit.c
+++ b/builtin/verify-commit.c
@@ -5,12 +5,11 @@
*
* Based on git-verify-tag
*/
-#include "cache.h"
-#include "config.h"
#include "builtin.h"
+#include "config.h"
#include "gettext.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "repository.h"
#include "commit.h"
#include "run-command.h"
diff --git a/builtin/verify-pack.c b/builtin/verify-pack.c
index 190fd69540..011dddd2dc 100644
--- a/builtin/verify-pack.c
+++ b/builtin/verify-pack.c
@@ -1,9 +1,9 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
#include "gettext.h"
#include "run-command.h"
#include "parse-options.h"
+#include "strbuf.h"
#define VERIFY_PACK_VERBOSE 01
#define VERIFY_PACK_STAT_ONLY 02
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index c6019a0ad8..d8753270eb 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -5,9 +5,8 @@
*
* Based on git-verify-tag.sh
*/
-#include "cache.h"
-#include "config.h"
#include "builtin.h"
+#include "config.h"
#include "gettext.h"
#include "tag.h"
#include "run-command.h"
diff --git a/builtin/worktree.c b/builtin/worktree.c
index a61bc32189..4780b3b5f5 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -1,8 +1,9 @@
-#include "cache.h"
+#include "builtin.h"
#include "abspath.h"
+#include "advice.h"
#include "checkout.h"
#include "config.h"
-#include "builtin.h"
+#include "copy.h"
#include "dir.h"
#include "environment.h"
#include "gettext.h"
@@ -10,9 +11,13 @@
#include "object-file.h"
#include "object-name.h"
#include "parse-options.h"
+#include "path.h"
#include "strvec.h"
#include "branch.h"
+#include "read-cache-ll.h"
#include "refs.h"
+#include "remote.h"
+#include "repository.h"
#include "run-command.h"
#include "hook.h"
#include "sigchain.h"
@@ -24,7 +29,8 @@
#define BUILTIN_WORKTREE_ADD_USAGE \
N_("git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]\n" \
- " [-b <new-branch>] <path> [<commit-ish>]")
+ " [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]")
+
#define BUILTIN_WORKTREE_LIST_USAGE \
N_("git worktree list [-v | --porcelain [-z]]")
#define BUILTIN_WORKTREE_LOCK_USAGE \
@@ -40,6 +46,23 @@
#define BUILTIN_WORKTREE_UNLOCK_USAGE \
N_("git worktree unlock <worktree>")
+#define WORKTREE_ADD_DWIM_ORPHAN_INFER_TEXT \
+ _("No possible source branch, inferring '--orphan'")
+
+#define WORKTREE_ADD_ORPHAN_WITH_DASH_B_HINT_TEXT \
+ _("If you meant to create a worktree containing a new orphan branch\n" \
+ "(branch with no commits) for this repository, you can do so\n" \
+ "using the --orphan flag:\n" \
+ "\n" \
+ " git worktree add --orphan -b %s %s\n")
+
+#define WORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT \
+ _("If you meant to create a worktree containing a new orphan branch\n" \
+ "(branch with no commits) for this repository, you can do so\n" \
+ "using the --orphan flag:\n" \
+ "\n" \
+ " git worktree add --orphan %s\n")
+
static const char * const git_worktree_usage[] = {
BUILTIN_WORKTREE_ADD_USAGE,
BUILTIN_WORKTREE_LIST_USAGE,
@@ -97,6 +120,7 @@ struct add_opts {
int detach;
int quiet;
int checkout;
+ int orphan;
const char *keep_locked;
};
@@ -370,6 +394,22 @@ static int checkout_worktree(const struct add_opts *opts,
return run_command(&cp);
}
+static int make_worktree_orphan(const char * ref, const struct add_opts *opts,
+ struct strvec *child_env)
+{
+ struct strbuf symref = STRBUF_INIT;
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ validate_new_branchname(ref, &symref, 0);
+ strvec_pushl(&cp.args, "symbolic-ref", "HEAD", symref.buf, NULL);
+ if (opts->quiet)
+ strvec_push(&cp.args, "--quiet");
+ strvec_pushv(&cp.env, child_env->v);
+ strbuf_release(&symref);
+ cp.git_cmd = 1;
+ return run_command(&cp);
+}
+
static int add_worktree(const char *path, const char *refname,
const struct add_opts *opts)
{
@@ -399,7 +439,7 @@ static int add_worktree(const char *path, const char *refname,
die_if_checked_out(symref.buf, 0);
}
commit = lookup_commit_reference_by_name(refname);
- if (!commit)
+ if (!commit && !opts->orphan)
die(_("invalid reference: %s"), refname);
name = worktree_basename(path, &len);
@@ -488,10 +528,10 @@ static int add_worktree(const char *path, const char *refname,
strvec_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
cp.git_cmd = 1;
- if (!is_branch)
+ if (!is_branch && commit) {
strvec_pushl(&cp.args, "update-ref", "HEAD",
oid_to_hex(&commit->object.oid), NULL);
- else {
+ } else {
strvec_pushl(&cp.args, "symbolic-ref", "HEAD",
symref.buf, NULL);
if (opts->quiet)
@@ -503,6 +543,10 @@ static int add_worktree(const char *path, const char *refname,
if (ret)
goto done;
+ if (opts->orphan &&
+ (ret = make_worktree_orphan(refname, opts, &child_env)))
+ goto done;
+
if (opts->checkout &&
(ret = checkout_worktree(opts, &child_env)))
goto done;
@@ -522,7 +566,7 @@ done:
* Hook failure does not warrant worktree deletion, so run hook after
* is_junk is cleared, but do return appropriate code when hook fails.
*/
- if (!ret && opts->checkout) {
+ if (!ret && opts->checkout && !opts->orphan) {
struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
strvec_pushl(&opt.env, "GIT_DIR", "GIT_WORK_TREE", NULL);
@@ -570,7 +614,7 @@ static void print_preparing_worktree_line(int detach,
else {
struct commit *commit = lookup_commit_reference_by_name(branch);
if (!commit)
- die(_("invalid reference: %s"), branch);
+ BUG(_("unreachable: invalid reference: %s"), branch);
fprintf_ln(stderr, _("Preparing worktree (detached HEAD %s)"),
repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV));
}
@@ -578,6 +622,123 @@ static void print_preparing_worktree_line(int detach,
}
}
+/**
+ * Callback to short circuit iteration over refs on the first reference
+ * corresponding to a valid oid.
+ *
+ * Returns 0 on failure and non-zero on success.
+ */
+static int first_valid_ref(const char *refname,
+ const struct object_id *oid,
+ int flags,
+ void *cb_data)
+{
+ return 1;
+}
+
+/**
+ * Verifies HEAD and determines whether there exist any valid local references.
+ *
+ * - Checks whether HEAD points to a valid reference.
+ *
+ * - Checks whether any valid local branches exist.
+ *
+ * - Emits a warning if there exist any valid branches but HEAD does not point
+ * to a valid reference.
+ *
+ * Returns 1 if any of the previous checks are true, otherwise returns 0.
+ */
+static int can_use_local_refs(const struct add_opts *opts)
+{
+ if (head_ref(first_valid_ref, NULL)) {
+ return 1;
+ } else if (for_each_branch_ref(first_valid_ref, NULL)) {
+ if (!opts->quiet) {
+ struct strbuf path = STRBUF_INIT;
+ struct strbuf contents = STRBUF_INIT;
+
+ strbuf_add_real_path(&path, get_worktree_git_dir(NULL));
+ strbuf_addstr(&path, "/HEAD");
+ strbuf_read_file(&contents, path.buf, 64);
+ strbuf_stripspace(&contents, 0);
+ strbuf_strip_suffix(&contents, "\n");
+
+ warning(_("HEAD points to an invalid (or orphaned) reference.\n"
+ "HEAD path: '%s'\n"
+ "HEAD contents: '%s'"),
+ path.buf, contents.buf);
+ strbuf_release(&path);
+ strbuf_release(&contents);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Reports whether the necessary flags were set and whether the repository has
+ * remote references to attempt DWIM tracking of upstream branches.
+ *
+ * 1. Checks that `--guess-remote` was used or `worktree.guessRemote = true`.
+ *
+ * 2. Checks whether any valid remote branches exist.
+ *
+ * 3. Checks that there exists at least one remote and emits a warning/error
+ * if both checks 1. and 2. are false (can be bypassed with `--force`).
+ *
+ * Returns 1 if checks 1. and 2. are true, otherwise 0.
+ */
+static int can_use_remote_refs(const struct add_opts *opts)
+{
+ if (!guess_remote) {
+ return 0;
+ } else if (for_each_remote_ref(first_valid_ref, NULL)) {
+ return 1;
+ } else if (!opts->force && remote_get(NULL)) {
+ die(_("No local or remote refs exist despite at least one remote\n"
+ "present, stopping; use 'add -f' to overide or fetch a remote first"));
+ }
+ return 0;
+}
+
+/**
+ * Determines whether `--orphan` should be inferred in the evaluation of
+ * `worktree add path/` or `worktree add -b branch path/` and emits an error
+ * if the supplied arguments would produce an illegal combination when the
+ * `--orphan` flag is included.
+ *
+ * `opts` and `opt_track` contain the other options & flags supplied to the
+ * command.
+ *
+ * remote determines whether to check `can_use_remote_refs()` or not. This
+ * is primarily to differentiate between the basic `add` DWIM and `add -b`.
+ *
+ * Returns 1 when inferring `--orphan`, 0 otherwise, and emits an error when
+ * `--orphan` is inferred but doing so produces an illegal combination of
+ * options and flags. Additionally produces an error when remote refs are
+ * checked and the repo is in a state that looks like the user added a remote
+ * but forgot to fetch (and did not override the warning with -f).
+ */
+static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote)
+{
+ if (can_use_local_refs(opts)) {
+ return 0;
+ } else if (remote && can_use_remote_refs(opts)) {
+ return 0;
+ } else if (!opts->quiet) {
+ fprintf_ln(stderr, WORKTREE_ADD_DWIM_ORPHAN_INFER_TEXT);
+ }
+
+ if (opt_track) {
+ die(_("'%s' and '%s' cannot be used together"), "--orphan",
+ "--track");
+ } else if (!opts->checkout) {
+ die(_("'%s' and '%s' cannot be used together"), "--orphan",
+ "--no-checkout");
+ }
+ return 1;
+}
+
static const char *dwim_branch(const char *path, const char **new_branch)
{
int n;
@@ -614,6 +775,7 @@ static int add(int ac, const char **av, const char *prefix)
const char *opt_track = NULL;
const char *lock_reason = NULL;
int keep_locked = 0;
+ int used_new_branch_options;
struct option options[] = {
OPT__FORCE(&opts.force,
N_("checkout <branch> even if already checked out in other worktree"),
@@ -622,6 +784,7 @@ static int add(int ac, const char **av, const char *prefix)
N_("create a new branch")),
OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
N_("create or reset a branch")),
+ OPT_BOOL(0, "orphan", &opts.orphan, N_("create unborn/orphaned branch")),
OPT_BOOL('d', "detach", &opts.detach, N_("detach HEAD at named commit")),
OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
OPT_BOOL(0, "lock", &keep_locked, N_("keep the new working tree locked")),
@@ -642,6 +805,17 @@ static int add(int ac, const char **av, const char *prefix)
ac = parse_options(ac, av, prefix, options, git_worktree_add_usage, 0);
if (!!opts.detach + !!new_branch + !!new_branch_force > 1)
die(_("options '%s', '%s', and '%s' cannot be used together"), "-b", "-B", "--detach");
+ if (opts.detach && opts.orphan)
+ die(_("options '%s', and '%s' cannot be used together"),
+ "--orphan", "--detach");
+ if (opts.orphan && opt_track)
+ die(_("'%s' and '%s' cannot be used together"), "--orphan", "--track");
+ if (opts.orphan && !opts.checkout)
+ die(_("'%s' and '%s' cannot be used together"), "--orphan",
+ "--no-checkout");
+ if (opts.orphan && ac == 2)
+ die(_("'%s' and '%s' cannot be used together"), "--orphan",
+ _("<commit-ish>"));
if (lock_reason && !keep_locked)
die(_("the option '%s' requires '%s'"), "--reason", "--lock");
if (lock_reason)
@@ -654,6 +828,7 @@ static int add(int ac, const char **av, const char *prefix)
path = prefix_filename(prefix, av[0]);
branch = ac < 2 ? "HEAD" : av[1];
+ used_new_branch_options = new_branch || new_branch_force;
if (!strcmp(branch, "-"))
branch = "@{-1}";
@@ -670,13 +845,28 @@ static int add(int ac, const char **av, const char *prefix)
strbuf_release(&symref);
}
- if (ac < 2 && !new_branch && !opts.detach) {
+ if (opts.orphan && !new_branch) {
+ int n;
+ const char *s = worktree_basename(path, &n);
+ new_branch = xstrndup(s, n);
+ } else if (opts.orphan) {
+ // No-op
+ } else if (opts.detach) {
+ // Check HEAD
+ if (!strcmp(branch, "HEAD"))
+ can_use_local_refs(&opts);
+ } else if (ac < 2 && new_branch) {
+ // DWIM: Infer --orphan when repo has no refs.
+ opts.orphan = dwim_orphan(&opts, !!opt_track, 0);
+ } else if (ac < 2) {
+ // DWIM: Guess branch name from path.
const char *s = dwim_branch(path, &new_branch);
if (s)
branch = s;
- }
- if (ac == 2 && !new_branch && !opts.detach) {
+ // DWIM: Infer --orphan when repo has no refs.
+ opts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1);
+ } else if (ac == 2) {
struct object_id oid;
struct commit *commit;
const char *remote;
@@ -689,11 +879,31 @@ static int add(int ac, const char **av, const char *prefix)
branch = remote;
}
}
+
+ if (!strcmp(branch, "HEAD"))
+ can_use_local_refs(&opts);
+
}
+
+ if (!opts.orphan && !lookup_commit_reference_by_name(branch)) {
+ int attempt_hint = !opts.quiet && (ac < 2);
+ if (attempt_hint && used_new_branch_options) {
+ advise_if_enabled(ADVICE_WORKTREE_ADD_ORPHAN,
+ WORKTREE_ADD_ORPHAN_WITH_DASH_B_HINT_TEXT,
+ new_branch, path);
+ } else if (attempt_hint) {
+ advise_if_enabled(ADVICE_WORKTREE_ADD_ORPHAN,
+ WORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT, path);
+ }
+ die(_("invalid reference: %s"), branch);
+ }
+
if (!opts.quiet)
print_preparing_worktree_line(opts.detach, branch, new_branch, !!new_branch_force);
- if (new_branch) {
+ if (opts.orphan) {
+ branch = new_branch;
+ } else if (new_branch) {
struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
strvec_push(&cp.args, "branch");
diff --git a/builtin/write-tree.c b/builtin/write-tree.c
index 32e302a813..66e83d0ecb 100644
--- a/builtin/write-tree.c
+++ b/builtin/write-tree.c
@@ -5,7 +5,6 @@
*/
#define USE_THE_INDEX_VARIABLE
#include "builtin.h"
-#include "cache.h"
#include "config.h"
#include "environment.h"
#include "gettext.h"
@@ -13,6 +12,7 @@
#include "tree.h"
#include "cache-tree.h"
#include "parse-options.h"
+#include "repository.h"
static const char * const write_tree_usage[] = {
N_("git write-tree [--missing-ok] [--prefix=<prefix>/]"),
diff --git a/bulk-checkin.c b/bulk-checkin.c
index d843279715..e2f71db0f6 100644
--- a/bulk-checkin.c
+++ b/bulk-checkin.c
@@ -16,7 +16,7 @@
#include "tmp-objdir.h"
#include "packfile.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "wrapper.h"
static int odb_transaction_nesting;
diff --git a/bundle-uri.c b/bundle-uri.c
index f22490a2ca..a97dcbdb86 100644
--- a/bundle-uri.c
+++ b/bundle-uri.c
@@ -1,9 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "bundle-uri.h"
#include "bundle.h"
+#include "copy.h"
#include "environment.h"
#include "gettext.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "refs.h"
#include "run-command.h"
#include "hashmap.h"
diff --git a/bundle.c b/bundle.c
index a5505368de..8d5936c421 100644
--- a/bundle.c
+++ b/bundle.c
@@ -4,7 +4,7 @@
#include "environment.h"
#include "gettext.h"
#include "hex.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "repository.h"
#include "object.h"
#include "commit.h"
diff --git a/cache-tree.c b/cache-tree.c
index ebfe649b33..84d7491420 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "alloc.h"
#include "environment.h"
#include "hex.h"
@@ -8,7 +8,8 @@
#include "cache-tree.h"
#include "bulk-checkin.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "read-cache-ll.h"
#include "replace-object.h"
#include "promisor-remote.h"
#include "sparse-index.h"
diff --git a/checkout.c b/checkout.c
index 04238b2713..4256e71a7c 100644
--- a/checkout.c
+++ b/checkout.c
@@ -2,6 +2,7 @@
#include "object-name.h"
#include "remote.h"
#include "refspec.h"
+#include "repository.h"
#include "checkout.h"
#include "config.h"
#include "strbuf.h"
diff --git a/checkout.h b/checkout.h
index 1917f3b323..3c514a5ab4 100644
--- a/checkout.h
+++ b/checkout.h
@@ -1,7 +1,7 @@
#ifndef CHECKOUT_H
#define CHECKOUT_H
-#include "hash.h"
+#include "hash-ll.h"
/*
* Check if the branch name uniquely matches a branch name on a remote
diff --git a/chunk-format.c b/chunk-format.c
index 60a73c1b14..e7d613c907 100644
--- a/chunk-format.c
+++ b/chunk-format.c
@@ -3,6 +3,7 @@
#include "chunk-format.h"
#include "csum-file.h"
#include "gettext.h"
+#include "hash.h"
#include "trace2.h"
/*
diff --git a/chunk-format.h b/chunk-format.h
index 025c38f938..c7794e84ad 100644
--- a/chunk-format.h
+++ b/chunk-format.h
@@ -1,7 +1,7 @@
#ifndef CHUNK_FORMAT_H
#define CHUNK_FORMAT_H
-#include "hash.h"
+#include "hash-ll.h"
struct hashfile;
struct chunkfile;
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index a18b13a41d..2528f25e31 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -29,6 +29,7 @@ linux-TEST-vars)
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
export GIT_TEST_NO_WRITE_REV_INDEX=1
export GIT_TEST_CHECKOUT_WORKERS=2
+ export GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1
;;
linux-clang)
export GIT_TEST_DEFAULT_HASH=sha1
diff --git a/color.c b/color.c
index 6031998d3e..83abb11eda 100644
--- a/color.c
+++ b/color.c
@@ -1,10 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "config.h"
#include "color.h"
#include "editor.h"
#include "gettext.h"
#include "hex.h"
#include "pager.h"
+#include "strbuf.h"
static int git_use_color_default = GIT_COLOR_AUTO;
int color_stdout_is_tty = -1;
diff --git a/combine-diff.c b/combine-diff.c
index f7e9fb5747..11e9d7494a 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -1,5 +1,5 @@
-#include "cache.h"
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "object-store-ll.h"
#include "commit.h"
#include "convert.h"
#include "blob.h"
@@ -13,6 +13,7 @@
#include "xdiff/xmacros.h"
#include "log-tree.h"
#include "refs.h"
+#include "tree.h"
#include "userdiff.h"
#include "oid-array.h"
#include "revision.h"
diff --git a/commit-graph.c b/commit-graph.c
index 43558b4d9b..9e989f4349 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -12,8 +12,9 @@
#include "hash-lookup.h"
#include "commit-graph.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "oid-array.h"
+#include "path.h"
#include "alloc.h"
#include "hashmap.h"
#include "replace-object.h"
@@ -23,6 +24,7 @@
#include "shallow.h"
#include "json-writer.h"
#include "trace2.h"
+#include "tree.h"
#include "chunk-format.h"
#include "wrapper.h"
diff --git a/commit-graph.h b/commit-graph.h
index 83aaa1dbb9..5e534f0fcc 100644
--- a/commit-graph.h
+++ b/commit-graph.h
@@ -1,7 +1,7 @@
#ifndef COMMIT_GRAPH_H
#define COMMIT_GRAPH_H
-#include "object-store.h"
+#include "object-store-ll.h"
#include "oidset.h"
#define GIT_TEST_COMMIT_GRAPH "GIT_TEST_COMMIT_GRAPH"
diff --git a/commit.c b/commit.c
index 878b4473e4..2c3f53ede1 100644
--- a/commit.c
+++ b/commit.c
@@ -7,7 +7,7 @@
#include "hex.h"
#include "repository.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "pkt-line.h"
#include "utf8.h"
#include "diff.h"
@@ -26,6 +26,7 @@
#include "run-command.h"
#include "setup.h"
#include "shallow.h"
+#include "tree.h"
#include "hook.h"
static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
@@ -72,12 +73,19 @@ struct commit *lookup_commit_object(struct repository *r,
}
-struct commit *lookup_commit(struct repository *r, const struct object_id *oid)
+struct commit *lookup_commit_type(struct repository *r,
+ const struct object_id *oid,
+ enum object_type type)
{
struct object *obj = lookup_object(r, oid);
if (!obj)
return create_object(r, oid, alloc_commit_node(r));
- return object_as_type(obj, OBJ_COMMIT, 0);
+ return object_as_type_hint(obj, OBJ_COMMIT, type);
+}
+
+struct commit *lookup_commit(struct repository *r, const struct object_id *oid)
+{
+ return lookup_commit_type(r, oid, OBJ_NONE);
}
struct commit *lookup_commit_reference_by_name(const char *name)
@@ -96,6 +104,7 @@ struct commit *lookup_commit_reference_by_name(const char *name)
static timestamp_t parse_commit_date(const char *buf, const char *tail)
{
const char *dateptr;
+ const char *eol;
if (buf + 6 >= tail)
return 0;
@@ -107,16 +116,56 @@ static timestamp_t parse_commit_date(const char *buf, const char *tail)
return 0;
if (memcmp(buf, "committer", 9))
return 0;
- while (buf < tail && *buf++ != '>')
- /* nada */;
- if (buf >= tail)
+
+ /*
+ * Jump to end-of-line so that we can walk backwards to find the
+ * end-of-email ">". This is more forgiving of malformed cases
+ * because unexpected characters tend to be in the name and email
+ * fields.
+ */
+ eol = memchr(buf, '\n', tail - buf);
+ if (!eol)
return 0;
- dateptr = buf;
- while (buf < tail && *buf++ != '\n')
- /* nada */;
- if (buf >= tail)
+ dateptr = eol;
+ while (dateptr > buf && dateptr[-1] != '>')
+ dateptr--;
+ if (dateptr == buf)
return 0;
- /* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
+
+ /*
+ * Trim leading whitespace, but make sure we have at least one
+ * non-whitespace character, as parse_timestamp() will otherwise walk
+ * right past the newline we found in "eol" when skipping whitespace
+ * itself.
+ *
+ * In theory it would be sufficient to allow any character not matched
+ * by isspace(), but there's a catch: our isspace() does not
+ * necessarily match the behavior of parse_timestamp(), as the latter
+ * is implemented by system routines which match more exotic control
+ * codes, or even locale-dependent sequences.
+ *
+ * Since we expect the timestamp to be a number, we can check for that.
+ * Anything else (e.g., a non-numeric token like "foo") would just
+ * cause parse_timestamp() to return 0 anyway.
+ */
+ while (dateptr < eol && isspace(*dateptr))
+ dateptr++;
+ if (!isdigit(*dateptr) && *dateptr != '-')
+ return 0;
+
+ /*
+ * We know there is at least one digit (or dash), so we'll begin
+ * parsing there and stop at worst case at eol.
+ *
+ * Note that we may feed parse_timestamp() extra characters here if the
+ * commit is malformed, and it will parse as far as it can. For
+ * example, "123foo456" would return "123". That might be questionable
+ * (versus returning "0"), but it would help in a hypothetical case
+ * like "123456+0100", where the whitespace from the timezone is
+ * missing. Since such syntactic errors may be baked into history and
+ * hard to correct now, let's err on trying to make our best guess
+ * here, rather than insist on perfect syntax.
+ */
return parse_timestamp(dateptr, NULL, 10);
}
diff --git a/commit.h b/commit.h
index 69b2f376e9..e968118c88 100644
--- a/commit.h
+++ b/commit.h
@@ -2,13 +2,10 @@
#define COMMIT_H
#include "object.h"
-#include "tree.h"
-#include "strbuf.h"
-#include "decorate.h"
-#include "gpg-interface.h"
-#include "string-list.h"
-#include "pretty.h"
-#include "commit-slab.h"
+
+struct signature_check;
+struct strbuf;
+struct tree;
#define COMMIT_NOT_FROM_GRAPH 0xFFFFFFFF
#define GENERATION_NUMBER_INFINITY ((1ULL << 63) - 1)
@@ -78,6 +75,8 @@ struct commit *lookup_commit_object(struct repository *r, const struct object_id
* "oid" is not in the object cache.
*/
struct commit *lookup_commit(struct repository *r, const struct object_id *oid);
+struct commit *lookup_commit_type(struct repository *r, const struct object_id *oid,
+ enum object_type type);
struct commit *lookup_commit_reference(struct repository *r,
const struct object_id *oid);
struct commit *lookup_commit_reference_gently(struct repository *r,
diff --git a/common-main.c b/common-main.c
index f319317353..033778b3c5 100644
--- a/common-main.c
+++ b/common-main.c
@@ -1,8 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "exec-cmd.h"
#include "gettext.h"
#include "attr.h"
+#include "repository.h"
#include "setup.h"
+#include "strbuf.h"
#include "trace2.h"
/*
diff --git a/compat/fsmonitor/fsm-health-darwin.c b/compat/fsmonitor/fsm-health-darwin.c
index b9f709e854..5b1709d63f 100644
--- a/compat/fsmonitor/fsm-health-darwin.c
+++ b/compat/fsmonitor/fsm-health-darwin.c
@@ -1,6 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "config.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
#include "fsm-health.h"
#include "fsmonitor--daemon.h"
diff --git a/compat/fsmonitor/fsm-health-win32.c b/compat/fsmonitor/fsm-health-win32.c
index fe11bdd9ce..2d4e245beb 100644
--- a/compat/fsmonitor/fsm-health-win32.c
+++ b/compat/fsmonitor/fsm-health-win32.c
@@ -1,6 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "config.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
#include "fsm-health.h"
#include "fsmonitor--daemon.h"
#include "gettext.h"
diff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c
index eb25123fa1..6f3a95410c 100644
--- a/compat/fsmonitor/fsm-ipc-darwin.c
+++ b/compat/fsmonitor/fsm-ipc-darwin.c
@@ -1,8 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "config.h"
+#include "gettext.h"
#include "hex.h"
+#include "path.h"
+#include "repository.h"
#include "strbuf.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
#include "fsmonitor-ipc.h"
#include "fsmonitor-path-utils.h"
diff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c
index c9536dfb66..8928fa93ce 100644
--- a/compat/fsmonitor/fsm-ipc-win32.c
+++ b/compat/fsmonitor/fsm-ipc-win32.c
@@ -1,6 +1,7 @@
#include "git-compat-util.h"
#include "config.h"
#include "fsmonitor-ipc.h"
+#include "path.h"
const char *fsmonitor_ipc__get_path(struct repository *r) {
static char *ret;
diff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c
index 5eb6402ab8..36c7e13281 100644
--- a/compat/fsmonitor/fsm-listen-darwin.c
+++ b/compat/fsmonitor/fsm-listen-darwin.c
@@ -23,12 +23,14 @@
#endif
#endif
-#include "cache.h"
-#include "fsmonitor.h"
+#include "git-compat-util.h"
+#include "fsmonitor-ll.h"
#include "fsm-listen.h"
#include "fsmonitor--daemon.h"
#include "fsmonitor-path-utils.h"
#include "gettext.h"
+#include "string-list.h"
+#include "trace.h"
struct fsm_listen_data
{
diff --git a/compat/fsmonitor/fsm-listen-win32.c b/compat/fsmonitor/fsm-listen-win32.c
index 677b1bbdec..a361a7db20 100644
--- a/compat/fsmonitor/fsm-listen-win32.c
+++ b/compat/fsmonitor/fsm-listen-win32.c
@@ -1,6 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "config.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
#include "fsm-listen.h"
#include "fsmonitor--daemon.h"
#include "gettext.h"
diff --git a/compat/fsmonitor/fsm-path-utils-darwin.c b/compat/fsmonitor/fsm-path-utils-darwin.c
index 45eb4a9b9e..049f97eaaf 100644
--- a/compat/fsmonitor/fsm-path-utils-darwin.c
+++ b/compat/fsmonitor/fsm-path-utils-darwin.c
@@ -1,6 +1,8 @@
-#include "fsmonitor.h"
+#include "git-compat-util.h"
+#include "fsmonitor-ll.h"
#include "fsmonitor-path-utils.h"
#include "gettext.h"
+#include "trace.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
diff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c
index 4024baafb9..c8a3e9dcdb 100644
--- a/compat/fsmonitor/fsm-path-utils-win32.c
+++ b/compat/fsmonitor/fsm-path-utils-win32.c
@@ -1,7 +1,8 @@
-#include "cache.h"
-#include "fsmonitor.h"
+#include "git-compat-util.h"
+#include "fsmonitor-ll.h"
#include "fsmonitor-path-utils.h"
#include "gettext.h"
+#include "trace.h"
/*
* Check remote working directory protocol.
diff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c
index 58b623fbb9..a382590635 100644
--- a/compat/fsmonitor/fsm-settings-darwin.c
+++ b/compat/fsmonitor/fsm-settings-darwin.c
@@ -1,6 +1,6 @@
#include "git-compat-util.h"
#include "config.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
#include "fsmonitor-ipc.h"
#include "fsmonitor-settings.h"
#include "fsmonitor-path-utils.h"
diff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c
index a8af31b71d..b6f6744494 100644
--- a/compat/fsmonitor/fsm-settings-win32.c
+++ b/compat/fsmonitor/fsm-settings-win32.c
@@ -1,7 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "config.h"
#include "repository.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
#include "fsmonitor-settings.h"
#include "fsmonitor-path-utils.h"
diff --git a/compat/mingw.c b/compat/mingw.c
index abbc3faf32..559abb1c61 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -6,13 +6,13 @@
#include <wchar.h>
#include "../strbuf.h"
#include "../run-command.h"
-#include "../cache.h"
#include "../abspath.h"
#include "../alloc.h"
#include "win32/lazyload.h"
#include "../config.h"
#include "../environment.h"
#include "../trace2.h"
+#include "../symlinks.h"
#include "../wrapper.h"
#include "dir.h"
#include "gettext.h"
diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c
index 8a9881db07..0bd5c24250 100644
--- a/compat/precompose_utf8.c
+++ b/compat/precompose_utf8.c
@@ -5,10 +5,12 @@
#define PRECOMPOSE_UNICODE_C
-#include "cache.h"
+#include "git-compat-util.h"
#include "config.h"
#include "environment.h"
#include "gettext.h"
+#include "path.h"
+#include "strbuf.h"
#include "utf8.h"
#include "precompose_utf8.h"
diff --git a/compat/sha1-chunked.c b/compat/sha1-chunked.c
index 6adfcfd540..a4a6f930d7 100644
--- a/compat/sha1-chunked.c
+++ b/compat/sha1-chunked.c
@@ -1,4 +1,5 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "hash-ll.h"
int git_SHA1_Update_Chunked(platform_SHA_CTX *c, const void *data, size_t len)
{
diff --git a/compat/simple-ipc/ipc-win32.c b/compat/simple-ipc/ipc-win32.c
index 6adce3c650..5276f658a1 100644
--- a/compat/simple-ipc/ipc-win32.c
+++ b/compat/simple-ipc/ipc-win32.c
@@ -1,6 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "abspath.h"
#include "gettext.h"
+#include "hex.h"
#include "simple-ipc.h"
#include "strbuf.h"
#include "pkt-line.h"
@@ -21,27 +22,27 @@
static int initialize_pipe_name(const char *path, wchar_t *wpath, size_t alloc)
{
int off = 0;
- struct strbuf realpath = STRBUF_INIT;
-
- if (!strbuf_realpath(&realpath, path, 0))
- return -1;
+ int ret = 0;
+ git_SHA_CTX sha1ctx;
+ struct strbuf real_path = STRBUF_INIT;
+ struct strbuf pipe_name = STRBUF_INIT;
+ unsigned char hash[GIT_MAX_RAWSZ];
- off = swprintf(wpath, alloc, L"\\\\.\\pipe\\");
- if (xutftowcs(wpath + off, realpath.buf, alloc - off) < 0)
+ if (!strbuf_realpath(&real_path, path, 0))
return -1;
- /* Handle drive prefix */
- if (wpath[off] && wpath[off + 1] == L':') {
- wpath[off + 1] = L'_';
- off += 2;
- }
+ git_SHA1_Init(&sha1ctx);
+ git_SHA1_Update(&sha1ctx, real_path.buf, real_path.len);
+ git_SHA1_Final(hash, &sha1ctx);
+ strbuf_release(&real_path);
- for (; wpath[off]; off++)
- if (wpath[off] == L'/')
- wpath[off] = L'\\';
+ strbuf_addf(&pipe_name, "git-fsmonitor-%s", hash_to_hex(hash));
+ off = swprintf(wpath, alloc, L"\\\\.\\pipe\\");
+ if (xutftowcs(wpath + off, pipe_name.buf, alloc - off) < 0)
+ ret = -1;
- strbuf_release(&realpath);
- return 0;
+ strbuf_release(&pipe_name);
+ return ret;
}
static enum ipc_active_state get_active_state(wchar_t *pipe_path)
diff --git a/compat/win32/trace2_win32_process_info.c b/compat/win32/trace2_win32_process_info.c
index e3e895c78a..a2b1506f9c 100644
--- a/compat/win32/trace2_win32_process_info.c
+++ b/compat/win32/trace2_win32_process_info.c
@@ -1,5 +1,6 @@
-#include "../../cache.h"
+#include "../../git-compat-util.h"
#include "../../json-writer.h"
+#include "../../repository.h"
#include "../../trace2.h"
#include "lazyload.h"
#include <Psapi.h>
diff --git a/config.c b/config.c
index 43b0d3fb57..e08e2332a6 100644
--- a/config.c
+++ b/config.c
@@ -5,7 +5,7 @@
* Copyright (C) Johannes Schindelin, 2005
*
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "abspath.h"
#include "advice.h"
#include "alloc.h"
@@ -25,16 +25,20 @@
#include "hashmap.h"
#include "string-list.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "pager.h"
+#include "path.h"
#include "utf8.h"
#include "dir.h"
#include "color.h"
#include "replace-object.h"
#include "refs.h"
#include "setup.h"
+#include "strvec.h"
#include "trace2.h"
+#include "wildmatch.h"
#include "worktree.h"
+#include "ws.h"
#include "wrapper.h"
#include "write-or-die.h"
diff --git a/connect.c b/connect.c
index 3a0186280c..37674f7112 100644
--- a/connect.c
+++ b/connect.c
@@ -12,6 +12,7 @@
#include "url.h"
#include "string-list.h"
#include "oid-array.h"
+#include "path.h"
#include "transport.h"
#include "trace2.h"
#include "strbuf.h"
diff --git a/connected.c b/connected.c
index d672521da4..8f89376dbc 100644
--- a/connected.c
+++ b/connected.c
@@ -1,7 +1,7 @@
#include "git-compat-util.h"
#include "gettext.h"
#include "hex.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "run-command.h"
#include "sigchain.h"
#include "connected.h"
diff --git a/contrib/coccinelle/README b/contrib/coccinelle/README
index d1daa1f626..055ad0e06a 100644
--- a/contrib/coccinelle/README
+++ b/contrib/coccinelle/README
@@ -1,7 +1,9 @@
-This directory provides examples of Coccinelle (http://coccinelle.lip6.fr/)
-semantic patches that might be useful to developers.
+= coccinelle
-There are two types of semantic patches:
+This directory provides Coccinelle (http://coccinelle.lip6.fr/) semantic patches
+that might be useful to developers.
+
+== Types of semantic patches
* Using the semantic transformation to check for bad patterns in the code;
The target 'make coccicheck' is designed to check for these patterns and
@@ -42,7 +44,7 @@ There are two types of semantic patches:
This allows to expose plans of pending large scale refactorings without
impacting the bad pattern checks.
-Git-specific tips & things to know about how we run "spatch":
+== Git-specific tips & things to know about how we run "spatch":
* The "make coccicheck" will piggy-back on
"COMPUTE_HEADER_DEPENDENCIES". If you've built a given object file
@@ -90,3 +92,33 @@ Git-specific tips & things to know about how we run "spatch":
The absolute times will differ for you, but the relative speedup
from caching should be on that order.
+
+== Authoring and reviewing coccinelle changes
+
+* When a .cocci is made, both the Git changes and .cocci file should be
+ reviewed. When reviewing such a change, do your best to understand the .cocci
+ changes (e.g. by asking the author to explain the change) and be explicit
+ about your understanding of the changes. This helps us decide whether input
+ from coccinelle experts is needed or not. If you aren't sure of the cocci
+ changes, indicate what changes you actively endorse and leave an Acked-by
+ (instead of Reviewed-by).
+
+* Authors should consider that reviewers may not be coccinelle experts, thus the
+ the .cocci changes may not be self-evident. A plain text description of the
+ changes is strongly encouraged, especially when using more esoteric features
+ of the language.
+
+* .cocci rules should target only the problem it is trying to solve; "collateral
+ damage" is not allowed. Reviewers should look out and flag overly-broad rules.
+
+* Consider the cost-benefit ratio of .cocci changes. In particular, consider the
+ effect on the runtime of "make coccicheck", and how often your .cocci check
+ will catch something valuable. As a rule of thumb, rules that can bail early
+ if a file doesn't have a particular token will have a small impact on runtime,
+ and vice-versa.
+
+* .cocci files used for refactoring should be temporarily kept in-tree to aid
+ the refactoring of out-of-tree code (e.g. in-flight topics). Periodically
+ evaluate the cost-benefit ratio to determine when the file should be removed.
+ For example, consider how many out-of-tree users are left and how much this
+ slows down "make coccicheck".
diff --git a/contrib/coccinelle/tests/unused.c b/contrib/coccinelle/tests/unused.c
deleted file mode 100644
index 8294d734ba..0000000000
--- a/contrib/coccinelle/tests/unused.c
+++ /dev/null
@@ -1,82 +0,0 @@
-void test_strbuf(void)
-{
- struct strbuf sb1 = STRBUF_INIT;
- struct strbuf sb2 = STRBUF_INIT;
- struct strbuf sb3 = STRBUF_INIT;
- struct strbuf sb4 = STRBUF_INIT;
- struct strbuf sb5;
- struct strbuf sb6 = { 0 };
- struct strbuf sb7 = STRBUF_INIT;
- struct strbuf sb8 = STRBUF_INIT;
- struct strbuf *sp1;
- struct strbuf *sp2;
- struct strbuf *sp3;
- struct strbuf *sp4 = xmalloc(sizeof(struct strbuf));
- struct strbuf *sp5 = xmalloc(sizeof(struct strbuf));
- struct strbuf *sp6 = xmalloc(sizeof(struct strbuf));
- struct strbuf *sp7;
-
- strbuf_init(&sb5, 0);
- strbuf_init(sp1, 0);
- strbuf_init(sp2, 0);
- strbuf_init(sp3, 0);
- strbuf_init(sp4, 0);
- strbuf_init(sp5, 0);
- strbuf_init(sp6, 0);
- strbuf_init(sp7, 0);
- sp7 = xmalloc(sizeof(struct strbuf));
-
- use_before(&sb3);
- use_as_str("%s", sb7.buf);
- use_as_str("%s", sp1->buf);
- use_as_str("%s", sp6->buf);
- pass_pp(&sp3);
-
- strbuf_release(&sb1);
- strbuf_reset(&sb2);
- strbuf_release(&sb3);
- strbuf_release(&sb4);
- strbuf_release(&sb5);
- strbuf_release(&sb6);
- strbuf_release(&sb7);
- strbuf_release(sp1);
- strbuf_release(sp2);
- strbuf_release(sp3);
- strbuf_release(sp4);
- strbuf_release(sp5);
- strbuf_release(sp6);
- strbuf_release(sp7);
-
- use_after(&sb4);
-
- if (when_strict())
- return;
- strbuf_release(&sb8);
-}
-
-void test_other(void)
-{
- struct string_list l = STRING_LIST_INIT_DUP;
- struct strbuf sb = STRBUF_INIT;
-
- string_list_clear(&l, 0);
- string_list_clear(&sb, 0);
-}
-
-void test_worktrees(void)
-{
- struct worktree **w1 = get_worktrees();
- struct worktree **w2 = get_worktrees();
- struct worktree **w3;
- struct worktree **w4;
-
- w3 = get_worktrees();
- w4 = get_worktrees();
-
- use_it(w4);
-
- free_worktrees(w1);
- free_worktrees(w2);
- free_worktrees(w3);
- free_worktrees(w4);
-}
diff --git a/contrib/coccinelle/tests/unused.res b/contrib/coccinelle/tests/unused.res
deleted file mode 100644
index 6d3e745683..0000000000
--- a/contrib/coccinelle/tests/unused.res
+++ /dev/null
@@ -1,45 +0,0 @@
-void test_strbuf(void)
-{
- struct strbuf sb3 = STRBUF_INIT;
- struct strbuf sb4 = STRBUF_INIT;
- struct strbuf sb7 = STRBUF_INIT;
- struct strbuf *sp1;
- struct strbuf *sp3;
- struct strbuf *sp6 = xmalloc(sizeof(struct strbuf));
- strbuf_init(sp1, 0);
- strbuf_init(sp3, 0);
- strbuf_init(sp6, 0);
-
- use_before(&sb3);
- use_as_str("%s", sb7.buf);
- use_as_str("%s", sp1->buf);
- use_as_str("%s", sp6->buf);
- pass_pp(&sp3);
-
- strbuf_release(&sb3);
- strbuf_release(&sb4);
- strbuf_release(&sb7);
- strbuf_release(sp1);
- strbuf_release(sp3);
- strbuf_release(sp6);
-
- use_after(&sb4);
-
- if (when_strict())
- return;
-}
-
-void test_other(void)
-{
-}
-
-void test_worktrees(void)
-{
- struct worktree **w4;
-
- w4 = get_worktrees();
-
- use_it(w4);
-
- free_worktrees(w4);
-}
diff --git a/contrib/coccinelle/unused.cocci b/contrib/coccinelle/unused.cocci
deleted file mode 100644
index d84046f82e..0000000000
--- a/contrib/coccinelle/unused.cocci
+++ /dev/null
@@ -1,43 +0,0 @@
-// This rule finds sequences of "unused" declerations and uses of a
-// variable, where "unused" is defined to include only calling the
-// equivalent of alloc, init & free functions on the variable.
-@@
-type T;
-identifier I;
-// STRBUF_INIT, but also e.g. STRING_LIST_INIT_DUP (so no anchoring)
-constant INIT_MACRO =~ "_INIT";
-identifier MALLOC1 =~ "^x?[mc]alloc$";
-identifier INIT_ASSIGN1 =~ "^get_worktrees$";
-identifier INIT_CALL1 =~ "^[a-z_]*_init$";
-identifier REL1 =~ "^[a-z_]*_(release|reset|clear|free)$";
-identifier REL2 =~ "^(release|clear|free)_[a-z_]*$";
-@@
-
-(
-- T I;
-|
-- T I = { 0 };
-|
-- T I = INIT_MACRO;
-|
-- T I = MALLOC1(...);
-|
-- T I = INIT_ASSIGN1(...);
-)
-
-<... when != \( I \| &I \)
-(
-- \( INIT_CALL1 \)( \( I \| &I \), ...);
-|
-- I = \( INIT_ASSIGN1 \)(...);
-|
-- I = MALLOC1(...);
-)
-...>
-
-(
-- \( REL1 \| REL2 \)( \( I \| &I \), ...);
-|
-- \( REL1 \| REL2 \)( \( &I \| I \) );
-)
- ... when != \( I \| &I \)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index dc95c34cc8..945d2543b0 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -767,7 +767,7 @@ __git_refs ()
track=""
;;
*)
- for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD REBASE_HEAD CHERRY_PICK_HEAD; do
+ for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD REBASE_HEAD CHERRY_PICK_HEAD REVERT_HEAD BISECT_HEAD AUTO_MERGE; do
case "$i" in
$match*|$umatch*)
if [ -e "$dir/$i" ]; then
diff --git a/contrib/credential/gnome-keyring/.gitignore b/contrib/credential/gnome-keyring/.gitignore
deleted file mode 100644
index 88d8fcdbce..0000000000
--- a/contrib/credential/gnome-keyring/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-git-credential-gnome-keyring
diff --git a/contrib/credential/gnome-keyring/Makefile b/contrib/credential/gnome-keyring/Makefile
deleted file mode 100644
index 22c19df94b..0000000000
--- a/contrib/credential/gnome-keyring/Makefile
+++ /dev/null
@@ -1,25 +0,0 @@
-MAIN:=git-credential-gnome-keyring
-all:: $(MAIN)
-
-CC = gcc
-RM = rm -f
-CFLAGS = -g -O2 -Wall
-PKG_CONFIG = pkg-config
-
--include ../../../config.mak.autogen
--include ../../../config.mak
-
-INCS:=$(shell $(PKG_CONFIG) --cflags gnome-keyring-1 glib-2.0)
-LIBS:=$(shell $(PKG_CONFIG) --libs gnome-keyring-1 glib-2.0)
-
-SRCS:=$(MAIN).c
-OBJS:=$(SRCS:.c=.o)
-
-%.o: %.c
- $(CC) $(CFLAGS) $(CPPFLAGS) $(INCS) -o $@ -c $<
-
-$(MAIN): $(OBJS)
- $(CC) -o $@ $(LDFLAGS) $^ $(LIBS)
-
-clean:
- @$(RM) $(MAIN) $(OBJS)
diff --git a/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c b/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
deleted file mode 100644
index 5927e27ae6..0000000000
--- a/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
+++ /dev/null
@@ -1,470 +0,0 @@
-/*
- * Copyright (C) 2011 John Szakmeister <john@szakmeister.net>
- * 2012 Philipp A. Hartmann <pah@qo.cx>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * Credits:
- * - GNOME Keyring API handling originally written by John Szakmeister
- * - ported to credential helper API by Philipp A. Hartmann
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <glib.h>
-#include <gnome-keyring.h>
-
-#ifdef GNOME_KEYRING_DEFAULT
-
- /* Modern gnome-keyring */
-
-#include <gnome-keyring-memory.h>
-
-#else
-
- /*
- * Support ancient gnome-keyring, circ. RHEL 5.X.
- * GNOME_KEYRING_DEFAULT seems to have been introduced with Gnome 2.22,
- * and the other features roughly around Gnome 2.20, 6 months before.
- * Ubuntu 8.04 used Gnome 2.22 (I think). Not sure any distro used 2.20.
- * So the existence/non-existence of GNOME_KEYRING_DEFAULT seems like
- * a decent thing to use as an indicator.
- */
-
-#define GNOME_KEYRING_DEFAULT NULL
-
-/*
- * ancient gnome-keyring returns DENIED when an entry is not found.
- * Setting NO_MATCH to DENIED will prevent us from reporting DENIED
- * errors during get and erase operations, but we will still report
- * DENIED errors during a store.
- */
-#define GNOME_KEYRING_RESULT_NO_MATCH GNOME_KEYRING_RESULT_DENIED
-
-#define gnome_keyring_memory_alloc g_malloc
-#define gnome_keyring_memory_free gnome_keyring_free_password
-#define gnome_keyring_memory_strdup g_strdup
-
-static const char *gnome_keyring_result_to_message(GnomeKeyringResult result)
-{
- switch (result) {
- case GNOME_KEYRING_RESULT_OK:
- return "OK";
- case GNOME_KEYRING_RESULT_DENIED:
- return "Denied";
- case GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON:
- return "No Keyring Daemon";
- case GNOME_KEYRING_RESULT_ALREADY_UNLOCKED:
- return "Already UnLocked";
- case GNOME_KEYRING_RESULT_NO_SUCH_KEYRING:
- return "No Such Keyring";
- case GNOME_KEYRING_RESULT_BAD_ARGUMENTS:
- return "Bad Arguments";
- case GNOME_KEYRING_RESULT_IO_ERROR:
- return "IO Error";
- case GNOME_KEYRING_RESULT_CANCELLED:
- return "Cancelled";
- case GNOME_KEYRING_RESULT_ALREADY_EXISTS:
- return "Already Exists";
- default:
- return "Unknown Error";
- }
-}
-
-/*
- * Support really ancient gnome-keyring, circ. RHEL 4.X.
- * Just a guess for the Glib version. Glib 2.8 was roughly Gnome 2.12 ?
- * Which was released with gnome-keyring 0.4.3 ??
- */
-#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 8
-
-static void gnome_keyring_done_cb(GnomeKeyringResult result, gpointer user_data)
-{
- gpointer *data = (gpointer *)user_data;
- int *done = (int *)data[0];
- GnomeKeyringResult *r = (GnomeKeyringResult *)data[1];
-
- *r = result;
- *done = 1;
-}
-
-static void wait_for_request_completion(int *done)
-{
- GMainContext *mc = g_main_context_default();
- while (!*done)
- g_main_context_iteration(mc, TRUE);
-}
-
-static GnomeKeyringResult gnome_keyring_item_delete_sync(const char *keyring, guint32 id)
-{
- int done = 0;
- GnomeKeyringResult result;
- gpointer data[] = { &done, &result };
-
- gnome_keyring_item_delete(keyring, id, gnome_keyring_done_cb, data,
- NULL);
-
- wait_for_request_completion(&done);
-
- return result;
-}
-
-#endif
-#endif
-
-/*
- * This credential struct and API is simplified from git's credential.{h,c}
- */
-struct credential {
- char *protocol;
- char *host;
- unsigned short port;
- char *path;
- char *username;
- char *password;
-};
-
-#define CREDENTIAL_INIT { 0 }
-
-typedef int (*credential_op_cb)(struct credential *);
-
-struct credential_operation {
- char *name;
- credential_op_cb op;
-};
-
-#define CREDENTIAL_OP_END { NULL, NULL }
-
-/* ----------------- GNOME Keyring functions ----------------- */
-
-/* create a special keyring option string, if path is given */
-static char *keyring_object(struct credential *c)
-{
- if (!c->path)
- return NULL;
-
- if (c->port)
- return g_strdup_printf("%s:%hd/%s", c->host, c->port, c->path);
-
- return g_strdup_printf("%s/%s", c->host, c->path);
-}
-
-static int keyring_get(struct credential *c)
-{
- char *object = NULL;
- GList *entries;
- GnomeKeyringNetworkPasswordData *password_data;
- GnomeKeyringResult result;
-
- if (!c->protocol || !(c->host || c->path))
- return EXIT_FAILURE;
-
- object = keyring_object(c);
-
- result = gnome_keyring_find_network_password_sync(
- c->username,
- NULL /* domain */,
- c->host,
- object,
- c->protocol,
- NULL /* authtype */,
- c->port,
- &entries);
-
- g_free(object);
-
- if (result == GNOME_KEYRING_RESULT_NO_MATCH)
- return EXIT_SUCCESS;
-
- if (result == GNOME_KEYRING_RESULT_CANCELLED)
- return EXIT_SUCCESS;
-
- if (result != GNOME_KEYRING_RESULT_OK) {
- g_critical("%s", gnome_keyring_result_to_message(result));
- return EXIT_FAILURE;
- }
-
- /* pick the first one from the list */
- password_data = (GnomeKeyringNetworkPasswordData *)entries->data;
-
- gnome_keyring_memory_free(c->password);
- c->password = gnome_keyring_memory_strdup(password_data->password);
-
- if (!c->username)
- c->username = g_strdup(password_data->user);
-
- gnome_keyring_network_password_list_free(entries);
-
- return EXIT_SUCCESS;
-}
-
-
-static int keyring_store(struct credential *c)
-{
- guint32 item_id;
- char *object = NULL;
- GnomeKeyringResult result;
-
- /*
- * Sanity check that what we are storing is actually sensible.
- * In particular, we can't make a URL without a protocol field.
- * Without either a host or pathname (depending on the scheme),
- * we have no primary key. And without a username and password,
- * we are not actually storing a credential.
- */
- if (!c->protocol || !(c->host || c->path) ||
- !c->username || !c->password)
- return EXIT_FAILURE;
-
- object = keyring_object(c);
-
- result = gnome_keyring_set_network_password_sync(
- GNOME_KEYRING_DEFAULT,
- c->username,
- NULL /* domain */,
- c->host,
- object,
- c->protocol,
- NULL /* authtype */,
- c->port,
- c->password,
- &item_id);
-
- g_free(object);
-
- if (result != GNOME_KEYRING_RESULT_OK &&
- result != GNOME_KEYRING_RESULT_CANCELLED) {
- g_critical("%s", gnome_keyring_result_to_message(result));
- return EXIT_FAILURE;
- }
-
- return EXIT_SUCCESS;
-}
-
-static int keyring_erase(struct credential *c)
-{
- char *object = NULL;
- GList *entries;
- GnomeKeyringNetworkPasswordData *password_data;
- GnomeKeyringResult result;
-
- /*
- * Sanity check that we actually have something to match
- * against. The input we get is a restrictive pattern,
- * so technically a blank credential means "erase everything".
- * But it is too easy to accidentally send this, since it is equivalent
- * to empty input. So explicitly disallow it, and require that the
- * pattern have some actual content to match.
- */
- if (!c->protocol && !c->host && !c->path && !c->username)
- return EXIT_FAILURE;
-
- object = keyring_object(c);
-
- result = gnome_keyring_find_network_password_sync(
- c->username,
- NULL /* domain */,
- c->host,
- object,
- c->protocol,
- NULL /* authtype */,
- c->port,
- &entries);
-
- g_free(object);
-
- if (result == GNOME_KEYRING_RESULT_NO_MATCH)
- return EXIT_SUCCESS;
-
- if (result == GNOME_KEYRING_RESULT_CANCELLED)
- return EXIT_SUCCESS;
-
- if (result != GNOME_KEYRING_RESULT_OK) {
- g_critical("%s", gnome_keyring_result_to_message(result));
- return EXIT_FAILURE;
- }
-
- /* pick the first one from the list (delete all matches?) */
- password_data = (GnomeKeyringNetworkPasswordData *)entries->data;
-
- result = gnome_keyring_item_delete_sync(
- password_data->keyring, password_data->item_id);
-
- gnome_keyring_network_password_list_free(entries);
-
- if (result != GNOME_KEYRING_RESULT_OK) {
- g_critical("%s", gnome_keyring_result_to_message(result));
- return EXIT_FAILURE;
- }
-
- return EXIT_SUCCESS;
-}
-
-/*
- * Table with helper operation callbacks, used by generic
- * credential helper main function.
- */
-static struct credential_operation const credential_helper_ops[] = {
- { "get", keyring_get },
- { "store", keyring_store },
- { "erase", keyring_erase },
- CREDENTIAL_OP_END
-};
-
-/* ------------------ credential functions ------------------ */
-
-static void credential_init(struct credential *c)
-{
- memset(c, 0, sizeof(*c));
-}
-
-static void credential_clear(struct credential *c)
-{
- g_free(c->protocol);
- g_free(c->host);
- g_free(c->path);
- g_free(c->username);
- gnome_keyring_memory_free(c->password);
-
- credential_init(c);
-}
-
-static int credential_read(struct credential *c)
-{
- char *buf;
- size_t line_len;
- char *key;
- char *value;
-
- key = buf = gnome_keyring_memory_alloc(1024);
-
- while (fgets(buf, 1024, stdin)) {
- line_len = strlen(buf);
-
- if (line_len && buf[line_len-1] == '\n')
- buf[--line_len] = '\0';
-
- if (!line_len)
- break;
-
- value = strchr(buf, '=');
- if (!value) {
- g_warning("invalid credential line: %s", key);
- gnome_keyring_memory_free(buf);
- return -1;
- }
- *value++ = '\0';
-
- if (!strcmp(key, "protocol")) {
- g_free(c->protocol);
- c->protocol = g_strdup(value);
- } else if (!strcmp(key, "host")) {
- g_free(c->host);
- c->host = g_strdup(value);
- value = strrchr(c->host, ':');
- if (value) {
- *value++ = '\0';
- c->port = atoi(value);
- }
- } else if (!strcmp(key, "path")) {
- g_free(c->path);
- c->path = g_strdup(value);
- } else if (!strcmp(key, "username")) {
- g_free(c->username);
- c->username = g_strdup(value);
- } else if (!strcmp(key, "password")) {
- gnome_keyring_memory_free(c->password);
- c->password = gnome_keyring_memory_strdup(value);
- while (*value)
- *value++ = '\0';
- }
- /*
- * Ignore other lines; we don't know what they mean, but
- * this future-proofs us when later versions of git do
- * learn new lines, and the helpers are updated to match.
- */
- }
-
- gnome_keyring_memory_free(buf);
-
- return 0;
-}
-
-static void credential_write_item(FILE *fp, const char *key, const char *value)
-{
- if (!value)
- return;
- fprintf(fp, "%s=%s\n", key, value);
-}
-
-static void credential_write(const struct credential *c)
-{
- /* only write username/password, if set */
- credential_write_item(stdout, "username", c->username);
- credential_write_item(stdout, "password", c->password);
-}
-
-static void usage(const char *name)
-{
- struct credential_operation const *try_op = credential_helper_ops;
- const char *basename = strrchr(name, '/');
-
- basename = (basename) ? basename + 1 : name;
- fprintf(stderr, "usage: %s <", basename);
- while (try_op->name) {
- fprintf(stderr, "%s", (try_op++)->name);
- if (try_op->name)
- fprintf(stderr, "%s", "|");
- }
- fprintf(stderr, "%s", ">\n");
-}
-
-int main(int argc, char *argv[])
-{
- int ret = EXIT_SUCCESS;
-
- struct credential_operation const *try_op = credential_helper_ops;
- struct credential cred = CREDENTIAL_INIT;
-
- if (!argv[1]) {
- usage(argv[0]);
- exit(EXIT_FAILURE);
- }
-
- g_set_application_name("Git Credential Helper");
-
- /* lookup operation callback */
- while (try_op->name && strcmp(argv[1], try_op->name))
- try_op++;
-
- /* unsupported operation given -- ignore silently */
- if (!try_op->name || !try_op->op)
- goto out;
-
- ret = credential_read(&cred);
- if (ret)
- goto out;
-
- /* perform credential operation */
- ret = (*try_op->op)(&cred);
-
- credential_write(&cred);
-
-out:
- credential_clear(&cred);
- return ret;
-}
diff --git a/contrib/credential/libsecret/.gitignore b/contrib/credential/libsecret/.gitignore
new file mode 100644
index 0000000000..4fa22359e2
--- /dev/null
+++ b/contrib/credential/libsecret/.gitignore
@@ -0,0 +1 @@
+git-credential-libsecret
diff --git a/contrib/credential/libsecret/git-credential-libsecret.c b/contrib/credential/libsecret/git-credential-libsecret.c
index 2c5d76d789..31cf32ad96 100644
--- a/contrib/credential/libsecret/git-credential-libsecret.c
+++ b/contrib/credential/libsecret/git-credential-libsecret.c
@@ -39,6 +39,8 @@ struct credential {
char *path;
char *username;
char *password;
+ char *password_expiry_utc;
+ char *oauth_refresh_token;
};
#define CREDENTIAL_INIT { 0 }
@@ -54,6 +56,25 @@ struct credential_operation {
/* ----------------- Secret Service functions ----------------- */
+static const SecretSchema schema = {
+ "org.git.Password",
+ /* Ignore schema name during search for backwards compatibility */
+ SECRET_SCHEMA_DONT_MATCH_NAME,
+ {
+ /*
+ * libsecret assumes attribute values are non-confidential and
+ * unchanging, so we can't include oauth_refresh_token or
+ * password_expiry_utc.
+ */
+ { "user", SECRET_SCHEMA_ATTRIBUTE_STRING },
+ { "object", SECRET_SCHEMA_ATTRIBUTE_STRING },
+ { "protocol", SECRET_SCHEMA_ATTRIBUTE_STRING },
+ { "port", SECRET_SCHEMA_ATTRIBUTE_INTEGER },
+ { "server", SECRET_SCHEMA_ATTRIBUTE_STRING },
+ { NULL, 0 },
+ }
+};
+
static char *make_label(struct credential *c)
{
if (c->port)
@@ -101,7 +122,7 @@ static int keyring_get(struct credential *c)
attributes = make_attr_list(c);
items = secret_service_search_sync(service,
- SECRET_SCHEMA_COMPAT_NETWORK,
+ &schema,
attributes,
SECRET_SEARCH_LOAD_SECRETS | SECRET_SEARCH_UNLOCK,
NULL,
@@ -117,6 +138,7 @@ static int keyring_get(struct credential *c)
SecretItem *item;
SecretValue *secret;
const char *s;
+ gchar **parts;
item = items->data;
secret = secret_item_get_secret(item);
@@ -130,8 +152,27 @@ static int keyring_get(struct credential *c)
s = secret_value_get_text(secret);
if (s) {
- g_free(c->password);
- c->password = g_strdup(s);
+ /*
+ * Passwords and other attributes encoded in following format:
+ * hunter2
+ * password_expiry_utc=1684189401
+ * oauth_refresh_token=xyzzy
+ */
+ parts = g_strsplit(s, "\n", 0);
+ if (g_strv_length(parts) >= 1) {
+ g_free(c->password);
+ c->password = g_strdup(parts[0]);
+ }
+ for (int i = 1; i < g_strv_length(parts); i++) {
+ if (g_str_has_prefix(parts[i], "password_expiry_utc=")) {
+ g_free(c->password_expiry_utc);
+ c->password_expiry_utc = g_strdup(&parts[i][20]);
+ } else if (g_str_has_prefix(parts[i], "oauth_refresh_token=")) {
+ g_free(c->oauth_refresh_token);
+ c->oauth_refresh_token = g_strdup(&parts[i][20]);
+ }
+ }
+ g_strfreev(parts);
}
g_hash_table_unref(attributes);
@@ -148,6 +189,7 @@ static int keyring_store(struct credential *c)
char *label = NULL;
GHashTable *attributes = NULL;
GError *error = NULL;
+ GString *secret = NULL;
/*
* Sanity check that what we are storing is actually sensible.
@@ -162,13 +204,23 @@ static int keyring_store(struct credential *c)
label = make_label(c);
attributes = make_attr_list(c);
- secret_password_storev_sync(SECRET_SCHEMA_COMPAT_NETWORK,
+ secret = g_string_new(c->password);
+ if (c->password_expiry_utc) {
+ g_string_append_printf(secret, "\npassword_expiry_utc=%s",
+ c->password_expiry_utc);
+ }
+ if (c->oauth_refresh_token) {
+ g_string_append_printf(secret, "\noauth_refresh_token=%s",
+ c->oauth_refresh_token);
+ }
+ secret_password_storev_sync(&schema,
attributes,
NULL,
label,
- c->password,
+ secret->str,
NULL,
&error);
+ g_string_free(secret, TRUE);
g_free(label);
g_hash_table_unref(attributes);
@@ -198,7 +250,7 @@ static int keyring_erase(struct credential *c)
return EXIT_FAILURE;
attributes = make_attr_list(c);
- secret_password_clearv_sync(SECRET_SCHEMA_COMPAT_NETWORK,
+ secret_password_clearv_sync(&schema,
attributes,
NULL,
&error);
@@ -238,23 +290,24 @@ static void credential_clear(struct credential *c)
g_free(c->path);
g_free(c->username);
g_free(c->password);
+ g_free(c->password_expiry_utc);
+ g_free(c->oauth_refresh_token);
credential_init(c);
}
static int credential_read(struct credential *c)
{
- char *buf;
- size_t line_len;
+ char *buf = NULL;
+ size_t alloc;
+ ssize_t line_len;
char *key;
char *value;
- key = buf = g_malloc(1024);
+ while ((line_len = getline(&buf, &alloc, stdin)) > 0) {
+ key = buf;
- while (fgets(buf, 1024, stdin)) {
- line_len = strlen(buf);
-
- if (line_len && buf[line_len-1] == '\n')
+ if (buf[line_len-1] == '\n')
buf[--line_len] = '\0';
if (!line_len)
@@ -285,11 +338,19 @@ static int credential_read(struct credential *c)
} else if (!strcmp(key, "username")) {
g_free(c->username);
c->username = g_strdup(value);
+ } else if (!strcmp(key, "password_expiry_utc")) {
+ g_free(c->password_expiry_utc);
+ c->password_expiry_utc = g_strdup(value);
} else if (!strcmp(key, "password")) {
g_free(c->password);
c->password = g_strdup(value);
while (*value)
*value++ = '\0';
+ } else if (!strcmp(key, "oauth_refresh_token")) {
+ g_free(c->oauth_refresh_token);
+ c->oauth_refresh_token = g_strdup(value);
+ while (*value)
+ *value++ = '\0';
}
/*
* Ignore other lines; we don't know what they mean, but
@@ -298,7 +359,7 @@ static int credential_read(struct credential *c)
*/
}
- g_free(buf);
+ free(buf);
return 0;
}
@@ -315,6 +376,10 @@ static void credential_write(const struct credential *c)
/* only write username/password, if set */
credential_write_item(stdout, "username", c->username);
credential_write_item(stdout, "password", c->password);
+ credential_write_item(stdout, "password_expiry_utc",
+ c->password_expiry_utc);
+ credential_write_item(stdout, "oauth_refresh_token",
+ c->oauth_refresh_token);
}
static void usage(const char *name)
diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
index e29cc28779..5f2e5f16c8 100644
--- a/contrib/credential/osxkeychain/git-credential-osxkeychain.c
+++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
@@ -113,14 +113,16 @@ static void add_internet_password(void)
static void read_credential(void)
{
- char buf[1024];
+ char *buf = NULL;
+ size_t alloc;
+ ssize_t line_len;
- while (fgets(buf, sizeof(buf), stdin)) {
+ while ((line_len = getline(&buf, &alloc, stdin)) > 0) {
char *v;
if (!strcmp(buf, "\n"))
break;
- buf[strlen(buf)-1] = '\0';
+ buf[line_len-1] = '\0';
v = strchr(buf, '=');
if (!v)
@@ -165,6 +167,8 @@ static void read_credential(void)
* learn new lines, and the helpers are updated to match.
*/
}
+
+ free(buf);
}
int main(int argc, const char **argv)
diff --git a/contrib/credential/wincred/git-credential-wincred.c b/contrib/credential/wincred/git-credential-wincred.c
index ead6e267c7..96f10613ae 100644
--- a/contrib/credential/wincred/git-credential-wincred.c
+++ b/contrib/credential/wincred/git-credential-wincred.c
@@ -6,6 +6,7 @@
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
+#include <wincred.h>
/* common helpers */
@@ -33,65 +34,8 @@ static void *xmalloc(size_t size)
return ret;
}
-/* MinGW doesn't have wincred.h, so we need to define stuff */
-
-typedef struct _CREDENTIAL_ATTRIBUTEW {
- LPWSTR Keyword;
- DWORD Flags;
- DWORD ValueSize;
- LPBYTE Value;
-} CREDENTIAL_ATTRIBUTEW, *PCREDENTIAL_ATTRIBUTEW;
-
-typedef struct _CREDENTIALW {
- DWORD Flags;
- DWORD Type;
- LPWSTR TargetName;
- LPWSTR Comment;
- FILETIME LastWritten;
- DWORD CredentialBlobSize;
- LPBYTE CredentialBlob;
- DWORD Persist;
- DWORD AttributeCount;
- PCREDENTIAL_ATTRIBUTEW Attributes;
- LPWSTR TargetAlias;
- LPWSTR UserName;
-} CREDENTIALW, *PCREDENTIALW;
-
-#define CRED_TYPE_GENERIC 1
-#define CRED_PERSIST_LOCAL_MACHINE 2
-#define CRED_MAX_ATTRIBUTES 64
-
-typedef BOOL (WINAPI *CredWriteWT)(PCREDENTIALW, DWORD);
-typedef BOOL (WINAPI *CredEnumerateWT)(LPCWSTR, DWORD, DWORD *,
- PCREDENTIALW **);
-typedef VOID (WINAPI *CredFreeT)(PVOID);
-typedef BOOL (WINAPI *CredDeleteWT)(LPCWSTR, DWORD, DWORD);
-
-static HMODULE advapi;
-static CredWriteWT CredWriteW;
-static CredEnumerateWT CredEnumerateW;
-static CredFreeT CredFree;
-static CredDeleteWT CredDeleteW;
-
-static void load_cred_funcs(void)
-{
- /* load DLLs */
- advapi = LoadLibraryExA("advapi32.dll", NULL,
- LOAD_LIBRARY_SEARCH_SYSTEM32);
- if (!advapi)
- die("failed to load advapi32.dll");
-
- /* get function pointers */
- CredWriteW = (CredWriteWT)GetProcAddress(advapi, "CredWriteW");
- CredEnumerateW = (CredEnumerateWT)GetProcAddress(advapi,
- "CredEnumerateW");
- CredFree = (CredFreeT)GetProcAddress(advapi, "CredFree");
- CredDeleteW = (CredDeleteWT)GetProcAddress(advapi, "CredDeleteW");
- if (!CredWriteW || !CredEnumerateW || !CredFree || !CredDeleteW)
- die("failed to load functions");
-}
-
-static WCHAR *wusername, *password, *protocol, *host, *path, target[1024];
+static WCHAR *wusername, *password, *protocol, *host, *path, target[1024],
+ *password_expiry_utc;
static void write_item(const char *what, LPCWSTR wbuf, int wlen)
{
@@ -183,6 +127,7 @@ static void get_credential(void)
CREDENTIALW **creds;
DWORD num_creds;
int i;
+ CREDENTIAL_ATTRIBUTEW *attr;
if (!CredEnumerateW(L"git:*", 0, &num_creds, &creds))
return;
@@ -195,6 +140,14 @@ static void get_credential(void)
write_item("password",
(LPCWSTR)creds[i]->CredentialBlob,
creds[i]->CredentialBlobSize / sizeof(WCHAR));
+ for (int j = 0; j < creds[i]->AttributeCount; j++) {
+ attr = creds[i]->Attributes + j;
+ if (!wcscmp(attr->Keyword, L"git_password_expiry_utc")) {
+ write_item("password_expiry_utc", (LPCWSTR)attr->Value,
+ attr->ValueSize / sizeof(WCHAR));
+ break;
+ }
+ }
break;
}
@@ -204,6 +157,7 @@ static void get_credential(void)
static void store_credential(void)
{
CREDENTIALW cred;
+ CREDENTIAL_ATTRIBUTEW expiry_attr;
if (!wusername || !password)
return;
@@ -217,6 +171,14 @@ static void store_credential(void)
cred.Persist = CRED_PERSIST_LOCAL_MACHINE;
cred.AttributeCount = 0;
cred.Attributes = NULL;
+ if (password_expiry_utc != NULL) {
+ expiry_attr.Keyword = L"git_password_expiry_utc";
+ expiry_attr.Value = (LPVOID)password_expiry_utc;
+ expiry_attr.ValueSize = (wcslen(password_expiry_utc)) * sizeof(WCHAR);
+ expiry_attr.Flags = 0;
+ cred.Attributes = &expiry_attr;
+ cred.AttributeCount = 1;
+ }
cred.TargetAlias = NULL;
cred.UserName = wusername;
@@ -249,17 +211,28 @@ static WCHAR *utf8_to_utf16_dup(const char *str)
return wstr;
}
+#define KB (1024)
+
static void read_credential(void)
{
- char buf[1024];
+ size_t alloc = 100 * KB;
+ char *buf = calloc(alloc, sizeof(*buf));
- while (fgets(buf, sizeof(buf), stdin)) {
+ while (fgets(buf, alloc, stdin)) {
char *v;
- int len = strlen(buf);
+ size_t len = strlen(buf);
+ int ends_in_newline = 0;
/* strip trailing CR / LF */
- while (len && strchr("\r\n", buf[len - 1]))
+ if (len && buf[len - 1] == '\n') {
+ buf[--len] = 0;
+ ends_in_newline = 1;
+ }
+ if (len && buf[len - 1] == '\r')
buf[--len] = 0;
+ if (!ends_in_newline)
+ die("bad input: %s", buf);
+
if (!*buf)
break;
@@ -278,12 +251,16 @@ static void read_credential(void)
wusername = utf8_to_utf16_dup(v);
} else if (!strcmp(buf, "password"))
password = utf8_to_utf16_dup(v);
+ else if (!strcmp(buf, "password_expiry_utc"))
+ password_expiry_utc = utf8_to_utf16_dup(v);
/*
* Ignore other lines; we don't know what they mean, but
* this future-proofs us when later versions of git do
* learn new lines, and the helpers are updated to match.
*/
}
+
+ free(buf);
}
int main(int argc, char *argv[])
@@ -292,7 +269,7 @@ int main(int argc, char *argv[])
"usage: git credential-wincred <get|store|erase>\n";
if (!argv[1])
- die(usage);
+ die("%s", usage);
/* git use binary pipes to avoid CRLF-issues */
_setmode(_fileno(stdin), _O_BINARY);
@@ -300,8 +277,6 @@ int main(int argc, char *argv[])
read_credential();
- load_cred_funcs();
-
if (!protocol || !(host || path))
return 0;
diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
index 10c9c87839..7db4c45676 100755
--- a/contrib/subtree/git-subtree.sh
+++ b/contrib/subtree/git-subtree.sh
@@ -34,8 +34,8 @@ git subtree pull --prefix=<prefix> <repository> <ref>
git subtree push --prefix=<prefix> <repository> <refspec>
--
h,help show the help
-q quiet
-d show debug messages
+q,quiet quiet
+d,debug show debug messages
P,prefix= the name of the subdir to split out
options for 'split' (also: 'push')
annotate= add a prefix to commit message of new commits
diff --git a/convert.c b/convert.c
index 5a2ea5308d..3d8325d49e 100644
--- a/convert.c
+++ b/convert.c
@@ -1,19 +1,21 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "advice.h"
#include "config.h"
#include "convert.h"
+#include "copy.h"
#include "gettext.h"
#include "hex.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "attr.h"
#include "run-command.h"
#include "quote.h"
+#include "read-cache-ll.h"
#include "sigchain.h"
#include "pkt-line.h"
#include "sub-process.h"
#include "trace.h"
#include "utf8.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
#include "wrapper.h"
/*
@@ -1314,7 +1316,7 @@ void convert_attrs(struct index_state *istate,
git_config(read_convert_config, NULL);
}
- git_check_attr(istate, NULL, path, check);
+ git_check_attr(istate, path, check);
ccheck = check->items;
ca->crlf_action = git_path_check_crlf(ccheck + 4);
if (ca->crlf_action == CRLF_UNDEFINED)
diff --git a/convert.h b/convert.h
index 0a6e4086b8..d925589444 100644
--- a/convert.h
+++ b/convert.h
@@ -4,7 +4,7 @@
#ifndef CONVERT_H
#define CONVERT_H
-#include "hash.h"
+#include "hash-ll.h"
#include "string-list.h"
struct index_state;
diff --git a/copy.c b/copy.c
index c3250f0822..882c79cffb 100644
--- a/copy.c
+++ b/copy.c
@@ -1,4 +1,6 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "copy.h"
+#include "path.h"
#include "wrapper.h"
int copy_fd(int ifd, int ofd)
diff --git a/copy.h b/copy.h
new file mode 100644
index 0000000000..2af77cba86
--- /dev/null
+++ b/copy.h
@@ -0,0 +1,10 @@
+#ifndef COPY_H
+#define COPY_H
+
+#define COPY_READ_ERROR (-2)
+#define COPY_WRITE_ERROR (-3)
+int copy_fd(int ifd, int ofd);
+int copy_file(const char *dst, const char *src, int mode);
+int copy_file_with_time(const char *dst, const char *src, int mode);
+
+#endif /* COPY_H */
diff --git a/credential.c b/credential.c
index e6417bf880..023b59d571 100644
--- a/credential.c
+++ b/credential.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "abspath.h"
#include "config.h"
#include "credential.h"
@@ -8,6 +8,7 @@
#include "url.h"
#include "prompt.h"
#include "sigchain.h"
+#include "strbuf.h"
#include "urlmatch.h"
#include "git-compat-util.h"
@@ -24,6 +25,7 @@ void credential_clear(struct credential *c)
free(c->path);
free(c->username);
free(c->password);
+ free(c->oauth_refresh_token);
string_list_clear(&c->helpers, 0);
strvec_clear(&c->wwwauth_headers);
@@ -238,11 +240,16 @@ int credential_read(struct credential *c, FILE *fp)
} else if (!strcmp(key, "path")) {
free(c->path);
c->path = xstrdup(value);
+ } else if (!strcmp(key, "wwwauth[]")) {
+ strvec_push(&c->wwwauth_headers, value);
} else if (!strcmp(key, "password_expiry_utc")) {
errno = 0;
c->password_expiry_utc = parse_timestamp(value, NULL, 10);
if (c->password_expiry_utc == 0 || errno == ERANGE)
c->password_expiry_utc = TIME_MAX;
+ } else if (!strcmp(key, "oauth_refresh_token")) {
+ free(c->oauth_refresh_token);
+ c->oauth_refresh_token = xstrdup(value);
} else if (!strcmp(key, "url")) {
credential_from_url(c, value);
} else if (!strcmp(key, "quit")) {
@@ -278,6 +285,7 @@ void credential_write(const struct credential *c, FILE *fp)
credential_write_item(fp, "path", c->path, 0);
credential_write_item(fp, "username", c->username, 0);
credential_write_item(fp, "password", c->password, 0);
+ credential_write_item(fp, "oauth_refresh_token", c->oauth_refresh_token, 0);
if (c->password_expiry_utc != TIME_MAX) {
char *s = xstrfmt("%"PRItime, c->password_expiry_utc);
credential_write_item(fp, "password_expiry_utc", s, 0);
@@ -403,6 +411,7 @@ void credential_reject(struct credential *c)
FREE_AND_NULL(c->username);
FREE_AND_NULL(c->password);
+ FREE_AND_NULL(c->oauth_refresh_token);
c->password_expiry_utc = TIME_MAX;
c->approved = 0;
}
diff --git a/credential.h b/credential.h
index 2b5958cd43..b8e2936d1d 100644
--- a/credential.h
+++ b/credential.h
@@ -141,6 +141,7 @@ struct credential {
char *protocol;
char *host;
char *path;
+ char *oauth_refresh_token;
timestamp_t password_expiry_utc;
};
diff --git a/csum-file.c b/csum-file.c
index 82ae2973d3..daf9b06dff 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -10,6 +10,7 @@
#include "git-compat-util.h"
#include "progress.h"
#include "csum-file.h"
+#include "hash.h"
#include "wrapper.h"
static void verify_buffer_or_die(struct hashfile *f,
diff --git a/csum-file.h b/csum-file.h
index 566e05cbd2..bc5bec27ac 100644
--- a/csum-file.h
+++ b/csum-file.h
@@ -1,7 +1,7 @@
#ifndef CSUM_FILE_H
#define CSUM_FILE_H
-#include "hash.h"
+#include "hash-ll.h"
#include "write-or-die.h"
struct progress;
diff --git a/daemon.c b/daemon.c
index 75c3c06457..7139cc201d 100644
--- a/daemon.c
+++ b/daemon.c
@@ -1,8 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "abspath.h"
#include "alloc.h"
#include "config.h"
#include "environment.h"
+#include "path.h"
#include "pkt-line.h"
#include "protocol.h"
#include "run-command.h"
diff --git a/date.c b/date.c
index e867ebf6b7..619ada5b20 100644
--- a/date.c
+++ b/date.c
@@ -4,10 +4,11 @@
* Copyright (C) Linus Torvalds, 2005
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "date.h"
#include "gettext.h"
#include "pager.h"
+#include "strbuf.h"
/*
* This is like mktime, but without normalization of tm_wday and tm_yday.
diff --git a/decorate.c b/decorate.c
index 71e79daa82..a5c43c0c14 100644
--- a/decorate.c
+++ b/decorate.c
@@ -3,7 +3,6 @@
* data.
*/
#include "git-compat-util.h"
-#include "hashmap.h"
#include "object.h"
#include "decorate.h"
diff --git a/detect-compiler b/detect-compiler
index 50087f5670..a87650b71b 100755
--- a/detect-compiler
+++ b/detect-compiler
@@ -17,7 +17,15 @@ get_family() {
}
get_version() {
- get_version_line | sed 's/^.* version \([0-9][^ ]*\).*/\1/'
+ # A string that begins with a digit up to the next SP
+ ver=$(get_version_line | sed 's/^.* version \([0-9][^ ]*\).*/\1/')
+
+ # There are known -variant suffixes that do not affect the
+ # meaning of the main version number. Strip them.
+ ver=${ver%-win32}
+ ver=${ver%-posix}
+
+ echo "$ver"
}
print_flags() {
diff --git a/diagnose.c b/diagnose.c
index f1aebf0b50..8430064000 100644
--- a/diagnose.c
+++ b/diagnose.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "diagnose.h"
#include "compat/disk.h"
#include "archive.h"
@@ -7,7 +7,7 @@
#include "gettext.h"
#include "hex.h"
#include "strvec.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "packfile.h"
#include "parse-options.h"
#include "write-or-die.h"
diff --git a/diff-lib.c b/diff-lib.c
index d292405a26..9c2b0134e6 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -1,23 +1,28 @@
/*
* Copyright (C) 2005 Junio C Hamano
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "quote.h"
#include "commit.h"
#include "diff.h"
#include "diffcore.h"
#include "gettext.h"
+#include "hash.h"
#include "hex.h"
#include "object-name.h"
+#include "read-cache.h"
#include "revision.h"
#include "cache-tree.h"
#include "unpack-trees.h"
#include "refs.h"
+#include "repository.h"
#include "submodule.h"
+#include "symlinks.h"
#include "trace.h"
#include "dir.h"
#include "fsmonitor.h"
#include "commit-reach.h"
+#include "config.h"
/*
* diff-files
@@ -69,29 +74,72 @@ static int check_removed(const struct index_state *istate, const struct cache_en
* Return 1 when changes are detected, 0 otherwise. If the DIRTY_SUBMODULES
* option is set, the caller does not only want to know if a submodule is
* modified at all but wants to know all the conditions that are met (new
- * commits, untracked content and/or modified content).
+ * commits, untracked content and/or modified content). If
+ * defer_submodule_status bit is set, dirty_submodule will be left to the
+ * caller to set. defer_submodule_status can also be set to 0 in this
+ * function if there is no need to check if the submodule is modified.
*/
static int match_stat_with_submodule(struct diff_options *diffopt,
const struct cache_entry *ce,
struct stat *st, unsigned ce_option,
- unsigned *dirty_submodule)
+ unsigned *dirty_submodule, int *defer_submodule_status,
+ unsigned *ignore_untracked)
{
int changed = ie_match_stat(diffopt->repo->index, ce, st, ce_option);
+ int defer = 0;
+
if (S_ISGITLINK(ce->ce_mode)) {
struct diff_flags orig_flags = diffopt->flags;
if (!diffopt->flags.override_submodule_config)
set_diffopt_flags_from_submodule_config(diffopt, ce->name);
- if (diffopt->flags.ignore_submodules)
+ if (diffopt->flags.ignore_submodules) {
changed = 0;
- else if (!diffopt->flags.ignore_dirty_submodules &&
- (!changed || diffopt->flags.dirty_submodules))
- *dirty_submodule = is_submodule_modified(ce->name,
- diffopt->flags.ignore_untracked_in_submodules);
+ } else if (!diffopt->flags.ignore_dirty_submodules &&
+ (!changed || diffopt->flags.dirty_submodules)) {
+ if (defer_submodule_status && *defer_submodule_status) {
+ defer = 1;
+ *ignore_untracked = diffopt->flags.ignore_untracked_in_submodules;
+ } else {
+ *dirty_submodule = is_submodule_modified(ce->name,
+ diffopt->flags.ignore_untracked_in_submodules);
+ }
+ }
diffopt->flags = orig_flags;
}
+
+ if (defer_submodule_status)
+ *defer_submodule_status = defer;
return changed;
}
+/**
+ * Records diff_change if there is a change in the entry from run_diff_files.
+ * If there is no change, then the cache entry is marked CE_UPTODATE and
+ * CE_FSMONITOR_VALID. If there is no change and the find_copies_harder flag
+ * is not set, then the function returns early.
+ */
+static void record_file_diff(struct diff_options *options, unsigned newmode,
+ unsigned dirty_submodule, int changed,
+ struct index_state *istate,
+ struct cache_entry *ce)
+{
+ unsigned int oldmode;
+ const struct object_id *old_oid, *new_oid;
+
+ if (!changed && !dirty_submodule) {
+ ce_mark_uptodate(ce);
+ mark_fsmonitor_valid(istate, ce);
+ if (!options->flags.find_copies_harder)
+ return;
+ }
+ oldmode = ce->ce_mode;
+ old_oid = &ce->oid;
+ new_oid = changed ? null_oid() : &ce->oid;
+ diff_change(options, oldmode, newmode, old_oid, new_oid,
+ !is_null_oid(old_oid), !is_null_oid(new_oid),
+ ce->name, 0, dirty_submodule);
+}
+
int run_diff_files(struct rev_info *revs, unsigned int option)
{
int entries, i;
@@ -100,6 +148,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
? CE_MATCH_RACY_IS_DIRTY : 0);
uint64_t start = getnanotime();
struct index_state *istate = revs->diffopt.repo->index;
+ struct string_list submodules = STRING_LIST_INIT_NODUP;
diff_set_mnemonic_prefix(&revs->diffopt, "i/", "w/");
@@ -109,11 +158,10 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
diff_unmerged_stage = 2;
entries = istate->cache_nr;
for (i = 0; i < entries; i++) {
- unsigned int oldmode, newmode;
+ unsigned int newmode;
struct cache_entry *ce = istate->cache[i];
int changed;
- unsigned dirty_submodule = 0;
- const struct object_id *old_oid, *new_oid;
+ int defer_submodule_status = 1;
if (diff_can_quit_early(&revs->diffopt))
break;
@@ -224,6 +272,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
newmode = ce->ce_mode;
} else {
struct stat st;
+ unsigned ignore_untracked = 0;
changed = check_removed(istate, ce, &st);
if (changed) {
@@ -245,26 +294,52 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
}
changed = match_stat_with_submodule(&revs->diffopt, ce, &st,
- ce_option, &dirty_submodule);
+ ce_option, NULL,
+ &defer_submodule_status,
+ &ignore_untracked);
newmode = ce_mode_from_stat(ce, st.st_mode);
- }
-
- if (!changed && !dirty_submodule) {
- ce_mark_uptodate(ce);
- mark_fsmonitor_valid(istate, ce);
- if (!revs->diffopt.flags.find_copies_harder)
+ if (defer_submodule_status) {
+ struct submodule_status_util tmp = {
+ .changed = changed,
+ .dirty_submodule = 0,
+ .ignore_untracked = ignore_untracked,
+ .newmode = newmode,
+ .ce = ce,
+ .path = ce->name,
+ };
+ struct string_list_item *item;
+
+ item = string_list_append(&submodules, ce->name);
+ item->util = xmalloc(sizeof(tmp));
+ memcpy(item->util, &tmp, sizeof(tmp));
continue;
+ }
}
- oldmode = ce->ce_mode;
- old_oid = &ce->oid;
- new_oid = changed ? null_oid() : &ce->oid;
- diff_change(&revs->diffopt, oldmode, newmode,
- old_oid, new_oid,
- !is_null_oid(old_oid),
- !is_null_oid(new_oid),
- ce->name, 0, dirty_submodule);
+ if (!defer_submodule_status)
+ record_file_diff(&revs->diffopt, newmode, 0,
+ changed,istate, ce);
+ }
+ if (submodules.nr) {
+ unsigned long parallel_jobs;
+ struct string_list_item *item;
+
+ if (git_config_get_ulong("submodule.diffjobs", &parallel_jobs))
+ parallel_jobs = 1;
+ else if (!parallel_jobs)
+ parallel_jobs = online_cpus();
+
+ if (get_submodules_status(&submodules, parallel_jobs))
+ die(_("submodule status failed"));
+ for_each_string_list_item(item, &submodules) {
+ struct submodule_status_util *util = item->util;
+
+ record_file_diff(&revs->diffopt, util->newmode,
+ util->dirty_submodule, util->changed,
+ istate, util->ce);
+ }
}
+ string_list_clear(&submodules, 1);
diffcore_std(&revs->diffopt);
diff_flush(&revs->diffopt);
trace_performance_since(start, "diff-files");
@@ -312,7 +387,7 @@ static int get_stat_data(const struct index_state *istate,
return -1;
}
changed = match_stat_with_submodule(diffopt, ce, &st,
- 0, dirty_submodule);
+ 0, dirty_submodule, NULL, NULL);
if (changed) {
mode = ce_mode_from_stat(ce, st.st_mode);
oid = null_oid();
diff --git a/diff-merges.c b/diff-merges.c
index ec97616db1..4641332510 100644
--- a/diff-merges.c
+++ b/diff-merges.c
@@ -3,12 +3,16 @@
#include "gettext.h"
#include "revision.h"
+#include "strbuf.h"
typedef void (*diff_merges_setup_func_t)(struct rev_info *);
static void set_separate(struct rev_info *revs);
static diff_merges_setup_func_t set_to_default = set_separate;
static int suppress_m_parsing;
+static int hide = 0;
+static int m_imply_p = 0;
+static int got_m = 0;
static void suppress(struct rev_info *revs)
{
@@ -22,10 +26,15 @@ static void suppress(struct rev_info *revs)
revs->remerge_diff = 0;
}
+static void set_need_diff(struct rev_info *revs)
+{
+ revs->merges_need_diff = !hide;
+}
+
static void common_setup(struct rev_info *revs)
{
suppress(revs);
- revs->merges_need_diff = 1;
+ set_need_diff(revs);
}
static void set_none(struct rev_info *revs)
@@ -33,6 +42,18 @@ static void set_none(struct rev_info *revs)
suppress(revs);
}
+static void set_hide(struct rev_info *revs)
+{
+ hide = 1;
+ set_need_diff(revs);
+}
+
+static void set_no_hide(struct rev_info *revs)
+{
+ hide = 0;
+ set_need_diff(revs);
+}
+
static void set_separate(struct rev_info *revs)
{
common_setup(revs);
@@ -71,6 +92,10 @@ static diff_merges_setup_func_t func_by_opt(const char *optarg)
{
if (!strcmp(optarg, "off") || !strcmp(optarg, "none"))
return set_none;
+ if (!strcmp(optarg, "hide"))
+ return set_hide;
+ if (!strcmp(optarg, "no-hide"))
+ return set_no_hide;
if (!strcmp(optarg, "1") || !strcmp(optarg, "first-parent"))
return set_first_parent;
if (!strcmp(optarg, "separate"))
@@ -88,12 +113,25 @@ static diff_merges_setup_func_t func_by_opt(const char *optarg)
static void set_diff_merges(struct rev_info *revs, const char *optarg)
{
- diff_merges_setup_func_t func = func_by_opt(optarg);
-
- if (!func)
- die(_("invalid value for '%s': '%s'"), "--diff-merges", optarg);
-
- func(revs);
+ char const delim = ',';
+ struct strbuf **opts = strbuf_split_str(optarg, delim, -1);
+ struct strbuf **p;
+
+ for (p = opts; *p; p++) {
+ diff_merges_setup_func_t func;
+ char *opt = (*p)->buf;
+ int len = (*p)->len;
+
+ if (opt[len - 1] == delim)
+ opt[len - 1] = '\0';
+ func = func_by_opt(opt);
+ if (!func) {
+ strbuf_list_free(opts);
+ die(_("invalid value for '%s': '%s'"), "--diff-merges", opt);
+ }
+ func(revs);
+ }
+ strbuf_list_free(opts);
}
/*
@@ -107,7 +145,25 @@ int diff_merges_config(const char *value)
if (!func)
return -1;
- set_to_default = func;
+ if (func == set_hide)
+ hide = 1;
+ else if (func == set_no_hide)
+ hide = 0;
+ else
+ set_to_default = func;
+
+ return 0;
+}
+
+int diff_merges_hide_config(int on)
+{
+ hide = on;
+ return 0;
+}
+
+int diff_merges_m_imply_p_config(int on)
+{
+ m_imply_p = on;
return 0;
}
@@ -124,7 +180,9 @@ int diff_merges_parse_opts(struct rev_info *revs, const char **argv)
if (!suppress_m_parsing && !strcmp(arg, "-m")) {
set_to_default(revs);
- revs->merges_need_diff = 0;
+ set_hide(revs);
+ revs->merges_imply_patch = m_imply_p;
+ got_m = 1;
} else if (!strcmp(arg, "-c")) {
set_combined(revs);
revs->merges_imply_patch = 1;
@@ -185,5 +243,7 @@ void diff_merges_setup_revs(struct rev_info *revs)
if (revs->merges_imply_patch || revs->merges_need_diff) {
if (!revs->diffopt.output_format)
revs->diffopt.output_format = DIFF_FORMAT_PATCH;
- }
+ } else if (got_m)
+ warning(_("legacy use of lone '-m' detected: please use '--diff-merges=on,hide' instead, as '-m' may imply '-p'"));
+
}
diff --git a/diff-merges.h b/diff-merges.h
index 19639689bb..9f0b3901fe 100644
--- a/diff-merges.h
+++ b/diff-merges.h
@@ -11,6 +11,10 @@ struct rev_info;
int diff_merges_config(const char *value);
+int diff_merges_hide_config(int hide);
+
+int diff_merges_m_imply_p_config(int on);
+
void diff_merges_suppress_m_parsing(void);
int diff_merges_parse_opts(struct rev_info *revs, const char **argv);
diff --git a/diff.c b/diff.c
index 1e83aaee6b..351d1d0591 100644
--- a/diff.c
+++ b/diff.c
@@ -1,9 +1,10 @@
/*
* Copyright (C) 2005 Junio C Hamano
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "abspath.h"
#include "alloc.h"
+#include "base85.h"
#include "config.h"
#include "convert.h"
#include "environment.h"
@@ -19,13 +20,13 @@
#include "attr.h"
#include "run-command.h"
#include "utf8.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "userdiff.h"
#include "submodule-config.h"
#include "submodule.h"
#include "hashmap.h"
#include "mem-pool.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
#include "string-list.h"
#include "strvec.h"
#include "graph.h"
@@ -38,8 +39,10 @@
#include "dir.h"
#include "object-file.h"
#include "object-name.h"
+#include "read-cache-ll.h"
#include "setup.h"
#include "strmap.h"
+#include "ws.h"
#include "wrapper.h"
#ifdef NO_FAST_WORKING_DIRECTORY
@@ -3005,6 +3008,24 @@ static int dirstat_compare(const void *_a, const void *_b)
return strcmp(a->name, b->name);
}
+static void conclude_dirstat(struct diff_options *options,
+ struct dirstat_dir *dir,
+ unsigned long changed)
+{
+ struct dirstat_file *to_free = dir->files;
+
+ if (!changed) {
+ /* This can happen even with many files, if everything was renames */
+ ;
+ } else {
+ /* Show all directories with more than x% of the changes */
+ QSORT(dir->files, dir->nr, dirstat_compare);
+ gather_dirstat(options, dir, changed, "", 0);
+ }
+
+ free(to_free);
+}
+
static void show_dirstat(struct diff_options *options)
{
int i;
@@ -3094,13 +3115,7 @@ found_damage:
dir.nr++;
}
- /* This can happen even with many files, if everything was renames */
- if (!changed)
- return;
-
- /* Show all directories with more than x% of the changes */
- QSORT(dir.files, dir.nr, dirstat_compare);
- gather_dirstat(options, &dir, changed, "", 0);
+ conclude_dirstat(options, &dir, changed);
}
static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *options)
@@ -3138,13 +3153,7 @@ static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *o
dir.nr++;
}
- /* This can happen even with many files, if everything was renames */
- if (!changed)
- return;
-
- /* Show all directories with more than x% of the changes */
- QSORT(dir.files, dir.nr, dirstat_compare);
- gather_dirstat(options, &dir, changed, "", 0);
+ conclude_dirstat(options, &dir, changed);
}
static void free_diffstat_file(struct diffstat_file *f)
@@ -4928,6 +4937,7 @@ static int diff_opt_stat(const struct option *opt, const char *value, int unset)
} else
BUG("%s should not get here", opt->long_name);
+ options->output_format &= ~DIFF_FORMAT_NO_OUTPUT;
options->output_format |= DIFF_FORMAT_DIFFSTAT;
options->stat_name_width = name_width;
options->stat_graph_width = graph_width;
@@ -4947,6 +4957,7 @@ static int parse_dirstat_opt(struct diff_options *options, const char *params)
* The caller knows a dirstat-related option is given from the command
* line; allow it to say "return this_function();"
*/
+ options->output_format &= ~DIFF_FORMAT_NO_OUTPUT;
options->output_format |= DIFF_FORMAT_DIRSTAT;
return 1;
}
@@ -5146,6 +5157,7 @@ static int diff_opt_compact_summary(const struct option *opt,
options->flags.stat_with_summary = 0;
} else {
options->flags.stat_with_summary = 1;
+ options->output_format &= ~DIFF_FORMAT_NO_OUTPUT;
options->output_format |= DIFF_FORMAT_DIFFSTAT;
}
return 0;
@@ -5491,9 +5503,8 @@ struct option *add_diff_options(const struct option *opts,
OPT_BITOP('p', "patch", &options->output_format,
N_("generate patch"),
DIFF_FORMAT_PATCH, DIFF_FORMAT_NO_OUTPUT),
- OPT_BIT_F('s', "no-patch", &options->output_format,
- N_("suppress diff output"),
- DIFF_FORMAT_NO_OUTPUT, PARSE_OPT_NONEG),
+ OPT_SET_INT('s', "no-patch", &options->output_format,
+ N_("suppress diff output"), DIFF_FORMAT_NO_OUTPUT),
OPT_BITOP('u', NULL, &options->output_format,
N_("generate patch"),
DIFF_FORMAT_PATCH, DIFF_FORMAT_NO_OUTPUT),
@@ -5502,9 +5513,9 @@ struct option *add_diff_options(const struct option *opts,
PARSE_OPT_NONEG | PARSE_OPT_OPTARG, diff_opt_unified),
OPT_BOOL('W', "function-context", &options->flags.funccontext,
N_("generate diffs with <n> lines context")),
- OPT_BIT_F(0, "raw", &options->output_format,
+ OPT_BITOP(0, "raw", &options->output_format,
N_("generate the diff in raw format"),
- DIFF_FORMAT_RAW, PARSE_OPT_NONEG),
+ DIFF_FORMAT_RAW, DIFF_FORMAT_NO_OUTPUT),
OPT_BITOP(0, "patch-with-raw", &options->output_format,
N_("synonym for '-p --raw'"),
DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW,
@@ -5513,12 +5524,12 @@ struct option *add_diff_options(const struct option *opts,
N_("synonym for '-p --stat'"),
DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT,
DIFF_FORMAT_NO_OUTPUT),
- OPT_BIT_F(0, "numstat", &options->output_format,
+ OPT_BITOP(0, "numstat", &options->output_format,
N_("machine friendly --stat"),
- DIFF_FORMAT_NUMSTAT, PARSE_OPT_NONEG),
- OPT_BIT_F(0, "shortstat", &options->output_format,
+ DIFF_FORMAT_NUMSTAT, DIFF_FORMAT_NO_OUTPUT),
+ OPT_BITOP(0, "shortstat", &options->output_format,
N_("output only the last line of --stat"),
- DIFF_FORMAT_SHORTSTAT, PARSE_OPT_NONEG),
+ DIFF_FORMAT_SHORTSTAT, DIFF_FORMAT_NO_OUTPUT),
OPT_CALLBACK_F('X', "dirstat", options, N_("<param1,param2>..."),
N_("output the distribution of relative amount of changes for each sub-directory"),
PARSE_OPT_NONEG | PARSE_OPT_OPTARG,
@@ -5534,9 +5545,9 @@ struct option *add_diff_options(const struct option *opts,
OPT_BIT_F(0, "check", &options->output_format,
N_("warn if changes introduce conflict markers or whitespace errors"),
DIFF_FORMAT_CHECKDIFF, PARSE_OPT_NONEG),
- OPT_BIT_F(0, "summary", &options->output_format,
+ OPT_BITOP(0, "summary", &options->output_format,
N_("condensed summary such as creations, renames and mode changes"),
- DIFF_FORMAT_SUMMARY, PARSE_OPT_NONEG),
+ DIFF_FORMAT_SUMMARY, DIFF_FORMAT_NO_OUTPUT),
OPT_BIT_F(0, "name-only", &options->output_format,
N_("show only names of changed files"),
DIFF_FORMAT_NAME, PARSE_OPT_NONEG),
diff --git a/diff.h b/diff.h
index 6a0737b9c3..c2e0e63609 100644
--- a/diff.h
+++ b/diff.h
@@ -4,11 +4,12 @@
#ifndef DIFF_H
#define DIFF_H
-#include "tree-walk.h"
+#include "hash-ll.h"
#include "pathspec.h"
-#include "oidset.h"
#include "strbuf.h"
+struct oidset;
+
/**
* The diff API is for programs that compare two sets of files (e.g. two trees,
* one tree and the index) and present the found difference in various ways.
@@ -695,4 +696,6 @@ void print_stat_summary(FILE *fp, int files,
int insertions, int deletions);
void setup_diff_pager(struct diff_options *);
+extern int diff_auto_refresh_index;
+
#endif /* DIFF_H */
diff --git a/diffcore-break.c b/diffcore-break.c
index 5462420bbb..f57ece2757 100644
--- a/diffcore-break.c
+++ b/diffcore-break.c
@@ -1,9 +1,11 @@
/*
* Copyright (C) 2005 Junio C Hamano
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "diff.h"
#include "diffcore.h"
+#include "hash.h"
+#include "object.h"
#include "promisor-remote.h"
static int should_break(struct repository *r,
diff --git a/diffcore-order.c b/diffcore-order.c
index 57ccab2846..e7d20ebd2d 100644
--- a/diffcore-order.c
+++ b/diffcore-order.c
@@ -5,6 +5,7 @@
#include "gettext.h"
#include "diff.h"
#include "diffcore.h"
+#include "wildmatch.h"
static char **order;
static int order_cnt;
diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c
index 13c98a7b5e..b195fa4eb3 100644
--- a/diffcore-pickaxe.c
+++ b/diffcore-pickaxe.c
@@ -7,6 +7,7 @@
#include "diffcore.h"
#include "xdiff-interface.h"
#include "kwset.h"
+#include "oidset.h"
#include "pretty.h"
#include "quote.h"
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 8e2e7a3ad7..926b554bd5 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -6,7 +6,7 @@
#include "alloc.h"
#include "diff.h"
#include "diffcore.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "hashmap.h"
#include "mem-pool.h"
#include "oid-array.h"
diff --git a/diffcore.h b/diffcore.h
index 1701ed50b9..5ffe4ec788 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -4,7 +4,7 @@
#ifndef DIFFCORE_H
#define DIFFCORE_H
-#include "hash.h"
+#include "hash-ll.h"
struct diff_options;
struct mem_pool;
diff --git a/dir.c b/dir.c
index aa840995c4..3acac7beb1 100644
--- a/dir.c
+++ b/dir.c
@@ -13,8 +13,10 @@
#include "dir.h"
#include "environment.h"
#include "gettext.h"
+#include "name-hash.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "path.h"
#include "attr.h"
#include "refs.h"
#include "wildmatch.h"
@@ -22,10 +24,14 @@
#include "utf8.h"
#include "varint.h"
#include "ewah/ewok.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
+#include "read-cache-ll.h"
#include "setup.h"
+#include "sparse-index.h"
#include "submodule-config.h"
+#include "symlinks.h"
#include "trace2.h"
+#include "tree.h"
#include "wrapper.h"
/*
diff --git a/dir.h b/dir.h
index 3d6c87387e..80def6fc08 100644
--- a/dir.h
+++ b/dir.h
@@ -1,11 +1,14 @@
#ifndef DIR_H
#define DIR_H
+#include "hash-ll.h"
#include "hashmap.h"
#include "pathspec.h"
#include "statinfo.h"
#include "strbuf.h"
+struct repository;
+
/**
* The directory listing API is used to enumerate paths in the work tree,
* optionally taking `.git/info/exclude` and `.gitignore` files per directory
@@ -640,4 +643,19 @@ static inline int starts_with_dot_dot_slash_native(const char *const path)
return path_match_flags(path, what | PATH_MATCH_NATIVE);
}
+
+#if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
+#define DTYPE(de) ((de)->d_type)
+#else
+#undef DT_UNKNOWN
+#undef DT_DIR
+#undef DT_REG
+#undef DT_LNK
+#define DT_UNKNOWN 0
+#define DT_DIR 1
+#define DT_REG 2
+#define DT_LNK 3
+#define DTYPE(de) DT_UNKNOWN
+#endif
+
#endif
diff --git a/editor.c b/editor.c
index b34e10606d..38c5dbbb79 100644
--- a/editor.c
+++ b/editor.c
@@ -6,6 +6,7 @@
#include "environment.h"
#include "gettext.h"
#include "pager.h"
+#include "path.h"
#include "strbuf.h"
#include "strvec.h"
#include "run-command.h"
diff --git a/entry.c b/entry.c
index d89e61fa64..f9a7c726a4 100644
--- a/entry.c
+++ b/entry.c
@@ -1,12 +1,15 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "blob.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "dir.h"
#include "environment.h"
#include "gettext.h"
#include "hex.h"
+#include "name-hash.h"
+#include "sparse-index.h"
#include "streaming.h"
#include "submodule.h"
+#include "symlinks.h"
#include "progress.h"
#include "fsmonitor.h"
#include "entry.h"
diff --git a/environment.c b/environment.c
index 8a96997539..5e9b331279 100644
--- a/environment.c
+++ b/environment.c
@@ -7,7 +7,7 @@
* even if you might want to know where the git directory etc
* are.
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "abspath.h"
#include "branch.h"
#include "convert.h"
@@ -20,7 +20,8 @@
#include "commit.h"
#include "strvec.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "path.h"
#include "replace-object.h"
#include "tmp-objdir.h"
#include "chdir-notify.h"
@@ -67,7 +68,6 @@ int read_replace_refs = 1;
enum eol core_eol = EOL_UNSET;
int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN;
char *check_roundtrip_encoding = "SHIFT-JIS";
-unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
@@ -111,7 +111,7 @@ char *git_work_tree_cfg;
static char *git_namespace;
/*
- * Repository-local GIT_* environment variables; see cache.h for details.
+ * Repository-local GIT_* environment variables; see environment.h for details.
*/
const char * const local_repo_env[] = {
ALTERNATE_DB_ENVIRONMENT,
diff --git a/environment.h b/environment.h
index a63f0c6a24..ae4b6b91ec 100644
--- a/environment.h
+++ b/environment.h
@@ -1,9 +1,8 @@
#ifndef ENVIRONMENT_H
#define ENVIRONMENT_H
-#include "strvec.h"
-
struct repository;
+struct strvec;
/*
* The character that begins a commented line in user-editable file
@@ -55,6 +54,7 @@ const char *getenv_safe(struct strvec *argv, const char *name);
#define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH"
#define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS"
#define GIT_TEXT_DOMAIN_DIR_ENVIRONMENT "GIT_TEXTDOMAINDIR"
+#define GIT_ATTR_SOURCE_ENVIRONMENT "GIT_ATTR_SOURCE"
/*
* Environment variable used in handshaking the wire protocol.
diff --git a/exec-cmd.c b/exec-cmd.c
index 6f61846389..1d597e84ea 100644
--- a/exec-cmd.c
+++ b/exec-cmd.c
@@ -1,9 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "abspath.h"
#include "environment.h"
#include "exec-cmd.h"
#include "gettext.h"
+#include "path.h"
#include "quote.h"
+#include "run-command.h"
#include "strvec.h"
#include "trace.h"
#include "trace2.h"
diff --git a/fetch-pack.c b/fetch-pack.c
index 6fa6e8af9a..a432eacab9 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -2,6 +2,7 @@
#include "alloc.h"
#include "repository.h"
#include "config.h"
+#include "date.h"
#include "environment.h"
#include "gettext.h"
#include "hex.h"
@@ -23,7 +24,8 @@
#include "oid-array.h"
#include "oidset.h"
#include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "path.h"
#include "connected.h"
#include "fetch-negotiator.h"
#include "fsck.h"
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index 5af0d4715b..ac9c1b9c6c 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -4,7 +4,7 @@
#include "environment.h"
#include "refs.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "diff.h"
#include "diff-merges.h"
#include "hex.h"
@@ -15,6 +15,7 @@
#include "fmt-merge-msg.h"
#include "commit-reach.h"
#include "gpg-interface.h"
+#include "wildmatch.h"
static int use_branch_desc;
static int suppress_dest_pattern_seen;
@@ -508,7 +509,8 @@ static void fmt_tag_signature(struct strbuf *tagbuf,
strbuf_complete_line(tagbuf);
if (sig->len) {
strbuf_addch(tagbuf, '\n');
- strbuf_add_commented_lines(tagbuf, sig->buf, sig->len);
+ strbuf_add_commented_lines(tagbuf, sig->buf, sig->len,
+ comment_line_char);
}
}
@@ -554,7 +556,8 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
strbuf_addch(&tagline, '\n');
strbuf_add_commented_lines(&tagline,
origins.items[first_tag].string,
- strlen(origins.items[first_tag].string));
+ strlen(origins.items[first_tag].string),
+ comment_line_char);
strbuf_insert(&tagbuf, 0, tagline.buf,
tagline.len);
strbuf_release(&tagline);
@@ -562,7 +565,8 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
strbuf_addch(&tagbuf, '\n');
strbuf_add_commented_lines(&tagbuf,
origins.items[i].string,
- strlen(origins.items[i].string));
+ strlen(origins.items[i].string),
+ comment_line_char);
fmt_tag_signature(&tagbuf, &sig, buf, len);
}
strbuf_release(&payload);
diff --git a/fsck.c b/fsck.c
index adbe8bf59e..a219d6f2c0 100644
--- a/fsck.c
+++ b/fsck.c
@@ -1,7 +1,10 @@
#include "git-compat-util.h"
#include "alloc.h"
+#include "date.h"
+#include "dir.h"
#include "hex.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "path.h"
#include "repository.h"
#include "object.h"
#include "attr.h"
diff --git a/fsmonitor--daemon.h b/fsmonitor--daemon.h
index e24838f9a8..70d776c54f 100644
--- a/fsmonitor--daemon.h
+++ b/fsmonitor--daemon.h
@@ -3,7 +3,6 @@
#ifdef HAVE_FSMONITOR_DAEMON_BACKEND
-#include "cache.h"
#include "dir.h"
#include "run-command.h"
#include "simple-ipc.h"
diff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c
index 866828e299..88575aa54c 100644
--- a/fsmonitor-ipc.c
+++ b/fsmonitor-ipc.c
@@ -1,8 +1,9 @@
-#include "cache.h"
-#include "fsmonitor.h"
+#include "git-compat-util.h"
+#include "fsmonitor-ll.h"
#include "gettext.h"
#include "simple-ipc.h"
#include "fsmonitor-ipc.h"
+#include "repository.h"
#include "run-command.h"
#include "strbuf.h"
#include "trace2.h"
diff --git a/fsmonitor-ll.h b/fsmonitor-ll.h
new file mode 100644
index 0000000000..0504ca07d6
--- /dev/null
+++ b/fsmonitor-ll.h
@@ -0,0 +1,52 @@
+#ifndef FSMONITOR_LL_H
+#define FSMONITOR_LL_H
+
+struct index_state;
+struct strbuf;
+
+extern struct trace_key trace_fsmonitor;
+
+/*
+ * Read the fsmonitor index extension and (if configured) restore the
+ * CE_FSMONITOR_VALID state.
+ */
+int read_fsmonitor_extension(struct index_state *istate, const void *data, unsigned long sz);
+
+/*
+ * Fill the fsmonitor_dirty ewah bits with their state from the index,
+ * before it is split during writing.
+ */
+void fill_fsmonitor_bitmap(struct index_state *istate);
+
+/*
+ * Write the CE_FSMONITOR_VALID state into the fsmonitor index
+ * extension. Reads from the fsmonitor_dirty ewah in the index.
+ */
+void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate);
+
+/*
+ * Add/remove the fsmonitor index extension
+ */
+void add_fsmonitor(struct index_state *istate);
+void remove_fsmonitor(struct index_state *istate);
+
+/*
+ * Add/remove the fsmonitor index extension as necessary based on the current
+ * core.fsmonitor setting.
+ */
+void tweak_fsmonitor(struct index_state *istate);
+
+/*
+ * Run the configured fsmonitor integration script and clear the
+ * CE_FSMONITOR_VALID bit for any files returned as dirty. Also invalidate
+ * any corresponding untracked cache directory structures. Optimized to only
+ * run the first time it is called.
+ */
+void refresh_fsmonitor(struct index_state *istate);
+
+/*
+ * Does the received result contain the "trivial" response?
+ */
+int fsmonitor_is_trivial_response(const struct strbuf *query_result);
+
+#endif /* FSMONITOR_LL_H */
diff --git a/fsmonitor.c b/fsmonitor.c
index 28c083d4d8..f670c50937 100644
--- a/fsmonitor.c
+++ b/fsmonitor.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "config.h"
#include "dir.h"
#include "environment.h"
diff --git a/fsmonitor.h b/fsmonitor.h
index c67e0ebc09..5195a8624d 100644
--- a/fsmonitor.h
+++ b/fsmonitor.h
@@ -1,56 +1,13 @@
#ifndef FSMONITOR_H
#define FSMONITOR_H
-#include "cache.h"
+#include "fsmonitor-ll.h"
#include "dir.h"
#include "fsmonitor-settings.h"
+#include "object.h"
+#include "read-cache-ll.h"
#include "trace.h"
-extern struct trace_key trace_fsmonitor;
-
-/*
- * Read the fsmonitor index extension and (if configured) restore the
- * CE_FSMONITOR_VALID state.
- */
-int read_fsmonitor_extension(struct index_state *istate, const void *data, unsigned long sz);
-
-/*
- * Fill the fsmonitor_dirty ewah bits with their state from the index,
- * before it is split during writing.
- */
-void fill_fsmonitor_bitmap(struct index_state *istate);
-
-/*
- * Write the CE_FSMONITOR_VALID state into the fsmonitor index
- * extension. Reads from the fsmonitor_dirty ewah in the index.
- */
-void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate);
-
-/*
- * Add/remove the fsmonitor index extension
- */
-void add_fsmonitor(struct index_state *istate);
-void remove_fsmonitor(struct index_state *istate);
-
-/*
- * Add/remove the fsmonitor index extension as necessary based on the current
- * core.fsmonitor setting.
- */
-void tweak_fsmonitor(struct index_state *istate);
-
-/*
- * Run the configured fsmonitor integration script and clear the
- * CE_FSMONITOR_VALID bit for any files returned as dirty. Also invalidate
- * any corresponding untracked cache directory structures. Optimized to only
- * run the first time it is called.
- */
-void refresh_fsmonitor(struct index_state *istate);
-
-/*
- * Does the received result contain the "trivial" response?
- */
-int fsmonitor_is_trivial_response(const struct strbuf *query_result);
-
/*
* Check if refresh_fsmonitor has been called at least once.
* refresh_fsmonitor is idempotent. Returns true if fsmonitor is
diff --git a/git-compat-util.h b/git-compat-util.h
index 5b2b99c17c..1889da7986 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -625,8 +625,6 @@ static inline int git_has_dir_sep(const char *path)
#include "compat/bswap.h"
-#include "wildmatch.h"
-
struct strbuf;
/* General helper functions */
diff --git a/git-send-email.perl b/git-send-email.perl
index 66c9171109..affbb88509 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -87,8 +87,10 @@ git send-email --dump-aliases
Automating:
--identity <str> * Use the sendemail.<id> options.
- --to-cmd <str> * Email To: via `<str> \$patch_path`
- --cc-cmd <str> * Email Cc: via `<str> \$patch_path`
+ --to-cmd <str> * Email To: via `<str> \$patch_path`.
+ --cc-cmd <str> * Email Cc: via `<str> \$patch_path`.
+ --header-cmd <str> * Add headers via `<str> \$patch_path`.
+ --no-header-cmd * Disable any header command in use.
--suppress-cc <str> * author, self, sob, cc, cccmd, body, bodycc, misc-by, all.
--[no-]cc-cover * Email Cc: addresses in the cover letter.
--[no-]to-cover * Email To: addresses in the cover letter.
@@ -202,7 +204,7 @@ my (@to,@cc,@xh,$envelope_sender,
$author,$sender,$smtp_authpass,$annotate,$compose,$time);
# Things we either get from config, *or* are overridden on the
# command-line.
-my ($no_cc, $no_to, $no_bcc, $no_identity);
+my ($no_cc, $no_to, $no_bcc, $no_identity, $no_header_cmd);
my (@config_to, @getopt_to);
my (@config_cc, @getopt_cc);
my (@config_bcc, @getopt_bcc);
@@ -269,7 +271,7 @@ sub do_edit {
# Variables with corresponding config settings
my ($suppress_from, $signed_off_by_cc);
my ($cover_cc, $cover_to);
-my ($to_cmd, $cc_cmd);
+my ($to_cmd, $cc_cmd, $header_cmd);
my ($smtp_server, $smtp_server_port, @smtp_server_options);
my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);
my ($batch_size, $relogin_delay);
@@ -318,6 +320,7 @@ my %config_settings = (
"tocmd" => \$to_cmd,
"cc" => \@config_cc,
"cccmd" => \$cc_cmd,
+ "headercmd" => \$header_cmd,
"aliasfiletype" => \$aliasfiletype,
"bcc" => \@config_bcc,
"suppresscc" => \@suppress_cc,
@@ -519,6 +522,8 @@ my %options = (
"compose" => \$compose,
"quiet" => \$quiet,
"cc-cmd=s" => \$cc_cmd,
+ "header-cmd=s" => \$header_cmd,
+ "no-header-cmd" => \$no_header_cmd,
"suppress-from!" => \$suppress_from,
"no-suppress-from" => sub {$suppress_from = 0},
"suppress-cc=s" => \@suppress_cc,
@@ -792,7 +797,19 @@ if (@rev_list_opts) {
@rev_list_opts);
}
-@files = handle_backup_files(@files);
+if (defined $sender) {
+ $sender =~ s/^\s+|\s+$//g;
+ ($sender) = expand_aliases($sender);
+} else {
+ $sender = $repoauthor->() || $repocommitter->() || '';
+}
+
+# $sender could be an already sanitized address
+# (e.g. sendemail.from could be manually sanitized by user).
+# But it's a no-op to run sanitize_address on an already sanitized address.
+$sender = sanitize_address($sender);
+
+$time = time - scalar $#files;
if ($validate) {
# FIFOs can only be read once, exclude them from validation.
@@ -810,6 +827,7 @@ if ($validate) {
$ENV{GIT_SENDEMAIL_FILE_TOTAL} = "$num_files";
foreach my $r (@real_files) {
$ENV{GIT_SENDEMAIL_FILE_COUNTER} = "$num";
+ pre_process_file($r, 1);
validate_patch($r, $target_xfer_encoding);
$num += 1;
}
@@ -817,6 +835,8 @@ if ($validate) {
delete $ENV{GIT_SENDEMAIL_FILE_TOTAL};
}
+@files = handle_backup_files(@files);
+
if (@files) {
unless ($quiet) {
print $_,"\n" for (@files);
@@ -1065,18 +1085,6 @@ if (!$force) {
}
}
-if (defined $sender) {
- $sender =~ s/^\s+|\s+$//g;
- ($sender) = expand_aliases($sender);
-} else {
- $sender = $repoauthor->() || $repocommitter->() || '';
-}
-
-# $sender could be an already sanitized address
-# (e.g. sendemail.from could be manually sanitized by user).
-# But it's a no-op to run sanitize_address on an already sanitized address.
-$sender = sanitize_address($sender);
-
my $to_whom = __("To whom should the emails be sent (if anyone)?");
my $prompting = 0;
if (!@initial_to && !defined $to_cmd) {
@@ -1236,10 +1244,6 @@ sub make_message_id {
#print "new message id = $message_id\n"; # Was useful for debugging
}
-
-
-$time = time - scalar $#files;
-
sub unquote_rfc2047 {
local ($_) = @_;
my $charset;
@@ -1517,16 +1521,7 @@ sub file_name_is_absolute {
return File::Spec::Functions::file_name_is_absolute($path);
}
-# Prepares the email, then asks the user what to do.
-#
-# If the user chooses to send the email, it's sent and 1 is returned.
-# If the user chooses not to send the email, 0 is returned.
-# If the user decides they want to make further edits, -1 is returned and the
-# caller is expected to call send_message again after the edits are performed.
-#
-# If an error occurs sending the email, this just dies.
-
-sub send_message {
+sub gen_header {
my @recipients = unique_email_list(@to);
@cc = (grep { my $cc = extract_valid_address_or_die($_);
not grep { $cc eq $_ || $_ =~ /<\Q${cc}\E>$/ } @recipients
@@ -1568,6 +1563,22 @@ Message-ID: $message_id
if (@xh) {
$header .= join("\n", @xh) . "\n";
}
+ my $recipients_ref = \@recipients;
+ return ($recipients_ref, $to, $date, $gitversion, $cc, $ccline, $header);
+}
+
+# Prepares the email, then asks the user what to do.
+#
+# If the user chooses to send the email, it's sent and 1 is returned.
+# If the user chooses not to send the email, 0 is returned.
+# If the user decides they want to make further edits, -1 is returned and the
+# caller is expected to call send_message again after the edits are performed.
+#
+# If an error occurs sending the email, this just dies.
+
+sub send_message {
+ my ($recipients_ref, $to, $date, $gitversion, $cc, $ccline, $header) = gen_header();
+ my @recipients = @$recipients_ref;
my @sendmail_parameters = ('-i', @recipients);
my $raw_from = $sender;
@@ -1757,11 +1768,8 @@ $in_reply_to = $initial_in_reply_to;
$references = $initial_in_reply_to || '';
$message_num = 0;
-# Prepares the email, prompts the user, sends it out
-# Returns 0 if an edit was done and the function should be called again, or 1
-# otherwise.
-sub process_file {
- my ($t) = @_;
+sub pre_process_file {
+ my ($t, $quiet) = @_;
open my $fh, "<", $t or die sprintf(__("can't open file %s"), $t);
@@ -1780,16 +1788,17 @@ sub process_file {
$subject = $initial_subject;
$message = "";
$message_num++;
- # First unfold multiline header fields
+ undef $message_id;
+ # Retrieve and unfold header fields.
+ my @header_lines = ();
while(<$fh>) {
last if /^\s*$/;
- if (/^\s+\S/ and @header) {
- chomp($header[$#header]);
- s/^\s+/ /;
- $header[$#header] .= $_;
- } else {
- push(@header, $_);
- }
+ push(@header_lines, $_);
+ }
+ @header = unfold_headers(@header_lines);
+ # Add computed headers, if applicable.
+ unless ($no_header_cmd || ! $header_cmd) {
+ push @header, invoke_header_cmd($header_cmd, $t);
}
# Now parse the header
foreach(@header) {
@@ -1915,9 +1924,9 @@ sub process_file {
}
close $fh;
- push @to, recipients_cmd("to-cmd", "to", $to_cmd, $t)
+ push @to, recipients_cmd("to-cmd", "to", $to_cmd, $t, $quiet)
if defined $to_cmd;
- push @cc, recipients_cmd("cc-cmd", "cc", $cc_cmd, $t)
+ push @cc, recipients_cmd("cc-cmd", "cc", $cc_cmd, $t, $quiet)
if defined $cc_cmd && !$suppress_cc{'cccmd'};
if ($broken_encoding{$t} && !$has_content_type) {
@@ -1976,6 +1985,15 @@ sub process_file {
@initial_to = @to;
}
}
+}
+
+# Prepares the email, prompts the user, and sends it out
+# Returns 0 if an edit was done and the function should be called again, or 1
+# on the email being successfully sent out.
+sub process_file {
+ my ($t) = @_;
+
+ pre_process_file($t, $quiet);
my $message_was_sent = send_message();
if ($message_was_sent == -1) {
@@ -2021,15 +2039,64 @@ foreach my $t (@files) {
}
}
+# Execute a command and return its output lines as an array. Blank
+# lines which do not appear at the end of the output are reported as
+# errors.
+sub execute_cmd {
+ my ($prefix, $cmd, $file) = @_;
+ my @lines = ();
+ my $seen_blank_line = 0;
+ open my $fh, "-|", "$cmd \Q$file\E"
+ or die sprintf(__("(%s) Could not execute '%s'"), $prefix, $cmd);
+ while (my $line = <$fh>) {
+ die sprintf(__("(%s) Malformed output from '%s'"), $prefix, $cmd)
+ if $seen_blank_line;
+ if ($line =~ /^$/) {
+ $seen_blank_line = $line =~ /^$/;
+ next;
+ }
+ push @lines, $line;
+ }
+ close $fh
+ or die sprintf(__("(%s) failed to close pipe to '%s'"), $prefix, $cmd);
+ return @lines;
+}
+
+# Process headers lines, unfolding multiline headers as defined by RFC
+# 2822.
+sub unfold_headers {
+ my @headers;
+ foreach(@_) {
+ last if /^\s*$/;
+ if (/^\s+\S/ and @headers) {
+ chomp($headers[$#headers]);
+ s/^\s+/ /;
+ $headers[$#headers] .= $_;
+ } else {
+ push(@headers, $_);
+ }
+ }
+ return @headers;
+}
+
+# Invoke the provided CMD with FILE as an argument, which should
+# output RFC 2822 email headers. Fold multiline headers and return the
+# headers as an array.
+sub invoke_header_cmd {
+ my ($cmd, $file) = @_;
+ my @lines = execute_cmd("header-cmd", $header_cmd, $file);
+ return unfold_headers(@lines);
+}
+
# Execute a command (e.g. $to_cmd) to get a list of email addresses
# and return a results array
sub recipients_cmd {
- my ($prefix, $what, $cmd, $file) = @_;
-
+ my ($prefix, $what, $cmd, $file, $quiet) = @_;
+ my @lines = ();
my @addresses = ();
- open my $fh, "-|", "$cmd \Q$file\E"
- or die sprintf(__("(%s) Could not execute '%s'"), $prefix, $cmd);
- while (my $address = <$fh>) {
+
+ @lines = execute_cmd($prefix, $cmd, $file);
+ for my $address (@lines) {
$address =~ s/^\s*//g;
$address =~ s/\s*$//g;
$address = sanitize_address($address);
@@ -2038,8 +2105,6 @@ sub recipients_cmd {
printf(__("(%s) Adding %s: %s from: '%s'\n"),
$prefix, $what, $address, $cmd) unless $quiet;
}
- close $fh
- or die sprintf(__("(%s) failed to close pipe to '%s'"), $prefix, $cmd);
return @addresses;
}
@@ -2110,10 +2175,21 @@ sub validate_patch {
chdir($repo->wc_path() or $repo->repo_path())
or die("chdir: $!");
local $ENV{"GIT_DIR"} = $repo->repo_path();
+
+ my ($recipients_ref, $to, $date, $gitversion, $cc, $ccline, $header) = gen_header();
+
+ require File::Temp;
+ my ($header_filehandle, $header_filename) = File::Temp::tempfile(
+ TEMPLATE => ".gitsendemail.header.XXXXXX",
+ DIR => $repo->repo_path(),
+ UNLINK => 1,
+ );
+ print $header_filehandle $header;
+
my @cmd = ("git", "hook", "run", "--ignore-missing",
$hook_name, "--");
- my @cmd_msg = (@cmd, "<patch>");
- my @cmd_run = (@cmd, $target);
+ my @cmd_msg = (@cmd, "<patch>", "<header>");
+ my @cmd_run = (@cmd, $target, $header_filename);
$hook_error = system_or_msg(\@cmd_run, undef, "@cmd_msg");
chdir($cwd_save) or die("chdir: $!");
}
diff --git a/git.c b/git.c
index a1252e4cd3..d64dc8e63c 100644
--- a/git.c
+++ b/git.c
@@ -5,10 +5,12 @@
#include "gettext.h"
#include "help.h"
#include "pager.h"
+#include "read-cache-ll.h"
#include "run-command.h"
#include "alias.h"
#include "replace-object.h"
#include "setup.h"
+#include "attr.h"
#include "shallow.h"
#include "trace.h"
#include "trace2.h"
@@ -314,6 +316,21 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
} else {
exit(list_cmds(cmd));
}
+ } else if (!strcmp(cmd, "--attr-source")) {
+ if (*argc < 2) {
+ fprintf(stderr, _("no attribute source given for --attr-source\n" ));
+ usage(git_usage_string);
+ }
+ setenv(GIT_ATTR_SOURCE_ENVIRONMENT, (*argv)[1], 1);
+ if (envchanged)
+ *envchanged = 1;
+ (*argv)++;
+ (*argc)--;
+ } else if (skip_prefix(cmd, "--attr-source=", &cmd)) {
+ set_git_attr_source(cmd);
+ setenv(GIT_ATTR_SOURCE_ENVIRONMENT, cmd, 1);
+ if (envchanged)
+ *envchanged = 1;
} else {
fprintf(stderr, _("unknown option: %s\n"), cmd);
usage(git_usage_string);
diff --git a/gitk-git/gitk b/gitk-git/gitk
index 0ae7d68590..df3ba2ea99 100755
--- a/gitk-git/gitk
+++ b/gitk-git/gitk
@@ -353,6 +353,16 @@ proc parseviewrevs {view revs} {
return $ret
}
+# Escapes a list of filter paths to be passed to git log via stdin. Note that
+# paths must not be quoted.
+proc escape_filter_paths {paths} {
+ set escaped [list]
+ foreach path $paths {
+ lappend escaped [string map {\\ \\\\ "\ " "\\\ "} $path]
+ }
+ return $escaped
+}
+
# Start off a git log process and arrange to read its output
proc start_rev_list {view} {
global startmsecs commitidx viewcomplete curview
@@ -405,14 +415,17 @@ proc start_rev_list {view} {
if {$revs eq {}} {
return 0
}
- set args [concat $vflags($view) $revs]
+ set args $vflags($view)
} else {
+ set revs {}
set args $vorigargs($view)
}
if {[catch {
set fd [open [concat | git log --no-color -z --pretty=raw $show_notes \
- --parents --boundary $args "--" $files] r]
+ --parents --boundary $args --stdin \
+ "<<[join [concat $revs "--" \
+ [escape_filter_paths $files]] "\\n"]"] r]
} err]} {
error_popup "[mc "Error executing git log:"] $err"
return 0
@@ -554,13 +567,20 @@ proc updatecommits {} {
set revs $newrevs
set vposids($view) [lsort -unique [concat $oldpos $vposids($view)]]
}
- set args [concat $vflags($view) $revs --not $oldpos]
+ set args $vflags($view)
+ foreach r $oldpos {
+ lappend revs "^$r"
+ }
} else {
+ set revs {}
set args $vorigargs($view)
}
if {[catch {
set fd [open [concat | git log --no-color -z --pretty=raw $show_notes \
- --parents --boundary $args "--" $vfilelimit($view)] r]
+ --parents --boundary $args --stdin \
+ "<<[join [concat $revs "--" \
+ [escape_filter_paths \
+ $vfilelimit($view)]] "\\n"]"] r]
} err]} {
error_popup "[mc "Error executing git log:"] $err"
return
@@ -10231,10 +10251,16 @@ proc getallcommits {} {
foreach id $seeds {
lappend ids "^$id"
}
+ lappend ids "--"
}
}
if {$ids ne {}} {
- set fd [open [concat $cmd $ids] r]
+ if {$ids eq "--all"} {
+ set cmd [concat $cmd "--all"]
+ } else {
+ set cmd [concat $cmd --stdin "<<[join $ids "\\n"]"]
+ }
+ set fd [open $cmd r]
fconfigure $fd -blocking 0
incr allcommits
nowbusy allcommits
diff --git a/gpg-interface.c b/gpg-interface.c
index f3ac5acdd9..6a3817bbca 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -1,16 +1,19 @@
#include "git-compat-util.h"
#include "commit.h"
#include "config.h"
+#include "date.h"
#include "gettext.h"
#include "run-command.h"
#include "strbuf.h"
#include "dir.h"
#include "ident.h"
#include "gpg-interface.h"
+#include "path.h"
#include "sigchain.h"
#include "tempfile.h"
#include "alias.h"
#include "wrapper.h"
+#include "environment.h"
static int git_gpg_config(const char *, const char *, void *);
@@ -584,8 +587,8 @@ static int verify_ssh_signed_buffer(struct signature_check *sigc,
}
}
- strbuf_stripspace(&ssh_keygen_out, 0);
- strbuf_stripspace(&ssh_keygen_err, 0);
+ strbuf_stripspace(&ssh_keygen_out, '\0');
+ strbuf_stripspace(&ssh_keygen_err, '\0');
/* Add stderr outputs to show the user actual ssh-keygen errors */
strbuf_add(&ssh_keygen_out, ssh_principals_err.buf, ssh_principals_err.len);
strbuf_add(&ssh_keygen_out, ssh_keygen_err.buf, ssh_keygen_err.len);
diff --git a/grep.c b/grep.c
index 073559f2cd..ebcd647478 100644
--- a/grep.c
+++ b/grep.c
@@ -3,7 +3,8 @@
#include "gettext.h"
#include "grep.h"
#include "hex.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "pretty.h"
#include "userdiff.h"
#include "xdiff-interface.h"
#include "diff.h"
diff --git a/hash-ll.h b/hash-ll.h
new file mode 100644
index 0000000000..8d7973769f
--- /dev/null
+++ b/hash-ll.h
@@ -0,0 +1,295 @@
+#ifndef HASH_LL_H
+#define HASH_LL_H
+
+#if defined(SHA1_APPLE)
+#include <CommonCrypto/CommonDigest.h>
+#elif defined(SHA1_OPENSSL)
+#include <openssl/sha.h>
+#elif defined(SHA1_DC)
+#include "sha1dc_git.h"
+#else /* SHA1_BLK */
+#include "block-sha1/sha1.h"
+#endif
+
+#if defined(SHA256_NETTLE)
+#include "sha256/nettle.h"
+#elif defined(SHA256_GCRYPT)
+#define SHA256_NEEDS_CLONE_HELPER
+#include "sha256/gcrypt.h"
+#elif defined(SHA256_OPENSSL)
+#include <openssl/sha.h>
+#else
+#include "sha256/block/sha256.h"
+#endif
+
+#ifndef platform_SHA_CTX
+/*
+ * platform's underlying implementation of SHA-1; could be OpenSSL,
+ * blk_SHA, Apple CommonCrypto, etc... Note that the relevant
+ * SHA-1 header may have already defined platform_SHA_CTX for our
+ * own implementations like block-sha1, so we list
+ * the default for OpenSSL compatible SHA-1 implementations here.
+ */
+#define platform_SHA_CTX SHA_CTX
+#define platform_SHA1_Init SHA1_Init
+#define platform_SHA1_Update SHA1_Update
+#define platform_SHA1_Final SHA1_Final
+#endif
+
+#define git_SHA_CTX platform_SHA_CTX
+#define git_SHA1_Init platform_SHA1_Init
+#define git_SHA1_Update platform_SHA1_Update
+#define git_SHA1_Final platform_SHA1_Final
+
+#ifndef platform_SHA256_CTX
+#define platform_SHA256_CTX SHA256_CTX
+#define platform_SHA256_Init SHA256_Init
+#define platform_SHA256_Update SHA256_Update
+#define platform_SHA256_Final SHA256_Final
+#endif
+
+#define git_SHA256_CTX platform_SHA256_CTX
+#define git_SHA256_Init platform_SHA256_Init
+#define git_SHA256_Update platform_SHA256_Update
+#define git_SHA256_Final platform_SHA256_Final
+
+#ifdef platform_SHA256_Clone
+#define git_SHA256_Clone platform_SHA256_Clone
+#endif
+
+#ifdef SHA1_MAX_BLOCK_SIZE
+#include "compat/sha1-chunked.h"
+#undef git_SHA1_Update
+#define git_SHA1_Update git_SHA1_Update_Chunked
+#endif
+
+static inline void git_SHA1_Clone(git_SHA_CTX *dst, const git_SHA_CTX *src)
+{
+ memcpy(dst, src, sizeof(*dst));
+}
+
+#ifndef SHA256_NEEDS_CLONE_HELPER
+static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *src)
+{
+ memcpy(dst, src, sizeof(*dst));
+}
+#endif
+
+/*
+ * Note that these constants are suitable for indexing the hash_algos array and
+ * comparing against each other, but are otherwise arbitrary, so they should not
+ * be exposed to the user or serialized to disk. To know whether a
+ * git_hash_algo struct points to some usable hash function, test the format_id
+ * field for being non-zero. Use the name field for user-visible situations and
+ * the format_id field for fixed-length fields on disk.
+ */
+/* An unknown hash function. */
+#define GIT_HASH_UNKNOWN 0
+/* SHA-1 */
+#define GIT_HASH_SHA1 1
+/* SHA-256 */
+#define GIT_HASH_SHA256 2
+/* Number of algorithms supported (including unknown). */
+#define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)
+
+/* "sha1", big-endian */
+#define GIT_SHA1_FORMAT_ID 0x73686131
+
+/* The length in bytes and in hex digits of an object name (SHA-1 value). */
+#define GIT_SHA1_RAWSZ 20
+#define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
+/* The block size of SHA-1. */
+#define GIT_SHA1_BLKSZ 64
+
+/* "s256", big-endian */
+#define GIT_SHA256_FORMAT_ID 0x73323536
+
+/* The length in bytes and in hex digits of an object name (SHA-256 value). */
+#define GIT_SHA256_RAWSZ 32
+#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
+/* The block size of SHA-256. */
+#define GIT_SHA256_BLKSZ 64
+
+/* The length in byte and in hex digits of the largest possible hash value. */
+#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ
+#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ
+/* The largest possible block size for any supported hash. */
+#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ
+
+struct object_id {
+ unsigned char hash[GIT_MAX_RAWSZ];
+ int algo; /* XXX requires 4-byte alignment */
+};
+
+#define GET_OID_QUIETLY 01
+#define GET_OID_COMMIT 02
+#define GET_OID_COMMITTISH 04
+#define GET_OID_TREE 010
+#define GET_OID_TREEISH 020
+#define GET_OID_BLOB 040
+#define GET_OID_FOLLOW_SYMLINKS 0100
+#define GET_OID_RECORD_PATH 0200
+#define GET_OID_ONLY_TO_DIE 04000
+#define GET_OID_REQUIRE_PATH 010000
+
+#define GET_OID_DISAMBIGUATORS \
+ (GET_OID_COMMIT | GET_OID_COMMITTISH | \
+ GET_OID_TREE | GET_OID_TREEISH | \
+ GET_OID_BLOB)
+
+enum get_oid_result {
+ FOUND = 0,
+ MISSING_OBJECT = -1, /* The requested object is missing */
+ SHORT_NAME_AMBIGUOUS = -2,
+ /* The following only apply when symlinks are followed */
+ DANGLING_SYMLINK = -4, /*
+ * The initial symlink is there, but
+ * (transitively) points to a missing
+ * in-tree file
+ */
+ SYMLINK_LOOP = -5,
+ NOT_DIR = -6, /*
+ * Somewhere along the symlink chain, a path is
+ * requested which contains a file as a
+ * non-final element.
+ */
+};
+
+/* A suitably aligned type for stack allocations of hash contexts. */
+union git_hash_ctx {
+ git_SHA_CTX sha1;
+ git_SHA256_CTX sha256;
+};
+typedef union git_hash_ctx git_hash_ctx;
+
+typedef void (*git_hash_init_fn)(git_hash_ctx *ctx);
+typedef void (*git_hash_clone_fn)(git_hash_ctx *dst, const git_hash_ctx *src);
+typedef void (*git_hash_update_fn)(git_hash_ctx *ctx, const void *in, size_t len);
+typedef void (*git_hash_final_fn)(unsigned char *hash, git_hash_ctx *ctx);
+typedef void (*git_hash_final_oid_fn)(struct object_id *oid, git_hash_ctx *ctx);
+
+struct git_hash_algo {
+ /*
+ * The name of the algorithm, as appears in the config file and in
+ * messages.
+ */
+ const char *name;
+
+ /* A four-byte version identifier, used in pack indices. */
+ uint32_t format_id;
+
+ /* The length of the hash in binary. */
+ size_t rawsz;
+
+ /* The length of the hash in hex characters. */
+ size_t hexsz;
+
+ /* The block size of the hash. */
+ size_t blksz;
+
+ /* The hash initialization function. */
+ git_hash_init_fn init_fn;
+
+ /* The hash context cloning function. */
+ git_hash_clone_fn clone_fn;
+
+ /* The hash update function. */
+ git_hash_update_fn update_fn;
+
+ /* The hash finalization function. */
+ git_hash_final_fn final_fn;
+
+ /* The hash finalization function for object IDs. */
+ git_hash_final_oid_fn final_oid_fn;
+
+ /* The OID of the empty tree. */
+ const struct object_id *empty_tree;
+
+ /* The OID of the empty blob. */
+ const struct object_id *empty_blob;
+
+ /* The all-zeros OID. */
+ const struct object_id *null_oid;
+};
+extern const struct git_hash_algo hash_algos[GIT_HASH_NALGOS];
+
+/*
+ * Return a GIT_HASH_* constant based on the name. Returns GIT_HASH_UNKNOWN if
+ * the name doesn't match a known algorithm.
+ */
+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);
+/* Identical, except for a pointer to struct git_hash_algo. */
+static inline int hash_algo_by_ptr(const struct git_hash_algo *p)
+{
+ return p - hash_algos;
+}
+
+const struct object_id *null_oid(void);
+
+static inline int hashcmp_algop(const unsigned char *sha1, const unsigned char *sha2, const struct git_hash_algo *algop)
+{
+ /*
+ * Teach the compiler that there are only two possibilities of hash size
+ * here, so that it can optimize for this case as much as possible.
+ */
+ if (algop->rawsz == GIT_MAX_RAWSZ)
+ return memcmp(sha1, sha2, GIT_MAX_RAWSZ);
+ return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
+}
+
+static inline int hasheq_algop(const unsigned char *sha1, const unsigned char *sha2, const struct git_hash_algo *algop)
+{
+ /*
+ * We write this here instead of deferring to hashcmp so that the
+ * compiler can properly inline it and avoid calling memcmp.
+ */
+ if (algop->rawsz == GIT_MAX_RAWSZ)
+ return !memcmp(sha1, sha2, GIT_MAX_RAWSZ);
+ return !memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
+}
+
+static inline void oidcpy(struct object_id *dst, const struct object_id *src)
+{
+ memcpy(dst->hash, src->hash, GIT_MAX_RAWSZ);
+ dst->algo = src->algo;
+}
+
+static inline struct object_id *oiddup(const struct object_id *src)
+{
+ struct object_id *dst = xmalloc(sizeof(struct object_id));
+ oidcpy(dst, src);
+ return dst;
+}
+
+static inline void oid_set_algo(struct object_id *oid, const struct git_hash_algo *algop)
+{
+ oid->algo = hash_algo_by_ptr(algop);
+}
+
+/*
+ * Converts a cryptographic hash (e.g. SHA-1) into an int-sized hash code
+ * for use in hash tables. Cryptographic hashes are supposed to have
+ * uniform distribution, so in contrast to `memhash()`, this just copies
+ * the first `sizeof(int)` bytes without shuffling any bits. Note that
+ * the results will be different on big-endian and little-endian
+ * platforms, so they should not be stored or transferred over the net.
+ */
+static inline unsigned int oidhash(const struct object_id *oid)
+{
+ /*
+ * Equivalent to 'return *(unsigned int *)oid->hash;', but safe on
+ * platforms that don't support unaligned reads.
+ */
+ unsigned int hash;
+ memcpy(&hash, oid->hash, sizeof(hash));
+ return hash;
+}
+
+const char *empty_tree_oid_hex(void);
+const char *empty_blob_oid_hex(void);
+
+#endif
diff --git a/hash-lookup.c b/hash-lookup.c
index b98ed5e11e..9f0f95e2b9 100644
--- a/hash-lookup.c
+++ b/hash-lookup.c
@@ -1,5 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "hash.h"
#include "hash-lookup.h"
+#include "read-cache-ll.h"
static uint32_t take2(const struct object_id *oid, size_t ofs)
{
diff --git a/hash.h b/hash.h
index d39f73618c..615ae0691d 100644
--- a/hash.h
+++ b/hash.h
@@ -1,250 +1,11 @@
#ifndef HASH_H
#define HASH_H
+#include "hash-ll.h"
#include "repository.h"
-#if defined(SHA1_APPLE)
-#include <CommonCrypto/CommonDigest.h>
-#elif defined(SHA1_OPENSSL)
-#include <openssl/sha.h>
-#elif defined(SHA1_DC)
-#include "sha1dc_git.h"
-#else /* SHA1_BLK */
-#include "block-sha1/sha1.h"
-#endif
-
-#if defined(SHA256_NETTLE)
-#include "sha256/nettle.h"
-#elif defined(SHA256_GCRYPT)
-#define SHA256_NEEDS_CLONE_HELPER
-#include "sha256/gcrypt.h"
-#elif defined(SHA256_OPENSSL)
-#include <openssl/sha.h>
-#else
-#include "sha256/block/sha256.h"
-#endif
-
-#ifndef platform_SHA_CTX
-/*
- * platform's underlying implementation of SHA-1; could be OpenSSL,
- * blk_SHA, Apple CommonCrypto, etc... Note that the relevant
- * SHA-1 header may have already defined platform_SHA_CTX for our
- * own implementations like block-sha1, so we list
- * the default for OpenSSL compatible SHA-1 implementations here.
- */
-#define platform_SHA_CTX SHA_CTX
-#define platform_SHA1_Init SHA1_Init
-#define platform_SHA1_Update SHA1_Update
-#define platform_SHA1_Final SHA1_Final
-#endif
-
-#define git_SHA_CTX platform_SHA_CTX
-#define git_SHA1_Init platform_SHA1_Init
-#define git_SHA1_Update platform_SHA1_Update
-#define git_SHA1_Final platform_SHA1_Final
-
-#ifndef platform_SHA256_CTX
-#define platform_SHA256_CTX SHA256_CTX
-#define platform_SHA256_Init SHA256_Init
-#define platform_SHA256_Update SHA256_Update
-#define platform_SHA256_Final SHA256_Final
-#endif
-
-#define git_SHA256_CTX platform_SHA256_CTX
-#define git_SHA256_Init platform_SHA256_Init
-#define git_SHA256_Update platform_SHA256_Update
-#define git_SHA256_Final platform_SHA256_Final
-
-#ifdef platform_SHA256_Clone
-#define git_SHA256_Clone platform_SHA256_Clone
-#endif
-
-#ifdef SHA1_MAX_BLOCK_SIZE
-#include "compat/sha1-chunked.h"
-#undef git_SHA1_Update
-#define git_SHA1_Update git_SHA1_Update_Chunked
-#endif
-
-static inline void git_SHA1_Clone(git_SHA_CTX *dst, const git_SHA_CTX *src)
-{
- memcpy(dst, src, sizeof(*dst));
-}
-
-#ifndef SHA256_NEEDS_CLONE_HELPER
-static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *src)
-{
- memcpy(dst, src, sizeof(*dst));
-}
-#endif
-
-/*
- * Note that these constants are suitable for indexing the hash_algos array and
- * comparing against each other, but are otherwise arbitrary, so they should not
- * be exposed to the user or serialized to disk. To know whether a
- * git_hash_algo struct points to some usable hash function, test the format_id
- * field for being non-zero. Use the name field for user-visible situations and
- * the format_id field for fixed-length fields on disk.
- */
-/* An unknown hash function. */
-#define GIT_HASH_UNKNOWN 0
-/* SHA-1 */
-#define GIT_HASH_SHA1 1
-/* SHA-256 */
-#define GIT_HASH_SHA256 2
-/* Number of algorithms supported (including unknown). */
-#define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)
-
-/* "sha1", big-endian */
-#define GIT_SHA1_FORMAT_ID 0x73686131
-
-/* The length in bytes and in hex digits of an object name (SHA-1 value). */
-#define GIT_SHA1_RAWSZ 20
-#define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
-/* The block size of SHA-1. */
-#define GIT_SHA1_BLKSZ 64
-
-/* "s256", big-endian */
-#define GIT_SHA256_FORMAT_ID 0x73323536
-
-/* The length in bytes and in hex digits of an object name (SHA-256 value). */
-#define GIT_SHA256_RAWSZ 32
-#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
-/* The block size of SHA-256. */
-#define GIT_SHA256_BLKSZ 64
-
-/* The length in byte and in hex digits of the largest possible hash value. */
-#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ
-#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ
-/* The largest possible block size for any supported hash. */
-#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ
-
-struct object_id {
- unsigned char hash[GIT_MAX_RAWSZ];
- int algo; /* XXX requires 4-byte alignment */
-};
-
-#define GET_OID_QUIETLY 01
-#define GET_OID_COMMIT 02
-#define GET_OID_COMMITTISH 04
-#define GET_OID_TREE 010
-#define GET_OID_TREEISH 020
-#define GET_OID_BLOB 040
-#define GET_OID_FOLLOW_SYMLINKS 0100
-#define GET_OID_RECORD_PATH 0200
-#define GET_OID_ONLY_TO_DIE 04000
-#define GET_OID_REQUIRE_PATH 010000
-
-#define GET_OID_DISAMBIGUATORS \
- (GET_OID_COMMIT | GET_OID_COMMITTISH | \
- GET_OID_TREE | GET_OID_TREEISH | \
- GET_OID_BLOB)
-
-enum get_oid_result {
- FOUND = 0,
- MISSING_OBJECT = -1, /* The requested object is missing */
- SHORT_NAME_AMBIGUOUS = -2,
- /* The following only apply when symlinks are followed */
- DANGLING_SYMLINK = -4, /*
- * The initial symlink is there, but
- * (transitively) points to a missing
- * in-tree file
- */
- SYMLINK_LOOP = -5,
- NOT_DIR = -6, /*
- * Somewhere along the symlink chain, a path is
- * requested which contains a file as a
- * non-final element.
- */
-};
-
-/* A suitably aligned type for stack allocations of hash contexts. */
-union git_hash_ctx {
- git_SHA_CTX sha1;
- git_SHA256_CTX sha256;
-};
-typedef union git_hash_ctx git_hash_ctx;
-
-typedef void (*git_hash_init_fn)(git_hash_ctx *ctx);
-typedef void (*git_hash_clone_fn)(git_hash_ctx *dst, const git_hash_ctx *src);
-typedef void (*git_hash_update_fn)(git_hash_ctx *ctx, const void *in, size_t len);
-typedef void (*git_hash_final_fn)(unsigned char *hash, git_hash_ctx *ctx);
-typedef void (*git_hash_final_oid_fn)(struct object_id *oid, git_hash_ctx *ctx);
-
-struct git_hash_algo {
- /*
- * The name of the algorithm, as appears in the config file and in
- * messages.
- */
- const char *name;
-
- /* A four-byte version identifier, used in pack indices. */
- uint32_t format_id;
-
- /* The length of the hash in binary. */
- size_t rawsz;
-
- /* The length of the hash in hex characters. */
- size_t hexsz;
-
- /* The block size of the hash. */
- size_t blksz;
-
- /* The hash initialization function. */
- git_hash_init_fn init_fn;
-
- /* The hash context cloning function. */
- git_hash_clone_fn clone_fn;
-
- /* The hash update function. */
- git_hash_update_fn update_fn;
-
- /* The hash finalization function. */
- git_hash_final_fn final_fn;
-
- /* The hash finalization function for object IDs. */
- git_hash_final_oid_fn final_oid_fn;
-
- /* The OID of the empty tree. */
- const struct object_id *empty_tree;
-
- /* The OID of the empty blob. */
- const struct object_id *empty_blob;
-
- /* The all-zeros OID. */
- const struct object_id *null_oid;
-};
-extern const struct git_hash_algo hash_algos[GIT_HASH_NALGOS];
-
-/*
- * Return a GIT_HASH_* constant based on the name. Returns GIT_HASH_UNKNOWN if
- * the name doesn't match a known algorithm.
- */
-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);
-/* Identical, except for a pointer to struct git_hash_algo. */
-static inline int hash_algo_by_ptr(const struct git_hash_algo *p)
-{
- return p - hash_algos;
-}
-
#define the_hash_algo the_repository->hash_algo
-const struct object_id *null_oid(void);
-
-static inline int hashcmp_algop(const unsigned char *sha1, const unsigned char *sha2, const struct git_hash_algo *algop)
-{
- /*
- * Teach the compiler that there are only two possibilities of hash size
- * here, so that it can optimize for this case as much as possible.
- */
- if (algop->rawsz == GIT_MAX_RAWSZ)
- return memcmp(sha1, sha2, GIT_MAX_RAWSZ);
- return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
-}
-
static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
{
return hashcmp_algop(sha1, sha2, the_hash_algo);
@@ -260,17 +21,6 @@ static inline int oidcmp(const struct object_id *oid1, const struct object_id *o
return hashcmp_algop(oid1->hash, oid2->hash, algop);
}
-static inline int hasheq_algop(const unsigned char *sha1, const unsigned char *sha2, const struct git_hash_algo *algop)
-{
- /*
- * We write this here instead of deferring to hashcmp so that the
- * compiler can properly inline it and avoid calling memcmp.
- */
- if (algop->rawsz == GIT_MAX_RAWSZ)
- return !memcmp(sha1, sha2, GIT_MAX_RAWSZ);
- return !memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
-}
-
static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)
{
return hasheq_algop(sha1, sha2, the_hash_algo);
@@ -296,12 +46,6 @@ static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
memcpy(sha_dst, sha_src, the_hash_algo->rawsz);
}
-static inline void oidcpy(struct object_id *dst, const struct object_id *src)
-{
- memcpy(dst->hash, src->hash, GIT_MAX_RAWSZ);
- dst->algo = src->algo;
-}
-
/* Like oidcpy() but zero-pads the unused bytes in dst's hash array. */
static inline void oidcpy_with_padding(struct object_id *dst,
const struct object_id *src)
@@ -318,13 +62,6 @@ static inline void oidcpy_with_padding(struct object_id *dst,
dst->algo = src->algo;
}
-static inline struct object_id *oiddup(const struct object_id *src)
-{
- struct object_id *dst = xmalloc(sizeof(struct object_id));
- oidcpy(dst, src);
- return dst;
-}
-
static inline void hashclr(unsigned char *hash)
{
memset(hash, 0, the_hash_algo->rawsz);
@@ -362,12 +99,4 @@ static inline int is_empty_tree_oid(const struct object_id *oid)
return oideq(oid, the_hash_algo->empty_tree);
}
-static inline void oid_set_algo(struct object_id *oid, const struct git_hash_algo *algop)
-{
- oid->algo = hash_algo_by_ptr(algop);
-}
-
-const char *empty_tree_oid_hex(void);
-const char *empty_blob_oid_hex(void);
-
#endif
diff --git a/hashmap.h b/hashmap.h
index e2cf9c78d8..c8216e47bb 100644
--- a/hashmap.h
+++ b/hashmap.h
@@ -1,8 +1,6 @@
#ifndef HASHMAP_H
#define HASHMAP_H
-#include "hash.h"
-
/*
* Generic implementation of hash-based key-value mappings.
*
@@ -121,25 +119,6 @@ unsigned int memihash(const void *buf, size_t len);
unsigned int memihash_cont(unsigned int hash_seed, const void *buf, size_t len);
/*
- * Converts a cryptographic hash (e.g. SHA-1) into an int-sized hash code
- * for use in hash tables. Cryptographic hashes are supposed to have
- * uniform distribution, so in contrast to `memhash()`, this just copies
- * the first `sizeof(int)` bytes without shuffling any bits. Note that
- * the results will be different on big-endian and little-endian
- * platforms, so they should not be stored or transferred over the net.
- */
-static inline unsigned int oidhash(const struct object_id *oid)
-{
- /*
- * Equivalent to 'return *(unsigned int *)oid->hash;', but safe on
- * platforms that don't support unaligned reads.
- */
- unsigned int hash;
- memcpy(&hash, oid->hash, sizeof(hash));
- return hash;
-}
-
-/*
* struct hashmap_entry is an opaque structure representing an entry in the
* hash table.
* Ideally it should be followed by an int-sized member to prevent unused
diff --git a/hex.c b/hex.c
index 0a1bddc128..7bb440e794 100644
--- a/hex.c
+++ b/hex.c
@@ -1,4 +1,5 @@
#include "git-compat-util.h"
+#include "hash.h"
#include "hex.h"
const signed char hexval_table[256] = {
diff --git a/hex.h b/hex.h
index e2abfc56fa..7df4b3c460 100644
--- a/hex.h
+++ b/hex.h
@@ -1,7 +1,7 @@
#ifndef HEX_H
#define HEX_H
-#include "hash.h"
+#include "hash-ll.h"
extern const signed char hexval_table[256];
static inline unsigned int hexval(unsigned char c)
diff --git a/hook.c b/hook.c
index 76e322f580..f6306d72b3 100644
--- a/hook.c
+++ b/hook.c
@@ -1,7 +1,9 @@
#include "git-compat-util.h"
+#include "abspath.h"
#include "advice.h"
#include "gettext.h"
#include "hook.h"
+#include "path.h"
#include "run-command.h"
#include "config.h"
#include "strbuf.h"
diff --git a/http-backend.c b/http-backend.c
index ac146d85c5..895fac35f7 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -4,6 +4,7 @@
#include "environment.h"
#include "git-zlib.h"
#include "hex.h"
+#include "path.h"
#include "repository.h"
#include "refs.h"
#include "pkt-line.h"
@@ -15,7 +16,7 @@
#include "url.h"
#include "strvec.h"
#include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "protocol.h"
#include "date.h"
#include "wrapper.h"
@@ -559,7 +560,7 @@ static void get_info_refs(struct strbuf *hdr, char *arg UNUSED)
} else {
select_getanyfile(hdr);
- for_each_namespaced_ref(show_text_ref, &buf);
+ for_each_namespaced_ref(NULL, show_text_ref, &buf);
send_strbuf(hdr, "text/plain", &buf);
}
strbuf_release(&buf);
diff --git a/http-push.c b/http-push.c
index 76ab5dbb09..9ab2383d2b 100644
--- a/http-push.c
+++ b/http-push.c
@@ -15,8 +15,10 @@
#include "setup.h"
#include "sigchain.h"
#include "strvec.h"
+#include "tree.h"
+#include "tree-walk.h"
#include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "commit-reach.h"
#ifdef EXPAT_NEEDS_XMLPARSE_H
diff --git a/http-walker.c b/http-walker.c
index bba306b2d5..78d99f7c4b 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -7,7 +7,7 @@
#include "list.h"
#include "transport.h"
#include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
struct alt_base {
char *base;
diff --git a/http.c b/http.c
index bb58bb3e6a..25e4c8a1ae 100644
--- a/http.c
+++ b/http.c
@@ -18,7 +18,7 @@
#include "protocol.h"
#include "string-list.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);
static int trace_curl_data = 1;
diff --git a/imap-send.c b/imap-send.c
index a62424e90a..d1f312a07c 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -29,30 +29,21 @@
#include "run-command.h"
#include "parse-options.h"
#include "setup.h"
+#include "strbuf.h"
#include "wrapper.h"
#if defined(NO_OPENSSL) && !defined(HAVE_OPENSSL_CSPRNG)
typedef void *SSL;
#endif
-#ifdef USE_CURL_FOR_IMAP_SEND
#include "http.h"
-#endif
-
-#if defined(USE_CURL_FOR_IMAP_SEND)
-/* Always default to curl if it's available. */
-#define USE_CURL_DEFAULT 1
-#else
-/* We don't have curl, so continue to use the historical implementation */
-#define USE_CURL_DEFAULT 0
-#endif
static int verbosity;
-static int use_curl = USE_CURL_DEFAULT;
+static int use_curl = 1;
static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
- OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
+ OPT_HIDDEN_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
OPT_END()
};
@@ -210,14 +201,7 @@ static void socket_perror(const char *func, struct imap_socket *sock, int ret)
}
}
-#ifdef NO_OPENSSL
-static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
-{
- fprintf(stderr, "SSL requested but SSL support not compiled in\n");
- return -1;
-}
-
-#else
+#ifndef NO_OPENSSL
static int host_matches(const char *host, const char *pattern)
{
@@ -266,7 +250,7 @@ static int verify_hostname(X509 *cert, const char *hostname)
cname, hostname);
}
-static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
+static int ssl_socket_connect(struct imap_socket *sock, int verify)
{
#if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
const SSL_METHOD *meth;
@@ -292,8 +276,7 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
return -1;
}
- if (use_tls_only)
- SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+ SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
if (verify)
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
@@ -938,7 +921,7 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
return;
cred->protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
- cred->host = xstrdup(srvc->host);
+ cred->host = xstrdup(srvc->tunnel ? "tunnel" : srvc->host);
cred->username = xstrdup_or_null(srvc->user);
cred->password = xstrdup_or_null(srvc->pass);
@@ -957,7 +940,8 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
struct imap_store *ctx;
struct imap *imap;
char *arg, *rsp;
- int s = -1, preauth;
+ int preauth;
+ struct child_process tunnel = CHILD_PROCESS_INIT;
CALLOC_ARRAY(ctx, 1);
@@ -966,107 +950,19 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
imap->in_progress_append = &imap->in_progress;
/* open connection to IMAP server */
+ imap_info("Starting tunnel '%s'... ", srvc->tunnel);
- if (srvc->tunnel) {
- struct child_process tunnel = CHILD_PROCESS_INIT;
-
- imap_info("Starting tunnel '%s'... ", srvc->tunnel);
-
- strvec_push(&tunnel.args, srvc->tunnel);
- tunnel.use_shell = 1;
- tunnel.in = -1;
- tunnel.out = -1;
- if (start_command(&tunnel))
- die("cannot start proxy %s", srvc->tunnel);
-
- imap->buf.sock.fd[0] = tunnel.out;
- imap->buf.sock.fd[1] = tunnel.in;
-
- imap_info("ok\n");
- } else {
-#ifndef NO_IPV6
- struct addrinfo hints, *ai0, *ai;
- int gai;
- char portstr[6];
-
- xsnprintf(portstr, sizeof(portstr), "%d", srvc->port);
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_protocol = IPPROTO_TCP;
-
- imap_info("Resolving %s... ", srvc->host);
- gai = getaddrinfo(srvc->host, portstr, &hints, &ai);
- if (gai) {
- fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
- goto bail;
- }
- imap_info("ok\n");
-
- for (ai0 = ai; ai; ai = ai->ai_next) {
- char addr[NI_MAXHOST];
-
- s = socket(ai->ai_family, ai->ai_socktype,
- ai->ai_protocol);
- if (s < 0)
- continue;
-
- getnameinfo(ai->ai_addr, ai->ai_addrlen, addr,
- sizeof(addr), NULL, 0, NI_NUMERICHOST);
- imap_info("Connecting to [%s]:%s... ", addr, portstr);
-
- if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
- close(s);
- s = -1;
- perror("connect");
- continue;
- }
-
- break;
- }
- freeaddrinfo(ai0);
-#else /* NO_IPV6 */
- struct hostent *he;
- struct sockaddr_in addr;
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_port = htons(srvc->port);
- addr.sin_family = AF_INET;
-
- imap_info("Resolving %s... ", srvc->host);
- he = gethostbyname(srvc->host);
- if (!he) {
- perror("gethostbyname");
- goto bail;
- }
- imap_info("ok\n");
-
- addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
-
- s = socket(PF_INET, SOCK_STREAM, 0);
-
- imap_info("Connecting to %s:%hu... ", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
- if (connect(s, (struct sockaddr *)&addr, sizeof(addr))) {
- close(s);
- s = -1;
- perror("connect");
- }
-#endif
- if (s < 0) {
- fputs("Error: unable to connect to server.\n", stderr);
- goto bail;
- }
+ strvec_push(&tunnel.args, srvc->tunnel);
+ tunnel.use_shell = 1;
+ tunnel.in = -1;
+ tunnel.out = -1;
+ if (start_command(&tunnel))
+ die("cannot start proxy %s", srvc->tunnel);
- imap->buf.sock.fd[0] = s;
- imap->buf.sock.fd[1] = dup(s);
+ imap->buf.sock.fd[0] = tunnel.out;
+ imap->buf.sock.fd[1] = tunnel.in;
- if (srvc->use_ssl &&
- ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
- close(s);
- goto bail;
- }
- imap_info("ok\n");
- }
+ imap_info("ok\n");
/* read the greeting string */
if (buffer_gets(&imap->buf, &rsp)) {
@@ -1094,7 +990,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (!srvc->use_ssl && CAP(STARTTLS)) {
if (imap_exec(ctx, NULL, "STARTTLS") != RESP_OK)
goto bail;
- if (ssl_socket_connect(&imap->buf.sock, 1,
+ if (ssl_socket_connect(&imap->buf.sock,
srvc->ssl_verify))
goto bail;
/* capabilities may have changed, so get the new capabilities */
@@ -1112,7 +1008,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (!CAP(AUTH_CRAM_MD5)) {
fprintf(stderr, "You specified "
"CRAM-MD5 as authentication method, "
- "but %s doesn't support it.\n", srvc->host);
+ "but tunnel doesn't support it.\n");
goto bail;
}
/* CRAM-MD5 */
@@ -1124,13 +1020,13 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
goto bail;
}
} else {
- fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+ fprintf(stderr, "Unknown authentication method:%s\n", srvc->auth_method);
goto bail;
}
} else {
if (CAP(NOLOGIN)) {
- fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
- srvc->user, srvc->host);
+ fprintf(stderr, "Skipping account %s, server forbids LOGIN\n",
+ srvc->user);
goto bail;
}
if (!imap->buf.sock.ssl)
@@ -1399,7 +1295,6 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
return 0;
}
-#ifdef USE_CURL_FOR_IMAP_SEND
static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
{
CURL *curl;
@@ -1518,7 +1413,6 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
return res != CURLE_OK;
}
-#endif
int cmd_main(int argc, const char **argv)
{
@@ -1534,17 +1428,8 @@ int cmd_main(int argc, const char **argv)
if (argc)
usage_with_options(imap_send_usage, imap_send_options);
-#ifndef USE_CURL_FOR_IMAP_SEND
- if (use_curl) {
- warning("--curl not supported in this build");
- use_curl = 0;
- }
-#elif defined(NO_OPENSSL)
- if (!use_curl) {
- warning("--no-curl not supported in this build");
- use_curl = 1;
- }
-#endif
+ if (!use_curl)
+ die(_("the --no-curl option to imap-send has been deprecated"));
if (!server.port)
server.port = server.use_ssl ? 993 : 143;
@@ -1553,12 +1438,9 @@ int cmd_main(int argc, const char **argv)
fprintf(stderr, "no imap store specified\n");
return 1;
}
- if (!server.host) {
- if (!server.tunnel) {
- fprintf(stderr, "no imap host specified\n");
- return 1;
- }
- server.host = "tunnel";
+ if (!server.host && !server.tunnel) {
+ fprintf(stderr, "no imap host specified\n");
+ return 1;
}
/* read the messages */
@@ -1580,13 +1462,8 @@ int cmd_main(int argc, const char **argv)
/* write it to the imap server */
- if (server.tunnel)
- return append_msgs_to_imap(&server, &all_msgs, total);
-
-#ifdef USE_CURL_FOR_IMAP_SEND
- if (use_curl)
+ if (!server.tunnel)
return curl_append_msgs_to_imap(&server, &all_msgs, total);
-#endif
return append_msgs_to_imap(&server, &all_msgs, total);
}
diff --git a/khash.h b/khash.h
index 85362718c5..ff88163177 100644
--- a/khash.h
+++ b/khash.h
@@ -26,7 +26,7 @@
#ifndef __AC_KHASH_H
#define __AC_KHASH_H
-#include "hashmap.h"
+#include "hash.h"
#define AC_VERSION_KHASH_H "0.2.8"
@@ -61,7 +61,7 @@ static inline khint_t __ac_X31_hash_string(const char *s)
static const double __ac_HASH_UPPER = 0.77;
#define __KHASH_TYPE(name, khkey_t, khval_t) \
- typedef struct { \
+ typedef struct kh_##name { \
khint_t n_buckets, size, n_occupied, upper_bound; \
khint32_t *flags; \
khkey_t *keys; \
diff --git a/line-log.c b/line-log.c
index 10c19daec4..2eff914bf3 100644
--- a/line-log.c
+++ b/line-log.c
@@ -8,6 +8,7 @@
#include "diff.h"
#include "commit.h"
#include "decorate.h"
+#include "repository.h"
#include "revision.h"
#include "xdiff-interface.h"
#include "strbuf.h"
@@ -18,6 +19,7 @@
#include "setup.h"
#include "strvec.h"
#include "bloom.h"
+#include "tree-walk.h"
static void range_set_grow(struct range_set *rs, size_t extra)
{
diff --git a/list-objects-filter-options.h b/list-objects-filter-options.h
index f620612586..55fab8563d 100644
--- a/list-objects-filter-options.h
+++ b/list-objects-filter-options.h
@@ -3,7 +3,6 @@
#include "gettext.h"
#include "object.h"
-#include "string-list.h"
#include "strbuf.h"
struct option;
diff --git a/list-objects-filter.c b/list-objects-filter.c
index 5d270ce598..e075a66c99 100644
--- a/list-objects-filter.c
+++ b/list-objects-filter.c
@@ -16,7 +16,7 @@
#include "oidmap.h"
#include "oidset.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
/* Remember to update object flag allocation in object.h */
/*
diff --git a/list-objects.c b/list-objects.c
index eecca721ac..672a4cd529 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -12,7 +12,7 @@
#include "list-objects-filter.h"
#include "list-objects-filter-options.h"
#include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "trace.h"
struct traversal_context {
diff --git a/log-tree.c b/log-tree.c
index 143b86fecf..f807e286c8 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -5,7 +5,7 @@
#include "environment.h"
#include "hex.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "repository.h"
#include "tmp-objdir.h"
#include "commit.h"
@@ -16,6 +16,7 @@
#include "reflog-walk.h"
#include "refs.h"
#include "replace-object.h"
+#include "revision.h"
#include "string-list.h"
#include "color.h"
#include "gpg-interface.h"
@@ -24,6 +25,8 @@
#include "help.h"
#include "range-diff.h"
#include "strmap.h"
+#include "tree.h"
+#include "wildmatch.h"
#include "write-or-die.h"
static struct decoration name_decoration = { "object names" };
diff --git a/log-tree.h b/log-tree.h
index e7e4641cf8..bdb6432815 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -1,7 +1,7 @@
#ifndef LOG_TREE_H
#define LOG_TREE_H
-#include "revision.h"
+struct rev_info;
struct log_info {
struct commit *commit, *parent;
diff --git a/ls-refs.c b/ls-refs.c
index b9f3e08ec3..c9a723ba89 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -1,6 +1,7 @@
#include "git-compat-util.h"
#include "environment.h"
#include "gettext.h"
+#include "hash.h"
#include "hex.h"
#include "repository.h"
#include "refs.h"
@@ -71,7 +72,7 @@ struct ls_refs_data {
unsigned symrefs;
struct strvec prefixes;
struct strbuf buf;
- struct string_list hidden_refs;
+ struct strvec hidden_refs;
unsigned unborn : 1;
};
@@ -154,7 +155,7 @@ int ls_refs(struct repository *r, struct packet_reader *request)
memset(&data, 0, sizeof(data));
strvec_init(&data.prefixes);
strbuf_init(&data.buf, 0);
- string_list_init_dup(&data.hidden_refs);
+ strvec_init(&data.hidden_refs);
git_config(ls_refs_config, &data);
@@ -192,11 +193,11 @@ int ls_refs(struct repository *r, struct packet_reader *request)
strvec_push(&data.prefixes, "");
refs_for_each_fullref_in_prefixes(get_main_ref_store(r),
get_git_namespace(), data.prefixes.v,
- send_ref, &data);
+ data.hidden_refs.v, send_ref, &data);
packet_fflush(stdout);
strvec_clear(&data.prefixes);
strbuf_release(&data.buf);
- string_list_clear(&data.hidden_refs, 0);
+ strvec_clear(&data.hidden_refs);
return 0;
}
diff --git a/mailmap.c b/mailmap.c
index 5dc5223c43..3d6a5e9400 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -3,7 +3,7 @@
#include "string-list.h"
#include "mailmap.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "setup.h"
const char *git_mailmap_file;
diff --git a/match-trees.c b/match-trees.c
index 5877fc64a8..0885ac681c 100644
--- a/match-trees.c
+++ b/match-trees.c
@@ -1,8 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "hex.h"
+#include "match-trees.h"
+#include "strbuf.h"
#include "tree.h"
#include "tree-walk.h"
-#include "object-store.h"
+#include "object-store-ll.h"
static int score_missing(unsigned mode)
{
diff --git a/match-trees.h b/match-trees.h
new file mode 100644
index 0000000000..e3877acd58
--- /dev/null
+++ b/match-trees.h
@@ -0,0 +1,10 @@
+#ifndef MATCH_TREES_H
+#define MATCH_TREES_H
+
+struct object_id;
+struct repository;
+
+void shift_tree(struct repository *, const struct object_id *, const struct object_id *, struct object_id *, int);
+void shift_tree_by(struct repository *, const struct object_id *, const struct object_id *, struct object_id *, const char *);
+
+#endif /* MATCH_TREES_H */
diff --git a/merge-blobs.c b/merge-blobs.c
index 5632ff6abb..9293cbf75c 100644
--- a/merge-blobs.c
+++ b/merge-blobs.c
@@ -1,10 +1,10 @@
#include "git-compat-util.h"
#include "run-command.h"
#include "xdiff-interface.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
#include "blob.h"
#include "merge-blobs.h"
-#include "object-store.h"
+#include "object-store-ll.h"
static int fill_mmfile_blob(mmfile_t *f, struct blob *obj)
{
diff --git a/ll-merge.c b/merge-ll.c
index 28bc94c45d..740b8c6bfd 100644
--- a/ll-merge.c
+++ b/merge-ll.c
@@ -4,14 +4,15 @@
* Copyright (c) 2007 Junio C Hamano
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "config.h"
#include "convert.h"
#include "attr.h"
#include "xdiff-interface.h"
#include "run-command.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
#include "quote.h"
+#include "strbuf.h"
#include "wrapper.h"
struct ll_merge_driver;
@@ -393,7 +394,7 @@ enum ll_merge_result ll_merge(mmbuffer_t *result_buf,
normalize_file(theirs, path, istate);
}
- git_check_attr(istate, NULL, path, check);
+ git_check_attr(istate, path, check);
ll_driver_name = check->items[0].value;
if (check->items[1].value) {
marker_size = atoi(check->items[1].value);
@@ -421,7 +422,7 @@ int ll_merge_marker_size(struct index_state *istate, const char *path)
if (!check)
check = attr_check_initl("conflict-marker-size", NULL);
- git_check_attr(istate, NULL, path, check);
+ git_check_attr(istate, path, check);
if (check->items[0].value) {
marker_size = atoi(check->items[0].value);
if (marker_size <= 0)
diff --git a/ll-merge.h b/merge-ll.h
index e4a20e81a3..e4a20e81a3 100644
--- a/ll-merge.h
+++ b/merge-ll.h
diff --git a/merge-ort-wrappers.c b/merge-ort-wrappers.c
index c00dfbab1c..4acedf3c33 100644
--- a/merge-ort-wrappers.c
+++ b/merge-ort-wrappers.c
@@ -1,7 +1,10 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "gettext.h"
+#include "hash.h"
#include "merge-ort.h"
#include "merge-ort-wrappers.h"
+#include "read-cache-ll.h"
+#include "tree.h"
#include "commit.h"
diff --git a/merge-ort.c b/merge-ort.c
index 34ec2675a2..8631c99700 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -14,7 +14,7 @@
* "cale", "peedy", or "ins" instead of "ort"?)
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "merge-ort.h"
#include "alloc.h"
@@ -30,13 +30,17 @@
#include "gettext.h"
#include "hex.h"
#include "entry.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
+#include "match-trees.h"
#include "mem-pool.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "oid-array.h"
+#include "path.h"
#include "promisor-remote.h"
+#include "read-cache-ll.h"
#include "revision.h"
+#include "sparse-index.h"
#include "strmap.h"
#include "submodule-config.h"
#include "submodule.h"
diff --git a/merge-ort.h b/merge-ort.h
index a994c9a5fc..ce56ec1a78 100644
--- a/merge-ort.h
+++ b/merge-ort.h
@@ -2,7 +2,7 @@
#define MERGE_ORT_H
#include "merge-recursive.h"
-#include "hash.h"
+#include "hash-ll.h"
struct commit;
struct tree;
diff --git a/merge-recursive.c b/merge-recursive.c
index 9875bdb11c..43f6b2d036 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -3,7 +3,7 @@
* Fredrik Kuivinen.
* The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "merge-recursive.h"
#include "advice.h"
@@ -20,16 +20,21 @@
#include "environment.h"
#include "gettext.h"
#include "hex.h"
-#include "ll-merge.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.h"
+#include "object-store-ll.h"
+#include "path.h"
#include "repository.h"
#include "revision.h"
+#include "sparse-index.h"
#include "string-list.h"
#include "submodule-config.h"
#include "submodule.h"
+#include "symlinks.h"
#include "tag.h"
#include "tree-walk.h"
#include "unpack-trees.h"
diff --git a/merge.c b/merge.c
index da7fa652c2..b60925459c 100644
--- a/merge.c
+++ b/merge.c
@@ -1,12 +1,16 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "diff.h"
#include "diffcore.h"
#include "gettext.h"
+#include "hash.h"
#include "hex.h"
#include "lockfile.h"
+#include "merge.h"
#include "commit.h"
+#include "repository.h"
#include "run-command.h"
#include "resolve-undo.h"
+#include "tree.h"
#include "tree-walk.h"
#include "unpack-trees.h"
#include "dir.h"
diff --git a/merge.h b/merge.h
new file mode 100644
index 0000000000..21ac7ef2f1
--- /dev/null
+++ b/merge.h
@@ -0,0 +1,17 @@
+#ifndef MERGE_H
+#define MERGE_H
+
+struct commit_list;
+struct object_id;
+struct repository;
+
+int try_merge_command(struct repository *r,
+ const char *strategy, size_t xopts_nr,
+ const char **xopts, struct commit_list *common,
+ const char *head_arg, struct commit_list *remotes);
+int checkout_fast_forward(struct repository *r,
+ const struct object_id *from,
+ const struct object_id *to,
+ int overwrite_ignore);
+
+#endif /* MERGE_H */
diff --git a/midx.c b/midx.c
index 281a1406a8..db459e448b 100644
--- a/midx.c
+++ b/midx.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "abspath.h"
#include "alloc.h"
#include "config.h"
@@ -9,7 +9,7 @@
#include "lockfile.h"
#include "packfile.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "hash-lookup.h"
#include "midx.h"
#include "progress.h"
diff --git a/name-hash.c b/name-hash.c
index fb13716e43..251f036eef 100644
--- a/name-hash.c
+++ b/name-hash.c
@@ -5,9 +5,12 @@
*
* Copyright (C) 2008 Linus Torvalds
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "environment.h"
#include "gettext.h"
+#include "name-hash.h"
+#include "object.h"
+#include "read-cache-ll.h"
#include "thread-utils.h"
#include "trace.h"
#include "trace2.h"
diff --git a/name-hash.h b/name-hash.h
new file mode 100644
index 0000000000..b1b4b0fb33
--- /dev/null
+++ b/name-hash.h
@@ -0,0 +1,16 @@
+#ifndef NAME_HASH_H
+#define NAME_HASH_H
+
+struct cache_entry;
+struct index_state;
+
+int index_dir_exists(struct index_state *istate, const char *name, int namelen);
+void adjust_dirname_case(struct index_state *istate, char *name);
+struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
+
+int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
+void add_name_hash(struct index_state *istate, struct cache_entry *ce);
+void remove_name_hash(struct index_state *istate, struct cache_entry *ce);
+void free_name_hash(struct index_state *istate);
+
+#endif /* NAME_HASH_H */
diff --git a/negotiator/default.c b/negotiator/default.c
index f4b78eb47d..9a5b696327 100644
--- a/negotiator/default.c
+++ b/negotiator/default.c
@@ -4,6 +4,7 @@
#include "../fetch-negotiator.h"
#include "../prio-queue.h"
#include "../refs.h"
+#include "../repository.h"
#include "../tag.h"
/* Remember to update object flag allocation in object.h */
@@ -55,30 +56,49 @@ static int clear_marks(const char *refname, const struct object_id *oid,
static void mark_common(struct negotiation_state *ns, struct commit *commit,
int ancestors_only, int dont_parse)
{
- if (commit != NULL && !(commit->object.flags & COMMON)) {
- struct object *o = (struct object *)commit;
+ struct prio_queue queue = { NULL };
+
+ if (!commit || (commit->object.flags & COMMON))
+ return;
+
+ prio_queue_put(&queue, commit);
+ if (!ancestors_only) {
+ commit->object.flags |= COMMON;
- if (!ancestors_only)
- o->flags |= COMMON;
+ if ((commit->object.flags & SEEN) && !(commit->object.flags & POPPED))
+ ns->non_common_revs--;
+ }
+ while ((commit = prio_queue_get(&queue))) {
+ struct object *o = (struct object *)commit;
if (!(o->flags & SEEN))
rev_list_push(ns, commit, SEEN);
else {
struct commit_list *parents;
- if (!ancestors_only && !(o->flags & POPPED))
- ns->non_common_revs--;
if (!o->parsed && !dont_parse)
if (repo_parse_commit(the_repository, commit))
- return;
+ continue;
for (parents = commit->parents;
parents;
- parents = parents->next)
- mark_common(ns, parents->item, 0,
- dont_parse);
+ parents = parents->next) {
+ struct commit *p = parents->item;
+
+ if (p->object.flags & COMMON)
+ continue;
+
+ p->object.flags |= COMMON;
+
+ if ((p->object.flags & SEEN) && !(p->object.flags & POPPED))
+ ns->non_common_revs--;
+
+ prio_queue_put(&queue, parents->item);
+ }
}
}
+
+ clear_prio_queue(&queue);
}
/*
diff --git a/negotiator/skipping.c b/negotiator/skipping.c
index c7d6ab39bc..5b91520430 100644
--- a/negotiator/skipping.c
+++ b/negotiator/skipping.c
@@ -5,6 +5,7 @@
#include "../hex.h"
#include "../prio-queue.h"
#include "../refs.h"
+#include "../repository.h"
#include "../tag.h"
/* Remember to update object flag allocation in object.h */
@@ -85,29 +86,37 @@ static int clear_marks(const char *refname, const struct object_id *oid,
}
/*
- * Mark this SEEN commit and all its SEEN ancestors as COMMON.
+ * Mark this SEEN commit and all its parsed SEEN ancestors as COMMON.
*/
static void mark_common(struct data *data, struct commit *seen_commit)
{
struct prio_queue queue = { NULL };
struct commit *c;
+ if (seen_commit->object.flags & COMMON)
+ return;
+
prio_queue_put(&queue, seen_commit);
+ seen_commit->object.flags |= COMMON;
while ((c = prio_queue_get(&queue))) {
struct commit_list *p;
- if (c->object.flags & COMMON)
- return;
- c->object.flags |= COMMON;
+
if (!(c->object.flags & POPPED))
data->non_common_revs--;
if (!c->object.parsed)
- return;
+ continue;
for (p = c->parents; p; p = p->next) {
- if (p->item->object.flags & SEEN)
- prio_queue_put(&queue, p->item);
+ if (!(p->item->object.flags & SEEN) ||
+ (p->item->object.flags & COMMON))
+ continue;
+
+ p->item->object.flags |= COMMON;
+ prio_queue_put(&queue, p->item);
}
}
+
+ clear_prio_queue(&queue);
}
/*
diff --git a/notes-cache.c b/notes-cache.c
index fbcdfd0dfe..0e1d5b1ac7 100644
--- a/notes-cache.c
+++ b/notes-cache.c
@@ -1,9 +1,11 @@
#include "git-compat-util.h"
#include "notes-cache.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "pretty.h"
#include "repository.h"
#include "commit.h"
#include "refs.h"
+#include "strbuf.h"
static int notes_cache_match_validity(struct repository *r,
const char *ref,
diff --git a/notes-merge.c b/notes-merge.c
index 233e49e319..071947894e 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -5,13 +5,14 @@
#include "refs.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "path.h"
#include "repository.h"
#include "diff.h"
#include "diffcore.h"
#include "hex.h"
#include "xdiff-interface.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
#include "dir.h"
#include "notes.h"
#include "notes-merge.h"
diff --git a/notes-utils.c b/notes-utils.c
index cb88171b7b..4a793eb347 100644
--- a/notes-utils.c
+++ b/notes-utils.c
@@ -6,6 +6,7 @@
#include "refs.h"
#include "notes-utils.h"
#include "repository.h"
+#include "strbuf.h"
void create_notes_commit(struct repository *r,
struct notes_tree *t,
diff --git a/notes.c b/notes.c
index f51a2d3630..503dbc4be1 100644
--- a/notes.c
+++ b/notes.c
@@ -4,7 +4,7 @@
#include "hex.h"
#include "notes.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "blob.h"
#include "tree.h"
#include "utf8.h"
diff --git a/object-file.c b/object-file.c
index af18e38527..8d87720dd5 100644
--- a/object-file.c
+++ b/object-file.c
@@ -6,7 +6,7 @@
* This handles basic git object files - packing, unpacking,
* creation etc.
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "abspath.h"
#include "alloc.h"
#include "config.h"
@@ -38,6 +38,8 @@
#include "packfile.h"
#include "object-file.h"
#include "object-store.h"
+#include "oidtree.h"
+#include "path.h"
#include "promisor-remote.h"
#include "setup.h"
#include "submodule.h"
diff --git a/object-file.h b/object-file.h
index e0cfc3a5db..d6414610f8 100644
--- a/object-file.h
+++ b/object-file.h
@@ -4,6 +4,8 @@
#include "git-zlib.h"
#include "object.h"
+struct index_state;
+
/*
* Set this to 0 to prevent oid_object_info_extended() from fetching missing
* blobs. This has a difference only if extensions.partialClone is set.
diff --git a/object-name.c b/object-name.c
index 538e8a8f62..0bfa29dbbf 100644
--- a/object-name.c
+++ b/object-name.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "object-name.h"
#include "advice.h"
#include "config.h"
@@ -14,8 +14,11 @@
#include "remote.h"
#include "dir.h"
#include "oid-array.h"
+#include "oidtree.h"
#include "packfile.h"
-#include "object-store.h"
+#include "pretty.h"
+#include "object-store-ll.h"
+#include "read-cache-ll.h"
#include "repository.h"
#include "setup.h"
#include "submodule.h"
@@ -766,6 +769,21 @@ static void find_abbrev_len_packed(struct min_abbrev_data *mad)
find_abbrev_len_for_pack(p, mad);
}
+void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
+ const struct object_id *oid, int abbrev_len)
+{
+ int r;
+ strbuf_grow(sb, GIT_MAX_HEXSZ + 1);
+ r = repo_find_unique_abbrev_r(repo, sb->buf + sb->len, oid, abbrev_len);
+ strbuf_setlen(sb, sb->len + r);
+}
+
+void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
+ int abbrev_len)
+{
+ strbuf_repo_add_unique_abbrev(sb, the_repository, oid, abbrev_len);
+}
+
int repo_find_unique_abbrev_r(struct repository *r, char *hex,
const struct object_id *oid, int len)
{
diff --git a/object-name.h b/object-name.h
index 1d63698f42..9ae5223071 100644
--- a/object-name.h
+++ b/object-name.h
@@ -40,6 +40,15 @@ struct object_context {
const char *repo_find_unique_abbrev(struct repository *r, const struct object_id *oid, int len);
int repo_find_unique_abbrev_r(struct repository *r, char *hex, const struct object_id *oid, int len);
+/**
+ * Add the abbreviation, as generated by repo_find_unique_abbrev(), of `sha1` to
+ * the strbuf `sb`.
+ */
+void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
+ const struct object_id *oid, int abbrev_len);
+void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
+ int abbrev_len);
+
int repo_get_oid(struct repository *r, const char *str, struct object_id *oid);
__attribute__((format (printf, 2, 3)))
int get_oidf(struct object_id *oid, const char *fmt, ...);
diff --git a/object-store-ll.h b/object-store-ll.h
new file mode 100644
index 0000000000..e8f22cdb1b
--- /dev/null
+++ b/object-store-ll.h
@@ -0,0 +1,533 @@
+#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 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;
+
+ /*
+ * 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;
+ uint32_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;
+ /* 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);
+
+/*
+ * 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,
+ 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, 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(each_packed_object_fn, void *,
+ enum for_each_object_flags flags);
+
+#endif /* OBJECT_STORE_LL_H */
diff --git a/object-store.h b/object-store.h
index 4ac91e375d..1b3e3d7d01 100644
--- a/object-store.h
+++ b/object-store.h
@@ -1,537 +1,11 @@
#ifndef OBJECT_STORE_H
#define OBJECT_STORE_H
-#include "object.h"
-#include "oidmap.h"
-#include "list.h"
-#include "oid-array.h"
-#include "strbuf.h"
-#include "thread-utils.h"
#include "khash.h"
#include "dir.h"
-#include "oidtree.h"
-#include "oidset.h"
-
-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;
-
- /*
- * 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;
-};
+#include "object-store-ll.h"
KHASH_INIT(odb_path_map, const char * /* key: odb_path */,
struct object_directory *, 1, fspathhash, fspatheq)
-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;
- uint32_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;
- /* 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;
- kh_odb_path_map_t *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);
-
-/*
- * 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,
- 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, 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(each_packed_object_fn, void *,
- enum for_each_object_flags flags);
-
#endif /* OBJECT_STORE_H */
diff --git a/object.c b/object.c
index 6d4ef1524d..60f19e4b43 100644
--- a/object.c
+++ b/object.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "gettext.h"
#include "hex.h"
#include "object.h"
@@ -6,6 +6,7 @@
#include "object-file.h"
#include "object-store.h"
#include "blob.h"
+#include "statinfo.h"
#include "tree.h"
#include "commit.h"
#include "tag.h"
@@ -180,6 +181,18 @@ void *object_as_type(struct object *obj, enum object_type type, int quiet)
}
}
+void *object_as_type_hint(struct object *obj, enum object_type type,
+ enum object_type hint)
+{
+ if (hint != OBJ_NONE && obj->type != OBJ_NONE && obj->type != type) {
+ error(_("object %s is a %s, not a %s"), oid_to_hex(&obj->oid),
+ type_name(type), type_name(obj->type));
+ obj->type = type;
+ return NULL;
+ }
+ return object_as_type(obj, type, 0);;
+}
+
struct object *lookup_unknown_object(struct repository *r, const struct object_id *oid)
{
struct object *obj = lookup_object(r, oid);
@@ -213,13 +226,13 @@ struct object *parse_object_buffer(struct repository *r, const struct object_id
obj = NULL;
if (type == OBJ_BLOB) {
- struct blob *blob = lookup_blob(r, oid);
+ struct blob *blob = lookup_blob_type(r, oid, type);
if (blob) {
parse_blob_buffer(blob);
obj = &blob->object;
}
} else if (type == OBJ_TREE) {
- struct tree *tree = lookup_tree(r, oid);
+ struct tree *tree = lookup_tree_type(r, oid, type);
if (tree) {
obj = &tree->object;
if (!tree->buffer)
@@ -231,7 +244,7 @@ struct object *parse_object_buffer(struct repository *r, const struct object_id
}
}
} else if (type == OBJ_COMMIT) {
- struct commit *commit = lookup_commit(r, oid);
+ struct commit *commit = lookup_commit_type(r, oid, type);
if (commit) {
if (parse_commit_buffer(r, commit, buffer, size, 1))
return NULL;
@@ -243,7 +256,7 @@ struct object *parse_object_buffer(struct repository *r, const struct object_id
obj = &commit->object;
}
} else if (type == OBJ_TAG) {
- struct tag *tag = lookup_tag(r, oid);
+ struct tag *tag = lookup_tag_type(r, oid, type);
if (tag) {
if (parse_tag_buffer(r, tag, buffer, size))
return NULL;
@@ -356,6 +369,12 @@ void object_list_free(struct object_list **list)
*/
static char object_array_slopbuf[1];
+void object_array_init(struct object_array *array)
+{
+ struct object_array blank = OBJECT_ARRAY_INIT;
+ memcpy(array, &blank, sizeof(*array));
+}
+
void add_object_array_with_path(struct object *obj, const char *name,
struct object_array *array,
unsigned mode, const char *path)
diff --git a/object.h b/object.h
index 96e52e24fb..e7432f79dc 100644
--- a/object.h
+++ b/object.h
@@ -1,9 +1,10 @@
#ifndef OBJECT_H
#define OBJECT_H
-#include "hash.h"
+#include "hash-ll.h"
struct buffer_slab;
+struct repository;
struct parsed_object_pool {
struct object **obj_hash;
@@ -57,6 +58,8 @@ struct object_array {
#define OBJECT_ARRAY_INIT { 0 }
+void object_array_init(struct object_array *array);
+
/*
* object flag allocation:
* revision.h: 0---------10 15 23------27
@@ -186,6 +189,8 @@ struct object *lookup_object(struct repository *r, const struct object_id *oid);
void *create_object(struct repository *r, const struct object_id *oid, void *obj);
void *object_as_type(struct object *obj, enum object_type type, int quiet);
+void *object_as_type_hint(struct object *obj, enum object_type type,
+ enum object_type hint);
/*
* Returns the object, having parsed it to find out what it is.
diff --git a/oidmap.c b/oidmap.c
index 8c1a139c97..8b1bc4dec9 100644
--- a/oidmap.c
+++ b/oidmap.c
@@ -1,4 +1,5 @@
#include "git-compat-util.h"
+#include "hash.h"
#include "oidmap.h"
static int oidmap_neq(const void *hashmap_cmp_fn_data UNUSED,
diff --git a/oidmap.h b/oidmap.h
index c1642927fa..05c673eb7c 100644
--- a/oidmap.h
+++ b/oidmap.h
@@ -1,6 +1,7 @@
#ifndef OIDMAP_H
#define OIDMAP_H
+#include "hash-ll.h"
#include "hashmap.h"
/*
diff --git a/oidtree.h b/oidtree.h
index 77898f510a..55c83513fd 100644
--- a/oidtree.h
+++ b/oidtree.h
@@ -2,7 +2,7 @@
#define OIDTREE_H
#include "cbtree.h"
-#include "hash.h"
+#include "hash-ll.h"
#include "mem-pool.h"
struct oidtree {
diff --git a/oss-fuzz/fuzz-pack-idx.c b/oss-fuzz/fuzz-pack-idx.c
index 609a343ee3..3e190214d1 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.h"
+#include "object-store-ll.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 faf67c94d3..d86f4e739a 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -3,7 +3,7 @@
#include "environment.h"
#include "gettext.h"
#include "hex.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "commit.h"
#include "tag.h"
#include "diff.h"
@@ -15,9 +15,12 @@
#include "pack-bitmap.h"
#include "hash-lookup.h"
#include "pack-objects.h"
+#include "path.h"
#include "commit-reach.h"
#include "prio-queue.h"
#include "trace2.h"
+#include "tree.h"
+#include "tree-walk.h"
struct bitmapped_commit {
struct commit *commit;
diff --git a/pack-bitmap.c b/pack-bitmap.c
index e0fad723bf..07dda2b22e 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -17,7 +17,7 @@
#include "repository.h"
#include "trace2.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "list-objects-filter-options.h"
#include "midx.h"
#include "config.h"
@@ -137,6 +137,43 @@ static struct ewah_bitmap *lookup_stored_bitmap(struct stored_bitmap *st)
return composed;
}
+static size_t bitmap_index_seek(struct bitmap_index *bitmap_git, size_t offset,
+ int whence)
+{
+ switch (whence) {
+ case SEEK_SET:
+ bitmap_git->map_pos = offset;
+ break;
+ case SEEK_CUR:
+ bitmap_git->map_pos = st_add(bitmap_git->map_pos, offset);
+ break;
+ default:
+ BUG("unhandled seek whence: %d", whence);
+ }
+
+ if (bitmap_git->map_pos >= bitmap_git->map_size)
+ BUG("bitmap position exceeds size (%"PRIuMAX" >= %"PRIuMAX")",
+ (uintmax_t)bitmap_git->map_pos,
+ (uintmax_t)bitmap_git->map_size);
+
+ return bitmap_git->map_pos;
+}
+
+static int bitmap_index_seek_commit(struct bitmap_index *bitmap_git,
+ struct object_id *oid,
+ size_t pos)
+{
+ const int bitmap_header_size = 6;
+
+ bitmap_index_seek(bitmap_git, pos, SEEK_SET);
+
+ if (bitmap_git->map_size - bitmap_git->map_pos < bitmap_header_size)
+ return error(_("corrupt ewah bitmap: truncated header for "
+ "bitmap of commit \"%s\""),
+ oid_to_hex(oid));
+ return 0;
+}
+
/*
* Read a bitmap from the current read position on the mmaped
* index, and increase the read position accordingly
@@ -155,7 +192,7 @@ static struct ewah_bitmap *read_bitmap_1(struct bitmap_index *index)
return NULL;
}
- index->map_pos += bitmap_size;
+ bitmap_index_seek(index, bitmap_size, SEEK_CUR);
return b;
}
@@ -211,7 +248,7 @@ static int load_bitmap_header(struct bitmap_index *index)
index->entry_count = ntohl(header->entry_count);
index->checksum = header->checksum;
- index->map_pos += header_size;
+ bitmap_index_seek(index, header_size, SEEK_CUR);
return 0;
}
@@ -247,16 +284,18 @@ static struct stored_bitmap *store_bitmap(struct bitmap_index *index,
return stored;
}
-static inline uint32_t read_be32(const unsigned char *buffer, size_t *pos)
+static uint32_t read_be32(struct bitmap_index *bitmap_git)
{
- uint32_t result = get_be32(buffer + *pos);
- (*pos) += sizeof(result);
+ uint32_t result = get_be32(bitmap_git->map + bitmap_git->map_pos);
+ bitmap_index_seek(bitmap_git, sizeof(uint32_t), SEEK_CUR);
return result;
}
-static inline uint8_t read_u8(const unsigned char *buffer, size_t *pos)
+static uint8_t read_u8(struct bitmap_index *bitmap_git)
{
- return buffer[(*pos)++];
+ uint8_t result = bitmap_git->map[bitmap_git->map_pos];
+ bitmap_index_seek(bitmap_git, sizeof(uint8_t), SEEK_CUR);
+ return result;
}
#define MAX_XOR_OFFSET 160
@@ -285,9 +324,9 @@ static int load_bitmap_entries_v1(struct bitmap_index *index)
if (index->map_size - index->map_pos < 6)
return error(_("corrupt ewah bitmap: truncated header for entry %d"), i);
- commit_idx_pos = read_be32(index->map, &index->map_pos);
- xor_offset = read_u8(index->map, &index->map_pos);
- flags = read_u8(index->map, &index->map_pos);
+ commit_idx_pos = read_be32(index);
+ xor_offset = read_u8(index);
+ flags = read_u8(index);
if (nth_bitmap_object_oid(index, &oid, commit_idx_pos) < 0)
return error(_("corrupt ewah bitmap: commit index %u out of range"),
@@ -717,7 +756,6 @@ static struct stored_bitmap *lazy_bitmap_for_commit(struct bitmap_index *bitmap_
struct object_id *oid = &commit->object.oid;
struct ewah_bitmap *bitmap;
struct stored_bitmap *xor_bitmap = NULL;
- const int bitmap_header_size = 6;
static struct bitmap_lookup_table_xor_item *xor_items = NULL;
static size_t xor_items_nr = 0, xor_items_alloc = 0;
static int is_corrupt = 0;
@@ -776,15 +814,14 @@ static struct stored_bitmap *lazy_bitmap_for_commit(struct bitmap_index *bitmap_
while (xor_items_nr) {
xor_item = &xor_items[xor_items_nr - 1];
- bitmap_git->map_pos = xor_item->offset;
- if (bitmap_git->map_size - bitmap_git->map_pos < bitmap_header_size) {
- error(_("corrupt ewah bitmap: truncated header for bitmap of commit \"%s\""),
- oid_to_hex(&xor_item->oid));
+
+ if (bitmap_index_seek_commit(bitmap_git, &xor_item->oid,
+ xor_item->offset) < 0)
goto corrupt;
- }
- bitmap_git->map_pos += sizeof(uint32_t) + sizeof(uint8_t);
- xor_flags = read_u8(bitmap_git->map, &bitmap_git->map_pos);
+ bitmap_index_seek(bitmap_git,
+ sizeof(uint32_t) + sizeof(uint8_t), SEEK_CUR);
+ xor_flags = read_u8(bitmap_git);
bitmap = read_bitmap_1(bitmap_git);
if (!bitmap)
@@ -794,12 +831,8 @@ static struct stored_bitmap *lazy_bitmap_for_commit(struct bitmap_index *bitmap_
xor_items_nr--;
}
- bitmap_git->map_pos = offset;
- if (bitmap_git->map_size - bitmap_git->map_pos < bitmap_header_size) {
- error(_("corrupt ewah bitmap: truncated header for bitmap of commit \"%s\""),
- oid_to_hex(oid));
+ if (bitmap_index_seek_commit(bitmap_git, oid, offset) < 0)
goto corrupt;
- }
/*
* Don't bother reading the commit's index position or its xor
@@ -824,8 +857,9 @@ static struct stored_bitmap *lazy_bitmap_for_commit(struct bitmap_index *bitmap_
* Instead, we can skip ahead and immediately read the flags and
* ewah bitmap.
*/
- bitmap_git->map_pos += sizeof(uint32_t) + sizeof(uint8_t);
- flags = read_u8(bitmap_git->map, &bitmap_git->map_pos);
+ bitmap_index_seek(bitmap_git, sizeof(uint32_t) + sizeof(uint8_t),
+ SEEK_CUR);
+ flags = read_u8(bitmap_git);
bitmap = read_bitmap_1(bitmap_git);
if (!bitmap)
@@ -1043,6 +1077,160 @@ static int add_commit_to_bitmap(struct bitmap_index *bitmap_git,
return 1;
}
+static struct bitmap *fill_in_bitmap(struct bitmap_index *bitmap_git,
+ struct rev_info *revs,
+ struct bitmap *base,
+ struct bitmap *seen)
+{
+ struct include_data incdata;
+ struct bitmap_show_data show_data;
+
+ if (!base)
+ base = bitmap_new();
+
+ incdata.bitmap_git = bitmap_git;
+ incdata.base = base;
+ incdata.seen = seen;
+
+ revs->include_check = should_include;
+ revs->include_check_obj = should_include_obj;
+ revs->include_check_data = &incdata;
+
+ if (prepare_revision_walk(revs))
+ die(_("revision walk setup failed"));
+
+ show_data.bitmap_git = bitmap_git;
+ show_data.base = base;
+
+ traverse_commit_list(revs, show_commit, show_object, &show_data);
+
+ revs->include_check = NULL;
+ revs->include_check_obj = NULL;
+ revs->include_check_data = NULL;
+
+ return base;
+}
+
+struct bitmap_boundary_cb {
+ struct bitmap_index *bitmap_git;
+ struct bitmap *base;
+
+ struct object_array boundary;
+};
+
+static void show_boundary_commit(struct commit *commit, void *_data)
+{
+ struct bitmap_boundary_cb *data = _data;
+
+ if (commit->object.flags & BOUNDARY)
+ add_object_array(&commit->object, "", &data->boundary);
+
+ if (commit->object.flags & UNINTERESTING) {
+ if (bitmap_walk_contains(data->bitmap_git, data->base,
+ &commit->object.oid))
+ return;
+
+ add_commit_to_bitmap(data->bitmap_git, &data->base, commit);
+ }
+}
+
+static void show_boundary_object(struct object *object,
+ const char *name, void *data)
+{
+ BUG("should not be called");
+}
+
+static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git,
+ struct rev_info *revs,
+ struct object_list *roots)
+{
+ struct bitmap_boundary_cb cb;
+ struct object_list *root;
+ unsigned int i;
+ unsigned int tmp_blobs, tmp_trees, tmp_tags;
+ int any_missing = 0;
+
+ cb.bitmap_git = bitmap_git;
+ cb.base = bitmap_new();
+ object_array_init(&cb.boundary);
+
+ revs->ignore_missing_links = 1;
+
+ /*
+ * OR in any existing reachability bitmaps among `roots` into
+ * `cb.base`.
+ */
+ for (root = roots; root; root = root->next) {
+ struct object *object = root->item;
+ if (object->type != OBJ_COMMIT ||
+ bitmap_walk_contains(bitmap_git, cb.base, &object->oid))
+ continue;
+
+ if (add_commit_to_bitmap(bitmap_git, &cb.base,
+ (struct commit *)object))
+ continue;
+
+ any_missing = 1;
+ }
+
+ if (!any_missing)
+ goto cleanup;
+
+ tmp_blobs = revs->blob_objects;
+ tmp_trees = revs->tree_objects;
+ tmp_tags = revs->blob_objects;
+ revs->blob_objects = 0;
+ revs->tree_objects = 0;
+ revs->tag_objects = 0;
+
+ /*
+ * We didn't have complete coverage of the roots. First setup a
+ * revision walk to (a) OR in any bitmaps that are UNINTERESTING
+ * between the tips and boundary, and (b) record the boundary.
+ */
+ trace2_region_enter("pack-bitmap", "boundary-prepare", the_repository);
+ if (prepare_revision_walk(revs))
+ die("revision walk setup failed");
+ trace2_region_leave("pack-bitmap", "boundary-prepare", the_repository);
+
+ trace2_region_enter("pack-bitmap", "boundary-traverse", the_repository);
+ revs->boundary = 1;
+ traverse_commit_list_filtered(revs,
+ show_boundary_commit,
+ show_boundary_object,
+ &cb, NULL);
+ revs->boundary = 0;
+ trace2_region_leave("pack-bitmap", "boundary-traverse", the_repository);
+
+ revs->blob_objects = tmp_blobs;
+ revs->tree_objects = tmp_trees;
+ revs->tag_objects = tmp_tags;
+
+ reset_revision_walk();
+ clear_object_flags(UNINTERESTING);
+
+ /*
+ * Then add the boundary commit(s) as fill-in traversal tips.
+ */
+ trace2_region_enter("pack-bitmap", "boundary-fill-in", the_repository);
+ for (i = 0; i < cb.boundary.nr; i++) {
+ struct object *obj = cb.boundary.objects[i].item;
+ if (bitmap_walk_contains(bitmap_git, cb.base, &obj->oid))
+ obj->flags |= SEEN;
+ else
+ add_pending_object(revs, obj, "");
+ }
+ if (revs->pending.nr)
+ cb.base = fill_in_bitmap(bitmap_git, revs, cb.base, NULL);
+ trace2_region_leave("pack-bitmap", "boundary-fill-in", the_repository);
+
+cleanup:
+ object_array_clear(&cb.boundary);
+ revs->ignore_missing_links = 0;
+
+ return cb.base;
+}
+
static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
struct rev_info *revs,
struct object_list *roots,
@@ -1109,33 +1297,19 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
}
if (needs_walk) {
- struct include_data incdata;
- struct bitmap_show_data show_data;
-
- if (!base)
- base = bitmap_new();
-
- incdata.bitmap_git = bitmap_git;
- incdata.base = base;
- incdata.seen = seen;
-
- revs->include_check = should_include;
- revs->include_check_obj = should_include_obj;
- revs->include_check_data = &incdata;
-
- if (prepare_revision_walk(revs))
- die(_("revision walk setup failed"));
-
- show_data.bitmap_git = bitmap_git;
- show_data.base = base;
-
- traverse_commit_list(revs,
- show_commit, show_object,
- &show_data);
-
- revs->include_check = NULL;
- revs->include_check_obj = NULL;
- revs->include_check_data = NULL;
+ /*
+ * This fill-in traversal may walk over some objects
+ * again, since we have already traversed in order to
+ * find the boundary.
+ *
+ * But this extra walk should be extremely cheap, since
+ * all commit objects are loaded into memory, and
+ * because we skip walking to parents that are
+ * UNINTERESTING, since it will be marked in the haves
+ * bitmap already (or it has an on-disk bitmap, since
+ * OR-ing it in covers all of its ancestors).
+ */
+ base = fill_in_bitmap(bitmap_git, revs, base, seen);
}
return base;
@@ -1528,6 +1702,7 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
int filter_provided_objects)
{
unsigned int i;
+ int use_boundary_traversal;
struct object_list *wants = NULL;
struct object_list *haves = NULL;
@@ -1578,13 +1753,21 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
object_list_insert(object, &wants);
}
- /*
- * if we have a HAVES list, but none of those haves is contained
- * in the packfile that has a bitmap, we don't have anything to
- * optimize here
- */
- if (haves && !in_bitmapped_pack(bitmap_git, haves))
- goto cleanup;
+ use_boundary_traversal = git_env_bool(GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL, -1);
+ if (use_boundary_traversal < 0) {
+ prepare_repo_settings(revs->repo);
+ use_boundary_traversal = revs->repo->settings.pack_use_bitmap_boundary_traversal;
+ }
+
+ if (!use_boundary_traversal) {
+ /*
+ * if we have a HAVES list, but none of those haves is contained
+ * in the packfile that has a bitmap, we don't have anything to
+ * optimize here
+ */
+ if (haves && !in_bitmapped_pack(bitmap_git, haves))
+ goto cleanup;
+ }
/* if we don't want anything, we're done here */
if (!wants)
@@ -1598,18 +1781,32 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
if (load_bitmap(revs->repo, bitmap_git) < 0)
goto cleanup;
- object_array_clear(&revs->pending);
+ if (!use_boundary_traversal)
+ object_array_clear(&revs->pending);
if (haves) {
- revs->ignore_missing_links = 1;
- haves_bitmap = find_objects(bitmap_git, revs, haves, NULL);
- reset_revision_walk();
- revs->ignore_missing_links = 0;
+ if (use_boundary_traversal) {
+ trace2_region_enter("pack-bitmap", "haves/boundary", the_repository);
+ haves_bitmap = find_boundary_objects(bitmap_git, revs, haves);
+ trace2_region_leave("pack-bitmap", "haves/boundary", the_repository);
+ } else {
+ trace2_region_enter("pack-bitmap", "haves/classic", the_repository);
+ revs->ignore_missing_links = 1;
+ haves_bitmap = find_objects(bitmap_git, revs, haves, NULL);
+ reset_revision_walk();
+ revs->ignore_missing_links = 0;
+ trace2_region_leave("pack-bitmap", "haves/classic", the_repository);
+ }
if (!haves_bitmap)
BUG("failed to perform bitmap walk");
}
+ if (use_boundary_traversal) {
+ object_array_clear(&revs->pending);
+ reset_revision_walk();
+ }
+
wants_bitmap = find_objects(bitmap_git, revs, wants, haves_bitmap);
if (!wants_bitmap)
@@ -2346,3 +2543,48 @@ int bitmap_is_preferred_refname(struct repository *r, const char *refname)
return 0;
}
+
+static int verify_bitmap_file(const char *name)
+{
+ struct stat st;
+ unsigned char *data;
+ int fd = git_open(name);
+ int res = 0;
+
+ /* It is OK to not have the file. */
+ if (fd < 0 || fstat(fd, &st)) {
+ if (fd >= 0)
+ close(fd);
+ return 0;
+ }
+
+ data = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ close(fd);
+ if (!hashfile_checksum_valid(data, st.st_size))
+ res = error(_("bitmap file '%s' has invalid checksum"),
+ name);
+
+ munmap(data, st.st_size);
+ return res;
+}
+
+int verify_bitmap_files(struct repository *r)
+{
+ int res = 0;
+
+ 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);
+ 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);
+ free(pack_bitmap_name);
+ }
+
+ return res;
+}
diff --git a/pack-bitmap.h b/pack-bitmap.h
index f0180b5276..5273a6a019 100644
--- a/pack-bitmap.h
+++ b/pack-bitmap.h
@@ -62,6 +62,10 @@ void traverse_bitmap_commit_list(struct bitmap_index *,
void test_bitmap_walk(struct rev_info *revs);
int test_bitmap_commits(struct repository *r);
int test_bitmap_hashes(struct repository *r);
+
+#define GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL \
+ "GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL"
+
struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
int filter_provided_objects);
uint32_t midx_preferred_pack(struct bitmap_index *bitmap_git);
@@ -111,4 +115,6 @@ int bitmap_is_midx(struct bitmap_index *bitmap_git);
const struct string_list *bitmap_preferred_tips(struct repository *r);
int bitmap_is_preferred_refname(struct repository *r, const char *refname);
+int verify_bitmap_files(struct repository *r);
+
#endif
diff --git a/pack-check.c b/pack-check.c
index 049f2f0bfc..977f619618 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -7,7 +7,7 @@
#include "progress.h"
#include "packfile.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
struct idx_entry {
off_t offset;
diff --git a/pack-mtimes.c b/pack-mtimes.c
index 020a37f8fe..cdf30b8d2b 100644
--- a/pack-mtimes.c
+++ b/pack-mtimes.c
@@ -2,8 +2,9 @@
#include "gettext.h"
#include "pack-mtimes.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "packfile.h"
+#include "strbuf.h"
static char *pack_mtimes_filename(struct packed_git *p)
{
diff --git a/pack-objects.h b/pack-objects.h
index 579476687c..0d78db40cb 100644
--- a/pack-objects.h
+++ b/pack-objects.h
@@ -1,7 +1,7 @@
#ifndef PACK_OBJECTS_H
#define PACK_OBJECTS_H
-#include "object-store.h"
+#include "object-store-ll.h"
#include "thread-utils.h"
#include "pack.h"
diff --git a/pack-revindex.c b/pack-revindex.c
index 1f51b712e8..7fffcad912 100644
--- a/pack-revindex.c
+++ b/pack-revindex.c
@@ -2,8 +2,9 @@
#include "gettext.h"
#include "pack-revindex.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "packfile.h"
+#include "strbuf.h"
#include "trace2.h"
#include "config.h"
#include "midx.h"
diff --git a/pack-write.c b/pack-write.c
index 3b3ce89de6..af48813a9b 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -10,6 +10,8 @@
#include "oidmap.h"
#include "pack-objects.h"
#include "pack-revindex.h"
+#include "path.h"
+#include "strbuf.h"
#include "wrapper.h"
void reset_pack_idx_option(struct pack_idx_option *opts)
diff --git a/packfile.c b/packfile.c
index 57df0c1956..c60aeb05ec 100644
--- a/packfile.c
+++ b/packfile.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "alloc.h"
#include "environment.h"
#include "gettext.h"
@@ -19,7 +19,7 @@
#include "tree-walk.h"
#include "tree.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "midx.h"
#include "commit-graph.h"
#include "pack-revindex.h"
diff --git a/packfile.h b/packfile.h
index 665603b696..c3692308b8 100644
--- a/packfile.h
+++ b/packfile.h
@@ -6,10 +6,22 @@
/* in object-store.h */
struct packed_git;
-struct pack_entry;
-struct pack_window;
struct object_info;
+struct pack_window {
+ struct pack_window *next;
+ unsigned char *base;
+ off_t offset;
+ size_t len;
+ unsigned int last_used;
+ unsigned int inuse_cnt;
+};
+
+struct pack_entry {
+ off_t offset;
+ struct packed_git *p;
+};
+
/*
* Generate the filename to be used for a pack file with checksum "sha1" and
* extension "ext". The result is written into the strbuf "buf", overwriting
diff --git a/parallel-checkout.c b/parallel-checkout.c
index 50fd7fe31e..602fbf19d3 100644
--- a/parallel-checkout.c
+++ b/parallel-checkout.c
@@ -1,15 +1,18 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "alloc.h"
#include "config.h"
#include "entry.h"
#include "gettext.h"
+#include "hash.h"
#include "hex.h"
#include "parallel-checkout.h"
#include "pkt-line.h"
#include "progress.h"
+#include "read-cache-ll.h"
#include "run-command.h"
#include "sigchain.h"
#include "streaming.h"
+#include "symlinks.h"
#include "thread-utils.h"
#include "trace2.h"
#include "wrapper.h"
diff --git a/parse-options-cb.c b/parse-options-cb.c
index 298decf48f..a24521dee0 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -3,6 +3,7 @@
#include "branch.h"
#include "commit.h"
#include "color.h"
+#include "date.h"
#include "environment.h"
#include "gettext.h"
#include "object-name.h"
diff --git a/parse-options.c b/parse-options.c
index b6803647d0..f8a155ee13 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -5,6 +5,7 @@
#include "commit.h"
#include "color.h"
#include "gettext.h"
+#include "strbuf.h"
#include "utf8.h"
static int disallow_abbreviated_options;
diff --git a/patch-ids.c b/patch-ids.c
index 19af7bee98..c3e1a0dd21 100644
--- a/patch-ids.c
+++ b/patch-ids.c
@@ -1,6 +1,7 @@
#include "git-compat-util.h"
#include "diff.h"
#include "commit.h"
+#include "hash.h"
#include "hash-lookup.h"
#include "hex.h"
#include "patch-ids.h"
diff --git a/path.c b/path.c
index 7c1cd8182a..044a50bad0 100644
--- a/path.c
+++ b/path.c
@@ -15,7 +15,7 @@
#include "submodule-config.h"
#include "path.h"
#include "packfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "lockfile.h"
#include "exec-cmd.h"
#include "wrapper.h"
@@ -1213,6 +1213,26 @@ int normalize_path_copy(char *dst, const char *src)
return normalize_path_copy_len(dst, src, NULL);
}
+int strbuf_normalize_path(struct strbuf *src)
+{
+ struct strbuf dst = STRBUF_INIT;
+
+ strbuf_grow(&dst, src->len);
+ if (normalize_path_copy(dst.buf, src->buf) < 0) {
+ strbuf_release(&dst);
+ return -1;
+ }
+
+ /*
+ * normalize_path does not tell us the new length, so we have to
+ * compute it by looking for the new NUL it placed
+ */
+ strbuf_setlen(&dst, strlen(dst.buf));
+ strbuf_swap(src, &dst);
+ strbuf_release(&dst);
+ return 0;
+}
+
/*
* path = Canonical absolute path
* prefixes = string_list containing normalized, absolute paths without
diff --git a/path.h b/path.h
index 60e83a49a9..639372edd9 100644
--- a/path.h
+++ b/path.h
@@ -191,6 +191,11 @@ const char *remove_leading_path(const char *in, const char *prefix);
const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
int normalize_path_copy(char *dst, const char *src);
+/**
+ * Normalize in-place the path contained in the strbuf. If an error occurs,
+ * the contents of "sb" are left untouched, and -1 is returned.
+ */
+int strbuf_normalize_path(struct strbuf *src);
int longest_ancestor_length(const char *path, struct string_list *prefixes);
char *strip_path_suffix(const char *path, const char *suffix);
int daemon_avoid_alias(const char *path);
diff --git a/pathspec.c b/pathspec.c
index 6972d515f0..4991455281 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "abspath.h"
#include "config.h"
#include "dir.h"
@@ -6,9 +6,13 @@
#include "gettext.h"
#include "pathspec.h"
#include "attr.h"
+#include "read-cache.h"
+#include "repository.h"
#include "setup.h"
#include "strvec.h"
+#include "symlinks.h"
#include "quote.h"
+#include "wildmatch.h"
/*
* Finds which of the given pathspecs match items in the index.
@@ -734,7 +738,7 @@ int match_pathspec_attrs(struct index_state *istate,
if (name[namelen])
name = to_free = xmemdupz(name, namelen);
- git_check_attr(istate, NULL, name, item->attr_check);
+ git_check_attr(istate, name, item->attr_check);
free(to_free);
diff --git a/pkt-line.c b/pkt-line.c
index 3561d85358..62b4208b66 100644
--- a/pkt-line.c
+++ b/pkt-line.c
@@ -1,4 +1,5 @@
-#include "cache.h"
+#include "git-compat-util.h"
+#include "copy.h"
#include "pkt-line.h"
#include "gettext.h"
#include "hex.h"
diff --git a/pkt-line.h b/pkt-line.h
index 8e9846f315..7c23a4bfaf 100644
--- a/pkt-line.h
+++ b/pkt-line.h
@@ -246,4 +246,6 @@ void packet_writer_error(struct packet_writer *writer, const char *fmt, ...);
void packet_writer_delim(struct packet_writer *writer);
void packet_writer_flush(struct packet_writer *writer);
+void packet_trace_identity(const char *prog);
+
#endif
diff --git a/preload-index.c b/preload-index.c
index 4abf9c983b..e44530c80c 100644
--- a/preload-index.c
+++ b/preload-index.c
@@ -1,16 +1,19 @@
/*
* Copyright (C) 2008 Linus Torvalds
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "pathspec.h"
#include "dir.h"
#include "environment.h"
#include "fsmonitor.h"
#include "gettext.h"
#include "config.h"
+#include "preload-index.h"
#include "progress.h"
+#include "read-cache.h"
#include "thread-utils.h"
#include "repository.h"
+#include "symlinks.h"
#include "trace2.h"
/*
diff --git a/preload-index.h b/preload-index.h
new file mode 100644
index 0000000000..251b1ed88e
--- /dev/null
+++ b/preload-index.h
@@ -0,0 +1,15 @@
+#ifndef PRELOAD_INDEX_H
+#define PRELOAD_INDEX_H
+
+struct index_state;
+struct pathspec;
+struct repository;
+
+void preload_index(struct index_state *index,
+ const struct pathspec *pathspec,
+ unsigned int refresh_flags);
+int repo_read_index_preload(struct repository *,
+ const struct pathspec *pathspec,
+ unsigned refresh_flags);
+
+#endif /* PRELOAD_INDEX_H */
diff --git a/pretty.c b/pretty.c
index 0bb938021b..d4bc4b0286 100644
--- a/pretty.c
+++ b/pretty.c
@@ -4,6 +4,7 @@
#include "commit.h"
#include "environment.h"
#include "gettext.h"
+#include "hash.h"
#include "hex.h"
#include "utf8.h"
#include "diff.h"
@@ -18,6 +19,7 @@
#include "gpg-interface.h"
#include "trailer.h"
#include "run-command.h"
+#include "object-name.h"
/*
* The limit for formatting directives, which enable the caller to append
diff --git a/progress.c b/progress.c
index 72d5e0c73c..f695798aca 100644
--- a/progress.c
+++ b/progress.c
@@ -12,6 +12,7 @@
#include "git-compat-util.h"
#include "pager.h"
#include "progress.h"
+#include "repository.h"
#include "strbuf.h"
#include "trace.h"
#include "trace2.h"
diff --git a/promisor-remote.c b/promisor-remote.c
index 1adcd6fb0a..db88d2064d 100644
--- a/promisor-remote.c
+++ b/promisor-remote.c
@@ -1,7 +1,7 @@
#include "git-compat-util.h"
#include "gettext.h"
#include "hex.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "promisor-remote.h"
#include "config.h"
#include "trace2.h"
diff --git a/protocol-caps.c b/protocol-caps.c
index 94c51862c5..808a68c974 100644
--- a/protocol-caps.c
+++ b/protocol-caps.c
@@ -4,10 +4,10 @@
#include "hex.h"
#include "pkt-line.h"
#include "strvec.h"
-#include "hash.h"
+#include "hash-ll.h"
#include "hex.h"
#include "object.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "string-list.h"
#include "strbuf.h"
diff --git a/prune-packed.c b/prune-packed.c
index 58412b4fb9..e54daf740a 100644
--- a/prune-packed.c
+++ b/prune-packed.c
@@ -1,7 +1,7 @@
#include "git-compat-util.h"
#include "environment.h"
#include "gettext.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "packfile.h"
#include "progress.h"
#include "prune-packed.h"
diff --git a/range-diff.c b/range-diff.c
index a1e0cffb9f..2e86063491 100644
--- a/range-diff.c
+++ b/range-diff.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "environment.h"
#include "gettext.h"
#include "range-diff.h"
@@ -13,6 +13,7 @@
#include "commit.h"
#include "pager.h"
#include "pretty.h"
+#include "repository.h"
#include "userdiff.h"
#include "apply.h"
#include "revision.h"
diff --git a/reachable.c b/reachable.c
index 55bb114353..0136c4b78b 100644
--- a/reachable.c
+++ b/reachable.c
@@ -13,9 +13,11 @@
#include "list-objects.h"
#include "packfile.h"
#include "worktree.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "pack-bitmap.h"
#include "pack-mtimes.h"
+#include "config.h"
+#include "run-command.h"
struct connectivity_progress {
struct progress *progress;
@@ -67,8 +69,77 @@ struct recent_data {
timestamp_t timestamp;
report_recent_object_fn *cb;
int ignore_in_core_kept_packs;
+
+ struct oidset extra_recent_oids;
+ int extra_recent_oids_loaded;
};
+static int run_one_gc_recent_objects_hook(struct oidset *set,
+ const char *args)
+{
+ struct child_process cmd = CHILD_PROCESS_INIT;
+ struct strbuf buf = STRBUF_INIT;
+ FILE *out;
+ int ret = 0;
+
+ cmd.use_shell = 1;
+ cmd.out = -1;
+
+ strvec_push(&cmd.args, args);
+
+ if (start_command(&cmd))
+ return -1;
+
+ out = xfdopen(cmd.out, "r");
+ while (strbuf_getline(&buf, out) != EOF) {
+ struct object_id oid;
+ const char *rest;
+
+ if (parse_oid_hex(buf.buf, &oid, &rest) || *rest) {
+ ret = error(_("invalid extra cruft tip: '%s'"), buf.buf);
+ break;
+ }
+
+ oidset_insert(set, &oid);
+ }
+
+ fclose(out);
+ ret |= finish_command(&cmd);
+
+ strbuf_release(&buf);
+ return ret;
+}
+
+static void load_gc_recent_objects(struct recent_data *data)
+{
+ const struct string_list *programs;
+ int ret = 0;
+ size_t i;
+
+ data->extra_recent_oids_loaded = 1;
+
+ if (git_config_get_string_multi("gc.recentobjectshook", &programs))
+ return;
+
+ for (i = 0; i < programs->nr; i++) {
+ ret = run_one_gc_recent_objects_hook(&data->extra_recent_oids,
+ programs->items[i].string);
+ if (ret)
+ die(_("unable to enumerate additional cruft tips"));
+ }
+}
+
+static int obj_is_recent(const struct object_id *oid, timestamp_t mtime,
+ struct recent_data *data)
+{
+ if (mtime > data->timestamp)
+ return 1;
+
+ if (!data->extra_recent_oids_loaded)
+ load_gc_recent_objects(data);
+ return oidset_contains(&data->extra_recent_oids, oid);
+}
+
static void add_recent_object(const struct object_id *oid,
struct packed_git *pack,
off_t offset,
@@ -78,7 +149,7 @@ static void add_recent_object(const struct object_id *oid,
struct object *obj;
enum object_type type;
- if (mtime <= data->timestamp)
+ if (!obj_is_recent(oid, mtime, data))
return;
/*
@@ -193,16 +264,24 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
data.cb = cb;
data.ignore_in_core_kept_packs = ignore_in_core_kept_packs;
+ oidset_init(&data.extra_recent_oids, 0);
+ data.extra_recent_oids_loaded = 0;
+
r = for_each_loose_object(add_recent_loose, &data,
FOR_EACH_OBJECT_LOCAL_ONLY);
if (r)
- return r;
+ goto done;
flags = FOR_EACH_OBJECT_LOCAL_ONLY | FOR_EACH_OBJECT_PACK_ORDER;
if (ignore_in_core_kept_packs)
flags |= FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS;
- return for_each_packed_object(add_recent_packed, &data, flags);
+ r = for_each_packed_object(add_recent_packed, &data, flags);
+
+done:
+ oidset_clear(&data.extra_recent_oids);
+
+ return r;
}
static int mark_object_seen(const struct object_id *oid,
diff --git a/cache.h b/read-cache-ll.h
index 71e2fe74c4..9a1a7edc5a 100644
--- a/cache.h
+++ b/read-cache-ll.h
@@ -1,44 +1,10 @@
-#ifndef CACHE_H
-#define CACHE_H
+#ifndef READ_CACHE_LL_H
+#define READ_CACHE_LL_H
-#include "git-compat-util.h"
-#include "strbuf.h"
+#include "hash-ll.h"
#include "hashmap.h"
-#include "gettext.h"
-#include "string-list.h"
-#include "pathspec.h"
-#include "object.h"
#include "statinfo.h"
-#if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
-#define DTYPE(de) ((de)->d_type)
-#else
-#undef DT_UNKNOWN
-#undef DT_DIR
-#undef DT_REG
-#undef DT_LNK
-#define DT_UNKNOWN 0
-#define DT_DIR 1
-#define DT_REG 2
-#define DT_LNK 3
-#define DTYPE(de) DT_UNKNOWN
-#endif
-
-/*
- * Some mode bits are also used internally for computations.
- *
- * They *must* not overlap with any valid modes, and they *must* not be emitted
- * to outside world - i.e. appear on disk or network. In other words, it's just
- * temporary fields, which we internally use, but they have to stay in-house.
- *
- * ( such approach is valid, as standard S_IF* fits into 16 bits, and in Git
- * codebase mode is `unsigned int` which is assumed to be at least 32 bits )
- */
-
-/* used internally in tree-diff */
-#define S_DIFFTREE_IFXMIN_NEQ 0x80000000
-
-
/*
* Basic data structures for the directory cache
*/
@@ -157,42 +123,6 @@ static inline unsigned create_ce_flags(unsigned stage)
#define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
#define ce_intent_to_add(ce) ((ce)->ce_flags & CE_INTENT_TO_ADD)
-static inline unsigned int ce_mode_from_stat(const struct cache_entry *ce,
- unsigned int mode)
-{
- extern int trust_executable_bit, has_symlinks;
- if (!has_symlinks && S_ISREG(mode) &&
- ce && S_ISLNK(ce->ce_mode))
- return ce->ce_mode;
- if (!trust_executable_bit && S_ISREG(mode)) {
- if (ce && S_ISREG(ce->ce_mode))
- return ce->ce_mode;
- return create_ce_mode(0666);
- }
- return create_ce_mode(mode);
-}
-static inline int ce_to_dtype(const struct cache_entry *ce)
-{
- unsigned ce_mode = ntohl(ce->ce_mode);
- if (S_ISREG(ce_mode))
- return DT_REG;
- else if (S_ISDIR(ce_mode) || S_ISGITLINK(ce_mode))
- return DT_DIR;
- else if (S_ISLNK(ce_mode))
- return DT_LNK;
- else
- return DT_UNKNOWN;
-}
-
-static inline int ce_path_match(struct index_state *istate,
- const struct cache_entry *ce,
- const struct pathspec *pathspec,
- char *seen)
-{
- return match_pathspec(istate, pathspec, ce->name, ce_namelen(ce), 0, seen,
- S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode));
-}
-
#define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
#define SOMETHING_CHANGED (1 << 0) /* unclassified changes go here */
@@ -276,12 +206,6 @@ struct index_state {
void index_state_init(struct index_state *istate, struct repository *r);
void release_index(struct index_state *istate);
-/* Name hashing */
-int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
-void add_name_hash(struct index_state *istate, struct cache_entry *ce);
-void remove_name_hash(struct index_state *istate, struct cache_entry *ce);
-void free_name_hash(struct index_state *istate);
-
/* Cache entry creation and cleanup */
/*
@@ -349,31 +273,14 @@ typedef int (*must_prefetch_predicate)(const struct cache_entry *);
void prefetch_cache_entries(const struct index_state *istate,
must_prefetch_predicate must_prefetch);
-#ifdef USE_THE_INDEX_VARIABLE
-extern struct index_state the_index;
-#endif
-
-#define INIT_DB_QUIET 0x0001
-#define INIT_DB_EXIST_OK 0x0002
-
-int init_db(const char *git_dir, const char *real_git_dir,
- const char *template_dir, int hash_algo,
- const char *initial_branch, unsigned int flags);
-void initialize_repository_version(int hash_algo, int reinit);
-
/* Initialize and use the cache information */
struct lock_file;
-void preload_index(struct index_state *index,
- const struct pathspec *pathspec,
- unsigned int refresh_flags);
int do_read_index(struct index_state *istate, const char *path,
int must_exist); /* for testting only! */
int read_index_from(struct index_state *, const char *path,
const char *gitdir);
int is_index_unborn(struct index_state *);
-void ensure_full_index(struct index_state *istate);
-
/* For use with `write_locked_index()`. */
#define COMMIT_LOCK (1 << 0)
#define SKIP_IF_UNCHANGED (1 << 1)
@@ -416,9 +323,6 @@ int repo_index_has_changes(struct repository *repo,
int verify_path(const char *path, unsigned mode);
int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
-int index_dir_exists(struct index_state *istate, const char *name, int namelen);
-void adjust_dirname_case(struct index_state *istate, char *name);
-struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
/*
* Searches for an entry defined by name and namelen in the given index.
@@ -527,19 +431,6 @@ int has_racy_timestamp(struct index_state *istate);
int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
-/*
- * Record to sd the data from st that we use to check whether a file
- * might have changed.
- */
-void fill_stat_data(struct stat_data *sd, struct stat *st);
-
-/*
- * Return 0 if st is consistent with a file not having been changed
- * since sd was filled. If there are differences, return a
- * combination of MTIME_CHANGED, CTIME_CHANGED, OWNER_CHANGED,
- * INODE_CHANGED, and DATA_CHANGED.
- */
-int match_stat_data(const struct stat_data *sd, struct stat *st);
int match_stat_data_racy(const struct index_state *istate,
const struct stat_data *sd, struct stat *st);
@@ -578,158 +469,13 @@ void set_alternate_index_output(const char *);
extern int verify_index_checksum;
extern int verify_ce_order;
-#define MTIME_CHANGED 0x0001
-#define CTIME_CHANGED 0x0002
-#define OWNER_CHANGED 0x0004
-#define MODE_CHANGED 0x0008
-#define INODE_CHANGED 0x0010
-#define DATA_CHANGED 0x0020
-#define TYPE_CHANGED 0x0040
-
-int base_name_compare(const char *name1, size_t len1, int mode1,
- const char *name2, size_t len2, int mode2);
-int df_name_compare(const char *name1, size_t len1, int mode1,
- const char *name2, size_t len2, int mode2);
-int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);
-int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);
-
-struct cache_def {
- struct strbuf path;
- int flags;
- int track_flags;
- int prefix_len_stat_func;
-};
-#define CACHE_DEF_INIT { \
- .path = STRBUF_INIT, \
-}
-static inline void cache_def_clear(struct cache_def *cache)
-{
- strbuf_release(&cache->path);
-}
-
-int has_symlink_leading_path(const char *name, int len);
-int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
-int check_leading_path(const char *name, int len, int warn_on_lstat_err);
-int has_dirs_only_path(const char *name, int len, int prefix_len);
-void invalidate_lstat_cache(void);
-void schedule_dir_for_removal(const char *name, int len);
-void remove_scheduled_dirs(void);
-
-struct pack_window {
- struct pack_window *next;
- unsigned char *base;
- off_t offset;
- size_t len;
- unsigned int last_used;
- unsigned int inuse_cnt;
-};
-
-struct pack_entry {
- off_t offset;
- struct packed_git *p;
-};
-
-/* Dumb servers support */
-int update_server_info(int);
-
-#define COPY_READ_ERROR (-2)
-#define COPY_WRITE_ERROR (-3)
-int copy_fd(int ifd, int ofd);
-int copy_file(const char *dst, const char *src, int mode);
-int copy_file_with_time(const char *dst, const char *src, int mode);
-
-/* base85 */
-int decode_85(char *dst, const char *line, int linelen);
-void encode_85(char *buf, const unsigned char *data, int bytes);
+int cmp_cache_name_compare(const void *a_, const void *b_);
-/* pkt-line.c */
-void packet_trace_identity(const char *prog);
+int add_files_to_cache(struct repository *repo, const char *prefix,
+ const struct pathspec *pathspec, int include_sparse,
+ int flags);
-/* add */
-/*
- * return 0 if success, 1 - if addition of a file failed and
- * ADD_FILES_IGNORE_ERRORS was specified in flags
- */
-int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags);
-
-/* diff.c */
-extern int diff_auto_refresh_index;
-
-/* match-trees.c */
-void shift_tree(struct repository *, const struct object_id *, const struct object_id *, struct object_id *, int);
-void shift_tree_by(struct repository *, const struct object_id *, const struct object_id *, struct object_id *, const char *);
-
-/*
- * whitespace rules.
- * used by both diff and apply
- * last two digits are tab width
- */
-#define WS_BLANK_AT_EOL 0100
-#define WS_SPACE_BEFORE_TAB 0200
-#define WS_INDENT_WITH_NON_TAB 0400
-#define WS_CR_AT_EOL 01000
-#define WS_BLANK_AT_EOF 02000
-#define WS_TAB_IN_INDENT 04000
-#define WS_TRAILING_SPACE (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
-#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
-#define WS_TAB_WIDTH_MASK 077
-/* All WS_* -- when extended, adapt diff.c emit_symbol */
-#define WS_RULE_MASK 07777
-extern unsigned whitespace_rule_cfg;
-unsigned whitespace_rule(struct index_state *, const char *);
-unsigned parse_whitespace_rule(const char *);
-unsigned ws_check(const char *line, int len, unsigned ws_rule);
-void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
-char *whitespace_error_string(unsigned ws);
-void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
-int ws_blank_line(const char *line, int len);
-#define ws_tab_width(rule) ((rule) & WS_TAB_WIDTH_MASK)
-
-/* ls-files */
void overlay_tree_on_index(struct index_state *istate,
const char *tree_name, const char *prefix);
-/* merge.c */
-struct commit_list;
-int try_merge_command(struct repository *r,
- const char *strategy, size_t xopts_nr,
- const char **xopts, struct commit_list *common,
- const char *head_arg, struct commit_list *remotes);
-int checkout_fast_forward(struct repository *r,
- const struct object_id *from,
- const struct object_id *to,
- int overwrite_ignore);
-
-
-int sane_execvp(const char *file, char *const argv[]);
-
-/*
- * A struct to encapsulate the concept of whether a file has changed
- * since we last checked it. This uses criteria similar to those used
- * for the index.
- */
-struct stat_validity {
- struct stat_data *sd;
-};
-
-void stat_validity_clear(struct stat_validity *sv);
-
-/*
- * Returns 1 if the path is a regular file (or a symlink to a regular
- * file) and matches the saved stat_validity, 0 otherwise. A missing
- * or inaccessible file is considered a match if the struct was just
- * initialized, or if the previous update found an inaccessible file.
- */
-int stat_validity_check(struct stat_validity *sv, const char *path);
-
-/*
- * Update the stat_validity from a file opened at descriptor fd. If
- * the file is missing, inaccessible, or not a regular file, then
- * future calls to stat_validity_check will match iff one of those
- * conditions continues to be true.
- */
-void stat_validity_update(struct stat_validity *sv, int fd);
-
-int versioncmp(const char *s1, const char *s2);
-
-#endif /* CACHE_H */
+#endif /* READ_CACHE_LL_H */
diff --git a/read-cache.c b/read-cache.c
index e919af3c77..b9a995e5a1 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -3,9 +3,11 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "alloc.h"
+#include "bulk-checkin.h"
#include "config.h"
+#include "date.h"
#include "diff.h"
#include "diffcore.h"
#include "hex.h"
@@ -15,7 +17,7 @@
#include "refs.h"
#include "dir.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "oid-array.h"
#include "tree.h"
#include "commit.h"
@@ -23,13 +25,19 @@
#include "environment.h"
#include "gettext.h"
#include "mem-pool.h"
+#include "name-hash.h"
#include "object-name.h"
+#include "path.h"
+#include "preload-index.h"
+#include "read-cache.h"
#include "resolve-undo.h"
+#include "revision.h"
#include "run-command.h"
#include "strbuf.h"
#include "trace2.h"
#include "varint.h"
#include "split-index.h"
+#include "symlinks.h"
#include "utf8.h"
#include "fsmonitor.h"
#include "thread-utils.h"
@@ -173,61 +181,6 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n
add_index_entry(istate, new_entry, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
}
-void fill_stat_data(struct stat_data *sd, struct stat *st)
-{
- sd->sd_ctime.sec = (unsigned int)st->st_ctime;
- sd->sd_mtime.sec = (unsigned int)st->st_mtime;
- sd->sd_ctime.nsec = ST_CTIME_NSEC(*st);
- sd->sd_mtime.nsec = ST_MTIME_NSEC(*st);
- sd->sd_dev = st->st_dev;
- sd->sd_ino = st->st_ino;
- sd->sd_uid = st->st_uid;
- sd->sd_gid = st->st_gid;
- sd->sd_size = st->st_size;
-}
-
-int match_stat_data(const struct stat_data *sd, struct stat *st)
-{
- int changed = 0;
-
- if (sd->sd_mtime.sec != (unsigned int)st->st_mtime)
- changed |= MTIME_CHANGED;
- if (trust_ctime && check_stat &&
- sd->sd_ctime.sec != (unsigned int)st->st_ctime)
- changed |= CTIME_CHANGED;
-
-#ifdef USE_NSEC
- if (check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st))
- changed |= MTIME_CHANGED;
- if (trust_ctime && check_stat &&
- sd->sd_ctime.nsec != ST_CTIME_NSEC(*st))
- changed |= CTIME_CHANGED;
-#endif
-
- if (check_stat) {
- if (sd->sd_uid != (unsigned int) st->st_uid ||
- sd->sd_gid != (unsigned int) st->st_gid)
- changed |= OWNER_CHANGED;
- if (sd->sd_ino != (unsigned int) st->st_ino)
- changed |= INODE_CHANGED;
- }
-
-#ifdef USE_STDEV
- /*
- * st_dev breaks on network filesystems where different
- * clients will have different views of what "device"
- * the filesystem is on
- */
- if (check_stat && sd->sd_dev != (unsigned int) st->st_dev)
- changed |= INODE_CHANGED;
-#endif
-
- if (sd->sd_size != (unsigned int) st->st_size)
- changed |= DATA_CHANGED;
-
- return changed;
-}
-
/*
* This only updates the "non-critical" parts of the directory
* cache, ie the parts that aren't tracked by GIT, and only used
@@ -498,87 +451,30 @@ int ie_modified(struct index_state *istate,
return 0;
}
-int base_name_compare(const char *name1, size_t len1, int mode1,
- const char *name2, size_t len2, int mode2)
+static int cache_name_stage_compare(const char *name1, int len1, int stage1,
+ const char *name2, int len2, int stage2)
{
- unsigned char c1, c2;
- size_t len = len1 < len2 ? len1 : len2;
int cmp;
- cmp = memcmp(name1, name2, len);
- if (cmp)
- return cmp;
- c1 = name1[len];
- c2 = name2[len];
- if (!c1 && S_ISDIR(mode1))
- c1 = '/';
- if (!c2 && S_ISDIR(mode2))
- c2 = '/';
- return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
-}
-
-/*
- * df_name_compare() is identical to base_name_compare(), except it
- * compares conflicting directory/file entries as equal. Note that
- * while a directory name compares as equal to a regular file, they
- * then individually compare _differently_ to a filename that has
- * a dot after the basename (because '\0' < '.' < '/').
- *
- * This is used by routines that want to traverse the git namespace
- * but then handle conflicting entries together when possible.
- */
-int df_name_compare(const char *name1, size_t len1, int mode1,
- const char *name2, size_t len2, int mode2)
-{
- unsigned char c1, c2;
- size_t len = len1 < len2 ? len1 : len2;
- int cmp;
-
- cmp = memcmp(name1, name2, len);
+ cmp = name_compare(name1, len1, name2, len2);
if (cmp)
return cmp;
- /* Directories and files compare equal (same length, same name) */
- if (len1 == len2)
- return 0;
- c1 = name1[len];
- if (!c1 && S_ISDIR(mode1))
- c1 = '/';
- c2 = name2[len];
- if (!c2 && S_ISDIR(mode2))
- c2 = '/';
- if (c1 == '/' && !c2)
- return 0;
- if (c2 == '/' && !c1)
- return 0;
- return c1 - c2;
-}
-int name_compare(const char *name1, size_t len1, const char *name2, size_t len2)
-{
- size_t min_len = (len1 < len2) ? len1 : len2;
- int cmp = memcmp(name1, name2, min_len);
- if (cmp)
- return cmp;
- if (len1 < len2)
+ if (stage1 < stage2)
return -1;
- if (len1 > len2)
+ if (stage1 > stage2)
return 1;
return 0;
}
-int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2)
+int cmp_cache_name_compare(const void *a_, const void *b_)
{
- int cmp;
-
- cmp = name_compare(name1, len1, name2, len2);
- if (cmp)
- return cmp;
+ const struct cache_entry *ce1, *ce2;
- if (stage1 < stage2)
- return -1;
- if (stage1 > stage2)
- return 1;
- return 0;
+ ce1 = *((const struct cache_entry **)a_);
+ ce2 = *((const struct cache_entry **)b_);
+ return cache_name_stage_compare(ce1->name, ce1->ce_namelen, ce_stage(ce1),
+ ce2->name, ce2->ce_namelen, ce_stage(ce2));
}
static int index_name_stage_pos(struct index_state *istate,
@@ -3589,35 +3485,6 @@ void *read_blob_data_from_index(struct index_state *istate,
return data;
}
-void stat_validity_clear(struct stat_validity *sv)
-{
- FREE_AND_NULL(sv->sd);
-}
-
-int stat_validity_check(struct stat_validity *sv, const char *path)
-{
- struct stat st;
-
- if (stat(path, &st) < 0)
- return sv->sd == NULL;
- if (!sv->sd)
- return 0;
- return S_ISREG(st.st_mode) && !match_stat_data(sv->sd, &st);
-}
-
-void stat_validity_update(struct stat_validity *sv, int fd)
-{
- struct stat st;
-
- if (fstat(fd, &st) < 0 || !S_ISREG(st.st_mode))
- stat_validity_clear(sv);
- else {
- if (!sv->sd)
- CALLOC_ARRAY(sv->sd, 1);
- fill_stat_data(sv->sd, &st);
- }
-}
-
void move_index_extensions(struct index_state *dst, struct index_state *src)
{
dst->untracked = src->untracked;
@@ -3861,3 +3728,240 @@ void prefetch_cache_entries(const struct index_state *istate,
to_fetch.oid, to_fetch.nr);
oid_array_clear(&to_fetch);
}
+
+static int read_one_entry_opt(struct index_state *istate,
+ const struct object_id *oid,
+ struct strbuf *base,
+ const char *pathname,
+ unsigned mode, int opt)
+{
+ int len;
+ struct cache_entry *ce;
+
+ if (S_ISDIR(mode))
+ return READ_TREE_RECURSIVE;
+
+ len = strlen(pathname);
+ ce = make_empty_cache_entry(istate, base->len + len);
+
+ ce->ce_mode = create_ce_mode(mode);
+ ce->ce_flags = create_ce_flags(1);
+ ce->ce_namelen = base->len + len;
+ memcpy(ce->name, base->buf, base->len);
+ memcpy(ce->name + base->len, pathname, len+1);
+ oidcpy(&ce->oid, oid);
+ return add_index_entry(istate, ce, opt);
+}
+
+static int read_one_entry(const struct object_id *oid, struct strbuf *base,
+ const char *pathname, unsigned mode,
+ void *context)
+{
+ struct index_state *istate = context;
+ return read_one_entry_opt(istate, oid, base, pathname,
+ mode,
+ ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
+}
+
+/*
+ * This is used when the caller knows there is no existing entries at
+ * the stage that will conflict with the entry being added.
+ */
+static int read_one_entry_quick(const struct object_id *oid, struct strbuf *base,
+ const char *pathname, unsigned mode,
+ void *context)
+{
+ struct index_state *istate = context;
+ return read_one_entry_opt(istate, oid, base, pathname,
+ mode, ADD_CACHE_JUST_APPEND);
+}
+
+/*
+ * Read the tree specified with --with-tree option
+ * (typically, HEAD) into stage #1 and then
+ * squash them down to stage #0. This is used for
+ * --error-unmatch to list and check the path patterns
+ * that were given from the command line. We are not
+ * going to write this index out.
+ */
+void overlay_tree_on_index(struct index_state *istate,
+ const char *tree_name, const char *prefix)
+{
+ struct tree *tree;
+ struct object_id oid;
+ struct pathspec pathspec;
+ struct cache_entry *last_stage0 = NULL;
+ int i;
+ read_tree_fn_t fn = NULL;
+ int err;
+
+ if (repo_get_oid(the_repository, tree_name, &oid))
+ die("tree-ish %s not found.", tree_name);
+ tree = parse_tree_indirect(&oid);
+ if (!tree)
+ die("bad tree-ish %s", tree_name);
+
+ /* Hoist the unmerged entries up to stage #3 to make room */
+ /* TODO: audit for interaction with sparse-index. */
+ ensure_full_index(istate);
+ for (i = 0; i < istate->cache_nr; i++) {
+ struct cache_entry *ce = istate->cache[i];
+ if (!ce_stage(ce))
+ continue;
+ ce->ce_flags |= CE_STAGEMASK;
+ }
+
+ if (prefix) {
+ static const char *(matchbuf[1]);
+ matchbuf[0] = NULL;
+ parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC,
+ PATHSPEC_PREFER_CWD, prefix, matchbuf);
+ } else
+ memset(&pathspec, 0, sizeof(pathspec));
+
+ /*
+ * See if we have cache entry at the stage. If so,
+ * do it the original slow way, otherwise, append and then
+ * sort at the end.
+ */
+ for (i = 0; !fn && i < istate->cache_nr; i++) {
+ const struct cache_entry *ce = istate->cache[i];
+ if (ce_stage(ce) == 1)
+ fn = read_one_entry;
+ }
+
+ if (!fn)
+ fn = read_one_entry_quick;
+ err = read_tree(the_repository, tree, &pathspec, fn, istate);
+ clear_pathspec(&pathspec);
+ if (err)
+ die("unable to read tree entries %s", tree_name);
+
+ /*
+ * Sort the cache entry -- we need to nuke the cache tree, though.
+ */
+ if (fn == read_one_entry_quick) {
+ cache_tree_free(&istate->cache_tree);
+ QSORT(istate->cache, istate->cache_nr, cmp_cache_name_compare);
+ }
+
+ for (i = 0; i < istate->cache_nr; i++) {
+ struct cache_entry *ce = istate->cache[i];
+ switch (ce_stage(ce)) {
+ case 0:
+ last_stage0 = ce;
+ /* fallthru */
+ default:
+ continue;
+ case 1:
+ /*
+ * If there is stage #0 entry for this, we do not
+ * need to show it. We use CE_UPDATE bit to mark
+ * such an entry.
+ */
+ if (last_stage0 &&
+ !strcmp(last_stage0->name, ce->name))
+ ce->ce_flags |= CE_UPDATE;
+ }
+ }
+}
+
+struct update_callback_data {
+ struct index_state *index;
+ int include_sparse;
+ int flags;
+ int add_errors;
+};
+
+static int fix_unmerged_status(struct diff_filepair *p,
+ struct update_callback_data *data)
+{
+ if (p->status != DIFF_STATUS_UNMERGED)
+ return p->status;
+ if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL) && !p->two->mode)
+ /*
+ * This is not an explicit add request, and the
+ * path is missing from the working tree (deleted)
+ */
+ return DIFF_STATUS_DELETED;
+ else
+ /*
+ * Either an explicit add request, or path exists
+ * in the working tree. An attempt to explicitly
+ * add a path that does not exist in the working tree
+ * will be caught as an error by the caller immediately.
+ */
+ return DIFF_STATUS_MODIFIED;
+}
+
+static void update_callback(struct diff_queue_struct *q,
+ struct diff_options *opt UNUSED, void *cbdata)
+{
+ int i;
+ struct update_callback_data *data = cbdata;
+
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filepair *p = q->queue[i];
+ const char *path = p->one->path;
+
+ if (!data->include_sparse &&
+ !path_in_sparse_checkout(path, data->index))
+ continue;
+
+ switch (fix_unmerged_status(p, data)) {
+ default:
+ die(_("unexpected diff status %c"), p->status);
+ case DIFF_STATUS_MODIFIED:
+ case DIFF_STATUS_TYPE_CHANGED:
+ if (add_file_to_index(data->index, path, data->flags)) {
+ if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
+ die(_("updating files failed"));
+ data->add_errors++;
+ }
+ break;
+ case DIFF_STATUS_DELETED:
+ if (data->flags & ADD_CACHE_IGNORE_REMOVAL)
+ break;
+ if (!(data->flags & ADD_CACHE_PRETEND))
+ remove_file_from_index(data->index, path);
+ if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
+ printf(_("remove '%s'\n"), path);
+ break;
+ }
+ }
+}
+
+int add_files_to_cache(struct repository *repo, const char *prefix,
+ const struct pathspec *pathspec, int include_sparse,
+ int flags)
+{
+ struct update_callback_data data;
+ struct rev_info rev;
+
+ memset(&data, 0, sizeof(data));
+ data.index = repo->index;
+ data.include_sparse = include_sparse;
+ data.flags = flags;
+
+ repo_init_revisions(repo, &rev, prefix);
+ setup_revisions(0, NULL, &rev, NULL);
+ if (pathspec)
+ copy_pathspec(&rev.prune_data, pathspec);
+ rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
+ rev.diffopt.format_callback = update_callback;
+ rev.diffopt.format_callback_data = &data;
+ rev.diffopt.flags.override_submodule_config = 1;
+ rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
+
+ /*
+ * Use an ODB transaction to optimize adding multiple objects.
+ * This function is invoked from commands other than 'add', which
+ * may not have their own transaction active.
+ */
+ begin_odb_transaction();
+ run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
+ end_odb_transaction();
+
+ release_revisions(&rev);
+ return !!data.add_errors;
+}
diff --git a/read-cache.h b/read-cache.h
new file mode 100644
index 0000000000..043da1f1aa
--- /dev/null
+++ b/read-cache.h
@@ -0,0 +1,45 @@
+#ifndef READ_CACHE_H
+#define READ_CACHE_H
+
+#include "read-cache-ll.h"
+#include "object.h"
+#include "pathspec.h"
+
+static inline unsigned int ce_mode_from_stat(const struct cache_entry *ce,
+ unsigned int mode)
+{
+ extern int trust_executable_bit, has_symlinks;
+ if (!has_symlinks && S_ISREG(mode) &&
+ ce && S_ISLNK(ce->ce_mode))
+ return ce->ce_mode;
+ if (!trust_executable_bit && S_ISREG(mode)) {
+ if (ce && S_ISREG(ce->ce_mode))
+ return ce->ce_mode;
+ return create_ce_mode(0666);
+ }
+ return create_ce_mode(mode);
+}
+
+static inline int ce_to_dtype(const struct cache_entry *ce)
+{
+ unsigned ce_mode = ntohl(ce->ce_mode);
+ if (S_ISREG(ce_mode))
+ return DT_REG;
+ else if (S_ISDIR(ce_mode) || S_ISGITLINK(ce_mode))
+ return DT_DIR;
+ else if (S_ISLNK(ce_mode))
+ return DT_LNK;
+ else
+ return DT_UNKNOWN;
+}
+
+static inline int ce_path_match(struct index_state *istate,
+ const struct cache_entry *ce,
+ const struct pathspec *pathspec,
+ char *seen)
+{
+ return match_pathspec(istate, pathspec, ce->name, ce_namelen(ce), 0, seen,
+ S_ISDIR(ce->ce_mode) || S_ISGITLINK(ce->ce_mode));
+}
+
+#endif /* READ_CACHE_H */
diff --git a/rebase-interactive.c b/rebase-interactive.c
index 789f407361..f286404d4b 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -5,6 +5,7 @@
#include "gettext.h"
#include "sequencer.h"
#include "rebase-interactive.h"
+#include "repository.h"
#include "strbuf.h"
#include "commit-slab.h"
#include "config.h"
@@ -71,13 +72,14 @@ void append_todo_help(int command_count,
if (!edit_todo) {
strbuf_addch(buf, '\n');
- strbuf_commented_addf(buf, Q_("Rebase %s onto %s (%d command)",
- "Rebase %s onto %s (%d commands)",
- command_count),
+ strbuf_commented_addf(buf, comment_line_char,
+ Q_("Rebase %s onto %s (%d command)",
+ "Rebase %s onto %s (%d commands)",
+ command_count),
shortrevisions, shortonto, command_count);
}
- strbuf_add_commented_lines(buf, msg, strlen(msg));
+ strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_char);
if (get_missing_commit_check_level() == MISSING_COMMIT_CHECK_ERROR)
msg = _("\nDo not remove any line. Use 'drop' "
@@ -86,7 +88,7 @@ void append_todo_help(int command_count,
msg = _("\nIf you remove a line here "
"THAT COMMIT WILL BE LOST.\n");
- strbuf_add_commented_lines(buf, msg, strlen(msg));
+ strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_char);
if (edit_todo)
msg = _("\nYou are editing the todo file "
@@ -97,7 +99,7 @@ void append_todo_help(int command_count,
msg = _("\nHowever, if you remove everything, "
"the rebase will be aborted.\n\n");
- strbuf_add_commented_lines(buf, msg, strlen(msg));
+ strbuf_add_commented_lines(buf, msg, strlen(msg), comment_line_char);
}
int edit_todo_list(struct repository *r, struct todo_list *todo_list,
@@ -129,7 +131,7 @@ int edit_todo_list(struct repository *r, struct todo_list *todo_list,
if (launch_sequence_editor(todo_file, &new_todo->buf, NULL))
return -2;
- strbuf_stripspace(&new_todo->buf, 1);
+ strbuf_stripspace(&new_todo->buf, comment_line_char);
if (initial && new_todo->buf.len == 0)
return -3;
diff --git a/ref-filter.c b/ref-filter.c
index 10aab14f03..771fa6c133 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -1,13 +1,14 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "alloc.h"
#include "environment.h"
#include "gettext.h"
+#include "gpg-interface.h"
#include "hex.h"
#include "parse-options.h"
#include "refs.h"
#include "wildmatch.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "oid-array.h"
#include "repository.h"
#include "commit.h"
@@ -19,6 +20,7 @@
#include "revision.h"
#include "utf8.h"
#include "version.h"
+#include "versioncmp.h"
#include "trailer.h"
#include "wt-status.h"
#include "commit-slab.h"
@@ -2102,12 +2104,12 @@ static int get_ref_atom_value(struct ref_array_item *ref, int atom,
* matches a pattern "refs/heads/mas") or a wildcard (e.g. the same ref
* matches "refs/heads/mas*", too).
*/
-static int match_pattern(const struct ref_filter *filter, const char *refname)
+static int match_pattern(const char **patterns, const char *refname,
+ const int ignore_case)
{
- const char **patterns = filter->name_patterns;
unsigned flags = 0;
- if (filter->ignore_case)
+ if (ignore_case)
flags |= WM_CASEFOLD;
/*
@@ -2132,9 +2134,10 @@ static int match_pattern(const struct ref_filter *filter, const char *refname)
* matches a pattern "refs/heads/" but not "refs/heads/m") or a
* wildcard (e.g. the same ref matches "refs/heads/m*", too).
*/
-static int match_name_as_path(const struct ref_filter *filter, const char *refname)
+static int match_name_as_path(const struct ref_filter *filter,
+ const char **pattern,
+ const char *refname)
{
- const char **pattern = filter->name_patterns;
int namelen = strlen(refname);
unsigned flags = WM_PATHNAME;
@@ -2163,8 +2166,18 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname)
if (!*filter->name_patterns)
return 1; /* No pattern always matches */
if (filter->match_as_path)
- return match_name_as_path(filter, refname);
- return match_pattern(filter, refname);
+ return match_name_as_path(filter, filter->name_patterns, refname);
+ return match_pattern(filter->name_patterns, refname,
+ filter->ignore_case);
+}
+
+static int filter_exclude_match(struct ref_filter *filter, const char *refname)
+{
+ if (!filter->exclude.nr)
+ return 0;
+ if (filter->match_as_path)
+ return match_name_as_path(filter, filter->exclude.v, refname);
+ return match_pattern(filter->exclude.v, refname, filter->ignore_case);
}
/*
@@ -2196,11 +2209,13 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
if (!filter->name_patterns[0]) {
/* no patterns; we have to look at everything */
- return for_each_fullref_in("", cb, cb_data);
+ return refs_for_each_fullref_in(get_main_ref_store(the_repository),
+ "", filter->exclude.v, cb, cb_data);
}
return refs_for_each_fullref_in_prefixes(get_main_ref_store(the_repository),
NULL, filter->name_patterns,
+ filter->exclude.v,
cb, cb_data);
}
@@ -2334,6 +2349,9 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
if (!filter_pattern_match(filter, refname))
return 0;
+ if (filter_exclude_match(filter, refname))
+ return 0;
+
if (filter->points_at.nr && !match_points_at(&filter->points_at, oid, refname))
return 0;
@@ -2416,13 +2434,13 @@ void ref_array_clear(struct ref_array *array)
#define EXCLUDE_REACHED 0
#define INCLUDE_REACHED 1
static void reach_filter(struct ref_array *array,
- struct commit_list *check_reachable,
+ struct commit_list **check_reachable,
int include_reached)
{
int i, old_nr;
struct commit **to_clear;
- if (!check_reachable)
+ if (!*check_reachable)
return;
CALLOC_ARRAY(to_clear, array->nr);
@@ -2432,7 +2450,7 @@ static void reach_filter(struct ref_array *array,
}
tips_reachable_from_bases(the_repository,
- check_reachable,
+ *check_reachable,
to_clear, array->nr,
UNINTERESTING);
@@ -2453,8 +2471,8 @@ static void reach_filter(struct ref_array *array,
clear_commit_marks_many(old_nr, to_clear, ALL_REV_FLAGS);
- while (check_reachable) {
- struct commit *merge_commit = pop_commit(&check_reachable);
+ while (*check_reachable) {
+ struct commit *merge_commit = pop_commit(check_reachable);
clear_commit_marks(merge_commit, ALL_REV_FLAGS);
}
@@ -2551,8 +2569,8 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
clear_contains_cache(&ref_cbdata.no_contains_cache);
/* Filters that need revision walking */
- reach_filter(array, filter->reachable_from, INCLUDE_REACHED);
- reach_filter(array, filter->unreachable_from, EXCLUDE_REACHED);
+ reach_filter(array, &filter->reachable_from, INCLUDE_REACHED);
+ reach_filter(array, &filter->unreachable_from, EXCLUDE_REACHED);
save_commit_buffer = save_commit_buffer_orig;
return ret;
@@ -2864,3 +2882,20 @@ int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset)
return 0;
}
+
+void ref_filter_init(struct ref_filter *filter)
+{
+ struct ref_filter blank = REF_FILTER_INIT;
+ memcpy(filter, &blank, sizeof(blank));
+}
+
+void ref_filter_clear(struct ref_filter *filter)
+{
+ strvec_clear(&filter->exclude);
+ oid_array_clear(&filter->points_at);
+ free_commit_list(filter->with_commit);
+ free_commit_list(filter->no_commit);
+ free_commit_list(filter->reachable_from);
+ free_commit_list(filter->unreachable_from);
+ ref_filter_init(filter);
+}
diff --git a/ref-filter.h b/ref-filter.h
index 430701cfb7..1524bc463a 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -6,6 +6,7 @@
#include "refs.h"
#include "commit.h"
#include "string-list.h"
+#include "strvec.h"
/* Quoting styles */
#define QUOTE_NONE 0
@@ -59,6 +60,7 @@ struct ref_array {
struct ref_filter {
const char **name_patterns;
+ struct strvec exclude;
struct oid_array points_at;
struct commit_list *with_commit;
struct commit_list *no_commit;
@@ -92,6 +94,10 @@ struct ref_format {
struct string_list bases;
};
+#define REF_FILTER_INIT { \
+ .points_at = OID_ARRAY_INIT, \
+ .exclude = STRVEC_INIT, \
+}
#define REF_FORMAT_INIT { \
.use_color = -1, \
.bases = STRING_LIST_INIT_DUP, \
@@ -109,6 +115,9 @@ struct ref_format {
#define OPT_REF_SORT(var) \
OPT_STRING_LIST(0, "sort", (var), \
N_("key"), N_("field name to sort on"))
+#define OPT_REF_FILTER_EXCLUDE(var) \
+ OPT_STRVEC(0, "exclude", &(var)->exclude, \
+ N_("pattern"), N_("exclude refs which match pattern"))
/*
* API for filtering a set of refs. Based on the type of refs the user
@@ -167,4 +176,7 @@ void filter_ahead_behind(struct repository *r,
struct ref_format *format,
struct ref_array *array);
+void ref_filter_init(struct ref_filter *filter);
+void ref_filter_clear(struct ref_filter *filter);
+
#endif /* REF_FILTER_H */
diff --git a/reflog-walk.c b/reflog-walk.c
index 4ba1a10c82..d337e64431 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -3,6 +3,7 @@
#include "commit.h"
#include "refs.h"
#include "diff.h"
+#include "repository.h"
#include "revision.h"
#include "string-list.h"
#include "reflog-walk.h"
diff --git a/reflog.c b/reflog.c
index 9c09443088..9ad50e7d93 100644
--- a/reflog.c
+++ b/reflog.c
@@ -1,9 +1,11 @@
#include "git-compat-util.h"
#include "gettext.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "reflog.h"
#include "refs.h"
#include "revision.h"
+#include "tree.h"
+#include "tree-walk.h"
#include "worktree.h"
/* Remember to update object flag allocation in object.h */
diff --git a/refs.c b/refs.c
index d2a98e1c21..3065e514fd 100644
--- a/refs.c
+++ b/refs.c
@@ -17,8 +17,9 @@
#include "run-command.h"
#include "hook.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "object.h"
+#include "path.h"
#include "tag.h"
#include "submodule.h"
#include "worktree.h"
@@ -28,6 +29,7 @@
#include "sigchain.h"
#include "date.h"
#include "commit.h"
+#include "wildmatch.h"
#include "wrapper.h"
/*
@@ -375,8 +377,8 @@ char *resolve_refdup(const char *refname, int resolve_flags,
oid, flags);
}
-/* The argument to filter_refs */
-struct ref_filter {
+/* The argument to for_each_filter_refs */
+struct for_each_ref_filter {
const char *pattern;
const char *prefix;
each_ref_fn *fn;
@@ -409,10 +411,11 @@ int ref_exists(const char *refname)
return refs_ref_exists(get_main_ref_store(the_repository), refname);
}
-static int filter_refs(const char *refname, const struct object_id *oid,
- int flags, void *data)
+static int for_each_filter_refs(const char *refname,
+ const struct object_id *oid,
+ int flags, void *data)
{
- struct ref_filter *filter = (struct ref_filter *)data;
+ struct for_each_ref_filter *filter = data;
if (wildmatch(filter->pattern, refname, 0))
return 0;
@@ -569,7 +572,7 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
const char *prefix, void *cb_data)
{
struct strbuf real_pattern = STRBUF_INIT;
- struct ref_filter filter;
+ struct for_each_ref_filter filter;
int ret;
if (!prefix && !starts_with(pattern, "refs/"))
@@ -589,7 +592,7 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
filter.prefix = prefix;
filter.fn = fn;
filter.cb_data = cb_data;
- ret = for_each_ref(filter_refs, &filter);
+ ret = for_each_ref(for_each_filter_refs, &filter);
strbuf_release(&real_pattern);
return ret;
@@ -1426,7 +1429,7 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
}
int parse_hide_refs_config(const char *var, const char *value, const char *section,
- struct string_list *hide_refs)
+ struct strvec *hide_refs)
{
const char *key;
if (!strcmp("transfer.hiderefs", var) ||
@@ -1437,22 +1440,23 @@ int parse_hide_refs_config(const char *var, const char *value, const char *secti
if (!value)
return config_error_nonbool(var);
- ref = xstrdup(value);
+
+ /* drop const to remove trailing '/' characters */
+ ref = (char *)strvec_push(hide_refs, value);
len = strlen(ref);
while (len && ref[len - 1] == '/')
ref[--len] = '\0';
- string_list_append_nodup(hide_refs, ref);
}
return 0;
}
int ref_is_hidden(const char *refname, const char *refname_full,
- const struct string_list *hide_refs)
+ const struct strvec *hide_refs)
{
int i;
for (i = hide_refs->nr - 1; i >= 0; i--) {
- const char *match = hide_refs->items[i].string;
+ const char *match = hide_refs->v[i];
const char *subject;
int neg = 0;
const char *p;
@@ -1525,7 +1529,9 @@ int head_ref(each_ref_fn fn, void *cb_data)
struct ref_iterator *refs_ref_iterator_begin(
struct ref_store *refs,
- const char *prefix, int trim,
+ const char *prefix,
+ const char **exclude_patterns,
+ int trim,
enum do_for_each_ref_flags flags)
{
struct ref_iterator *iter;
@@ -1541,8 +1547,7 @@ struct ref_iterator *refs_ref_iterator_begin(
}
}
- iter = refs->be->iterator_begin(refs, prefix, flags);
-
+ iter = refs->be->iterator_begin(refs, prefix, exclude_patterns, flags);
/*
* `iterator_begin()` already takes care of prefix, but we
* might need to do some trimming:
@@ -1576,7 +1581,7 @@ static int do_for_each_repo_ref(struct repository *r, const char *prefix,
if (!refs)
return 0;
- iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
+ iter = refs_ref_iterator_begin(refs, prefix, NULL, trim, flags);
return do_for_each_repo_ref_iterator(r, iter, fn, cb_data);
}
@@ -1598,6 +1603,7 @@ static int do_for_each_ref_helper(struct repository *r,
}
static int do_for_each_ref(struct ref_store *refs, const char *prefix,
+ const char **exclude_patterns,
each_ref_fn fn, int trim,
enum do_for_each_ref_flags flags, void *cb_data)
{
@@ -1607,7 +1613,8 @@ static int do_for_each_ref(struct ref_store *refs, const char *prefix,
if (!refs)
return 0;
- iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
+ iter = refs_ref_iterator_begin(refs, prefix, exclude_patterns, trim,
+ flags);
return do_for_each_repo_ref_iterator(the_repository, iter,
do_for_each_ref_helper, &hp);
@@ -1615,7 +1622,7 @@ static int do_for_each_ref(struct ref_store *refs, const char *prefix,
int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(refs, "", fn, 0, 0, cb_data);
+ return do_for_each_ref(refs, "", NULL, fn, 0, 0, cb_data);
}
int for_each_ref(each_ref_fn fn, void *cb_data)
@@ -1626,7 +1633,7 @@ int for_each_ref(each_ref_fn fn, void *cb_data)
int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(refs, prefix, fn, strlen(prefix), 0, cb_data);
+ return do_for_each_ref(refs, prefix, NULL, fn, strlen(prefix), 0, cb_data);
}
int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
@@ -1637,13 +1644,14 @@ int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data)
{
return do_for_each_ref(get_main_ref_store(the_repository),
- prefix, fn, 0, 0, cb_data);
+ prefix, NULL, fn, 0, 0, cb_data);
}
int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
+ const char **exclude_patterns,
each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(refs, prefix, fn, 0, 0, cb_data);
+ return do_for_each_ref(refs, prefix, exclude_patterns, fn, 0, 0, cb_data);
}
int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data)
@@ -1654,20 +1662,21 @@ int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_dat
DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
}
-int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
+int for_each_namespaced_ref(const char **exclude_patterns,
+ each_ref_fn fn, void *cb_data)
{
struct strbuf buf = STRBUF_INIT;
int ret;
strbuf_addf(&buf, "%srefs/", get_git_namespace());
ret = do_for_each_ref(get_main_ref_store(the_repository),
- buf.buf, fn, 0, 0, cb_data);
+ buf.buf, exclude_patterns, fn, 0, 0, cb_data);
strbuf_release(&buf);
return ret;
}
int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(refs, "", fn, 0,
+ return do_for_each_ref(refs, "", NULL, fn, 0,
DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
}
@@ -1737,6 +1746,7 @@ static void find_longest_prefixes(struct string_list *out,
int refs_for_each_fullref_in_prefixes(struct ref_store *ref_store,
const char *namespace,
const char **patterns,
+ const char **exclude_patterns,
each_ref_fn fn, void *cb_data)
{
struct string_list prefixes = STRING_LIST_INIT_DUP;
@@ -1752,7 +1762,8 @@ int refs_for_each_fullref_in_prefixes(struct ref_store *ref_store,
for_each_string_list_item(prefix, &prefixes) {
strbuf_addstr(&buf, prefix->string);
- ret = refs_for_each_fullref_in(ref_store, buf.buf, fn, cb_data);
+ ret = refs_for_each_fullref_in(ref_store, buf.buf,
+ exclude_patterns, fn, cb_data);
if (ret)
break;
strbuf_setlen(&buf, namespace_len);
@@ -2132,9 +2143,9 @@ void base_ref_store_init(struct ref_store *refs, struct repository *repo,
}
/* backend functions */
-int refs_pack_refs(struct ref_store *refs, unsigned int flags)
+int refs_pack_refs(struct ref_store *refs, struct pack_refs_opts *opts)
{
- return refs->be->pack_refs(refs, flags);
+ return refs->be->pack_refs(refs, opts);
}
int peel_iterated_oid(const struct object_id *base, struct object_id *peeled)
@@ -2407,7 +2418,7 @@ int refs_verify_refname_available(struct ref_store *refs,
strbuf_addstr(&dirname, refname + dirname.len);
strbuf_addch(&dirname, '/');
- iter = refs_ref_iterator_begin(refs, dirname.buf, 0,
+ iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0,
DO_FOR_EACH_INCLUDE_BROKEN);
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
if (skip &&
diff --git a/refs.h b/refs.h
index 123cfa4424..27d341d282 100644
--- a/refs.h
+++ b/refs.h
@@ -63,6 +63,12 @@ struct worktree;
#define RESOLVE_REF_NO_RECURSE 0x02
#define RESOLVE_REF_ALLOW_BAD_NAME 0x04
+struct pack_refs_opts {
+ unsigned int flags;
+ struct ref_exclusions *exclusions;
+ struct string_list *includes;
+};
+
const char *refs_resolve_ref_unsafe(struct ref_store *refs,
const char *refname,
int resolve_flags,
@@ -338,6 +344,7 @@ int for_each_ref(each_ref_fn fn, void *cb_data);
int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
+ const char **exclude_patterns,
each_ref_fn fn, void *cb_data);
int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data);
@@ -345,10 +352,15 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data);
* iterate all refs in "patterns" by partitioning patterns into disjoint sets
* and iterating the longest-common prefix of each set.
*
+ * references matching any pattern in "exclude_patterns" are omitted from the
+ * result set on a best-effort basis.
+ *
* callers should be prepared to ignore references that they did not ask for.
*/
int refs_for_each_fullref_in_prefixes(struct ref_store *refs,
- const char *namespace, const char **patterns,
+ const char *namespace,
+ const char **patterns,
+ const char **exclude_patterns,
each_ref_fn fn, void *cb_data);
/**
@@ -366,7 +378,8 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
const char *prefix, void *cb_data);
int head_ref_namespaced(each_ref_fn fn, void *cb_data);
-int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
+int for_each_namespaced_ref(const char **exclude_patterns,
+ each_ref_fn fn, void *cb_data);
/* can be used to learn about broken ref and symref */
int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data);
@@ -405,7 +418,7 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt,
* Write a packed-refs file for the current repository.
* flags: Combination of the above PACK_REFS_* flags.
*/
-int refs_pack_refs(struct ref_store *refs, unsigned int flags);
+int refs_pack_refs(struct ref_store *refs, struct pack_refs_opts *opts);
/*
* Setup reflog before using. Fill in err and return -1 on failure.
@@ -804,7 +817,7 @@ int update_ref(const char *msg, const char *refname,
unsigned int flags, enum action_on_err onerr);
int parse_hide_refs_config(const char *var, const char *value, const char *,
- struct string_list *);
+ struct strvec *);
/*
* Check whether a ref is hidden. If no namespace is set, both the first and
@@ -814,7 +827,7 @@ int parse_hide_refs_config(const char *var, const char *value, const char *,
* the ref is outside that namespace, the first parameter is NULL. The second
* parameter always points to the full ref name.
*/
-int ref_is_hidden(const char *, const char *, const struct string_list *);
+int ref_is_hidden(const char *, const char *, const struct strvec *);
/* Is this a per-worktree ref living in the refs/ namespace? */
int is_per_worktree_ref(const char *refname);
diff --git a/refs/debug.c b/refs/debug.c
index adc34c836f..b7ffc4ce67 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -1,6 +1,7 @@
#include "git-compat-util.h"
#include "hex.h"
#include "refs-internal.h"
+#include "string-list.h"
#include "trace.h"
static struct trace_key trace_refs = TRACE_KEY_INIT(REFS);
@@ -122,10 +123,10 @@ static int debug_initial_transaction_commit(struct ref_store *refs,
return res;
}
-static int debug_pack_refs(struct ref_store *ref_store, unsigned int flags)
+static int debug_pack_refs(struct ref_store *ref_store, struct pack_refs_opts *opts)
{
struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
- int res = drefs->refs->be->pack_refs(drefs->refs, flags);
+ int res = drefs->refs->be->pack_refs(drefs->refs, opts);
trace_printf_key(&trace_refs, "pack_refs: %d\n", res);
return res;
}
@@ -228,11 +229,12 @@ static struct ref_iterator_vtable debug_ref_iterator_vtable = {
static struct ref_iterator *
debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix,
- unsigned int flags)
+ const char **exclude_patterns, unsigned int flags)
{
struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
struct ref_iterator *res =
- drefs->refs->be->iterator_begin(drefs->refs, prefix, flags);
+ drefs->refs->be->iterator_begin(drefs->refs, prefix,
+ exclude_patterns, flags);
struct debug_ref_iterator *diter = xcalloc(1, sizeof(*diter));
base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable, 1);
diter->iter = res;
diff --git a/refs/files-backend.c b/refs/files-backend.c
index d0581ee41a..341354182b 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1,7 +1,9 @@
-#include "../cache.h"
+#include "../git-compat-util.h"
#include "../config.h"
+#include "../copy.h"
#include "../environment.h"
#include "../gettext.h"
+#include "../hash.h"
#include "../hex.h"
#include "../refs.h"
#include "refs-internal.h"
@@ -13,12 +15,15 @@
#include "../lockfile.h"
#include "../object.h"
#include "../object-file.h"
+#include "../path.h"
#include "../dir.h"
#include "../chdir-notify.h"
#include "../setup.h"
#include "../worktree.h"
#include "../wrapper.h"
#include "../write-or-die.h"
+#include "../revision.h"
+#include <wildmatch.h>
/*
* This backend uses the following flags in `ref_update::flags` for
@@ -827,7 +832,8 @@ static struct ref_iterator_vtable files_ref_iterator_vtable = {
static struct ref_iterator *files_ref_iterator_begin(
struct ref_store *ref_store,
- const char *prefix, unsigned int flags)
+ const char *prefix, const char **exclude_patterns,
+ unsigned int flags)
{
struct files_ref_store *refs;
struct ref_iterator *loose_iter, *packed_iter, *overlay_iter;
@@ -872,7 +878,7 @@ static struct ref_iterator *files_ref_iterator_begin(
* the packed and loose references.
*/
packed_iter = refs_ref_iterator_begin(
- refs->packed_ref_store, prefix, 0,
+ refs->packed_ref_store, prefix, exclude_patterns, 0,
DO_FOR_EACH_INCLUDE_BROKEN);
overlay_iter = overlay_ref_iterator_begin(loose_iter, packed_iter);
@@ -1173,17 +1179,15 @@ static void prune_refs(struct files_ref_store *refs, struct ref_to_prune **refs_
*/
static int should_pack_ref(const char *refname,
const struct object_id *oid, unsigned int ref_flags,
- unsigned int pack_flags)
+ struct pack_refs_opts *opts)
{
+ struct string_list_item *item;
+
/* Do not pack per-worktree refs: */
if (parse_worktree_ref(refname, NULL, NULL, NULL) !=
REF_WORKTREE_SHARED)
return 0;
- /* Do not pack non-tags unless PACK_REFS_ALL is set: */
- if (!(pack_flags & PACK_REFS_ALL) && !starts_with(refname, "refs/tags/"))
- return 0;
-
/* Do not pack symbolic refs: */
if (ref_flags & REF_ISSYMREF)
return 0;
@@ -1192,10 +1196,18 @@ static int should_pack_ref(const char *refname,
if (!ref_resolves_to_object(refname, the_repository, oid, ref_flags))
return 0;
- return 1;
+ if (ref_excluded(opts->exclusions, refname))
+ return 0;
+
+ for_each_string_list_item(item, opts->includes)
+ if (!wildmatch(item->string, refname, 0))
+ return 1;
+
+ return 0;
}
-static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
+static int files_pack_refs(struct ref_store *ref_store,
+ struct pack_refs_opts *opts)
{
struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB,
@@ -1220,8 +1232,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
* in the packed ref cache. If the reference should be
* pruned, also add it to refs_to_prune.
*/
- if (!should_pack_ref(iter->refname, iter->oid, iter->flags,
- flags))
+ if (!should_pack_ref(iter->refname, iter->oid, iter->flags, opts))
continue;
/*
@@ -1235,7 +1246,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
iter->refname, err.buf);
/* Schedule the loose reference for pruning if requested. */
- if ((flags & PACK_REFS_PRUNE)) {
+ if ((opts->flags & PACK_REFS_PRUNE)) {
struct ref_to_prune *n;
FLEX_ALLOC_STR(n, name, iter->refname);
oidcpy(&n->oid, iter->oid);
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index 34c0c4e20f..f909639c7d 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1,7 +1,8 @@
-#include "../cache.h"
+#include "../git-compat-util.h"
#include "../alloc.h"
#include "../config.h"
#include "../gettext.h"
+#include "../hash.h"
#include "../hex.h"
#include "../refs.h"
#include "refs-internal.h"
@@ -9,8 +10,10 @@
#include "../iterator.h"
#include "../lockfile.h"
#include "../chdir-notify.h"
+#include "../statinfo.h"
#include "../wrapper.h"
#include "../write-or-die.h"
+#include "../trace2.h"
enum mmap_strategy {
/*
@@ -302,7 +305,8 @@ static int cmp_packed_ref_records(const void *v1, const void *v2)
* Compare a snapshot record at `rec` to the specified NUL-terminated
* refname.
*/
-static int cmp_record_to_refname(const char *rec, const char *refname)
+static int cmp_record_to_refname(const char *rec, const char *refname,
+ int start)
{
const char *r1 = rec + the_hash_algo->hexsz + 1;
const char *r2 = refname;
@@ -311,7 +315,7 @@ static int cmp_record_to_refname(const char *rec, const char *refname)
if (*r1 == '\n')
return *r2 ? -1 : 0;
if (!*r2)
- return 1;
+ return start ? 1 : -1;
if (*r1 != *r2)
return (unsigned char)*r1 < (unsigned char)*r2 ? -1 : +1;
r1++;
@@ -526,22 +530,9 @@ static int load_contents(struct snapshot *snapshot)
return 1;
}
-/*
- * Find the place in `snapshot->buf` where the start of the record for
- * `refname` starts. If `mustexist` is true and the reference doesn't
- * exist, then return NULL. If `mustexist` is false and the reference
- * doesn't exist, then return the point where that reference would be
- * inserted, or `snapshot->eof` (which might be NULL) if it would be
- * inserted at the end of the file. In the latter mode, `refname`
- * doesn't have to be a proper reference name; for example, one could
- * search for "refs/replace/" to find the start of any replace
- * references.
- *
- * The record is sought using a binary search, so `snapshot->buf` must
- * be sorted.
- */
-static const char *find_reference_location(struct snapshot *snapshot,
- const char *refname, int mustexist)
+static const char *find_reference_location_1(struct snapshot *snapshot,
+ const char *refname, int mustexist,
+ int start)
{
/*
* This is not *quite* a garden-variety binary search, because
@@ -571,7 +562,7 @@ static const char *find_reference_location(struct snapshot *snapshot,
mid = lo + (hi - lo) / 2;
rec = find_start_of_record(lo, mid);
- cmp = cmp_record_to_refname(rec, refname);
+ cmp = cmp_record_to_refname(rec, refname, start);
if (cmp < 0) {
lo = find_end_of_record(mid, hi);
} else if (cmp > 0) {
@@ -588,6 +579,41 @@ static const char *find_reference_location(struct snapshot *snapshot,
}
/*
+ * Find the place in `snapshot->buf` where the start of the record for
+ * `refname` starts. If `mustexist` is true and the reference doesn't
+ * exist, then return NULL. If `mustexist` is false and the reference
+ * doesn't exist, then return the point where that reference would be
+ * inserted, or `snapshot->eof` (which might be NULL) if it would be
+ * inserted at the end of the file. In the latter mode, `refname`
+ * doesn't have to be a proper reference name; for example, one could
+ * search for "refs/replace/" to find the start of any replace
+ * references.
+ *
+ * The record is sought using a binary search, so `snapshot->buf` must
+ * be sorted.
+ */
+static const char *find_reference_location(struct snapshot *snapshot,
+ const char *refname, int mustexist)
+{
+ return find_reference_location_1(snapshot, refname, mustexist, 1);
+}
+
+/*
+ * Find the place in `snapshot->buf` after the end of the record for
+ * `refname`. In other words, find the location of first thing *after*
+ * `refname`.
+ *
+ * Other semantics are identical to the ones in
+ * `find_reference_location()`.
+ */
+static const char *find_reference_location_end(struct snapshot *snapshot,
+ const char *refname,
+ int mustexist)
+{
+ return find_reference_location_1(snapshot, refname, mustexist, 0);
+}
+
+/*
* Create a newly-allocated `snapshot` of the `packed-refs` file in
* its current state and return it. The return value will already have
* its reference count incremented.
@@ -778,6 +804,13 @@ struct packed_ref_iterator {
/* The end of the part of the buffer that will be iterated over: */
const char *eof;
+ struct jump_list_entry {
+ const char *start;
+ const char *end;
+ } *jump;
+ size_t jump_nr, jump_alloc;
+ size_t jump_pos;
+
/* Scratch space for current values: */
struct object_id oid, peeled;
struct strbuf refname_buf;
@@ -795,14 +828,35 @@ struct packed_ref_iterator {
*/
static int next_record(struct packed_ref_iterator *iter)
{
- const char *p = iter->pos, *eol;
+ const char *p, *eol;
strbuf_reset(&iter->refname_buf);
+ /*
+ * If iter->pos is contained within a skipped region, jump past
+ * it.
+ *
+ * Note that each skipped region is considered at most once,
+ * since they are ordered based on their starting position.
+ */
+ while (iter->jump_pos < iter->jump_nr) {
+ struct jump_list_entry *curr = &iter->jump[iter->jump_pos];
+ if (iter->pos < curr->start)
+ break; /* not to the next jump yet */
+
+ iter->jump_pos++;
+ if (iter->pos < curr->end) {
+ iter->pos = curr->end;
+ trace2_counter_add(TRACE2_COUNTER_ID_PACKED_REFS_JUMPS, 1);
+ break;
+ }
+ }
+
if (iter->pos == iter->eof)
return ITER_DONE;
iter->base.flags = REF_ISPACKED;
+ p = iter->pos;
if (iter->eof - p < the_hash_algo->hexsz + 2 ||
parse_oid_hex(p, &iter->oid, &p) ||
@@ -910,6 +964,7 @@ static int packed_ref_iterator_abort(struct ref_iterator *ref_iterator)
int ok = ITER_DONE;
strbuf_release(&iter->refname_buf);
+ free(iter->jump);
release_snapshot(iter->snapshot);
base_ref_iterator_free(ref_iterator);
return ok;
@@ -921,9 +976,135 @@ static struct ref_iterator_vtable packed_ref_iterator_vtable = {
.abort = packed_ref_iterator_abort
};
+static int jump_list_entry_cmp(const void *va, const void *vb)
+{
+ const struct jump_list_entry *a = va;
+ const struct jump_list_entry *b = vb;
+
+ if (a->start < b->start)
+ return -1;
+ if (a->start > b->start)
+ return 1;
+ return 0;
+}
+
+static int has_glob_special(const char *str)
+{
+ const char *p;
+ for (p = str; *p; p++) {
+ if (is_glob_special(*p))
+ return 1;
+ }
+ return 0;
+}
+
+static const char *ptr_max(const char *x, const char *y)
+{
+ if (x > y)
+ return x;
+ return y;
+}
+
+static void populate_excluded_jump_list(struct packed_ref_iterator *iter,
+ struct snapshot *snapshot,
+ const char **excluded_patterns)
+{
+ size_t i, j;
+ const char **pattern;
+ struct jump_list_entry *last_disjoint;
+
+ if (!excluded_patterns)
+ return;
+
+ for (pattern = excluded_patterns; *pattern; pattern++) {
+ /*
+ * We also can't feed any excludes from hidden refs
+ * config sections, since later rules may override
+ * previous ones. For example, with rules "refs/foo" and
+ * "!refs/foo/bar", we should show "refs/foo/bar" (and
+ * everything underneath it), but the earlier exclusion
+ * would cause us to skip all of "refs/foo". We likewise
+ * don't implement the namespace stripping required for
+ * '^' rules.
+ *
+ * Both are possible to do, but complicated, so avoid
+ * populating the jump list at all if we see either of
+ * these patterns.
+ */
+ if (**pattern == '!' || **pattern == '^')
+ return;
+ }
+
+ for (pattern = excluded_patterns; *pattern; pattern++) {
+ struct jump_list_entry *e;
+
+ /*
+ * We can't feed any excludes with globs in them to the
+ * refs machinery. It only understands prefix matching.
+ * We likewise can't even feed the string leading up to
+ * the first meta-character, as something like "foo[a]"
+ * should not exclude "foobar" (but the prefix "foo"
+ * would match that and mark it for exclusion).
+ */
+ if (has_glob_special(*pattern))
+ continue;
+
+ ALLOC_GROW(iter->jump, iter->jump_nr + 1, iter->jump_alloc);
+
+ e = &iter->jump[iter->jump_nr++];
+ e->start = find_reference_location(snapshot, *pattern, 0);
+ e->end = find_reference_location_end(snapshot, *pattern, 0);
+ }
+
+ if (!iter->jump_nr) {
+ /*
+ * Every entry in exclude_patterns has a meta-character,
+ * nothing to do here.
+ */
+ return;
+ }
+
+ QSORT(iter->jump, iter->jump_nr, jump_list_entry_cmp);
+
+ /*
+ * As an optimization, merge adjacent entries in the jump list
+ * to jump forwards as far as possible when entering a skipped
+ * region.
+ *
+ * For example, if we have two skipped regions:
+ *
+ * [[A, B], [B, C]]
+ *
+ * we want to combine that into a single entry jumping from A to
+ * C.
+ */
+ last_disjoint = iter->jump;
+
+ for (i = 1, j = 1; i < iter->jump_nr; i++) {
+ struct jump_list_entry *ours = &iter->jump[i];
+
+ if (ours->start == ours->end) {
+ /* ignore empty regions (no matching entries) */
+ continue;
+ } else if (ours->start <= last_disjoint->end) {
+ /* overlapping regions extend the previous one */
+ last_disjoint->end = ptr_max(last_disjoint->end, ours->end);
+ } else {
+ /* otherwise, insert a new region */
+ iter->jump[j++] = *ours;
+ last_disjoint = ours;
+
+ }
+ }
+
+ iter->jump_nr = j;
+ iter->jump_pos = 0;
+}
+
static struct ref_iterator *packed_ref_iterator_begin(
struct ref_store *ref_store,
- const char *prefix, unsigned int flags)
+ const char *prefix, const char **exclude_patterns,
+ unsigned int flags)
{
struct packed_ref_store *refs;
struct snapshot *snapshot;
@@ -955,6 +1136,9 @@ static struct ref_iterator *packed_ref_iterator_begin(
ref_iterator = &iter->base;
base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable, 1);
+ if (exclude_patterns)
+ populate_excluded_jump_list(iter, snapshot, exclude_patterns);
+
iter->snapshot = snapshot;
acquire_snapshot(snapshot);
@@ -1148,7 +1332,7 @@ static int write_with_updates(struct packed_ref_store *refs,
* list of refs is exhausted, set iter to NULL. When the list
* of updates is exhausted, leave i set to updates->nr.
*/
- iter = packed_ref_iterator_begin(&refs->base, "",
+ iter = packed_ref_iterator_begin(&refs->base, "", NULL,
DO_FOR_EACH_INCLUDE_BROKEN);
if ((ok = ref_iterator_advance(iter)) != ITER_OK)
iter = NULL;
@@ -1576,7 +1760,7 @@ static int packed_delete_refs(struct ref_store *ref_store, const char *msg,
}
static int packed_pack_refs(struct ref_store *ref_store UNUSED,
- unsigned int flags UNUSED)
+ struct pack_refs_opts *pack_opts UNUSED)
{
/*
* Packed refs are already packed. It might be that loose refs
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index dc1ca49c85..2294c4564f 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -1,6 +1,8 @@
#include "../git-compat-util.h"
#include "../alloc.h"
+#include "../hash.h"
#include "../refs.h"
+#include "../repository.h"
#include "refs-internal.h"
#include "ref-cache.h"
#include "../iterator.h"
diff --git a/refs/ref-cache.h b/refs/ref-cache.h
index cf4ad9070b..95c76e27c8 100644
--- a/refs/ref-cache.h
+++ b/refs/ref-cache.h
@@ -1,10 +1,11 @@
#ifndef REFS_REF_CACHE_H
#define REFS_REF_CACHE_H
-#include "hash.h"
+#include "hash-ll.h"
struct ref_dir;
struct ref_store;
+struct repository;
/*
* If this ref_cache is filled lazily, this function is used to load
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index a85d113123..9db8aec4da 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -367,8 +367,8 @@ int is_empty_ref_iterator(struct ref_iterator *ref_iterator);
*/
struct ref_iterator *refs_ref_iterator_begin(
struct ref_store *refs,
- const char *prefix, int trim,
- enum do_for_each_ref_flags flags);
+ const char *prefix, const char **exclude_patterns,
+ int trim, enum do_for_each_ref_flags flags);
/*
* A callback function used to instruct merge_ref_iterator how to
@@ -547,7 +547,8 @@ typedef int ref_transaction_commit_fn(struct ref_store *refs,
struct ref_transaction *transaction,
struct strbuf *err);
-typedef int pack_refs_fn(struct ref_store *ref_store, unsigned int flags);
+typedef int pack_refs_fn(struct ref_store *ref_store,
+ struct pack_refs_opts *opts);
typedef int create_symref_fn(struct ref_store *ref_store,
const char *ref_target,
const char *refs_heads_master,
@@ -570,7 +571,8 @@ typedef int copy_ref_fn(struct ref_store *ref_store,
*/
typedef struct ref_iterator *ref_iterator_begin_fn(
struct ref_store *ref_store,
- const char *prefix, unsigned int flags);
+ const char *prefix, const char **exclude_patterns,
+ unsigned int flags);
/* reflog functions */
diff --git a/refspec.c b/refspec.c
index 7b5c305514..57f6c2aaf9 100644
--- a/refspec.c
+++ b/refspec.c
@@ -1,10 +1,12 @@
#include "git-compat-util.h"
#include "alloc.h"
#include "gettext.h"
+#include "hash.h"
#include "hex.h"
#include "strvec.h"
#include "refs.h"
#include "refspec.h"
+#include "strbuf.h"
static struct refspec_item s_tag_refspec = {
.force = 0,
diff --git a/reftable/dump.c b/reftable/dump.c
index 155953d1b8..ce936b4e18 100644
--- a/reftable/dump.c
+++ b/reftable/dump.c
@@ -7,7 +7,7 @@ https://developers.google.com/open-source/licenses/bsd
*/
#include "git-compat-util.h"
-#include "hash.h"
+#include "hash-ll.h"
#include "reftable-blocksource.h"
#include "reftable-error.h"
diff --git a/reftable/error.c b/reftable/error.c
index 93941f2145..0d1766735e 100644
--- a/reftable/error.c
+++ b/reftable/error.c
@@ -6,6 +6,7 @@ license that can be found in the LICENSE file or at
https://developers.google.com/open-source/licenses/bsd
*/
+#include "system.h"
#include "reftable-error.h"
#include <stdio.h>
diff --git a/reftable/publicbasics.c b/reftable/publicbasics.c
index 0ad7d5c0ff..bcb82530d6 100644
--- a/reftable/publicbasics.c
+++ b/reftable/publicbasics.c
@@ -6,10 +6,10 @@ license that can be found in the LICENSE file or at
https://developers.google.com/open-source/licenses/bsd
*/
+#include "system.h"
#include "reftable-malloc.h"
#include "basics.h"
-#include "system.h"
static void *(*reftable_malloc_ptr)(size_t sz);
static void *(*reftable_realloc_ptr)(void *, size_t);
diff --git a/reftable/system.h b/reftable/system.h
index 18f9207dfe..6b74a81514 100644
--- a/reftable/system.h
+++ b/reftable/system.h
@@ -13,7 +13,7 @@ https://developers.google.com/open-source/licenses/bsd
#include "git-compat-util.h"
#include "strbuf.h"
-#include "hash.h" /* hash ID, sizes.*/
+#include "hash-ll.h" /* hash ID, sizes.*/
#include "dir.h" /* remove_dir_recursively, for tests.*/
int hash_size(uint32_t id);
diff --git a/reftable/tree.c b/reftable/tree.c
index b8899e060a..a5bf880985 100644
--- a/reftable/tree.c
+++ b/reftable/tree.c
@@ -6,10 +6,10 @@ license that can be found in the LICENSE file or at
https://developers.google.com/open-source/licenses/bsd
*/
+#include "system.h"
#include "tree.h"
#include "basics.h"
-#include "system.h"
struct tree_node *tree_search(void *key, struct tree_node **rootp,
int (*compare)(const void *, const void *),
diff --git a/reftable/tree_test.c b/reftable/tree_test.c
index cbff125588..ac3a045ad4 100644
--- a/reftable/tree_test.c
+++ b/reftable/tree_test.c
@@ -6,6 +6,7 @@ license that can be found in the LICENSE file or at
https://developers.google.com/open-source/licenses/bsd
*/
+#include "system.h"
#include "tree.h"
#include "basics.h"
diff --git a/remote.c b/remote.c
index 0764fca0db..6538b6037d 100644
--- a/remote.c
+++ b/remote.c
@@ -10,7 +10,8 @@
#include "refs.h"
#include "refspec.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "path.h"
#include "commit.h"
#include "diff.h"
#include "revision.h"
diff --git a/remote.h b/remote.h
index 73638cefeb..929c7c676d 100644
--- a/remote.h
+++ b/remote.h
@@ -1,6 +1,7 @@
#ifndef REMOTE_H
#define REMOTE_H
+#include "hash-ll.h"
#include "hashmap.h"
#include "refspec.h"
diff --git a/replace-object.c b/replace-object.c
index e98825d585..ca4017635d 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.h"
+#include "object-store-ll.h"
#include "replace-object.h"
#include "refs.h"
#include "repository.h"
diff --git a/replace-object.h b/replace-object.h
index 500482b02b..a964db1670 100644
--- a/replace-object.h
+++ b/replace-object.h
@@ -3,7 +3,7 @@
#include "oidmap.h"
#include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
/*
* Do replace refs need to be checked this run? This variable is
diff --git a/repo-settings.c b/repo-settings.c
index d220c5dd9f..7b566d729d 100644
--- a/repo-settings.c
+++ b/repo-settings.c
@@ -41,8 +41,10 @@ void prepare_repo_settings(struct repository *r)
repo_cfg_bool(r, "feature.experimental", &experimental, 0);
/* Defaults modified by feature.* */
- if (experimental)
+ if (experimental) {
r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_SKIPPING;
+ r->settings.pack_use_bitmap_boundary_traversal = 1;
+ }
if (manyfiles) {
r->settings.index_version = 4;
r->settings.index_skip_hash = 1;
@@ -62,6 +64,9 @@ void prepare_repo_settings(struct repository *r)
repo_cfg_bool(r, "index.sparse", &r->settings.sparse_index, 0);
repo_cfg_bool(r, "index.skiphash", &r->settings.index_skip_hash, r->settings.index_skip_hash);
repo_cfg_bool(r, "pack.readreverseindex", &r->settings.pack_read_reverse_index, 1);
+ repo_cfg_bool(r, "pack.usebitmapboundarytraversal",
+ &r->settings.pack_use_bitmap_boundary_traversal,
+ r->settings.pack_use_bitmap_boundary_traversal);
/*
* The GIT_TEST_MULTI_PACK_INDEX variable is special in that
diff --git a/repository.c b/repository.c
index c53e480e32..8c77b7ed23 100644
--- a/repository.c
+++ b/repository.c
@@ -3,13 +3,15 @@
* declaration matches the definition in this file.
*/
#define USE_THE_INDEX_VARIABLE
-#include "cache.h"
+#include "git-compat-util.h"
#include "abspath.h"
#include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "config.h"
#include "object.h"
#include "lockfile.h"
+#include "path.h"
+#include "read-cache-ll.h"
#include "remote.h"
#include "setup.h"
#include "submodule-config.h"
diff --git a/repository.h b/repository.h
index 1a13ff2867..5dbf8b0374 100644
--- a/repository.h
+++ b/repository.h
@@ -1,8 +1,6 @@
#ifndef REPOSITORY_H
#define REPOSITORY_H
-#include "path.h"
-
struct config_set;
struct fsmonitor_settings;
struct git_hash_algo;
@@ -37,6 +35,7 @@ struct repo_settings {
int command_requires_full_index;
int sparse_index;
int pack_read_reverse_index;
+ int pack_use_bitmap_boundary_traversal;
struct fsmonitor_settings *fsmonitor; /* lazily loaded */
@@ -169,6 +168,9 @@ struct repository {
};
extern struct repository *the_repository;
+#ifdef USE_THE_INDEX_VARIABLE
+extern struct index_state the_index;
+#endif
/*
* Define a custom repository layout. Any field can be NULL, which
@@ -220,9 +222,6 @@ int repo_hold_locked_index(struct repository *repo,
struct lock_file *lf,
int flags);
-int repo_read_index_preload(struct repository *,
- const struct pathspec *pathspec,
- unsigned refresh_flags);
int repo_read_index_unmerged(struct repository *);
/*
* Opportunistically update the index but do not complain if we can't.
diff --git a/rerere.c b/rerere.c
index 7abc94bf44..e2b8597f88 100644
--- a/rerere.c
+++ b/rerere.c
@@ -1,20 +1,23 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "abspath.h"
#include "alloc.h"
#include "config.h"
+#include "copy.h"
#include "gettext.h"
#include "hex.h"
#include "lockfile.h"
#include "string-list.h"
+#include "read-cache-ll.h"
#include "rerere.h"
#include "xdiff-interface.h"
#include "dir.h"
#include "resolve-undo.h"
-#include "ll-merge.h"
+#include "merge-ll.h"
#include "attr.h"
+#include "path.h"
#include "pathspec.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "hash-lookup.h"
#include "strmap.h"
#include "wrapper.h"
diff --git a/reset.h b/reset.h
index a28f81829d..10708d8ddc 100644
--- a/reset.h
+++ b/reset.h
@@ -1,7 +1,7 @@
#ifndef RESET_H
#define RESET_H
-#include "hash.h"
+#include "hash-ll.h"
#include "repository.h"
#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
diff --git a/resolve-undo.c b/resolve-undo.c
index e81096e2d4..7817f5d6db 100644
--- a/resolve-undo.c
+++ b/resolve-undo.c
@@ -1,6 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "dir.h"
+#include "hash.h"
+#include "read-cache.h"
#include "resolve-undo.h"
+#include "sparse-index.h"
#include "string-list.h"
/* The only error case is to run out of memory in string-list */
diff --git a/resolve-undo.h b/resolve-undo.h
index d1ea972771..c5deafc92f 100644
--- a/resolve-undo.h
+++ b/resolve-undo.h
@@ -6,7 +6,7 @@ struct index_state;
struct pathspec;
struct string_list;
-#include "hash.h"
+#include "hash-ll.h"
struct resolve_undo_info {
unsigned int mode[3];
diff --git a/revision.c b/revision.c
index b33cc1d106..c53e9f84ae 100644
--- a/revision.c
+++ b/revision.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "alloc.h"
#include "config.h"
#include "environment.h"
@@ -6,7 +6,7 @@
#include "hex.h"
#include "object-name.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "tag.h"
#include "blob.h"
#include "tree.h"
@@ -31,7 +31,9 @@
#include "bisect.h"
#include "packfile.h"
#include "worktree.h"
+#include "read-cache.h"
#include "setup.h"
+#include "sparse-index.h"
#include "strvec.h"
#include "trace2.h"
#include "commit-reach.h"
@@ -44,6 +46,7 @@
#include "list-objects-filter-options.h"
#include "resolve-undo.h"
#include "parse-options.h"
+#include "wildmatch.h"
volatile show_early_output_fn_t show_early_output;
@@ -1558,7 +1561,7 @@ void init_ref_exclusions(struct ref_exclusions *exclusions)
void clear_ref_exclusions(struct ref_exclusions *exclusions)
{
string_list_clear(&exclusions->excluded_refs, 0);
- string_list_clear(&exclusions->hidden_refs, 0);
+ strvec_clear(&exclusions->hidden_refs);
exclusions->hidden_refs_configured = 0;
}
@@ -2670,7 +2673,7 @@ static int for_each_bisect_ref(struct ref_store *refs, each_ref_fn fn,
struct strbuf bisect_refs = STRBUF_INIT;
int status;
strbuf_addf(&bisect_refs, "refs/bisect/%s", term);
- status = refs_for_each_fullref_in(refs, bisect_refs.buf, fn, cb_data);
+ status = refs_for_each_fullref_in(refs, bisect_refs.buf, NULL, fn, cb_data);
strbuf_release(&bisect_refs);
return status;
}
diff --git a/revision.h b/revision.h
index e8f6de9684..7f219cde62 100644
--- a/revision.h
+++ b/revision.h
@@ -7,8 +7,10 @@
#include "pretty.h"
#include "diff.h"
#include "commit-slab-decl.h"
+#include "decorate.h"
#include "ident.h"
#include "list-objects-filter-options.h"
+#include "strvec.h"
/**
* The revision walking API offers functions to build a list of revisions
@@ -86,7 +88,7 @@ struct rev_cmdline_info {
struct ref_exclusions {
/*
* Excluded refs is a list of wildmatch patterns. If any of the
- * patterns matches, the reference will be excluded.
+ * patterns match, the reference will be excluded.
*/
struct string_list excluded_refs;
@@ -94,7 +96,7 @@ struct ref_exclusions {
* Hidden refs is a list of patterns that is to be hidden via
* `ref_is_hidden()`.
*/
- struct string_list hidden_refs;
+ struct strvec hidden_refs;
/*
* Indicates whether hidden refs have been configured. This is to
@@ -109,7 +111,7 @@ struct ref_exclusions {
*/
#define REF_EXCLUSIONS_INIT { \
.excluded_refs = STRING_LIST_INIT_DUP, \
- .hidden_refs = STRING_LIST_INIT_DUP, \
+ .hidden_refs = STRVEC_INIT, \
}
struct oidset;
diff --git a/run-command.c b/run-command.c
index e64bb08a5b..3e8b268ca2 100644
--- a/run-command.c
+++ b/run-command.c
@@ -1,10 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "run-command.h"
#include "environment.h"
#include "exec-cmd.h"
#include "gettext.h"
#include "sigchain.h"
#include "strvec.h"
+#include "symlinks.h"
#include "thread-utils.h"
#include "strbuf.h"
#include "string-list.h"
@@ -15,6 +16,7 @@
#include "packfile.h"
#include "hook.h"
#include "compat/nonblock.h"
+#include "alloc.h"
void child_process_init(struct child_process *child)
{
@@ -1530,6 +1532,11 @@ static void pp_init(struct parallel_processes *pp,
if (!opts->get_next_task)
BUG("you need to specify a get_next_task function");
+ if (opts->ungroup) {
+ if (opts->on_stderr_output)
+ BUG("on_stderr_output and ungroup are incompatible with each other");
+ }
+
CALLOC_ARRAY(pp->children, n);
if (!opts->ungroup)
CALLOC_ARRAY(pp->pfd, n);
@@ -1654,14 +1661,19 @@ static void pp_buffer_stderr(struct parallel_processes *pp,
for (size_t i = 0; i < opts->processes; i++) {
if (pp->children[i].state == GIT_CP_WORKING &&
pp->pfd[i].revents & (POLLIN | POLLHUP)) {
- int n = strbuf_read_once(&pp->children[i].err,
- pp->children[i].process.err, 0);
+ ssize_t n = strbuf_read_once(&pp->children[i].err,
+ pp->children[i].process.err, 0);
if (n == 0) {
close(pp->children[i].process.err);
pp->children[i].state = GIT_CP_WAIT_CLEANUP;
- } else if (n < 0)
+ } else if (n < 0) {
if (errno != EAGAIN)
die_errno("read");
+ } else if (opts->on_stderr_output) {
+ opts->on_stderr_output(&pp->children[i].err,
+ pp->children[i].err.len - n,
+ opts->data, pp->children[i].data);
+ }
}
}
}
diff --git a/run-command.h b/run-command.h
index 072db56a4d..fc3e6a2287 100644
--- a/run-command.h
+++ b/run-command.h
@@ -409,6 +409,25 @@ typedef int (*start_failure_fn)(struct strbuf *out,
void *pp_task_cb);
/**
+ * This callback is called whenever output from a child process is buffered
+ *
+ * See run_processes_parallel() below for a discussion of the "struct
+ * strbuf *out" parameter.
+ *
+ * The offset refers to the number of bytes originally in "out" before
+ * the output from the child process was buffered. Therefore, the buffer
+ * range, "out + buf" to the end of "out", would contain the buffer of
+ * the child process output.
+ *
+ * pp_cb is the callback cookie as passed into run_processes_parallel,
+ * pp_task_cb is the callback cookie as passed into get_next_task_fn.
+ *
+ * This function is incompatible with "ungroup"
+ */
+typedef void (*on_stderr_output_fn)(struct strbuf *out, size_t offset,
+ void *pp_cb, void *pp_task_cb);
+
+/**
* This callback is called on every child process that finished processing.
*
* See run_processes_parallel() below for a discussion of the "struct
@@ -462,6 +481,12 @@ struct run_process_parallel_opts
start_failure_fn start_failure;
/**
+ * on_stderr_output: See on_stderr_output_fn() above. Unless you need
+ * to capture output from child processes, leave this as NULL.
+ */
+ on_stderr_output_fn on_stderr_output;
+
+ /**
* task_finished: See task_finished_fn() above. This can be
* NULL to omit any special handling.
*/
@@ -503,7 +528,7 @@ void run_processes_parallel(const struct run_process_parallel_opts *opts);
* exception of GIT_CONFIG_PARAMETERS and GIT_CONFIG_COUNT (which cause the
* corresponding environment variables to be unset in the subprocess) and adds
* an environment variable pointing to new_git_dir. See local_repo_env in
- * cache.h for more information.
+ * environment.h for more information.
*/
void prepare_other_repo_env(struct strvec *env, const char *new_git_dir);
@@ -564,4 +589,6 @@ enum start_bg_result start_bg_command(struct child_process *cmd,
void *cb_data,
unsigned int timeout_sec);
+int sane_execvp(const char *file, char *const argv[]);
+
#endif
diff --git a/send-pack.c b/send-pack.c
index 2089143555..9510bef856 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -1,10 +1,11 @@
#include "git-compat-util.h"
#include "config.h"
#include "commit.h"
+#include "date.h"
#include "gettext.h"
#include "hex.h"
#include "refs.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "pkt-line.h"
#include "sideband.h"
#include "run-command.h"
diff --git a/sequencer.c b/sequencer.c
index c88d1d9553..19a1eb603f 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1,8 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "abspath.h"
#include "advice.h"
#include "alloc.h"
#include "config.h"
+#include "copy.h"
#include "environment.h"
#include "gettext.h"
#include "hex.h"
@@ -10,7 +11,7 @@
#include "dir.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "object.h"
#include "pager.h"
#include "commit.h"
@@ -22,11 +23,14 @@
#include "utf8.h"
#include "cache-tree.h"
#include "diff.h"
+#include "path.h"
#include "revision.h"
#include "rerere.h"
+#include "merge.h"
#include "merge-ort.h"
#include "merge-ort-wrappers.h"
#include "refs.h"
+#include "sparse-index.h"
#include "strvec.h"
#include "quote.h"
#include "trailer.h"
@@ -137,6 +141,11 @@ static GIT_PATH_FUNC(rebase_path_amend, "rebase-merge/amend")
*/
static GIT_PATH_FUNC(rebase_path_stopped_sha, "rebase-merge/stopped-sha")
/*
+ * When we stop for the user to resolve conflicts this file contains
+ * the patch of the commit that is being picked.
+ */
+static GIT_PATH_FUNC(rebase_path_patch, "rebase-merge/patch")
+/*
* For the post-rewrite hook, we make a list of rewritten commits and
* their new sha1s. The rewritten-pending list keeps the sha1s of
* commits that have been processed, but not committed yet,
@@ -659,11 +668,12 @@ void append_conflicts_hint(struct index_state *istate,
}
strbuf_addch(msgbuf, '\n');
- strbuf_commented_addf(msgbuf, "Conflicts:\n");
+ strbuf_commented_addf(msgbuf, comment_line_char, "Conflicts:\n");
for (i = 0; i < istate->cache_nr;) {
const struct cache_entry *ce = istate->cache[i++];
if (ce_stage(ce)) {
- strbuf_commented_addf(msgbuf, "\t%s\n", ce->name);
+ strbuf_commented_addf(msgbuf, comment_line_char,
+ "\t%s\n", ce->name);
while (i < istate->cache_nr &&
!strcmp(ce->name, istate->cache[i]->name))
i++;
@@ -1054,7 +1064,7 @@ static int run_git_commit(const char *defmsg,
if (is_rebase_i(opts) &&
((opts->committer_date_is_author_date && !opts->ignore_date) ||
- !(!defmsg && (flags & AMEND_MSG))) &&
+ !(flags & AMEND_MSG)) &&
read_env_script(&cmd.env)) {
const char *gpg_opt = gpg_sign_opt_quoted(opts);
@@ -1142,7 +1152,8 @@ void cleanup_message(struct strbuf *msgbuf,
cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
strbuf_setlen(msgbuf, wt_status_locate_end(msgbuf->buf, msgbuf->len));
if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
- strbuf_stripspace(msgbuf, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
+ strbuf_stripspace(msgbuf,
+ cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0');
}
/*
@@ -1173,7 +1184,8 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
return 0;
- strbuf_stripspace(&tmpl, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
+ strbuf_stripspace(&tmpl,
+ cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0');
if (!skip_prefix(sb->buf, tmpl.buf, &start))
start = sb->buf;
strbuf_release(&tmpl);
@@ -1545,7 +1557,8 @@ static int try_to_commit(struct repository *r,
cleanup = opts->default_msg_cleanup;
if (cleanup != COMMIT_MSG_CLEANUP_NONE)
- strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
+ strbuf_stripspace(msg,
+ cleanup == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0');
if ((flags & EDIT_MSG) && message_is_empty(msg, cleanup)) {
res = 1; /* run 'git commit' to display error message */
goto out;
@@ -1839,7 +1852,7 @@ static void add_commented_lines(struct strbuf *buf, const void *str, size_t len)
s += count;
len -= count;
}
- strbuf_add_commented_lines(buf, s, len);
+ strbuf_add_commented_lines(buf, s, len, comment_line_char);
}
/* Does the current fixup chain contain a squash command? */
@@ -1938,7 +1951,7 @@ static int append_squash_message(struct strbuf *buf, const char *body,
strbuf_addf(buf, _(nth_commit_msg_fmt),
++opts->current_fixup_count + 1);
strbuf_addstr(buf, "\n\n");
- strbuf_add_commented_lines(buf, body, commented_len);
+ strbuf_add_commented_lines(buf, body, commented_len, comment_line_char);
/* buf->buf may be reallocated so store an offset into the buffer */
fixup_off = buf->len;
strbuf_addstr(buf, body + commented_len);
@@ -2028,7 +2041,8 @@ static int update_squash_messages(struct repository *r,
_(first_commit_msg_str));
strbuf_addstr(&buf, "\n\n");
if (is_fixup_flag(command, flag))
- strbuf_add_commented_lines(&buf, body, strlen(body));
+ strbuf_add_commented_lines(&buf, body, strlen(body),
+ comment_line_char);
else
strbuf_addstr(&buf, body);
@@ -2047,7 +2061,8 @@ static int update_squash_messages(struct repository *r,
strbuf_addf(&buf, _(skip_nth_commit_msg_fmt),
++opts->current_fixup_count + 1);
strbuf_addstr(&buf, "\n\n");
- strbuf_add_commented_lines(&buf, body, strlen(body));
+ strbuf_add_commented_lines(&buf, body, strlen(body),
+ comment_line_char);
} else
return error(_("unknown command: %d"), command);
repo_unuse_commit_buffer(r, commit, message);
@@ -2215,8 +2230,6 @@ static int do_pick_commit(struct repository *r,
if (opts->allow_ff && !is_fixup(command) &&
((parent && oideq(&parent->object.oid, &head)) ||
(!parent && unborn))) {
- if (is_rebase_i(opts))
- write_author_script(msg.message);
res = fast_forward_to(r, &commit->object.oid, &head, unborn,
opts);
if (res || command != TODO_REWORD)
@@ -2240,6 +2253,8 @@ static int do_pick_commit(struct repository *r,
*/
if (command == TODO_REVERT) {
+ const char *orig_subject;
+
base = commit;
base_label = msg.label;
next = parent;
@@ -2247,6 +2262,18 @@ static int do_pick_commit(struct repository *r,
if (opts->commit_use_reference) {
strbuf_addstr(&msgbuf,
"# *** SAY WHY WE ARE REVERTING ON THE TITLE LINE ***");
+ } else if (skip_prefix(msg.subject, "Revert \"", &orig_subject)) {
+ if (skip_prefix(orig_subject, "Revert \"", &orig_subject)) {
+ /*
+ * This prevents the generation of somewhat unintuitive (even if
+ * not incorrect) 'Reapply "Revert "' titles from legacy double
+ * reverts. Fixing up deeper recursions is left to the user.
+ */
+ strbuf_addstr(&msgbuf, "Revert \"Reapply \"");
+ } else {
+ strbuf_addstr(&msgbuf, "Reapply \"");
+ }
+ strbuf_addstr(&msgbuf, orig_subject);
} else {
strbuf_addstr(&msgbuf, "Revert \"");
strbuf_addstr(&msgbuf, msg.subject);
@@ -2323,9 +2350,10 @@ static int do_pick_commit(struct repository *r,
command == TODO_REVERT) {
res = do_recursive_merge(r, base, next, base_label, next_label,
&head, &msgbuf, opts);
- if (res < 0)
+ if (res < 0) {
+ unlink(rebase_path_author_script());
goto leave;
-
+ }
res |= write_message(msgbuf.buf, msgbuf.len,
git_path_merge_msg(r), 0);
} else {
@@ -2476,7 +2504,6 @@ void todo_list_release(struct todo_list *todo_list)
static struct todo_item *append_new_todo(struct todo_list *todo_list)
{
ALLOC_GROW(todo_list->items, todo_list->nr + 1, todo_list->alloc);
- todo_list->total_nr++;
return todo_list->items + todo_list->nr++;
}
@@ -2667,7 +2694,7 @@ int todo_list_parse_insn_buffer(struct repository *r, char *buf,
char *p = buf, *next_p;
int i, res = 0, fixup_okay = file_exists(rebase_path_done());
- todo_list->current = todo_list->nr = 0;
+ todo_list->current = todo_list->nr = todo_list->total_nr = 0;
for (i = 1; *p; i++, p = next_p) {
char *eol = strchrnul(p, '\n');
@@ -2688,6 +2715,9 @@ int todo_list_parse_insn_buffer(struct repository *r, char *buf,
item->commit = NULL;
}
+ if (item->command != TODO_COMMENT)
+ todo_list->total_nr++;
+
if (fixup_okay)
; /* do nothing */
else if (is_fixup(item->command))
@@ -3379,7 +3409,8 @@ give_advice:
return -1;
}
-static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
+static int save_todo(struct todo_list *todo_list, struct replay_opts *opts,
+ int reschedule)
{
struct lock_file todo_lock = LOCK_INIT;
const char *todo_path = get_todo_path(opts);
@@ -3389,7 +3420,7 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
* rebase -i writes "git-rebase-todo" without the currently executing
* command, appending it to "done" instead.
*/
- if (is_rebase_i(opts))
+ if (is_rebase_i(opts) && !reschedule)
next++;
fd = hold_lock_file_for_update(&todo_lock, todo_path, 0);
@@ -3402,7 +3433,7 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
if (commit_lock_file(&todo_lock) < 0)
return error(_("failed to finalize '%s'"), todo_path);
- if (is_rebase_i(opts) && next > 0) {
+ if (is_rebase_i(opts) && !reschedule && next > 0) {
const char *done = rebase_path_done();
int fd = open(done, O_CREAT | O_WRONLY | O_APPEND, 0666);
int ret = 0;
@@ -3494,7 +3525,6 @@ static int make_patch(struct repository *r,
return -1;
res |= write_rebase_head(&commit->object.oid);
- strbuf_addf(&buf, "%s/patch", get_dir(opts));
memset(&log_tree_opt, 0, sizeof(log_tree_opt));
repo_init_revisions(r, &log_tree_opt, NULL);
log_tree_opt.abbrev = 0;
@@ -3502,7 +3532,7 @@ static int make_patch(struct repository *r,
log_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH;
log_tree_opt.disable_stdin = 1;
log_tree_opt.no_commit_id = 1;
- log_tree_opt.diffopt.file = fopen(buf.buf, "w");
+ log_tree_opt.diffopt.file = fopen(rebase_path_patch(), "w");
log_tree_opt.diffopt.use_color = GIT_COLOR_NEVER;
if (!log_tree_opt.diffopt.file)
res |= error_errno(_("could not open '%s'"), buf.buf);
@@ -3625,14 +3655,14 @@ static int do_exec(struct repository *r, const char *command_line)
" git rebase --continue\n"
"\n"),
command_line,
- dirty ? N_("and made changes to the index and/or the "
- "working tree\n") : "");
+ dirty ? _("and made changes to the index and/or the "
+ "working tree.\n") : "");
if (status == 127)
/* command not found */
status = 1;
} else if (dirty) {
warning(_("execution succeeded: %s\nbut "
- "left changes to the index and/or the working tree\n"
+ "left changes to the index and/or the working tree.\n"
"Commit or stash your changes, and then run\n"
"\n"
" git rebase --continue\n"
@@ -4141,6 +4171,8 @@ static int do_merge(struct repository *r,
if (ret < 0) {
error(_("could not even attempt to merge '%.*s'"),
merge_arg_len, arg);
+ unlink(rebase_path_author_script());
+ unlink(git_path_merge_msg(r));
goto leave_merge;
}
/*
@@ -4269,7 +4301,7 @@ void todo_list_filter_update_refs(struct repository *r,
if (!is_null_oid(&rec->after))
continue;
- for (j = 0; !found && j < todo_list->total_nr; j++) {
+ for (j = 0; !found && j < todo_list->nr; j++) {
struct todo_item *item = &todo_list->items[j];
const char *arg = todo_list->buf.buf + item->arg_offset;
@@ -4299,7 +4331,7 @@ void todo_list_filter_update_refs(struct repository *r,
* For each todo_item, check if its ref is in the update_refs list.
* If not, then add it as an un-updated ref.
*/
- for (i = 0; i < todo_list->total_nr; i++) {
+ for (i = 0; i < todo_list->nr; i++) {
struct todo_item *item = &todo_list->items[i];
const char *arg = todo_list->buf.buf + item->arg_offset;
int j, found = 0;
@@ -4628,6 +4660,68 @@ N_("Could not execute the todo command\n"
" git rebase --edit-todo\n"
" git rebase --continue\n");
+static int pick_one_commit(struct repository *r,
+ struct todo_list *todo_list,
+ struct replay_opts *opts,
+ int *check_todo, int* reschedule)
+{
+ int res;
+ struct todo_item *item = todo_list->items + todo_list->current;
+ const char *arg = todo_item_get_arg(todo_list, item);
+ if (is_rebase_i(opts))
+ opts->reflog_message = reflog_message(
+ opts, command_to_string(item->command), NULL);
+
+ res = do_pick_commit(r, item, opts, is_final_fixup(todo_list),
+ check_todo);
+ if (is_rebase_i(opts) && res < 0) {
+ /* Reschedule */
+ *reschedule = 1;
+ return -1;
+ }
+ if (item->command == TODO_EDIT) {
+ struct commit *commit = item->commit;
+ if (!res) {
+ if (!opts->verbose)
+ term_clear_line();
+ fprintf(stderr, _("Stopped at %s... %.*s\n"),
+ short_commit_name(commit), item->arg_len, arg);
+ }
+ return error_with_patch(r, commit,
+ arg, item->arg_len, opts, res, !res);
+ }
+ if (is_rebase_i(opts) && !res)
+ record_in_rewritten(&item->commit->object.oid,
+ peek_command(todo_list, 1));
+ if (res && is_fixup(item->command)) {
+ if (res == 1)
+ intend_to_amend();
+ return error_failed_squash(r, item->commit, opts,
+ item->arg_len, arg);
+ } else if (res && is_rebase_i(opts) && item->commit) {
+ int to_amend = 0;
+ struct object_id oid;
+
+ /*
+ * If we are rewording and have either
+ * fast-forwarded already, or are about to
+ * create a new root commit, we want to amend,
+ * otherwise we do not.
+ */
+ if (item->command == TODO_REWORD &&
+ !repo_get_oid(r, "HEAD", &oid) &&
+ (oideq(&item->commit->object.oid, &oid) ||
+ (opts->have_squash_onto &&
+ oideq(&opts->squash_onto, &oid))))
+ to_amend = 1;
+
+ return res | error_with_patch(r, item->commit,
+ arg, item->arg_len, opts,
+ res, to_amend);
+ }
+ return res;
+}
+
static int pick_commits(struct repository *r,
struct todo_list *todo_list,
struct replay_opts *opts)
@@ -4643,12 +4737,17 @@ static int pick_commits(struct repository *r,
if (read_and_refresh_cache(r, opts))
return -1;
+ unlink(rebase_path_message());
+ unlink(rebase_path_stopped_sha());
+ unlink(rebase_path_amend());
+ unlink(rebase_path_patch());
+
while (todo_list->current < todo_list->nr) {
struct todo_item *item = todo_list->items + todo_list->current;
const char *arg = todo_item_get_arg(todo_list, item);
int check_todo = 0;
- if (save_todo(todo_list, opts))
+ if (save_todo(todo_list, opts, reschedule))
return -1;
if (is_rebase_i(opts)) {
if (item->command != TODO_COMMENT) {
@@ -4666,10 +4765,7 @@ static int pick_commits(struct repository *r,
todo_list->total_nr,
opts->verbose ? "\n" : "\r");
}
- unlink(rebase_path_message());
unlink(rebase_path_author_script());
- unlink(rebase_path_stopped_sha());
- unlink(rebase_path_amend());
unlink(git_path_merge_head(r));
unlink(git_path_auto_merge(r));
delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
@@ -4681,66 +4777,10 @@ static int pick_commits(struct repository *r,
}
}
if (item->command <= TODO_SQUASH) {
- if (is_rebase_i(opts))
- opts->reflog_message = reflog_message(opts,
- command_to_string(item->command), NULL);
-
- res = do_pick_commit(r, item, opts,
- is_final_fixup(todo_list),
- &check_todo);
- if (is_rebase_i(opts) && res < 0) {
- /* Reschedule */
- advise(_(rescheduled_advice),
- get_item_line_length(todo_list,
- todo_list->current),
- get_item_line(todo_list,
- todo_list->current));
- todo_list->current--;
- if (save_todo(todo_list, opts))
- return -1;
- }
- if (item->command == TODO_EDIT) {
- struct commit *commit = item->commit;
- if (!res) {
- if (!opts->verbose)
- term_clear_line();
- fprintf(stderr,
- _("Stopped at %s... %.*s\n"),
- short_commit_name(commit),
- item->arg_len, arg);
- }
- return error_with_patch(r, commit,
- arg, item->arg_len, opts, res, !res);
- }
- if (is_rebase_i(opts) && !res)
- record_in_rewritten(&item->commit->object.oid,
- peek_command(todo_list, 1));
- if (res && is_fixup(item->command)) {
- if (res == 1)
- intend_to_amend();
- return error_failed_squash(r, item->commit, opts,
- item->arg_len, arg);
- } else if (res && is_rebase_i(opts) && item->commit) {
- int to_amend = 0;
- struct object_id oid;
-
- /*
- * If we are rewording and have either
- * fast-forwarded already, or are about to
- * create a new root commit, we want to amend,
- * otherwise we do not.
- */
- if (item->command == TODO_REWORD &&
- !repo_get_oid(r, "HEAD", &oid) &&
- (oideq(&item->commit->object.oid, &oid) ||
- (opts->have_squash_onto &&
- oideq(&opts->squash_onto, &oid))))
- to_amend = 1;
-
- return res | error_with_patch(r, item->commit,
- arg, item->arg_len, opts,
- res, to_amend);
- }
+ res = pick_one_commit(r, todo_list, opts, &check_todo,
+ &reschedule);
+ if (!res && item->command == TODO_EDIT)
+ return 0;
} else if (item->command == TODO_EXEC) {
char *end_of_arg = (char *)(arg + item->arg_len);
int saved = *end_of_arg;
@@ -4788,14 +4828,10 @@ static int pick_commits(struct repository *r,
get_item_line_length(todo_list,
todo_list->current),
get_item_line(todo_list, todo_list->current));
- todo_list->current--;
- if (save_todo(todo_list, opts))
+ if (save_todo(todo_list, opts, reschedule))
return -1;
if (item->commit)
- return error_with_patch(r,
- item->commit,
- arg, item->arg_len,
- opts, res, 0);
+ write_rebase_head(&item->commit->object.oid);
} else if (is_rebase_i(opts) && check_todo && !res &&
reread_todo_if_changed(r, todo_list, opts)) {
return -1;
@@ -6147,7 +6183,8 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
todo_list_to_strbuf(r, &new_todo, &buf2, -1, 0);
strbuf_swap(&new_todo.buf, &buf2);
strbuf_release(&buf2);
- new_todo.total_nr -= new_todo.nr;
+ /* Nothing is done yet, and we're reparsing, so let's reset the count */
+ new_todo.total_nr = 0;
if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) < 0)
BUG("invalid todo list after expanding IDs:\n%s",
new_todo.buf.buf);
diff --git a/serve.c b/serve.c
index 5329c91011..a1d71134d4 100644
--- a/serve.c
+++ b/serve.c
@@ -1,6 +1,7 @@
#include "git-compat-util.h"
#include "repository.h"
#include "config.h"
+#include "hash-ll.h"
#include "pkt-line.h"
#include "version.h"
#include "ls-refs.h"
diff --git a/server-info.c b/server-info.c
index 68098ddd1a..382e481a2b 100644
--- a/server-info.c
+++ b/server-info.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "alloc.h"
#include "dir.h"
#include "environment.h"
@@ -9,8 +9,10 @@
#include "commit.h"
#include "tag.h"
#include "packfile.h"
+#include "path.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "server-info.h"
#include "strbuf.h"
#include "wrapper.h"
diff --git a/server-info.h b/server-info.h
new file mode 100644
index 0000000000..13bbde2c55
--- /dev/null
+++ b/server-info.h
@@ -0,0 +1,7 @@
+#ifndef SERVER_INFO_H
+#define SERVER_INFO_H
+
+/* Dumb servers support */
+int update_server_info(int);
+
+#endif /* SERVER_INFO_H */
diff --git a/setup.c b/setup.c
index 59abc16ba6..3c383c972c 100644
--- a/setup.c
+++ b/setup.c
@@ -1,17 +1,22 @@
#include "git-compat-util.h"
#include "abspath.h"
+#include "copy.h"
#include "environment.h"
+#include "exec-cmd.h"
#include "gettext.h"
#include "object-name.h"
+#include "refs.h"
#include "repository.h"
#include "config.h"
#include "dir.h"
#include "setup.h"
#include "string-list.h"
#include "chdir-notify.h"
+#include "path.h"
#include "promisor-remote.h"
#include "quote.h"
#include "trace2.h"
+#include "worktree.h"
#include "wrapper.h"
static int inside_git_dir = -1;
@@ -1352,6 +1357,7 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
}
if (is_git_directory(dir->buf)) {
+ trace2_data_string("setup", NULL, "implicit-bare-repository", dir->buf);
if (get_allowed_bare_repo() == ALLOWED_BARE_REPO_EXPLICIT)
return GIT_DIR_DISALLOWED_BARE;
if (!ensure_valid_ownership(NULL, NULL, dir->buf, report))
@@ -1706,3 +1712,495 @@ int daemonize(void)
return 0;
#endif
}
+
+#ifndef DEFAULT_GIT_TEMPLATE_DIR
+#define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
+#endif
+
+#ifdef NO_TRUSTABLE_FILEMODE
+#define TEST_FILEMODE 0
+#else
+#define TEST_FILEMODE 1
+#endif
+
+#define GIT_DEFAULT_HASH_ENVIRONMENT "GIT_DEFAULT_HASH"
+
+static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
+ DIR *dir)
+{
+ size_t path_baselen = path->len;
+ size_t template_baselen = template_path->len;
+ struct dirent *de;
+
+ /* Note: if ".git/hooks" file exists in the repository being
+ * re-initialized, /etc/core-git/templates/hooks/update would
+ * cause "git init" to fail here. I think this is sane but
+ * it means that the set of templates we ship by default, along
+ * with the way the namespace under .git/ is organized, should
+ * be really carefully chosen.
+ */
+ safe_create_dir(path->buf, 1);
+ while ((de = readdir(dir)) != NULL) {
+ struct stat st_git, st_template;
+ int exists = 0;
+
+ strbuf_setlen(path, path_baselen);
+ strbuf_setlen(template_path, template_baselen);
+
+ if (de->d_name[0] == '.')
+ continue;
+ strbuf_addstr(path, de->d_name);
+ strbuf_addstr(template_path, de->d_name);
+ if (lstat(path->buf, &st_git)) {
+ if (errno != ENOENT)
+ die_errno(_("cannot stat '%s'"), path->buf);
+ }
+ else
+ exists = 1;
+
+ if (lstat(template_path->buf, &st_template))
+ die_errno(_("cannot stat template '%s'"), template_path->buf);
+
+ if (S_ISDIR(st_template.st_mode)) {
+ DIR *subdir = opendir(template_path->buf);
+ if (!subdir)
+ die_errno(_("cannot opendir '%s'"), template_path->buf);
+ strbuf_addch(path, '/');
+ strbuf_addch(template_path, '/');
+ copy_templates_1(path, template_path, subdir);
+ closedir(subdir);
+ }
+ else if (exists)
+ continue;
+ else if (S_ISLNK(st_template.st_mode)) {
+ struct strbuf lnk = STRBUF_INIT;
+ if (strbuf_readlink(&lnk, template_path->buf,
+ st_template.st_size) < 0)
+ die_errno(_("cannot readlink '%s'"), template_path->buf);
+ if (symlink(lnk.buf, path->buf))
+ die_errno(_("cannot symlink '%s' '%s'"),
+ lnk.buf, path->buf);
+ strbuf_release(&lnk);
+ }
+ else if (S_ISREG(st_template.st_mode)) {
+ if (copy_file(path->buf, template_path->buf, st_template.st_mode))
+ die_errno(_("cannot copy '%s' to '%s'"),
+ template_path->buf, path->buf);
+ }
+ else
+ error(_("ignoring template %s"), template_path->buf);
+ }
+}
+
+static void copy_templates(const char *template_dir, const char *init_template_dir)
+{
+ struct strbuf path = STRBUF_INIT;
+ struct strbuf template_path = STRBUF_INIT;
+ size_t template_len;
+ struct repository_format template_format = REPOSITORY_FORMAT_INIT;
+ struct strbuf err = STRBUF_INIT;
+ DIR *dir;
+ char *to_free = NULL;
+
+ if (!template_dir)
+ template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
+ if (!template_dir)
+ template_dir = init_template_dir;
+ if (!template_dir)
+ template_dir = to_free = system_path(DEFAULT_GIT_TEMPLATE_DIR);
+ if (!template_dir[0]) {
+ free(to_free);
+ return;
+ }
+
+ strbuf_addstr(&template_path, template_dir);
+ strbuf_complete(&template_path, '/');
+ template_len = template_path.len;
+
+ dir = opendir(template_path.buf);
+ if (!dir) {
+ warning(_("templates not found in %s"), template_dir);
+ goto free_return;
+ }
+
+ /* Make sure that template is from the correct vintage */
+ strbuf_addstr(&template_path, "config");
+ read_repository_format(&template_format, template_path.buf);
+ strbuf_setlen(&template_path, template_len);
+
+ /*
+ * No mention of version at all is OK, but anything else should be
+ * verified.
+ */
+ if (template_format.version >= 0 &&
+ verify_repository_format(&template_format, &err) < 0) {
+ warning(_("not copying templates from '%s': %s"),
+ template_dir, err.buf);
+ strbuf_release(&err);
+ goto close_free_return;
+ }
+
+ strbuf_addstr(&path, get_git_common_dir());
+ strbuf_complete(&path, '/');
+ copy_templates_1(&path, &template_path, dir);
+close_free_return:
+ closedir(dir);
+free_return:
+ free(to_free);
+ strbuf_release(&path);
+ strbuf_release(&template_path);
+ clear_repository_format(&template_format);
+}
+
+/*
+ * If the git_dir is not directly inside the working tree, then git will not
+ * find it by default, and we need to set the worktree explicitly.
+ */
+static int needs_work_tree_config(const char *git_dir, const char *work_tree)
+{
+ if (!strcmp(work_tree, "/") && !strcmp(git_dir, "/.git"))
+ return 0;
+ if (skip_prefix(git_dir, work_tree, &git_dir) &&
+ !strcmp(git_dir, "/.git"))
+ return 0;
+ return 1;
+}
+
+void initialize_repository_version(int hash_algo, int reinit)
+{
+ char repo_version_string[10];
+ int repo_version = GIT_REPO_VERSION;
+
+ if (hash_algo != GIT_HASH_SHA1)
+ repo_version = GIT_REPO_VERSION_READ;
+
+ /* This forces creation of new config file */
+ xsnprintf(repo_version_string, sizeof(repo_version_string),
+ "%d", repo_version);
+ git_config_set("core.repositoryformatversion", repo_version_string);
+
+ if (hash_algo != GIT_HASH_SHA1)
+ git_config_set("extensions.objectformat",
+ hash_algos[hash_algo].name);
+ else if (reinit)
+ git_config_set_gently("extensions.objectformat", NULL);
+}
+
+static int create_default_files(const char *template_path,
+ const char *original_git_dir,
+ const char *initial_branch,
+ const struct repository_format *fmt,
+ int prev_bare_repository,
+ int init_shared_repository,
+ int quiet)
+{
+ struct stat st1;
+ struct strbuf buf = STRBUF_INIT;
+ char *path;
+ char junk[2];
+ int reinit;
+ int filemode;
+ struct strbuf err = STRBUF_INIT;
+ const char *init_template_dir = NULL;
+ const char *work_tree = get_git_work_tree();
+
+ /*
+ * First copy the templates -- we might have the default
+ * config file there, in which case we would want to read
+ * from it after installing.
+ *
+ * Before reading that config, we also need to clear out any cached
+ * values (since we've just potentially changed what's available on
+ * disk).
+ */
+ git_config_get_pathname("init.templatedir", &init_template_dir);
+ copy_templates(template_path, init_template_dir);
+ free((char *)init_template_dir);
+ git_config_clear();
+ reset_shared_repository();
+ git_config(git_default_config, NULL);
+
+ /*
+ * We must make sure command-line options continue to override any
+ * values we might have just re-read from the config.
+ */
+ if (init_shared_repository != -1)
+ set_shared_repository(init_shared_repository);
+ /*
+ * TODO: heed core.bare from config file in templates if no
+ * command-line override given
+ */
+ is_bare_repository_cfg = prev_bare_repository || !work_tree;
+ /* TODO (continued):
+ *
+ * Unfortunately, the line above is equivalent to
+ * is_bare_repository_cfg = !work_tree;
+ * which ignores the config entirely even if no `--[no-]bare`
+ * command line option was present.
+ *
+ * To see why, note that before this function, there was this call:
+ * prev_bare_repository = is_bare_repository()
+ * expanding the right hand side:
+ * = is_bare_repository_cfg && !get_git_work_tree()
+ * = is_bare_repository_cfg && !work_tree
+ * note that the last simplification above is valid because nothing
+ * calls repo_init() or set_git_work_tree() between any of the
+ * relevant calls in the code, and thus the !get_git_work_tree()
+ * calls will return the same result each time. So, what we are
+ * interested in computing is the right hand side of the line of
+ * code just above this comment:
+ * prev_bare_repository || !work_tree
+ * = is_bare_repository_cfg && !work_tree || !work_tree
+ * = !work_tree
+ * because "A && !B || !B == !B" for all boolean values of A & B.
+ */
+
+ /*
+ * We would have created the above under user's umask -- under
+ * shared-repository settings, we would need to fix them up.
+ */
+ if (get_shared_repository()) {
+ adjust_shared_perm(get_git_dir());
+ }
+
+ /*
+ * We need to create a "refs" dir in any case so that older
+ * versions of git can tell that this is a repository.
+ */
+ safe_create_dir(git_path("refs"), 1);
+ adjust_shared_perm(git_path("refs"));
+
+ if (refs_init_db(&err))
+ die("failed to set up refs db: %s", err.buf);
+
+ /*
+ * Point the HEAD symref to the initial branch with if HEAD does
+ * not yet exist.
+ */
+ path = git_path_buf(&buf, "HEAD");
+ reinit = (!access(path, R_OK)
+ || readlink(path, junk, sizeof(junk)-1) != -1);
+ if (!reinit) {
+ char *ref;
+
+ if (!initial_branch)
+ initial_branch = git_default_branch_name(quiet);
+
+ ref = xstrfmt("refs/heads/%s", initial_branch);
+ if (check_refname_format(ref, 0) < 0)
+ die(_("invalid initial branch name: '%s'"),
+ initial_branch);
+
+ if (create_symref("HEAD", ref, NULL) < 0)
+ exit(1);
+ free(ref);
+ }
+
+ initialize_repository_version(fmt->hash_algo, 0);
+
+ /* Check filemode trustability */
+ path = git_path_buf(&buf, "config");
+ filemode = TEST_FILEMODE;
+ if (TEST_FILEMODE && !lstat(path, &st1)) {
+ struct stat st2;
+ filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) &&
+ !lstat(path, &st2) &&
+ st1.st_mode != st2.st_mode &&
+ !chmod(path, st1.st_mode));
+ if (filemode && !reinit && (st1.st_mode & S_IXUSR))
+ filemode = 0;
+ }
+ git_config_set("core.filemode", filemode ? "true" : "false");
+
+ if (is_bare_repository())
+ git_config_set("core.bare", "true");
+ else {
+ git_config_set("core.bare", "false");
+ /* allow template config file to override the default */
+ if (log_all_ref_updates == LOG_REFS_UNSET)
+ git_config_set("core.logallrefupdates", "true");
+ if (needs_work_tree_config(original_git_dir, work_tree))
+ git_config_set("core.worktree", work_tree);
+ }
+
+ if (!reinit) {
+ /* Check if symlink is supported in the work tree */
+ path = git_path_buf(&buf, "tXXXXXX");
+ if (!close(xmkstemp(path)) &&
+ !unlink(path) &&
+ !symlink("testing", path) &&
+ !lstat(path, &st1) &&
+ S_ISLNK(st1.st_mode))
+ unlink(path); /* good */
+ else
+ git_config_set("core.symlinks", "false");
+
+ /* Check if the filesystem is case-insensitive */
+ path = git_path_buf(&buf, "CoNfIg");
+ if (!access(path, F_OK))
+ git_config_set("core.ignorecase", "true");
+ probe_utf8_pathname_composition();
+ }
+
+ strbuf_release(&buf);
+ return reinit;
+}
+
+static void create_object_directory(void)
+{
+ struct strbuf path = STRBUF_INIT;
+ size_t baselen;
+
+ strbuf_addstr(&path, get_object_directory());
+ baselen = path.len;
+
+ safe_create_dir(path.buf, 1);
+
+ strbuf_setlen(&path, baselen);
+ strbuf_addstr(&path, "/pack");
+ safe_create_dir(path.buf, 1);
+
+ strbuf_setlen(&path, baselen);
+ strbuf_addstr(&path, "/info");
+ safe_create_dir(path.buf, 1);
+
+ strbuf_release(&path);
+}
+
+static void separate_git_dir(const char *git_dir, const char *git_link)
+{
+ struct stat st;
+
+ if (!stat(git_link, &st)) {
+ const char *src;
+
+ if (S_ISREG(st.st_mode))
+ src = read_gitfile(git_link);
+ else if (S_ISDIR(st.st_mode))
+ src = git_link;
+ else
+ die(_("unable to handle file type %d"), (int)st.st_mode);
+
+ if (rename(src, git_dir))
+ die_errno(_("unable to move %s to %s"), src, git_dir);
+ repair_worktrees(NULL, NULL);
+ }
+
+ write_file(git_link, "gitdir: %s", git_dir);
+}
+
+static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash)
+{
+ const char *env = getenv(GIT_DEFAULT_HASH_ENVIRONMENT);
+ /*
+ * If we already have an initialized repo, don't allow the user to
+ * specify a different algorithm, as that could cause corruption.
+ * Otherwise, if the user has specified one on the command line, use it.
+ */
+ if (repo_fmt->version >= 0 && hash != GIT_HASH_UNKNOWN && hash != repo_fmt->hash_algo)
+ die(_("attempt to reinitialize repository with different hash"));
+ else if (hash != GIT_HASH_UNKNOWN)
+ repo_fmt->hash_algo = hash;
+ else if (env) {
+ int env_algo = hash_algo_by_name(env);
+ if (env_algo == GIT_HASH_UNKNOWN)
+ die(_("unknown hash algorithm '%s'"), env);
+ repo_fmt->hash_algo = env_algo;
+ }
+}
+
+int init_db(const char *git_dir, const char *real_git_dir,
+ const char *template_dir, int hash, const char *initial_branch,
+ int init_shared_repository, unsigned int flags)
+{
+ int reinit;
+ int exist_ok = flags & INIT_DB_EXIST_OK;
+ char *original_git_dir = real_pathdup(git_dir, 1);
+ struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
+ int prev_bare_repository;
+
+ if (real_git_dir) {
+ struct stat st;
+
+ if (!exist_ok && !stat(git_dir, &st))
+ die(_("%s already exists"), git_dir);
+
+ if (!exist_ok && !stat(real_git_dir, &st))
+ die(_("%s already exists"), real_git_dir);
+
+ set_git_dir(real_git_dir, 1);
+ git_dir = get_git_dir();
+ separate_git_dir(git_dir, original_git_dir);
+ }
+ else {
+ set_git_dir(git_dir, 1);
+ git_dir = get_git_dir();
+ }
+ startup_info->have_repository = 1;
+
+ /* Ensure `core.hidedotfiles` is processed */
+ git_config(platform_core_config, NULL);
+
+ safe_create_dir(git_dir, 0);
+
+ prev_bare_repository = is_bare_repository();
+
+ /* Check to see if the repository version is right.
+ * Note that a newly created repository does not have
+ * config file, so this will not fail. What we are catching
+ * is an attempt to reinitialize new repository with an old tool.
+ */
+ check_repository_format(&repo_fmt);
+
+ validate_hash_algorithm(&repo_fmt, hash);
+
+ reinit = create_default_files(template_dir, original_git_dir,
+ initial_branch, &repo_fmt,
+ prev_bare_repository,
+ init_shared_repository,
+ flags & INIT_DB_QUIET);
+ if (reinit && initial_branch)
+ warning(_("re-init: ignored --initial-branch=%s"),
+ initial_branch);
+
+ create_object_directory();
+
+ if (get_shared_repository()) {
+ char buf[10];
+ /* We do not spell "group" and such, so that
+ * the configuration can be read by older version
+ * of git. Note, we use octal numbers for new share modes,
+ * and compatibility values for PERM_GROUP and
+ * PERM_EVERYBODY.
+ */
+ if (get_shared_repository() < 0)
+ /* force to the mode value */
+ xsnprintf(buf, sizeof(buf), "0%o", -get_shared_repository());
+ else if (get_shared_repository() == PERM_GROUP)
+ xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_GROUP);
+ else if (get_shared_repository() == PERM_EVERYBODY)
+ xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY);
+ else
+ BUG("invalid value for shared_repository");
+ git_config_set("core.sharedrepository", buf);
+ git_config_set("receive.denyNonFastforwards", "true");
+ }
+
+ if (!(flags & INIT_DB_QUIET)) {
+ int len = strlen(git_dir);
+
+ if (reinit)
+ printf(get_shared_repository()
+ ? _("Reinitialized existing shared Git repository in %s%s\n")
+ : _("Reinitialized existing Git repository in %s%s\n"),
+ git_dir, len && git_dir[len-1] != '/' ? "/" : "");
+ else
+ printf(get_shared_repository()
+ ? _("Initialized empty shared Git repository in %s%s\n")
+ : _("Initialized empty Git repository in %s%s\n"),
+ git_dir, len && git_dir[len-1] != '/' ? "/" : "");
+ }
+
+ free(original_git_dir);
+ return 0;
+}
diff --git a/setup.h b/setup.h
index 4c1ca9d0c9..58fd2605dd 100644
--- a/setup.h
+++ b/setup.h
@@ -140,6 +140,15 @@ int verify_repository_format(const struct repository_format *format,
*/
void check_repository_format(struct repository_format *fmt);
+#define INIT_DB_QUIET 0x0001
+#define INIT_DB_EXIST_OK 0x0002
+
+int init_db(const char *git_dir, const char *real_git_dir,
+ const char *template_dir, int hash_algo,
+ const char *initial_branch, int init_shared_repository,
+ unsigned int flags);
+void initialize_repository_version(int hash_algo, int reinit);
+
/*
* NOTE NOTE NOTE!!
*
diff --git a/shallow.c b/shallow.c
index 128f56179e..f3ef94d4c9 100644
--- a/shallow.c
+++ b/shallow.c
@@ -1,22 +1,24 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "alloc.h"
#include "hex.h"
#include "repository.h"
#include "tempfile.h"
#include "lockfile.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "commit.h"
#include "tag.h"
#include "pkt-line.h"
#include "remote.h"
#include "refs.h"
#include "oid-array.h"
+#include "path.h"
#include "diff.h"
#include "revision.h"
#include "commit-slab.h"
#include "list-objects.h"
#include "commit-reach.h"
#include "shallow.h"
+#include "statinfo.h"
#include "trace.h"
#include "wrapper.h"
diff --git a/sparse-index.c b/sparse-index.c
index 886054729e..90d0462256 100644
--- a/sparse-index.c
+++ b/sparse-index.c
@@ -1,7 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "alloc.h"
#include "environment.h"
#include "gettext.h"
+#include "name-hash.h"
+#include "read-cache-ll.h"
#include "repository.h"
#include "sparse-index.h"
#include "tree.h"
@@ -10,7 +12,7 @@
#include "cache-tree.h"
#include "config.h"
#include "dir.h"
-#include "fsmonitor.h"
+#include "fsmonitor-ll.h"
struct modify_index_context {
struct index_state *write;
diff --git a/sparse-index.h b/sparse-index.h
index 59a92d819e..a16f3e67d7 100644
--- a/sparse-index.h
+++ b/sparse-index.h
@@ -37,4 +37,6 @@ struct pattern_list;
*/
void expand_index(struct index_state *istate, struct pattern_list *pl);
+void ensure_full_index(struct index_state *istate);
+
#endif
diff --git a/split-index.c b/split-index.c
index 5602b74994..0ee3865a55 100644
--- a/split-index.c
+++ b/split-index.c
@@ -1,8 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "alloc.h"
#include "gettext.h"
+#include "hash.h"
#include "mem-pool.h"
+#include "read-cache-ll.h"
#include "split-index.h"
+#include "strbuf.h"
#include "ewah/ewok.h"
struct split_index *init_split_index(struct index_state *istate)
diff --git a/split-index.h b/split-index.h
index 1a153f47ba..15a29cd08c 100644
--- a/split-index.h
+++ b/split-index.h
@@ -1,7 +1,7 @@
#ifndef SPLIT_INDEX_H
#define SPLIT_INDEX_H
-#include "hash.h"
+#include "hash-ll.h"
struct index_state;
struct strbuf;
diff --git a/statinfo.c b/statinfo.c
new file mode 100644
index 0000000000..17bb8966c3
--- /dev/null
+++ b/statinfo.c
@@ -0,0 +1,87 @@
+#include "git-compat-util.h"
+#include "environment.h"
+#include "statinfo.h"
+
+void fill_stat_data(struct stat_data *sd, struct stat *st)
+{
+ sd->sd_ctime.sec = (unsigned int)st->st_ctime;
+ sd->sd_mtime.sec = (unsigned int)st->st_mtime;
+ sd->sd_ctime.nsec = ST_CTIME_NSEC(*st);
+ sd->sd_mtime.nsec = ST_MTIME_NSEC(*st);
+ sd->sd_dev = st->st_dev;
+ sd->sd_ino = st->st_ino;
+ sd->sd_uid = st->st_uid;
+ sd->sd_gid = st->st_gid;
+ sd->sd_size = st->st_size;
+}
+
+int match_stat_data(const struct stat_data *sd, struct stat *st)
+{
+ int changed = 0;
+
+ if (sd->sd_mtime.sec != (unsigned int)st->st_mtime)
+ changed |= MTIME_CHANGED;
+ if (trust_ctime && check_stat &&
+ sd->sd_ctime.sec != (unsigned int)st->st_ctime)
+ changed |= CTIME_CHANGED;
+
+#ifdef USE_NSEC
+ if (check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st))
+ changed |= MTIME_CHANGED;
+ if (trust_ctime && check_stat &&
+ sd->sd_ctime.nsec != ST_CTIME_NSEC(*st))
+ changed |= CTIME_CHANGED;
+#endif
+
+ if (check_stat) {
+ if (sd->sd_uid != (unsigned int) st->st_uid ||
+ sd->sd_gid != (unsigned int) st->st_gid)
+ changed |= OWNER_CHANGED;
+ if (sd->sd_ino != (unsigned int) st->st_ino)
+ changed |= INODE_CHANGED;
+ }
+
+#ifdef USE_STDEV
+ /*
+ * st_dev breaks on network filesystems where different
+ * clients will have different views of what "device"
+ * the filesystem is on
+ */
+ if (check_stat && sd->sd_dev != (unsigned int) st->st_dev)
+ changed |= INODE_CHANGED;
+#endif
+
+ if (sd->sd_size != (unsigned int) st->st_size)
+ changed |= DATA_CHANGED;
+
+ return changed;
+}
+
+void stat_validity_clear(struct stat_validity *sv)
+{
+ FREE_AND_NULL(sv->sd);
+}
+
+int stat_validity_check(struct stat_validity *sv, const char *path)
+{
+ struct stat st;
+
+ if (stat(path, &st) < 0)
+ return sv->sd == NULL;
+ if (!sv->sd)
+ return 0;
+ return S_ISREG(st.st_mode) && !match_stat_data(sv->sd, &st);
+}
+
+void stat_validity_update(struct stat_validity *sv, int fd)
+{
+ struct stat st;
+
+ if (fstat(fd, &st) < 0 || !S_ISREG(st.st_mode))
+ stat_validity_clear(sv);
+ else {
+ if (!sv->sd)
+ CALLOC_ARRAY(sv->sd, 1);
+ fill_stat_data(sv->sd, &st);
+ }
+}
diff --git a/statinfo.h b/statinfo.h
index e49e3054ea..bb9b61bc47 100644
--- a/statinfo.h
+++ b/statinfo.h
@@ -1,6 +1,8 @@
#ifndef STATINFO_H
#define STATINFO_H
+struct index_state;
+
/*
* The "cache_time" is just the low 32 bits of the
* time. It doesn't matter if it overflows - we only
@@ -21,4 +23,53 @@ struct stat_data {
unsigned int sd_size;
};
+/*
+ * A struct to encapsulate the concept of whether a file has changed
+ * since we last checked it. This uses criteria similar to those used
+ * for the index.
+ */
+struct stat_validity {
+ struct stat_data *sd;
+};
+
+#define MTIME_CHANGED 0x0001
+#define CTIME_CHANGED 0x0002
+#define OWNER_CHANGED 0x0004
+#define MODE_CHANGED 0x0008
+#define INODE_CHANGED 0x0010
+#define DATA_CHANGED 0x0020
+#define TYPE_CHANGED 0x0040
+
+/*
+ * Record to sd the data from st that we use to check whether a file
+ * might have changed.
+ */
+void fill_stat_data(struct stat_data *sd, struct stat *st);
+
+/*
+ * Return 0 if st is consistent with a file not having been changed
+ * since sd was filled. If there are differences, return a
+ * combination of MTIME_CHANGED, CTIME_CHANGED, OWNER_CHANGED,
+ * INODE_CHANGED, and DATA_CHANGED.
+ */
+int match_stat_data(const struct stat_data *sd, struct stat *st);
+
+void stat_validity_clear(struct stat_validity *sv);
+
+/*
+ * Returns 1 if the path is a regular file (or a symlink to a regular
+ * file) and matches the saved stat_validity, 0 otherwise. A missing
+ * or inaccessible file is considered a match if the struct was just
+ * initialized, or if the previous update found an inaccessible file.
+ */
+int stat_validity_check(struct stat_validity *sv, const char *path);
+
+/*
+ * Update the stat_validity from a file opened at descriptor fd. If
+ * the file is missing, inaccessible, or not a regular file, then
+ * future calls to stat_validity_check will match iff one of those
+ * conditions continues to be true.
+ */
+void stat_validity_update(struct stat_validity *sv, int fd);
+
#endif
diff --git a/strbuf.c b/strbuf.c
index 729378ec82..37fd5247b3 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -1,11 +1,9 @@
#include "git-compat-util.h"
-#include "abspath.h"
#include "alloc.h"
-#include "environment.h"
#include "gettext.h"
#include "hex.h"
-#include "object-name.h"
-#include "refs.h"
+#include "path.h"
+#include "strbuf.h"
#include "string-list.h"
#include "utf8.h"
#include "date.h"
@@ -364,7 +362,8 @@ static void add_lines(struct strbuf *out,
strbuf_complete_line(out);
}
-void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size)
+void strbuf_add_commented_lines(struct strbuf *out, const char *buf,
+ size_t size, char comment_line_char)
{
static char prefix1[3];
static char prefix2[2];
@@ -376,7 +375,8 @@ void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size
add_lines(out, prefix1, prefix2, buf, size);
}
-void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...)
+void strbuf_commented_addf(struct strbuf *sb, char comment_line_char,
+ const char *fmt, ...)
{
va_list params;
struct strbuf buf = STRBUF_INIT;
@@ -386,7 +386,7 @@ void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...)
strbuf_vaddf(&buf, fmt, params);
va_end(params);
- strbuf_add_commented_lines(sb, buf.buf, buf.len);
+ strbuf_add_commented_lines(sb, buf.buf, buf.len, comment_line_char);
if (incomplete_line)
sb->buf[--sb->len] = '\0';
@@ -810,25 +810,6 @@ void strbuf_addstr_xml_quoted(struct strbuf *buf, const char *s)
}
}
-int is_rfc3986_reserved_or_unreserved(char ch)
-{
- if (is_rfc3986_unreserved(ch))
- return 1;
- switch (ch) {
- case '!': case '*': case '\'': case '(': case ')': case ';':
- case ':': case '@': case '&': case '=': case '+': case '$':
- case ',': case '/': case '?': case '#': case '[': case ']':
- return 1;
- }
- return 0;
-}
-
-int is_rfc3986_unreserved(char ch)
-{
- return isalnum(ch) ||
- ch == '-' || ch == '_' || ch == '.' || ch == '~';
-}
-
static void strbuf_add_urlencode(struct strbuf *sb, const char *s, size_t len,
char_predicate allow_unencoded_fn)
{
@@ -899,42 +880,6 @@ void strbuf_humanise_rate(struct strbuf *buf, off_t bytes)
strbuf_humanise(buf, bytes, 1);
}
-void strbuf_add_absolute_path(struct strbuf *sb, const char *path)
-{
- if (!*path)
- die("The empty string is not a valid path");
- if (!is_absolute_path(path)) {
- struct stat cwd_stat, pwd_stat;
- size_t orig_len = sb->len;
- char *cwd = xgetcwd();
- char *pwd = getenv("PWD");
- if (pwd && strcmp(pwd, cwd) &&
- !stat(cwd, &cwd_stat) &&
- (cwd_stat.st_dev || cwd_stat.st_ino) &&
- !stat(pwd, &pwd_stat) &&
- pwd_stat.st_dev == cwd_stat.st_dev &&
- pwd_stat.st_ino == cwd_stat.st_ino)
- strbuf_addstr(sb, pwd);
- else
- strbuf_addstr(sb, cwd);
- if (sb->len > orig_len && !is_dir_sep(sb->buf[sb->len - 1]))
- strbuf_addch(sb, '/');
- free(cwd);
- }
- strbuf_addstr(sb, path);
-}
-
-void strbuf_add_real_path(struct strbuf *sb, const char *path)
-{
- if (sb->len) {
- struct strbuf resolved = STRBUF_INIT;
- strbuf_realpath(&resolved, path, 1);
- strbuf_addbuf(sb, &resolved);
- strbuf_release(&resolved);
- } else
- strbuf_realpath(sb, path, 1);
-}
-
int printf_ln(const char *fmt, ...)
{
int ret;
@@ -1079,21 +1024,6 @@ void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm,
strbuf_setlen(sb, sb->len + len);
}
-void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
- const struct object_id *oid, int abbrev_len)
-{
- int r;
- strbuf_grow(sb, GIT_MAX_HEXSZ + 1);
- r = repo_find_unique_abbrev_r(repo, sb->buf + sb->len, oid, abbrev_len);
- strbuf_setlen(sb, sb->len + r);
-}
-
-void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
- int abbrev_len)
-{
- strbuf_repo_add_unique_abbrev(sb, the_repository, oid, abbrev_len);
-}
-
/*
* Returns the length of a line, without trailing spaces.
*
@@ -1123,10 +1053,10 @@ static size_t cleanup(char *line, size_t len)
*
* If last line does not have a newline at the end, one is added.
*
- * Enable skip_comments to skip every line starting with comment
- * character.
+ * Pass a non-NUL comment_line_char to skip every line starting
+ * with it.
*/
-void strbuf_stripspace(struct strbuf *sb, int skip_comments)
+void strbuf_stripspace(struct strbuf *sb, char comment_line_char)
{
size_t empties = 0;
size_t i, j, len, newlen;
@@ -1139,7 +1069,8 @@ void strbuf_stripspace(struct strbuf *sb, int skip_comments)
eol = memchr(sb->buf + i, '\n', sb->len - i);
len = eol ? eol - (sb->buf + i) + 1 : sb->len - i;
- if (skip_comments && len && sb->buf[i] == comment_line_char) {
+ if (comment_line_char && len &&
+ sb->buf[i] == comment_line_char) {
newlen = 0;
continue;
}
@@ -1160,26 +1091,6 @@ void strbuf_stripspace(struct strbuf *sb, int skip_comments)
strbuf_setlen(sb, j);
}
-int strbuf_normalize_path(struct strbuf *src)
-{
- struct strbuf dst = STRBUF_INIT;
-
- strbuf_grow(&dst, src->len);
- if (normalize_path_copy(dst.buf, src->buf) < 0) {
- strbuf_release(&dst);
- return -1;
- }
-
- /*
- * normalize_path does not tell us the new length, so we have to
- * compute it by looking for the new NUL it placed
- */
- strbuf_setlen(&dst, strlen(dst.buf));
- strbuf_swap(src, &dst);
- strbuf_release(&dst);
- return 0;
-}
-
void strbuf_strip_file_from_path(struct strbuf *sb)
{
char *path_sep = find_last_dir_sep(sb->buf);
diff --git a/strbuf.h b/strbuf.h
index 3dfeadb44c..8903195416 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -1,6 +1,14 @@
#ifndef STRBUF_H
#define STRBUF_H
+/*
+ * NOTE FOR STRBUF DEVELOPERS
+ *
+ * strbuf is a low-level primitive; as such it should interact only
+ * with other low-level primitives. Do not introduce new functions
+ * which interact with higher-level APIs.
+ */
+
struct string_list;
/**
@@ -72,10 +80,6 @@ struct strbuf {
extern char strbuf_slopbuf[];
#define STRBUF_INIT { .buf = strbuf_slopbuf }
-/*
- * Predeclare this here, since cache.h includes this file before it defines the
- * struct.
- */
struct object_id;
/**
@@ -283,7 +287,8 @@ void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
* by a comment character and a blank.
*/
void strbuf_add_commented_lines(struct strbuf *out,
- const char *buf, size_t size);
+ const char *buf, size_t size,
+ char comment_line_char);
/**
@@ -412,8 +417,8 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
* Add a formatted string prepended by a comment character and a
* blank to the buffer.
*/
-__attribute__((format (printf, 2, 3)))
-void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...);
+__attribute__((format (printf, 3, 4)))
+void strbuf_commented_addf(struct strbuf *sb, char comment_line_char, const char *fmt, ...);
__attribute__((format (printf,2,0)))
void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
@@ -528,28 +533,6 @@ int strbuf_getwholeline_fd(struct strbuf *sb, int fd, int term);
int strbuf_getcwd(struct strbuf *sb);
/**
- * Add a path to a buffer, converting a relative path to an
- * absolute one in the process. Symbolic links are not
- * resolved.
- */
-void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
-
-/**
- * Canonize `path` (make it absolute, resolve symlinks, remove extra
- * slashes) and append it to `sb`. Die with an informative error
- * message if there is a problem.
- *
- * The directory part of `path` (i.e., everything up to the last
- * dir_sep) must denote a valid, existing directory, but the last
- * component need not exist.
- *
- * Callers that don't mind links should use the more lightweight
- * strbuf_add_absolute_path() instead.
- */
-void strbuf_add_real_path(struct strbuf *sb, const char *path);
-
-
-/**
* Normalize in-place the path contained in the strbuf. See
* normalize_path_copy() for details. If an error occurs, the contents of "sb"
* are left untouched, and -1 is returned.
@@ -557,10 +540,11 @@ void strbuf_add_real_path(struct strbuf *sb, const char *path);
int strbuf_normalize_path(struct strbuf *sb);
/**
- * Strip whitespace from a buffer. The second parameter controls if
- * comments are considered contents to be removed or not.
+ * Strip whitespace from a buffer. If comment_line_char is non-NUL,
+ * then lines beginning with that character are considered comments,
+ * thus removed.
*/
-void strbuf_stripspace(struct strbuf *buf, int skip_comments);
+void strbuf_stripspace(struct strbuf *buf, char comment_line_char);
static inline int strbuf_strip_suffix(struct strbuf *sb, const char *suffix)
{
@@ -630,16 +614,6 @@ void strbuf_add_separated_string_list(struct strbuf *str,
*/
void strbuf_list_free(struct strbuf **list);
-/**
- * Add the abbreviation, as generated by repo_find_unique_abbrev(), of `sha1` to
- * the strbuf `sb`.
- */
-struct repository;
-void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
- const struct object_id *oid, int abbrev_len);
-void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
- int abbrev_len);
-
/*
* Remove the filename from the provided path string. If the path
* contains a trailing separator, then the path is considered a directory
@@ -704,9 +678,6 @@ int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
typedef int (*char_predicate)(char ch);
-int is_rfc3986_unreserved(char ch);
-int is_rfc3986_reserved_or_unreserved(char ch);
-
void strbuf_addstr_urlencode(struct strbuf *sb, const char *name,
char_predicate allow_unencoded_fn);
diff --git a/streaming.c b/streaming.c
index 21e39585e8..49791ab958 100644
--- a/streaming.c
+++ b/streaming.c
@@ -7,7 +7,7 @@
#include "streaming.h"
#include "repository.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "replace-object.h"
#include "packfile.h"
#include "wrapper.h"
diff --git a/submodule-config.c b/submodule-config.c
index 7fc0812b64..8a247b3be0 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -4,14 +4,16 @@
#include "environment.h"
#include "gettext.h"
#include "hex.h"
+#include "path.h"
#include "repository.h"
#include "config.h"
#include "submodule-config.h"
#include "submodule.h"
#include "strbuf.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "parse-options.h"
+#include "thread-utils.h"
#include "tree-walk.h"
/*
@@ -339,26 +341,6 @@ int option_fetch_parse_recurse_submodules(const struct option *opt,
return 0;
}
-static int parse_update_recurse(const char *opt, const char *arg,
- int die_on_error)
-{
- switch (git_parse_maybe_bool(arg)) {
- case 1:
- return RECURSE_SUBMODULES_ON;
- case 0:
- return RECURSE_SUBMODULES_OFF;
- default:
- if (die_on_error)
- die("bad %s argument: %s", opt, arg);
- return RECURSE_SUBMODULES_ERROR;
- }
-}
-
-int parse_update_recurse_submodules_arg(const char *opt, const char *arg)
-{
- return parse_update_recurse(opt, arg, 1);
-}
-
static int parse_push_recurse(const char *opt, const char *arg,
int die_on_error)
{
diff --git a/submodule-config.h b/submodule-config.h
index c2045875bb..fda6ad0162 100644
--- a/submodule-config.h
+++ b/submodule-config.h
@@ -55,7 +55,6 @@ int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
struct option;
int option_fetch_parse_recurse_submodules(const struct option *opt,
const char *arg, int unset);
-int parse_update_recurse_submodules_arg(const char *opt, const char *arg);
int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
void repo_read_gitmodules(struct repository *repo, int skip_if_read);
void gitmodules_config_oid(const struct object_id *commit_oid);
diff --git a/submodule.c b/submodule.c
index 2e78f51349..cba36d6080 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "abspath.h"
#include "alloc.h"
#include "repository.h"
@@ -20,14 +20,16 @@
#include "strvec.h"
#include "blob.h"
#include "thread-utils.h"
+#include "path.h"
#include "quote.h"
#include "remote.h"
#include "worktree.h"
#include "parse-options.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "commit-reach.h"
+#include "read-cache-ll.h"
#include "setup.h"
#include "shallow.h"
#include "trace2.h"
@@ -36,6 +38,10 @@ static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
static int initialized_fetch_ref_tips;
static struct oid_array ref_tips_before_fetch;
static struct oid_array ref_tips_after_fetch;
+#define STATUS_PORCELAIN_START_ERROR \
+ N_("could not run 'git status --porcelain=2' in submodule %s")
+#define STATUS_PORCELAIN_FAIL_ERROR \
+ N_("'git status --porcelain=2' failed in submodule %s")
/*
* Check if the .gitmodules file is unmerged. Parsing of the .gitmodules file
@@ -232,21 +238,12 @@ int git_default_submodule_config(const char *var, const char *value,
return 0;
}
-int option_parse_recurse_submodules_worktree_updater(const struct option *opt,
- const char *arg, int unset)
+void set_config_update_recurse_submodules(int value)
{
- if (unset) {
- config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
- return 0;
- }
- if (arg)
- config_update_recurse_submodules =
- parse_update_recurse_submodules_arg(opt->long_name,
- arg);
- else
- config_update_recurse_submodules = RECURSE_SUBMODULES_ON;
-
- return 0;
+ if (value < 0 || value > 1)
+ BUG("config_update_recurse_submodules is a boolean");
+ config_update_recurse_submodules = value ? RECURSE_SUBMODULES_ON
+ : RECURSE_SUBMODULES_OFF;
}
/*
@@ -1376,6 +1373,13 @@ int submodule_touches_in_range(struct repository *r,
return ret;
}
+struct submodule_parallel_status {
+ size_t index_count;
+ int result;
+
+ struct string_list *submodule_names;
+};
+
struct submodule_parallel_fetch {
/*
* The index of the last index entry processed by
@@ -1458,6 +1462,12 @@ struct fetch_task {
struct oid_array *commits; /* Ensure these commits are fetched */
};
+struct status_task {
+ const char *path;
+ struct strbuf out;
+ int ignore_untracked;
+};
+
/**
* When a submodule is not defined in .gitmodules, we cannot access it
* via the regular submodule-config. Create a fake submodule, which we can
@@ -1877,14 +1887,10 @@ out:
return spf.result;
}
-unsigned is_submodule_modified(const char *path, int ignore_untracked)
+static int verify_submodule_git_directory(const char *path)
{
- struct child_process cp = CHILD_PROCESS_INIT;
- struct strbuf buf = STRBUF_INIT;
- FILE *fp;
- unsigned dirty_submodule = 0;
const char *git_dir;
- int ignore_cp_exit_code = 0;
+ struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, "%s/.git", path);
git_dir = read_gitfile(buf.buf);
@@ -1897,65 +1903,212 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
/* The submodule is not checked out, so it is not modified */
return 0;
}
- strbuf_reset(&buf);
+ strbuf_release(&buf);
+ return 1;
+}
- strvec_pushl(&cp.args, "status", "--porcelain=2", NULL);
+static void prepare_status_porcelain(struct child_process *cp,
+ const char *path, int ignore_untracked)
+{
+ strvec_pushl(&cp->args, "status", "--porcelain=2", NULL);
if (ignore_untracked)
- strvec_push(&cp.args, "-uno");
+ strvec_push(&cp->args, "-uno");
+
+ prepare_submodule_repo_env(&cp->env);
+ cp->git_cmd = 1;
+ cp->no_stdin = 1;
+ cp->out = -1;
+ cp->dir = path;
+}
+
+static int parse_status_porcelain(char *str, size_t len,
+ unsigned *dirty_submodule,
+ int ignore_untracked)
+{
+ /* regular untracked files */
+ if (str[0] == '?')
+ *dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED;
+
+ if (str[0] == 'u' ||
+ str[0] == '1' ||
+ str[0] == '2') {
+ /* T = line type, XY = status, SSSS = submodule state */
+ if (len < strlen("T XY SSSS"))
+ BUG("invalid status --porcelain=2 line %s",
+ str);
+
+ if (str[5] == 'S' && str[8] == 'U')
+ /* nested untracked file */
+ *dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED;
+
+ if (str[0] == 'u' ||
+ str[0] == '2' ||
+ memcmp(str + 5, "S..U", 4))
+ /* other change */
+ *dirty_submodule |= DIRTY_SUBMODULE_MODIFIED;
+ }
- prepare_submodule_repo_env(&cp.env);
- cp.git_cmd = 1;
- cp.no_stdin = 1;
- cp.out = -1;
- cp.dir = path;
+ if ((*dirty_submodule & DIRTY_SUBMODULE_MODIFIED) &&
+ ((*dirty_submodule & DIRTY_SUBMODULE_UNTRACKED) ||
+ ignore_untracked)) {
+ /*
+ * We're not interested in any further information from
+ * the child any more, neither output nor its exit code.
+ */
+ return 1;
+ }
+ return 0;
+}
+
+unsigned is_submodule_modified(const char *path, int ignore_untracked)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ struct strbuf buf = STRBUF_INIT;
+ FILE *fp;
+ unsigned dirty_submodule = 0;
+ int ignore_cp_exit_code = 0;
+
+ if (!verify_submodule_git_directory(path))
+ return 0;
+
+ prepare_status_porcelain(&cp, path, ignore_untracked);
if (start_command(&cp))
- die(_("Could not run 'git status --porcelain=2' in submodule %s"), path);
+ die(_(STATUS_PORCELAIN_START_ERROR), path);
fp = xfdopen(cp.out, "r");
while (strbuf_getwholeline(&buf, fp, '\n') != EOF) {
- /* regular untracked files */
- if (buf.buf[0] == '?')
- dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED;
-
- if (buf.buf[0] == 'u' ||
- buf.buf[0] == '1' ||
- buf.buf[0] == '2') {
- /* T = line type, XY = status, SSSS = submodule state */
- if (buf.len < strlen("T XY SSSS"))
- BUG("invalid status --porcelain=2 line %s",
- buf.buf);
-
- if (buf.buf[5] == 'S' && buf.buf[8] == 'U')
- /* nested untracked file */
- dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED;
-
- if (buf.buf[0] == 'u' ||
- buf.buf[0] == '2' ||
- memcmp(buf.buf + 5, "S..U", 4))
- /* other change */
- dirty_submodule |= DIRTY_SUBMODULE_MODIFIED;
- }
+ char *str = buf.buf;
+ const size_t len = buf.len;
- if ((dirty_submodule & DIRTY_SUBMODULE_MODIFIED) &&
- ((dirty_submodule & DIRTY_SUBMODULE_UNTRACKED) ||
- ignore_untracked)) {
- /*
- * We're not interested in any further information from
- * the child any more, neither output nor its exit code.
- */
- ignore_cp_exit_code = 1;
+ ignore_cp_exit_code = parse_status_porcelain(str, len, &dirty_submodule,
+ ignore_untracked);
+ if (ignore_cp_exit_code)
break;
- }
}
fclose(fp);
if (finish_command(&cp) && !ignore_cp_exit_code)
- die(_("'git status --porcelain=2' failed in submodule %s"), path);
+ die(_(STATUS_PORCELAIN_FAIL_ERROR), path);
strbuf_release(&buf);
return dirty_submodule;
}
+static struct status_task *
+get_status_task_from_index(struct submodule_parallel_status *sps,
+ struct strbuf *err)
+{
+ for (; sps->index_count < sps->submodule_names->nr; sps->index_count++) {
+ struct submodule_status_util *util = sps->submodule_names->items[sps->index_count].util;
+ struct status_task *task;
+
+ if (!verify_submodule_git_directory(util->path))
+ continue;
+
+ task = xmalloc(sizeof(*task));
+ task->path = util->path;
+ task->ignore_untracked = util->ignore_untracked;
+ strbuf_init(&task->out, 0);
+ sps->index_count++;
+ return task;
+ }
+ return NULL;
+}
+
+static int get_next_submodule_status(struct child_process *cp,
+ struct strbuf *err, void *data,
+ void **task_cb)
+{
+ struct submodule_parallel_status *sps = data;
+ struct status_task *task = get_status_task_from_index(sps, err);
+
+ if (!task)
+ return 0;
+
+ child_process_init(cp);
+ prepare_submodule_repo_env_in_gitdir(&cp->env);
+ prepare_status_porcelain(cp, task->path, task->ignore_untracked);
+ *task_cb = task;
+ return 1;
+}
+
+static int status_start_failure(struct strbuf *err,
+ void *cb, void *task_cb)
+{
+ struct submodule_parallel_status *sps = cb;
+ struct status_task *task = task_cb;
+
+ sps->result = 1;
+ strbuf_addf(err, _(STATUS_PORCELAIN_START_ERROR), task->path);
+ return 0;
+}
+
+static void status_on_stderr_output(struct strbuf *out,
+ size_t offset,
+ void *cb, void *task_cb)
+{
+ struct status_task *task = task_cb;
+
+ strbuf_add(&task->out, out->buf + offset, out->len - offset);
+ strbuf_setlen(out, offset);
+}
+
+static int status_finish(int retvalue, struct strbuf *err,
+ void *cb, void *task_cb)
+{
+ struct submodule_parallel_status *sps = cb;
+ struct status_task *task = task_cb;
+ struct string_list_item *it =
+ string_list_lookup(sps->submodule_names, task->path);
+ struct submodule_status_util *util = it->util;
+ struct string_list list = STRING_LIST_INIT_DUP;
+ struct string_list_item *item;
+
+ if (retvalue) {
+ sps->result = 1;
+ strbuf_addf(err, _(STATUS_PORCELAIN_FAIL_ERROR), task->path);
+ }
+
+ string_list_split(&list, task->out.buf, '\n', -1);
+ for_each_string_list_item(item, &list) {
+ if (parse_status_porcelain(item->string,
+ strlen(item->string),
+ &util->dirty_submodule,
+ util->ignore_untracked))
+ break;
+ }
+ string_list_clear(&list, 0);
+ strbuf_release(&task->out);
+ free(task);
+
+ return 0;
+}
+
+int get_submodules_status(struct string_list *submodules,
+ int max_parallel_jobs)
+{
+ struct submodule_parallel_status sps = {
+ .submodule_names = submodules,
+ };
+ const struct run_process_parallel_opts opts = {
+ .tr2_category = "submodule",
+ .tr2_label = "parallel/status",
+
+ .processes = max_parallel_jobs,
+
+ .get_next_task = get_next_submodule_status,
+ .start_failure = status_start_failure,
+ .on_stderr_output = status_on_stderr_output,
+ .task_finished = status_finish,
+ .data = &sps,
+ };
+
+ string_list_sort(sps.submodule_names);
+ run_processes_parallel(&opts);
+
+ return sps.result;
+}
+
int submodule_uses_gitfile(const char *path)
{
struct child_process cp = CHILD_PROCESS_INIT;
diff --git a/submodule.h b/submodule.h
index c55a25ca37..9bda93272c 100644
--- a/submodule.h
+++ b/submodule.h
@@ -41,6 +41,13 @@ struct submodule_update_strategy {
.type = SM_UPDATE_UNSPECIFIED, \
}
+struct submodule_status_util {
+ int changed, ignore_untracked;
+ unsigned dirty_submodule, newmode;
+ struct cache_entry *ce;
+ const char *path;
+};
+
int is_gitmodules_unmerged(struct index_state *istate);
int is_writing_gitmodules_ok(void);
int is_staging_gitmodules_ok(struct index_state *istate);
@@ -51,9 +58,9 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *,
const char *path);
int git_default_submodule_config(const char *var, const char *value, void *cb);
-struct option;
-int option_parse_recurse_submodules_worktree_updater(const struct option *opt,
- const char *arg, int unset);
+/* Sets static state 'config_update_recurse_submodules'. 'value' must be 0 or 1. */
+void set_config_update_recurse_submodules(int value);
+
int is_tree_submodule_active(struct repository *repo,
const struct object_id *treeish_name,
const char *path);
@@ -94,6 +101,8 @@ int fetch_submodules(struct repository *r,
int command_line_option,
int default_option,
int quiet, int max_parallel_jobs);
+int get_submodules_status(struct string_list *submodules,
+ int max_parallel_jobs);
unsigned is_submodule_modified(const char *path, int ignore_untracked);
int submodule_uses_gitfile(const char *path);
diff --git a/symlinks.c b/symlinks.c
index 27ecc93693..b29e340c2d 100644
--- a/symlinks.c
+++ b/symlinks.c
@@ -1,6 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "gettext.h"
#include "setup.h"
+#include "symlinks.h"
static int threaded_check_leading_path(struct cache_def *cache, const char *name,
int len, int warn_on_lstat_err);
diff --git a/symlinks.h b/symlinks.h
new file mode 100644
index 0000000000..7ae3d5b856
--- /dev/null
+++ b/symlinks.h
@@ -0,0 +1,28 @@
+#ifndef SYMLINKS_H
+#define SYMLINKS_H
+
+#include "strbuf.h"
+
+struct cache_def {
+ struct strbuf path;
+ int flags;
+ int track_flags;
+ int prefix_len_stat_func;
+};
+#define CACHE_DEF_INIT { \
+ .path = STRBUF_INIT, \
+}
+static inline void cache_def_clear(struct cache_def *cache)
+{
+ strbuf_release(&cache->path);
+}
+
+int has_symlink_leading_path(const char *name, int len);
+int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
+int check_leading_path(const char *name, int len, int warn_on_lstat_err);
+int has_dirs_only_path(const char *name, int len, int prefix_len);
+void invalidate_lstat_cache(void);
+void schedule_dir_for_removal(const char *name, int len);
+void remove_scheduled_dirs(void);
+
+#endif /* SYMLINKS_H */
diff --git a/t/README b/t/README
index bdfac4cceb..b71a065e4a 100644
--- a/t/README
+++ b/t/README
@@ -442,6 +442,10 @@ GIT_TEST_INDEX_VERSION=<n> exercises the index read/write code path
for the index version specified. Can be set to any valid version
(currently 2, 3, or 4).
+GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=<boolean> if enabled will
+use the boundary-based bitmap traversal algorithm. See the documentation
+of `pack.useBitmapBoundaryTraversal` for more details.
+
GIT_TEST_PACK_SPARSE=<boolean> if disabled will default the pack-objects
builtin to use the non-sparse object walk. This can still be overridden by
the --sparse command-line argument.
diff --git a/t/helper/test-bloom.c b/t/helper/test-bloom.c
index d2b30d644d..aabe31d724 100644
--- a/t/helper/test-bloom.c
+++ b/t/helper/test-bloom.c
@@ -2,6 +2,7 @@
#include "bloom.h"
#include "hex.h"
#include "commit.h"
+#include "repository.h"
#include "setup.h"
static struct bloom_filter_settings settings = DEFAULT_BLOOM_FILTER_SETTINGS;
diff --git a/t/helper/test-cache-tree.c b/t/helper/test-cache-tree.c
index cdaf5046f5..e7236392c8 100644
--- a/t/helper/test-cache-tree.c
+++ b/t/helper/test-cache-tree.c
@@ -1,11 +1,12 @@
#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
-#include "cache.h"
#include "gettext.h"
#include "hex.h"
#include "tree.h"
#include "cache-tree.h"
#include "parse-options.h"
+#include "read-cache-ll.h"
+#include "repository.h"
#include "setup.h"
static char const * const test_cache_tree_usage[] = {
diff --git a/t/helper/test-ctype.c b/t/helper/test-ctype.c
index 71a1a5c9b0..e5659df40b 100644
--- a/t/helper/test-ctype.c
+++ b/t/helper/test-ctype.c
@@ -27,6 +27,8 @@ static int is_in(const char *s, int ch)
if (is_in(s, i) != t(i)) \
report_error(#t, i); \
} \
+ if (t(EOF)) \
+ report_error(#t, EOF); \
}
#define DIGIT "0123456789"
diff --git a/t/helper/test-dump-cache-tree.c b/t/helper/test-dump-cache-tree.c
index 2041ca1857..c38f546e4f 100644
--- a/t/helper/test-dump-cache-tree.c
+++ b/t/helper/test-dump-cache-tree.c
@@ -1,9 +1,11 @@
#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
-#include "cache.h"
+#include "hash.h"
#include "hex.h"
#include "tree.h"
#include "cache-tree.h"
+#include "read-cache-ll.h"
+#include "repository.h"
#include "setup.h"
static void dump_one(struct cache_tree *it, const char *pfx, const char *x)
diff --git a/t/helper/test-dump-fsmonitor.c b/t/helper/test-dump-fsmonitor.c
index 7c6f50158b..4f215fea02 100644
--- a/t/helper/test-dump-fsmonitor.c
+++ b/t/helper/test-dump-fsmonitor.c
@@ -1,5 +1,6 @@
#include "test-tool.h"
-#include "cache.h"
+#include "read-cache-ll.h"
+#include "repository.h"
#include "setup.h"
int cmd__dump_fsmonitor(int ac UNUSED, const char **av UNUSED)
diff --git a/t/helper/test-dump-split-index.c b/t/helper/test-dump-split-index.c
index d1badd7112..5cf0b26dca 100644
--- a/t/helper/test-dump-split-index.c
+++ b/t/helper/test-dump-split-index.c
@@ -1,7 +1,8 @@
#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
-#include "cache.h"
#include "hex.h"
+#include "read-cache-ll.h"
+#include "repository.h"
#include "setup.h"
#include "split-index.h"
#include "ewah/ewok.h"
diff --git a/t/helper/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c
index 9225ced534..b4af9712fe 100644
--- a/t/helper/test-dump-untracked-cache.c
+++ b/t/helper/test-dump-untracked-cache.c
@@ -1,8 +1,9 @@
#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
-#include "cache.h"
#include "dir.h"
#include "hex.h"
+#include "read-cache-ll.h"
+#include "repository.h"
#include "setup.h"
static int compare_untracked(const void *a_, const void *b_)
diff --git a/t/helper/test-example-decorate.c b/t/helper/test-example-decorate.c
index 2cf302ffcb..2ed910adaa 100644
--- a/t/helper/test-example-decorate.c
+++ b/t/helper/test-example-decorate.c
@@ -2,6 +2,7 @@
#include "git-compat-util.h"
#include "object.h"
#include "decorate.h"
+#include "repository.h"
int cmd__example_decorate(int argc UNUSED, const char **argv UNUSED)
{
diff --git a/t/helper/test-fsmonitor-client.c b/t/helper/test-fsmonitor-client.c
index 14522b4c47..58d1dc5fc8 100644
--- a/t/helper/test-fsmonitor-client.c
+++ b/t/helper/test-fsmonitor-client.c
@@ -4,9 +4,10 @@
*/
#include "test-tool.h"
-#include "cache.h"
#include "parse-options.h"
#include "fsmonitor-ipc.h"
+#include "read-cache-ll.h"
+#include "repository.h"
#include "setup.h"
#include "thread-utils.h"
#include "trace2.h"
diff --git a/t/helper/test-hash-speed.c b/t/helper/test-hash-speed.c
index f40d9ad0c2..b235da594f 100644
--- a/t/helper/test-hash-speed.c
+++ b/t/helper/test-hash-speed.c
@@ -1,5 +1,5 @@
#include "test-tool.h"
-#include "cache.h"
+#include "hash-ll.h"
#define NUM_SECONDS 3
diff --git a/t/helper/test-index-version.c b/t/helper/test-index-version.c
index a06c45c1f8..f3c2dbe0a2 100644
--- a/t/helper/test-index-version.c
+++ b/t/helper/test-index-version.c
@@ -1,5 +1,5 @@
#include "test-tool.h"
-#include "cache.h"
+#include "read-cache-ll.h"
int cmd__index_version(int argc UNUSED, const char **argv UNUSED)
{
diff --git a/t/helper/test-lazy-init-name-hash.c b/t/helper/test-lazy-init-name-hash.c
index f23d983c11..187a115d57 100644
--- a/t/helper/test-lazy-init-name-hash.c
+++ b/t/helper/test-lazy-init-name-hash.c
@@ -1,8 +1,10 @@
#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
-#include "cache.h"
#include "environment.h"
+#include "name-hash.h"
#include "parse-options.h"
+#include "read-cache-ll.h"
+#include "repository.h"
#include "setup.h"
#include "trace.h"
diff --git a/t/helper/test-match-trees.c b/t/helper/test-match-trees.c
index 35a2b8005c..d0db5ff26f 100644
--- a/t/helper/test-match-trees.c
+++ b/t/helper/test-match-trees.c
@@ -1,7 +1,8 @@
#include "test-tool.h"
-#include "cache.h"
#include "hex.h"
+#include "match-trees.h"
#include "object-name.h"
+#include "repository.h"
#include "setup.h"
#include "tree.h"
diff --git a/t/helper/test-mergesort.c b/t/helper/test-mergesort.c
index 737e0c5235..42ccc87051 100644
--- a/t/helper/test-mergesort.c
+++ b/t/helper/test-mergesort.c
@@ -1,7 +1,7 @@
#include "test-tool.h"
-#include "cache.h"
#include "mem-pool.h"
#include "mergesort.h"
+#include "strbuf.h"
static uint32_t minstd_rand(uint32_t *state)
{
diff --git a/t/helper/test-oid-array.c b/t/helper/test-oid-array.c
index 30e1947b90..eef68833b7 100644
--- a/t/helper/test-oid-array.c
+++ b/t/helper/test-oid-array.c
@@ -1,8 +1,8 @@
#include "test-tool.h"
-#include "cache.h"
#include "hex.h"
#include "oid-array.h"
#include "setup.h"
+#include "strbuf.h"
static int print_oid(const struct object_id *oid, void *data)
{
diff --git a/t/helper/test-oidmap.c b/t/helper/test-oidmap.c
index d9931544a8..bd30244a54 100644
--- a/t/helper/test-oidmap.c
+++ b/t/helper/test-oidmap.c
@@ -2,6 +2,7 @@
#include "hex.h"
#include "object-name.h"
#include "oidmap.h"
+#include "repository.h"
#include "setup.h"
#include "strbuf.h"
#include "string-list.h"
diff --git a/t/helper/test-oidtree.c b/t/helper/test-oidtree.c
index 5b98f2f70a..c7a1d4c642 100644
--- a/t/helper/test-oidtree.c
+++ b/t/helper/test-oidtree.c
@@ -1,8 +1,8 @@
#include "test-tool.h"
-#include "cache.h"
#include "hex.h"
#include "oidtree.h"
#include "setup.h"
+#include "strbuf.h"
static enum cb_next print_oid(const struct object_id *oid, void *data UNUSED)
{
diff --git a/t/helper/test-pack-mtimes.c b/t/helper/test-pack-mtimes.c
index 0f3fbeec53..67a964ef90 100644
--- a/t/helper/test-pack-mtimes.c
+++ b/t/helper/test-pack-mtimes.c
@@ -1,7 +1,7 @@
#include "test-tool.h"
#include "hex.h"
#include "strbuf.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "packfile.h"
#include "pack-mtimes.h"
#include "setup.h"
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index b66039e575..00fa281a9c 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -1,6 +1,6 @@
#include "test-tool.h"
-#include "cache.h"
#include "parse-options.h"
+#include "strbuf.h"
#include "string-list.h"
#include "trace2.h"
diff --git a/t/helper/test-partial-clone.c b/t/helper/test-partial-clone.c
index 362bd64a4c..910a128614 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.h"
+#include "object-store-ll.h"
#include "setup.h"
/*
diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c
index 6355c9e4b6..70396fa384 100644
--- a/t/helper/test-path-utils.c
+++ b/t/helper/test-path-utils.c
@@ -1,7 +1,8 @@
#include "test-tool.h"
-#include "cache.h"
#include "abspath.h"
#include "environment.h"
+#include "path.h"
+#include "read-cache-ll.h"
#include "setup.h"
#include "string-list.h"
#include "trace.h"
diff --git a/t/helper/test-reach.c b/t/helper/test-reach.c
index 5b6f217441..ef58f10c2d 100644
--- a/t/helper/test-reach.c
+++ b/t/helper/test-reach.c
@@ -139,7 +139,7 @@ int cmd__reach(int ac, const char **av)
printf("%s(X,_,_,0,0):%d\n", av[1], can_all_from_reach_with_flag(&X_obj, 2, 4, 0, 0));
} else if (!strcmp(av[1], "commit_contains")) {
- struct ref_filter filter;
+ struct ref_filter filter = REF_FILTER_INIT;
struct contains_cache cache;
init_contains_cache(&cache);
diff --git a/t/helper/test-read-cache.c b/t/helper/test-read-cache.c
index a4c24d0e42..56c2d25f35 100644
--- a/t/helper/test-read-cache.c
+++ b/t/helper/test-read-cache.c
@@ -1,7 +1,8 @@
#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
-#include "cache.h"
#include "config.h"
+#include "read-cache-ll.h"
+#include "repository.h"
#include "setup.h"
#include "wrapper.h"
diff --git a/t/helper/test-read-graph.c b/t/helper/test-read-graph.c
index 3ac496e27e..8c7a83f578 100644
--- a/t/helper/test-read-graph.c
+++ b/t/helper/test-read-graph.c
@@ -1,7 +1,7 @@
#include "test-tool.h"
#include "commit-graph.h"
#include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "bloom.h"
#include "setup.h"
diff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c
index 05c4f2b262..e9a444ddba 100644
--- a/t/helper/test-read-midx.c
+++ b/t/helper/test-read-midx.c
@@ -1,10 +1,10 @@
#include "test-tool.h"
-#include "cache.h"
#include "hex.h"
#include "midx.h"
#include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "pack-bitmap.h"
+#include "packfile.h"
#include "setup.h"
static int read_midx_file(const char *object_dir, int show_objects)
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 6d8f844e9c..13865425dd 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -3,8 +3,11 @@
#include "refs.h"
#include "setup.h"
#include "worktree.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "path.h"
#include "repository.h"
+#include "strbuf.h"
+#include "revision.h"
struct flag_definition {
const char *name;
@@ -116,8 +119,16 @@ static struct flag_definition pack_flags[] = { FLAG_DEF(PACK_REFS_PRUNE),
static int cmd_pack_refs(struct ref_store *refs, const char **argv)
{
unsigned int flags = arg_flags(*argv++, "flags", pack_flags);
+ static struct ref_exclusions exclusions = REF_EXCLUSIONS_INIT;
+ static struct string_list included_refs = STRING_LIST_INIT_NODUP;
+ struct pack_refs_opts pack_opts = { .flags = flags,
+ .exclusions = &exclusions,
+ .includes = &included_refs };
- return refs_pack_refs(refs, flags);
+ if (pack_opts.flags & PACK_REFS_ALL)
+ string_list_append(pack_opts.includes, "*");
+
+ return refs_pack_refs(refs, &pack_opts);
}
static int cmd_create_symref(struct ref_store *refs, const char **argv)
@@ -175,6 +186,15 @@ static int cmd_for_each_ref(struct ref_store *refs, const char **argv)
return refs_for_each_ref_in(refs, prefix, each_ref, NULL);
}
+static int cmd_for_each_ref__exclude(struct ref_store *refs, const char **argv)
+{
+ const char *prefix = notnull(*argv++, "prefix");
+ const char **exclude_patterns = argv;
+
+ return refs_for_each_fullref_in(refs, prefix, exclude_patterns, each_ref,
+ NULL);
+}
+
static int cmd_resolve_ref(struct ref_store *refs, const char **argv)
{
struct object_id oid = *null_oid();
@@ -307,6 +327,7 @@ static struct command commands[] = {
{ "delete-refs", cmd_delete_refs },
{ "rename-ref", cmd_rename_ref },
{ "for-each-ref", cmd_for_each_ref },
+ { "for-each-ref--exclude", cmd_for_each_ref__exclude },
{ "resolve-ref", cmd_resolve_ref },
{ "verify-ref", cmd_verify_ref },
{ "for-each-reflog", cmd_for_each_reflog },
diff --git a/t/helper/test-reftable.c b/t/helper/test-reftable.c
index 1f0a28cbb6..00237ef0d9 100644
--- a/t/helper/test-reftable.c
+++ b/t/helper/test-reftable.c
@@ -1,3 +1,4 @@
+#include "reftable/system.h"
#include "reftable/reftable-tests.h"
#include "test-tool.h"
diff --git a/t/helper/test-repository.c b/t/helper/test-repository.c
index bafd2a5bf9..4cd8a952e5 100644
--- a/t/helper/test-repository.c
+++ b/t/helper/test-repository.c
@@ -4,7 +4,7 @@
#include "config.h"
#include "environment.h"
#include "hex.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "object.h"
#include "repository.h"
#include "setup.h"
diff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c
index 0c62b9de18..f346951bc2 100644
--- a/t/helper/test-revision-walking.c
+++ b/t/helper/test-revision-walking.c
@@ -11,6 +11,7 @@
#include "test-tool.h"
#include "commit.h"
#include "diff.h"
+#include "repository.h"
#include "revision.h"
#include "setup.h"
diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c
index c0ed8722c8..86ca155bda 100644
--- a/t/helper/test-run-command.c
+++ b/t/helper/test-run-command.c
@@ -49,6 +49,20 @@ static int no_job(struct child_process *cp UNUSED,
return 0;
}
+static void on_stderr_output(struct strbuf *out,
+ size_t offset,
+ void *pp_cb UNUSED,
+ void *pp_task_cb UNUSED)
+{
+ struct string_list list = STRING_LIST_INIT_DUP;
+ struct string_list_item *item;
+
+ string_list_split(&list, out->buf + offset, '\n', -1);
+ for_each_string_list_item(item, &list)
+ fprintf(stderr, "on_stderr_output: %s\n", item->string);
+ string_list_clear(&list, 0);
+}
+
static int task_finished(int result UNUSED,
struct strbuf *err,
void *pp_cb UNUSED,
@@ -436,6 +450,12 @@ int cmd__run_command(int argc, const char **argv)
opts.ungroup = 1;
}
+ if (!strcmp(argv[1], "--on-stderr-output")) {
+ argv += 1;
+ argc -= 1;
+ opts.on_stderr_output = on_stderr_output;
+ }
+
jobs = atoi(argv[2]);
strvec_clear(&proc.args);
strvec_pushv(&proc.args, (const char **)argv + 3);
diff --git a/t/helper/test-scrap-cache-tree.c b/t/helper/test-scrap-cache-tree.c
index 3fecd06d17..0a816a96e2 100644
--- a/t/helper/test-scrap-cache-tree.c
+++ b/t/helper/test-scrap-cache-tree.c
@@ -1,7 +1,8 @@
#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
-#include "cache.h"
#include "lockfile.h"
+#include "read-cache-ll.h"
+#include "repository.h"
#include "setup.h"
#include "tree.h"
#include "cache-tree.h"
diff --git a/t/helper/test-sha1.c b/t/helper/test-sha1.c
index 71fe5c6145..dcb7f6c003 100644
--- a/t/helper/test-sha1.c
+++ b/t/helper/test-sha1.c
@@ -1,5 +1,5 @@
#include "test-tool.h"
-#include "cache.h"
+#include "hash-ll.h"
int cmd__sha1(int ac, const char **av)
{
diff --git a/t/helper/test-sha256.c b/t/helper/test-sha256.c
index 0ac6a99d5f..08cf149001 100644
--- a/t/helper/test-sha256.c
+++ b/t/helper/test-sha256.c
@@ -1,5 +1,5 @@
#include "test-tool.h"
-#include "cache.h"
+#include "hash-ll.h"
int cmd__sha256(int ac, const char **av)
{
diff --git a/t/helper/test-strcmp-offset.c b/t/helper/test-strcmp-offset.c
index 96b9a5b529..d8473cf2fc 100644
--- a/t/helper/test-strcmp-offset.c
+++ b/t/helper/test-strcmp-offset.c
@@ -1,5 +1,5 @@
#include "test-tool.h"
-#include "cache.h"
+#include "read-cache-ll.h"
int cmd__strcmp_offset(int argc UNUSED, const char **argv)
{
diff --git a/t/helper/test-string-list.c b/t/helper/test-string-list.c
index 63df88575c..e2aad611d1 100644
--- a/t/helper/test-string-list.c
+++ b/t/helper/test-string-list.c
@@ -1,5 +1,5 @@
#include "test-tool.h"
-#include "cache.h"
+#include "strbuf.h"
#include "string-list.h"
/*
diff --git a/t/helper/test-submodule-config.c b/t/helper/test-submodule-config.c
index 5e462faa9d..9df2f03ac8 100644
--- a/t/helper/test-submodule-config.c
+++ b/t/helper/test-submodule-config.c
@@ -1,6 +1,8 @@
#include "test-tool.h"
#include "config.h"
+#include "hash.h"
#include "object-name.h"
+#include "repository.h"
#include "setup.h"
#include "submodule-config.h"
#include "submodule.h"
diff --git a/t/helper/test-submodule-nested-repo-config.c b/t/helper/test-submodule-nested-repo-config.c
index d31f5e48ab..ecd40ded99 100644
--- a/t/helper/test-submodule-nested-repo-config.c
+++ b/t/helper/test-submodule-nested-repo-config.c
@@ -1,4 +1,5 @@
#include "test-tool.h"
+#include "repository.h"
#include "setup.h"
#include "submodule-config.h"
diff --git a/t/helper/test-submodule.c b/t/helper/test-submodule.c
index 7cbd59922a..356e0a26c5 100644
--- a/t/helper/test-submodule.c
+++ b/t/helper/test-submodule.c
@@ -2,6 +2,7 @@
#include "test-tool-utils.h"
#include "parse-options.h"
#include "remote.h"
+#include "repository.h"
#include "setup.h"
#include "submodule-config.h"
#include "submodule.h"
diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c
index 98f071452a..20c7495f38 100644
--- a/t/helper/test-trace2.c
+++ b/t/helper/test-trace2.c
@@ -3,6 +3,7 @@
#include "run-command.h"
#include "exec-cmd.h"
#include "config.h"
+#include "repository.h"
#include "trace2.h"
typedef int(fn_unit_test)(int argc, const char **argv);
diff --git a/t/helper/test-wildmatch.c b/t/helper/test-wildmatch.c
index a95bb4da9b..b4ff5f986a 100644
--- a/t/helper/test-wildmatch.c
+++ b/t/helper/test-wildmatch.c
@@ -1,4 +1,5 @@
#include "test-tool.h"
+#include "wildmatch.h"
int cmd__wildmatch(int argc, const char **argv)
{
diff --git a/t/helper/test-write-cache.c b/t/helper/test-write-cache.c
index a93417ed3a..f084034d38 100644
--- a/t/helper/test-write-cache.c
+++ b/t/helper/test-write-cache.c
@@ -1,7 +1,8 @@
#define USE_THE_INDEX_VARIABLE
#include "test-tool.h"
-#include "cache.h"
#include "lockfile.h"
+#include "read-cache-ll.h"
+#include "repository.h"
#include "setup.h"
int cmd__write_cache(int argc, const char **argv)
diff --git a/t/lib-credential.sh b/t/lib-credential.sh
index 5ea8bc9f1d..4ad498adbc 100644
--- a/t/lib-credential.sh
+++ b/t/lib-credential.sh
@@ -43,6 +43,8 @@ helper_test_clean() {
reject $1 https example.com store-user
reject $1 https example.com user1
reject $1 https example.com user2
+ reject $1 https example.com user3
+ reject $1 https example.com user4
reject $1 http path.tld user
reject $1 https timeout.tld user
reject $1 https sso.tld
@@ -270,6 +272,35 @@ helper_test() {
password=
EOF
'
+
+ : ${GIT_TEST_LONG_CRED_BUFFER:=1024}
+ # 23 bytes accounts for "wwwauth[]=basic realm=" plus NUL
+ LONG_VALUE_LEN=$((GIT_TEST_LONG_CRED_BUFFER - 23))
+ LONG_VALUE=$(perl -e 'print "a" x shift' $LONG_VALUE_LEN)
+
+ test_expect_success "helper ($HELPER) not confused by long header" '
+ check approve $HELPER <<-\EOF &&
+ protocol=https
+ host=victim.example.com
+ username=user
+ password=to-be-stolen
+ EOF
+
+ check fill $HELPER <<-EOF
+ protocol=https
+ host=badguy.example.com
+ wwwauth[]=basic realm=${LONG_VALUE}host=victim.example.com
+ --
+ protocol=https
+ host=badguy.example.com
+ username=askpass-username
+ password=askpass-password
+ wwwauth[]=basic realm=${LONG_VALUE}host=victim.example.com
+ --
+ askpass: Username for '\''https://badguy.example.com'\'':
+ askpass: Password for '\''https://askpass-username@badguy.example.com'\'':
+ EOF
+ '
}
helper_test_timeout() {
@@ -298,6 +329,64 @@ helper_test_timeout() {
'
}
+helper_test_password_expiry_utc() {
+ HELPER=$1
+
+ test_expect_success "helper ($HELPER) stores password_expiry_utc" '
+ check approve $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=user3
+ password=pass
+ password_expiry_utc=9999999999
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) gets password_expiry_utc" '
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=user3
+ --
+ protocol=https
+ host=example.com
+ username=user3
+ password=pass
+ password_expiry_utc=9999999999
+ --
+ EOF
+ '
+}
+
+helper_test_oauth_refresh_token() {
+ HELPER=$1
+
+ test_expect_success "helper ($HELPER) stores oauth_refresh_token" '
+ check approve $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=user4
+ password=pass
+ oauth_refresh_token=xyzzy
+ EOF
+ '
+
+ test_expect_success "helper ($HELPER) gets oauth_refresh_token" '
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=example.com
+ username=user4
+ --
+ protocol=https
+ host=example.com
+ username=user4
+ password=pass
+ oauth_refresh_token=xyzzy
+ --
+ EOF
+ '
+}
+
write_script askpass <<\EOF
echo >&2 askpass: $*
what=$(echo $1 | cut -d" " -f1 | tr A-Z a-z | tr -cd a-z)
diff --git a/t/lib-diff-alternative.sh b/t/lib-diff-alternative.sh
index a8f5d3274a..c4dc2d46dc 100644
--- a/t/lib-diff-alternative.sh
+++ b/t/lib-diff-alternative.sh
@@ -112,15 +112,36 @@ EOF
STRATEGY=$1
- test_expect_success "$STRATEGY diff from attributes" '
+ test_expect_success "setup attributes files for tests with $STRATEGY" '
+ git checkout -b master &&
echo "file* diff=driver" >.gitattributes &&
- git config diff.driver.algorithm "$STRATEGY" &&
- test_must_fail git diff --no-index file1 file2 > output &&
- cat expect &&
- cat output &&
+ git add file1 file2 .gitattributes &&
+ git commit -m "adding files" &&
+ git checkout -b branchA &&
+ echo "file* diff=driverA" >.gitattributes &&
+ git add .gitattributes &&
+ git commit -m "adding driverA as diff driver" &&
+ git checkout master &&
+ git clone --bare --no-local . bare.git
+ '
+
+ test_expect_success "$STRATEGY diff from attributes" '
+ test_must_fail git -c diff.driver.algorithm=$STRATEGY diff --no-index file1 file2 > output &&
test_cmp expect output
'
+ test_expect_success "diff from attributes with bare repo with source" '
+ git -C bare.git --attr-source=branchA -c diff.driver.algorithm=myers \
+ -c diff.driverA.algorithm=$STRATEGY \
+ diff HEAD:file1 HEAD:file2 >output &&
+ test_cmp expect output
+ '
+
+ test_expect_success "diff from attributes with bare repo with invalid source" '
+ test_must_fail git -C bare.git --attr-source=invalid-branch diff \
+ HEAD:file1 HEAD:file2
+ '
+
test_expect_success "$STRATEGY diff from attributes has valid diffstat" '
echo "file* diff=driver" >.gitattributes &&
git config diff.driver.algorithm "$STRATEGY" &&
diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
index dee14992c5..9acb0d5d19 100644
--- a/t/lib-submodule-update.sh
+++ b/t/lib-submodule-update.sh
@@ -802,7 +802,7 @@ test_submodule_recursing_with_args_common () {
git branch -t no_submodule origin/no_submodule &&
$command no_submodule &&
test_superproject_content origin/no_submodule &&
- ! test_path_is_dir sub1 &&
+ test_path_is_missing sub1 &&
test_must_fail git config -f .git/modules/sub1/config core.worktree &&
test_must_fail git config -f .git/modules/sub1/modules/sub2/config core.worktree
)
diff --git a/t/perf/p2000-sparse-operations.sh b/t/perf/p2000-sparse-operations.sh
index 60d1de0662..8fd2a427c2 100755
--- a/t/perf/p2000-sparse-operations.sh
+++ b/t/perf/p2000-sparse-operations.sh
@@ -129,5 +129,10 @@ test_perf_on_all git grep --cached bogus -- "f2/f1/f1/*"
test_perf_on_all git write-tree
test_perf_on_all git describe --dirty
test_perf_on_all 'echo >>new && git describe --dirty'
+test_perf_on_all git diff-files
+test_perf_on_all git diff-files -- $SPARSE_CONE/a
+test_perf_on_all 'echo >>a && git diff-index HEAD'
+test_perf_on_all git diff-index HEAD "**a"
+test_perf_on_all git diff-index --cached HEAD
test_done
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
index 89b306cb11..26e082f05b 100755
--- a/t/t0003-attributes.sh
+++ b/t/t0003-attributes.sh
@@ -30,8 +30,17 @@ attr_check_quote () {
attr_check_source () {
path="$1" expect="$2" source="$3" git_opts="$4" &&
- git $git_opts check-attr --source $source test -- "$path" >actual 2>err &&
echo "$path: test: $expect" >expect &&
+
+ git $git_opts check-attr --source $source test -- "$path" >actual 2>err &&
+ test_cmp expect actual &&
+ test_must_be_empty err &&
+
+ git $git_opts --attr-source="$source" check-attr test -- "$path" >actual 2>err &&
+ test_cmp expect actual &&
+ test_must_be_empty err
+
+ GIT_ATTR_SOURCE="$source" git $git_opts check-attr test -- "$path" >actual 2>err &&
test_cmp expect actual &&
test_must_be_empty err
}
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
index 35cc8c3b39..81946e87cc 100755
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
@@ -125,7 +125,7 @@ test_expect_success 'update with autocrlf=input' '
munge_cr append dir/two &&
git update-index -- one dir/two &&
differs=$(git diff-index --cached HEAD) &&
- verbose test -z "$differs"
+ test -z "$differs"
'
@@ -138,7 +138,7 @@ test_expect_success 'update with autocrlf=true' '
munge_cr append dir/two &&
git update-index -- one dir/two &&
differs=$(git diff-index --cached HEAD) &&
- verbose test -z "$differs"
+ test -z "$differs"
'
@@ -153,7 +153,7 @@ test_expect_success 'checkout with autocrlf=true' '
test "$one" = $(git hash-object --stdin <one) &&
test "$two" = $(git hash-object --stdin <dir/two) &&
differs=$(git diff-index --cached HEAD) &&
- verbose test -z "$differs"
+ test -z "$differs"
'
test_expect_success 'checkout with autocrlf=input' '
@@ -167,7 +167,7 @@ test_expect_success 'checkout with autocrlf=input' '
test "$one" = $(git hash-object --stdin <one) &&
test "$two" = $(git hash-object --stdin <dir/two) &&
differs=$(git diff-index --cached HEAD) &&
- verbose test -z "$differs"
+ test -z "$differs"
'
test_expect_success 'apply patch (autocrlf=input)' '
@@ -177,7 +177,7 @@ test_expect_success 'apply patch (autocrlf=input)' '
git read-tree --reset -u HEAD &&
git apply patch.file &&
- verbose test "$patched" = "$(git hash-object --stdin <one)"
+ test "$patched" = "$(git hash-object --stdin <one)"
'
test_expect_success 'apply patch --cached (autocrlf=input)' '
@@ -187,7 +187,7 @@ test_expect_success 'apply patch --cached (autocrlf=input)' '
git read-tree --reset -u HEAD &&
git apply --cached patch.file &&
- verbose test "$patched" = $(git rev-parse :one)
+ test "$patched" = $(git rev-parse :one)
'
test_expect_success 'apply patch --index (autocrlf=input)' '
@@ -197,8 +197,8 @@ test_expect_success 'apply patch --index (autocrlf=input)' '
git read-tree --reset -u HEAD &&
git apply --index patch.file &&
- verbose test "$patched" = $(git rev-parse :one) &&
- verbose test "$patched" = $(git hash-object --stdin <one)
+ test "$patched" = $(git rev-parse :one) &&
+ test "$patched" = $(git hash-object --stdin <one)
'
test_expect_success 'apply patch (autocrlf=true)' '
@@ -208,7 +208,7 @@ test_expect_success 'apply patch (autocrlf=true)' '
git read-tree --reset -u HEAD &&
git apply patch.file &&
- verbose test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
+ test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
'
test_expect_success 'apply patch --cached (autocrlf=true)' '
@@ -218,7 +218,7 @@ test_expect_success 'apply patch --cached (autocrlf=true)' '
git read-tree --reset -u HEAD &&
git apply --cached patch.file &&
- verbose test "$patched" = $(git rev-parse :one)
+ test "$patched" = $(git rev-parse :one)
'
test_expect_success 'apply patch --index (autocrlf=true)' '
@@ -228,8 +228,8 @@ test_expect_success 'apply patch --index (autocrlf=true)' '
git read-tree --reset -u HEAD &&
git apply --index patch.file &&
- verbose test "$patched" = $(git rev-parse :one) &&
- verbose test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
+ test "$patched" = $(git rev-parse :one) &&
+ test "$patched" = "$(remove_cr <one | git hash-object --stdin)"
'
test_expect_success '.gitattributes says two is binary' '
@@ -240,7 +240,7 @@ test_expect_success '.gitattributes says two is binary' '
git read-tree --reset -u HEAD &&
! has_cr dir/two &&
- verbose has_cr one &&
+ has_cr one &&
! has_cr three
'
@@ -259,8 +259,8 @@ test_expect_success '.gitattributes says two and three are text' '
echo "t* crlf" >.gitattributes &&
git read-tree --reset -u HEAD &&
- verbose has_cr dir/two &&
- verbose has_cr three
+ has_cr dir/two &&
+ has_cr three
'
test_expect_success 'in-tree .gitattributes (1)' '
@@ -273,7 +273,7 @@ test_expect_success 'in-tree .gitattributes (1)' '
git read-tree --reset -u HEAD &&
! has_cr one &&
- verbose has_cr three
+ has_cr three
'
test_expect_success 'in-tree .gitattributes (2)' '
@@ -283,7 +283,7 @@ test_expect_success 'in-tree .gitattributes (2)' '
git checkout-index -f -q -u -a &&
! has_cr one &&
- verbose has_cr three
+ has_cr three
'
test_expect_success 'in-tree .gitattributes (3)' '
@@ -294,7 +294,7 @@ test_expect_success 'in-tree .gitattributes (3)' '
git checkout-index -u one dir/two three &&
! has_cr one &&
- verbose has_cr three
+ has_cr three
'
test_expect_success 'in-tree .gitattributes (4)' '
@@ -305,7 +305,7 @@ test_expect_success 'in-tree .gitattributes (4)' '
git checkout-index -u .gitattributes &&
! has_cr one &&
- verbose has_cr three
+ has_cr three
'
test_expect_success 'checkout with existing .gitattributes' '
diff --git a/t/t0035-safe-bare-repository.sh b/t/t0035-safe-bare-repository.sh
index 11c15a48aa..038b8b788d 100755
--- a/t/t0035-safe-bare-repository.sh
+++ b/t/t0035-safe-bare-repository.sh
@@ -7,13 +7,26 @@ TEST_PASSES_SANITIZE_LEAK=true
pwd="$(pwd)"
-expect_accepted () {
- git "$@" rev-parse --git-dir
+expect_accepted_implicit () {
+ test_when_finished 'rm "$pwd/trace.perf"' &&
+ GIT_TRACE2_PERF="$pwd/trace.perf" git "$@" rev-parse --git-dir &&
+ # Note: we're intentionally only checking that the bare repo has a
+ # directory *prefix* of $pwd
+ grep -F "implicit-bare-repository:$pwd" "$pwd/trace.perf"
+}
+
+expect_accepted_explicit () {
+ test_when_finished 'rm "$pwd/trace.perf"' &&
+ GIT_DIR="$1" GIT_TRACE2_PERF="$pwd/trace.perf" git rev-parse --git-dir &&
+ ! grep -F "implicit-bare-repository:$pwd" "$pwd/trace.perf"
}
expect_rejected () {
- test_must_fail git "$@" rev-parse --git-dir 2>err &&
- grep -F "cannot use bare repository" err
+ test_when_finished 'rm "$pwd/trace.perf"' &&
+ test_env GIT_TRACE2_PERF="$pwd/trace.perf" \
+ test_must_fail git "$@" rev-parse --git-dir 2>err &&
+ grep -F "cannot use bare repository" err &&
+ grep -F "implicit-bare-repository:$pwd" "$pwd/trace.perf"
}
test_expect_success 'setup bare repo in worktree' '
@@ -22,12 +35,13 @@ test_expect_success 'setup bare repo in worktree' '
'
test_expect_success 'safe.bareRepository unset' '
- expect_accepted -C outer-repo/bare-repo
+ test_unconfig --global safe.bareRepository &&
+ expect_accepted_implicit -C outer-repo/bare-repo
'
test_expect_success 'safe.bareRepository=all' '
test_config_global safe.bareRepository all &&
- expect_accepted -C outer-repo/bare-repo
+ expect_accepted_implicit -C outer-repo/bare-repo
'
test_expect_success 'safe.bareRepository=explicit' '
@@ -47,7 +61,7 @@ test_expect_success 'safe.bareRepository in the repository' '
test_expect_success 'safe.bareRepository on the command line' '
test_config_global safe.bareRepository explicit &&
- expect_accepted -C outer-repo/bare-repo \
+ expect_accepted_implicit -C outer-repo/bare-repo \
-c safe.bareRepository=all
'
@@ -60,4 +74,8 @@ test_expect_success 'safe.bareRepository in included file' '
expect_rejected -C outer-repo/bare-repo
'
+test_expect_success 'no trace when GIT_DIR is explicitly provided' '
+ expect_accepted_explicit "$pwd/outer-repo/bare-repo"
+'
+
test_done
diff --git a/t/t0041-usage.sh b/t/t0041-usage.sh
index c4fc34eb18..9ea974b0c6 100755
--- a/t/t0041-usage.sh
+++ b/t/t0041-usage.sh
@@ -5,6 +5,7 @@ test_description='Test commands behavior when given invalid argument value'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup ' '
diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh
index e2411f6a9b..883d871dfb 100755
--- a/t/t0061-run-command.sh
+++ b/t/t0061-run-command.sh
@@ -135,6 +135,15 @@ test_expect_success 'run_command runs in parallel with more jobs available than
test_cmp expect actual
'
+test_expect_success 'run_command runs in parallel with more jobs available than tasks --on-stderr-output' '
+ test-tool run-command --on-stderr-output run-command-parallel 5 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>err &&
+ test_must_be_empty out &&
+ test 4 = $(grep -c "on_stderr_output: Hello" err) &&
+ test 4 = $(grep -c "on_stderr_output: World" err) &&
+ sed "/on_stderr_output/d" err >err1 &&
+ test_cmp expect err1
+'
+
test_expect_success 'run_command runs ungrouped in parallel with more jobs available than tasks' '
test-tool run-command --ungroup run-command-parallel 5 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>err &&
test_line_count = 8 out &&
@@ -147,6 +156,15 @@ test_expect_success 'run_command runs in parallel with as many jobs as tasks' '
test_cmp expect actual
'
+test_expect_success 'run_command runs in parallel with as many jobs as tasks --on-stderr-output' '
+ test-tool run-command --on-stderr-output run-command-parallel 4 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>err &&
+ test_must_be_empty out &&
+ test 4 = $(grep -c "on_stderr_output: Hello" err) &&
+ test 4 = $(grep -c "on_stderr_output: World" err) &&
+ sed "/on_stderr_output/d" err >err1 &&
+ test_cmp expect err1
+'
+
test_expect_success 'run_command runs ungrouped in parallel with as many jobs as tasks' '
test-tool run-command --ungroup run-command-parallel 4 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>err &&
test_line_count = 8 out &&
@@ -159,6 +177,15 @@ test_expect_success 'run_command runs in parallel with more tasks than jobs avai
test_cmp expect actual
'
+test_expect_success 'run_command runs in parallel with more tasks than jobs available --on-stderr-output' '
+ test-tool run-command --on-stderr-output run-command-parallel 3 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>err &&
+ test_must_be_empty out &&
+ test 4 = $(grep -c "on_stderr_output: Hello" err) &&
+ test 4 = $(grep -c "on_stderr_output: World" err) &&
+ sed "/on_stderr_output/d" err >err1 &&
+ test_cmp expect err1
+'
+
test_expect_success 'run_command runs ungrouped in parallel with more tasks than jobs available' '
test-tool run-command --ungroup run-command-parallel 3 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>err &&
test_line_count = 8 out &&
@@ -180,6 +207,12 @@ test_expect_success 'run_command is asked to abort gracefully' '
test_cmp expect actual
'
+test_expect_success 'run_command is asked to abort gracefully --on-stderr-output' '
+ test-tool run-command --on-stderr-output run-command-abort 3 false >out 2>err &&
+ test_must_be_empty out &&
+ test_cmp expect err
+'
+
test_expect_success 'run_command is asked to abort gracefully (ungroup)' '
test-tool run-command --ungroup run-command-abort 3 false >out 2>err &&
test_must_be_empty out &&
@@ -196,6 +229,12 @@ test_expect_success 'run_command outputs ' '
test_cmp expect actual
'
+test_expect_success 'run_command outputs --on-stderr-output' '
+ test-tool run-command --on-stderr-output run-command-no-jobs 3 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>err &&
+ test_must_be_empty out &&
+ test_cmp expect err
+'
+
test_expect_success 'run_command outputs (ungroup) ' '
test-tool run-command --ungroup run-command-no-jobs 3 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>err &&
test_must_be_empty out &&
diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh
index c66d91e82d..a4f5bba507 100755
--- a/t/t0300-credentials.sh
+++ b/t/t0300-credentials.sh
@@ -214,6 +214,24 @@ test_expect_success 'credential_approve stores password expiry' '
EOF
'
+test_expect_success 'credential_approve stores oauth refresh token' '
+ check approve useless <<-\EOF
+ protocol=http
+ host=example.com
+ username=foo
+ password=bar
+ oauth_refresh_token=xyzzy
+ --
+ --
+ useless: store
+ useless: protocol=http
+ useless: host=example.com
+ useless: username=foo
+ useless: password=bar
+ useless: oauth_refresh_token=xyzzy
+ EOF
+'
+
test_expect_success 'do not bother storing password-less credential' '
check approve useless <<-\EOF
protocol=http
@@ -808,7 +826,7 @@ test_expect_success 'credential config with partial URLs' '
git -c credential.$partial.helper=yep \
-c credential.with%0anewline.username=uh-oh \
- credential fill <stdin >stdout 2>stderr &&
+ credential fill <stdin 2>stderr &&
test_i18ngrep "skipping credential lookup for key" stderr
'
diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh
index 698b7159f0..8300faadea 100755
--- a/t/t0301-credential-cache.sh
+++ b/t/t0301-credential-cache.sh
@@ -29,6 +29,8 @@ test_atexit 'git credential-cache exit'
# test that the daemon works with no special setup
helper_test cache
+helper_test_password_expiry_utc cache
+helper_test_oauth_refresh_token cache
test_expect_success 'socket defaults to ~/.cache/git/credential/socket' '
test_when_finished "
diff --git a/t/t0303-credential-external.sh b/t/t0303-credential-external.sh
index f028fd1418..095574bfc6 100755
--- a/t/t0303-credential-external.sh
+++ b/t/t0303-credential-external.sh
@@ -45,6 +45,8 @@ test -z "$GIT_TEST_CREDENTIAL_HELPER_SETUP" ||
helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER"
helper_test "$GIT_TEST_CREDENTIAL_HELPER"
+helper_test_password_expiry_utc "$GIT_TEST_CREDENTIAL_HELPER"
+helper_test_oauth_refresh_token "$GIT_TEST_CREDENTIAL_HELPER"
if test -z "$GIT_TEST_CREDENTIAL_HELPER_TIMEOUT"; then
say "# skipping timeout tests (GIT_TEST_CREDENTIAL_HELPER_TIMEOUT not set)"
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index 8eac74b59c..03c1bfb86b 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -447,6 +447,33 @@ test_expect_success FUNNYNAMES '--batch-check, -z with newline in input' '
test_cmp expect actual
'
+test_expect_success '--batch-check, -z with newline in non-existent named object' '
+ printf "HEAD:newline${LF}missing" >in &&
+ git cat-file --batch-check -z <in >actual &&
+
+ printf "\"HEAD:newline\\\\nmissing\" missing\n" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success FUNNYNAMES '--batch-check, -z with missing object having newline in name' '
+ git init missing-object-newline &&
+ (
+ cd missing-object-newline &&
+ file="newline${LF}embedded" &&
+ echo_without_newline "hello" > $file &&
+ git add "$file" &&
+ git commit -m "file with newline embedded" &&
+ test_tick &&
+
+ sha1=$(git rev-parse HEAD:"$file") &&
+ rm .git/objects/$(test_oid_to_path $sha1) &&
+ printf "HEAD:$file" >in &&
+ git cat-file --batch-check -z <in >actual &&
+ printf "\"HEAD:newline\\\\nembedded\" missing\n" >expect &&
+ test_cmp expect actual
+ )
+'
+
batch_command_multiple_info="info $hello_sha1
info $tree_sha1
info $commit_sha1
diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
index 0c784813f1..090ecbc680 100755
--- a/t/t1092-sparse-checkout-compatibility.sh
+++ b/t/t1092-sparse-checkout-compatibility.sh
@@ -1377,7 +1377,7 @@ test_expect_success 'index.sparse disabled inline uses full index' '
! test_region index ensure_full_index trace2.txt
'
-ensure_not_expanded () {
+run_sparse_index_trace2 () {
rm -f trace2.txt &&
if test -z "$WITHOUT_UNTRACKED_TXT"
then
@@ -1397,7 +1397,16 @@ ensure_not_expanded () {
git -C sparse-index "$@" \
>sparse-index-out \
2>sparse-index-error || return 1
- fi &&
+ fi
+}
+
+ensure_expanded () {
+ run_sparse_index_trace2 "$@" &&
+ test_region index ensure_full_index trace2.txt
+}
+
+ensure_not_expanded () {
+ run_sparse_index_trace2 "$@" &&
test_region ! index ensure_full_index trace2.txt
}
@@ -2021,6 +2030,50 @@ test_expect_success 'sparse index is not expanded: rm' '
ensure_not_expanded rm -r deep
'
+test_expect_success 'sparse index is not expanded: diff-index' '
+ init_repos &&
+
+ echo "new" >>sparse-index/g &&
+ git -C sparse-index add g &&
+ git -C sparse-index commit -m "dummy" &&
+ ensure_not_expanded diff-index HEAD~1 &&
+
+ echo "text" >>sparse-index/deep/a &&
+
+ ensure_not_expanded diff-index HEAD deep/a &&
+ ensure_not_expanded diff-index HEAD deep/*
+'
+test_expect_success 'diff-index pathspec expands index when necessary' '
+ init_repos &&
+
+ echo "text" >>sparse-index/deep/a &&
+
+ # pathspec that should expand index
+ ! ensure_not_expanded diff-index "*/a" &&
+ ! ensure_not_expanded diff-index "**a"
+'
+
+test_expect_success 'diff-index with pathspec outside sparse definition' '
+ init_repos &&
+
+ test_sparse_match test_must_fail git diff-index HEAD folder2/a
+'
+
+test_expect_success 'match all: diff-index' '
+ init_repos &&
+
+ test_all_match git diff-index HEAD &&
+ write_script edit-contents <<-\EOF &&
+ echo text >>$1
+ EOF
+ run_on_all ../edit-contents g &&
+ run_on_all git add g &&
+ run_on_all git commit -m "two" &&
+ run_on_all rm g &&
+ test_all_match git diff-index HEAD &&
+ test_all_match git diff-index --cached HEAD~1
+'
+
test_expect_success 'grep with and --cached' '
init_repos &&
@@ -2080,22 +2133,32 @@ test_expect_success 'grep sparse directory within submodules' '
test_cmp actual expect
'
-test_expect_success 'write-tree on all' '
+test_expect_success 'write-tree' '
init_repos &&
+ test_all_match git write-tree &&
+
write_script edit-contents <<-\EOF &&
echo text >>"$1"
EOF
+ # make a change inside the sparse cone
run_on_all ../edit-contents deep/a &&
- run_on_all git update-index deep/a &&
+ test_all_match git update-index deep/a &&
test_all_match git write-tree &&
+ test_all_match git status --porcelain=v2 &&
+ # make a change outside the sparse cone
run_on_all mkdir -p folder1 &&
run_on_all cp a folder1/a &&
run_on_all ../edit-contents folder1/a &&
- run_on_all git update-index folder1/a &&
- test_all_match git write-tree
+ test_all_match git update-index folder1/a &&
+ test_all_match git write-tree &&
+ test_all_match git status --porcelain=v2 &&
+
+ # check that SKIP_WORKTREE files are not materialized
+ test_path_is_missing sparse-checkout/folder2/a &&
+ test_path_is_missing sparse-index/folder2/a
'
test_expect_success 'sparse-index is not expanded: write-tree' '
@@ -2108,4 +2171,57 @@ test_expect_success 'sparse-index is not expanded: write-tree' '
ensure_not_expanded write-tree
'
+test_expect_success 'diff-files with pathspec inside sparse definition' '
+ init_repos &&
+
+ write_script edit-contents <<-\EOF &&
+ echo text >>"$1"
+ EOF
+
+ run_on_all ../edit-contents deep/a &&
+
+ test_all_match git diff-files &&
+
+ test_all_match git diff-files -- deep/a &&
+
+ # test wildcard
+ test_all_match git diff-files -- "deep/*"
+'
+
+test_expect_success 'diff-files with pathspec outside sparse definition' '
+ init_repos &&
+
+ test_sparse_match git diff-files -- folder2/a &&
+
+ write_script edit-contents <<-\EOF &&
+ echo text >>"$1"
+ EOF
+
+ # The directory "folder1" is outside the cone of interest
+ # and will not exist in the sparse checkout repositories.
+ # Create it as needed, add file "folder1/a" there with
+ # contents that is different from the staged version.
+ run_on_all mkdir -p folder1 &&
+ run_on_all cp a folder1/a &&
+
+ run_on_all ../edit-contents folder1/a &&
+ test_all_match git diff-files &&
+ test_all_match git diff-files -- folder1/a &&
+ test_all_match git diff-files -- "folder*/a"
+'
+
+test_expect_success 'sparse index is not expanded: diff-files' '
+ init_repos &&
+
+ write_script edit-contents <<-\EOF &&
+ echo text >>"$1"
+ EOF
+
+ run_on_all ../edit-contents deep/a &&
+
+ ensure_not_expanded diff-files &&
+ ensure_not_expanded diff-files -- deep/a &&
+ ensure_not_expanded diff-files -- "deep/*"
+'
+
test_done
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index f1d42b62b0..86bfbc2b36 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -98,6 +98,23 @@ test_expect_success 'subsections are not canonicalized by git-config' '
test_cmp_config two section.SubSection.key
'
+test_missing_key () {
+ local key="$1" &&
+ local title="$2" &&
+ test_expect_success "value for $title is not printed" '
+ test_must_fail git config "$key" >out 2>err &&
+ test_must_be_empty out &&
+ test_must_be_empty err
+ '
+}
+
+test_missing_key 'missingsection.missingkey' 'missing section and missing key'
+test_missing_key 'missingsection.penguin' 'missing section and existing key'
+test_missing_key 'section.missingkey' 'existing section and missing key'
+test_missing_key 'section.MissingSubSection.missingkey' 'missing subsection and missing key'
+test_missing_key 'section.SubSection.missingkey' 'existing subsection and missing key'
+test_missing_key 'section.MissingSubSection.key' 'missing subsection and existing key'
+
cat > .git/config <<\EOF
[alpha]
bar = foo
@@ -1488,35 +1505,29 @@ test_expect_success 'git config ignores pairs without count' '
test_must_be_empty error
'
-test_expect_success 'git config ignores pairs with zero count' '
- test_must_fail env \
- GIT_CONFIG_COUNT=0 \
- GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
- git config pair.one
-'
-
test_expect_success 'git config ignores pairs exceeding count' '
GIT_CONFIG_COUNT=1 \
GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
GIT_CONFIG_KEY_1="pair.two" GIT_CONFIG_VALUE_1="value" \
- git config --get-regexp "pair.*" >actual &&
+ git config --get-regexp "pair.*" >actual 2>error &&
cat >expect <<-EOF &&
pair.one value
EOF
- test_cmp expect actual
+ test_cmp expect actual &&
+ test_must_be_empty error
'
test_expect_success 'git config ignores pairs with zero count' '
test_must_fail env \
GIT_CONFIG_COUNT=0 GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
- git config pair.one >error &&
+ git config pair.one 2>error &&
test_must_be_empty error
'
test_expect_success 'git config ignores pairs with empty count' '
test_must_fail env \
GIT_CONFIG_COUNT= GIT_CONFIG_KEY_0="pair.one" GIT_CONFIG_VALUE_0="value" \
- git config pair.one >error &&
+ git config pair.one 2>error &&
test_must_be_empty error
'
@@ -1601,11 +1612,11 @@ test_expect_success 'git config --edit respects core.editor' '
# malformed configuration files
test_expect_success 'barf on syntax error' '
cat >.git/config <<-\EOF &&
- # broken section line
+ # broken key=value
[section]
key garbage
EOF
- test_must_fail git config --get section.key >actual 2>error &&
+ test_must_fail git config --get section.key 2>error &&
test_i18ngrep " line 3 " error
'
@@ -1615,17 +1626,17 @@ test_expect_success 'barf on incomplete section header' '
[section
key = value
EOF
- test_must_fail git config --get section.key >actual 2>error &&
+ test_must_fail git config --get section.key 2>error &&
test_i18ngrep " line 2 " error
'
test_expect_success 'barf on incomplete string' '
cat >.git/config <<-\EOF &&
- # broken section line
+ # broken value string
[section]
key = "value string
EOF
- test_must_fail git config --get section.key >actual 2>error &&
+ test_must_fail git config --get section.key 2>error &&
test_i18ngrep " line 3 " error
'
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
index 1b6437ec07..e5a0d65caa 100755
--- a/t/t1301-shared-repo.sh
+++ b/t/t1301-shared-repo.sh
@@ -52,6 +52,28 @@ test_expect_success 'shared=all' '
test 2 = $(git config core.sharedrepository)
'
+test_expect_failure 'template can set core.bare' '
+ test_when_finished "rm -rf subdir" &&
+ test_when_finished "rm -rf templates" &&
+ test_config core.bare true &&
+ umask 0022 &&
+ mkdir -p templates/ &&
+ cp .git/config templates/config &&
+ git init --template=templates subdir &&
+ test_path_exists subdir/HEAD
+'
+
+test_expect_success 'template can set core.bare but overridden by command line' '
+ test_when_finished "rm -rf subdir" &&
+ test_when_finished "rm -rf templates" &&
+ test_config core.bare true &&
+ umask 0022 &&
+ mkdir -p templates/ &&
+ cp .git/config templates/config &&
+ git init --no-bare --template=templates subdir &&
+ test_path_exists subdir/.git/HEAD
+'
+
test_expect_success POSIXPERM 'update-server-info honors core.sharedRepository' '
: > a1 &&
git add a1 &&
@@ -89,7 +111,7 @@ do
rm -f .git/info/refs &&
git update-server-info &&
actual="$(test_modebits .git/info/refs)" &&
- verbose test "x$actual" = "x-$y"
+ test "x$actual" = "x-$y"
'
@@ -99,7 +121,7 @@ do
rm -f .git/info/refs &&
git update-server-info &&
actual="$(test_modebits .git/info/refs)" &&
- verbose test "x$actual" = "x-$x"
+ test "x$actual" = "x-$x"
'
diff --git a/t/t1419-exclude-refs.sh b/t/t1419-exclude-refs.sh
new file mode 100755
index 0000000000..0e91e2f399
--- /dev/null
+++ b/t/t1419-exclude-refs.sh
@@ -0,0 +1,131 @@
+#!/bin/sh
+
+test_description='test exclude_patterns functionality in main ref store'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+for_each_ref__exclude () {
+ GIT_TRACE2_PERF=1 test-tool ref-store main \
+ for-each-ref--exclude "$@" >actual.raw
+ cut -d ' ' -f 2 actual.raw
+}
+
+for_each_ref () {
+ git for-each-ref --format='%(refname)' "$@"
+}
+
+assert_jumps () {
+ local nr="$1"
+ local trace="$2"
+
+ grep -q "name:jumps_made value:$nr" $trace
+}
+
+assert_no_jumps () {
+ ! assert_jumps ".*" "$1"
+}
+
+test_expect_success 'setup' '
+ test_commit --no-tag base &&
+ base="$(git rev-parse HEAD)" &&
+
+ for name in foo bar baz quux
+ do
+ for i in 1 2 3
+ do
+ echo "create refs/heads/$name/$i $base" || return 1
+ done || return 1
+ done >in &&
+ echo "delete refs/heads/main" >>in &&
+
+ git update-ref --stdin <in &&
+ git pack-refs --all
+'
+
+test_expect_success 'excluded region in middle' '
+ for_each_ref__exclude refs/heads refs/heads/foo >actual 2>perf &&
+ for_each_ref refs/heads/bar refs/heads/baz refs/heads/quux >expect &&
+
+ test_cmp expect actual &&
+ assert_jumps 1 perf
+'
+
+test_expect_success 'excluded region at beginning' '
+ for_each_ref__exclude refs/heads refs/heads/bar >actual 2>perf &&
+ for_each_ref refs/heads/baz refs/heads/foo refs/heads/quux >expect &&
+
+ test_cmp expect actual &&
+ assert_jumps 1 perf
+'
+
+test_expect_success 'excluded region at end' '
+ for_each_ref__exclude refs/heads refs/heads/quux >actual 2>perf &&
+ for_each_ref refs/heads/foo refs/heads/bar refs/heads/baz >expect &&
+
+ test_cmp expect actual &&
+ assert_jumps 1 perf
+'
+
+test_expect_success 'disjoint excluded regions' '
+ for_each_ref__exclude refs/heads refs/heads/bar refs/heads/quux >actual 2>perf &&
+ for_each_ref refs/heads/baz refs/heads/foo >expect &&
+
+ test_cmp expect actual &&
+ assert_jumps 2 perf
+'
+
+test_expect_success 'adjacent, non-overlapping excluded regions' '
+ for_each_ref__exclude refs/heads refs/heads/bar refs/heads/baz >actual 2>perf &&
+ for_each_ref refs/heads/foo refs/heads/quux >expect &&
+
+ test_cmp expect actual &&
+ assert_jumps 1 perf
+'
+
+test_expect_success 'overlapping excluded regions' '
+ for_each_ref__exclude refs/heads refs/heads/ba refs/heads/baz >actual 2>perf &&
+ for_each_ref refs/heads/foo refs/heads/quux >expect &&
+
+ test_cmp expect actual &&
+ assert_jumps 1 perf
+'
+
+test_expect_success 'several overlapping excluded regions' '
+ for_each_ref__exclude refs/heads \
+ refs/heads/bar refs/heads/baz refs/heads/foo >actual 2>perf &&
+ for_each_ref refs/heads/quux >expect &&
+
+ test_cmp expect actual &&
+ assert_jumps 1 perf
+'
+
+test_expect_success 'non-matching excluded section' '
+ for_each_ref__exclude refs/heads refs/heads/does/not/exist >actual 2>perf &&
+ for_each_ref >expect &&
+
+ test_cmp expect actual &&
+ assert_no_jumps
+'
+
+test_expect_success 'meta-characters are discarded' '
+ for_each_ref__exclude refs/heads "refs/heads/ba*" >actual 2>perf &&
+ for_each_ref >expect &&
+
+ test_cmp expect actual &&
+ assert_no_jumps
+'
+
+test_expect_success 'complex hidden ref rules are discarded' '
+ for_each_ref__exclude refs/heads refs/heads/foo "!refs/heads/foo/1" \
+ >actual 2>perf &&
+ for_each_ref >expect &&
+
+ test_cmp expect actual &&
+ assert_no_jumps
+'
+
+test_done
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index bca46378b2..8c442adb1a 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -989,10 +989,7 @@ test_expect_success 'fsck error and recovery on invalid object type' '
garbage_blob=$(git hash-object --stdin -w -t garbage --literally </dev/null) &&
- cat >err.expect <<-\EOF &&
- fatal: invalid object type
- EOF
- test_must_fail git fsck >out 2>err &&
+ test_must_fail git fsck 2>err &&
grep -e "^error" -e "^fatal" err >errors &&
test_line_count = 1 errors &&
grep "$garbage_blob: object is of unknown type '"'"'garbage'"'"':" err
diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh
index de1d48f3ba..dd811b7fb4 100755
--- a/t/t1502-rev-parse-parseopt.sh
+++ b/t/t1502-rev-parse-parseopt.sh
@@ -302,14 +302,14 @@ test_expect_success 'test --parseopt help output: "wrapped" options normal "or:"
|EOF
END_EXPECT
- test_must_fail git rev-parse --parseopt -- -h >out <spec >actual &&
+ test_must_fail git rev-parse --parseopt -- -h <spec >actual &&
test_cmp expect actual
'
test_expect_success 'test --parseopt invalid opt-spec' '
test_write_lines x -- "=, x" >spec &&
echo "fatal: missing opt-spec before option flags" >expect &&
- test_must_fail git rev-parse --parseopt -- >out <spec 2>err &&
+ test_must_fail git rev-parse --parseopt -- <spec 2>err &&
test_cmp expect err
'
@@ -339,7 +339,7 @@ test_expect_success 'test --parseopt help output: multi-line blurb after empty l
|EOF
END_EXPECT
- test_must_fail git rev-parse --parseopt -- -h >out <spec >actual &&
+ test_must_fail git rev-parse --parseopt -- -h <spec >actual &&
test_cmp expect actual
'
diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh
index d94c72c672..cb9ef7e329 100755
--- a/t/t1507-rev-parse-upstream.sh
+++ b/t/t1507-rev-parse-upstream.sh
@@ -97,7 +97,8 @@ test_expect_success 'my-side@{u} resolves to correct commit' '
commit_subject my-side >actual &&
test_cmp expect actual &&
echo 5 >expect &&
- commit_subject my-side@{u} >actual
+ commit_subject my-side@{u} >actual &&
+ test_cmp expect actual
'
test_expect_success 'not-tracking@{u} fails' '
diff --git a/t/t2019-checkout-ambiguous-ref.sh b/t/t2019-checkout-ambiguous-ref.sh
index 2c8c926b4d..9540588664 100755
--- a/t/t2019-checkout-ambiguous-ref.sh
+++ b/t/t2019-checkout-ambiguous-ref.sh
@@ -16,7 +16,7 @@ test_expect_success 'setup ambiguous refs' '
'
test_expect_success 'checkout ambiguous ref succeeds' '
- git checkout ambiguity >stdout 2>stderr
+ git checkout ambiguity 2>stderr
'
test_expect_success 'checkout produces ambiguity warning' '
@@ -37,7 +37,7 @@ test_expect_success 'checkout reports switch to branch' '
'
test_expect_success 'checkout vague ref succeeds' '
- git checkout vagueness >stdout 2>stderr &&
+ git checkout vagueness 2>stderr &&
test_set_prereq VAGUENESS_SUCCESS
'
diff --git a/t/t2021-checkout-overwrite.sh b/t/t2021-checkout-overwrite.sh
index 034f62c13c..ecfacf0f7f 100755
--- a/t/t2021-checkout-overwrite.sh
+++ b/t/t2021-checkout-overwrite.sh
@@ -77,7 +77,7 @@ test_expect_success 'checkout --overwrite-ignore should succeed if only ignored
echo autogenerated information >some_dir/ignore &&
echo ignore >.git/info/exclude &&
git checkout --overwrite-ignore df_conflict &&
- ! test_path_is_dir some_dir
+ test_path_is_file some_dir
'
test_done
diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh
index d587e0b20d..1c914879a4 100755
--- a/t/t2400-worktree-add.sh
+++ b/t/t2400-worktree-add.sh
@@ -118,14 +118,17 @@ test_expect_success '"add" worktree creating new branch' '
)
'
-test_expect_success 'die the same branch is already checked out' '
+test_expect_success 'die if the same branch is already checked out' '
(
cd here &&
- test_must_fail git checkout newmain
+ test_must_fail git checkout newmain &&
+ test_must_fail git checkout -B newmain &&
+ test_must_fail git switch newmain &&
+ test_must_fail git switch -C newmain
)
'
-test_expect_success SYMLINKS 'die the same branch is already checked out (symlink)' '
+test_expect_success SYMLINKS 'die if the same branch is already checked out (symlink)' '
head=$(git -C there rev-parse --git-path HEAD) &&
ref=$(git -C there symbolic-ref HEAD) &&
rm "$head" &&
@@ -133,17 +136,34 @@ test_expect_success SYMLINKS 'die the same branch is already checked out (symlin
test_must_fail git -C here checkout newmain
'
-test_expect_success 'not die the same branch is already checked out' '
+test_expect_success 'allow creating multiple worktrees for same branch with force' '
+ git worktree add --force anothernewmain newmain
+'
+
+test_expect_success 'allow checkout/reset from the conflicted branch' '
(
cd here &&
- git worktree add --force anothernewmain newmain
+ git checkout -b conflictedmain newmain &&
+ git checkout -B conflictedmain newmain &&
+ git switch -C conflictedmain newmain
)
'
-test_expect_success 'not die on re-checking out current branch' '
+test_expect_success 'and not die on re-checking out current branch even if conflicted' '
(
cd there &&
- git checkout newmain
+ git checkout newmain &&
+ git switch newmain
+ )
+'
+
+test_expect_success 'unless using force without --ignore-other-worktrees' '
+ (
+ cd there &&
+ test_must_fail git checkout -B newmain &&
+ test_must_fail git switch -C newmain &&
+ git checkout --ignore-other-worktrees -B newmain &&
+ git switch --ignore-other-worktrees -C newmain
)
'
@@ -298,17 +318,24 @@ test_expect_success '"add" no auto-vivify with --detach and <branch> omitted' '
test_must_fail git -C mish/mash symbolic-ref HEAD
'
-test_expect_success '"add" -b/-B mutually exclusive' '
- test_must_fail git worktree add -b poodle -B poodle bamboo main
-'
-
-test_expect_success '"add" -b/--detach mutually exclusive' '
- test_must_fail git worktree add -b poodle --detach bamboo main
-'
+# Helper function to test mutually exclusive options.
+#
+# Note: Quoted arguments containing spaces are not supported.
+test_wt_add_excl () {
+ local opts="$*" &&
+ test_expect_success "'worktree add' with '$opts' has mutually exclusive options" '
+ test_must_fail git worktree add $opts 2>actual &&
+ grep -E "fatal:( options)? .* cannot be used together" actual
+ '
+}
-test_expect_success '"add" -B/--detach mutually exclusive' '
- test_must_fail git worktree add -B poodle --detach bamboo main
-'
+test_wt_add_excl -b poodle -B poodle bamboo main
+test_wt_add_excl -b poodle --detach bamboo main
+test_wt_add_excl -B poodle --detach bamboo main
+test_wt_add_excl --orphan --detach bamboo
+test_wt_add_excl --orphan --no-checkout bamboo
+test_wt_add_excl --orphan bamboo main
+test_wt_add_excl --orphan -b bamboo wtdir/ main
test_expect_success '"add -B" fails if the branch is checked out' '
git rev-parse newmain >before &&
@@ -326,10 +353,111 @@ test_expect_success 'add -B' '
'
test_expect_success 'add --quiet' '
+ test_when_finished "git worktree remove -f -f another-worktree" &&
git worktree add --quiet another-worktree main 2>actual &&
test_must_be_empty actual
'
+test_expect_success 'add --quiet -b' '
+ test_when_finished "git branch -D quietnewbranch" &&
+ test_when_finished "git worktree remove -f -f another-worktree" &&
+ git worktree add --quiet -b quietnewbranch another-worktree 2>actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success '"add --orphan"' '
+ test_when_finished "git worktree remove -f -f orphandir" &&
+ git worktree add --orphan -b neworphan orphandir &&
+ echo refs/heads/neworphan >expected &&
+ git -C orphandir symbolic-ref HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '"add --orphan (no -b)"' '
+ test_when_finished "git worktree remove -f -f neworphan" &&
+ git worktree add --orphan neworphan &&
+ echo refs/heads/neworphan >expected &&
+ git -C neworphan symbolic-ref HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '"add --orphan --quiet"' '
+ test_when_finished "git worktree remove -f -f orphandir" &&
+ git worktree add --quiet --orphan -b neworphan orphandir 2>log.actual &&
+ test_must_be_empty log.actual &&
+ echo refs/heads/neworphan >expected &&
+ git -C orphandir symbolic-ref HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '"add --orphan" fails if the branch already exists' '
+ test_when_finished "git branch -D existingbranch" &&
+ git worktree add -b existingbranch orphandir main &&
+ git worktree remove orphandir &&
+ test_must_fail git worktree add --orphan -b existingbranch orphandir
+'
+
+test_expect_success '"add --orphan" with empty repository' '
+ test_when_finished "rm -rf empty_repo" &&
+ echo refs/heads/newbranch >expected &&
+ GIT_DIR="empty_repo" git init --bare &&
+ git -C empty_repo worktree add --orphan -b newbranch worktreedir &&
+ git -C empty_repo/worktreedir symbolic-ref HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success '"add" worktree with orphan branch and lock' '
+ git worktree add --lock --orphan -b orphanbr orphan-with-lock &&
+ test_when_finished "git worktree unlock orphan-with-lock || :" &&
+ test -f .git/worktrees/orphan-with-lock/locked
+'
+
+test_expect_success '"add" worktree with orphan branch, lock, and reason' '
+ lock_reason="why not" &&
+ git worktree add --detach --lock --reason "$lock_reason" orphan-with-lock-reason main &&
+ test_when_finished "git worktree unlock orphan-with-lock-reason || :" &&
+ test -f .git/worktrees/orphan-with-lock-reason/locked &&
+ echo "$lock_reason" >expect &&
+ test_cmp expect .git/worktrees/orphan-with-lock-reason/locked
+'
+
+# Note: Quoted arguments containing spaces are not supported.
+test_wt_add_orphan_hint () {
+ local context="$1" &&
+ local use_branch=$2 &&
+ shift 2 &&
+ local opts="$*" &&
+ test_expect_success "'worktree add' show orphan hint in bad/orphan HEAD w/ $context" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (cd repo && test_commit commit) &&
+ git -C repo switch --orphan noref &&
+ test_must_fail git -C repo worktree add $opts foobar/ 2>actual &&
+ ! grep "error: unknown switch" actual &&
+ grep "hint: If you meant to create a worktree containing a new orphan branch" actual &&
+ if [ $use_branch -eq 1 ]
+ then
+ grep -E "^hint:\s+git worktree add --orphan -b \S+ \S+\s*$" actual
+ else
+ grep -E "^hint:\s+git worktree add --orphan \S+\s*$" actual
+ fi
+
+ '
+}
+
+test_wt_add_orphan_hint 'no opts' 0
+test_wt_add_orphan_hint '-b' 1 -b foobar_branch
+test_wt_add_orphan_hint '-B' 1 -B foobar_branch
+
+test_expect_success "'worktree add' doesn't show orphan hint in bad/orphan HEAD w/ --quiet" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (cd repo && test_commit commit) &&
+ test_must_fail git -C repo worktree add --quiet foobar_branch foobar/ 2>actual &&
+ ! grep "error: unknown switch" actual &&
+ ! grep "hint: If you meant to create a worktree containing a new orphan branch" actual
+'
+
test_expect_success 'local clone from linked checkout' '
git clone --local here here-clone &&
( cd here-clone && git fsck )
@@ -446,6 +574,14 @@ setup_remote_repo () {
)
}
+test_expect_success '"add" <path> <remote/branch> w/ no HEAD' '
+ test_when_finished rm -rf repo_upstream repo_local foo &&
+ setup_remote_repo repo_upstream repo_local &&
+ git -C repo_local config --bool core.bare true &&
+ git -C repo_local branch -D main &&
+ git -C repo_local worktree add ./foo repo_upstream/foo
+'
+
test_expect_success '--no-track avoids setting up tracking' '
test_when_finished rm -rf repo_upstream repo_local foo &&
setup_remote_repo repo_upstream repo_local &&
@@ -528,6 +664,35 @@ test_expect_success 'git worktree add --guess-remote sets up tracking' '
test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
)
'
+test_expect_success 'git worktree add --guess-remote sets up tracking (quiet)' '
+ test_when_finished rm -rf repo_a repo_b foo &&
+ setup_remote_repo repo_a repo_b &&
+ (
+ cd repo_b &&
+ git worktree add --quiet --guess-remote ../foo 2>actual &&
+ test_must_be_empty actual
+ ) &&
+ (
+ cd foo &&
+ test_branch_upstream foo repo_a foo &&
+ test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
+ )
+'
+
+test_expect_success 'git worktree --no-guess-remote (quiet)' '
+ test_when_finished rm -rf repo_a repo_b foo &&
+ setup_remote_repo repo_a repo_b &&
+ (
+ cd repo_b &&
+ git worktree add --quiet --no-guess-remote ../foo
+ ) &&
+ (
+ cd foo &&
+ test_must_fail git config "branch.foo.remote" &&
+ test_must_fail git config "branch.foo.merge" &&
+ test_cmp_rev ! refs/remotes/repo_a/foo refs/heads/foo
+ )
+'
test_expect_success 'git worktree add with worktree.guessRemote sets up tracking' '
test_when_finished rm -rf repo_a repo_b foo &&
@@ -560,6 +725,348 @@ test_expect_success 'git worktree --no-guess-remote option overrides config' '
)
'
+test_dwim_orphan () {
+ local info_text="No possible source branch, inferring '--orphan'" &&
+ local fetch_error_text="fatal: No local or remote refs exist despite at least one remote" &&
+ local orphan_hint="hint: If you meant to create a worktree containing a new orphan branch" &&
+ local invalid_ref_regex="^fatal: invalid reference:\s\+.*" &&
+ local bad_combo_regex="^fatal: '[a-z-]\+' and '[a-z-]\+' cannot be used together" &&
+
+ local git_ns="repo" &&
+ local dashc_args="-C $git_ns" &&
+ local use_cd=0 &&
+
+ local bad_head=0 &&
+ local empty_repo=1 &&
+ local local_ref=0 &&
+ local use_quiet=0 &&
+ local remote=0 &&
+ local remote_ref=0 &&
+ local use_detach=0 &&
+ local use_new_branch=0 &&
+
+ local outcome="$1" &&
+ local outcome_text &&
+ local success &&
+ shift &&
+ local args="" &&
+ local context="" &&
+ case "$outcome" in
+ "infer")
+ success=1 &&
+ outcome_text='"add" DWIM infer --orphan'
+ ;;
+ "no_infer")
+ success=1 &&
+ outcome_text='"add" DWIM doesnt infer --orphan'
+ ;;
+ "fetch_error")
+ success=0 &&
+ outcome_text='"add" error need fetch'
+ ;;
+ "fatal_orphan_bad_combo")
+ success=0 &&
+ outcome_text='"add" error inferred "--orphan" gives illegal opts combo'
+ ;;
+ "warn_bad_head")
+ success=0 &&
+ outcome_text='"add" error, warn on bad HEAD, hint use orphan'
+ ;;
+ *)
+ echo "test_dwim_orphan(): invalid outcome: '$outcome'" >&2 &&
+ return 1
+ ;;
+ esac &&
+ while [ $# -gt 0 ]
+ do
+ case "$1" in
+ # How and from where to create the worktree
+ "-C_repo")
+ use_cd=0 &&
+ git_ns="repo" &&
+ dashc_args="-C $git_ns" &&
+ context="$context, 'git -C repo'"
+ ;;
+ "-C_wt")
+ use_cd=0 &&
+ git_ns="wt" &&
+ dashc_args="-C $git_ns" &&
+ context="$context, 'git -C wt'"
+ ;;
+ "cd_repo")
+ use_cd=1 &&
+ git_ns="repo" &&
+ dashc_args="" &&
+ context="$context, 'cd repo && git'"
+ ;;
+ "cd_wt")
+ use_cd=1 &&
+ git_ns="wt" &&
+ dashc_args="" &&
+ context="$context, 'cd wt && git'"
+ ;;
+
+ # Bypass the "pull first" warning
+ "force")
+ args="$args --force" &&
+ context="$context, --force"
+ ;;
+
+ # Try to use remote refs when DWIM
+ "guess_remote")
+ args="$args --guess-remote" &&
+ context="$context, --guess-remote"
+ ;;
+ "no_guess_remote")
+ args="$args --no-guess-remote" &&
+ context="$context, --no-guess-remote"
+ ;;
+
+ # Whether there is at least one local branch present
+ "local_ref")
+ empty_repo=0 &&
+ local_ref=1 &&
+ context="$context, >=1 local branches"
+ ;;
+ "no_local_ref")
+ empty_repo=0 &&
+ context="$context, 0 local branches"
+ ;;
+
+ # Whether the HEAD points at a valid ref (skip this opt when no refs)
+ "good_head")
+ # requires: local_ref
+ context="$context, valid HEAD"
+ ;;
+ "bad_head")
+ bad_head=1 &&
+ context="$context, invalid (or orphan) HEAD"
+ ;;
+
+ # Whether the code path is tested with the base add command, -b, or --detach
+ "no_-b")
+ use_new_branch=0 &&
+ context="$context, no --branch"
+ ;;
+ "-b")
+ use_new_branch=1 &&
+ context="$context, --branch"
+ ;;
+ "detach")
+ use_detach=1 &&
+ context="$context, --detach"
+ ;;
+
+ # Whether to check that all output is suppressed (except errors)
+ # or that the output is as expected
+ "quiet")
+ use_quiet=1 &&
+ args="$args --quiet" &&
+ context="$context, --quiet"
+ ;;
+ "no_quiet")
+ use_quiet=0 &&
+ context="$context, no --quiet (expect output)"
+ ;;
+
+ # Whether there is at least one remote attached to the repo
+ "remote")
+ empty_repo=0 &&
+ remote=1 &&
+ context="$context, >=1 remotes"
+ ;;
+ "no_remote")
+ empty_repo=0 &&
+ remote=0 &&
+ context="$context, 0 remotes"
+ ;;
+
+ # Whether there is at least one valid remote ref
+ "remote_ref")
+ # requires: remote
+ empty_repo=0 &&
+ remote_ref=1 &&
+ context="$context, >=1 fetched remote branches"
+ ;;
+ "no_remote_ref")
+ empty_repo=0 &&
+ remote_ref=0 &&
+ context="$context, 0 fetched remote branches"
+ ;;
+
+ # Options or flags that become illegal when --orphan is inferred
+ "no_checkout")
+ args="$args --no-checkout" &&
+ context="$context, --no-checkout"
+ ;;
+ "track")
+ args="$args --track" &&
+ context="$context, --track"
+ ;;
+
+ # All other options are illegal
+ *)
+ echo "test_dwim_orphan(): invalid arg: '$1'" >&2 &&
+ return 1
+ ;;
+ esac &&
+ shift
+ done &&
+ context="${context#', '}" &&
+ if [ $use_new_branch -eq 1 ]
+ then
+ args="$args -b foo"
+ elif [ $use_detach -eq 1 ]
+ then
+ args="$args --detach"
+ else
+ context="DWIM (no --branch), $context"
+ fi &&
+ if [ $empty_repo -eq 1 ]
+ then
+ context="empty repo, $context"
+ fi &&
+ args="$args ../foo" &&
+ context="${context%', '}" &&
+ test_expect_success "$outcome_text w/ $context" '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ if [ $local_ref -eq 1 ] && [ "$git_ns" = "repo" ]
+ then
+ (cd repo && test_commit commit) &&
+ if [ $bad_head -eq 1 ]
+ then
+ git -C repo symbolic-ref HEAD refs/heads/badbranch
+ fi
+ elif [ $local_ref -eq 1 ] && [ "$git_ns" = "wt" ]
+ then
+ test_when_finished "git -C repo worktree remove -f ../wt" &&
+ git -C repo worktree add --orphan -b main ../wt &&
+ (cd wt && test_commit commit) &&
+ if [ $bad_head -eq 1 ]
+ then
+ git -C wt symbolic-ref HEAD refs/heads/badbranch
+ fi
+ elif [ $local_ref -eq 0 ] && [ "$git_ns" = "wt" ]
+ then
+ test_when_finished "git -C repo worktree remove -f ../wt" &&
+ git -C repo worktree add --orphan -b orphanbranch ../wt
+ fi &&
+
+ if [ $remote -eq 1 ]
+ then
+ test_when_finished "rm -rf upstream" &&
+ git init upstream &&
+ (cd upstream && test_commit commit) &&
+ git -C upstream switch -c foo &&
+ git -C repo remote add upstream ../upstream
+ fi &&
+
+ if [ $remote_ref -eq 1 ]
+ then
+ git -C repo fetch
+ fi &&
+ if [ $success -eq 1 ]
+ then
+ test_when_finished git -C repo worktree remove ../foo
+ fi &&
+ (
+ if [ $use_cd -eq 1 ]
+ then
+ cd $git_ns
+ fi &&
+ if [ "$outcome" = "infer" ]
+ then
+ git $dashc_args worktree add $args 2>actual &&
+ if [ $use_quiet -eq 1 ]
+ then
+ test_must_be_empty actual
+ else
+ grep "$info_text" actual
+ fi
+ elif [ "$outcome" = "no_infer" ]
+ then
+ git $dashc_args worktree add $args 2>actual &&
+ if [ $use_quiet -eq 1 ]
+ then
+ test_must_be_empty actual
+ else
+ ! grep "$info_text" actual
+ fi
+ elif [ "$outcome" = "fetch_error" ]
+ then
+ test_must_fail git $dashc_args worktree add $args 2>actual &&
+ grep "$fetch_error_text" actual
+ elif [ "$outcome" = "fatal_orphan_bad_combo" ]
+ then
+ test_must_fail git $dashc_args worktree add $args 2>actual &&
+ if [ $use_quiet -eq 1 ]
+ then
+ ! grep "$info_text" actual
+ else
+ grep "$info_text" actual
+ fi &&
+ grep "$bad_combo_regex" actual
+ elif [ "$outcome" = "warn_bad_head" ]
+ then
+ test_must_fail git $dashc_args worktree add $args 2>actual &&
+ if [ $use_quiet -eq 1 ]
+ then
+ grep "$invalid_ref_regex" actual &&
+ ! grep "$orphan_hint" actual
+ else
+ headpath=$(git $dashc_args rev-parse --sq --path-format=absolute --git-path HEAD) &&
+ headcontents=$(cat "$headpath") &&
+ grep "HEAD points to an invalid (or orphaned) reference" actual &&
+ grep "HEAD path:\s*.$headpath." actual &&
+ grep "HEAD contents:\s*.$headcontents." actual &&
+ grep "$orphan_hint" actual &&
+ ! grep "$info_text" actual
+ fi &&
+ grep "$invalid_ref_regex" actual
+ else
+ # Unreachable
+ false
+ fi
+ ) &&
+ if [ $success -ne 1 ]
+ then
+ test_path_is_missing foo
+ fi
+ '
+}
+
+for quiet_mode in "no_quiet" "quiet"
+do
+ for changedir_type in "cd_repo" "cd_wt" "-C_repo" "-C_wt"
+ do
+ dwim_test_args="$quiet_mode $changedir_type"
+ test_dwim_orphan 'infer' $dwim_test_args no_-b
+ test_dwim_orphan 'no_infer' $dwim_test_args no_-b local_ref good_head
+ test_dwim_orphan 'infer' $dwim_test_args no_-b no_local_ref no_remote no_remote_ref no_guess_remote
+ test_dwim_orphan 'infer' $dwim_test_args no_-b no_local_ref remote no_remote_ref no_guess_remote
+ test_dwim_orphan 'fetch_error' $dwim_test_args no_-b no_local_ref remote no_remote_ref guess_remote
+ test_dwim_orphan 'infer' $dwim_test_args no_-b no_local_ref remote no_remote_ref guess_remote force
+ test_dwim_orphan 'no_infer' $dwim_test_args no_-b no_local_ref remote remote_ref guess_remote
+
+ test_dwim_orphan 'infer' $dwim_test_args -b
+ test_dwim_orphan 'no_infer' $dwim_test_args -b local_ref good_head
+ test_dwim_orphan 'infer' $dwim_test_args -b no_local_ref no_remote no_remote_ref no_guess_remote
+ test_dwim_orphan 'infer' $dwim_test_args -b no_local_ref remote no_remote_ref no_guess_remote
+ test_dwim_orphan 'infer' $dwim_test_args -b no_local_ref remote no_remote_ref guess_remote
+ test_dwim_orphan 'infer' $dwim_test_args -b no_local_ref remote remote_ref guess_remote
+
+ test_dwim_orphan 'warn_bad_head' $dwim_test_args no_-b local_ref bad_head
+ test_dwim_orphan 'warn_bad_head' $dwim_test_args -b local_ref bad_head
+ test_dwim_orphan 'warn_bad_head' $dwim_test_args detach local_ref bad_head
+ done
+
+ test_dwim_orphan 'fatal_orphan_bad_combo' $quiet_mode no_-b no_checkout
+ test_dwim_orphan 'fatal_orphan_bad_combo' $quiet_mode no_-b track
+ test_dwim_orphan 'fatal_orphan_bad_combo' $quiet_mode -b no_checkout
+ test_dwim_orphan 'fatal_orphan_bad_combo' $quiet_mode -b track
+done
+
post_checkout_hook () {
test_when_finished "rm -rf .git/hooks" &&
mkdir .git/hooks &&
diff --git a/t/t3013-ls-files-format.sh b/t/t3013-ls-files-format.sh
index ef6fb53f7f..6e6ea0b6f3 100755
--- a/t/t3013-ls-files-format.sh
+++ b/t/t3013-ls-files-format.sh
@@ -38,6 +38,41 @@ test_expect_success 'git ls-files --format objectname v.s. -s' '
test_cmp expect actual
'
+test_expect_success 'git ls-files --format objecttype' '
+ git ls-files --format="%(objectname)" o1.txt o4.txt o6.txt >objectname &&
+ git cat-file --batch-check="%(objecttype)" >expect <objectname &&
+ git ls-files --format="%(objecttype)" o1.txt o4.txt o6.txt >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git ls-files --format objectsize' '
+ cat>expect <<-\EOF &&
+26
+29
+27
+26
+-
+26
+ EOF
+ git ls-files --format="%(objectsize)" >actual &&
+
+ test_cmp expect actual
+'
+
+test_expect_success 'git ls-files --format objectsize:padded' '
+ cat>expect <<-\EOF &&
+ 26
+ 29
+ 27
+ 26
+ -
+ 26
+ EOF
+ git ls-files --format="%(objectsize:padded)" >actual &&
+
+ test_cmp expect actual
+'
+
test_expect_success 'git ls-files --format v.s. --eol' '
git ls-files --eol >tmp &&
sed -e "s/ / /g" -e "s/ */ /g" tmp >expect 2>err &&
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 5a8a48287c..98b6c8ac34 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -239,6 +239,21 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou
git worktree prune
'
+test_expect_success 'git branch -M fails if updating any linked working tree fails' '
+ git worktree add -b baz bazdir1 &&
+ git worktree add -f bazdir2 baz &&
+ touch .git/worktrees/bazdir1/HEAD.lock &&
+ test_must_fail git branch -M baz bam &&
+ test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam &&
+ git branch -M bam baz &&
+ rm .git/worktrees/bazdir1/HEAD.lock &&
+ touch .git/worktrees/bazdir2/HEAD.lock &&
+ test_must_fail git branch -M baz bam &&
+ test $(git -C bazdir1 rev-parse --abbrev-ref HEAD) = bam &&
+ rm -rf bazdir1 bazdir2 &&
+ git worktree prune
+'
+
test_expect_success 'git branch -M baz bam should succeed within a worktree in which baz is checked out' '
git checkout -b baz &&
git worktree add -f bazdir baz &&
@@ -283,6 +298,20 @@ test_expect_success 'git branch -M and -C fail on detached HEAD' '
test_cmp expect err
'
+test_expect_success 'git branch -m should work with orphan branches' '
+ test_when_finished git checkout - &&
+ test_when_finished git worktree remove -f wt &&
+ git worktree add wt --detach &&
+ # rename orphan in another worktreee
+ git -C wt checkout --orphan orphan-foo-wt &&
+ git branch -m orphan-foo-wt orphan-bar-wt &&
+ test orphan-bar-wt=$(git -C orphan-worktree branch --show-current) &&
+ # rename orphan in the current worktree
+ git checkout --orphan orphan-foo &&
+ git branch -m orphan-foo orphan-bar &&
+ test orphan-bar=$(git branch --show-current)
+'
+
test_expect_success 'git branch -d on orphan HEAD (merged)' '
test_when_finished git checkout main &&
git checkout --orphan orphan &&
diff --git a/t/t3202-show-branch.sh b/t/t3202-show-branch.sh
index ea7cfd1951..be20ebe1d5 100755
--- a/t/t3202-show-branch.sh
+++ b/t/t3202-show-branch.sh
@@ -221,4 +221,22 @@ test_expect_success 'fatal descriptions on non-existent branch' '
test_cmp expect actual
'
+test_expect_success 'error descriptions on orphan branch' '
+ test_when_finished git worktree remove -f wt &&
+ git worktree add wt --detach &&
+ git -C wt checkout --orphan orphan-branch &&
+ test_branch_op_in_wt() {
+ test_orphan_error() {
+ test_must_fail git $* 2>actual &&
+ test_i18ngrep "No commit on branch .orphan-branch. yet.$" actual
+ } &&
+ test_orphan_error -C wt branch $1 $2 && # implicit branch
+ test_orphan_error -C wt branch $1 orphan-branch $2 && # explicit branch
+ test_orphan_error branch $1 orphan-branch $2 # different worktree
+ } &&
+ test_branch_op_in_wt --edit-description &&
+ test_branch_op_in_wt --set-upstream-to=ne &&
+ test_branch_op_in_wt -c new-branch
+'
+
test_done
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
index 07a0ff93de..9f399d2f75 100755
--- a/t/t3210-pack-refs.sh
+++ b/t/t3210-pack-refs.sh
@@ -108,6 +108,43 @@ test_expect_success \
git branch -d n/o/p &&
git branch n'
+test_expect_success 'test excluded refs are not packed' '
+ git branch dont_pack1 &&
+ git branch dont_pack2 &&
+ git branch pack_this &&
+ git pack-refs --all --exclude "refs/heads/dont_pack*" &&
+ test -f .git/refs/heads/dont_pack1 &&
+ test -f .git/refs/heads/dont_pack2 &&
+ ! test -f .git/refs/heads/pack_this'
+
+test_expect_success 'test --no-exclude refs clears excluded refs' '
+ git branch dont_pack3 &&
+ git branch dont_pack4 &&
+ git pack-refs --all --exclude "refs/heads/dont_pack*" --no-exclude &&
+ ! test -f .git/refs/heads/dont_pack3 &&
+ ! test -f .git/refs/heads/dont_pack4'
+
+test_expect_success 'test only included refs are packed' '
+ git branch pack_this1 &&
+ git branch pack_this2 &&
+ git tag dont_pack5 &&
+ git pack-refs --include "refs/heads/pack_this*" &&
+ test -f .git/refs/tags/dont_pack5 &&
+ ! test -f .git/refs/heads/pack_this1 &&
+ ! test -f .git/refs/heads/pack_this2'
+
+test_expect_success 'test --no-include refs clears included refs' '
+ git branch pack1 &&
+ git branch pack2 &&
+ git pack-refs --include "refs/heads/pack*" --no-include &&
+ test -f .git/refs/heads/pack1 &&
+ test -f .git/refs/heads/pack2'
+
+test_expect_success 'test --exclude takes precedence over --include' '
+ git branch dont_pack5 &&
+ git pack-refs --include "refs/heads/pack*" --exclude "refs/heads/pack*" &&
+ test -f .git/refs/heads/dont_pack5'
+
test_expect_success \
'see if up-to-date packed refs are preserved' \
'git branch q &&
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh
index 3288aaec7d..dbadcf1375 100755
--- a/t/t3301-notes.sh
+++ b/t/t3301-notes.sh
@@ -362,6 +362,7 @@ test_expect_success 'do not create empty note with -m ""' '
'
test_expect_success 'create note with combination of -m and -F' '
+ test_when_finished git notes remove HEAD &&
cat >expect-combine_m_and_F <<-EOF &&
foo
@@ -380,6 +381,25 @@ test_expect_success 'create note with combination of -m and -F' '
test_cmp expect-combine_m_and_F actual
'
+test_expect_success 'create note with combination of -m and -F and --separator' '
+ cat >expect-combine_m_and_F <<-\EOF &&
+ foo
+ -------
+ xyzzy
+ -------
+ bar
+ -------
+ zyxxy
+ -------
+ baz
+ EOF
+ echo "xyzzy" >note_a &&
+ echo "zyxxy" >note_b &&
+ git notes add -m "foo" -F note_a -m "bar" -F note_b -m "baz" --separator "-------" &&
+ git notes show >actual &&
+ test_cmp expect-combine_m_and_F actual
+'
+
test_expect_success 'remove note with "git notes remove"' '
git notes remove HEAD^ &&
git notes remove &&
@@ -521,6 +541,85 @@ test_expect_success 'listing non-existing notes fails' '
test_must_be_empty actual
'
+test_expect_success 'append: specify an empty separator' '
+ test_when_finished git notes remove HEAD &&
+ cat >expect <<-\EOF &&
+ notes-1
+
+ notes-2
+ EOF
+
+ git notes add -m "notes-1" &&
+ git notes append --separator="" -m "notes-2" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append: specify separator with line break' '
+ test_when_finished git notes remove HEAD &&
+ cat >expect <<-\EOF &&
+ notes-1
+ -------
+ notes-2
+ EOF
+
+ git notes add -m "notes-1" &&
+ git notes append --separator="-------$LF" -m "notes-2" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append: specify separator without line break' '
+ test_when_finished git notes remove HEAD &&
+ cat >expect <<-\EOF &&
+ notes-1
+ -------
+ notes-2
+ EOF
+
+ git notes add -m "notes-1" &&
+ git notes append --separator="-------" -m "notes-2" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append: specify separator with multiple messages' '
+ test_when_finished git notes remove HEAD &&
+ cat >expect <<-\EOF &&
+ notes-1
+ -------
+ notes-2
+ -------
+ notes-3
+ EOF
+
+ git notes add -m "notes-1" &&
+ git notes append --separator="-------" -m "notes-2" -m "notes-3" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append note with combination of -m and -F and --separator' '
+ test_when_finished git notes remove HEAD &&
+ cat >expect-combine_m_and_F <<-\EOF &&
+ m-notes-1
+ -------
+ f-notes-1
+ -------
+ m-notes-2
+ -------
+ f-notes-2
+ -------
+ m-notes-3
+ EOF
+
+ echo "f-notes-1" >note_a &&
+ echo "f-notes-2" >note_b &&
+ git notes append -m "m-notes-1" -F note_a -m "m-notes-2" -F note_b -m "m-notes-3" --separator "-------" &&
+ git notes show >actual &&
+ test_cmp expect-combine_m_and_F actual
+'
+
test_expect_success 'append to existing note with "git notes append"' '
cat >expect <<-EOF &&
Initial set of notes
@@ -818,6 +917,33 @@ test_expect_success 'create note from blob with "git notes add -C" reuses blob i
test_cmp blob actual
'
+test_expect_success 'create note from blob with "-C", also specify "-m", "-F" and "--separator"' '
+ # 8th will be reuseed in following tests, so rollback when the test is done
+ test_when_finished "git notes remove && git notes add -C $(cat blob)" &&
+ commit=$(git rev-parse HEAD) &&
+ cat >expect <<-EOF &&
+ commit $commit
+ Author: A U Thor <author@example.com>
+ Date: Thu Apr 7 15:20:13 2005 -0700
+
+ ${indent}8th
+
+ Notes:
+ ${indent}This is a blob object
+ ${indent}-------
+ ${indent}This is created by -m
+ ${indent}-------
+ ${indent}This is created by -F
+ EOF
+
+ git notes remove &&
+ echo "This is a blob object" | git hash-object -w --stdin >blob &&
+ echo "This is created by -F" >note_a &&
+ git notes add -C $(cat blob) -m "This is created by -m" -F note_a --separator="-------" &&
+ git log -1 >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'create note from other note with "git notes add -c"' '
test_commit 9th &&
commit=$(git rev-parse HEAD) &&
diff --git a/t/t3321-notes-stripspace.sh b/t/t3321-notes-stripspace.sh
new file mode 100755
index 0000000000..028d825e8f
--- /dev/null
+++ b/t/t3321-notes-stripspace.sh
@@ -0,0 +1,577 @@
+#!/bin/sh
+#
+# Copyright (c) 2023 Teng Long
+#
+
+test_description='Test commit notes with stripspace behavior'
+
+. ./test-lib.sh
+
+MULTI_LF="$LF$LF$LF"
+write_script fake_editor <<\EOF
+echo "$MSG" >"$1"
+echo "$MSG" >&2
+EOF
+GIT_EDITOR=./fake_editor
+export GIT_EDITOR
+
+test_expect_success 'setup the commit' '
+ test_commit 1st
+'
+
+test_expect_success 'add note by editor' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying single "-m", "--stripspace" is the default behavior' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ git notes add -m "${LF}first-line${MULTI_LF}second-line${LF}" &&
+ git notes show >actual &&
+ test_cmp expect actual &&
+ git notes remove &&
+ git notes add --stripspace -m "${LF}first-line${MULTI_LF}second-line${LF}" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying single "-m" and "--no-stripspace" ' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ ${LF}first-line${MULTI_LF}second-line
+ EOF
+
+ git notes add --no-stripspace \
+ -m "${LF}first-line${MULTI_LF}second-line${LF}" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying multiple "-m", "--stripspace" is the default behavior' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ git notes add -m "${LF}" \
+ -m "first-line" \
+ -m "${MULTI_LF}" \
+ -m "second-line" \
+ -m "${LF}" &&
+ git notes show >actual &&
+ test_cmp expect actual &&
+ git notes remove &&
+ git notes add --stripspace -m "${LF}" \
+ -m "first-line" \
+ -m "${MULTI_LF}" \
+ -m "second-line" \
+ -m "${LF}" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add notes by specifying multiple "-m" and "--no-stripspace"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ ${LF}
+ first-line
+ ${MULTI_LF}
+ second-line${LF}
+ EOF
+
+ git notes add --no-stripspace \
+ -m "${LF}" \
+ -m "first-line" \
+ -m "${MULTI_LF}" \
+ -m "second-line" \
+ -m "${LF}" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying single "-F", "--stripspace" is the default behavior' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ cat >note-file <<-EOF &&
+ ${LF}
+ first-line
+ ${MULTI_LF}
+ second-line
+ ${LF}
+ EOF
+
+ git notes add -F note-file &&
+ git notes show >actual &&
+ test_cmp expect actual &&
+ git notes remove &&
+ git notes add --stripspace -F note-file &&
+ git notes show >actual
+'
+
+test_expect_success 'add note by specifying single "-F" and "--no-stripspace"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ ${LF}
+ first-line
+ ${MULTI_LF}
+ second-line
+ ${LF}
+ EOF
+
+ cat >note-file <<-EOF &&
+ ${LF}
+ first-line
+ ${MULTI_LF}
+ second-line
+ ${LF}
+ EOF
+
+ git notes add --no-stripspace -F note-file &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying multiple "-F", "--stripspace" is the default behavior' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ file-1-first-line
+
+ file-1-second-line
+
+ file-2-first-line
+
+ file-2-second-line
+ EOF
+
+ cat >note-file-1 <<-EOF &&
+ ${LF}
+ file-1-first-line
+ ${MULTI_LF}
+ file-1-second-line
+ ${LF}
+ EOF
+
+ cat >note-file-2 <<-EOF &&
+ ${LF}
+ file-2-first-line
+ ${MULTI_LF}
+ file-2-second-line
+ ${LF}
+ EOF
+
+ git notes add -F note-file-1 -F note-file-2 &&
+ git notes show >actual &&
+ test_cmp expect actual &&
+ git notes remove &&
+ git notes add --stripspace -F note-file-1 -F note-file-2 &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying multiple "-F" with "--no-stripspace"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ ${LF}
+ file-1-first-line
+ ${MULTI_LF}
+ file-1-second-line
+ ${LF}
+
+ ${LF}
+ file-2-first-line
+ ${MULTI_LF}
+ file-2-second-line
+ ${LF}
+ EOF
+
+ cat >note-file-1 <<-EOF &&
+ ${LF}
+ file-1-first-line
+ ${MULTI_LF}
+ file-1-second-line
+ ${LF}
+ EOF
+
+ cat >note-file-2 <<-EOF &&
+ ${LF}
+ file-2-first-line
+ ${MULTI_LF}
+ file-2-second-line
+ ${LF}
+ EOF
+
+ git notes add --no-stripspace -F note-file-1 -F note-file-2 &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append note by editor' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ git notes add -m "first-line" &&
+ MSG="${MULTI_LF}second-line${LF}" git notes append &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append note by specifying single "-m"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ git notes add -m "${LF}first-line" &&
+ git notes append -m "${MULTI_LF}second-line${LF}" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append note by specifying multiple "-m"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ git notes add -m "${LF}first-line" &&
+ git notes append -m "${MULTI_LF}" \
+ -m "second-line" \
+ -m "${LF}" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying single "-F"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ cat >note-file <<-EOF &&
+ ${LF}
+ first-line
+ ${MULTI_LF}
+ second-line
+ ${LF}
+ EOF
+
+ git notes add -F note-file &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add notes by specifying multiple "-F"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ file-1-first-line
+
+ file-1-second-line
+
+ file-2-first-line
+
+ file-2-second-line
+ EOF
+
+ cat >note-file-1 <<-EOF &&
+ ${LF}
+ file-1-first-line
+ ${MULTI_LF}
+ file-1-second-line
+ ${LF}
+ EOF
+
+ cat >note-file-2 <<-EOF &&
+ ${LF}
+ file-2-first-line
+ ${MULTI_LF}
+ file-2-second-line
+ ${LF}
+ EOF
+
+ git notes add -F note-file-1 -F note-file-2 &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append note by specifying single "-F"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ initial-line
+
+ first-line
+
+ second-line
+ EOF
+
+ cat >note-file <<-EOF &&
+ ${LF}
+ first-line
+ ${MULTI_LF}
+ second-line
+ ${LF}
+ EOF
+
+ git notes add -m "initial-line" &&
+ git notes append -F note-file &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append notes by specifying multiple "-F"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ initial-line
+
+ file-1-first-line
+
+ file-1-second-line
+
+ file-2-first-line
+
+ file-2-second-line
+ EOF
+
+ cat >note-file-1 <<-EOF &&
+ ${LF}
+ file-1-first-line
+ ${MULTI_LF}
+ file-1-second-line
+ ${LF}
+ EOF
+
+ cat >note-file-2 <<-EOF &&
+ ${LF}
+ file-2-first-line
+ ${MULTI_LF}
+ file-2-second-line
+ ${LF}
+ EOF
+
+ git notes add -m "initial-line" &&
+ git notes append -F note-file-1 -F note-file-2 &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'append note by specifying multiple "-F" with "--no-stripspace"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ initial-line
+ ${LF}${LF}
+ file-1-first-line
+ ${MULTI_LF}
+ file-1-second-line
+ ${LF}
+
+ ${LF}
+ file-2-first-line
+ ${MULTI_LF}
+ file-2-second-line
+ ${LF}
+ EOF
+
+ cat >note-file-1 <<-EOF &&
+ ${LF}
+ file-1-first-line
+ ${MULTI_LF}
+ file-1-second-line
+ ${LF}
+ EOF
+
+ cat >note-file-2 <<-EOF &&
+ ${LF}
+ file-2-first-line
+ ${MULTI_LF}
+ file-2-second-line
+ ${LF}
+ EOF
+
+ git notes add -m "initial-line" &&
+ git notes append --no-stripspace -F note-file-1 -F note-file-2 &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add notes with empty messages' '
+ rev=$(git rev-parse HEAD) &&
+ git notes add -m "${LF}" \
+ -m "${MULTI_LF}" \
+ -m "${LF}" >actual 2>&1 &&
+ test_i18ngrep "Removing note for object" actual
+'
+
+test_expect_success 'add note by specifying "-C", "--no-stripspace" is the default behavior' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ ${LF}
+ first-line
+ ${MULTI_LF}
+ second-line
+ ${LF}
+ EOF
+
+ cat expect | git hash-object -w --stdin >blob &&
+ git notes add -C $(cat blob) &&
+ git notes show >actual &&
+ test_cmp expect actual &&
+ git notes remove &&
+ git notes add --no-stripspace -C $(cat blob) &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'reuse note by specifying "-C" and "--stripspace"' '
+ test_when_finished "git notes remove" &&
+ cat >data <<-EOF &&
+ ${LF}
+ first-line
+ ${MULTI_LF}
+ second-line
+ ${LF}
+ EOF
+
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ cat data | git hash-object -w --stdin >blob &&
+ git notes add --stripspace -C $(cat blob) &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'reuse with "-C" and add note with "-m", "-m" will stripspace all together' '
+ test_when_finished "git notes remove" &&
+ cat >data <<-EOF &&
+ ${LF}
+ first-line
+ ${MULTI_LF}
+ second-line
+ ${LF}
+ EOF
+
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+
+ third-line
+ EOF
+
+ cat data | git hash-object -w --stdin >blob &&
+ git notes add -C $(cat blob) -m "third-line" &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note with "-m" and reuse note with "-C", "-C" will not stripspace all together' '
+ test_when_finished "git notes remove" &&
+ cat >data <<-EOF &&
+
+ second-line
+ EOF
+
+ cat >expect <<-EOF &&
+ first-line
+ ${LF}
+ second-line
+ EOF
+
+ cat data | git hash-object -w --stdin >blob &&
+ git notes add -m "first-line" -C $(cat blob) &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying "-c", "--stripspace" is the default behavior' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ echo "initial-line" | git hash-object -w --stdin >blob &&
+ MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add -c $(cat blob) &&
+ git notes show >actual &&
+ test_cmp expect actual &&
+ git notes remove &&
+ MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add --stripspace -c $(cat blob) &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'add note by specifying "-c" with "--no-stripspace"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ ${LF}first-line${MULTI_LF}second-line${LF}
+ EOF
+
+ echo "initial-line" | git hash-object -w --stdin >blob &&
+ MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add --no-stripspace -c $(cat blob) &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'edit note by specifying "-c", "--stripspace" is the default behavior' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ first-line
+
+ second-line
+ EOF
+
+ MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes edit &&
+ git notes show >actual &&
+ test_cmp expect actual &&
+ git notes remove &&
+ MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes edit --stripspace &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'edit note by specifying "-c" with "--no-stripspace"' '
+ test_when_finished "git notes remove" &&
+ cat >expect <<-EOF &&
+ ${LF}first-line${MULTI_LF}second-line${LF}
+ EOF
+
+ MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add --no-stripspace &&
+ git notes show >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
index 79b0640c00..e9e03ca4b5 100755
--- a/t/t3402-rebase-merge.sh
+++ b/t/t3402-rebase-merge.sh
@@ -8,6 +8,7 @@ test_description='git rebase --merge test'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
T="A quick brown fox
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index ff0afad63e..653c19bc9c 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1276,20 +1276,37 @@ test_expect_success 'todo count' '
'
test_expect_success 'rebase -i commits that overwrite untracked files (pick)' '
- git checkout --force branch2 &&
+ git checkout --force A &&
git clean -f &&
+ cat >todo <<-EOF &&
+ exec >file2
+ pick $(git rev-parse B) B
+ pick $(git rev-parse C) C
+ pick $(git rev-parse D) D
+ exec cat .git/rebase-merge/done >actual
+ EOF
(
- set_fake_editor &&
- FAKE_LINES="edit 1 2" git rebase -i A
- ) &&
- test_cmp_rev HEAD F &&
- test_path_is_missing file6 &&
- >file6 &&
- test_must_fail git rebase --continue &&
- test_cmp_rev HEAD F &&
- rm file6 &&
+ set_replace_editor todo &&
+ test_must_fail git rebase -i A
+ ) &&
+ test_cmp_rev HEAD B &&
+ head -n3 todo >expect &&
+ test_cmp expect .git/rebase-merge/done &&
+ rm file2 &&
+ test_path_is_missing .git/rebase-merge/author-script &&
+ test_path_is_missing .git/rebase-merge/patch &&
+ test_path_is_missing .git/MERGE_MSG &&
+ test_path_is_missing .git/rebase-merge/message &&
+ test_path_is_missing .git/rebase-merge/stopped-sha &&
+ echo changed >file1 &&
+ git add file1 &&
+ test_must_fail git rebase --continue 2>err &&
+ grep "error: you have staged changes in your working tree" err &&
+ git reset --hard HEAD &&
git rebase --continue &&
- test_cmp_rev HEAD I
+ test_cmp_rev HEAD D &&
+ tail -n3 todo >>expect &&
+ test_cmp expect actual
'
test_expect_success 'rebase -i commits that overwrite untracked files (squash)' '
@@ -1306,6 +1323,11 @@ test_expect_success 'rebase -i commits that overwrite untracked files (squash)'
test_must_fail git rebase --continue &&
test_cmp_rev HEAD F &&
rm file6 &&
+ test_path_is_missing .git/rebase-merge/author-script &&
+ test_path_is_missing .git/rebase-merge/patch &&
+ test_path_is_missing .git/MERGE_MSG &&
+ test_path_is_missing .git/rebase-merge/message &&
+ test_path_is_missing .git/rebase-merge/stopped-sha &&
git rebase --continue &&
test $(git cat-file commit HEAD | sed -ne \$p) = I &&
git reset --hard original-branch2
@@ -1324,6 +1346,11 @@ test_expect_success 'rebase -i commits that overwrite untracked files (no ff)' '
test_must_fail git rebase --continue &&
test $(git cat-file commit HEAD | sed -ne \$p) = F &&
rm file6 &&
+ test_path_is_missing .git/rebase-merge/author-script &&
+ test_path_is_missing .git/rebase-merge/patch &&
+ test_path_is_missing .git/MERGE_MSG &&
+ test_path_is_missing .git/rebase-merge/message &&
+ test_path_is_missing .git/rebase-merge/stopped-sha &&
git rebase --continue &&
test $(git cat-file commit HEAD | sed -ne \$p) = I
'
diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh
index 48b76f8232..1b3e97c875 100755
--- a/t/t3427-rebase-subtree.sh
+++ b/t/t3427-rebase-subtree.sh
@@ -74,9 +74,9 @@ test_expect_success 'Rebase -Xsubtree --empty=ask --onto commit' '
test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --onto files-main main &&
: first pick results in no changes &&
git rebase --skip &&
- verbose test "$(commit_message HEAD~2)" = "topic_4" &&
- verbose test "$(commit_message HEAD~)" = "files_subtree/topic_5" &&
- verbose test "$(commit_message HEAD)" = "Empty commit"
+ test "$(commit_message HEAD~2)" = "topic_4" &&
+ test "$(commit_message HEAD~)" = "files_subtree/topic_5" &&
+ test "$(commit_message HEAD)" = "Empty commit"
'
test_expect_success 'Rebase -Xsubtree --empty=ask --rebase-merges --onto commit' '
@@ -85,9 +85,9 @@ test_expect_success 'Rebase -Xsubtree --empty=ask --rebase-merges --onto commit'
test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --rebase-merges --onto files-main --root &&
: first pick results in no changes &&
git rebase --skip &&
- verbose test "$(commit_message HEAD~2)" = "topic_4" &&
- verbose test "$(commit_message HEAD~)" = "files_subtree/topic_5" &&
- verbose test "$(commit_message HEAD)" = "Empty commit"
+ test "$(commit_message HEAD~2)" = "topic_4" &&
+ test "$(commit_message HEAD~)" = "files_subtree/topic_5" &&
+ test "$(commit_message HEAD)" = "Empty commit"
'
test_done
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index f03599c63b..dc831dcfcd 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -128,14 +128,24 @@ test_expect_success 'generate correct todo list' '
'
test_expect_success '`reset` refuses to overwrite untracked files' '
- git checkout -b refuse-to-reset &&
+ git checkout B &&
test_commit dont-overwrite-untracked &&
- git checkout @{-1} &&
- : >dont-overwrite-untracked.t &&
- echo "reset refs/tags/dont-overwrite-untracked" >script-from-scratch &&
+ cat >script-from-scratch <<-EOF &&
+ exec >dont-overwrite-untracked.t
+ pick $(git rev-parse B) B
+ reset refs/tags/dont-overwrite-untracked
+ pick $(git rev-parse C) C
+ exec cat .git/rebase-merge/done >actual
+ EOF
test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
- test_must_fail git rebase -ir HEAD &&
- git rebase --abort
+ test_must_fail git rebase -ir A &&
+ test_cmp_rev HEAD B &&
+ head -n3 script-from-scratch >expect &&
+ test_cmp expect .git/rebase-merge/done &&
+ rm dont-overwrite-untracked.t &&
+ git rebase --continue &&
+ tail -n3 script-from-scratch >>expect &&
+ test_cmp expect actual
'
test_expect_success '`reset` rejects trees' '
@@ -167,14 +177,21 @@ test_expect_success 'failed `merge -C` writes patch (may be rescheduled, too)' '
test_must_fail git rebase -ir HEAD &&
grep "^merge -C .* G$" .git/rebase-merge/done &&
grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
- test_path_is_file .git/rebase-merge/patch &&
+ test_path_is_missing .git/rebase-merge/patch &&
+ test_path_is_missing .git/rebase-merge/author-script &&
+ test_path_is_missing .git/MERGE_MSG &&
+ test_path_is_missing .git/rebase-merge/message &&
+ test_path_is_missing .git/rebase-merge/stopped-sha &&
: fail because of merge conflict &&
- rm G.t .git/rebase-merge/patch &&
git reset --hard conflicting-G &&
test_must_fail git rebase --continue &&
! grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
- test_path_is_file .git/rebase-merge/patch
+ test_path_is_file .git/rebase-merge/patch &&
+ test_path_is_file .git/rebase-merge/author-script &&
+ test_path_is_file .git/MERGE_MSG &&
+ test_path_is_file .git/rebase-merge/message &&
+ test_path_is_file .git/rebase-merge/stopped-sha
'
test_expect_success 'failed `merge <branch>` does not crash' '
@@ -578,4 +595,12 @@ test_expect_success '--rebase-merges with message matched with onto label' '
EOF
'
+test_expect_success 'progress shows the correct total' '
+ git checkout -b progress H &&
+ git rebase --rebase-merges --force-rebase --verbose A 2> err &&
+ # Expecting "Rebasing (N/14)" here, no bogus total number
+ grep "^Rebasing.*/14.$" err >progress &&
+ test_line_count = 14 progress
+'
+
test_done
diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
index 2f3e3e2416..e2ef619323 100755
--- a/t/t3501-revert-cherry-pick.sh
+++ b/t/t3501-revert-cherry-pick.sh
@@ -1,14 +1,6 @@
#!/bin/sh
-test_description='test cherry-pick and revert with renames
-
- --
- + rename2: renames oops to opos
- + rename1: renames oops to spoo
- + added: adds extra line to oops
- ++ initial: has lines in oops
-
-'
+test_description='miscellaneous basic tests for cherry-pick and revert'
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
@@ -63,6 +55,14 @@ test_expect_success 'revert --nonsense' '
test_i18ngrep "[Uu]sage:" msg
'
+# the following two test cherry-pick and revert with renames
+#
+# --
+# + rename2: renames oops to opos
+# + rename1: renames oops to spoo
+# + added: adds extra line to oops
+# ++ initial: has lines in oops
+
test_expect_success 'cherry-pick after renaming branch' '
git checkout rename2 &&
diff --git a/t/t3515-revert-subjects.sh b/t/t3515-revert-subjects.sh
new file mode 100755
index 0000000000..4c231b48a5
--- /dev/null
+++ b/t/t3515-revert-subjects.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='git revert'
+
+. ./test-lib.sh
+
+test_expect_success 'fresh reverts' '
+ test_commit --no-tag A file1 &&
+ test_commit --no-tag B file1 &&
+ git revert --no-edit HEAD &&
+ echo "Revert \"B\"" > expect &&
+ git log -1 --pretty=%s > actual &&
+ test_cmp expect actual &&
+ git revert --no-edit HEAD &&
+ echo "Reapply \"B\"" > expect &&
+ git log -1 --pretty=%s > actual &&
+ test_cmp expect actual &&
+ git revert --no-edit HEAD &&
+ echo "Revert \"Reapply \"B\"\"" > expect &&
+ git log -1 --pretty=%s > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'legacy double revert' '
+ test_commit --no-tag "Revert \"Revert \"B\"\"" file1 &&
+ git revert --no-edit HEAD &&
+ echo "Revert \"Reapply \"B\"\"" > expect &&
+ git log -1 --pretty=%s > actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh
index bfcaae390f..8d50331b8c 100755
--- a/t/t4000-diff-format.sh
+++ b/t/t4000-diff-format.sh
@@ -5,6 +5,9 @@
test_description='Test built-in diff output engine.
+We happen to know that all diff plumbing and diff Porcelain share the
+same command line parser, so testing one should be sufficient; pick
+diff-files as a representative.
'
TEST_PASSES_SANITIZE_LEAK=true
@@ -16,9 +19,11 @@ Line 2
line 3'
cat path0 >path1
chmod +x path1
+mkdir path2
+>path2/path3
test_expect_success 'update-index --add two files with and without +x.' '
- git update-index --add path0 path1
+ git update-index --add path0 path1 path2/path3
'
mv path0 path0-
@@ -91,4 +96,31 @@ test_expect_success 'git diff-files --patch --no-patch does not show the patch'
test_must_be_empty err
'
+
+# Smudge path2/path3 so that dirstat has something to show
+date >path2/path3
+
+for format in stat raw numstat shortstat summary \
+ dirstat cumulative dirstat-by-file \
+ patch-with-raw patch-with-stat compact-summary
+do
+ test_expect_success "--no-patch in 'git diff-files --no-patch --$format' is a no-op" '
+ git diff-files --no-patch "--$format" >actual &&
+ git diff-files "--$format" >expect &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "--no-patch clears all previous ones" '
+ git diff-files --$format -s -p >actual &&
+ git diff-files -p >expect &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "--no-patch in 'git diff --no-patch --$format' is a no-op" '
+ git diff --no-patch "--$format" >actual &&
+ git diff "--$format" >expect &&
+ test_cmp expect actual
+ '
+done
+
test_done
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 5de1d19075..3a687c1617 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -334,6 +334,7 @@ log --first-parent --diff-merges=off -p master
log -p --first-parent master
log -p --diff-merges=first-parent master
log --diff-merges=first-parent master
+log --diff-merges=first-parent --diff-merges=hide master
log -m -p --first-parent master
log -m -p master
log --cc -m -p master
@@ -460,7 +461,18 @@ EOF
test_expect_success 'log -m matches pure log' '
git log master >result &&
process_diffs result >expected &&
- git log -m >result &&
+ git log -m master >result &&
+ process_diffs result >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'log --diff-merges=on matches -m only with --diff-merges=hide' '
+ git log -m master >result &&
+ process_diffs result >expected &&
+ git log --diff-merges=on master >result &&
+ process_diffs result >actual &&
+ ! test_cmp expected actual &&
+ git log --diff-merges=on --diff-merges=hide master >result &&
process_diffs result >actual &&
test_cmp expected actual
'
@@ -473,6 +485,14 @@ test_expect_success 'log --diff-merges=on matches --diff-merges=separate' '
test_cmp expected actual
'
+test_expect_success 'log --diff-merges=<V1>,<V2>' '
+ git log --diff-merges=1,hide master >result &&
+ process_diffs result >expected &&
+ git log --diff-merges=1 --diff-merges=hide master >result &&
+ process_diffs result >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'deny wrong log.diffMerges config' '
test_config log.diffMerges wrong-value &&
test_expect_code 128 git log
@@ -496,6 +516,55 @@ test_expect_success 'git config log.diffMerges first-parent vs -m' '
test_cmp expected actual
'
+test_expect_success 'git config log.diffMerges hide: has effect' '
+ git log --diff-merges=on master >result &&
+ process_diffs result >no-hide &&
+ test_config log.diffMerges hide &&
+ git log --diff-merges=on master >result &&
+ process_diffs result >hide &&
+ ! test_cmp no-hide hide
+'
+
+test_expect_success 'git config log.diffMerges no-hide: is the default' '
+ git log --diff-merges=on master >result &&
+ process_diffs result >default &&
+ test_config log.diffMerges no-hide &&
+ git log --diff-merges=on master >result &&
+ process_diffs result >no-hide &&
+ test_cmp default no-hide
+'
+
+# As "-m" is synonym for "--diff-merges=hide --diff-merges=on", the
+# "log.diffMerges=hide" configuration should have no effect on "-m"
+test_expect_success 'git config log.diffMerges hide: has no effect on -m' '
+ git log -m master >result &&
+ process_diffs result >expected &&
+ test_config log.diffMerges hide &&
+ git log -m master >result &&
+ process_diffs result >actual &&
+ test_cmp expected actual
+'
+
+# As "--cc" implies "-p", the "log.diffMerges=hide" configuration
+# should have no effect on "--cc"
+test_expect_success 'git config log.diffMerges hide: has no effect on --cc' '
+ git log --cc master >result &&
+ process_diffs result >expected &&
+ test_config log.diffMerges hide &&
+ git log --cc master >result &&
+ process_diffs result >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'git config log.diffMerges-m-imply-p has proper effect' '
+ git log -m -p master >result &&
+ process_diffs result >expected &&
+ test_config log.diffMerges-m-imply-p true &&
+ git log -m master >result &&
+ process_diffs result >actual &&
+ test_cmp expected actual
+'
+
# -m in "git diff-index" means "match missing", that differs
# from its meaning in "git diff". Let's check it in diff-index.
# The line in the output for removed file should disappear when
diff --git a/t/t4013/diff.log_--diff-merges=first-parent_--diff-merges=hide_master b/t/t4013/diff.log_--diff-merges=first-parent_--diff-merges=hide_master
new file mode 100644
index 0000000000..596caec642
--- /dev/null
+++ b/t/t4013/diff.log_--diff-merges=first-parent_--diff-merges=hide_master
@@ -0,0 +1,34 @@
+$ git log --diff-merges=first-parent --diff-merges=hide master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 42a2b9a13b..c8d555771d 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -63,6 +63,25 @@ do
test_i18ngrep ! fatal msg &&
test_i18ngrep ! error msg
'
+
+ test_expect_success "builtin $p pattern compiles on bare repo with --attr-source" '
+ test_when_finished "rm -rf bare.git" &&
+ git checkout -B master &&
+ git add . &&
+ echo "*.java diff=notexist" >.gitattributes &&
+ git add .gitattributes &&
+ git commit -am "changing gitattributes" &&
+ git checkout -B branchA &&
+ echo "*.java diff=$p" >.gitattributes &&
+ git add .gitattributes &&
+ git commit -am "changing gitattributes" &&
+ git clone --bare --no-local . bare.git &&
+ git -C bare.git symbolic-ref HEAD refs/heads/master &&
+ test_expect_code 1 git -C bare.git --attr-source=branchA \
+ diff --exit-code HEAD:A.java HEAD:B.java 2>msg &&
+ test_i18ngrep ! fatal msg &&
+ test_i18ngrep ! error msg
+ '
done
test_expect_success 'last regexp must not be negated' '
diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh
index 1c89050a97..6fed993ea0 100755
--- a/t/t4022-diff-rewrite.sh
+++ b/t/t4022-diff-rewrite.sh
@@ -24,7 +24,7 @@ test_expect_success setup '
test_expect_success 'detect rewrite' '
actual=$(git diff-files -B --summary test) &&
- verbose expr "$actual" : " rewrite test ([0-9]*%)$"
+ expr "$actual" : " rewrite test ([0-9]*%)$"
'
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
index 40164ae07d..1c747cc325 100755
--- a/t/t4027-diff-submodule.sh
+++ b/t/t4027-diff-submodule.sh
@@ -34,6 +34,25 @@ test_expect_success setup '
subtip=$3 subprev=$2
'
+test_expect_success 'diff in superproject with submodules respects parallel settings' '
+ test_when_finished "rm -f trace.out" &&
+ (
+ GIT_TRACE=$(pwd)/trace.out git diff &&
+ grep "1 tasks" trace.out &&
+ >trace.out &&
+
+ git config submodule.diffJobs 8 &&
+ GIT_TRACE=$(pwd)/trace.out git diff &&
+ grep "8 tasks" trace.out &&
+ >trace.out &&
+
+ GIT_TRACE=$(pwd)/trace.out git -c submodule.diffJobs=0 diff &&
+ grep "preparing to run up to [0-9]* tasks" trace.out &&
+ ! grep "up to 0 tasks" trace.out &&
+ >trace.out
+ )
+'
+
test_expect_success 'git diff --raw HEAD' '
hexsz=$(test_oid hexsz) &&
git diff --raw --abbrev=$hexsz HEAD >actual &&
@@ -70,6 +89,18 @@ test_expect_success 'git diff HEAD with dirty submodule (work tree)' '
test_cmp expect.body actual.body
'
+test_expect_success 'git diff HEAD with dirty submodule (work tree, parallel)' '
+ (
+ cd sub &&
+ git reset --hard &&
+ echo >>world
+ ) &&
+ git -c submodule.diffJobs=8 diff HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev-dirty &&
+ test_cmp expect.body actual.body
+'
+
test_expect_success 'git diff HEAD with dirty submodule (index)' '
(
cd sub &&
diff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh
index 7fec2cb9cd..70224c3da1 100755
--- a/t/t4047-diff-dirstat.sh
+++ b/t/t4047-diff-dirstat.sh
@@ -1,6 +1,8 @@
#!/bin/sh
test_description='diff --dirstat tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# set up two commits where the second commit has these files
diff --git a/t/t4062-diff-pickaxe.sh b/t/t4062-diff-pickaxe.sh
index 9aaa068ed9..a90b46b678 100755
--- a/t/t4062-diff-pickaxe.sh
+++ b/t/t4062-diff-pickaxe.sh
@@ -24,7 +24,7 @@ test_expect_success '-G matches' '
test_expect_success '-S --pickaxe-regex' '
git diff --name-only -S0 --pickaxe-regex HEAD^ >out &&
- verbose test 4096-zeroes.txt = "$(cat out)"
+ test 4096-zeroes.txt = "$(cat out)"
'
test_done
diff --git a/t/t4067-diff-partial-clone.sh b/t/t4067-diff-partial-clone.sh
index f60f5cbd65..7af3a08862 100755
--- a/t/t4067-diff-partial-clone.sh
+++ b/t/t4067-diff-partial-clone.sh
@@ -151,7 +151,7 @@ test_expect_success 'diff does not fetch anything if inexact rename detection is
# Ensure no fetches.
GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff --raw -M HEAD^ HEAD &&
- ! test_path_exists trace
+ test_path_is_missing trace
'
test_expect_success 'diff --break-rewrites fetches only if necessary, and batches blobs if it does' '
@@ -171,7 +171,7 @@ test_expect_success 'diff --break-rewrites fetches only if necessary, and batche
# Ensure no fetches.
GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff --raw -M HEAD^ HEAD &&
- ! test_path_exists trace &&
+ test_path_is_missing trace &&
# But with --break-rewrites, ensure that there is exactly 1 negotiation
# by checking that there is only 1 "done" line sent. ("done" marks the
diff --git a/t/t4115-apply-symlink.sh b/t/t4115-apply-symlink.sh
index e95e6d4e7d..a22a90d552 100755
--- a/t/t4115-apply-symlink.sh
+++ b/t/t4115-apply-symlink.sh
@@ -74,7 +74,7 @@ test_expect_success SYMLINKS 'symlink escape when creating new files' '
error: affected file ${SQ}renamed-symlink/create-me${SQ} is beyond a symbolic link
EOF
test_cmp expected_stderr stderr &&
- ! test_path_exists .git/create-me
+ test_path_is_missing .git/create-me
'
test_expect_success SYMLINKS 'symlink escape when modifying file' '
diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh
index e89e1f54b6..85e90acb09 100755
--- a/t/t4212-log-corrupt.sh
+++ b/t/t4212-log-corrupt.sh
@@ -8,8 +8,9 @@ TEST_PASSES_SANITIZE_LEAK=true
test_expect_success 'setup' '
test_commit foo &&
- git cat-file commit HEAD |
- sed "/^author /s/>/>-<>/" >broken_email.commit &&
+ git cat-file commit HEAD >ok.commit &&
+ sed "s/>/>-<>/" <ok.commit >broken_email.commit &&
+
git hash-object --literally -w -t commit broken_email.commit >broken_email.hash &&
git update-ref refs/heads/broken_email $(cat broken_email.hash)
'
@@ -43,6 +44,11 @@ test_expect_success 'git log --format with broken author email' '
test_must_be_empty actual.err
'
+test_expect_success '--until handles broken email' '
+ git rev-list --until=1980-01-01 broken_email >actual &&
+ test_must_be_empty actual
+'
+
munge_author_date () {
git cat-file commit "$1" >commit.orig &&
sed "s/^\(author .*>\) [0-9]*/\1 $2/" <commit.orig >commit.munge &&
@@ -86,4 +92,45 @@ test_expect_success 'absurdly far-in-future date' '
git log -1 --format=%ad $commit
'
+test_expect_success 'create commits with whitespace committer dates' '
+ # It is important that this subject line is numeric, since we want to
+ # be sure we are not confused by skipping whitespace and accidentally
+ # parsing the subject as a timestamp.
+ #
+ # Do not use munge_author_date here. Besides not hitting the committer
+ # line, it leaves the timezone intact, and we want nothing but
+ # whitespace.
+ #
+ # We will make two munged commits here. The first, ws_commit, will
+ # be purely spaces. The second contains a vertical tab, which is
+ # considered a space by strtoumax(), but not by our isspace().
+ test_commit 1234567890 &&
+ git cat-file commit HEAD >commit.orig &&
+ sed "s/>.*/> /" <commit.orig >commit.munge &&
+ ws_commit=$(git hash-object --literally -w -t commit commit.munge) &&
+ sed "s/>.*/> $(printf "\013")/" <commit.orig >commit.munge &&
+ vt_commit=$(git hash-object --literally -w -t commit commit.munge)
+'
+
+test_expect_success '--until treats whitespace date as sentinel' '
+ echo $ws_commit >expect &&
+ git rev-list --until=1980-01-01 $ws_commit >actual &&
+ test_cmp expect actual &&
+
+ echo $vt_commit >expect &&
+ git rev-list --until=1980-01-01 $vt_commit >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'pretty-printer handles whitespace date' '
+ # as with the %ad test above, we will show these as the empty string,
+ # not the 1970 epoch date. This is intentional; see 7d9a281941 (t4212:
+ # test bogus timestamps with git-log, 2014-02-24) for more discussion.
+ echo : >expect &&
+ git log -1 --format="%at:%ct" $ws_commit >actual &&
+ test_cmp expect actual &&
+ git log -1 --format="%at:%ct" $vt_commit >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t4300-merge-tree.sh b/t/t4300-merge-tree.sh
index c52c8a21fa..57c4f26e46 100755
--- a/t/t4300-merge-tree.sh
+++ b/t/t4300-merge-tree.sh
@@ -334,4 +334,22 @@ test_expect_success 'turn tree to file' '
test_cmp expect actual
'
+test_expect_success 'merge-tree respects core.useReplaceRefs=false' '
+ test_commit merge-to &&
+ test_commit valid base &&
+ git reset --hard HEAD^ &&
+ test_commit malicious base &&
+
+ test_when_finished "git replace -d $(git rev-parse valid^0)" &&
+ git replace valid^0 malicious^0 &&
+
+ tree=$(git -c core.useReplaceRefs=true merge-tree --write-tree merge-to valid) &&
+ merged=$(git cat-file -p $tree:base) &&
+ test malicious = $merged &&
+
+ tree=$(git -c core.useReplaceRefs=false merge-tree --write-tree merge-to valid) &&
+ merged=$(git cat-file -p $tree:base) &&
+ test valid = $merged
+'
+
test_done
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index 662ae9b152..b4df545e5a 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -16,7 +16,7 @@ add_blob() {
before=$(git count-objects | sed "s/ .*//") &&
BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
- verbose test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
test_path_is_file $BLOB_FILE &&
test-tool chmtime =+0 $BLOB_FILE
}
@@ -51,11 +51,11 @@ test_expect_success 'prune stale packs' '
test_expect_success 'prune --expire' '
add_blob &&
git prune --expire=1.hour.ago &&
- verbose test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
test_path_is_file $BLOB_FILE &&
test-tool chmtime =-86500 $BLOB_FILE &&
git prune --expire 1.day &&
- verbose test $before = $(git count-objects | sed "s/ .*//") &&
+ test $before = $(git count-objects | sed "s/ .*//") &&
test_path_is_missing $BLOB_FILE
'
@@ -63,11 +63,11 @@ test_expect_success 'gc: implicit prune --expire' '
add_blob &&
test-tool chmtime =-$((2*$week-30)) $BLOB_FILE &&
git gc --no-cruft &&
- verbose test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
test_path_is_file $BLOB_FILE &&
test-tool chmtime =-$((2*$week+1)) $BLOB_FILE &&
git gc --no-cruft &&
- verbose test $before = $(git count-objects | sed "s/ .*//") &&
+ test $before = $(git count-objects | sed "s/ .*//") &&
test_path_is_missing $BLOB_FILE
'
@@ -138,7 +138,7 @@ test_expect_success 'gc --no-prune' '
test-tool chmtime =-$((5001*$day)) $BLOB_FILE &&
git config gc.pruneExpire 2.days.ago &&
git gc --no-prune --no-cruft &&
- verbose test 1 = $(git count-objects | sed "s/ .*//") &&
+ test 1 = $(git count-objects | sed "s/ .*//") &&
test_path_is_file $BLOB_FILE
'
@@ -192,10 +192,10 @@ test_expect_success 'gc: prune old objects after local clone' '
git clone --no-hardlinks . aclone &&
(
cd aclone &&
- verbose test 1 = $(git count-objects | sed "s/ .*//") &&
+ test 1 = $(git count-objects | sed "s/ .*//") &&
test_path_is_file $BLOB_FILE &&
git gc --prune --no-cruft &&
- verbose test 0 = $(git count-objects | sed "s/ .*//") &&
+ test 0 = $(git count-objects | sed "s/ .*//") &&
test_path_is_missing $BLOB_FILE
)
'
@@ -350,4 +350,18 @@ test_expect_success 'old reachable-from-recent retained with bitmaps' '
test_must_fail git cat-file -e $to_drop
'
+test_expect_success 'gc.recentObjectsHook' '
+ add_blob &&
+ test-tool chmtime =-86500 $BLOB_FILE &&
+
+ write_script precious-objects <<-EOF &&
+ echo $BLOB
+ EOF
+ test_config gc.recentObjectsHook ./precious-objects &&
+
+ git prune --expire=now &&
+
+ git cat-file -p $BLOB
+'
+
test_done
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
index 526a5a506e..78c1c6c923 100755
--- a/t/t5310-pack-bitmaps.sh
+++ b/t/t5310-pack-bitmaps.sh
@@ -9,6 +9,10 @@ test_description='exercise basic bitmap functionality'
# their place.
GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0
+# Likewise, allow individual tests to control whether or not they use
+# the boundary-based traversal.
+sane_unset GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL
+
objpath () {
echo ".git/objects/$(echo "$1" | sed -e 's|\(..\)|\1/|')"
}
@@ -457,6 +461,13 @@ test_bitmap_cases () {
test_bitmap_cases
+GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1
+export GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL
+
+test_bitmap_cases
+
+sane_unset GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL
+
test_expect_success 'incremental repack fails when bitmaps are requested' '
test_commit more-1 &&
test_must_fail git repack -d 2>err &&
@@ -468,6 +479,33 @@ test_expect_success 'incremental repack can disable bitmaps' '
git repack -d --no-write-bitmap-index
'
+test_expect_success 'boundary-based traversal is used when requested' '
+ git repack -a -d --write-bitmap-index &&
+
+ for argv in \
+ "git -c pack.useBitmapBoundaryTraversal=true" \
+ "git -c feature.experimental=true" \
+ "GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=1 git"
+ do
+ eval "GIT_TRACE2_EVENT=1 $argv rev-list --objects \
+ --use-bitmap-index second..other 2>perf" &&
+ grep "\"region_enter\".*\"label\":\"haves/boundary\"" perf ||
+ return 1
+ done &&
+
+ for argv in \
+ "git -c pack.useBitmapBoundaryTraversal=false" \
+ "git -c feature.experimental=true -c pack.useBitmapBoundaryTraversal=false" \
+ "GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=0 git -c pack.useBitmapBoundaryTraversal=true" \
+ "GIT_TEST_PACK_USE_BITMAP_BOUNDARY_TRAVERSAL=0 git -c feature.experimental=true"
+ do
+ eval "GIT_TRACE2_EVENT=1 $argv rev-list --objects \
+ --use-bitmap-index second..other 2>perf" &&
+ grep "\"region_enter\".*\"label\":\"haves/classic\"" perf ||
+ return 1
+ done
+'
+
test_bitmap_cases "pack.writeBitmapLookupTable"
test_expect_success 'verify writing bitmap lookup table when enabled' '
diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh
index 0882cbb6e4..f771c442d4 100755
--- a/t/t5326-multi-pack-bitmaps.sh
+++ b/t/t5326-multi-pack-bitmaps.sh
@@ -434,4 +434,48 @@ test_expect_success 'tagged commits are selected for bitmapping' '
)
'
+corrupt_file () {
+ chmod a+w "$1" &&
+ printf "bogus" | dd of="$1" bs=1 seek="12" conv=notrunc
+}
+
+test_expect_success 'git fsck correctly identifies good and bad bitmaps' '
+ git init valid &&
+ test_when_finished rm -rf valid &&
+
+ test_commit_bulk 20 &&
+ git repack -adbf &&
+
+ # Move pack-bitmap aside so it is not deleted
+ # in next repack.
+ packbitmap=$(ls .git/objects/pack/pack-*.bitmap) &&
+ mv "$packbitmap" "$packbitmap.bak" &&
+
+ test_commit_bulk 10 &&
+ git repack -b --write-midx &&
+ midxbitmap=$(ls .git/objects/pack/multi-pack-index-*.bitmap) &&
+
+ # Copy MIDX bitmap to backup. Copy pack bitmap from backup.
+ cp "$midxbitmap" "$midxbitmap.bak" &&
+ cp "$packbitmap.bak" "$packbitmap" &&
+
+ # fsck works at first
+ git fsck 2>err &&
+ test_must_be_empty err &&
+
+ corrupt_file "$packbitmap" &&
+ test_must_fail git fsck 2>err &&
+ grep "bitmap file '\''$packbitmap'\'' has invalid checksum" err &&
+
+ cp "$packbitmap.bak" "$packbitmap" &&
+ corrupt_file "$midxbitmap" &&
+ test_must_fail git fsck 2>err &&
+ grep "bitmap file '\''$midxbitmap'\'' has invalid checksum" err &&
+
+ corrupt_file "$packbitmap" &&
+ test_must_fail git fsck 2>err &&
+ grep "bitmap file '\''$midxbitmap'\'' has invalid checksum" err &&
+ grep "bitmap file '\''$packbitmap'\'' has invalid checksum" err
+'
+
test_done
diff --git a/t/t5329-pack-objects-cruft.sh b/t/t5329-pack-objects-cruft.sh
index 303f7a5d84..3ae61ca995 100755
--- a/t/t5329-pack-objects-cruft.sh
+++ b/t/t5329-pack-objects-cruft.sh
@@ -739,4 +739,175 @@ test_expect_success 'cruft objects are freshend via loose' '
)
'
+test_expect_success 'gc.recentObjectsHook' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ # Create a handful of objects.
+ #
+ # - one reachable commit, "base", designated for the reachable
+ # pack
+ # - one unreachable commit, "cruft.discard", which is marked
+ # for deletion
+ # - one unreachable commit, "cruft.old", which would be marked
+ # for deletion, but is rescued as an extra cruft tip
+ # - one unreachable commit, "cruft.new", which is not marked
+ # for deletion
+ test_commit base &&
+ git branch -M main &&
+
+ git checkout --orphan discard &&
+ git rm -fr . &&
+ test_commit --no-tag cruft.discard &&
+
+ git checkout --orphan old &&
+ git rm -fr . &&
+ test_commit --no-tag cruft.old &&
+ cruft_old="$(git rev-parse HEAD)" &&
+
+ git checkout --orphan new &&
+ git rm -fr . &&
+ test_commit --no-tag cruft.new &&
+ cruft_new="$(git rev-parse HEAD)" &&
+
+ git checkout main &&
+ git branch -D discard old new &&
+ git reflog expire --all --expire=all &&
+
+ # mark cruft.old with an mtime that is many minutes
+ # older than the expiration period, and mark cruft.new
+ # with an mtime that is in the future (and thus not
+ # eligible for pruning).
+ test-tool chmtime -2000 "$objdir/$(test_oid_to_path $cruft_old)" &&
+ test-tool chmtime +1000 "$objdir/$(test_oid_to_path $cruft_new)" &&
+
+ # Write the list of cruft objects we expect to
+ # accumulate, which is comprised of everything reachable
+ # from cruft.old and cruft.new, but not cruft.discard.
+ git rev-list --objects --no-object-names \
+ $cruft_old $cruft_new >cruft.raw &&
+ sort cruft.raw >cruft.expect &&
+
+ # Write the script to list extra tips, which are limited
+ # to cruft.old, in this case.
+ write_script extra-tips <<-EOF &&
+ echo $cruft_old
+ EOF
+ git config gc.recentObjectsHook ./extra-tips &&
+
+ git repack --cruft --cruft-expiration=now -d &&
+
+ mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
+ git show-index <${mtimes%.mtimes}.idx >cruft &&
+ cut -d" " -f2 cruft | sort >cruft.actual &&
+ test_cmp cruft.expect cruft.actual &&
+
+ # Ensure that the "old" objects are removed after
+ # dropping the gc.recentObjectsHook hook.
+ git config --unset gc.recentObjectsHook &&
+ git repack --cruft --cruft-expiration=now -d &&
+
+ mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
+ git show-index <${mtimes%.mtimes}.idx >cruft &&
+ cut -d" " -f2 cruft | sort >cruft.actual &&
+
+ git rev-list --objects --no-object-names $cruft_new >cruft.raw &&
+ cp cruft.expect cruft.old &&
+ sort cruft.raw >cruft.expect &&
+ test_cmp cruft.expect cruft.actual &&
+
+ # ensure objects which are no longer in the cruft pack were
+ # removed from the repository
+ for object in $(comm -13 cruft.expect cruft.old)
+ do
+ test_must_fail git cat-file -t $object || return 1
+ done
+ )
+'
+
+test_expect_success 'multi-valued gc.recentObjectsHook' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+ git branch -M main &&
+
+ git checkout --orphan cruft.a &&
+ git rm -fr . &&
+ test_commit --no-tag cruft.a &&
+ cruft_a="$(git rev-parse HEAD)" &&
+
+ git checkout --orphan cruft.b &&
+ git rm -fr . &&
+ test_commit --no-tag cruft.b &&
+ cruft_b="$(git rev-parse HEAD)" &&
+
+ git checkout main &&
+ git branch -D cruft.a cruft.b &&
+ git reflog expire --all --expire=all &&
+
+ echo "echo $cruft_a" | write_script extra-tips.a &&
+ echo "echo $cruft_b" | write_script extra-tips.b &&
+ echo "false" | write_script extra-tips.c &&
+
+ git rev-list --objects --no-object-names $cruft_a $cruft_b \
+ >cruft.raw &&
+ sort cruft.raw >cruft.expect &&
+
+ # ensure that each extra cruft tip is saved by its
+ # respective hook
+ git config --add gc.recentObjectsHook ./extra-tips.a &&
+ git config --add gc.recentObjectsHook ./extra-tips.b &&
+ git repack --cruft --cruft-expiration=now -d &&
+
+ mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
+ git show-index <${mtimes%.mtimes}.idx >cruft &&
+ cut -d" " -f2 cruft | sort >cruft.actual &&
+ test_cmp cruft.expect cruft.actual &&
+
+ # ensure that a dirty exit halts cruft pack generation
+ git config --add gc.recentObjectsHook ./extra-tips.c &&
+ test_must_fail git repack --cruft --cruft-expiration=now -d 2>err &&
+ grep "unable to enumerate additional cruft tips" err &&
+
+ # and that the existing cruft pack is left alone
+ test_path_is_file "$mtimes"
+ )
+'
+
+test_expect_success 'additional cruft blobs via gc.recentObjectsHook' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+
+ blob=$(echo "unreachable" | git hash-object -w --stdin) &&
+
+ # mark the unreachable blob we wrote above as having
+ # aged out of the retention period
+ test-tool chmtime -2000 "$objdir/$(test_oid_to_path $blob)" &&
+
+ # Write the script to list extra tips, which is just the
+ # extra blob as above.
+ write_script extra-tips <<-EOF &&
+ echo $blob
+ EOF
+ git config gc.recentObjectsHook ./extra-tips &&
+
+ git repack --cruft --cruft-expiration=now -d &&
+
+ mtimes="$(ls .git/objects/pack/pack-*.mtimes)" &&
+ git show-index <${mtimes%.mtimes}.idx >cruft &&
+ cut -d" " -f2 cruft >actual &&
+ echo $blob >expect &&
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh
index 5f3ff051ca..c490a5137f 100755
--- a/t/t5407-post-rewrite-hook.sh
+++ b/t/t5407-post-rewrite-hook.sh
@@ -173,6 +173,17 @@ test_fail_interactive_rebase () {
)
}
+test_expect_success 'git rebase with failed pick' '
+ test_fail_interactive_rebase "exec_>bar pick 1" --onto C A E &&
+ rm bar &&
+ git rebase --continue &&
+ echo rebase >expected.args &&
+ cat >expected.data <<-EOF &&
+ $(git rev-parse E) $(git rev-parse HEAD)
+ EOF
+ verify_hook_input
+'
+
test_expect_success 'git rebase -i (unchanged)' '
git reset --hard D &&
clear_hook_input &&
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index dc44da9c79..4f289063ce 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -1118,59 +1118,6 @@ test_expect_success 'fetching with auto-gc does not lock up' '
)
'
-test_expect_success 'fetch aligned output' '
- git clone . full-output &&
- test_commit looooooooooooong-tag &&
- (
- cd full-output &&
- git -c fetch.output=full fetch origin >actual 2>&1 &&
- grep -e "->" actual | cut -c 22- >../actual
- ) &&
- cat >expect <<-\EOF &&
- main -> origin/main
- looooooooooooong-tag -> looooooooooooong-tag
- EOF
- test_cmp expect actual
-'
-
-test_expect_success 'fetch compact output' '
- git clone . compact &&
- test_commit extraaa &&
- (
- cd compact &&
- git -c fetch.output=compact fetch origin >actual 2>&1 &&
- grep -e "->" actual | cut -c 22- >../actual
- ) &&
- cat >expect <<-\EOF &&
- main -> origin/*
- extraaa -> *
- EOF
- test_cmp expect actual
-'
-
-test_expect_success '--no-show-forced-updates' '
- mkdir forced-updates &&
- (
- cd forced-updates &&
- git init &&
- test_commit 1 &&
- test_commit 2
- ) &&
- git clone forced-updates forced-update-clone &&
- git clone forced-updates no-forced-update-clone &&
- git -C forced-updates reset --hard HEAD~1 &&
- (
- cd forced-update-clone &&
- git fetch --show-forced-updates origin 2>output &&
- test_i18ngrep "(forced update)" output
- ) &&
- (
- cd no-forced-update-clone &&
- git fetch --no-show-forced-updates origin 2>output &&
- test_i18ngrep ! "(forced update)" output
- )
-'
-
for section in fetch transfer
do
test_expect_success "$section.hideRefs affects connectivity check" '
diff --git a/t/t5523-push-upstream.sh b/t/t5523-push-upstream.sh
index c9acc07635..1b8d609879 100755
--- a/t/t5523-push-upstream.sh
+++ b/t/t5523-push-upstream.sh
@@ -61,12 +61,20 @@ test_expect_success 'push -u :topic_2' '
check_config topic_2 upstream refs/heads/other2
'
-test_expect_success 'push -u --all' '
+test_expect_success 'push -u --all(the same behavior with--branches)' '
git branch all1 &&
git branch all2 &&
git push -u --all &&
check_config all1 upstream refs/heads/all1 &&
- check_config all2 upstream refs/heads/all2
+ check_config all2 upstream refs/heads/all2 &&
+ git config --get-regexp branch.all* > expect &&
+ git config --remove-section branch.all1 &&
+ git config --remove-section branch.all2 &&
+ git push -u --branches &&
+ check_config all1 upstream refs/heads/all1 &&
+ check_config all2 upstream refs/heads/all2 &&
+ git config --get-regexp branch.all* > actual &&
+ test_cmp expect actual
'
test_expect_success 'push -u HEAD' '
diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
index dcdbe26a08..26e933f93a 100755
--- a/t/t5526-fetch-submodules.sh
+++ b/t/t5526-fetch-submodules.sh
@@ -1180,4 +1180,17 @@ test_expect_success 'fetch --all with --recurse-submodules with multiple' '
test_line_count = 2 fetch-subs
'
+test_expect_success "fetch --all with --no-recurse-submodules only fetches superproject" '
+ test_when_finished "rm -rf src_clone" &&
+
+ git clone --recurse-submodules src src_clone &&
+ (
+ cd src_clone &&
+ git remote add secondary ../src &&
+ git config submodule.recurse true &&
+ git fetch --all --no-recurse-submodules 2>../fetch-log
+ ) &&
+ ! grep "Fetching submodule" fetch-log
+'
+
test_done
diff --git a/t/t5543-atomic-push.sh b/t/t5543-atomic-push.sh
index 70431122a4..04b47ad84a 100755
--- a/t/t5543-atomic-push.sh
+++ b/t/t5543-atomic-push.sh
@@ -117,7 +117,10 @@ test_expect_success 'atomic push fails if one branch fails' '
test_commit five &&
git checkout main &&
test_commit six &&
- test_must_fail git push --atomic --all up
+ test_must_fail git push --atomic --all up >output-all 2>&1 &&
+ # --all and --branches have the same behavior when be combined with --atomic
+ test_must_fail git push --atomic --branches up >output-branches 2>&1 &&
+ test_cmp output-all output-branches
) &&
test_refs main HEAD@{7} &&
test_refs second HEAD@{4}
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index 0908534f25..21b7767cbd 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -611,6 +611,33 @@ test_expect_success 'client falls back from v2 to v0 to match server' '
grep symref=HEAD:refs/heads/ trace
'
+test_expect_success 'create empty http-accessible SHA-256 repository' '
+ mkdir "$HTTPD_DOCUMENT_ROOT_PATH/sha256.git" &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH/sha256.git" &&
+ git --bare init --object-format=sha256
+ )
+'
+
+test_expect_success 'clone empty SHA-256 repository with protocol v2' '
+ rm -fr sha256 &&
+ echo sha256 >expected &&
+ git -c protocol.version=2 clone "$HTTPD_URL/smart/sha256.git" &&
+ git -C sha256 rev-parse --show-object-format >actual &&
+ test_cmp actual expected &&
+ git ls-remote "$HTTPD_URL/smart/sha256.git" >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'clone empty SHA-256 repository with protocol v0' '
+ rm -fr sha256 &&
+ echo sha256 >expected &&
+ GIT_TRACE=1 GIT_TRACE_PACKET=1 git -c protocol.version=0 clone "$HTTPD_URL/smart/sha256.git" &&
+ git -C sha256 rev-parse --show-object-format >actual &&
+ test_cmp actual expected &&
+ git ls-remote "$HTTPD_URL/smart/sha256.git" >actual &&
+ test_must_be_empty actual
+'
+
test_expect_success 'passing hostname resolution information works' '
BOGUS_HOST=gitbogusexamplehost.invalid &&
BOGUS_HTTPD_URL=$HTTPD_PROTO://$BOGUS_HOST:$LIB_HTTPD_PORT &&
diff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh
index 09097eff3f..4e917bf87d 100755
--- a/t/t5572-pull-submodule.sh
+++ b/t/t5572-pull-submodule.sh
@@ -121,7 +121,7 @@ test_expect_success "fetch.recurseSubmodules option triggers recursive fetch (bu
sub_oid=$(git -C child rev-parse HEAD) &&
git -C super/sub cat-file -e $sub_oid &&
# Check that the submodule worktree did not update
- ! test_path_is_file super/sub/merge_strategy_5.t
+ test_path_is_missing super/sub/merge_strategy_5.t
'
test_expect_success "fetch.recurseSubmodules takes precedence over submodule.recurse" '
@@ -134,7 +134,7 @@ test_expect_success "fetch.recurseSubmodules takes precedence over submodule.rec
sub_oid=$(git -C child rev-parse HEAD) &&
git -C super/sub cat-file -e $sub_oid &&
# Check that the submodule worktree did not update
- ! test_path_is_file super/sub/merge_strategy_6.t
+ test_path_is_missing super/sub/merge_strategy_6.t
'
test_expect_success 'pull --rebase --recurse-submodules (remote superproject submodule changes, local submodule changes)' '
diff --git a/t/t5574-fetch-output.sh b/t/t5574-fetch-output.sh
new file mode 100755
index 0000000000..90e6dcb9a7
--- /dev/null
+++ b/t/t5574-fetch-output.sh
@@ -0,0 +1,293 @@
+#!/bin/sh
+
+test_description='git fetch output format'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+test_expect_success 'fetch with invalid output format configuration' '
+ test_when_finished "rm -rf clone" &&
+ git clone . clone &&
+
+ test_must_fail git -C clone -c fetch.output fetch origin 2>actual.err &&
+ cat >expect <<-EOF &&
+ error: missing value for ${SQ}fetch.output${SQ}
+ fatal: unable to parse ${SQ}fetch.output${SQ} from command-line config
+ EOF
+ test_cmp expect actual.err &&
+
+ test_must_fail git -C clone -c fetch.output= fetch origin 2>actual.err &&
+ cat >expect <<-EOF &&
+ fatal: invalid value for ${SQ}fetch.output${SQ}: ${SQ}${SQ}
+ EOF
+ test_cmp expect actual.err &&
+
+ test_must_fail git -C clone -c fetch.output=garbage fetch origin 2>actual.err &&
+ cat >expect <<-EOF &&
+ fatal: invalid value for ${SQ}fetch.output${SQ}: ${SQ}garbage${SQ}
+ EOF
+ test_cmp expect actual.err
+'
+
+test_expect_success 'fetch aligned output' '
+ git clone . full-output &&
+ test_commit looooooooooooong-tag &&
+ (
+ cd full-output &&
+ git -c fetch.output=full fetch origin >actual 2>&1 &&
+ grep -e "->" actual | cut -c 22- >../actual
+ ) &&
+ cat >expect <<-\EOF &&
+ main -> origin/main
+ looooooooooooong-tag -> looooooooooooong-tag
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'fetch compact output' '
+ git clone . compact &&
+ test_commit extraaa &&
+ (
+ cd compact &&
+ git -c fetch.output=compact fetch origin >actual 2>&1 &&
+ grep -e "->" actual | cut -c 22- >../actual
+ ) &&
+ cat >expect <<-\EOF &&
+ main -> origin/*
+ extraaa -> *
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'fetch porcelain output' '
+ test_when_finished "rm -rf porcelain" &&
+
+ # Set up a bunch of references that we can use to demonstrate different
+ # kinds of flag symbols in the output format.
+ MAIN_OLD=$(git rev-parse HEAD) &&
+ git branch "fast-forward" &&
+ git branch "deleted-branch" &&
+ git checkout -b force-updated &&
+ test_commit --no-tag force-update-old &&
+ FORCE_UPDATED_OLD=$(git rev-parse HEAD) &&
+ git checkout main &&
+
+ # Clone and pre-seed the repositories. We fetch references into two
+ # namespaces so that we can test that rejected and force-updated
+ # references are reported properly.
+ refspecs="refs/heads/*:refs/unforced/* +refs/heads/*:refs/forced/*" &&
+ git clone . porcelain &&
+ git -C porcelain fetch origin $refspecs &&
+
+ # Now that we have set up the client repositories we can change our
+ # local references.
+ git branch new-branch &&
+ git branch -d deleted-branch &&
+ git checkout fast-forward &&
+ test_commit --no-tag fast-forward-new &&
+ FAST_FORWARD_NEW=$(git rev-parse HEAD) &&
+ git checkout force-updated &&
+ git reset --hard HEAD~ &&
+ test_commit --no-tag force-update-new &&
+ FORCE_UPDATED_NEW=$(git rev-parse HEAD) &&
+
+ cat >expect <<-EOF &&
+ - $MAIN_OLD $ZERO_OID refs/forced/deleted-branch
+ - $MAIN_OLD $ZERO_OID refs/unforced/deleted-branch
+ $MAIN_OLD $FAST_FORWARD_NEW refs/unforced/fast-forward
+ ! $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/unforced/force-updated
+ * $ZERO_OID $MAIN_OLD refs/unforced/new-branch
+ $MAIN_OLD $FAST_FORWARD_NEW refs/forced/fast-forward
+ + $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/forced/force-updated
+ * $ZERO_OID $MAIN_OLD refs/forced/new-branch
+ $MAIN_OLD $FAST_FORWARD_NEW refs/remotes/origin/fast-forward
+ + $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/remotes/origin/force-updated
+ * $ZERO_OID $MAIN_OLD refs/remotes/origin/new-branch
+ EOF
+
+ # Execute a dry-run fetch first. We do this to assert that the dry-run
+ # and non-dry-run fetches produces the same output. Execution of the
+ # fetch is expected to fail as we have a rejected reference update.
+ test_must_fail git -C porcelain fetch \
+ --porcelain --dry-run --prune origin $refspecs >actual &&
+ test_cmp expect actual &&
+
+ # And now we perform a non-dry-run fetch.
+ test_must_fail git -C porcelain fetch \
+ --porcelain --prune origin $refspecs >actual 2>stderr &&
+ test_cmp expect actual &&
+ test_must_be_empty stderr
+'
+
+test_expect_success 'fetch porcelain with multiple remotes' '
+ test_when_finished "rm -rf porcelain" &&
+
+ git switch --create multiple-remotes &&
+ git clone . porcelain &&
+ git -C porcelain remote add second-remote "$PWD" &&
+ git -C porcelain fetch second-remote &&
+
+ test_commit --no-tag multi-commit &&
+ old_commit=$(git rev-parse HEAD~) &&
+ new_commit=$(git rev-parse HEAD) &&
+
+ cat >expect <<-EOF &&
+ $old_commit $new_commit refs/remotes/origin/multiple-remotes
+ $old_commit $new_commit refs/remotes/second-remote/multiple-remotes
+ EOF
+
+ git -C porcelain fetch --porcelain --all >actual 2>stderr &&
+ test_cmp expect actual &&
+ test_must_be_empty stderr
+'
+
+test_expect_success 'fetch porcelain refuses to work with submodules' '
+ test_when_finished "rm -rf porcelain" &&
+
+ cat >expect <<-EOF &&
+ fatal: options ${SQ}--porcelain${SQ} and ${SQ}--recurse-submodules${SQ} cannot be used together
+ EOF
+
+ git init porcelain &&
+ test_must_fail git -C porcelain fetch --porcelain --recurse-submodules=yes 2>stderr &&
+ test_cmp expect stderr &&
+
+ test_must_fail git -C porcelain fetch --porcelain --recurse-submodules=on-demand 2>stderr &&
+ test_cmp expect stderr
+'
+
+test_expect_success 'fetch porcelain overrides fetch.output config' '
+ test_when_finished "rm -rf porcelain" &&
+
+ git switch --create config-override &&
+ git clone . porcelain &&
+ test_commit new-commit &&
+ old_commit=$(git rev-parse HEAD~) &&
+ new_commit=$(git rev-parse HEAD) &&
+
+ cat >expect <<-EOF &&
+ $old_commit $new_commit refs/remotes/origin/config-override
+ * $ZERO_OID $new_commit refs/tags/new-commit
+ EOF
+
+ git -C porcelain -c fetch.output=compact fetch --porcelain >stdout 2>stderr &&
+ test_must_be_empty stderr &&
+ test_cmp expect stdout
+'
+
+test_expect_success 'fetch --no-porcelain overrides previous --porcelain' '
+ test_when_finished "rm -rf no-porcelain" &&
+
+ git switch --create no-porcelain &&
+ git clone . no-porcelain &&
+ test_commit --no-tag no-porcelain &&
+ old_commit=$(git rev-parse --short HEAD~) &&
+ new_commit=$(git rev-parse --short HEAD) &&
+
+ cat >expect <<-EOF &&
+ From $(test-tool path-utils real_path .)/.
+ $old_commit..$new_commit no-porcelain -> origin/no-porcelain
+ EOF
+
+ git -C no-porcelain fetch --porcelain --no-porcelain >stdout 2>stderr &&
+ test_cmp expect stderr &&
+ test_must_be_empty stdout
+'
+
+test_expect_success 'fetch output with HEAD' '
+ test_when_finished "rm -rf head" &&
+ git clone . head &&
+
+ git -C head fetch --dry-run origin HEAD >actual.out 2>actual.err &&
+ cat >expect <<-EOF &&
+ From $(test-tool path-utils real_path .)/.
+ * branch HEAD -> FETCH_HEAD
+ EOF
+ test_must_be_empty actual.out &&
+ test_cmp expect actual.err &&
+
+ git -C head fetch origin HEAD >actual.out 2>actual.err &&
+ test_must_be_empty actual.out &&
+ test_cmp expect actual.err &&
+
+ git -C head fetch --dry-run origin HEAD:foo >actual.out 2>actual.err &&
+ cat >expect <<-EOF &&
+ From $(test-tool path-utils real_path .)/.
+ * [new ref] HEAD -> foo
+ EOF
+ test_must_be_empty actual.out &&
+ test_cmp expect actual.err &&
+
+ git -C head fetch origin HEAD:foo >actual.out 2>actual.err &&
+ test_must_be_empty actual.out &&
+ test_cmp expect actual.err
+'
+
+test_expect_success 'fetch porcelain output with HEAD' '
+ test_when_finished "rm -rf head" &&
+ git clone . head &&
+ COMMIT_ID=$(git rev-parse HEAD) &&
+
+ git -C head fetch --porcelain --dry-run origin HEAD >actual &&
+ cat >expect <<-EOF &&
+ * $ZERO_OID $COMMIT_ID FETCH_HEAD
+ EOF
+ test_cmp expect actual &&
+
+ git -C head fetch --porcelain origin HEAD >actual &&
+ test_cmp expect actual &&
+
+ git -C head fetch --porcelain --dry-run origin HEAD:foo >actual &&
+ cat >expect <<-EOF &&
+ * $ZERO_OID $COMMIT_ID refs/heads/foo
+ EOF
+ test_cmp expect actual &&
+
+ git -C head fetch --porcelain origin HEAD:foo >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'fetch output with object ID' '
+ test_when_finished "rm -rf object-id" &&
+ git clone . object-id &&
+ commit=$(git rev-parse HEAD) &&
+
+ git -C object-id fetch --dry-run origin $commit:object-id >actual.out 2>actual.err &&
+ cat >expect <<-EOF &&
+ From $(test-tool path-utils real_path .)/.
+ * [new ref] $commit -> object-id
+ EOF
+ test_must_be_empty actual.out &&
+ test_cmp expect actual.err &&
+
+ git -C object-id fetch origin $commit:object-id >actual.out 2>actual.err &&
+ test_must_be_empty actual.out &&
+ test_cmp expect actual.err
+'
+
+test_expect_success '--no-show-forced-updates' '
+ mkdir forced-updates &&
+ (
+ cd forced-updates &&
+ git init &&
+ test_commit 1 &&
+ test_commit 2
+ ) &&
+ git clone forced-updates forced-update-clone &&
+ git clone forced-updates no-forced-update-clone &&
+ git -C forced-updates reset --hard HEAD~1 &&
+ (
+ cd forced-update-clone &&
+ git fetch --show-forced-updates origin 2>output &&
+ test_i18ngrep "(forced update)" output
+ ) &&
+ (
+ cd no-forced-update-clone &&
+ git fetch --no-show-forced-updates origin 2>output &&
+ test_i18ngrep ! "(forced update)" output
+ )
+'
+
+test_done
diff --git a/t/t5583-push-branches.sh b/t/t5583-push-branches.sh
new file mode 100755
index 0000000000..e7e1b6dab6
--- /dev/null
+++ b/t/t5583-push-branches.sh
@@ -0,0 +1,115 @@
+#!/bin/sh
+
+test_description='check the consisitency of behavior of --all and --branches'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+delete_refs() {
+ dir=$1
+ shift
+ rm -rf deletes
+ for arg in $*
+ do
+ echo "delete ${arg}" >>deletes
+ done
+ git -C $dir update-ref --stdin < deletes
+}
+
+test_expect_success 'setup bare remote' '
+ git init --bare remote-1 &&
+ git -C remote-1 config gc.auto 0 &&
+ test_commit one &&
+ git push remote-1 HEAD
+'
+
+test_expect_success 'setup different types of references' '
+ cat >refs <<-EOF &&
+ update refs/heads/branch-1 HEAD
+ update refs/heads/branch-2 HEAD
+ EOF
+
+ git tag -a -m "annotated" annotated-1 HEAD &&
+ git tag -a -m "annotated" annotated-2 HEAD &&
+ git update-ref --stdin < refs
+'
+
+test_expect_success '--all and --branches have the same behavior' '
+ test_when_finished "delete_refs remote-1 \
+ refs/heads/branch-1 \
+ refs/heads/branch-2" &&
+ git push remote-1 --all &&
+ commit=$(git rev-parse HEAD) &&
+ cat >expect <<-EOF &&
+ $commit refs/heads/branch-1
+ $commit refs/heads/branch-2
+ $commit refs/heads/main
+ EOF
+
+ git -C remote-1 show-ref --heads >actual.all &&
+ delete_refs remote-1 refs/heads/branch-1 refs/heads/branch-2 &&
+ git push remote-1 --branches &&
+ git -C remote-1 show-ref --heads >actual.branches &&
+ test_cmp actual.all actual.branches &&
+ test_cmp expect actual.all
+'
+
+test_expect_success '--all or --branches can not be combined with refspecs' '
+ test_must_fail git push remote-1 --all main >actual.all 2>&1 &&
+ test_must_fail git push remote-1 --branches main >actual.branches 2>&1 &&
+ test_cmp actual.all actual.branches &&
+ grep "be combined with refspecs" actual.all
+'
+
+test_expect_success '--all or --branches can not be combined with --mirror' '
+ test_must_fail git push remote-1 --all --mirror >actual.all 2>&1 &&
+ test_must_fail git push remote-1 --branches --mirror >actual.branches 2>&1 &&
+ test_cmp actual.all actual.branches &&
+ grep "cannot be used together" actual.all
+'
+
+test_expect_success '--all or --branches can not be combined with --tags' '
+ test_must_fail git push remote-1 --all --tags >actual.all 2>&1 &&
+ test_must_fail git push remote-1 --branches --tags >actual.branches 2>&1 &&
+ test_cmp actual.all actual.branches &&
+ grep "cannot be used together" actual.all
+'
+
+
+test_expect_success '--all or --branches can not be combined with --delete' '
+ test_must_fail git push remote-1 --all --delete >actual.all 2>&1 &&
+ test_must_fail git push remote-1 --branches --delete >actual.branches 2>&1 &&
+ test_cmp actual.all actual.branches &&
+ grep "cannot be used together" actual.all
+'
+
+test_expect_success '--all or --branches combines with --follow-tags have same behavior' '
+ test_when_finished "delete_refs remote-1 \
+ refs/heads/branch-1 \
+ refs/heads/branch-2 \
+ refs/tags/annotated-1 \
+ refs/tags/annotated-2" &&
+ git push remote-1 --all --follow-tags &&
+ git -C remote-1 show-ref > actual.all &&
+ cat >expect <<-EOF &&
+ $commit refs/heads/branch-1
+ $commit refs/heads/branch-2
+ $commit refs/heads/main
+ $(git rev-parse annotated-1) refs/tags/annotated-1
+ $(git rev-parse annotated-2) refs/tags/annotated-2
+ EOF
+
+ delete_refs remote-1 \
+ refs/heads/branch-1 \
+ refs/heads/branch-2 \
+ refs/tags/annotated-1 \
+ refs/tags/annotated-2 &&
+ git push remote-1 --branches --follow-tags &&
+ git -C remote-1 show-ref >actual.branches &&
+ test_cmp actual.all actual.branches &&
+ test_cmp expect actual.all
+'
+
+test_done
diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh
index 27f9f77638..5890319b97 100755
--- a/t/t5606-clone-options.sh
+++ b/t/t5606-clone-options.sh
@@ -120,6 +120,16 @@ test_expect_success 'prefers -c config over --template config' '
'
+test_expect_failure 'prefers --template config even for core.bare' '
+
+ template="$TRASH_DIRECTORY/template-with-bare-config" &&
+ mkdir "$template" &&
+ git config --file "$template/config" core.bare true &&
+ git clone "--template=$template" parent clone-bare-config &&
+ test "$(git -C clone-bare-config config --local core.bare)" = "true" &&
+ test_path_is_file clone-bare-config/HEAD
+'
+
test_expect_success 'prefers config "clone.defaultRemoteName" over default' '
test_config_global clone.defaultRemoteName from_config &&
diff --git a/t/t5700-protocol-v1.sh b/t/t5700-protocol-v1.sh
index 6c8d4c6cf1..a73b4d4ff6 100755
--- a/t/t5700-protocol-v1.sh
+++ b/t/t5700-protocol-v1.sh
@@ -244,15 +244,28 @@ test_expect_success 'push with ssh:// using protocol v1' '
grep "push< version 1" log
'
+test_expect_success 'clone propagates object-format from empty repo' '
+ test_when_finished "rm -fr src256 dst256" &&
+
+ echo sha256 >expect &&
+ git init --object-format=sha256 src256 &&
+ git clone --no-local src256 dst256 &&
+ git -C dst256 rev-parse --show-object-format >actual &&
+
+ test_cmp expect actual
+'
+
# Test protocol v1 with 'http://' transport
#
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
-test_expect_success 'create repo to be served by http:// transport' '
+test_expect_success 'create repos to be served by http:// transport' '
git init "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" config http.receivepack true &&
- test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" one
+ test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" one &&
+ git init --object-format=sha256 "$HTTPD_DOCUMENT_ROOT_PATH/sha256" &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH/sha256" config http.receivepack true
'
test_expect_success 'clone with http:// using protocol v1' '
@@ -269,6 +282,20 @@ test_expect_success 'clone with http:// using protocol v1' '
grep "git< version 1" log
'
+test_expect_success 'clone with http:// using protocol v1 with empty SHA-256 repo' '
+ GIT_TRACE_PACKET=1 GIT_TRACE_CURL=1 git -c protocol.version=1 \
+ clone "$HTTPD_URL/smart/sha256" sha256 2>log &&
+
+ echo sha256 >expect &&
+ git -C sha256 rev-parse --show-object-format >actual &&
+ test_cmp expect actual &&
+
+ # Client requested to use protocol v1
+ grep "Git-Protocol: version=1" log &&
+ # Server responded using protocol v1
+ grep "git< version 1" log
+'
+
test_expect_success 'fetch with http:// using protocol v1' '
test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" two &&
diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh
index 41d0ca00b1..573eb97a0f 100755
--- a/t/t6006-rev-list-format.sh
+++ b/t/t6006-rev-list-format.sh
@@ -493,7 +493,7 @@ test_expect_success 'empty email' '
test_tick &&
C=$(GIT_AUTHOR_EMAIL= git commit-tree HEAD^{tree} </dev/null) &&
A=$(git show --pretty=format:%an,%ae,%ad%n -s $C) &&
- verbose test "$A" = "$GIT_AUTHOR_NAME,,Thu Apr 7 15:14:13 2005 -0700"
+ test "$A" = "$GIT_AUTHOR_NAME,,Thu Apr 7 15:14:13 2005 -0700"
'
test_expect_success 'del LF before empty (1)' '
diff --git a/t/t6102-rev-list-unexpected-objects.sh b/t/t6102-rev-list-unexpected-objects.sh
index 9350b5fd2c..98e36e7369 100755
--- a/t/t6102-rev-list-unexpected-objects.sh
+++ b/t/t6102-rev-list-unexpected-objects.sh
@@ -130,4 +130,150 @@ test_expect_success 'traverse unexpected non-blob tag (seen)' '
test_i18ngrep "not a blob" output
'
+test_expect_success !SANITIZE_LEAK 'setup unexpected non-tag tag' '
+ test_when_finished "git tag -d tag-commit tag-tag" &&
+
+ git tag -a -m"my tagged commit" tag-commit $commit &&
+ tag_commit=$(git rev-parse tag-commit) &&
+ git tag -a -m"my tagged tag" tag-tag tag-commit &&
+ tag_tag=$(git rev-parse tag-tag) &&
+
+ git cat-file tag tag-tag >good-tag-tag &&
+ git cat-file tag tag-commit >good-commit-tag &&
+
+ sed -e "s/$tag_commit/$commit/" <good-tag-tag >broken-tag-tag-commit &&
+ sed -e "s/$tag_commit/$tree/" <good-tag-tag >broken-tag-tag-tree &&
+ sed -e "s/$tag_commit/$blob/" <good-tag-tag >broken-tag-tag-blob &&
+
+ sed -e "s/$commit/$tag_commit/" <good-commit-tag >broken-commit-tag-tag &&
+ sed -e "s/$commit/$tree/" <good-commit-tag >broken-commit-tag-tree &&
+ sed -e "s/$commit/$blob/" <good-commit-tag >broken-commit-tag-blob &&
+
+ tag_tag_commit=$(git hash-object -w -t tag broken-tag-tag-commit) &&
+ tag_tag_tree=$(git hash-object -w -t tag broken-tag-tag-tree) &&
+ tag_tag_blob=$(git hash-object -w -t tag broken-tag-tag-blob) &&
+
+ git update-ref refs/tags/tag_tag_commit $tag_tag_commit &&
+ git update-ref refs/tags/tag_tag_tree $tag_tag_tree &&
+ git update-ref refs/tags/tag_tag_blob $tag_tag_blob &&
+
+ commit_tag_tag=$(git hash-object -w -t tag broken-commit-tag-tag) &&
+ commit_tag_tree=$(git hash-object -w -t tag broken-commit-tag-tree) &&
+ commit_tag_blob=$(git hash-object -w -t tag broken-commit-tag-blob) &&
+
+ git update-ref refs/tags/commit_tag_tag $commit_tag_tag &&
+ git update-ref refs/tags/commit_tag_tree $commit_tag_tree &&
+ git update-ref refs/tags/commit_tag_blob $commit_tag_blob
+'
+
+test_expect_success !SANITIZE_LEAK 'traverse unexpected incorrectly typed tag (to commit & tag)' '
+ test_must_fail git rev-list --objects $tag_tag_commit 2>err &&
+ cat >expect <<-EOF &&
+ error: object $commit is a commit, not a tag
+ fatal: bad object $commit
+ EOF
+ test_cmp expect err &&
+
+ test_must_fail git rev-list --objects $commit_tag_tag 2>err &&
+ cat >expect <<-EOF &&
+ error: object $tag_commit is a tag, not a commit
+ fatal: bad object $tag_commit
+ EOF
+ test_cmp expect err
+'
+
+test_expect_success !SANITIZE_LEAK 'traverse unexpected incorrectly typed tag (to tree)' '
+ test_must_fail git rev-list --objects $tag_tag_tree 2>err &&
+ cat >expect <<-EOF &&
+ error: object $tree is a tree, not a tag
+ fatal: bad object $tree
+ EOF
+ test_cmp expect err &&
+
+ test_must_fail git rev-list --objects $commit_tag_tree 2>err &&
+ cat >expect <<-EOF &&
+ error: object $tree is a tree, not a commit
+ fatal: bad object $tree
+ EOF
+ test_cmp expect err
+'
+
+test_expect_success !SANITIZE_LEAK 'traverse unexpected incorrectly typed tag (to blob)' '
+ test_must_fail git rev-list --objects $tag_tag_blob 2>err &&
+ cat >expect <<-EOF &&
+ error: object $blob is a blob, not a tag
+ fatal: bad object $blob
+ EOF
+ test_cmp expect err &&
+
+ test_must_fail git rev-list --objects $commit_tag_blob 2>err &&
+ cat >expect <<-EOF &&
+ error: object $blob is a blob, not a commit
+ fatal: bad object $blob
+ EOF
+ test_cmp expect err
+'
+
+test_expect_success !SANITIZE_LEAK 'traverse unexpected non-tag tag (tree seen to blob)' '
+ test_must_fail git rev-list --objects $tree $commit_tag_blob 2>err &&
+ cat >expect <<-EOF &&
+ error: object $blob is a blob, not a commit
+ fatal: bad object $blob
+ EOF
+ test_cmp expect err &&
+
+ test_must_fail git rev-list --objects $tree $tag_tag_blob 2>err &&
+ cat >expect <<-EOF &&
+ error: object $blob is a blob, not a tag
+ fatal: bad object $blob
+ EOF
+ test_cmp expect err
+'
+
+
+test_expect_success !SANITIZE_LEAK 'traverse unexpected objects with for-each-ref' '
+ cat >expect <<-EOF &&
+ error: bad tag pointer to $tree in $tag_tag_tree
+ fatal: parse_object_buffer failed on $tag_tag_tree for refs/tags/tag_tag_tree
+ EOF
+ test_must_fail git for-each-ref --format="%(*objectname)" 2>actual &&
+ test_cmp expect actual
+'
+
+>fsck-object-isa
+test_expect_success 'setup: unexpected objects with fsck' '
+ test_must_fail git fsck 2>err &&
+ sed -n -e "/^error: object .* is a .*, not a .*$/ {
+ s/^error: object \([0-9a-f]*\) is a \([a-z]*\), not a [a-z]*$/\\1 \\2/;
+ p;
+ }" <err >fsck-object-isa
+'
+
+while read oid type
+do
+ test_expect_success "fsck knows unexpected object $oid is $type" '
+ git cat-file -t $oid >expect &&
+ echo $type >actual &&
+ test_cmp expect actual
+ '
+done <fsck-object-isa
+
+test_expect_success !SANITIZE_LEAK 'traverse unexpected non-tag tag (blob seen to blob)' '
+ test_must_fail git rev-list --objects $blob $commit_tag_blob 2>err &&
+ cat >expected <<-EOF &&
+ error: object $blob is a blob, not a commit
+ error: bad tag pointer to $blob in $commit_tag_blob
+ fatal: bad object $commit_tag_blob
+ EOF
+ test_cmp expected err &&
+
+ test_must_fail git rev-list --objects $blob $tag_tag_blob 2>err &&
+ cat >expected <<-EOF &&
+ error: object $blob is a blob, not a tag
+ error: bad tag pointer to $blob in $tag_tag_blob
+ fatal: bad object $tag_tag_blob
+ EOF
+ test_cmp expected err
+'
+
test_done
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 5c00607608..7e8d578522 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -448,6 +448,41 @@ test_expect_success 'exercise glob patterns with prefixes' '
'
cat >expected <<\EOF
+refs/tags/bar
+refs/tags/baz
+refs/tags/testtag
+EOF
+
+test_expect_success 'exercise patterns with prefix exclusions' '
+ for tag in foo/one foo/two foo/three bar baz
+ do
+ git tag "$tag" || return 1
+ done &&
+ test_when_finished "git tag -d foo/one foo/two foo/three bar baz" &&
+ git for-each-ref --format="%(refname)" \
+ refs/tags/ --exclude=refs/tags/foo >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+refs/tags/bar
+refs/tags/baz
+refs/tags/foo/one
+refs/tags/testtag
+EOF
+
+test_expect_success 'exercise patterns with pattern exclusions' '
+ for tag in foo/one foo/two foo/three bar baz
+ do
+ git tag "$tag" || return 1
+ done &&
+ test_when_finished "git tag -d foo/one foo/two foo/three bar baz" &&
+ git for-each-ref --format="%(refname)" \
+ refs/tags/ --exclude="refs/tags/foo/t*" >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
'refs/heads/main'
'refs/remotes/origin/main'
'refs/tags/testtag'
diff --git a/t/t6501-freshen-objects.sh b/t/t6501-freshen-objects.sh
index dbfa8a4d4c..4521508b83 100755
--- a/t/t6501-freshen-objects.sh
+++ b/t/t6501-freshen-objects.sh
@@ -152,7 +152,7 @@ test_expect_success 'do not complain about existing broken links (commit)' '
EOF
commit=$(git hash-object -t commit -w broken-commit) &&
git gc --no-cruft -q 2>stderr &&
- verbose git cat-file -e $commit &&
+ git cat-file -e $commit &&
test_must_be_empty stderr
'
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index d72cef8826..898a920532 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -4,6 +4,10 @@ test_description='git mv in subdirs'
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-diff-data.sh
+index_at_path () {
+ git ls-files --format='%(objectmode) %(objectname) %(stage)' "$@"
+}
+
test_expect_success 'mv -f refreshes updated index entry' '
echo test >bar &&
git add bar &&
@@ -187,7 +191,8 @@ test_expect_success "Michael Cassar's test case" '
git mv papers/unsorted/Thesis.pdf papers/all-papers/moo-blah.pdf &&
T=$(git write-tree) &&
- git ls-tree -r $T | verbose grep partA/outline.txt
+ git ls-tree -r $T >out &&
+ grep partA/outline.txt out
'
rm -fr papers partA path?
@@ -260,12 +265,12 @@ test_expect_success 'git mv should not change sha1 of moved cache entry' '
git init &&
echo 1 >dirty &&
git add dirty &&
- entry="$(git ls-files --stage dirty | cut -f 1)" &&
+ entry="$(index_at_path dirty)" &&
git mv dirty dirty2 &&
- test "$entry" = "$(git ls-files --stage dirty2 | cut -f 1)" &&
+ test "$entry" = "$(index_at_path dirty2)" &&
echo 2 >dirty2 &&
git mv dirty2 dirty &&
- test "$entry" = "$(git ls-files --stage dirty | cut -f 1)"
+ test "$entry" = "$(index_at_path dirty)"
'
rm -f dirty dirty2
@@ -342,7 +347,7 @@ test_expect_success 'git mv cannot move a submodule in a file' '
'
test_expect_success 'git mv moves a submodule with a .git directory and no .gitmodules' '
- entry="$(git ls-files --stage sub | cut -f 1)" &&
+ entry="$(index_at_path sub)" &&
git rm .gitmodules &&
(
cd sub &&
@@ -353,7 +358,7 @@ test_expect_success 'git mv moves a submodule with a .git directory and no .gitm
mkdir mod &&
git mv sub mod/sub &&
test_path_is_missing sub &&
- test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+ test "$entry" = "$(index_at_path mod/sub)" &&
git -C mod/sub status &&
git update-index --refresh &&
git diff-files --quiet
@@ -363,7 +368,7 @@ test_expect_success 'git mv moves a submodule with a .git directory and .gitmodu
rm -rf mod &&
git reset --hard &&
git submodule update &&
- entry="$(git ls-files --stage sub | cut -f 1)" &&
+ entry="$(index_at_path sub)" &&
(
cd sub &&
rm -f .git &&
@@ -373,7 +378,7 @@ test_expect_success 'git mv moves a submodule with a .git directory and .gitmodu
mkdir mod &&
git mv sub mod/sub &&
test_path_is_missing sub &&
- test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+ test "$entry" = "$(index_at_path mod/sub)" &&
git -C mod/sub status &&
echo mod/sub >expected &&
git config -f .gitmodules submodule.sub.path >actual &&
@@ -386,11 +391,11 @@ test_expect_success 'git mv moves a submodule with gitfile' '
rm -rf mod &&
git reset --hard &&
git submodule update &&
- entry="$(git ls-files --stage sub | cut -f 1)" &&
+ entry="$(index_at_path sub)" &&
mkdir mod &&
git -C mod mv ../sub/ . &&
test_path_is_missing sub &&
- test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+ test "$entry" = "$(index_at_path mod/sub)" &&
git -C mod/sub status &&
echo mod/sub >expected &&
git config -f .gitmodules submodule.sub.path >actual &&
@@ -404,12 +409,12 @@ test_expect_success 'mv does not complain when no .gitmodules file is found' '
git reset --hard &&
git submodule update &&
git rm .gitmodules &&
- entry="$(git ls-files --stage sub | cut -f 1)" &&
+ entry="$(index_at_path sub)" &&
mkdir mod &&
git mv sub mod/sub 2>actual.err &&
test_must_be_empty actual.err &&
test_path_is_missing sub &&
- test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+ test "$entry" = "$(index_at_path mod/sub)" &&
git -C mod/sub status &&
git update-index --refresh &&
git diff-files --quiet
@@ -420,7 +425,7 @@ test_expect_success 'mv will error out on a modified .gitmodules file unless sta
git reset --hard &&
git submodule update &&
git config -f .gitmodules foo.bar true &&
- entry="$(git ls-files --stage sub | cut -f 1)" &&
+ entry="$(index_at_path sub)" &&
mkdir mod &&
test_must_fail git mv sub mod/sub 2>actual.err &&
test_file_not_empty actual.err &&
@@ -430,7 +435,7 @@ test_expect_success 'mv will error out on a modified .gitmodules file unless sta
git mv sub mod/sub 2>actual.err &&
test_must_be_empty actual.err &&
test_path_is_missing sub &&
- test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+ test "$entry" = "$(index_at_path mod/sub)" &&
git -C mod/sub status &&
git update-index --refresh &&
git diff-files --quiet
@@ -442,13 +447,13 @@ test_expect_success 'mv issues a warning when section is not found in .gitmodule
git submodule update &&
git config -f .gitmodules --remove-section submodule.sub &&
git add .gitmodules &&
- entry="$(git ls-files --stage sub | cut -f 1)" &&
+ entry="$(index_at_path sub)" &&
echo "warning: Could not find section in .gitmodules where path=sub" >expect.err &&
mkdir mod &&
git mv sub mod/sub 2>actual.err &&
test_cmp expect.err actual.err &&
test_path_is_missing sub &&
- test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" &&
+ test "$entry" = "$(index_at_path mod/sub)" &&
git -C mod/sub status &&
git update-index --refresh &&
git diff-files --quiet
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 0fe6ba93a2..e689db4292 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -2188,4 +2188,23 @@ test_expect_success 'Does --[no-]contains stop at commits? Yes!' '
test_cmp expected actual
'
+test_expect_success 'If tag is created then tag message file is unlinked' '
+ test_when_finished "git tag -d foo" &&
+ write_script fakeeditor <<-\EOF &&
+ echo Message >.git/TAG_EDITMSG
+ EOF
+ GIT_EDITOR=./fakeeditor git tag -a foo &&
+ test_path_is_missing .git/TAG_EDITMSG
+'
+
+test_expect_success 'If tag cannot be created then tag message file is not unlinked' '
+ test_when_finished "git tag -d foo/bar && rm .git/TAG_EDITMSG" &&
+ write_script fakeeditor <<-\EOF &&
+ echo Message >.git/TAG_EDITMSG
+ EOF
+ git tag foo/bar &&
+ test_must_fail env GIT_EDITOR=./fakeeditor git tag -a foo &&
+ test_path_exists .git/TAG_EDITMSG
+'
+
test_done
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index c975eb54d2..0ef7b78457 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -120,7 +120,7 @@ test_expect_success 'git clean with relative prefix' '
grep part3 |
sed -n -e "s|^Would remove ||p"
) &&
- verbose test "$would_clean" = ../src/part3.c
+ test "$would_clean" = ../src/part3.c
'
test_expect_success 'git clean with absolute path' '
@@ -133,7 +133,7 @@ test_expect_success 'git clean with absolute path' '
grep part3 |
sed -n -e "s|^Would remove ||p"
) &&
- verbose test "$would_clean" = ../src/part3.c
+ test "$would_clean" = ../src/part3.c
'
test_expect_success 'git clean with out of work tree relative path' '
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
index d050091345..7da64e4c4c 100755
--- a/t/t7506-status-submodule.sh
+++ b/t/t7506-status-submodule.sh
@@ -412,4 +412,29 @@ test_expect_success 'status with added file in nested submodule (short)' '
EOF
'
+test_expect_success 'status in superproject with submodules respects parallel settings' '
+ test_when_finished "rm -f trace.out" &&
+ (
+ GIT_TRACE=$(pwd)/trace.out git status &&
+ grep "1 tasks" trace.out &&
+ >trace.out &&
+
+ git config submodule.diffJobs 8 &&
+ GIT_TRACE=$(pwd)/trace.out git status &&
+ grep "8 tasks" trace.out &&
+ >trace.out &&
+
+ GIT_TRACE=$(pwd)/trace.out git -c submodule.diffJobs=0 status &&
+ grep "preparing to run up to [0-9]* tasks" trace.out &&
+ ! grep "up to 0 tasks" trace.out &&
+ >trace.out
+ )
+'
+
+test_expect_success 'status in superproject with submodules (parallel)' '
+ git -C super status --porcelain >output &&
+ git -C super -c submodule.diffJobs=8 status --porcelain >output_parallel &&
+ diff output output_parallel
+'
+
test_done
diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh
index ebb267855f..ba428c18a8 100755
--- a/t/t7701-repack-unpack-unreachable.sh
+++ b/t/t7701-repack-unpack-unreachable.sh
@@ -113,6 +113,37 @@ test_expect_success 'do not bother loosening old objects' '
test_must_fail git cat-file -p $obj2
'
+test_expect_success 'gc.recentObjectsHook' '
+ obj1=$(echo one | git hash-object -w --stdin) &&
+ obj2=$(echo two | git hash-object -w --stdin) &&
+ obj3=$(echo three | git hash-object -w --stdin) &&
+ pack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) &&
+ pack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) &&
+ pack3=$(echo $obj3 | git pack-objects .git/objects/pack/pack) &&
+ git prune-packed &&
+
+ git cat-file -p $obj1 &&
+ git cat-file -p $obj2 &&
+ git cat-file -p $obj3 &&
+
+ git tag -a -m tag obj2-tag $obj2 &&
+ obj2_tag="$(git rev-parse obj2-tag)" &&
+
+ write_script precious-objects <<-EOF &&
+ echo $obj2_tag
+ EOF
+ git config gc.recentObjectsHook ./precious-objects &&
+
+ test-tool chmtime =-86400 .git/objects/pack/pack-$pack2.pack &&
+ test-tool chmtime =-86400 .git/objects/pack/pack-$pack3.pack &&
+ git repack -A -d --unpack-unreachable=1.hour.ago &&
+
+ git cat-file -p $obj1 &&
+ git cat-file -p $obj2 &&
+ git cat-file -p $obj2_tag &&
+ test_must_fail git cat-file -p $obj3
+'
+
test_expect_success 'keep packed objects found only in index' '
echo my-unique-content >file &&
git add file &&
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 6520346246..48bf0af2ee 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -47,7 +47,7 @@ clean_fake_sendmail () {
test_expect_success $PREREQ 'Extract patches' '
patches=$(git format-patch -s --cc="One <one@example.com>" --cc=two@example.com -n HEAD^1) &&
- threaded_patches=$(git format-patch -o threaded -s --in-reply-to="format" HEAD^1)
+ threaded_patches=$(git format-patch -o threaded --thread=shallow -s --in-reply-to="format" HEAD^1)
'
# Test no confirm early to ensure remaining tests will not hang
@@ -374,13 +374,16 @@ test_expect_success $PREREQ,!AUTOIDENT 'broken implicit ident aborts send-email'
)
'
-test_expect_success $PREREQ 'setup tocmd and cccmd scripts' '
+test_expect_success $PREREQ 'setup cmd scripts' '
write_script tocmd-sed <<-\EOF &&
sed -n -e "s/^tocmd--//p" "$1"
EOF
- write_script cccmd-sed <<-\EOF
+ write_script cccmd-sed <<-\EOF &&
sed -n -e "s/^cccmd--//p" "$1"
EOF
+ write_script headercmd-sed <<-\EOF
+ sed -n -e "s/^headercmd--//p" "$1"
+ EOF
'
test_expect_success $PREREQ 'tocmd works' '
@@ -410,6 +413,70 @@ test_expect_success $PREREQ 'cccmd works' '
grep "^ cccmd@example.com" msgtxt1
'
+test_expect_success $PREREQ 'headercmd works' '
+ clean_fake_sendmail &&
+ cp $patches headercmd.patch &&
+ echo "headercmd--X-Debbugs-CC: dummy@example.com" >>headercmd.patch &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --header-cmd=./headercmd-sed \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ headercmd.patch \
+ &&
+ grep "^X-Debbugs-CC: dummy@example.com" msgtxt1
+'
+
+test_expect_success $PREREQ '--no-header-cmd works' '
+ clean_fake_sendmail &&
+ cp $patches headercmd.patch &&
+ echo "headercmd--X-Debbugs-CC: dummy@example.com" >>headercmd.patch &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --header-cmd=./headercmd-sed \
+ --no-header-cmd \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ headercmd.patch \
+ &&
+ ! grep "^X-Debbugs-CC: dummy@example.com" msgtxt1
+'
+
+test_expect_success $PREREQ 'multiline fields are correctly unfolded' '
+ clean_fake_sendmail &&
+ cp $patches headercmd.patch &&
+ write_script headercmd-multiline <<-\EOF &&
+ echo "X-Debbugs-CC: someone@example.com
+FoldedField: This is a tale
+ best told using
+ multiple lines."
+ EOF
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --header-cmd=./headercmd-multiline \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ headercmd.patch &&
+ grep "^FoldedField: This is a tale best told using multiple lines.$" msgtxt1
+'
+
+# Blank lines in the middle of the output of a command are invalid.
+test_expect_success $PREREQ 'malform output reported on blank lines in command output' '
+ clean_fake_sendmail &&
+ cp $patches headercmd.patch &&
+ write_script headercmd-malformed-output <<-\EOF &&
+ echo "X-Debbugs-CC: someone@example.com
+
+SomeOtherField: someone-else@example.com"
+ EOF
+ ! git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --header-cmd=./headercmd-malformed-output \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ headercmd.patch
+'
+
test_expect_success $PREREQ 'reject long lines' '
z8=zzzzzzzz &&
z64=$z8$z8$z8$z8$z8$z8$z8$z8 &&
@@ -540,7 +607,7 @@ test_expect_success $PREREQ "--validate respects relative core.hooksPath path" '
test_path_is_file my-hooks.ran &&
cat >expect <<-EOF &&
fatal: longline.patch: rejected by sendemail-validate hook
- fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch>'"'"' died with exit code 1
+ fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch> <header>'"'"' died with exit code 1
warning: no patches were sent
EOF
test_cmp expect actual
@@ -559,12 +626,50 @@ test_expect_success $PREREQ "--validate respects absolute core.hooksPath path" '
test_path_is_file my-hooks.ran &&
cat >expect <<-EOF &&
fatal: longline.patch: rejected by sendemail-validate hook
- fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch>'"'"' died with exit code 1
+ fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch> <header>'"'"' died with exit code 1
warning: no patches were sent
EOF
test_cmp expect actual
'
+test_expect_success $PREREQ "--validate hook supports header argument" '
+ write_script my-hooks/sendemail-validate <<-\EOF &&
+ if test "$#" -ge 2
+ then
+ grep "X-test-header: v1.0" "$2"
+ else
+ echo "No header arg passed"
+ exit 1
+ fi
+ EOF
+ test_config core.hooksPath "my-hooks" &&
+ rm -fr outdir &&
+ git format-patch \
+ --add-header="X-test-header: v1.0" \
+ -n HEAD^1 -o outdir &&
+ git send-email \
+ --dry-run \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ --validate \
+ outdir/000?-*.patch
+'
+
+test_expect_success $PREREQ 'clear message-id before parsing a new message' '
+ clean_fake_sendmail &&
+ echo true | write_script my-hooks/sendemail-validate &&
+ test_config core.hooksPath my-hooks &&
+ GIT_SEND_EMAIL_NOTTY=1 \
+ git send-email --validate --to=recipient@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches $threaded_patches &&
+ id0=$(grep "^Message-ID: " $threaded_patches) &&
+ id1=$(grep "^Message-ID: " msgtxt1) &&
+ id2=$(grep "^Message-ID: " msgtxt2) &&
+ test "z$id0" = "z$id2" &&
+ test "z$id1" != "z$id2"
+'
+
for enc in 7bit 8bit quoted-printable base64
do
test_expect_success $PREREQ "--transfer-encoding=$enc produces correct header" '
diff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh
index dc88d0e064..a4b3cb9492 100755
--- a/t/t9800-git-p4-basic.sh
+++ b/t/t9800-git-p4-basic.sh
@@ -330,7 +330,7 @@ test_expect_success 'initial import time from top change time' '
test_when_finished cleanup_git &&
(
cd "$git" &&
- gittime=$(git show -s --raw --pretty=format:%at HEAD) &&
+ gittime=$(git show -s --pretty=format:%at HEAD) &&
echo $p4time $gittime &&
test $p4time = $gittime
)
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index d6c0478d98..09af98a7dc 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -405,40 +405,40 @@ test_expect_success '__gitdir - remote as argument' '
test_expect_success '__git_dequote - plain unquoted word' '
__git_dequote unquoted-word &&
- verbose test unquoted-word = "$dequoted_word"
+ test unquoted-word = "$dequoted_word"
'
# input: b\a\c\k\'\\\"s\l\a\s\h\es
# expected: back'\"slashes
test_expect_success '__git_dequote - backslash escaped' '
__git_dequote "b\a\c\k\\'\''\\\\\\\"s\l\a\s\h\es" &&
- verbose test "back'\''\\\"slashes" = "$dequoted_word"
+ test "back'\''\\\"slashes" = "$dequoted_word"
'
# input: sin'gle\' '"quo'ted
# expected: single\ "quoted
test_expect_success '__git_dequote - single quoted' '
__git_dequote "'"sin'gle\\\\' '\\\"quo'ted"'" &&
- verbose test '\''single\ "quoted'\'' = "$dequoted_word"
+ test '\''single\ "quoted'\'' = "$dequoted_word"
'
# input: dou"ble\\" "\"\quot"ed
# expected: double\ "\quoted
test_expect_success '__git_dequote - double quoted' '
__git_dequote '\''dou"ble\\" "\"\quot"ed'\'' &&
- verbose test '\''double\ "\quoted'\'' = "$dequoted_word"
+ test '\''double\ "\quoted'\'' = "$dequoted_word"
'
# input: 'open single quote
test_expect_success '__git_dequote - open single quote' '
__git_dequote "'\''open single quote" &&
- verbose test "open single quote" = "$dequoted_word"
+ test "open single quote" = "$dequoted_word"
'
# input: "open double quote
test_expect_success '__git_dequote - open double quote' '
__git_dequote "\"open double quote" &&
- verbose test "open double quote" = "$dequoted_word"
+ test "open double quote" = "$dequoted_word"
'
@@ -616,7 +616,7 @@ test_expect_success '__git_is_configured_remote' '
test_when_finished "git remote remove remote_2" &&
git remote add remote_2 git://remote_2 &&
(
- verbose __git_is_configured_remote remote_2 &&
+ __git_is_configured_remote remote_2 &&
test_must_fail __git_is_configured_remote non-existent
)
'
@@ -2527,6 +2527,8 @@ test_expect_success 'git config - variable name' '
log.date Z
log.decorate Z
log.diffMerges Z
+ log.diffMergesHide Z
+ log.diffMerges-m-imply-p Z
EOF
'
@@ -2555,6 +2557,8 @@ test_expect_success 'git -c - variable name' '
log.date=Z
log.decorate=Z
log.diffMerges=Z
+ log.diffMergesHide=Z
+ log.diffMerges-m-imply-p=Z
EOF
'
@@ -2577,6 +2581,8 @@ test_expect_success 'git clone --config= - variable name' '
log.date=Z
log.decorate=Z
log.diffMerges=Z
+ log.diffMergesHide=Z
+ log.diffMerges-m-imply-p=Z
EOF
'
@@ -2596,30 +2602,30 @@ test_expect_success 'options with value' '
test_expect_success 'sourcing the completion script clears cached commands' '
(
__git_compute_all_commands &&
- verbose test -n "$__git_all_commands" &&
+ test -n "$__git_all_commands" &&
. "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
- verbose test -z "$__git_all_commands"
+ test -z "$__git_all_commands"
)
'
test_expect_success 'sourcing the completion script clears cached merge strategies' '
(
__git_compute_merge_strategies &&
- verbose test -n "$__git_merge_strategies" &&
+ test -n "$__git_merge_strategies" &&
. "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
- verbose test -z "$__git_merge_strategies"
+ test -z "$__git_merge_strategies"
)
'
test_expect_success 'sourcing the completion script clears cached --options' '
(
__gitcomp_builtin checkout &&
- verbose test -n "$__gitcomp_builtin_checkout" &&
+ test -n "$__gitcomp_builtin_checkout" &&
__gitcomp_builtin notes_edit &&
- verbose test -n "$__gitcomp_builtin_notes_edit" &&
+ test -n "$__gitcomp_builtin_notes_edit" &&
. "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" &&
- verbose test -z "$__gitcomp_builtin_checkout" &&
- verbose test -z "$__gitcomp_builtin_notes_edit"
+ test -z "$__gitcomp_builtin_checkout" &&
+ test -z "$__gitcomp_builtin_notes_edit"
)
'
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 999d46fafe..6e19ebc922 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -1227,15 +1227,6 @@ test_i18ngrep () {
return 1
}
-# Call any command "$@" but be more verbose about its
-# failure. This is handy for commands like "test" which do
-# not output anything when they fail.
-verbose () {
- "$@" && return 0
- echo >&4 "command failed: $(git rev-parse --sq-quote "$@")"
- return 1
-}
-
# Check if the file expected to be empty is indeed empty, and barfs
# otherwise.
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 293caf0f20..3315919eaf 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -157,6 +157,8 @@ parse_option () {
local opt="$1"
case "$opt" in
+ -c|--c|--co|--cou|--coun|--count|--counts)
+ record_counts=t ;;
-d|--d|--de|--deb|--debu|--debug)
debug=t ;;
-i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate)
@@ -1276,7 +1278,7 @@ test_done () {
finalize_test_output
- if test -z "$HARNESS_ACTIVE"
+ if test -z "$HARNESS_ACTIVE" || test -n "$record_counts"
then
mkdir -p "$TEST_RESULTS_DIR"
diff --git a/tag.c b/tag.c
index 96dbd5b2d5..c356d58890 100644
--- a/tag.c
+++ b/tag.c
@@ -1,8 +1,9 @@
#include "git-compat-util.h"
#include "environment.h"
+#include "gettext.h"
#include "tag.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "commit.h"
#include "tree.h"
#include "blob.h"
@@ -104,12 +105,18 @@ struct object *deref_tag_noverify(struct object *o)
return o;
}
-struct tag *lookup_tag(struct repository *r, const struct object_id *oid)
+struct tag *lookup_tag_type(struct repository *r, const struct object_id *oid,
+ enum object_type type)
{
struct object *obj = lookup_object(r, oid);
if (!obj)
return create_object(r, oid, alloc_tag_node(r));
- return object_as_type(obj, OBJ_TAG, 0);
+ return object_as_type_hint(obj, OBJ_TAG, type);
+}
+
+struct tag *lookup_tag(struct repository *r, const struct object_id *oid)
+{
+ return lookup_tag_type(r, oid, OBJ_NONE);
}
static timestamp_t parse_tag_date(const char *buf, const char *tail)
@@ -139,6 +146,7 @@ void release_tag_memory(struct tag *t)
int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, unsigned long size)
{
+ struct object *obj;
struct object_id oid;
char type[20];
const char *bufptr = data;
@@ -173,7 +181,10 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
type[nl - bufptr] = '\0';
bufptr = nl + 1;
- if (!strcmp(type, blob_type)) {
+ obj = lookup_object(r, &oid);
+ if (obj) {
+ item->tagged = obj;
+ } else if (!strcmp(type, blob_type)) {
item->tagged = (struct object *)lookup_blob(r, &oid);
} else if (!strcmp(type, tree_type)) {
item->tagged = (struct object *)lookup_tree(r, &oid);
@@ -186,10 +197,14 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
type, oid_to_hex(&item->object.oid));
}
- if (!item->tagged)
+ if (!item->tagged || strcmp(type_name(item->tagged->type), type)) {
+ if (item->tagged && item->tagged->parsed)
+ error(_("object %s is a %s, not a %s"), oid_to_hex(&oid),
+ type_name(item->tagged->type), type);
return error("bad tag pointer to %s in %s",
oid_to_hex(&oid),
oid_to_hex(&item->object.oid));
+ }
if (bufptr + 4 < tail && starts_with(bufptr, "tag "))
; /* good */
diff --git a/tag.h b/tag.h
index 3ce8e72192..42bd3e6401 100644
--- a/tag.h
+++ b/tag.h
@@ -12,6 +12,8 @@ struct tag {
timestamp_t date;
};
struct tag *lookup_tag(struct repository *r, const struct object_id *oid);
+struct tag *lookup_tag_type(struct repository *r, const struct object_id *oid,
+ enum object_type type);
int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, unsigned long size);
int parse_tag(struct tag *item);
void release_tag_memory(struct tag *t);
diff --git a/tempfile.c b/tempfile.c
index 50c377134c..6c88a63b42 100644
--- a/tempfile.c
+++ b/tempfile.c
@@ -43,6 +43,7 @@
*/
#include "git-compat-util.h"
+#include "abspath.h"
#include "path.h"
#include "tempfile.h"
#include "sigchain.h"
diff --git a/tmp-objdir.c b/tmp-objdir.c
index c33a554f92..5f9074ad1c 100644
--- a/tmp-objdir.c
+++ b/tmp-objdir.c
@@ -5,12 +5,13 @@
#include "dir.h"
#include "environment.h"
#include "object-file.h"
+#include "path.h"
#include "sigchain.h"
#include "string-list.h"
#include "strbuf.h"
#include "strvec.h"
#include "quote.h"
-#include "object-store.h"
+#include "object-store-ll.h"
struct tmp_objdir {
struct strbuf path;
diff --git a/trace2.c b/trace2.c
index 21264df71b..0efc4e7b95 100644
--- a/trace2.c
+++ b/trace2.c
@@ -2,6 +2,7 @@
#include "config.h"
#include "json-writer.h"
#include "quote.h"
+#include "repository.h"
#include "run-command.h"
#include "sigchain.h"
#include "thread-utils.h"
diff --git a/trace2.h b/trace2.h
index 4ced30c0db..9452e291f5 100644
--- a/trace2.h
+++ b/trace2.h
@@ -551,6 +551,8 @@ enum trace2_counter_id {
TRACE2_COUNTER_ID_TEST1 = 0, /* emits summary event only */
TRACE2_COUNTER_ID_TEST2, /* emits summary and thread events */
+ TRACE2_COUNTER_ID_PACKED_REFS_JUMPS, /* counts number of jumps */
+
/* Add additional counter definitions before here. */
TRACE2_NUMBER_OF_COUNTERS
};
diff --git a/trace2/tr2_cfg.c b/trace2/tr2_cfg.c
index 78cfc15d52..db817a80c5 100644
--- a/trace2/tr2_cfg.c
+++ b/trace2/tr2_cfg.c
@@ -4,6 +4,7 @@
#include "trace2.h"
#include "trace2/tr2_cfg.h"
#include "trace2/tr2_sysenv.h"
+#include "wildmatch.h"
static struct strbuf **tr2_cfg_patterns;
static int tr2_cfg_count_patterns;
diff --git a/trace2/tr2_ctr.c b/trace2/tr2_ctr.c
index b342d3b1a3..50570d0165 100644
--- a/trace2/tr2_ctr.c
+++ b/trace2/tr2_ctr.c
@@ -27,6 +27,11 @@ static struct tr2_counter_metadata tr2_counter_metadata[TRACE2_NUMBER_OF_COUNTER
.name = "test2",
.want_per_thread_events = 1,
},
+ [TRACE2_COUNTER_ID_PACKED_REFS_JUMPS] = {
+ .category = "packed-refs",
+ .name = "jumps_made",
+ .want_per_thread_events = 0,
+ },
/* Add additional metadata before here. */
};
diff --git a/trace2/tr2_tgt_event.c b/trace2/tr2_tgt_event.c
index 9e7aab6d51..2af53e5d4d 100644
--- a/trace2/tr2_tgt_event.c
+++ b/trace2/tr2_tgt_event.c
@@ -1,6 +1,7 @@
#include "git-compat-util.h"
#include "config.h"
#include "json-writer.h"
+#include "repository.h"
#include "run-command.h"
#include "version.h"
#include "trace2/tr2_dst.h"
diff --git a/trace2/tr2_tgt_normal.c b/trace2/tr2_tgt_normal.c
index 8672c2c2d0..1ebfb464d5 100644
--- a/trace2/tr2_tgt_normal.c
+++ b/trace2/tr2_tgt_normal.c
@@ -1,5 +1,6 @@
#include "git-compat-util.h"
#include "config.h"
+#include "repository.h"
#include "run-command.h"
#include "quote.h"
#include "version.h"
diff --git a/trace2/tr2_tgt_perf.c b/trace2/tr2_tgt_perf.c
index 3f2b2d5311..328e483a05 100644
--- a/trace2/tr2_tgt_perf.c
+++ b/trace2/tr2_tgt_perf.c
@@ -1,5 +1,6 @@
#include "git-compat-util.h"
#include "config.h"
+#include "repository.h"
#include "run-command.h"
#include "quote.h"
#include "version.h"
diff --git a/transport-helper.c b/transport-helper.c
index 6b816940dc..5c0bc6a896 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -8,6 +8,7 @@
#include "gettext.h"
#include "hex.h"
#include "object-name.h"
+#include "repository.h"
#include "revision.h"
#include "remote.h"
#include "string-list.h"
diff --git a/transport.c b/transport.c
index 67afdae57c..4dc187a388 100644
--- a/transport.c
+++ b/transport.c
@@ -27,7 +27,7 @@
#include "transport-internal.h"
#include "protocol.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "color.h"
#include "bundle-uri.h"
#include "wrapper.h"
diff --git a/tree-diff.c b/tree-diff.c
index 69031d7cba..966946848a 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -1,10 +1,25 @@
/*
* Helper functions for tree diff generation
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "diff.h"
#include "diffcore.h"
+#include "hash.h"
#include "tree.h"
+#include "tree-walk.h"
+
+/*
+ * Some mode bits are also used internally for computations.
+ *
+ * They *must* not overlap with any valid modes, and they *must* not be emitted
+ * to outside world - i.e. appear on disk or network. In other words, it's just
+ * temporary fields, which we internally use, but they have to stay in-house.
+ *
+ * ( such approach is valid, as standard S_IF* fits into 16 bits, and in Git
+ * codebase mode is `unsigned int` which is assumed to be at least 32 bits )
+ */
+
+#define S_DIFFTREE_IFXMIN_NEQ 0x80000000
/*
* internal mode marker, saying a tree entry != entry of tp[imin]
diff --git a/tree-walk.c b/tree-walk.c
index 2993c48c2f..42ed86ef58 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -1,11 +1,11 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "tree-walk.h"
#include "alloc.h"
#include "dir.h"
#include "gettext.h"
#include "hex.h"
#include "object-file.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "trace2.h"
#include "tree.h"
#include "pathspec.h"
diff --git a/tree-walk.h b/tree-walk.h
index 25fe27e352..01a9d8eb44 100644
--- a/tree-walk.h
+++ b/tree-walk.h
@@ -1,9 +1,10 @@
#ifndef TREE_WALK_H
#define TREE_WALK_H
-#include "hash.h"
+#include "hash-ll.h"
struct index_state;
+struct repository;
#define MAX_TRAVERSE_TREES 8
diff --git a/tree.c b/tree.c
index e9d51ce2e0..5710c38746 100644
--- a/tree.c
+++ b/tree.c
@@ -1,9 +1,9 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "cache-tree.h"
#include "hex.h"
#include "tree.h"
#include "object-name.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "blob.h"
#include "commit.h"
#include "tag.h"
@@ -94,22 +94,87 @@ int read_tree(struct repository *r,
return ret;
}
-int cmp_cache_name_compare(const void *a_, const void *b_)
+int base_name_compare(const char *name1, size_t len1, int mode1,
+ const char *name2, size_t len2, int mode2)
{
- const struct cache_entry *ce1, *ce2;
+ unsigned char c1, c2;
+ size_t len = len1 < len2 ? len1 : len2;
+ int cmp;
+
+ cmp = memcmp(name1, name2, len);
+ if (cmp)
+ return cmp;
+ c1 = name1[len];
+ c2 = name2[len];
+ if (!c1 && S_ISDIR(mode1))
+ c1 = '/';
+ if (!c2 && S_ISDIR(mode2))
+ c2 = '/';
+ return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
+}
- ce1 = *((const struct cache_entry **)a_);
- ce2 = *((const struct cache_entry **)b_);
- return cache_name_stage_compare(ce1->name, ce1->ce_namelen, ce_stage(ce1),
- ce2->name, ce2->ce_namelen, ce_stage(ce2));
+/*
+ * df_name_compare() is identical to base_name_compare(), except it
+ * compares conflicting directory/file entries as equal. Note that
+ * while a directory name compares as equal to a regular file, they
+ * then individually compare _differently_ to a filename that has
+ * a dot after the basename (because '\0' < '.' < '/').
+ *
+ * This is used by routines that want to traverse the git namespace
+ * but then handle conflicting entries together when possible.
+ */
+int df_name_compare(const char *name1, size_t len1, int mode1,
+ const char *name2, size_t len2, int mode2)
+{
+ unsigned char c1, c2;
+ size_t len = len1 < len2 ? len1 : len2;
+ int cmp;
+
+ cmp = memcmp(name1, name2, len);
+ if (cmp)
+ return cmp;
+ /* Directories and files compare equal (same length, same name) */
+ if (len1 == len2)
+ return 0;
+ c1 = name1[len];
+ if (!c1 && S_ISDIR(mode1))
+ c1 = '/';
+ c2 = name2[len];
+ if (!c2 && S_ISDIR(mode2))
+ c2 = '/';
+ if (c1 == '/' && !c2)
+ return 0;
+ if (c2 == '/' && !c1)
+ return 0;
+ return c1 - c2;
}
-struct tree *lookup_tree(struct repository *r, const struct object_id *oid)
+int name_compare(const char *name1, size_t len1, const char *name2, size_t len2)
+{
+ size_t min_len = (len1 < len2) ? len1 : len2;
+ int cmp = memcmp(name1, name2, min_len);
+ if (cmp)
+ return cmp;
+ if (len1 < len2)
+ return -1;
+ if (len1 > len2)
+ return 1;
+ return 0;
+}
+
+struct tree *lookup_tree_type(struct repository *r,
+ const struct object_id *oid,
+ enum object_type type)
{
struct object *obj = lookup_object(r, oid);
if (!obj)
return create_object(r, oid, alloc_tree_node(r));
- return object_as_type(obj, OBJ_TREE, 0);
+ return object_as_type_hint(obj, OBJ_TREE, type);
+}
+
+struct tree *lookup_tree(struct repository *r, const struct object_id *oid)
+{
+ return lookup_tree_type(r, oid, OBJ_NONE);
}
int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size)
diff --git a/tree.h b/tree.h
index 6efff003e2..0e7f40071c 100644
--- a/tree.h
+++ b/tree.h
@@ -3,6 +3,7 @@
#include "object.h"
+struct pathspec;
struct repository;
struct strbuf;
@@ -15,6 +16,8 @@ struct tree {
extern const char *tree_type;
struct tree *lookup_tree(struct repository *r, const struct object_id *oid);
+struct tree *lookup_tree_type(struct repository *r, const struct object_id *oid,
+ enum object_type type);
int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size);
@@ -28,7 +31,15 @@ void free_tree_buffer(struct tree *tree);
/* Parses and returns the tree in the given ent, chasing tags and commits. */
struct tree *parse_tree_indirect(const struct object_id *oid);
-int cmp_cache_name_compare(const void *a_, const void *b_);
+/*
+ * Functions for comparing pathnames
+ */
+int base_name_compare(const char *name1, size_t len1, int mode1,
+ const char *name2, size_t len2, int mode2);
+int df_name_compare(const char *name1, size_t len1, int mode1,
+ const char *name2, size_t len2, int mode2);
+int name_compare(const char *name1, size_t len1,
+ const char *name2, size_t len2);
#define READ_TREE_RECURSIVE 1
typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const char *, unsigned int, void *);
diff --git a/unpack-trees.c b/unpack-trees.c
index c0732aa0c2..87517364dc 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "advice.h"
#include "strvec.h"
#include "repository.h"
@@ -7,6 +7,7 @@
#include "environment.h"
#include "gettext.h"
#include "hex.h"
+#include "name-hash.h"
#include "tree.h"
#include "tree-walk.h"
#include "cache-tree.h"
@@ -14,13 +15,15 @@
#include "progress.h"
#include "refs.h"
#include "attr.h"
+#include "read-cache.h"
#include "split-index.h"
#include "sparse-index.h"
#include "submodule.h"
#include "submodule-config.h"
+#include "symlinks.h"
#include "trace2.h"
#include "fsmonitor.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "promisor-remote.h"
#include "entry.h"
#include "parallel-checkout.h"
diff --git a/unpack-trees.h b/unpack-trees.h
index 30622aeebf..9b827c307f 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -1,8 +1,8 @@
#ifndef UNPACK_TREES_H
#define UNPACK_TREES_H
-#include "cache.h"
#include "convert.h"
+#include "read-cache-ll.h"
#include "strvec.h"
#include "string-list.h"
#include "tree-walk.h"
diff --git a/upload-pack.c b/upload-pack.c
index 08633dc121..3a176a7209 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -7,7 +7,7 @@
#include "pkt-line.h"
#include "sideband.h"
#include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "oid-array.h"
#include "tag.h"
#include "object.h"
@@ -69,7 +69,7 @@ struct upload_pack_data {
struct object_array have_obj;
struct oid_array haves; /* v2 only */
struct string_list wanted_refs; /* v2 only */
- struct string_list hidden_refs;
+ struct strvec hidden_refs;
struct object_array shallows;
struct string_list deepen_not;
@@ -120,13 +120,14 @@ struct upload_pack_data {
unsigned allow_ref_in_want : 1; /* v2 only */
unsigned allow_sideband_all : 1; /* v2 only */
unsigned advertise_sid : 1;
+ unsigned sent_capabilities : 1;
};
static void upload_pack_data_init(struct upload_pack_data *data)
{
struct string_list symref = STRING_LIST_INIT_DUP;
struct string_list wanted_refs = STRING_LIST_INIT_DUP;
- struct string_list hidden_refs = STRING_LIST_INIT_DUP;
+ struct strvec hidden_refs = STRVEC_INIT;
struct object_array want_obj = OBJECT_ARRAY_INIT;
struct object_array have_obj = OBJECT_ARRAY_INIT;
struct oid_array haves = OID_ARRAY_INIT;
@@ -161,7 +162,7 @@ static void upload_pack_data_clear(struct upload_pack_data *data)
{
string_list_clear(&data->symref, 1);
string_list_clear(&data->wanted_refs, 1);
- string_list_clear(&data->hidden_refs, 0);
+ strvec_clear(&data->hidden_refs);
object_array_clear(&data->want_obj);
object_array_clear(&data->have_obj);
oid_array_clear(&data->haves);
@@ -601,11 +602,32 @@ static int get_common_commits(struct upload_pack_data *data,
}
}
+static int allow_hidden_refs(enum allow_uor allow_uor)
+{
+ return allow_uor & (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1);
+}
+
+static void for_each_namespaced_ref_1(each_ref_fn fn,
+ struct upload_pack_data *data)
+{
+ /*
+ * If `data->allow_uor` allows fetching hidden refs, we need to
+ * mark all references (including hidden ones), to check in
+ * `is_our_ref()` below.
+ *
+ * Otherwise, we only care about whether each reference's object
+ * has the OUR_REF bit set or not, so do not need to visit
+ * hidden references.
+ */
+ if (allow_hidden_refs(data->allow_uor))
+ for_each_namespaced_ref(NULL, fn, data);
+ else
+ for_each_namespaced_ref(data->hidden_refs.v, fn, data);
+}
+
static int is_our_ref(struct object *o, enum allow_uor allow_uor)
{
- int allow_hidden_ref = (allow_uor &
- (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));
- return o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF);
+ return o->flags & ((allow_hidden_refs(allow_uor) ? HIDDEN_REF : 0) | OUR_REF);
}
/*
@@ -854,7 +876,7 @@ static void deepen(struct upload_pack_data *data, int depth)
* marked with OUR_REF.
*/
head_ref_namespaced(check_ref, data);
- for_each_namespaced_ref(check_ref, data);
+ for_each_namespaced_ref_1(check_ref, data);
get_reachable_list(data, &reachable_shallows);
result = get_shallow_commits(&reachable_shallows,
@@ -1169,7 +1191,7 @@ static void receive_needs(struct upload_pack_data *data,
/* return non-zero if the ref is hidden, otherwise 0 */
static int mark_our_ref(const char *refname, const char *refname_full,
- const struct object_id *oid, const struct string_list *hidden_refs)
+ const struct object_id *oid, const struct strvec *hidden_refs)
{
struct object *o = lookup_unknown_object(the_repository, oid);
@@ -1206,18 +1228,17 @@ static void format_session_id(struct strbuf *buf, struct upload_pack_data *d) {
strbuf_addf(buf, " session-id=%s", trace2_session_id());
}
-static int send_ref(const char *refname, const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static void write_v0_ref(struct upload_pack_data *data,
+ const char *refname, const char *refname_nons,
+ const struct object_id *oid)
{
static const char *capabilities = "multi_ack thin-pack side-band"
" side-band-64k ofs-delta shallow deepen-since deepen-not"
" deepen-relative no-progress include-tag multi_ack_detailed";
- const char *refname_nons = strip_namespace(refname);
struct object_id peeled;
- struct upload_pack_data *data = cb_data;
if (mark_our_ref(refname_nons, refname, oid, &data->hidden_refs))
- return 0;
+ return;
if (capabilities) {
struct strbuf symref_info = STRBUF_INIT;
@@ -1240,12 +1261,20 @@ static int send_ref(const char *refname, const struct object_id *oid,
git_user_agent_sanitized());
strbuf_release(&symref_info);
strbuf_release(&session_id);
+ data->sent_capabilities = 1;
} else {
packet_fwrite_fmt(stdout, "%s %s\n", oid_to_hex(oid), refname_nons);
}
capabilities = NULL;
if (!peel_iterated_oid(oid, &peeled))
packet_fwrite_fmt(stdout, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
+ return;
+}
+
+static int send_ref(const char *refname, const struct object_id *oid,
+ int flag UNUSED, void *cb_data)
+{
+ write_v0_ref(cb_data, refname, strip_namespace(refname), oid);
return 0;
}
@@ -1378,7 +1407,11 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
if (advertise_refs)
data.no_done = 1;
head_ref_namespaced(send_ref, &data);
- for_each_namespaced_ref(send_ref, &data);
+ for_each_namespaced_ref_1(send_ref, &data);
+ if (!data.sent_capabilities) {
+ const char *refname = "capabilities^{}";
+ write_v0_ref(&data, refname, refname, null_oid());
+ }
/*
* fflush stdout before calling advertise_shallow_grafts because send_ref
* uses stdio.
@@ -1388,7 +1421,7 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
packet_flush(1);
} else {
head_ref_namespaced(check_ref, &data);
- for_each_namespaced_ref(check_ref, &data);
+ for_each_namespaced_ref_1(check_ref, &data);
}
if (!advertise_refs) {
@@ -1453,7 +1486,7 @@ static int parse_want(struct packet_writer *writer, const char *line,
static int parse_want_ref(struct packet_writer *writer, const char *line,
struct string_list *wanted_refs,
- struct string_list *hidden_refs,
+ struct strvec *hidden_refs,
struct object_array *want_obj)
{
const char *refname_nons;
diff --git a/userdiff.c b/userdiff.c
index eaec6ebb5e..664c7c1402 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -444,7 +444,7 @@ struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
check = attr_check_initl("diff", NULL);
if (!path)
return NULL;
- git_check_attr(istate, NULL, path, check);
+ git_check_attr(istate, path, check);
if (ATTR_TRUE(check->items[0].value))
return &driver_true;
diff --git a/versioncmp.c b/versioncmp.c
index 7498da96e0..74cc7c43f0 100644
--- a/versioncmp.c
+++ b/versioncmp.c
@@ -1,6 +1,7 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "config.h"
#include "string-list.h"
+#include "versioncmp.h"
/*
* versioncmp(): copied from string/strverscmp.c in glibc commit
diff --git a/versioncmp.h b/versioncmp.h
new file mode 100644
index 0000000000..879b510e82
--- /dev/null
+++ b/versioncmp.h
@@ -0,0 +1,6 @@
+#ifndef VERSIONCMP_H
+#define VERSIONCMP_H
+
+int versioncmp(const char *s1, const char *s2);
+
+#endif /* VERSIONCMP_H */
diff --git a/walker.c b/walker.c
index 24ff7dfdc2..65002a7220 100644
--- a/walker.c
+++ b/walker.c
@@ -3,8 +3,9 @@
#include "hex.h"
#include "walker.h"
#include "repository.h"
-#include "object-store.h"
+#include "object-store-ll.h"
#include "commit.h"
+#include "strbuf.h"
#include "tree.h"
#include "tree-walk.h"
#include "tag.h"
diff --git a/worktree.c b/worktree.c
index b5ee71c5eb..f9aa371ca7 100644
--- a/worktree.c
+++ b/worktree.c
@@ -3,6 +3,7 @@
#include "alloc.h"
#include "environment.h"
#include "gettext.h"
+#include "path.h"
#include "repository.h"
#include "refs.h"
#include "setup.h"
diff --git a/wrapper.c b/wrapper.c
index c130d7518b..67f5f5dbe1 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -1,10 +1,13 @@
/*
* Various trivial helper wrappers around standard functions
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "abspath.h"
#include "config.h"
#include "gettext.h"
+#include "object.h"
+#include "repository.h"
+#include "strbuf.h"
#include "trace2.h"
#include "wrapper.h"
diff --git a/ws.c b/ws.c
index da3d0e28cb..9456e2fdbe 100644
--- a/ws.c
+++ b/ws.c
@@ -3,8 +3,12 @@
*
* Copyright (c) 2007 Junio C Hamano
*/
-#include "cache.h"
+#include "git-compat-util.h"
#include "attr.h"
+#include "strbuf.h"
+#include "ws.h"
+
+unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
static struct whitespace_rule {
const char *rule_name;
@@ -79,7 +83,7 @@ unsigned whitespace_rule(struct index_state *istate, const char *pathname)
if (!attr_whitespace_rule)
attr_whitespace_rule = attr_check_initl("whitespace", NULL);
- git_check_attr(istate, NULL, pathname, attr_whitespace_rule);
+ git_check_attr(istate, pathname, attr_whitespace_rule);
value = attr_whitespace_rule->items[0].value;
if (ATTR_TRUE(value)) {
/* true (whitespace) */
diff --git a/ws.h b/ws.h
new file mode 100644
index 0000000000..5ba676c559
--- /dev/null
+++ b/ws.h
@@ -0,0 +1,33 @@
+#ifndef WS_H
+#define WS_H
+
+struct index_state;
+struct strbuf;
+
+/*
+ * whitespace rules.
+ * used by both diff and apply
+ * last two digits are tab width
+ */
+#define WS_BLANK_AT_EOL 0100
+#define WS_SPACE_BEFORE_TAB 0200
+#define WS_INDENT_WITH_NON_TAB 0400
+#define WS_CR_AT_EOL 01000
+#define WS_BLANK_AT_EOF 02000
+#define WS_TAB_IN_INDENT 04000
+#define WS_TRAILING_SPACE (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
+#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
+#define WS_TAB_WIDTH_MASK 077
+/* All WS_* -- when extended, adapt diff.c emit_symbol */
+#define WS_RULE_MASK 07777
+extern unsigned whitespace_rule_cfg;
+unsigned whitespace_rule(struct index_state *, const char *);
+unsigned parse_whitespace_rule(const char *);
+unsigned ws_check(const char *line, int len, unsigned ws_rule);
+void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
+char *whitespace_error_string(unsigned ws);
+void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
+int ws_blank_line(const char *line, int len);
+#define ws_tab_width(rule) ((rule) & WS_TAB_WIDTH_MASK)
+
+#endif /* WS_H */
diff --git a/wt-status.c b/wt-status.c
index 97b9c1c035..ea4e49c42b 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1,4 +1,4 @@
-#include "cache.h"
+#include "git-compat-util.h"
#include "advice.h"
#include "wt-status.h"
#include "object.h"
@@ -7,8 +7,10 @@
#include "diff.h"
#include "environment.h"
#include "gettext.h"
+#include "hash.h"
#include "hex.h"
#include "object-name.h"
+#include "path.h"
#include "revision.h"
#include "diffcore.h"
#include "quote.h"
@@ -18,10 +20,12 @@
#include "refs.h"
#include "submodule.h"
#include "column.h"
+#include "read-cache.h"
#include "setup.h"
#include "strbuf.h"
#include "trace.h"
#include "trace2.h"
+#include "tree.h"
#include "utf8.h"
#include "worktree.h"
#include "lockfile.h"
@@ -1023,7 +1027,7 @@ static void wt_longstatus_print_submodule_summary(struct wt_status *s, int uncom
if (s->display_comment_prefix) {
size_t len;
summary_content = strbuf_detach(&summary, &len);
- strbuf_add_commented_lines(&summary, summary_content, len);
+ strbuf_add_commented_lines(&summary, summary_content, len, comment_line_char);
free(summary_content);
}
@@ -1098,8 +1102,8 @@ void wt_status_append_cut_line(struct strbuf *buf)
{
const char *explanation = _("Do not modify or remove the line above.\nEverything below it will be ignored.");
- strbuf_commented_addf(buf, "%s", cut_line);
- strbuf_add_commented_lines(buf, explanation, strlen(explanation));
+ strbuf_commented_addf(buf, comment_line_char, "%s", cut_line);
+ strbuf_add_commented_lines(buf, explanation, strlen(explanation), comment_line_char);
}
void wt_status_add_cut_line(FILE *fp)
diff --git a/xdiff-interface.c b/xdiff-interface.c
index 0460e03f5e..abc7275a52 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -1,7 +1,8 @@
#include "git-compat-util.h"
#include "config.h"
#include "hex.h"
-#include "object-store.h"
+#include "object-store-ll.h"
+#include "strbuf.h"
#include "xdiff-interface.h"
#include "xdiff/xtypes.h"
#include "xdiff/xdiffi.h"
diff --git a/xdiff-interface.h b/xdiff-interface.h
index 3750794afe..733c364d26 100644
--- a/xdiff-interface.h
+++ b/xdiff-interface.h
@@ -1,7 +1,7 @@
#ifndef XDIFF_INTERFACE_H
#define XDIFF_INTERFACE_H
-#include "hash.h"
+#include "hash-ll.h"
#include "xdiff/xdiff.h"
/*